微信掃碼支付功能(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)

相關文章
相關標籤/搜索