之前一起吃饭听说了发票助手这个东西,可以生成发票抬头的二维码,扫码就可以开票了。

官方也有个小程序的 【税务发票助手】,微信中搜这个名字就可以了。

我准备在自己的小程序中也尝试一下,本来觉得只要拼接一下生成二维码就好了,结果发现不少坑。

具体的文档参照github上的 便捷开票二维码应用规范.pdf

拼接倒是没什么,最坑的是CRC-16校验码那里。通过网上找到的CRC(循环冗余校验)在线计算计算的和官方程序生成的总是不一样。

在网上找到了好几个版本,大部分生成的和上面的在线计算结果一样。最终找到了一个Java版的国税总局发票助手二维码生成的CRC计算,跑起来之后发现终于验证码一样了。

于是改成了C#版,还加上了QRCode的生成方法(需要安装QRCoder包)。

C#版代码

  1. public class ReceiptHelper
  2. {
  3. private const int crc16 = 0x8005;
  4. private const string ReceiptQRCodePath = "Uploads\\Receipt\\";
  5. /// <summary>
  6. /// 生成发票抬头QRCode图片
  7. /// </summary>
  8. /// <param name="id">数据ID</param>
  9. /// <param name="companyName">公司名称</param>
  10. /// <param name="taxNo">纳税人识别号</param>
  11. /// <param name="address">地址</param>
  12. /// <param name="phone">电话</param>
  13. /// <param name="bankName">开户行</param>
  14. /// <param name="account">账户</param>
  15. /// <returns>QRCode图片名</returns>
  16. public static string GenerateQRCodeImage(Guid id, string companyName, string taxNo, string address, string phone, string bankName, string account)
  17. {
  18. string strQRCode = GenerateQRCode(companyName, taxNo, address, phone, bankName, account);
  19. string path = System.Web.Hosting.HostingEnvironment.MapPath("~/") + ReceiptQRCodePath + id.ToString() + ".png";
  20. QRCodeGenerator qrGenerator = new QRCodeGenerator();
  21. QRCodeData qrCodeData = qrGenerator.CreateQrCode(strQRCode, QRCodeGenerator.ECCLevel.Q);
  22. QRCode qrCode = new QRCode(qrCodeData);
  23. Bitmap qrCodeImage = qrCode.GetGraphic(15);
  24. qrCodeImage.Save(path, System.Drawing.Imaging.ImageFormat.Png);
  25. return path;
  26. }
  27. /// <summary>
  28. /// 生成发票抬头QRCode内容
  29. /// </summary>
  30. /// <param name="companyName">公司名称</param>
  31. /// <param name="taxNo">纳税人识别号</param>
  32. /// <param name="address">地址</param>
  33. /// <param name="phone">电话</param>
  34. /// <param name="bankName">开户行</param>
  35. /// <param name="account">账户</param>
  36. /// <returns>QRCode图片名</returns>
  37. public static string GenerateQRCode(string companyName, string taxNo, string address, string phone, string bankName, string account)
  38. {
  39. string crcInput = $"{companyName}</>{taxNo}</>{address}{phone}</>{bankName}{account}</>"; // CRC校验内容
  40. string crcNo = CalcCRC16(crcInput); // 计算CRC16校验码
  41. // 起始符 + 版本号 + base64(名称</>纳税人识别号</>地址电话</>开户行及账号</>CRC) + 结束符
  42. StringBuilder sbQRCode = new StringBuilder();
  43. sbQRCode.Append("$");
  44. sbQRCode.Append("01");
  45. sbQRCode.Append(Convert.ToBase64String(Encoding.UTF8.GetBytes(crcInput + crcNo)));
  46. sbQRCode.Append(" $");
  47. return sbQRCode.ToString();
  48. }
  49. /// <summary>
  50. /// 计算CRC16校验码
  51. /// </summary>
  52. /// <param name="input"></param>
  53. /// <returns></returns>
  54. public static string CalcCRC16(string input)
  55. {
  56. sbyte[] inputs = GetSBytesForEncoding("UTF-8", input);
  57. int a = 0;
  58. for (int i = 0; i < inputs.Length; i++)
  59. {
  60. a = div(inputs[i], a);
  61. }
  62. sbyte r = 0;
  63. a = div(r, a);
  64. a = div(r, a);
  65. return a.ToString("X4");
  66. }
  67. private static int div(sbyte input, int a)
  68. {
  69. int temp;
  70. int data = input;
  71. for (int i = 0; i < 8; i++)
  72. {
  73. temp = a & 0x8000;
  74. a = a << 1;
  75. a = a & 0x0000ffff;
  76. int numIn = data & 0x80;
  77. numIn = numIn >> 7;
  78. a = a ^ numIn;
  79. if (temp == 0x8000)
  80. {
  81. a = a ^ crc16;
  82. }
  83. data = data << 1;
  84. a = a & 0x0000ffff;
  85. }
  86. return a;
  87. }
  88. private static sbyte[] GetSBytesForEncoding(string encoding, string s)
  89. {
  90. return GetSBytesForEncoding(Encoding.GetEncoding(encoding), s);
  91. }
  92. private static sbyte[] GetSBytesForEncoding(Encoding encoding, string s)
  93. {
  94. sbyte[] sbytes = new sbyte[encoding.GetByteCount(s)];
  95. encoding.GetBytes(s, 0, s.Length, (byte[])(object)sbytes, 0);
  96. return sbytes;
  97. }
  98. }

QRCoder的安装命令

  1. Install-Package QRCoder

也可以通过NuGet工具搜索QRCoder安装。

【C#】发票助手二维码生成的更多相关文章

  1. [开源]C#二维码生成解析工具,可添加自定义Logo

    二维码又称 QR Code,QR 全称 Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的 Bar Code 条形码能存更多的信息,也能表示更多的数据类型:比如:字 ...

  2. 聊聊 Web 项目二维码生成的最佳姿势

    在设计和实现的过程之后,你永远不知道部署上去的程序会已什么样的姿势运行. 本篇借一次生成二维码逻辑的不同实现,阐述 Web 项目中二维码生成的正确姿势. 文中如有批量,欢迎各位看客老爷拍砖.试运行前5 ...

  3. .NET 二维码生成(ThoughtWorks.QRCode)

    引用ThoughtWorks.QRCode.dll (源代码里有) 1.简单二维码生成及解码代码: //生成二维码方法一 private void CreateCode_Simple(string n ...

  4. iOS开发 二维码生成

    基于libqrencode的二维码生成 + (void)drawQRCode:(QRcode *)code context:(CGContextRef)ctx size:(CGFloat)size { ...

  5. PHP二维码生成的方法(google APi,PHP类库,libqrencode等)

    原文地址: http://blog.csdn.net/liuxinmingcode/article/details/7910975 ================================== ...

  6. Android 二维码 生成和识别(附Demo源码)

    今天讲一下目前移动领域很常用的技术——二维码.现在大街小巷.各大网站都有二维码的踪迹,不管是IOS. Android.WP都有相关支持的软件.之前我就想了解二维码是如何工作,最近因为工作需要使用相关技 ...

  7. Chrome浏览器二维码生成插件

      猛击就可以使用啦->>>猛击使用   源码如下: 源码打包   源码: jquery-2.1.3.min.js jquery.qrcode.min.js https://gith ...

  8. Android 二维码 生成和识别(转)

    原博客地址 :http://www.cnblogs.com/weixing/archive/2013/08/28/3287120.html 还有几个写的也可以参考一下:http://www.itnos ...

  9. wex5 实战 二维码生成,扫描,蓝牙打印

    给人设计了一个小模块,要求是,把一个单号生成二维码,实现扫描查询单号具体信息,并能通过蓝牙把二维码打印出来.功能实现并不复杂,今天一口气把它搞定.来看效果. 一   效果演示: 二.二维码生成 1 在 ...

随机推荐

  1. 在Laravel中使用swoole来取代nginx作为http服务器

    1.是什么限制Laravel框架的速度? Laravel框架启动的时候需要加载很多文件,再加上其出了名的生态环境好,所以在开发过程中我们就会发现有非常多的已经造好的轮子,这也就使得Laravel的一次 ...

  2. AntiXSS的作用

    XSS跨站脚本攻击        是指用户输入HTML编码对网站进行跨站攻击.            通过使用FCKeditor.FreeTextBox.Rich TextBox.Cute Edito ...

  3. Linux常用操作命令及快捷键

    Linux操作命令: 大体分为两类: 1.内部命令       help 命令(查看内部命令)  2.外部命令    命令 --help(查看外部命令) type 命令:用来查看该命令是内部命令还是外 ...

  4. 截屏 iOS

    #pragma mark - 截屏 + (UIImage *_Nonnull)screenShotWithController:(UIViewController *_Nonnull)controll ...

  5. iOS 判断数组array中是否包含元素a,取出a在array中的下标+数组方法详解

    目前找到来4个解决办法,第三个尤为简单方便 NSArray * arr = @["]; //是否包含 "]) { NSInteger index = [arr indexOfObj ...

  6. 童话故事 --- CPU的贴身侍卫ITCM和ICache

    "叮铃铃- 叮铃铃-" "谁呀?"黛丝博士打开了家门,"哇,高飞,你怎么来了?" 高飞狗:"好久不见,想来看看你,还买了你最喜欢吃 ...

  7. Dos与Linux的断行字符

    Symptom Dos和Linux下面的断行字符是不一样的,至于哪里不一样的呢,先举个例子看一看 Cause 我们也可以使用cat -A filename分别查看Dos和Linux下创建的文件,会发现 ...

  8. sql经典试题

    1.一道SQL语句面试题,关于group by表内容:2005-05-09 胜2005-05-09 胜2005-05-09 负2005-05-09 负2005-05-10 胜2005-05-10 负2 ...

  9. calling c++ from golang with swig--windows dll (四)

    calling c++ from golang with swig--windows dll 四 前面讲述了windows环境下golang如何通过swig调用C++ dll.由于编译c++代码使用了 ...

  10. selenium 封装

    周末无聊 在家封装一个pyselenium.可能这些封装大家都会使用,但是我还是根据我自己的习惯去选择性的去封装一些在我工作中用的,这样的话,我就不用去看selenium的api的,我可以根据我自己的 ...