微信扫码支付功能(2)---用户扫码支付成功,微信异步回调商户接口

用户扫码支付成功,微信异步回调商户

      上一篇博客完成用户扫码支付功能: http://www.javashuo.com/article/p-beqpwyxr-d.htmlhtml

当用户扫码支付成功以后,微信会异步回调商户接口,告知用户支付成功。好让商户进行下一步操做。git

1、接口说明

   一、流程图

这里要作的就是用户支付成功后,微信异步通知商户支付结果,商户收到通知后告知支付通知接收状况。 github

      二、接口说明

有关商户接口应注意如下几点:spring

     (1)该连接是经过【统一下单API】中提交的参数notify_url设置,若是连接没法访问,商户将没法接收到微信通知。数据库

     (2)notify_url不能有参数,外网能够直接访问,不能有访问控制(好比必需要登陆才能操做)。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”设计模式

     (3)支付完成后,微信会把相关支付结果和用户信息发送给商户,商户须要接收处理,并返回应答。api

     (4)对后台通知交互时,若是微信收到商户的应答不是成功或超时,微信认为通知失败,微信会经过必定的策略按期从新发起通知,尽量提升通知的成功率,但微信不微信

保证通知最终能成功。(通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒)注意:一样的通知可能会屡次发送给商户系统。商户系统必须可以正确处理并发

重复的通知。推荐的作法是,当收到通知进行处理时,首先检查对应业务数据的状态,判断该通知是否已经处理过,若是没有处理过再进行处理,若是处理过直接返回结果成功。app

在对业务数据进行状态检查和处理以前,要采用数据锁进行并发控制,以免函数重入形成的数据混乱。

    (5)特别提醒:商户系统对于支付结果通知的内容必定要作签名验证,防止数据泄漏致使出现“假通知”,形成资金损失

 

2、接口开发

    一、回调接口

    有关ngrok工具若是不了解的话,能够参考博客:   https://www.cnblogs.com/qdhxhz/p/9678137.html

    /**
     * 微信支付回调
     * 这里在统一下单中提供的notify_url地址是:http://jincou.vipgz1.idcfengye.com/api/v1/order/callback
     * 该域名是sunny-ngrok中的二级域名,经过它映射到微信本地
     */
    @RequestMapping("callback")
    public void orderCallback(HttpServletRequest request,HttpServletResponse response) throws Exception {
        InputStream inputStream =  request.getInputStream();
        //BufferedReader是包装设计模式,性能更搞
        BufferedReader in =  new BufferedReader(new InputStreamReader(inputStream,"UTF-8"));
        StringBuffer sb = new StringBuffer();
        //一、将微信回调信息转为字符串
        String line ;
        while ((line = in.readLine()) != null){
            sb.append(line);
        }
        in.close();
        inputStream.close();
        //二、将xml格式字符串格式转为map集合
        Map<String,String> callbackMap = WXPayUtil.xmlToMap(sb.toString());
        System.out.println(callbackMap.toString());
        //三、转为有序的map
        SortedMap<String,String> sortedMap = WXPayUtil.getSortedMap(callbackMap);
        //四、判断签名是否正确
        if(WXPayUtil.isCorrectSign(sortedMap,weChatConfig.getKey())){
            //五、判断回调信息是否成功
            if("SUCCESS".equals(sortedMap.get("result_code"))){
                //获取商户订单号
                //商户系统内部订单号,要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下惟一
                String outTradeNo = sortedMap.get("out_trade_no");
                System.out.println(outTradeNo);
                //六、数据库查找订单,若是存在则根据订单号更新该订单
                VideoOrder dbVideoOrder = videoOrderService.findByOutTradeNo(outTradeNo);
                System.out.println(dbVideoOrder);
                if(dbVideoOrder != null && dbVideoOrder.getState()==0){  //判断逻辑看业务场景
                    VideoOrder videoOrder = new VideoOrder();
                    videoOrder.setOpenid(sortedMap.get("openid"));
                    videoOrder.setOutTradeNo(outTradeNo);
                    videoOrder.setNotifyTime(new Date());
                    //修改支付状态,以前生成的订单支付状态是未支付,这里表面已经支付成功的订单
                    videoOrder.setState(1);
                    //根据商户订单号更新订单
                    int rows = videoOrderService.updateVideoOderByOutTradeNo(videoOrder);
                    System.out.println(rows);
                    //七、通知微信订单处理成功
                    if(rows == 0){
                        response.setContentType("text/xml");
                        response.getWriter().println("success");
                        return;
                    }
                }}
        }
        //七、通知微信订单处理失败
        response.setContentType("text/xml");
        response.getWriter().println("fail");

    }

    二、校验签名方法

    /**
     * 校验签名
     */
    public static boolean isCorrectSign(SortedMap<String, String> params, String key){

        //一、再进行一次生成sign
        String sign = createSign(params,key);
        String weixinPaySign = params.get("sign").toUpperCase();
        //将两次生成的sign比较看是否一致
        return weixinPaySign.equals(sign);
    }
    
    /**
     * 生成微信支付sign
     */
    public static String createSign(SortedMap<String, String> params, String key){
        StringBuilder sb = new StringBuilder();
        Set<Map.Entry<String, String>> es =  params.entrySet();
        Iterator<Map.Entry<String,String>> it =  es.iterator();
        //生成 stringA="appid=wxd930ea5d5a258f4f&body=test&device_info=1000&mch_id=10000100&nonce_str=ibuaiVcKdpRxkhJA";
        while (it.hasNext()){
            Map.Entry<String,String> entry = (Map.Entry<String,String>)it.next();
             String k = (String)entry.getKey();
             String v = (String)entry.getValue();
             if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)){
                sb.append(k+"="+v+"&");
             }
        }
        sb.append("key=").append(key);
        String sign = CommonUtils.MD5(sb.toString()).toUpperCase();
        return sign;
    }

      三、测试

(1)支付成功

(2)经过ngrok回调到本地,经过断点能够看出sb字符串格式

(3)将xml格式字符串转为map

成功!

 

github源码

githubhttps://github.com/yudiandemingzi/spring-boot-wechat-pay

 

 

我只是偶尔安静下来,对过去的种种思忖一番。那些曾经的旧时光里即使有过天真愚钝,也不值得谴责。毕竟,日后的日子,还很长。不断鼓励本身,

天一亮,又是崭新的起点,又是未知的征程(上校17)

相关文章
相关标签/搜索