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>
            統一編號:12345678<br>
            台北市信義區信義路 100 號
        </td>
        <td width="50%">
            <strong>買方</strong><br>
            客戶公司名稱<br>
            統一編號:87654321<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;">NT$ 50,000</td>
        <td style="text-align: right;">NT$ 2,500</td>
        <td style="text-align: right;">NT$ 52,500</td>
    </tr>
    <tr style="background-color: #F8F9FA;">
        <td>技術支援(年約)</td>
        <td style="text-align: center;">1</td>
        <td style="text-align: right;">NT$ 12,000</td>
        <td style="text-align: right;">NT$ 600</td>
        <td style="text-align: right;">NT$ 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;">NT$ 3,100</td>
        <td style="text-align: right;">NT$ 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>TW</ram:CountryID>
                </ram:PostalTradeAddress>
                <ram:SpecifiedTaxRegistration>
                    <ram:ID schemeID="VA">12345678</ram:ID>
                </ram:SpecifiedTaxRegistration>
            </ram:SellerTradeParty>
            <ram:BuyerTradeParty>
                <ram:Name>客戶公司名稱</ram:Name>
                <ram:PostalTradeAddress>
                    <ram:CountryID>TW</ram:CountryID>
                </ram:PostalTradeAddress>
            </ram:BuyerTradeParty>
        </ram:ApplicableHeaderTradeAgreement>

        <ram:ApplicableHeaderTradeSettlement>
            <ram:InvoiceCurrencyCode>TWD</ram:InvoiceCurrencyCode>
            <ram:SpecifiedTradeSettlementHeaderMonetarySummation>
                <ram:LineTotalAmount>62000.00</ram:LineTotalAmount>
                <ram:TaxBasisTotalAmount>62000.00</ram:TaxBasisTotalAmount>
                <ram:TaxTotalAmount currencyID="TWD">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('Taipei, Taiwan')
    ->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 授權釋出。