发票二维码的含义
-
二维码
从发票上截取的二维码
-
扫描结果
01,10,011001800211,65651348,105.46,20180709,05903676700178588016,C62D,
-
结果解析
-
样例
-
增值税电子普通发票:
01,10,011001800211,65651348,105.46,20180709,05903676700178588016,C62D,
该字符串以逗号分隔每一个属性值,从左到右依次是:
01:第一个属性值,尚未搞清楚含义;
10:第二个属性值,代表发票种类代码,10-增值税电子普通发票,04-增值税普通发票,01-增值税专用发票;
011001800211:第三个属性值,代表发票代码;
65651348:第四个属性值,代表发票号码;
105.46:第五个属性值,代表开票金额;
20180709:第六个属性值,代表开票日期,该值为2018年7月9日;
05903676700178588016:第七个属性值,代码发票校验码,我们都知道增值税专用发票是没有发票校验码的,没有则为空字符串;
C62D:第八个属性值,为CRC算法产生的机密信息;
-
增值税专用发票:
01,01,1200154130,03630024,94339.62,20180721,,AE2D,
其中"AE2D"为随机产生的机密信息;
-
增值税普通发票:
01,04,011001800211,65651348,1105.46,20180709,05903676700178588016,C62D,
其中"C62D"为CRC算法产生的机密信息;
JAVA生成发票二维码
引入依赖
<dependency>
<groupId>QRCode</groupId>
<artifactId>qrcode</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${project.basedir}/lib/QRCode.jar</systemPath>
</dependency>
字符串二维码或二维码读取
package com.bosssoft.cloud.coreservice.pdf.help;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import javax.imageio.ImageIO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.swetake.util.Qrcode;
import jp.sourceforge.qrcode.QRCodeDecoder;
import jp.sourceforge.qrcode.data.QRCodeImage;
import jp.sourceforge.qrcode.exception.DecodingFailedException;
/**
* Title: QRCodeUtil.java
*
* @author hmc
* @date 2018年7月18日
* <p>
* Description:二维码生成工具
* </p>
*
*/
public class QRCodeUtil {
private static Logger logger = LoggerFactory.getLogger(QRCodeUtil.class);
/**
* 生成二维码,返回二维码图片字节数组
*
* @param encodeddata 要包装成二维码的字符串
* @return
* @throws IOException
*/
public static byte[] qrCodeEncode(String encodeddata) throws IOException {
Qrcode qrcode = new Qrcode();
qrcode.setQrcodeErrorCorrect('L');// 纠错级别(L 7%、M 15%、Q 25%、H 30%)和版本有关
qrcode.setQrcodeEncodeMode('B');
qrcode.setQrcodeVersion(7);// 设置Qrcode包的版本
byte[] d = encodeddata.getBytes("GBK"); // 字符集
BufferedImage bi = new BufferedImage(139, 139, BufferedImage.TYPE_INT_RGB);
// createGraphics // 创建图层
Graphics2D g = bi.createGraphics();
g.setBackground(Color.WHITE); // 设置背景颜色(白色)
g.clearRect(0, 0, 139, 139); // 矩形 X、Y、width、height
g.setColor(Color.BLACK); // 设置图像颜色(黑色)
if (d.length > 0 && d.length < 123) {
boolean[][] b = qrcode.calQrcode(d);
for (int i = 0; i < b.length; i++) {
for (int j = 0; j < b.length; j++) {
if (b[j][i]) {
g.fillRect(j * 3 + 2, i * 3 + 2, 3, 3);
}
}
}
}
g.dispose(); // 释放此图形的上下文以及它使用的所有系统资源。调用 dispose 之后,就不能再使用 Graphics 对象
bi.flush(); // 刷新此 Image 对象正在使用的所有可重构的资源
// 输出
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(bi, "png", bos);
bos.close();
return bos.toByteArray();
}
/**
* 解析二维码,返回解析内容
*
* @param imageFile
* @return
*/
public static String qrCodeDecode(byte[] imageByte) {
String decodedData = null;
QRCodeDecoder decoder = new QRCodeDecoder();
BufferedImage image = null;
try {
ByteArrayInputStream bais = new ByteArrayInputStream(imageByte);
image = ImageIO.read(bais);
bais.close();
} catch (IOException e) {
logger.error("二维码解析Error: " + e.getMessage());
}
try {
decodedData = new String(decoder.decode(new MyQRCodeImage(image)), "GBK");
} catch (DecodingFailedException dfe) {
logger.error("Error: " + dfe.getMessage());
} catch (UnsupportedEncodingException e) {
logger.error("Error: " + e.getMessage());
}
return decodedData;
}
static class MyQRCodeImage implements QRCodeImage {
BufferedImage image;
public MyQRCodeImage(BufferedImage image) {
this.image = image;
}
public int getWidth() {
return image.getWidth();
}
public int getHeight() {
return image.getHeight();
}
public int getPixel(int x, int y) {
return image.getRGB(x, y);
}
}
}
二维码测试
@Test
public void testQR() {
String filePath = "D:\\practice\\targetQR.png";
// 二维码内容
// String encodeddata = "01,01,1200154130,03630024,94339.62,20180721,,C62D,";
String encodeddata = "01,04,011001800211,65651348,1105.46,20180709,05903676700178588016,C62D,";
// String encodeddata = "01,04,1100162320,80050866,189.62,20161221,50138840680157228141,A6F0,";
byte[] qrCodeEncode = null;
try {
qrCodeEncode = QRCodeUtil.qrCodeEncode(encodeddata);
writeFile(qrCodeEncode, filePath);
} catch (IOException e) {
e.printStackTrace();
}
// 解码
String reText = QRCodeUtil.qrCodeDecode(qrCodeEncode);
System.out.println(reText);
}
private void writeFile(byte[] bytes, String target) {
File file = new File(target);
try {
if (!file.exists())
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.close();
} catch (Exception e) {
System.out.println("写入文件异常");
e.printStackTrace();
}
}
后四位随机数生成方法
/**
* 电子发票生成二维码图片的规则
*
* @param
* @return
*/
public static String generateEInvoiceQuickMarkStr(String invoiceCode,String invoiceNumber,String totalAmount,String invoiceDate,String checkCode) {
StringBuilder builder = new StringBuilder("01,").append(InvoiceType.TAX_E_INVOICE.getType()).append(",")
.append(invoiceCode).append(",").append(invoiceNumber).append(",")
.append(totalAmount).append(",").append(invoiceDate)
.append(",").append(checkCode).append(",");
return new StringBuilder(builder)
.append(Integer.toHexString(CRC16_MINIM(builder.toString().getBytes())).toUpperCase()).append(",").toString();
}
/**
* CRC16_X25:多项式x15+x2+1(0x8005),初始值0x0000,低位在前,高位在后,结果与0x0000异或
*
* @param buffer
* @return
*/
public static int CRC16_MINIM(byte[] buffer) {
int wCRCin = 0x0000; // initial value 65535
int wCPoly = 0x8005; // 1000 0000 0000 0101
for (byte b : buffer) {
for (int i = 0; i < 8; i++) {
boolean bit = ((b >> (7 - i) & 1) == 1);
boolean c15 = ((wCRCin >> 15 & 1) == 1);
wCRCin <<= 1;
if (c15 ^ bit)
wCRCin ^= wCPoly;
}
}
wCRCin &= 0xffff;
return wCRCin ^= 0x0000;
}
生成样例