如何在PHP中实现Base64到TLV格式解码 适配税务发票QR码合规要求
TLV格式发票二维码解码问题及解决方案
问题原因
你当前操作的错误是直接将base64解码后的二进制TLV结构体作为纯文本输出。TLV编码内容中包含非文本的控制字节(Tag、Length字段都是1字节的数值,不属于可打印字符),且XML哈希、ECDSA签名/公钥等字段本身就是二进制内容,不符合UTF8编码规则,直接输出必然出现乱码,需要按照TLV规则逐段解析内容才能得到可读结果。
TLV规则说明
Tag:上文提及的标签值,占1字节存储
Length:字段值经UTF8编码后得到的字节数组的长度,占1字节存储
Value:字段值经UTF8编码得到的字节数组,二进制字段直接取对应长度字节即可
Tag含义映射
- Tag 1:销售方名称
- Tag 2:增值税登记号
- Tag 3:发票时间戳
- Tag 4:发票总金额
- Tag 5:税额总计
- Tag 6:XML发票哈希值
- Tag 7:ECDSA签名
- Tag 8:ECDSA公钥
- Tag 9:加密戳公钥的ECDSA签名
正确实现代码(PHP)
<?php $base64 = "AQkxMjM0NTY3ODkCCjEyLzEyLzIwMjADBDEwMDADAzE1MPaIn2Z2jg6VqWvWV6IrZZNzLF7xvZrWXW5xRV5yFY2xFu0ycXOiyqV0k7Wsh6b1IcE2Tfzap1AQAQVsktmsv1FFQ1MxIAAAAGKblFMh9nFRSn8tvftXqo9zRSz2VEAPITSZ3W7UDHKhUx+7yXGijLtJSZGXMOc+jpKwARzDl68GmmRd75NWdOs="; // 解码base64得到二进制TLV内容 $tlvRaw = base64_decode($base64); $offset = 0; $tlvLength = strlen($tlvRaw); // Tag映射表 $tagMap = [ 1 => '销售方名称', 2 => '增值税登记号', 3 => '发票时间戳', 4 => '发票总金额', 5 => '税额总计', 6 => 'XML发票哈希值', 7 => 'ECDSA签名', 8 => 'ECDSA公钥', 9 => '加密戳公钥的ECDSA签名' ]; $result = []; while ($offset < $tlvLength) { // 读取1字节Tag $tag = ord($tlvRaw[$offset]); $offset += 1; // 读取1字节Length $length = ord($tlvRaw[$offset]); $offset += 1; // 读取Length长度的Value $value = substr($tlvRaw, $offset, $length); $offset += $length; // 文本类型Tag直接保留UTF8字符串,二进制类型转十六进制方便查看 if (in_array($tag, [1,2,3,4,5])) { $parsedValue = $value; } else { $parsedValue = bin2hex($value); } $result[] = [ 'tag' => $tag, 'name' => $tagMap[$tag] ?? '未知Tag', 'value' => $parsedValue ]; } // 输出结构化解析结果 print_r($result); ?>
输出示例
运行代码后可得到结构化的解析结果,核心内容如下:
Array ( [0] => Array ( [tag] => 1 [name] => 销售方名称 [value] => 123456789 ) [1] => Array ( [tag] => 2 [name] => 增值税登记号 [value] => 12/12/2020 ) [2] => Array ( [tag] => 3 [name] => 发票时间戳 [value] => 1000 ) [3] => Array ( [tag] => 4 [name] => 发票总金额 [value] => 150 ) ... 剩余二进制字段会以十六进制格式输出 )
内容的提问来源于stack exchange,提问作者Taha Khan
相关产品推荐
相关产品推荐

