使用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
相关产品推荐
相关产品推荐

