Skip to content

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) 格式。

一份合规的电子发票包含两个部分:

  1. 可视化 PDF -- 符合 PDF/A-3 或 PDF/A-4f 标准的人类可读发票
  2. 嵌入式 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  │
        │  发送    │ │  上传     │ │  网络     │
        └──────────┘ └──────────┘ └──────────┘

延伸阅读

以 LGPL-3.0-or-later 许可证发布。