Micro-service calls up the payment record of the WeChat applet

Micro-service calls up the payment record of the WeChat applet

  • Application Applet

  • Applicant Business Number

  • View Applet Payment Document

    Be sure to read the document carefully and completely

  • Set Get Applet/Business Number parameter

    • Applet parameters
      • appid applet ID
      • openId Micro Credit User Unique Identification

        Note that the payment type used by the applet here is JSAPI, so within the parameters of the payment, openId is a required parameter

      View applets The path for these parameters is to login to the left page, tap Development, open the development settings, view the applet parameters

      Applet authentication, you need to improve the basic information of the applet, such as name, introduction, avatar, and so on, before the data is completed, the certification will appear.

    • Business Number Parameters
      • mch_id merchant ID
      • Merchant Key Logon Merchant-Account Center-API Security-API Key
  • Intranet Penetration

    • Continue validating the tools described earlier- natapp
  • Enter development

    • WeChat Unified Single Interface

          private static final String UNIFIEDORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
      	public static String getWXUnifiedorderPrepayId(String appId,String mchId,String body,String outTradeNo,
                                                          String totalFee,String notifyUrl,String  tradeType,
                                                          String openId,String mchPublicKey){
                  Map<String,String> payParameter = new HashMap<>(16);
                  //Applet ID
                  payParameter.put("appid",appId);
                  //Business Number
                  payParameter.put("mch_id",mchId);
                  //Commodity description
                  payParameter.put("body",body);
                  //Random String
                  payParameter.put("nonce_str", RandomUtil.randomString(32));
                  //Merchant Order Number
                  payParameter.put("out_trade_no",outTradeNo);
                  //Price amount
                  payParameter.put("total_fee",totalFee);
                  //Terminal IP
                  payParameter.put("spbill_create_ip", NetUtil.getLocalhostStr());
                  //Notification Address-Callback Address
                  payParameter.put("notify_url",notifyUrl);
                  //Transaction type
                  payParameter.put("trade_type",tradeType);
                  //openid -- This parameter is required by the applet's JSAPI
                  if (WXPayEnum.TradeType.JSAPI.getKey().equals(tradeType)){
                      payParameter.put("openid",openId);
                  }
                  //autograph
                  payParameter.put("sign", WXPayUtils.getSign(payParameter,mchPublicKey));
                  RestTemplate restTemplate = new RestTemplate();
                  List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
                  converterList.add(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
                  restTemplate.setMessageConverters(converterList);
                  String requestBody = XmlUtil.mapToXmlStr(payParameter,"xml");
                  URI uri = URI.create(UNIFIEDORDER_URL);
                  String responseStr = restTemplate.postForObject(uri,requestBody,String.class);
                  Map<String,Object> responseMap = XmlUtil.xmlToMap(responseStr);
                  //Determine if the communication ID requests WeChat successfully
                  if (responseMap.get("return_code").toString().equals(WXPayEnum.ReturnCode.FAIL.getKey())){
                      throw new CheckedException(responseMap.get("return_msg").toString());
                  }
                  //Check result_code to see if the transaction was successful
                  if (responseMap.get("result_code").toString().equals(WXPayEnum.ResultCode.FAIL.getKey())){
                      throw new CheckedException(WXPayEnum.ErrCode.getErrValue(responseMap.get("result_code").toString()));
                  }
                  return responseMap.get("prepay_id").toString();
              }
      

      This is to call the unified order of WeChat to get the pre-payment id of WeChat. Note that the pre-payment id should be saved in the database. If the user's payment is cancelled, the next payment will be made again, and the pre-payment id will be pulled up directly. If there is an error returning from the regeneration of WeChat, the order number will be reminded to be changed.

    • autograph

          public static String getSign(Map<String,String> signMap, String mchPublicKey) {
          //Exclude non-empty parameters from the set
          Predicate<Map.Entry<String, String>> predicateStr = x -> StringUtils.isNotBlank(x.getValue());
          //Argument name ASCII code sorting from small to large converts to url parameter splicing mode
          List<Object> list = signMap.entrySet().stream().filter(predicateStr).sorted(Comparator.comparing(x -> x.getKey())).map(et -> et.getKey() + "=" + et.getValue()).collect(Collectors.toList());
          String stringA = StringUtils.join(list,"&");
          System.out.println(stringA);
          //key to splice merchant platforms
          String stringSignTemp = stringA+"&key="+ mchPublicKey;
          System.out.println(stringSignTemp);
          //MD5 encryption and capitalization
          return SecureUtil.md5(stringSignTemp).toUpperCase();
          }
      
    • Payment Callback

          	public String wxNotify(HttpServletRequest httpRequest){
              return qnwlOrderService.wxNotify(httpRequest);
          }
      
      
          //Take out parameters
          		String requestInp = IoUtil.read(httpServletRequest.getInputStream(), CharsetUtil.UTF_8);
                  log.info("WeChat Payment Callback Return xml The format is as follows:" + requestInp);
                  Map<String, Object> requestMap = XmlUtil.xmlToMap(requestInp);
      
      
                  //The IoUtil and XmlUtil used here are both hutool tools
      

      WeChat's callback return parameter is a data stream stuffed into the returned request, so on my side, I took the data stream xml format as a string from HttpServletRequest, and then turned to map.

    • Verify Signature

      	public static boolean checkSign(Map<String,Object> signMap, String mchPublicKey) {
          //Remove sign
          String sign = Optional.ofNullable(signMap.get("sign")).orElseThrow(() -> new CheckedException("Signature is empty and cannot be verified")).toString();
          //Delete signatures from map
          signMap.remove("sign");
          //Exclude non-empty parameters from the set
          Predicate<Map.Entry<String, Object>> predicateStr = x -> StringUtils.isNotBlank(x.getValue() + "");
          //Argument name ASCII code sorting from small to large converts to url parameter splicing mode
          List<Object> list = signMap.entrySet().stream().filter(predicateStr).sorted(Comparator.comparing(x -> x.getKey())).map(et -> et.getKey() + "=" + et.getValue()).collect(Collectors.toList());
          String stringA = StringUtils.join(list,"&");
          System.out.println(stringA);
          //key to splice merchant platforms
          String stringSignTemp = stringA+"&key="+ mchPublicKey;
          System.out.println(stringSignTemp);
          //MD5 encryption and capitalization
          if (sign.equals(SecureUtil.md5(stringSignTemp).toUpperCase())){
              return true;
          }
          return false;
       }
      
    • Business code, return applet payment parameters

          	@GetMapping("/xxxxxxx/{orderNum}/{openId}")
          public R getWxAppletsPay(@PathVariable String orderNum,@PathVariable String openId){
              Order order =  xxxxxxx.getOne(Wrappers.query(new Order()).eq("order_num",orderNum));
              Optional.ofNullable(order).orElseThrow(() -> new CheckedException("Order failed with empty order information"));
              if (!QnwlOrderEnums.OrderStatus.ORDER_ACCEPT.key.equals(order.getStatus())){
                  throw new CheckedException("Order status is received before payment can be made");
              }
              String body = "xxxxxxx";
              String prepayId;
              String totalFee = Optional.ofNullable(order.getRealFreight()).orElseThrow(() -> new CheckedException("Payment for order fee is empty")).multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString();
              if (!Optional.ofNullable(order.getWxPayId()).filter(StringUtils::isNotBlank).isPresent()){
                  prepayId = WXPayUtils.getWXUnifiedorderPrepayId(XCX_APPID,MCH_ID,body,order.getOrderNum(),totalFee,NOTIFY_URL,WXPayEnum.TradeType.JSAPI.getKey(),openId,MCH_PUBLIC_KEY);
                  //Store WeChat's prepayment id to prevent payment from being pulled again next time.
                  order.setWxPayId(prepayId);
                  xxxxxx.updateById(order);
              } else {
                  prepayId = order.getWxPayId();
              }
              Map<String,String> mapPay = new HashMap<>(5);
              mapPay.put("appId",XCX_APPID);
              mapPay.put("timeStamp", String.valueOf(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()));
              mapPay.put("nonceStr", RandomUtil.randomString(32));
              mapPay.put("package","prepay_id=" + prepayId);
              mapPay.put("signType","MD5");
              String paySign = WXPayUtils.getSign(mapPay,MCH_PUBLIC_KEY);
              mapPay.put("paySign",paySign);
              return R.ok(mapPay);
          }
      

      After returning the parameters paid to the applet here, the function that pays is called by the front end of the applet and the payment is invoked. Note here that I used stripTrailingZeros().toPlainString() to pay the price here; the reason is that I use the decimal type, which retains two decimals, removing two decimals that follow the xx.00 decimal point.

  • Related issues encountered in development

    • How to take out parameters after WeChat callback

      The parameters of the WeChat callback are put in the data stream in the request, need to receive the request with HttpServletRequest, then use httpServletRequest.getInputStream(), take out the stream, turn the string to view it

    • How to Verify Signature

      Verify the signature here, you need to note that there are many fields in the xml returned by Wechat. You need to take out the sign, then sort and encrypt the remaining fields, and then compare with the sign returned by Wechat to release them consistently.The signature returned by the callback here is not the same as the sign you made the order request.

    • About Always Callback

      When testing the callback, I hit a breakpoint for the local area to see if the callback came in. There was a problem here.Since there was a breakpoint, I did not release the request in time, and returned, resulting in WeChat, which was called again in the next few seconds. So, I finished the breakpoint behind me, returned, WeChat or called me, which would be confusing. Later, I canceled the breakpoint, requested to come in, returned directly to WeChat without delay, and then did not call my callback interface all the time in the future.Questions.

  • Explain

    • How to Make Payment Callbacks for Micro Services

      When the micro service is testing locally, it calls back. I start to think about mapping the ports of the gateway to let the micro message callback, but the callback is affected by the original project, so when the local test callback, I just map my single portal service port to let the micro message callback directly, without the gateway, callback interface, to importRow exposure

Tags: Programming xml ascii Database

Posted on Mon, 11 May 2020 23:03:32 -0400 by gortron