Skip to content

微信支付

sqmax edited this page Jan 13, 2019 · 12 revisions

首先我们到微信支付的官方文档的开发步骤部分查看一下需要的设置,

业务流程时序图:

可参见业务流程

总结起来如下:

1、微信用户在H5网页内请求生成支付订单。

2、商户后台收到请求,会生成商户订单,调用统一下单API, 统一下单接口的链接是: https://api.mch.weixin.qq.com/pay/unifiedorder,这是一个post请求,需要带上如下样式的请求体:

<xml>
   <appid>wx2421b1c4370ec43b</appid>
   <attach>支付测试</attach>
   <body>JSAPI支付测试</body>
   <mch_id>10000100</mch_id>
   <detail><![CDATA[{ "goods_detail":[ { "goods_id":"iphone6s_16G", "wxpay_goods_id":"1001", "goods_name":"iPhone6s 16G", "quantity":1, "price":528800, "goods_category":"123456", "body":"苹果手机" }, { "goods_id":"iphone6s_32G", "wxpay_goods_id":"1002", "goods_name":"iPhone6s 32G", "quantity":1, "price":608800, "goods_category":"123789", "body":"苹果手机" } ] }]]></detail>
   <nonce_str>1add1a30ac87aa2db72f57a2375d8fec</nonce_str>
   <notify_url>http://wxpay.wxutil.com/pub_v2/pay/notify.v2.php</notify_url>
   <openid>oUpF8uMuAJO_M2pxb1Q9zNjWeS6o</openid>
   <out_trade_no>1415659990</out_trade_no>
   <spbill_create_ip>14.23.150.211</spbill_create_ip>
   <total_fee>1</total_fee>
   <trade_type>JSAPI</trade_type>
   <sign>0CB01533B8C1EF103065174F50BCA001</sign>
</xml> 

上面参数是需要动态传入的,各参数的意义和可填与否,见微信官方文档:统一下单

3、调用统一下单API后,会生成预支付信息(也是一段xml格式的字符串),下面是统一下单请求成功,返回的预支付信息的例子:

<xml>
   <return_code><![CDATA[SUCCESS]]></return_code>
   <return_msg><![CDATA[OK]]></return_msg>
   <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
   <mch_id><![CDATA[10000100]]></mch_id>
   <nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
   <openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
   <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
   <result_code><![CDATA[SUCCESS]]></result_code>
   <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
   <trade_type><![CDATA[JSAPI]]></trade_type>
</xml> 

4、商户后台将预支付信息填入JSAPI页面返回给用户,用户点击发起支付。 下面是一个freemarker模板的JSAPI例子(create.ftl):

<script>
    function onBridgeReady(){
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest', {
                "appId":"${payResponse.appId}",     //公众号名称,由商户传入
                "timeStamp":"${payResponse.timeStamp}",         //时间戳,自1970年以来的秒数
                "nonceStr":"${payResponse.nonceStr}", //随机串
                "package":"${payResponse.packAge}",
                "signType":"MD5",         //微信签名方式:
                "paySign":"${payResponse.paySign}" //微信签名
            },
            function(res){
                if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                    alert('支付成功');
                }else if(res.err_msg == "get_brand_wcpay_request:cancel") {
                    alert('支付过程中用户取消');
                }else if(res.err_msg == "get_brand_wcpay_request:fail") {
                    alert('支付失败');
                }else {
                    alert('未知异常');
                }
                <#--if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。-->
                <#--location.href="${returnUrl}"-->
    }
        );
    }
    if (typeof WeixinJSBridge == "undefined"){
        if( document.addEventListener ){
            document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
        }else if (document.attachEvent){
            document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
            document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
        }
    }else{
        onBridgeReady();
    }

</script>

5、然后微信支付系统接收到JSAPI接口请求,会检查参数合法性和授权域权限,返回验证结果给用户,并要求支付授权,用户确认支付,输入密码,提交授权给微信支付系统。

6、然后微信支付系统验证授权后,异步通知商户支付结果

下面是微信官方文档对【支付结果通知】应用场景的描述:

支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答。 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新发起通知,尽可能提高通知的成功率,但微信不保证通知最终能成功。 (通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒) 注意:同样的通知可能会多次发送给商户系统。商户系统必须能够正确处理重复的通知。 推荐的做法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,如果没有处理过再进行处理,如果处理过直接返回结果成功。在对业务数据进行状态检查和处理之前,要采用数据锁进行并发控制,以避免函数重入造成的数据混乱。 特别提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄漏导致出现“假通知”,造成资金损失。 详情见支付结果通知

这里的异步通知url是【统一下单API】中提交的参数notify_url设置的,这个异步请求还带有一个请求体(一段xml格式的字符串),如下:

<xml>
  <appid><![CDATA[wx2421b1c4370ec43b]]></appid>
  <attach><![CDATA[支付测试]]></attach>
  <bank_type><![CDATA[CFT]]></bank_type>
  <fee_type><![CDATA[CNY]]></fee_type>
  <is_subscribe><![CDATA[Y]]></is_subscribe>
  <mch_id><![CDATA[10000100]]></mch_id>
  <nonce_str><![CDATA[5d2b6c2a8db53831f7eda20af46e531c]]></nonce_str>
  <openid><![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]]></openid>
  <out_trade_no><![CDATA[1409811653]]></out_trade_no>
  <result_code><![CDATA[SUCCESS]]></result_code>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <sign><![CDATA[B552ED6B279343CB493C5DD0D78AB241]]></sign>
  <sub_mch_id><![CDATA[10000100]]></sub_mch_id>
  <time_end><![CDATA[20140903131540]]></time_end>
  <total_fee>1</total_fee>
<coupon_fee_0><![CDATA[10]]></coupon_fee_0>
<coupon_count><![CDATA[1]]></coupon_count>
<coupon_type><![CDATA[CASH]]></coupon_type>
<coupon_id><![CDATA[10000]]></coupon_id>
  <trade_type><![CDATA[JSAPI]]></trade_type>
  <transaction_id><![CDATA[1004400740201409030005092168]]></transaction_id>
</xml> 

7、然后商户后台告知微信支付系统异步通知处理结果。通知信息如下:

<xml>
  <return_code><![CDATA[SUCCESS]]></return_code>
  <return_msg><![CDATA[OK]]></return_msg>
</xml>

如果微信给我们商户后台发送异步通知后,我们不给它回应,就一直会收到异步通知,所以商户后台接收到微信支付结果通知后,应该返回以上内容给微信支付系统,微信支付系统就不会再发来异步通知请求。

微信发来异步通知请求,我们应该做以下事情: 1.验证签名。2.支付状态。3. 支付金额。4. 支付人(下单人==支付人) 以上四步不仅适用于微信支付异步通知,做其它支付异步通知也是这样,第四部不是必要的。

下面是我们的这里异步通知处理过程:

@Override
public PayResponse notify(String notifyData) {
    //1.验证签名
    //2.支付状态
    //3. 支付金额
    //4. 支付人(下单人==支付人)
    PayResponse payResponse=bestPayService.asyncNotify(notifyData);//可以完成1、2两步
    log.info("【微信支付 异步通知】,payResponse={}",JsonUtil.toJson(payResponse));

    //查询订单
    OrderDTO orderDTO=orderService.findOne(payResponse.getOrderId());

    //判断订单是否存在
    if(orderDTO==null){
        log.error("【微信支付】 异步通知,订单不存在,orderId={}",payResponse.getOrderId());
        throw new SellException(ResultEnum.ORDER_NOT_EXIST);
    }
    //判断金额是否一致(0.10   0.1)
    if(!MathUtil.equals(payResponse.getOrderAmount(),orderDTO.getOrderAmount().doubleValue())){
        log.error("【微信支付】 异步通知,订单金额不一致,orderId={},微信通知金额={},系统金额={}",
                payResponse.getOrderId(),
                payResponse.getOrderAmount(),
                orderDTO.getOrderAmount());
        throw new SellException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY);
    }
    //修改订单的支付状态
    orderService.paid(orderDTO);

    return payResponse;
}

以上是对微信官方文档的解读,其实使用SDK的方式容易,参见Demo

因为微信支付需要较高的权限,只有认证了得服务号才有使用微信支付接口的权限,我们个人很难申请到,所以还需要向老师借用账号,借用地址,按说明填写好。