Enterprise 版本
Compliance — 深度参考
Compliance 模块会将一份完成的 PDF 路由到一个外部校验 sidecar,并返回一份归一化后的结果。ComplianceGateway 从 ComplianceProfile 解析出负责的 sidecar,强制执行失败即关闭的可用性策略,并把每个工具的判定包装进 ExternalValidationResult。桥接器随附于 veraPDF(PDF/A、PDF/UA、PDF 2.0 Arlington)、EU DSS(PAdES 级别)、组合式 Mustang/KoSIT sidecar(ZUGFeRD、Factur-X、EN 16931)以及一个独立的 KoSIT 守护进程。该模块还提供 AiReadyCertifier 就绪度盖戳,以及一个用于运行官方 KoSIT XRechnung 测试套件的运行器。
可用性与授权
标题为“可用性与授权”的章节此能力随附于 NextPDF Enterprise(nextpdf/enterprise),并通过一个 Enterprise 层级的授权信封激活。缺少该权益的部署不会加载此能力的类。比较各版本并获取授权。
Compliance/Evidence 接口面受 enterprise.compliance.evidence 能力授权。缺失或过期的权益会拒绝该功能;它不会静默降级行为。
| 层级 | Compliance 接口面 |
|---|---|
| Core | 进程内的字节流与语法检查;不委派给外部 sidecar。 |
| Pro | 进程内的 EN 16931 / Factur-X / ZUGFeRD 校验;无外部 sidecar。 |
| Enterprise | 外部校验器网关(本模块),带有归一化结果与失败即关闭策略。 |
Pro 的进程内电子发票校验器与 Enterprise 的外部 ZUGFeRD sidecar 是两个不同的接口面。外部校验器网关仅随 nextpdf/enterprise 包发行。
公共 API 接口面
标题为“公共 API 接口面”的章节composer require nextpdf/enterprise:^3| 符号 | 参数 | 默认行为 | 返回 | 抛出或失败于 | 说明 |
|---|---|---|---|---|---|
ComplianceGateway::__construct | list<ExternalValidator> $validators, LoggerInterface $logger, bool $optional = false | 按工具名索引各校验器 | — | — | 可选模式将可用性检查降级为仅告警 |
ComplianceGateway::validate | string $pdfContent, ComplianceProfile $profile, array $options = [] | 通过 ComplianceProfile::toolName() 解析校验器,检查可用性,委派调用 | ?ExternalValidationResult | ComplianceSidecarUnavailableException;InvalidArgumentException(该工具未注册校验器) | 仅在可选模式且 sidecar 宕机时返回 null |
ComplianceGateway::validateAllProfiles | string $pdfContent, string $toolName | 校验映射到该工具的每个 profile | list<ExternalValidationResult> | 与 validate() 相同 | 跳过 null(可选模式)结果 |
ComplianceGateway::healthCheck | — | 探测每个已注册 sidecar 的健康端点 | array<string, bool> | — | 报告可达性;不校验任何文档 |
ComplianceGateway::buildComplianceMatrix(静态) | list<ExternalValidationResult> $results, string $commitSha | 将结果归约为一个带版本化 schema 的矩阵 | array<string, mixed> | — | schema 版本 1.0;记录工具输出,不作任何断言 |
ComplianceProfile(枚举) | 15 个以字符串为底的 case | 将每个 profile 映射到一个标准标签与一个工具 | — | — | standardReference(): string、toolName(): string |
ExternalValidator(接口) | — | PSR-18 之上的 sidecar 桥接契约 | — | 传输失败时 validate() 抛出 ComplianceSidecarUnavailableException | getToolName()、isAvailable()、validate() |
VeraPdfValidator::validate | 接口签名 | 向 veraPDF REST sidecar 发起 multipart POST;解析 JSON 报告 | ExternalValidationResult | ComplianceSidecarUnavailableException;InvalidArgumentException(不支持的 profile) | PDF/A、PDF/UA、Arlington;仅解析 JSON,绝不解析 XML |
DssValidator::validate | 接口签名 | 向 EU DSS REST sidecar 发起 Base64 JSON POST | ExternalValidationResult | ComplianceSidecarUnavailableException;InvalidArgumentException(不支持的 profile) | PAdES B-B 到 B-LTA;构造函数拒绝低于一秒的超时 |
ZugferdExternalValidator::validate | 接口签名 | 向组合式 Mustang/KoSIT sidecar 发起 multipart POST | ExternalValidationResult | ComplianceSidecarUnavailableException(断路器开启时亦然);InvalidArgumentException(不支持的 profile) | ZUGFeRD 2.4、Factur-X 1.08、EN 16931;可选注入的断路器 |
KoSitValidator::validate | 接口签名 | 向独立 KoSIT 守护进程发起原始 XML POST | ExternalValidationResult | ComplianceSidecarUnavailableException;InvalidArgumentException(不支持的 profile) | 仅 EN 16931;以失败即关闭的方式解析 Schematron SVRL 报告 |
ExternalValidationResult | 只读值对象 | 归一化的工具判定 | — | — | passes()、fails()、nonConformanceCount()、toComplianceMatrix() |
NonConformance | 只读值对象 | 单条发现,含规则 id、条款、严重度、位置 | — | — | toArray() |
ComplianceSidecarUnavailableException | string $toolName, string $endpoint, int $code = 0, ?Throwable $previous = null | 失败即关闭的 sidecar 不可用信号 | — | — | 公开只读的 toolName 与 endpoint |
AiReadyCertifier::certify | string $pdfBytes | 评估三项就绪度准则;盖上 XMP 溯源信息 | array{0: AiReadyCertification, 1: string} | InvalidArgumentException(盖戳要求经典交叉引用表) | 当级别为 not_certified 时,第二个元素等于输入 |
AiReadyCertification | 只读值对象 | 就绪度评估,含级别、准则数量、问题、源哈希 | — | — | 内部就绪度标签,并非标准认证 |
XRechnungTestSuiteRunner::__construct | string $suitePath, ExternalValidator $validator, bool $useCuratedNegativeFallback = true | 解析已解压的套件目录 | — | InvalidArgumentException(目录不存在) | 针对官方 KoSIT XRechnung 测试套件 |
XRechnungTestSuiteRunner::run | bool $stopOnFirstFailure = false | 通过桥接器校验每个套件实例 | XRechnungTestSuiteResult | XRechnungTestSuiteException(校验器不可用;无 XML 文件) | 还有 isAvailable()、getSuitePath()、discoverTestFiles() |
XRechnungTestSuiteResult | 只读值对象 | 聚合的套件结果 | — | — | allPassed()、totalCount()、getFailures()、getErrors()、toSummary() |
XRechnungTestCaseResult | 只读值对象 | 单个用例的结果 | — | — | passed()、hasError()、getFilename() |
XRechnungTestSuiteException | 静态构造函数 | 套件运行时失败信号 | self | — | validatorUnavailable()、noTestFilesFound(string $suitePath) |
namespace NextPDF\Enterprise\Compliance;
final class ComplianceGateway{ /** @param list<ExternalValidator> $validators */ public function __construct( array $validators, private readonly LoggerInterface $logger, private readonly bool $optional = false, );
/** @param array<string, mixed> $options */ public function validate( string $pdfContent, ComplianceProfile $profile, array $options = [], ): ?ExternalValidationResult;
/** @return list<ExternalValidationResult> */ public function validateAllProfiles(string $pdfContent, string $toolName): array;
/** @return array<string, bool> */ public function healthCheck(): array;
/** * @param list<ExternalValidationResult> $results * @return array<string, mixed> */ public static function buildComplianceMatrix(array $results, string $commitSha): array;}interface ExternalValidator{ public function getToolName(): string;
public function isAvailable(): bool;
/** @param array<string, mixed> $options */ public function validate( string $pdfContent, ComplianceProfile $profile, array $options = [], ): ExternalValidationResult;}
enum ComplianceProfile: string{ case PdfA1b = 'pdfa-1b'; // PdfA2b, PdfA3b, PdfA4, PdfA4f, PdfUa1, PdfUa2, Pdf20Arlington, // PadesBasic, PadesTimestamp, PadesLongTerm, PadesArchive, // Zugferd24, FacturX108, En16931
public function standardReference(): string;
public function toolName(): string;}final class AiReadyCertifier{ /** @return array{0: AiReadyCertification, 1: string} Tuple of [certification, stamped PDF bytes] */ public function certify(string $pdfBytes): array;}行为契约
标题为“行为契约”的章节ComplianceGateway::validate() 会解析其 getToolName() 与 ComplianceProfile::toolName() 匹配的已注册 ExternalValidator,检查 isAvailable(),委派调用,并返回一个归一化的 ExternalValidationResult。可从外部观察到的规则如下:
- 失败即关闭的默认行为。 当解析出的 sidecar 不可用且未开启可选模式时,该调用会抛出
ComplianceSidecarUnavailableException。文档未被检查;它绝不会被当作已通过。 - 可选模式。 以
optional: true构造网关(运营方从NEXTPDF_COMPLIANCE_OPTIONAL环境变量接入)会把不可用的 sidecar 降级为一条日志警告并返回null。调用方必须将null视为“未检查”。可选模式仅覆盖预检的可用性探测;校验调用本身期间发生的传输失败在两种模式下都会抛出ComplianceSidecarUnavailableException。 - 未知 profile。 没有已注册校验器的 profile 会抛出
InvalidArgumentException;它绝不会静默通过。 - 通过语义。
ExternalValidationResult::passes()要求conformant为真且零个不符合项。每份结果都携带 profile、工具名与版本、断言数量、各项发现、被校验字节的 SHA-256、一个 UTC 时间戳,以及调用耗时。 - 矩阵是一条记录,而非一次断言。
buildComplianceMatrix()是一个静态归约器,产出一个带版本化 schema 的结构,其中含有工具版本以及用于可追溯性的 commit SHA。它记录工具输出;它本身不作任何断言。 - 数据流。 完整的 PDF 字节流会通过一个 PSR-18 客户端传输到所配置的 sidecar。每次校验都会通过 PSR-3 记录 profile、工具、通过/失败、断言数量以及耗时。
由 ComplianceProfile::standardReference() 与 ::toolName() 返回的 profile-到-工具路由:
| Profile case | 标准参考 | 工具 |
|---|---|---|
pdfa-1b, pdfa-2b, pdfa-3b, pdfa-4, pdfa-4f | ISO 19005-1/-2/-3/-4(Level B;4f 为 Level F) | veraPDF |
pdfua-1, pdfua-2 | ISO 14289-1:2014, ISO 14289-2:2024 | veraPDF |
pdf20-arlington | ISO 32000-2:2020(Arlington 模型) | veraPDF |
pades-b-b, pades-b-t, pades-b-lt, pades-b-lta | ETSI EN 319 142-1 B-B 到 B-LTA | EU DSS |
zugferd-2.4, factur-x-1.08, en-16931 | ZUGFeRD 2.4 / Factur-X 1.08 / EN 16931-1:2017 | Mustang/KoSIT |
AiReadyCertifier::certify() 评估三项准则:结构性签名的存在、LTV 健康度,以及未加密。三项准则全部通过得出级别 certified;通过一项或两项得出 partial;零项得出 not_certified。在 certified 或 partial 时,它会追加一次增量更新,携带一个 XMP 溯源流与一个 Catalog 覆盖;原始字节绝不会被改动。“certified”级别是一个 NextPDF 内部的就绪度标签,并非标准认证。
VeraPdfValidator 仅解析 JSON sidecar 响应(无 XML;在构造上即对 XXE 免疫)。KoSitValidator 解析守护进程的 XML SVRL 报告,其中 DOCTYPE 声明被拒绝、网络访问被禁用,并将无法解析的报告视为该调用的一次失败。
边界情形与失败模式
标题为“边界情形与失败模式”的章节- sidecar 超时或传输错误会从桥接器表现为
ComplianceSidecarUnavailableException;此时适用失败即关闭的默认行为。 - sidecar 返回非 200 响应会产生一份失败结果,带有工具特定的发现(例如
VERAPDF-HTTP-ERROR);它绝不是一次符合性通过。 - 格式不正确的 sidecar JSON 或 XML 主体是该调用的一次校验失败,而非一次符合性通过。
- 无签名的 EU DSS 结果以
DSS-NO-SIGNATURES失败。除TOTAL_PASSED以外的指示以DSS-SIG-INVALID失败。低于期望基线的签名级别以DSS-LEVEL-MISMATCH失败。 DssValidator会在每个请求上通过X-NextPDF-Timeout-Seconds头发布其单请求超时预算;集成方的 PSR-18 客户端必须遵循它,以使一个停滞的 sidecar 无法无限期阻塞调用线程。ZugferdExternalValidator可选地将 sidecar 调用经由一个注入的断路器路由;开启的断路器映射为ComplianceSidecarUnavailableException(快速失败,仍是失败即关闭)。默认是一个无操作的断路器。KoSitValidator::isAvailable()接受守护进程健康探测返回的 HTTP 200 与 405;守护进程在健康时以 405 应答 GET。- 当原始文档缺少经典交叉引用表(例如交叉引用流)时,
AiReadyCertifier盖戳会以InvalidArgumentException失败即关闭。 - 当校验器不可用或套件不含任何 XML 文件时,
XRechnungTestSuiteRunner::run()拒绝运行;在启用useCuratedNegativeFallback时,当套件未随附任何无效实例时,它会替换为一个经过策展的负样本语料。
FIPS 模式行为
标题为“FIPS 模式行为”的章节本模块不执行任何签名,也不进行任何密钥保管。FIPS 模式的算法策略由 Security 与 Signature 模块管辖。签名符合性委派给 EU DSS,由其自行作出判定。
符合性
标题为“符合性”的章节网关将符合性判定委派给外部工具;这一设计反映了各标准自身的界线——符合性是依照各项要求判定的,而非由生成端断言。
| 行为 | 参考 |
|---|---|
| 符合性处理器义务;符合性依照标准判定 | ISO 19005-4:2020 §5.2 |
| PDF/A-4 文件要求 vs. 生成端自我断言 | ISO 19005-4:2020 §6.6.4 |
| PDF/UA-2 符合性是文件的一项属性 | ISO 14289-2:2024 §6 |
| PAdES 基线签名级别 | ETSI EN 319 142-1 §5.4.3 |
判定由外部工具产出。NextPDF 不持有任何认证,也不授予任何认证;对某个 profile 的支持并不等于符合该 profile。校验结果是供参考的技术性结构检查记录,并非法律意见;请咨询你的合规团队,以判断监管层面是否充分。
开发说明
标题为“开发说明”的章节- 运营方负责托管与运行各 sidecar、对其进行版本固定、限制其网络可达范围、校验其 TLS,并掌控启用可选模式的环境。sidecar 端点是一道信任边界;针对文档、结果与日志的驻留地与留存控制由运营方负责。
buildComplianceMatrix()的输出是为 CI 可追溯性而设计的:固定 commit SHA,并将矩阵与构建产物一并归档。- XRechnung 运行器期望官方测试套件已解压到一个本地目录;其构造函数消息会指明公共下载来源。
- 内部机制细节保留在源代码仓库的内部文档中,不在本手册范围内。
发布边界
标题为“发布边界”的章节本页仅记录可从外部观察到的行为与受支持的公共 API 接口面。内部命名空间路径、辅助类、机制表、runbook 文件名以及工单前缀均不在范围内。
另请参阅
标题为“另请参阅”的章节- Compliance 能力概览
- Validation — 深度参考
- Evidence — 深度参考
- Pro Compliance —— 进程内电子发票(不同的接口面)
- Core Conformance