微信掃碼支付功能(2)---使用者掃碼支付成功,微信非同步回撥商戶介面
使用者掃碼支付成功,微信非同步回撥商戶
當用戶掃碼支付成功之後,微信會非同步回撥商戶介面,告知使用者支付成功。好讓商戶進行下一步操作。
一、介面說明
1、流程圖
這裡要做的就是使用者支付成功後,微信非同步通知商戶支付結果,商戶收到通知後告知支付通知接收情況。
2、介面說明
有關商戶介面應注意以下幾點:
(1)該連結是通過【統一下單API】中提交的引數notify_url設定,如果連結無法訪問,商戶將無法接收到微信通知。
(2)notify_url不能有引數,外網可以直接訪問,不能有訪問控制(比如必須要登入才能操作)。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action”
(3)支付完成後,微信會把相關支付結果和使用者資訊傳送給商戶,商戶需要接收處理,並返回應答。
(4)對後臺通知互動時,如果微信收到商戶的應答不是成功或超時,微信認為通知失敗,微信會通過一定的策略定期重新發起通知,儘可能提高通知的成功率,但微信不
保證通知最終能成功。(通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒)注意:同樣的通知可能會多次傳送給商戶系統。商戶系統必須能夠正確處理
重複的通知。推薦的做法是,當收到通知進行處理時,首先檢查對應業務資料的狀態,判斷該通知是否已經處理過,如果沒有處理過再進行處理,如果處理過直接返回結果成功。
在對業務資料進行狀態檢查和處理之前,要採用資料鎖進行併發控制,以避免函式重入造成的資料混亂。
(5)特別提醒:商戶系統對於支付結果通知的內容一定要做簽名驗證,防止資料洩漏導致出現“假通知”,造成資金損失。
二、介面開發
1、回撥介面
/** * 微信支付回撥 * 這裡在統一下單中提供的notify_url地址是:http://jincou.vipgz1.idcfengye.com/api/v1/order/callback * 該域名是sunny-ngrok中的二級域名,通過它對映到微信本地 */ @RequestMapping("callback") public void orderCallback(HttpServletRequest request,HttpServletResponse response) throwsException { InputStream inputStream = request.getInputStream(); //BufferedReader是包裝設計模式,效能更搞 BufferedReader in = new BufferedReader(new InputStreamReader(inputStream,"UTF-8")); StringBuffer sb = new StringBuffer(); //1、將微信回撥資訊轉為字串 String line ; while ((line = in.readLine()) != null){ sb.append(line); } in.close(); inputStream.close(); //2、將xml格式字串格式轉為map集合 Map<String,String> callbackMap = WXPayUtil.xmlToMap(sb.toString()); System.out.println(callbackMap.toString()); //3、轉為有序的map SortedMap<String,String> sortedMap = WXPayUtil.getSortedMap(callbackMap); //4、判斷簽名是否正確 if(WXPayUtil.isCorrectSign(sortedMap,weChatConfig.getKey())){ //5、判斷回撥資訊是否成功 if("SUCCESS".equals(sortedMap.get("result_code"))){ //獲取商戶訂單號 //商戶系統內部訂單號,要求32個字元內,只能是數字、大小寫字母_-|* 且在同一個商戶號下唯一 String outTradeNo = sortedMap.get("out_trade_no"); System.out.println(outTradeNo); //6、資料庫查詢訂單,如果存在則根據訂單號更新該訂單 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); //7、通知微信訂單處理成功 if(rows == 0){ response.setContentType("text/xml"); response.getWriter().println("success"); return; } }} } //7、通知微信訂單處理失敗 response.setContentType("text/xml"); response.getWriter().println("fail"); }
2、校驗簽名方法
/** * 校驗簽名 */ public static boolean isCorrectSign(SortedMap<String, String> params, String key){ //1、再進行一次生成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; }
3、測試
(1)支付成功
(2)通過ngrok回撥到本地,通過斷點可以看出sb字串格式
(3)將xml格式字串轉為map
成功!
github原始碼
我只是偶爾安靜下來,對過去的種種思忖一番。那些曾經的舊時光裡即便有過天真愚鈍,也不值得譴責。畢竟,往後的日子,還很長。不斷鼓勵自己,
天一亮,又是嶄新的起點,又是未知的征程(上校17)