上一篇博客完成用戶掃碼支付功能: http://www.javashuo.com/article/p-beqpwyxr-d.htmlhtml
當用戶掃碼支付成功以後,微信會異步回調商戶接口,告知用戶支付成功。好讓商戶進行下一步操做。git
這裏要作的就是用戶支付成功後,微信異步通知商戶支付結果,商戶收到通知後告知支付通知接收狀況。 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)特別提醒:商戶系統對於支付結果通知的內容必定要作簽名驗證,防止數據泄漏致使出現「假通知」,形成資金損失。
有關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: https://github.com/yudiandemingzi/spring-boot-wechat-pay
我只是偶爾安靜下來,對過去的種種思忖一番。那些曾經的舊時光裏即使有過天真愚鈍,也不值得譴責。畢竟,日後的日子,還很長。不斷鼓勵本身,
天一亮,又是嶄新的起點,又是未知的征程(上校17)