You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用PHP实现ZATCA合规API时遭遇CERTIFICATE_SIGNATURE_QRCODE_INVALID错误

使用PHP实现ZATCA合规API时遭遇CERTIFICATE_SIGNATURE_QRCODE_INVALID错误

看起来你在集成ZATCA合规API时碰到了头疼的签名不匹配问题,这个CERTIFICATE_SIGNATURE_QRCODE_INVALID错误我之前帮不少开发者排查过,我来给你梳理几个最可能的原因和解决方向:

1. QR码签名与XML签名使用的证书不一致

ZATCA的核心要求是:生成QR码时用于签名的私钥,必须和签署XML发票的私钥是同一套。很多人踩坑是因为测试环境和生产环境的证书搞混了,或者生成QR码时误用了其他证书的私钥。

  • 先核对:你用来签署XML发票的证书,和生成QR payload签名的私钥,是不是ZATCA给你的同一套合规证书(沙箱环境用沙箱证书,正式环境用生产证书)?
  • 确认签名算法:必须使用SHA256withRSA算法对QR payload签名,签名结果要做Base64编码后再放入QR码中。

2. QR Payload构造或编码错误

ZATCA的QR payload有严格的格式要求,任何字段的格式错误都可能导致签名不匹配:

  • 检查Payload字段顺序:必须是「卖家VAT号 | 发票日期(UTC时间格式,如2024-05-20T14:30:00Z) | 总金额 | 税额 | 发票哈希值」,字段之间用|分隔,不能有多余的空格或换行。
  • 避免Payload被意外修改:构造好Payload后,要确保在签名前没有被转义或修改(比如自动转义特殊字符会导致签名后的结果和API预期不一致)。

3. XML发票签名后被篡改

如果你的XML在签名完成后,又被添加了多余的空格、换行,或者修改了任何节点内容,都会导致签名失效:

  • 签名XML后绝对不能再修改任何内容,包括XML的缩进、换行符。建议签名后直接保存为文件,上传API时直接读取这个签名后的文件,不要做任何字符串处理。
  • 检查XML签名的覆盖范围:确保签名节点<ds:Signature>正确覆盖了整个Invoice的核心内容,没有遗漏必要的UBL节点。

给你一个PHP签名QR Payload的参考片段

// 构造符合ZATCA规范的QR Payload
$qrFields = [
    '3101234567890', // 卖家VAT号
    date('Y-m-d\TH:i:s\Z'), // UTC格式的发票日期
    '1000.00', // 发票总金额(含税额)
    '150.00', // 税额
    'a1b2c3d4e5f6...' // 发票内容的SHA-256哈希值
];
$qrPayload = implode('|', $qrFields);

// 加载ZATCA颁发的私钥(确保是PEM格式,无多余字符)
$privateKeyPath = __DIR__ . '/zatca_private_key.pem';
$privateKey = openssl_pkey_get_private(file_get_contents($privateKeyPath));

// 生成SHA256withRSA签名并Base64编码
$signature = '';
openssl_sign($qrPayload, $signature, $privateKey, OPENSSL_ALGO_SHA256);
$qrSignature = base64_encode($signature);

// 接下来将$qrSignature嵌入QR码,并确保XML签名使用同一私钥

如果按照上面的方向排查后还是有问题,建议把你生成QR码和签署XML的核心代码片段贴出来,我可以帮你更精准地定位问题。

备注:内容来源于stack exchange,提问作者Hassan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 14:28:08