ZUGFeRD / Factur-X 电子发票
ZUGFeRD 与 Factur-X 是欧洲广泛采用的电子发票标准。它们的核心概念是生成一份「混合式发票」:PDF 本身是人类可阅读的发票文件,同时在 PDF 内部嵌入一份机器可解析的 XML 结构化数据。这样一来,同一份文件既可以打印归档,也能被 ERP 系统自动导入处理。
什么是 ZUGFeRD / Factur-X?
ZUGFeRD(全名 Zentraler User Guide des Forums elektronische Rechnung Deutschland)源自德国,Factur-X 则是法国与欧盟的对应标准。两者在技术规格上完全相同,都基于 UN/CEFACT Cross Industry Invoice (CII) 格式。
一份合规的电子发票包含两个部分:
- 可视化 PDF -- 符合 PDF/A-3 或 PDF/A-4f 标准的人类可读发票
- 嵌入式 XML -- 以 CII 格式记录的结构化发票数据,作为 PDF 的附件嵌入
配置文件层级
根据数据完整度的不同,ZUGFeRD 定义了多种层级(Profile):
| 层级 | 数据内容 | 适用场景 |
|---|---|---|
| Minimum | 发票号码、日期、金额、税额 | 最基本的自动化处理 |
| Basic WL | 加上明细项目、付款条件 | 标准 B2B 发票 |
| Basic | 加上详细明细信息 | 最常使用的层级 |
| EN 16931 (Comfort) | 完整符合 EN 16931 | 欧盟公共采购 |
| Extended | 加上额外商业条款 | 复杂发票需求 |
| XRechnung | 德国政府专用格式 | 德国公共部门 |
完整示例
步骤一:创建 PDF/A-4f 文件
PDF/A-4f 是 PDF/A 系列中支持嵌入附件的子集,是 ZUGFeRD 电子发票的建议格式:
php
use YeeeFang\TcpdfNext\Document\PdfDocument;
use YeeeFang\TcpdfNext\Document\PageFormat;
use YeeeFang\TcpdfNext\Archive\PdfALevel;
use YeeeFang\TcpdfNext\Archive\OutputIntent;
use YeeeFang\TcpdfNext\Html\HtmlRenderer;
$pdf = PdfDocument::create()
->setPdfALevel(PdfALevel::PDF_A_4F)
->setOutputIntent(OutputIntent::sRGB())
->setTitle('电子发票 INV-2026-001')
->setAuthor('示例科技有限公司')
->setSubject('ZUGFeRD 2.3 电子发票')
->setPageFormat(PageFormat::A4)
->build();步骤二:渲染发票的可视内容
php
$renderer = new HtmlRenderer($pdf);
$pdf->addPage();
$invoiceHtml = <<<'HTML'
<h2 style="text-align: center; color: #2C3E50;">电子发票</h2>
<table cellpadding="5" border="0" width="100%">
<tr>
<td width="50%">
<strong>卖方</strong><br>
示例科技有限公司<br>
统一社会信用代码:91310000MA1FL8XX42<br>
上海市浦东新区张江路 100 号
</td>
<td width="50%">
<strong>买方</strong><br>
客户公司名称<br>
统一社会信用代码:91310000MA1GK9YY31<br>
北京市朝阳区建国路 200 号
</td>
</tr>
</table>
<br>
<table border="1" cellpadding="4" width="100%">
<tr style="background-color: #34495E; color: white;">
<th>品名</th>
<th>数量</th>
<th>单价</th>
<th>税额</th>
<th>合计</th>
</tr>
<tr>
<td>软件授权</td>
<td style="text-align: center;">1</td>
<td style="text-align: right;">¥ 50,000</td>
<td style="text-align: right;">¥ 2,500</td>
<td style="text-align: right;">¥ 52,500</td>
</tr>
<tr style="background-color: #F8F9FA;">
<td>技术支持(年约)</td>
<td style="text-align: center;">1</td>
<td style="text-align: right;">¥ 12,000</td>
<td style="text-align: right;">¥ 600</td>
<td style="text-align: right;">¥ 12,600</td>
</tr>
<tr style="background-color: #ECF0F1; font-weight: bold;">
<td colspan="3" style="text-align: right;">合计</td>
<td style="text-align: right;">¥ 3,100</td>
<td style="text-align: right;">¥ 65,100</td>
</tr>
</table>
HTML;
$renderer->writeHtml($invoiceHtml);步骤三:生成 ZUGFeRD XML
XML 遵循 UN/CEFACT CII 格式,记录发票的所有结构化数据:
php
$xml = <<<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<rsm:CrossIndustryInvoice
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
<rsm:ExchangedDocumentContext>
<ram:GuidelineSpecifiedDocumentContextParameter>
<ram:ID>urn:factur-x.eu:1p0:en16931</ram:ID>
</ram:GuidelineSpecifiedDocumentContextParameter>
</rsm:ExchangedDocumentContext>
<rsm:ExchangedDocument>
<ram:ID>INV-2026-001</ram:ID>
<ram:TypeCode>380</ram:TypeCode>
<ram:IssueDateTime>
<udt:DateTimeString format="102">20260216</udt:DateTimeString>
</ram:IssueDateTime>
</rsm:ExchangedDocument>
<rsm:SupplyChainTradeTransaction>
<ram:ApplicableHeaderTradeAgreement>
<ram:SellerTradeParty>
<ram:Name>示例科技有限公司</ram:Name>
<ram:PostalTradeAddress>
<ram:CountryID>CN</ram:CountryID>
</ram:PostalTradeAddress>
<ram:SpecifiedTaxRegistration>
<ram:ID schemeID="VA">91310000MA1FL8XX42</ram:ID>
</ram:SpecifiedTaxRegistration>
</ram:SellerTradeParty>
<ram:BuyerTradeParty>
<ram:Name>客户公司名称</ram:Name>
<ram:PostalTradeAddress>
<ram:CountryID>CN</ram:CountryID>
</ram:PostalTradeAddress>
</ram:BuyerTradeParty>
</ram:ApplicableHeaderTradeAgreement>
<ram:ApplicableHeaderTradeSettlement>
<ram:InvoiceCurrencyCode>CNY</ram:InvoiceCurrencyCode>
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
<ram:LineTotalAmount>62000.00</ram:LineTotalAmount>
<ram:TaxBasisTotalAmount>62000.00</ram:TaxBasisTotalAmount>
<ram:TaxTotalAmount currencyID="CNY">3100.00</ram:TaxTotalAmount>
<ram:GrandTotalAmount>65100.00</ram:GrandTotalAmount>
<ram:DuePayableAmount>65100.00</ram:DuePayableAmount>
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
</ram:ApplicableHeaderTradeSettlement>
</rsm:SupplyChainTradeTransaction>
</rsm:CrossIndustryInvoice>
XML;步骤四:将 XML 嵌入 PDF
php
use YeeeFang\TcpdfNext\Archive\EmbeddedFile;
use YeeeFang\TcpdfNext\Archive\AFRelationship;
$pdf->addEmbeddedFile(
EmbeddedFile::create()
->setFilename('factur-x.xml')
->setMimeType('text/xml')
->setContent($xml)
->setRelationship(AFRelationship::ALTERNATIVE)
->setDescription('Factur-X invoice data (EN 16931 profile)')
->setCreationDate(new \DateTimeImmutable('2026-02-16'))
->setModificationDate(new \DateTimeImmutable('2026-02-16'))
);步骤五:设置 ZUGFeRD XMP 元数据
XMP 元数据让 PDF 阅读器与验证工具能够辨识这是一份 ZUGFeRD / Factur-X 电子发票:
php
$pdf->getMetadata()
->setXmpProperty('fx:DocumentType', 'INVOICE')
->setXmpProperty('fx:DocumentFileName', 'factur-x.xml')
->setXmpProperty('fx:Version', '1.0')
->setXmpProperty('fx:ConformanceLevel', 'EN 16931');步骤六:可选加入数字签名
为发票加入 PAdES B-LTA 签名,确保长期有效性与不可否认性:
php
use YeeeFang\TcpdfNext\Signature\PdfSigner;
use YeeeFang\TcpdfNext\Signature\SignatureLevel;
$signer = new PdfSigner($pdf);
$signer->setCertificate($cert, $privateKey)
->setLevel(SignatureLevel::PAdES_B_LTA)
->setTimestampServer('https://timestamp.digicert.com')
->setReason('Invoice issuance')
->setLocation('Shanghai, China')
->sign();步骤七:输出发票
php
$pdf->save('/invoices/INV-2026-001.pdf');使用便捷工具类
TCPDF-Next 提供 ZugferdInvoice 工具类,简化上述流程:
php
use YeeeFang\TcpdfNext\Cookbook\ZugferdInvoice;
use YeeeFang\TcpdfNext\Cookbook\ZugferdProfile;
$invoice = ZugferdInvoice::create()
->setProfile(ZugferdProfile::EN16931)
->setPdfHtml($invoiceHtml)
->setXmlContent($xml)
->setOutputIntent(OutputIntent::sRGB())
->build();
// 可选签名
$invoice->sign($cert, $privateKey, SignatureLevel::PAdES_B_LTA, $tsaUrl);
$invoice->save('/invoices/INV-2026-001.pdf');验证
生成的电子发票可以通过外部工具验证 PDF/A 合规性与 XML 结构正确性:
bash
# 验证 PDF/A-4f 合规性
verapdf --flavour 4f /invoices/INV-2026-001.pdf
# 使用 Mustangproject 验证 Factur-X XML
java -jar Mustang-CLI.jar --action validate --source /invoices/INV-2026-001.pdf适用范围
虽然 ZUGFeRD / Factur-X 是欧洲标准,但「将结构化 XML 嵌入 PDF/A」的做法同样适用于中国的电子发票或其他需要人机双重可读的业务文件。这个模式可以延伸应用到任何需要兼顾视觉呈现与自动化处理的场景。
电子发票工作流程
┌──────────────┐ ┌───────────────┐ ┌──────────────┐
│ ERP 系统 │───→│ TCPDF-Next │───→│ PDF/A-4f │
│ (发票数据) │ │ (生成文件) │ │ + XML 附件 │
└──────────────┘ └───────────────┘ └──────┬───────┘
│
┌───────────────┐ │
│ 数字签名 │←───────────┘
│ (PAdES) │
└───────┬───────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Email │ │ 门户网站 │ │ PEPPOL │
│ 发送 │ │ 上传 │ │ 网络 │
└──────────┘ └──────────┘ └──────────┘延伸阅读
- PDF/A-4 归档 -- PDF/A-4f 嵌入附件的基础知识
- PAdES B-LTA 签名 -- 发票数字签名标准
- 发票生成(Laravel) -- Laravel 集成发票示例