hxzxy

付款码支付

付款码支付

付款码支付

概述

  • 付款码支付是指用户展示微信钱包内的“付款码”给商户系统扫描后直接完成支付,适用于线下场所面对面收银的场景,例如商超、便利店、餐饮、医院、学校、电影院和旅游景区等具有明确经营地址的实体场所

  • 商户平台-产品中心-付款码支付-申请开通

产品流程

  1. 用户打开付款码支付

  2. 线下收银员在自己的商户系统生成支付订单,商户系统发送支付请求

  3. 微信支付后台系统收到支付请求,根据验证密码规则判断是否验证用户的支付密码,不需要验证密码的交易直接发起扣款,需要验证密码的交易会弹出密码输入框。支付成功后微信端会弹出成功页面,支付失败会弹出错误提示

接入指引

  • 商户接入微信支付,调用API必须遵循以下规则:

    1. 传输方式:为保证交易安全性,采用HTTPS传输

    2. 提交方式:采用POST方法提交

    3. 数据格式:提交和返回数据都为XML格式,根节点名为xml

    4. 字符编码:统一采用UTF-8字符编码

    5. 签名算法:MD5,后续会兼容SHA1、SHA256、HMAC等。

    6. 签名要求:请求和接收数据均需要校验签名

    7. 证书要求:调用申请退款、撤销订单接口需要商户证书

    8. 判断逻辑:先判断协议字段返回,再判断业务返回,最后判断交易状态

  • 注意

    必须严格按照API的说明进行一单一支付,一单一红包,一单一付款,在未得到支付系统明确的回复之前不要换单,防止重复支付或者重复付款

开发指引

    1. 根据商户具体的情况,付款码支付接入模式可分为:商户后台接入和门店接入;

      根据用户是否需要输入支付密码可分为:免密模式和验密模式。

    2. 接入模式

    • 商户后台接入

      该模式适合具备统一后台系统的商户。门店收银台与商户后台通信,商户后台系统负责与微信支付系统发送交易请求和接收返回结果。

      主要是商户通过扫描用户的付款码信息,商户后台生成支付订单 并向微信后台提交请求 ,微信后台向用户发送验证请求,用户填写密码支付后 ,微信后台同时向用户和商户后台发送支付成功信息

    • 门店接入

      该模式适合门店收银台通过公网直接与微信后台通信的商户。门店收银台直接发起交易请求和处理返回结果。商户可以根据实际需要,处理门店和商户后台系统之间的其它业务流程。

安全规范

  • 签名算法步骤

    1. 设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA

    2. 特别注意以下重要规则:

      ◆ 参数名ASCII码从小到大排序(字典序);

      ◆ 如果参数的值为空不参与签名;

      ◆ 参数名区分大小写;

      ◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。

      ◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段

    3. 在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue( key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置)

      假设传送的参数如下:

      appid:wxd930ea5d5a258f4f // appid
      mch_id:10000100       // 商户id
      device_info:1000     //设备id
      body:test     //内容
      nonce_str:ibuaiVcKdpRxkhJA //随机字符串

      3.1: 对参数按照key=value的格式,并按照参数名ASCII字典序排序如下

      stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";

      3.2: 拼接API密钥

      stringSignTemp=stringA+"&key=192006250b4c09247ec02edce69f6a2d" //注:key为商户平台设置的密钥key
      sign=MD5(stringSignTemp).toUpperCase()="9A0A8659F005D6984697E2CA0A9CF3B7" //注:MD5签名方式
      sign=hash_hmac("sha256",stringSignTemp,key).toUpperCase()="6A9AE1657590FD6257D693A078E1C3E4BB6BA4DC30B23E0EE2496E54170DACD6" //注:HMAC-SHA256签名方式

      3.3: 最后发送的数据

      <xml>
       <appid>wxd930ea5d5a258f4f</appid>
       <mch_id>10000100</mch_id>
       <device_info>1000</device_info>
       <body>test</body>
       <nonce_str>ibuaiVcKdpRxkhJA</nonce_str>
       <sign>9A0A8659F005D6984697E2CA0A9CF3B7</sign>
      </xml>          

    编写代码

      1. 准备工作

        appId: 微信分配的应用ID

        mchId: 微信支付分配的商户号

      2. 统一下单

        收银员使用扫码设备读取微信用户付款码以后,二维码或条码信息会传送至商户收银台,由商户收银台或者商户后台调用该接口发起支付

        2.1 请求url: https://api.mch.weixin.qq.com/pay/micropay

        2.2 请求方式:post

        2.3 数据格式:xml

        2.4 请求参数

        appid 微信分配的应用ID
        mch_id 微信支付分配的商户号
        nonce_str 随机字符串
        sign 签名
        body 商品描述
        out_trade_no 商户订单号
        total_fee 总金额
        spbill_create_ip 终端IP
        auth_code 付款码
        请求实例
        <xml>
         <appid>wx2421b1c4370ec43b</appid>
         <attach>订单额外描述</attach>
         <auth_code>120269300684844649</auth_code>
         <body>付款码支付测试</body>
         <device_info>1000</device_info>
         <goods_tag></goods_tag>
         <mch_id>10000100</mch_id>
         <sub_mch_id>10000101</sub_mch_id>
         <nonce_str>8aaee146b1dee7cec9100add9b96cbe2</nonce_str>
         <out_trade_no>1415757673</out_trade_no>
         <spbill_create_ip>14.17.22.52</spbill_create_ip>
         <time_expire></time_expire>
         <total_fee>1</total_fee>
         <sign>C29DB7DB1FD4136B84AE35604756362C</sign>
        </xml>
2.5 相关依赖 基于IJPay做的
         <!--微信支付  -->
         <dependency>
<groupId>com.github.javen205</groupId>
<artifactId>IJPay-WxPay</artifactId>
<version>2.7.1</version>
</dependency>
<!-- jfinal 默认工具 -->
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jfinal</artifactId>
<version>3.3</version>
</dependency>
<dependency>
<groupId>com.jfinal</groupId>
<artifactId>jetty-server</artifactId>
<version>8.1.8</version>
</dependency>  
2.6 配置类
@Data
@Component
@ConfigurationProperties(prefix = "wx") // 从yml文件获取到配置信息
/**
* 功能描述:
* 〈微信常量配置类〉
* @author : hxz
* @date : 2021/2/5 11:04
*/
public class WxPayBean {
   /**appId**/
   private String appId;
   /**秘钥**/
   private String appSecret;
   /**商户id**/
   private String mchId;
   /**商户支付秘钥**/
   private String partnerKey;
    /** 商户证书API 证书中的 p12绝对路径**/
   private String certPath;
   /** 证书的秘钥 默认为mchId**/
   private String certPass;
}
2.7 yml
server:
port: 9090
wx:
appId: ********
appSecret: ********
micropayUrl: ********
partnerKey: ********
mchId: ********
2.8 代码
@RestController
@Slf4j
@RequestMapping("/wxPay")
public class WxPayController {

   @Autowired
   private  WxPayBean wxPayBean;
   private static final String USER_PAYING = "USERPAYING";
   /**
    * 功能描述:
    * 〈付款码支付〉
    * @param request 1
    * @return : java.lang.String
    * @author : hxz
    * @date : 2021/2/5 11:30
    */
   @RequestMapping(value = "/micropay", method = {RequestMethod.POST, RequestMethod.GET})
   public Object microPay(HttpServletRequest request) {
       //获取到付款码
       String authCode = request.getParameter("auth_code");
       //获取到金额
       String totalFee = request.getParameter("total_fee");
       if (StrKit.isBlank(totalFee)) {
           return new  RuntimeException("金额不能为空");
      }
       if (StrKit.isBlank(authCode)) {
           return new  RuntimeException("auth_code参数错误");
      }
       // 终端IP
       String ip = IpKit.getRealIp(request);
       if (StrKit.isBlank(ip)) {
           ip = "127.0.0.1";
      }
       Map<String, String> params = MicroPayModel.builder()
              .appid(wxPayBean.getAppId())
              .mch_id(wxPayBean.getMchId())
              .nonce_str(WxPayKit.generateStr())
              .body("IJPay 让支付触手可及-刷卡支付")
              .attach("Node.js 版:https://gitee.com/javen205/TNWXX")
              .out_trade_no(WxPayKit.generateStr())
              .total_fee("1")
              .spbill_create_ip(ip)
              .auth_code(authCode)
              .build()
              .creatSign(wxPayBean.getPartnerKey(), SignType.HMACSHA256);
       String xmlResult = WxPayApi.micropay(false, params);
       //同步返回结果
       log.info("xmlResult:" + xmlResult);
       Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
       String returnCode = result.get("return_code");
       String returnMsg = result.get("return_msg");
       if (!WxPayKit.codeIsOk(returnCode)) {
           //通讯失败
           String errCode = result.get("err_code");
           if (StrKit.notBlank(errCode)) {
               //用户支付中,需要输入密码
               if (USER_PAYING.equals(errCode)) {
                   //等待5秒后调用【查询订单API】
              }
          }
           log.info("提交刷卡支付失败>>" + xmlResult);
           return new AjaxResult().addError(

分类:

技术点:

相关文章: