当前位置:   article > 正文

小程序自建平台开发票保存到微信卡包(java篇)_weixin-java-miniapp 发票

weixin-java-miniapp 发票

目录

1 获取 Access token

2获取自身的开票平台识别码

3 获取授权页ticket

3.5 获取链接前需要先 设置商户联系方式

4 获取授权页链接

5 小程序打开授权页

6 收取授权完成事件推送

7 查询授权完成状态

8 创建发票卡券模板

 9 上传PDF

10 将电子发票卡券插入用户卡包


1 获取 Access token

这里的appid 和 secret  是公众号的 不要写小程序的哦

https请求方式: GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

参数说明

参数是否必须说明
grant_type获取access_token填写client_credential
appid第三方用户唯一凭证
secret第三方用户唯一凭证密钥,即appsecret

这个获取代码我就不写了哈 应该都知道

2获取自身的开票平台识别码

这个地方代码我也不写  上面获取到的 Access token传入进来就行了   自己用postman调用一下 拿到invoice_url中的s_pappid存起来就行了

请求URL:https://api.weixin.qq.com/card/invoice/seturl?access_token={access_token}

请求方法:POST

请求参数

请求参数使用JSON格式,传入空值{}

返回结果

返回结果为JSON格式,字段如下:

参数类型是否必填描述
errcodestring错误码
errmsgstring错误信息
invoice_urlstring该开票平台专用的授权链接。开票平台须将 invoice_url 内的 s_pappid 给到服务的商户,商户在请求授权链接时会向微信传入该参数,标识所使用的开票平台是哪家

这个获取到账号请截取下来 只需要获取一次 保存到配置文件即可

3 获取授权页ticket

ticket 的有效期,一般为 7200 秒

这个我也不写了 还是把上面获取的ACCESS_TOKEN传入 就能拿到ticket 记得写成一个方法 

请求方式

请求URL:https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=wx_card

请求方法:GET

返回结果

返回结果使用JSON格式,字段如下:

参数类型是否必填描述
errcodeInt错误码,含义见错误码
errmsgString错误信息,含义见错误码
ticketString临时票据,用于在获取授权链接时作为参数传入
expires_inIntticket 的有效期,一般为 7200 秒

示例代码

  1. 返回:
  2. {
  3. "errcode": 0,
  4. "errmsg":"ok",
  5. "ticket":"m7RQzjA_ljjEkt-JCoklRM5zrzYr-6PI09QydZmNXXz-opTqMv53aFj1ykRt_AOtvqidqZZsLhCDgwGC6nBDiA",
  6. "expires_in": 7200
  7. }

3.5 获取链接前需要先 设置商户联系方式

请求方式 请求URL:https://api.weixin.qq.com/card/invoice/setbizattr?action=set_contact&access_token={access_token}

请求方法:POST

请求参数使用JSON格式,字段如下:

参数类型是否必填描述
contactObject联系方式信息

contact是Object,里面包括以下字段:

参数类型是否必填描述
time_outint开票超时时间
phonestring联系电话
  1. public static JSONObject setContactInfo(String accessToken, int timeout, String phone) {
  2. try {
  3. String url = "https://api.weixin.qq.com/card/invoice/setbizattr?action=set_contact&access_token=" + accessToken;
  4. URL obj = new URL(url);
  5. HttpURLConnection conn = (HttpURLConnection) obj.openConnection();
  6. // 设置请求方法为POST
  7. conn.setRequestMethod("POST");
  8. // 设置请求头
  9. conn.setRequestProperty("Content-Type", "application/json");
  10. // 构造请求参数
  11. String requestBodyJson = "{\"contact\": {\"time_out\": " + timeout + ", \"phone\": \"" + phone + "\"}}";
  12. // 发送POST请求
  13. conn.setDoOutput(true);
  14. DataOutputStream wr = new DataOutputStream(conn.getOutputStream());
  15. wr.writeBytes(requestBodyJson);
  16. wr.flush();
  17. wr.close();
  18. // 读取响应
  19. BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  20. String inputLine;
  21. StringBuilder response = new StringBuilder();
  22. while ((inputLine = in.readLine()) != null) {
  23. response.append(inputLine);
  24. }
  25. in.close();
  26. // 解析JSON响应
  27. JSONObject result = new JSONObject(response.toString());
  28. conn.disconnect();
  29. return result;
  30. } catch (Exception e) {
  31. e.printStackTrace();
  32. JSONObject result = new JSONObject();
  33. return result.put("errcode", 1);
  34. }
  35. }

4 获取授权页链接

获取授权页  让用户填写 抬头

根据自己需求选 类型

请求方式

请求URL:https://api.weixin.qq.com/card/invoice/getauthurl?access_token={access_token}

请求方法:POST

请求参数

请求参数使用JSON格式,字段如下:

参数类型是否必填描述
s_pappidString开票平台在微信的标识号,商户需要找开票平台提供
order_idString订单id,在商户内单笔开票请求的唯一识别号,
moneyInt订单金额,以分为单位
timestampInt时间戳
sourceString开票来源,app:app开票,web:微信h5开票,wxa:小程序开发票,wap:普通网页开票
redirect_urlString授权成功后跳转页面。本字段只有在source为H5的时候需要填写,引导用户在微信中进行下一步流程。app开票因为从外部app拉起微信授权页,授权完成后自动回到原来的app,故无需填写。
ticketString从上一环节中获取
typeInt授权类型,0:开票授权,1:填写字段开票授权,2:领票授权

  1. /**
  2. * 获取授权url
  3. */
  4. public static JSONObject getAuthURLnew(String source,int money,String order_id,String ticket,String accessToken,String sPappid,int timestamp) throws Exception{
  5. if(money == 0 || order_id == null || ticket == null || sPappid==null){
  6. JSONObject errParams = new JSONObject();
  7. errParams.put("errcode",1);
  8. return errParams;
  9. }
  10. int code = sendPostRequest(accessToken);
  11. if(code == 1){
  12. JSONObject errParams = new JSONObject();
  13. errParams.put("errcode",1);
  14. return errParams;
  15. }
  16. String auth_url = "";
  17. // 准备请求参数
  18. JSONObject requestParams = new JSONObject();
  19. requestParams.put("s_pappid", sPappid);
  20. requestParams.put("order_id", order_id);
  21. requestParams.put("money", money); // 假设订单金额为1元,单位为分
  22. requestParams.put("timestamp", timestamp);
  23. requestParams.put("source", source); // 根据你的需求选择开票来源
  24. // requestParams.put("redirect_url", "YOUR_REDIRECT_URL"); // 如果需要的话
  25. requestParams.put("ticket", ticket);
  26. requestParams.put("type", 1); // 根据你的需求选择授权类型
  27. // 发送POST请求到微信API
  28. String apiUrl = "https://api.weixin.qq.com/card/invoice/getauthurl?access_token=" + accessToken;
  29. URL url = new URL(apiUrl);
  30. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  31. connection.setRequestMethod("POST");
  32. connection.setRequestProperty("Content-Type", "application/json; utf-8");
  33. connection.setRequestProperty("Accept", "application/json");
  34. connection.setDoOutput(true);
  35. try (OutputStream os = connection.getOutputStream()) {
  36. byte[] input = requestParams.toString().getBytes("utf-8");
  37. os.write(input, 0, input.length);
  38. }
  39. try (BufferedReader br = new BufferedReader(
  40. new InputStreamReader(connection.getInputStream(), "utf-8"))) {
  41. StringBuilder response = new StringBuilder();
  42. String responseLine;
  43. while ((responseLine = br.readLine()) != null) {
  44. response.append(responseLine.trim());
  45. }
  46. // 将JSON字符串转换为JSONObject
  47. JSONObject data = new JSONObject(response.toString());
  48. // 从JSONObject中获取auth_url的值
  49. auth_url = data.getString("auth_url");
  50. connection.disconnect();
  51. return data;
  52. }
  53. }

5 小程序打开授权页

小程序返回的数据是这样的

我们只需要 把返回的auth_url 和appid 换上去就行了代码下面

这是在微信开发者工具 你们逻辑哪个地方该打开这个授权页  就写在哪哦

  1. wx.navigateToMiniProgram({
  2. appId: '{appid}',
  3. path: '{auth_url}',
  4. success(res) {
  5. console.log('navigateToMiniProgram success:', res)
  6. },
  7. fail(error){
  8. console.log('navigateToMiniProgram fail:', error)
  9. },
  10. complete(res){
  11. console.log('navigateToMiniProgram complete:', res)
  12. }
  13. })

6 收取授权完成事件推送

在用户授权同意发票存入自己微信账户后,商户可以收到授权完成的状态推送。收到推送后,可以将order_id连同开票信息一并发送给开票平台,以便开票平台在开票成功后将电子发票插入用户卡包。

该事件将发送至开发者填写的URL(登录公众平台进入【开发者中心设置】,如果是open账号,也得用公众号账号来接,参考下图)。

这个你要去那里设置回调地址 接收那边的回调带给你个 订单id 然后可以通过这个id去查用户收钱填写的信息

我是这样处理的 回调地址是你们上面设置的 
           if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
                // 事件类型
                String eventType = eventMessage.getEvent();    
                // 收取授权完成事件推送
                if (eventType.equals(MessageUtil.USER_AUTHORIZE_INVOICE)) {
                    //授权成功的ID
                    Invoice invoice = new Invoice();
                    String succOrderId = eventMessage.getSuccOrderId();    
                    if(succOrderId!=null || succOrderId != ""){
                        invoice.getAuthdata(succOrderId);
                    }
                }

7 查询授权完成状态

上面进入的你回调之后

请求方式

请求URL:https://api.weixin.qq.com/card/invoice/getauthdata?access_token={access_token}

请求方法:POST

请求参数

请求参数使用JSON格式,字段如下:

参数类型是否必填描述
order_idstring发票order_id
s_pappidString开票平台在微信的标识,由开票平台告知商户
  1. // 查询授权完成状态
  2. @SuppressWarnings("unused")
  3. public static JSONObject callGetAuthDataAPI(String accessToken, String orderId, String sPappid) throws Exception {
  4. String API_URL = "https://api.weixin.qq.com/card/invoice/getauthdata?access_token=%s";
  5. String apiUrl = String.format(API_URL, accessToken);
  6. URL url = new URL(apiUrl);
  7. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  8. connection.setRequestMethod("POST");
  9. connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
  10. connection.setRequestProperty("Accept", "application/json");
  11. connection.setDoOutput(true);
  12. JSONObject requestBody = new JSONObject();
  13. requestBody.put("order_id", orderId);
  14. requestBody.put("s_pappid", sPappid);
  15. try (DataOutputStream os = new DataOutputStream(connection.getOutputStream())) {
  16. byte[] input = requestBody.toString().getBytes("utf-8");
  17. os.write(input, 0, input.length);
  18. }
  19. try (BufferedReader br = new BufferedReader(
  20. new InputStreamReader(connection.getInputStream(), "utf-8"))) {
  21. StringBuilder response = new StringBuilder();
  22. String responseLine;
  23. while ((responseLine = br.readLine()) != null) {
  24. response.append(responseLine.trim());
  25. }
  26. JSONObject result = new JSONObject(response.toString());
  27. return result;
  28. }
  29. }

 查询出来的的数据 自己记得插入数据库对应开票订单的信息

8 创建发票卡券模板

只需创建一次 把卡劵id card_id 存起来哈

  1. 返回:
  2. {
  3. "errcode": 0,
  4. "errmsg": "ok",
  5. "card_id": "pjZ8Yt9WoOePThU0NfUKz5-tBEWU"
  6. }

请求方式

请求URL:https://api.weixin.qq.com/card/invoice/platform/createcard?access_token={access_token}

请求方法:POST

请求参数

请求参数使用JSON格式,字段如下:

参数类型是否必填描述
invoice_infoObject发票模板对象

invoice_info为Object,里面包括以下字段:

参数类型是否必填描述
base_infoobject发票卡券模板基础信息
payeestring收款方(开票方)全称,显示在发票详情内。故建议一个收款方对应一个发票卡券模板
typestring发票类型

base_info为Object,里面包括以下字段:

参数类型是否必填描述
logo_urlstring发票商家 LOGO ,请参考 新增永久素材
titlestring收款方(显示在列表),上限为 9 个汉字,建议填入商户简称
custom_url_namestring开票平台自定义入口名称,与 custom_url 字段共同使用,长度限制在 5 个汉字内
custom_urlstring开票平台自定义入口跳转外链的地址链接 , 发票外跳的链接会带有发票参数,用于标识是从哪张发票跳出的链接
custom_url_sub_titlestring显示在入口右侧的 tips ,长度限制在 6 个汉字内
promotion_url_namestring营销场景的自定义入口
promotion_urlstring入口跳转外链的地址链接,发票外跳的链接会带有发票参数,用于标识是从那张发票跳出的链接
promotion_url_sub_titlestring显示在入口右侧的 tips ,长度限制在 6 个汉字内
  1. //创建发票卡劵模板
  2. public static JSONObject createInvoiceCardTemplate(String accessToken, JSONObject invoiceInfo) throws Exception {
  3. String url = "https://api.weixin.qq.com/card/invoice/platform/createcard?access_token="+accessToken;
  4. URL obj = new URL(url);
  5. HttpURLConnection con = (HttpURLConnection) obj.openConnection();
  6. // 设置请求方法
  7. con.setRequestMethod("POST");
  8. // 设置请求头
  9. con.setRequestProperty("Content-Type", "application/json; utf-8");
  10. con.setRequestProperty("Accept", "application/json");
  11. con.setDoOutput(true);
  12. // 构建请求体
  13. String jsonInputString = invoiceInfo.toString();
  14. try (OutputStream os = con.getOutputStream()) {
  15. byte[] input = jsonInputString.getBytes("utf-8");
  16. os.write(input, 0, input.length);
  17. }
  18. try (BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"))) {
  19. StringBuilder response = new StringBuilder();
  20. String responseLine;
  21. while ((responseLine = br.readLine()) != null) {
  22. response.append(responseLine.trim());
  23. }
  24. JSONObject result = new JSONObject(response.toString());
  25. int errcode = (int) result.get("errcode");
  26. if (errcode == 0) {
  27. return result;
  28. } else {
  29. JSONObject result1 = new JSONObject();
  30. return result1.put("errcode",1);
  31. }
  32. }
  33. }

 9 上传PDF

这里把财务开好的PDF发票文件  我这里写了一个pdf文件识别 拿到了对应的抬头信息

记得把返回的s_media_id在数据库对应订单存起来

  1. 返回:
  2. {
  3. "errcode":0,
  4. "errmsg":"ok",
  5. "s_media_id":"3015806758683707"
  6. }

请求方式

请求URL:https://api.weixin.qq.com/card/invoice/platform/setpdf?access_token={access_token}

请求方法:POST

请求参数

数据格式使用multipart/form-data,参数清单如下:

参数是否必填描述
pdf

form-data中媒体文件标识,有filename、filelength、content-type等信息

  1. //上传发票
  2. public static JSONObject uploadPDF(File file,String access_token,String invoiceName){
  3. JSONObject objectData = extractInvoiceNumber(file);
  4. int code = (int)objectData.get("errcode");
  5. JSONObject errParams = new JSONObject();
  6. if(code==1){
  7. errParams.put("errcode",1);
  8. errParams.put("errmsg","PDF文件解析失败");
  9. return errParams;
  10. }
  11. if(!invoiceName.equals(objectData.get("companyName"))){
  12. errParams.put("errcode",1);
  13. errParams.put("errmsg","发票抬头与学生需开不一致");
  14. return errParams;
  15. }
  16. String urlString = "https://api.weixin.qq.com/card/invoice/platform/setpdf?access_token="+access_token;
  17. URL url;
  18. try {
  19. url = new URL(urlString);
  20. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  21. conn.setRequestMethod("POST");
  22. conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
  23. conn.setDoOutput(true);
  24. conn.setDoInput(true);
  25. conn.setUseCaches(false);
  26. // 获取输出流
  27. OutputStream os = (OutputStream) conn.getOutputStream();
  28. // 写入multipart/form-data的边界
  29. String boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW";
  30. os.write(("--" + boundary + "\r\n").getBytes());
  31. // 写入文件表单字段
  32. String formFieldName = "pdf";
  33. String fileName = file.getName();
  34. String contentType = Files.probeContentType(file.toPath());
  35. String contentDisposition = "Content-Disposition: form-data; name=\"" + formFieldName + "\"; filename=\"" + fileName + "\"\r\n";
  36. os.write(contentDisposition.getBytes());
  37. os.write(("Content-Type: " + contentType + "\r\n\r\n").getBytes());
  38. // 写入文件内容
  39. Files.copy(file.toPath(), os);
  40. // 写入multipart/form-data的结束边界
  41. os.write(("\r\n--" + boundary + "--\r\n").getBytes());
  42. // 刷新输出流
  43. os.flush();
  44. os.close();
  45. // 获取响应
  46. int responseCode = conn.getResponseCode();
  47. if (responseCode == HttpURLConnection.HTTP_OK) {
  48. BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
  49. StringBuilder response = new StringBuilder();
  50. String line;
  51. while ((line = br.readLine()) != null) {
  52. response.append(line);
  53. }
  54. br.close();
  55. // 处理响应数据
  56. // 将JSON字符串转换为JSONObject
  57. JSONObject data = new JSONObject(response.toString());
  58. // 从JSONObject中获取auth_url的值
  59. int errcode = (int)data.get("errcode");
  60. if(errcode!=0){
  61. errParams.put("errcode",1);
  62. errParams.put("errmsg","PDF文件上传失败");
  63. return errParams;
  64. }
  65. String sMediaId = data.getString("s_media_id");
  66. if(sMediaId==null || sMediaId==""){
  67. errParams.put("errcode",1);
  68. errParams.put("errmsg","PDF文件上传失败");
  69. return errParams;
  70. }
  71. errParams.put("errcode",0);
  72. errParams.put("sMediaId",sMediaId);
  73. return errParams;
  74. } else {
  75. // 处理错误
  76. errParams.put("errcode",1);
  77. errParams.put("errmsg","PDF文件上传失败");
  78. return errParams;
  79. }
  80. } catch (Exception e) {
  81. // TODO Auto-generated catch block
  82. errParams.put("errcode",1);
  83. return errParams;
  84. }
  85. }

PDF发票文件识别信息工具方法   使用的正则表达式 

  1. public static JSONObject extractInvoiceNumber(File file) {
  2. JSONObject ObjectData = new JSONObject();
  3. try {
  4. // File file = new File("D:\\fp1.pdf");
  5. PDDocument document = PDDocument.load(file);
  6. PDFTextStripper pdfStripper = new PDFTextStripper();
  7. pdfStripper.setSortByPosition(true);
  8. String text = pdfStripper.getText(document);
  9. System.out.println(text);
  10. document.close();
  11. int fee = 0; //发票的金额,以分为单位
  12. String billingNo = ""; //发票号码
  13. String billingCode = ""; //发票代码
  14. int feeWithoutTax = 0;//不含税金额,以分为单位
  15. int tax = 0; //税额,以分为单位
  16. int billingTime = 0; //发票的开票时间,为10位时间戳(utc+8)
  17. String checkCode = ""; //校验码,发票pdf右上角,开票日期下的校验码;数电发票发票校验码为空
  18. String companyName = ""; //发票抬头
  19. Pattern pattern = Pattern.compile("发票号码:\\s*(\\d+)");
  20. Matcher matcher = pattern.matcher(text);
  21. List<String> companyList = new ArrayList<>();
  22. companyList.add("发票号码:\\s*(\\d+)");
  23. companyList.add("发票号码:(\\d+)");
  24. // 查找匹配的发票号码
  25. for (String patternName : companyList) {
  26. // 使用正则表达式进行匹配
  27. pattern = Pattern.compile(patternName);
  28. matcher = pattern.matcher(text);
  29. if (matcher.find()){
  30. billingNo = matcher.group(1);
  31. System.out.println("发票号码:"+billingNo);
  32. break;
  33. }
  34. }
  35. if (billingNo.equals("")){
  36. System.out.println("未找到发票抬头");
  37. }
  38. companyList.clear();
  39. companyList.add("购 名称:(.*?) ");
  40. companyList.add("名 称: (.*?)\\s");
  41. companyList.add(" 名 称: (.*?)\\s");
  42. companyList.add("买 名称:(.*?) ");
  43. // 查找匹配的发票抬头
  44. for (String patternName : companyList) {
  45. // 使用正则表达式进行匹配
  46. pattern = Pattern.compile(patternName);
  47. matcher = pattern.matcher(text);
  48. if (matcher.find()){
  49. companyName = matcher.group(1);
  50. System.out.println("提取的发票抬头为:" + companyName);
  51. break;
  52. }
  53. }
  54. if (companyName.equals("")){
  55. System.out.println("未找到发票抬头");
  56. }
  57. companyList.clear();
  58. companyList.add("(小写)¥(\\d+\\.\\d{2})");
  59. companyList.add(" \\(小写\\) ¥\\s*(\\d+\\.\\d{2})");
  60. companyList.add("(小写)¥\\s*(\\d+(\\.\\d+)?)");
  61. // 查找匹配的发票金额
  62. for (String patternName : companyList) {
  63. // 使用正则表达式进行匹配
  64. pattern = Pattern.compile(patternName);
  65. matcher = pattern.matcher(text);
  66. if (matcher.find()){
  67. String invoiceNumber = matcher.group(1);
  68. Double number = Double.parseDouble(invoiceNumber);
  69. number = number * 100;
  70. fee = (int) number.doubleValue();
  71. System.out.println("提取的发票金额为:" + fee);
  72. break;
  73. }
  74. }
  75. if (fee==0){
  76. System.out.println("未找到发票金额");
  77. }
  78. companyList.clear();
  79. companyList.add("合 计 ¥(\\d+\\.\\d{2})\\s*¥(\\d+\\.\\d{2})");
  80. companyList.add("合 计 ¥(\\d+\\.\\d{2})\\s*¥(\\d+\\.\\d{2})");
  81. companyList.add("合 计 ¥(\\d+\\.\\d{2})\\s*¥(\\d+\\.\\d{2})");
  82. // 查找匹配的不含税金额
  83. for (String patternName : companyList) {
  84. // 使用正则表达式进行匹配
  85. pattern = Pattern.compile(patternName);
  86. matcher = pattern.matcher(text);
  87. if (matcher.find()){
  88. String invoiceNumber = matcher.group(1);
  89. Double number = Double.parseDouble(invoiceNumber);
  90. number = number * 100;
  91. feeWithoutTax = (int) number.doubleValue();
  92. System.out.println("提取的不含税金额为:" + feeWithoutTax);
  93. break;
  94. }
  95. }
  96. if (feeWithoutTax==0){
  97. System.out.println("未找到不含税金额");
  98. }
  99. companyList.clear();
  100. companyList.add("合\\s*计\\s*¥\\d+(\\.\\d+)?\\s*¥(\\d+(\\.\\d+)?)");
  101. // 查找匹配的税金额
  102. for (String patternName : companyList) {
  103. // 使用正则表达式进行匹配
  104. pattern = Pattern.compile(patternName);
  105. matcher = pattern.matcher(text);
  106. if (matcher.find()){
  107. String invoiceNumber = matcher.group(2);
  108. Double number = Double.parseDouble(invoiceNumber);
  109. number = number * 100;
  110. tax = (int) number.doubleValue();
  111. System.out.println("提取的税金额为:" + tax);
  112. break;
  113. }
  114. }
  115. if (tax==0){
  116. System.out.println("未找到税金额");
  117. }
  118. if (text.contains("发票代码")) {
  119. companyList.clear();
  120. companyList.add("发票代码:\\s*(\\d+)");
  121. companyList.add("发票代码:(\\d+)");
  122. // 查找匹配的发票代码
  123. for (String patternName : companyList) {
  124. // 使用正则表达式进行匹配
  125. pattern = Pattern.compile(patternName);
  126. matcher = pattern.matcher(text);
  127. if (matcher.find()) {
  128. billingCode = matcher.group(1);
  129. System.out.println("提取的发票代码为:" + billingCode);
  130. break;
  131. }
  132. }
  133. if (billingCode.equals("")) {
  134. System.out.println("未找到发票代码");
  135. }
  136. }else{
  137. System.out.println("未找到发票代码");
  138. }
  139. companyList.clear();
  140. companyList.add("校 验 码:\\s*(\\d+(\\s+\\d+)*)");
  141. companyList.add("校 验 码:\\s*(\\d+(?:\\s+\\d+)*)");
  142. // 查找匹配的校 验 码
  143. for (String patternName : companyList) {
  144. // 使用正则表达式进行匹配
  145. pattern = Pattern.compile(patternName);
  146. matcher = pattern.matcher(text);
  147. if (matcher.find()) {
  148. checkCode = matcher.group(1);
  149. System.out.println("提取的校 验 码为:" + checkCode);
  150. break;
  151. }
  152. }
  153. if (checkCode.equals("")) {
  154. System.out.println("未找到校 验 码");
  155. }
  156. if (text.contains("开票日期")) {
  157. companyList.clear();
  158. companyList.add("开票日期:\\s*(\\d{4})\\s*年\\s*(\\d{1,2})\\s*月\\s*(\\d{1,2})\\s*日");
  159. companyList.add("开票日期:(\\d{4})年(\\d{1,2})月(\\d{1,2})日");
  160. // 查找匹配的校 验 码
  161. for (String patternName : companyList) {
  162. // 使用正则表达式进行匹配
  163. pattern = Pattern.compile(patternName);
  164. matcher = pattern.matcher(text);
  165. if (matcher.find()) {
  166. // 提取年份、月份和日期
  167. String year = matcher.group(1);
  168. String month = matcher.group(2);
  169. String day = matcher.group(3);
  170. // 组合成完整的日期字符串(如果需要特定格式,可以在这里进行格式化)
  171. String invoiceDate = year + "年" + month + "月" + day+"日";
  172. billingTime = format(invoiceDate);
  173. System.out.println("提取的开票日期为:" + invoiceDate);
  174. break;
  175. }
  176. }
  177. if (billingTime==0) {
  178. System.out.println("未找到开票日期");
  179. }
  180. }else{
  181. System.out.println("未找到开票日期");
  182. }
  183. ObjectData.put("errcode",0);
  184. ObjectData.put("fee",fee);
  185. ObjectData.put("billingNo",billingNo);
  186. ObjectData.put("billingCode",billingCode);
  187. ObjectData.put("feeWithoutTax",feeWithoutTax);
  188. ObjectData.put("tax",tax);
  189. ObjectData.put("billingTime",billingTime);
  190. ObjectData.put("checkCode",checkCode);
  191. ObjectData.put("companyName",companyName);
  192. return ObjectData;
  193. } catch (Exception e) {
  194. ObjectData.put("errcode",1);
  195. return ObjectData;
  196. }
  197. }
  198. //时间单位转换
  199. public static int format(String dateString) {
  200. SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
  201. sdf.setTimeZone(TimeZone.getTimeZone("GMT+8")); // 设置时区为GMT+8
  202. try {
  203. Date date = sdf.parse(dateString);
  204. return (int) (date.getTime() / 1000); // 转换为以秒为单位的时间戳
  205. } catch (Exception e) {
  206. return 1;
  207. }
  208. }

10 将电子发票卡券插入用户卡包

请求方式

请求URL:https://api.weixin.qq.com/card/invoice/insert?access_token={access_token}

请求方法:POST

请求参数

请求参数使用JSON格式,字段如下:

参数类型是否必填描述
order_idstring发票order_id,既商户给用户授权开票的订单号
card_idString发票card_id
appidString该订单号授权时使用的appid,一般为商户appid
card_extObject发票具体内容

card_ext为Object,包括以下内容:

参数类型是否必填描述
nonce_strString随机字符串,防止重复
user_cardObject用户信息结构体

user_card中包含一个invoice_user_data对象,invoice_user_data包含以下内容:

参数类型是否必填描述
feeInt发票的金额,以分为单位
titleString发票的抬头
billing_timeInt发票的开票时间,为10位时间戳(utc+8)
billing_noString发票的发票号码;数电发票传20位发票号码
billing_codeString发票的发票代码;数电发票发票代码为空
infoList商品详情结构,见下方
fee_without_taxInt不含税金额,以分为单位
taxInt税额,以分为单位
s_pdf_media_idString发票pdf文件上传到微信发票平台后,会生成一个发票s_media_id,该s_media_id可以直接用于关联发票PDF和发票卡券。发票上传参考“ 3 上传PDF ”一节
s_trip_pdf_media_idString其它消费附件的PDF,如行程单、水单等,PDF上传方式参考“ 3 上传PDF ”一节
check_codeString校验码,发票pdf右上角,开票日期下的校验码;数电发票发票校验码为空
buyer_numberString购买方纳税人识别号
buyer_address_and_phoneString购买方地址、电话
buyer_bank_accountString购买方开户行及账号
seller_numberString销售方纳税人识别号
seller_address_and_phoneString销售方地址、电话
seller_bank_accountString销售方开户行及账号
remarksString备注,发票右下角初
cashierString收款人,发票左下角处
makerString开票人,发票下方处

info为Object列表,列表中每个Object包含以下信息:

参数类型是否必填描述
nameString项目的名称
numInt项目的数量
unitString项目的单位,如个
priceInt项目的单价

  1. //将电子发票卡券插入用户卡包
  2. public static JSONObject pushCoupons(String invoiceName,String sMediaId,String accessToken,String orderId,File file,String cardId) {
  3. JSONObject errParams = new JSONObject();
  4. try{
  5. JSONObject objectData = extractInvoiceNumber(file);
  6. // String sMediaId = element.getChildText("s_media_id");
  7. //
  8. // String invoiceName=element.getChildText("invoice_name");
  9. String apiUrl = "https://api.weixin.qq.com/card/invoice/insert?access_token="+accessToken;
  10. JSONObject jsonParams = new JSONObject();
  11. JSONObject cardExt = new JSONObject();
  12. JSONObject userCard = new JSONObject();
  13. JSONObject invoiceUserData = new JSONObject();
  14. // List<Object> infoList = new ArrayList<Object>();
  15. // JSONObject info = new JSONObject();
  16. UUID uuid=UUID.randomUUID();
  17. invoiceUserData.put("fee", objectData.getInt("fee"));
  18. invoiceUserData.put("title", invoiceName);
  19. invoiceUserData.put("billing_time", objectData.getInt("billingTime"));
  20. invoiceUserData.put("billing_no", objectData.get("billingNo"));
  21. invoiceUserData.put("billing_code", objectData.get("billingCode"));
  22. invoiceUserData.put("fee_without_tax", objectData.getInt("feeWithoutTax"));
  23. invoiceUserData.put("tax", objectData.getInt("tax"));
  24. invoiceUserData.put("s_pdf_media_id", sMediaId);
  25. invoiceUserData.put("check_code", objectData.get("checkCode"));
  26. userCard.put("invoice_user_data", invoiceUserData);
  27. cardExt.put("nonce_str",uuid);
  28. cardExt.put("user_card", userCard);
  29. jsonParams.put("order_id", orderId);
  30. jsonParams.put("card_ext", cardExt);
  31. jsonParams.put("card_id", cardId); //模板id
  32. jsonParams.put("appid", "wxb92aeb7a8c2089e5");
  33. // 创建连接
  34. URL url = new URL(apiUrl);
  35. HttpURLConnection connection = (HttpURLConnection) url.openConnection();
  36. connection.setRequestMethod("POST");
  37. connection.setRequestProperty("Content-Type", "application/json; utf-8");
  38. connection.setRequestProperty("Accept", "application/json");
  39. connection.setDoOutput(true);
  40. // 写入请求数据
  41. try (OutputStream os = connection.getOutputStream()) {
  42. byte[] input = jsonParams.toString().getBytes("utf-8");
  43. os.write(input, 0, input.length);
  44. }
  45. // 读取响应
  46. try (BufferedReader br = new BufferedReader(
  47. new InputStreamReader(connection.getInputStream(), "utf-8"))) {
  48. StringBuilder response = new StringBuilder();
  49. String responseLine;
  50. while ((responseLine = br.readLine()) != null) {
  51. response.append(responseLine.trim());
  52. }
  53. // 将JSON字符串转换为JSONObject
  54. JSONObject data = new JSONObject(response.toString());
  55. connection.disconnect();
  56. return data;
  57. }
  58. }catch(Exception e){
  59. errParams.put("errcode",1);
  60. return errParams;
  61. }
  62. }

这样一套流程下来  就把发票插入到 对应的授权人的 微信卡包里啦

然后对应人公众号会收到一个收到发票的信息

创作不易 有问题可以留言 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/884580
推荐阅读
相关标签
  

闽ICP备14008679号