跳转到内容

发票与电子发票

Spec: EN 16931-1:2026, BT-24 Spec: Factur-X 1.08 Spec: ISO 32000-2:2020, §14.13 Evidence: Mixed evidence

一份现代发票,是同一个文件里的两份文件:一页供人阅读的版式内容,以及一段供税务系统解析的机器可读 XML 负载。本页从法规义务一路讲到产出,说明混合式发票场景——ZUGFeRD 与 Factur-X 实际上要求什么、NextPDF 如何附加并检查结构化负载,以及合规从哪里不再属于引擎工作、转而成为你的责任边界。

如今,多个司法管辖区已经强制要求企业对企业或企业对政府开具结构化发票。陷阱在于:一份混合式发票在屏幕上可能看起来完美无缺,却仍可能遭到退回。退回针对的是内嵌的 XML,而不是像素。如果结构化负载缺少规格识别码、声明了错误的配置文件,或以错误的关联方式附加,接收平台就会将它判为失败。在你这一侧,这类失败往往是静默的,而且可能要到数天后才暴露出来,期间还压着一笔被搁置的款项。

在生成文档的那一层一次性做对,远比等发票进入清算流程后再逐张发现问题要便宜得多。

  • 一份合规的混合式发票,是一个 PDF/A 载体,搭配一段 以关联文件形式内嵌的合规 XML 负载。载体的元数据必须声明该配置文件。
  • 最常决定能否被接受的单一字段,是 BT-24,也就是规格识别码EN 16931 商业规则 BR-1 要求每一张发票都必须带有它。其值是一个 URN,用以指明确切的配置文件——例如 EN 16931(COMFORT)配置文件的 urn:cen.eu:en16931:2017
  • NextPDF 的工作是对调用方提供的 XML 进行 内嵌与结构验证。它不会撰写发票内容,也 并非税务机关的验证器
  • 签发者仍须对法律与财税上的正确性负责。干净的验证结果只是该决策的一项输入,并不是证书。

NextPDF 把结构化发票视为一份 跨层级契约,而不是单一功能。核心引擎定义接口——一个内嵌器、一个配置文件描述器、一个验证器——Premium 层则提供实现。这样的分离是刻意设计的。公开接口在核心中保持稳定,因此无论背后是哪一层,调用方与测试都以相同方式处理配置文件和结果。

这个流程有四个阶段,顺序很重要,因为每个阶段都依赖前一个阶段。

  1. Author the invoice XML You (or your ERP) produce a UN/CEFACT CII or Peppol UBL payload. NextPDF never synthesizes invoice content.
  2. Produce the PDF/A carrier A PDF/A-3 or PDF/A-4f document is the human-readable face and the conformant container the standard requires.
  3. Embed the payload The embedder attaches the XML as an associated file with the correct AFRelationship, and injects the Factur-X XMP profile declaration.
  4. Validate, then ship A structural pass checks the root, the required sections, and BT-24 against the declared profile before the file leaves your system.
从头到尾的混合式电子发票情境:由你撰写 XML 并对其法律正确性负责;NextPDF 负责承载并对其进行结构检查;接收平台做出最终的接受决策。

内嵌器契约是字节进、字节出:输入一个 PDF/A 载体和一段 XML 负载,输出混合式 PDF 字节。在附加任何内容之前,XML 会先经过一道强化防护解析:它会拒绝 DOCTYPE、限制输入大小,并拒绝 XML 1.0 禁止的控制字符。这从根本上消除了 XML 外部实体(XXE)与 Billion-Laughs 攻击,因为发票 XML 经常来自你的信任边界之外。

配置文件描述器回答下游系统会问的四个问题:标准配置文件 URN、要声明的 BT-24 值、用于记录的稳定名称,以及一个与层级无关的判别值,让相等性测试能够将结果分组。描述器会如实呈现缺漏。ZUGFeRD MINIMUM 配置文件不带有 BT-24 规格识别码,描述器对它返回 null 而不是捏造一个。 这也是为什么 MINIMUM 与 BASIC WL 并非 EN 16931 合规。引擎把这种基数约束直接编码进去,而不是把它隐藏起来。

上述行为锚定在三类依据上,每一类承载的证据都不同。

标准 设定了义务。 Spec: EN 16931-1:2026, BR-1 规定每张发票都必须带有规格识别码。Factur-X 技术附录为每个配置文件固定了 URN 值,包括 EN 16931 配置文件的 urn:cen.eu:en16931:2017。载体本身属于 PDF 层面的问题: Spec: ISO 32000-2:2020, §9 指出当所有字体都已内嵌时,渲染最可预测、也最可靠——这直接相关,因为一张十年后仍须能被阅读的发票,不能依赖阅读端的字体环境。

代码 承载契约。 Evidence: Code-backed 核心的 EmbedderInterfaceProfileInterfaceValidatorInterface 是真实、与层级无关的接口。ProfileType::isEn16931Conformant() 在类型系统中编入 MINIMUM/BASIC WL 的排除规则。ValidationResult 是一个不可变的 DTO,其 isValid 是「无错误发现」与「无致命规则违反」两者的逻辑与。

行为 在 Premium 层以能力层级记载: Evidence: Standard-backed 内嵌器会把调用方提供的 ZUGFeRD 2.4/Factur-X 1.08 CII 或 Peppol UBL XML,附加到 PDF/A-4fPDF/A-3b 载体上,并使用正确的关联文件关系。验证器会检查 EN 16931 语义模型,以及 BT-24 识别码。Schematron 阶段会执行编译为 XSLT 的 CEN 规则集。其中没有任何部分会生成或修正发票内容。

下面的形状只是用来说明集成边界,并不是可直接复制粘贴的集成代码。载体构建和内嵌器来自 Premium 层,XML 则由你提供。

<?php
declare(strict_types=1);
use NextPDF\Contracts\EInvoice\ProfileType;
use NextPDF\Contracts\EInvoice\ValidatorContext;
use NextPDF\Contracts\EInvoice\ValidatorInterface;
use NextPDF\Contracts\EInvoice\EmbedderInterface;
use NextPDF\Contracts\EInvoice\EmbedderOptions;
use Psr\Log\LoggerInterface;
/**
* Validate first, embed second, never ship an invalid payload.
*
* @param non-empty-string $carrierPdf PDF/A-3 or PDF/A-4f carrier bytes
* @param non-empty-string $ciiXml Caller-authored UN/CEFACT CII XML
*
* @return non-empty-string Hybrid invoice bytes, ready to deliver
*/
function buildHybridInvoice(
ValidatorInterface $validator,
EmbedderInterface $embedder,
LoggerInterface $logger,
string $carrierPdf,
string $ciiXml,
): string {
// STRICT mode: a missing BT-24 is a hard error, not a warning.
$context = new ValidatorContext(
profile: ProfileType::EN16931,
strictMode: true,
);
$result = $validator->validate($ciiXml, $context);
if (!$result->isValid) {
foreach ($result->getErrors() as $finding) {
$logger->error('invoice.structural_finding', [
'message' => $finding->message,
]);
}
// A failed structural check is your signal to stop, not to ship.
throw new \RuntimeException('Invoice XML failed structural validation.');
}
$options = new EmbedderOptions(profile: ProfileType::EN16931);
return $embedder->embed($carrierPdf, $ciiXml, $options);
}

重点正是顺序。与一张被退回的发票和一笔被延迟的款项相比,验证成本很低,因此它会在负载被附加之前先执行。

最昂贵的误解是 「NextPDF 让我的发票变得合规。」 它并不会,而且它也明确说明了这一点。引擎会内嵌你所撰写的 XML,并对其进行结构检查。通过结构验证,意味着负载具有 EN 16931 所预期的结构,并声明了你所要求的配置文件。它 并不 意味着该发票在法律上充分、已经税务机关核准,或保证会被任何清算平台接受。国家层面的扩展与清算传输,在引擎层级属于范围之外。正如 EN 16931-1 自身所阐述的,核心模型承载着支持合规所需的信息。签发者有责任满足相关的法律规定。

第二个更隐蔽的陷阱是:支持某项标准,并不等于符合该标准。合规性是最终文件加上验证器共同构成的属性,而不是生成它的库天然具备的属性。

  • NextPDF 不会撰写或修正发票内容。 调用方负责提供有效的 XML,并且仍是发票签发者。空的发现清单,并不能让一段不合规的负载变得合规。
  • 结构化内嵌与 Schematron 验证属于 Premium 层能力。 核心定义契约;实现则需要商业软件包。请参阅下方的边界。
  • 验证器仅检查 EN 16931 语义模型,以及 ZUGFeRD/Factur-X/UBL 容器。 它并非税务机关的验证器。SDI、Chorus Pro 与 XRechnung 传输,在传输层级属于范围之外。
  • MINIMUM 与 BASIC WL 配置文件并非 EN 16931 合规,且不带有 BT-24 规格识别码——这是刻意设计,反映的是标准的基数要求,而非引擎限制。
  • Schematron 引擎需要 ext-xsl 规则集会在构建时编译。运行阶段只会执行预先编译好的 XSLT,并禁用网络与文件系统资源加载。
  • 本页所描述的是能力层级行为。它并未主张能够获得任何特定机关或平台的接受。
Structured e-invoice embedding and validation — edition availability
Edition Availability
Core

核心定义跨层级契约(EmbedderInterfaceProfileInterfaceValidatorInterface),但不随附任何结构化发票的实现。没有结构化负载的纯 PDF 载体,就是仅使用核心层得到的结果。

Pro

提供将 Factur-X 1.08 CII 内嵌到 PDF/A 载体的能力,以及 EN 16931 配置文件描述器。

Enterprise

增加 Peppol BIS 3.0 UBL 内嵌、XRechnung B2G CIUS、COMPAT/STRICT XML 验证,以及进程内的 Schematron 规则引擎。

  • 封存与 PDF/A——为什么发票载体是一个 PDF/A 文件,以及这为长期可读性提供了什么保证。
  • 整合决策指南——哪一个生态系统软件包适合开票管道(队列化生成、框架桥接或 Connect)。
  • 在生产环境中运行 NextPDF——当发票大规模流转时,如何观测验证发现与失败。
  • 混合式发票——一个同时包含人类可读页面与机器可读内嵌 XML 发票负载的单个 PDF 文件。
  • ZUGFeRD/Factur-X——同一种混合式发票做法的德语与法语名称:一段内嵌于 PDF/A 载体中的 UN/CEFACT 跨行业发票(CII)XML 负载。
  • EN 16931——定义电子发票核心元素语义数据模型的欧洲标准。
  • BT-24(规格识别码)——EN 16931 商业术语,其值是一个 URN,用以指明发票所遵循的确切配置文件。由商业规则 BR-1 强制要求。
  • 配置文件——也称为合规等级(MINIMUM、BASIC WL、BASIC、EN 16931、EXTENDED、XRECHNUNG)。决定一张发票必须带有哪些商业术语。
  • 关联文件——一个附加到 PDF 内部的文件,带有已声明的关系(AFRelationship),描述它如何与可见文件相关联;也就是承载发票 XML 的机制。
  • Schematron——一种用于表达 EN 16931 商业规则的规则语言。NextPDF 会执行编译为 XSLT 的 CEN 规则集。