最近为了在我们的应用里生成发票信息的二维码,根据国税总局的二维码文档(见文后),唯一麻烦的地方就是需要计算一个CRC16的值。中间主要碰到了两个问题,一是文档中没有描述中文编码方式,经过试验,我们发现编码方式应为GBK。二是CRC值的生成,网上找了一些代码,生成的CRC16总是和总局发票助手生成的CRC16不一样。最后还是自己根据以下算法写了一个:
1)将字节流的前2字节放入一个长度为16的寄存器(int);
2)如果寄存器的首位为1,将寄存器左移1位(寄存器的最低位从下一个字节获得),再与生成多项式的简记式(CRC16的生成多项式简记式为0x8005)异或;
否则仅将寄存器左移1位(寄存器的最低位从下一个字节获得);
3)重复第2步,直到字节流全部移入寄存器;
4)最后补充2个0字节,重复第二步
5)寄存器中的值则为CRC校验码。
示例代码如下
private int crc16 = 0x8005;
private int a = 0x0000;
public void testCRC() throws UnsupportedEncodingException {
String input = "杭州XX科技有限公司</>9133XXXXXX92XXX9X</>杭州市滨江区XXXxxx号xx大厦x层xxx室</>xxx股份有限公司xxxx</>";
byte[] inputs = input.getBytes("GBK");
for (int i = 0; i < inputs.length; i ++) {
div(inputs[i]);
}
byte r = 0;
div(r);
div(r);
System.out.println(Integer.toHexString(a));
}
private void div(byte input) {
int temp;
int data = input;
for (int i = 0; i < 8; i ++) {
temp = a & 0x8000;
a = a << 1;
a = a & 0x0000ffff;
int numIn = data & 0x80;
numIn = numIn >> 7;
a = a ^ numIn;
if (temp == 0x8000) {
a = a ^ crc16;
}
data = data << 1;
a = a & 0x0000ffff;
}
}
附国税总局便捷开票二维码应用规范:
便捷开票二维码应用规范
为便捷纳税人开具增值税发票,提高发票开具效率和准确性,参照国家相关标准,采用QR码码制,制定本应用规范。
一、编码要求
(一)二维码编码格式采用信息容量大、可靠性高、保密防伪性强的QR码码制。
(二)本规范中QR码符号规格采用版本12(小于等于419字符)、18(大于419字符,小于等于816字符)和25(大于816字符,小于等于1451字符)规格,并根据内容长度自动匹配。
(三)本规范中QR码纠错信息能力等级采用M级别,可纠错15%的数据码字。
(四)本规范中的QR码编码字符集采用字母、数字、中文汉字方式进行编码。
二、编码内容和格式
便捷开票二维码编码内容如下:
索引 | 名称 | 字符长度 | 说明 |
---|---|---|---|
1 | 起始符 | 1 | 特殊字符“$”表示开始 |
2 | 版本号 | 2 | 固定值01 |
3 | 分隔符 | 3 | 用英文半角“</>”组成分隔符,起始符与版本号之间、版本号与名称、CRC与结束符之间不使用分隔符 |
4 | 名称 | 100 | 变长字段,最大长度为100字符(50个汉字) |
5 | 纳税人识别号 | 20 | 变长字段,15至20字符。 |
6 | 地址电话 | 100 | 变长字段,最大长度为100字符(50个汉字) |
7 | 开户行及账号 | 100 | 变长字段,最大长度为100字符(50个汉字) |
8 | CRC及CRC16算法 | 4 | CRC标识符为4字符。从第四位开始到CRC标识符之前所有内容,包括“</>”分隔符采用CRC-16算法。具体算法:P(X)=X16+X15+X2+1高位在前,低位在后 |
9 | 结束符 | 1 | 使用特殊字符“$”表示结束符 |
便捷开票二维码内容格式如下: 起始符+版本号+base64(名称</>纳税人识别号</>地址电话</>开户行及账号</>CRC)+结束符
三、打印和显示要求
打印和显示二维码时,需遵循二维码大小、缩放比例的格式编排。
(一)二维码图案大小 二维码图案大小的高度、宽度不小于2.0CM×2.0CM。
(二)二维码周边留白区域 二维码周围的空白区域宽度至少要大于10个码元宽度。