如果你在 2026 年给德国 B2B 客户发送电子发票,文件要么符合 ZUGFeRD,要么在接收环节被退回。法国的 Factur-X 也是同样逻辑。格式本质上是一个 PDF/A-3 外壳,里面附带 EN 16931 CII XML;从零生成并不简单,而验证它需要参考引擎。
实际使用中,这个参考引擎就是 Mustang(mustangproject.org)。它是一个开源 Java 项目,会从 PDF/A-3 中提取嵌入 XML,并按 EN 16931 Schematron 验证。Mustang 对 ZUGFeRD 和 Factur-X 的支持最深,也是许多独立验证器实际运行的工具。
本文讲 Mustang 会抓出哪些失败模式,以及更快的运行方式。
Mustang 实际检查什么
当你把 Factur-X 或 ZUGFeRD PDF 交给 Mustang 时,它大致会做这些事:
- 提取嵌入文件。PDF/A-3 把附件放在
/EmbeddedFilesname tree 里。Mustang 会寻找标准文件名(Factur-X 的factur-x.xml,ZUGFeRD 2.x 的zugferd-invoice.xml),然后取出字节。 - 检查 AFRelationship。附件必须按 Factur-X / ZUGFeRD baseline 声明为
AFRelationship="Alternative"。其他值(Source、Data、Supplement)都会失败。 - 检查 XMP namespace 和版本。Factur-X 1.0 使用
urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#。ZUGFeRD 2.x 使用urn:zugferd:pdfa:CrossIndustryDocument:invoice:2p0#。namespace 或版本字符串错误都会失败。 - 按 Cross-Industry Invoice (CII) 解析 XML。XML 必须 well-formed,并且以正确的 CII root element(
rsm:CrossIndustryInvoice)开头。 - 运行 EN 16931 Schematron。这是验证主体:约 200 条业务规则,覆盖字段语义、必填代码、合计计算、VAT 逻辑、交易方标识等。
Pass = 这张发票可被欧盟范围内任何符合 EN 16931 的 AP 系统接受。Fail = 客户的 AP automation 会在接收时拒收,AR 团队拿到一个人工异常。
最常见的五类失败
这些问题经常出现在 validator 的 Mustang 栏里,尤其是团队第一次测试电子发票时。
1. AFRelationship 错误
ERROR: Embedded file factur-x.xml uses AFRelationship="Source",
expected "Alternative".
PDF 规范允许附件有多种 relationship type。Factur-X / ZUGFeRD 明确要求 Alternative,表示附件 XML 是可见 PDF 内容的另一种表示。如果你的 PDF generator 配成 Data(很多库的默认值),Mustang 会立刻失败。可见 PDF 仍然能正常显示,但结构化 payload 对 AP 系统来说无效。
2. XMP namespace 错误或缺失
ERROR: XMP metadata missing fx:DocumentType or fx:DocumentFileName under
namespace urn:factur-x:pdfa:CrossIndustryDocument:invoice:1p0#.
PDF 的 XMP packet 必须声明这是哪个 Factur-X profile(例如 MINIMUM、BASIC、EN 16931、EXTENDED),以及应该寻找哪个文件名。手写 PDF/A-3 wrapper 时很容易漏掉;gPdf 的 /api/v1/e-invoice/render endpoint 会自动输出这些字段。
3. CII XML 格式正确,但 EN 16931 Schematron 失败
ERROR: BR-CO-25 — In an invoice (BR-01) the
ram:SpecifiedTradePaymentTerms/ram:DueDateDateTime is required when
ram:DocumentTypeCode is 380.
这是现实中最大的一类失败。XML 语法没问题,但业务规则没过。EN 16931 Schematron 规则有稳定 ID(BR-01、BR-CO-25 等),可以在 EN 16931 规范里查。常见项:
- BR-01:invoice 必须有唯一发票号。
- BR-04:invoice 必须有开具日期。
- BR-05:invoice 必须有 invoice type code。
- BR-CO-25:document type 为 “Commercial invoice” 时必须有 payment terms。
- BR-Z-01:VAT category code 必须是
S、Z、E、AE、K、G、O、L、M之一。
修正源数据,重新构建,再验证。
4. PDF/A 外壳本身不通过
INFO: CII XML extracted and validates against EN 16931.
ERROR: PDF/A-3b conformance check failed: missing Output Intent.
这类情况是 Mustang 的 XML 检查通过,但底层 PDF/A-3 wrapper 失败。常见原因是 XML 写对了,但输出的是普通 PDF,而不是 PDF/A-3;附件存在,但归档外壳规则没有满足。gpdf.com/validator/ 会并行跑 veraPDF,所以 PDF/A-3 失败会出现在 veraPDF 栏,而 Mustang 仍显示 XML pass。
5. Encoding / declaration 不匹配
ERROR: XML declares <?xml version="1.0" encoding="UTF-8"?> but the
embedded byte stream is UTF-8 with BOM. Mustang strict mode rejects BOM.
这在使用会输出 UTF-8 BOM 的 XML 工具时很常见。XML 声明是 UTF-8,但嵌入的 raw bytes 带 BOM,Mustang strict mode 会拒绝。修复方式是在嵌入前移除 BOM(gPdf 的 e-invoice endpoint 会做标准化)。
不安装 Java 也能运行 Mustang
为了偶发检查,安装 Java + Mustang CLI 可以接受。但如果要持续验证——每次生成 invoice、每次 CI 断言 e-invoice compliance——这就是不必要的摩擦。
gpdf.com/validator/ 在浏览器里运行 Mustang:
- 把 Factur-X / ZUGFeRD PDF 拖到上传区域。
- Validator 提取嵌入 XML,并运行 Mustang 的 Schematron engine(编译为 JavaScript / WebAssembly,在 Cloudflare Worker 中执行)。
- Mustang report 会和 veraPDF 的 PDF/A-3 report 并排返回,因为两层都必须通过。
- 下载 JSON report,作为 QA 证据。
不需要登录,没有 quota。和你通过 Maven 安装的 Mustang 是同一类检查,只是作为免费公共服务提供。
TL;DR
Mustang 最常抓到 5 类问题;大多数可以归结为“文件由一个没有完整输出 Factur-X / ZUGFeRD PDF/A-3 的工具生成”。gPdf 的 E-invoice API 一次调用输出合规文件。validator 则用 Mustang + veraPDF 并行验证结果。
Mustang 抓到的大部分 bug 是 wrapper 或 AFRelationship 问题,而不只是 XML 业务语义。正确生成文件已经解决了大半;validator 是证明你做对了的收据。