淺析微信支付:支付結果通知
本文是【淺析微信支付】系列文章的第六篇,主要講解支付成功後,微信回撥商戶支付結果通知的處理。
淺析微信支付系列已經更新五篇了喲~,沒有看過的朋友們可以看一下哦。
ofollow,noindex" target="_blank">淺析微信支付:統一下單介面
前面一章已經講了如何呼叫統一下單介面和調起微信支付視窗,在呼叫下單介面時,我們會傳入 非同步接收微信支付結果通知的回撥地址
,顧名思義這個地址作用就是用來接收支付結果通知,當用戶在前端支付成功後,微信伺服器會自動呼叫此地址,然後商戶再進行處理。
1、支付結果通知
以下為介面官方解釋:
支付完成後,微信會把相關支付結果和使用者資訊傳送給商戶,商戶需要接收處理,並返回應答。 對後臺通知互動時,如果微信收到商戶的應答不是成功或超時,微信認為通知失敗,微信會通過一定的策略定期重新發起通知,儘可能提高通知的成功率,但微信不保證通知最終能成功。 (通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒) 注意:同樣的通知可能會多次傳送給商戶系統。商戶系統必須能夠正確處理重複的通知。 推薦的做法是,當收到通知進行處理時,首先檢查對應業務資料的狀態,判斷該通知是否已經處理過,如果沒有處理過再進行處理,如果處理過直接返回結果成功。在對業務資料進行狀態檢查和處理之前,要採用資料鎖進行併發控制,以避免函式重入造成的資料混亂。 特別提醒:商戶系統對於支付結果通知的內容一定要做簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止資料洩漏導致出現“假通知”,造成資金損失。 技術人員可登進微信商戶後臺掃描加入介面報警群。
支付結果通知介面文件地址:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7&index=8
需要注意的事項有以下幾點:
1. 該連結是通過【統一下單API】中提交的引數notify_url設定,如果連結無法訪問,商戶將無法接收到微信通知。 2. 通知url必須為直接可訪問的url,不能攜帶引數,也就是必須使用外網介面地址,不能使用本地除錯地址 3. 商戶需要接收處理,並返回應答。如果微信收到商戶的應答不是成功或超時,微信認為通知失敗,微信會通過一定的策略定期重新發起通知,儘可能提高通知的成功率,但微信不保證通知最終能成功。 4. 通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒 5. 同樣的通知可能會多次傳送給商戶系統。商戶系統必須能夠正確處理重複的通知。 6. 特別提醒:商戶系統對於支付結果通知的內容一定要做簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止資料洩漏導致出現“假通知”,造成資金損失。
PS:推薦的做法是,當收到通知進行處理時,首先檢查對應業務資料的狀態,判斷該通知是否已經處理過,如果沒有處理過再進行處理,如果處理過直接返回結果成功。在對業務資料進行狀態檢查和處理之前,要採用資料鎖進行併發控制,以避免函式重入造成的資料混亂。
關於具體的簽名和接收通知程式碼如下:
package imall.controller.wx; package ... /** * 微信支付Controller * * @author yclimb * @date 2018/6/15 */ @Api @RestController @RequestMapping("/weixin/pay") public class WXPayController extends BaseController { // 需要注入的一些service /** * 返回成功xml */ private String resSuccessXml = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; /** * 返回失敗xml */ private String resFailXml = "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[報文為空]]></return_msg></xml>"; /** * 該連結是通過【統一下單API】中提交的引數notify_url設定,如果連結無法訪問,商戶將無法接收到微信通知。 * 通知url必須為直接可訪問的url,不能攜帶引數。示例:notify_url:“https://pay.weixin.qq.com/wxpay/pay.action” * <p> * 支付完成後,微信會把相關支付結果和使用者資訊傳送給商戶,商戶需要接收處理,並返回應答。 * 對後臺通知互動時,如果微信收到商戶的應答不是成功或超時,微信認為通知失敗,微信會通過一定的策略定期重新發起通知,儘可能提高通知的成功率,但微信不保證通知最終能成功。 * (通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒) * 注意:同樣的通知可能會多次傳送給商戶系統。商戶系統必須能夠正確處理重複的通知。 * 推薦的做法是,當收到通知進行處理時,首先檢查對應業務資料的狀態,判斷該通知是否已經處理過,如果沒有處理過再進行處理,如果處理過直接返回結果成功。在對業務資料進行狀態檢查和處理之前,要採用資料鎖進行併發控制,以避免函式重入造成的資料混亂。 * 特別提醒:商戶系統對於支付結果通知的內容一定要做簽名驗證,防止資料洩漏導致出現“假通知”,造成資金損失。 * * @author yclimb * @date 2018/6/15 */ @ApiOperation(value = "微信支付|支付回撥介面", httpMethod = "POST", notes = "該連結是通過【統一下單API】中提交的引數notify_url設定,如果連結無法訪問,商戶將無法接收到微信通知。") @RequestMapping("/wxnotify") public void wxnotify(HttpServletRequest request, HttpServletResponse response) { String resXml = ""; InputStream inStream; try { inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } WXPayUtil.getLogger().info("wxnotify:微信支付----start----"); // 獲取微信呼叫我們notify_url的返回資訊 String result = new String(outSteam.toByteArray(), "utf-8"); WXPayUtil.getLogger().info("wxnotify:微信支付----result----=" + result); // 關閉流 outSteam.close(); inStream.close(); // xml轉換為map Map<String, String> resultMap = WXPayUtil.xmlToMap(result); boolean isSuccess = false; if (WXPayConstants.SUCCESS.equalsIgnoreCase(resultMap.get(WXPayConstants.RESULT_CODE))) { WXPayUtil.getLogger().info("wxnotify:微信支付----返回成功"); if (WXPayUtil.isSignatureValid(resultMap, WXPayConstants.API_KEY)) { // 訂單處理 操作 orderconroller 的回寫操作? WXPayUtil.getLogger().info("wxnotify:微信支付----驗證簽名成功"); // 通知微信.非同步確認成功.必寫.不然會一直通知後臺.八次之後就認為交易失敗了. resXml = resSuccessXml; isSuccess = true; } else { WXPayUtil.getLogger().error("wxnotify:微信支付----判斷簽名錯誤"); } } else { WXPayUtil.getLogger().error("wxnotify:支付失敗,錯誤資訊:" + resultMap.get(WXPayConstants.ERR_CODE_DES)); resXml = resFailXml; } // 付款記錄修改 & 記錄付款日誌 // 回撥方法,處理業務 - 修改訂單狀態 WXPayUtil.getLogger().info("wxnotify:微信支付回撥:修改的訂單===>" + resultMap.get("out_trade_no")); int updateResult = ...; if (updateResult > 0) { WXPayUtil.getLogger().info("wxnotify:微信支付回撥:修改訂單支付狀態成功"); } else { WXPayUtil.getLogger().error("wxnotify:微信支付回撥:修改訂單支付狀態失敗"); } } catch (Exception e) { WXPayUtil.getLogger().error("wxnotify:支付回調發布異常:", e); } finally { try { // 處理業務完畢 BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } catch (IOException e) { WXPayUtil.getLogger().error("wxnotify:支付回調發布異常:out:", e); } } } }
驗證是否本商戶返回的正確通知:
// xml轉換為map Map<String, String> resultMap = WXPayUtil.xmlToMap(result); // 判斷簽名是否正確,必須包含sign欄位,否則返回false。使用MD5簽名。 WXPayUtil.isSignatureValid(resultMap, WXPayConstants.API_KEY);
從以上程式碼可以得知,微信是以流的方式來呼叫支付結果通知,流中資料格式為xml,所以需要先對流進行解析,然後再將xml轉換為map,上面方法已經實現這個過程,有現成的程式碼可供參考,無需再重新編寫程式碼。
需要注意的是,不論成功或者失敗,必須向微信返回對應的返回值,如上方程式碼中的 resSuccessXml
、 resFailXml
,否則會引起重複呼叫的問題,在程式碼中我們應該規避風險。
2、對於支付單的處理
在收到支付結果後,我們會對系統中的支付單進行狀態修改等操作,此時需要注意,如果微信返回失敗,介面直接返回失敗即可;
如果微信通知付款成功,返回時有一個 out_trade_no
引數,此引數為呼叫 統一下單介面
時傳入微信的支付單號,可根據此引數取得對應的支付單,然後進行修改操作,若操作時出現異常,一定要向微信返回錯誤的xml程式碼,用微信的重試機制來二次回撥,否則就需要記錄通知資訊,在本系統自動重試來解決。
在處理微信驗證資料時,還需要注意微信最終返回的結果中是否使用了代金券,如果使用了代金券,還需要另行處理,此處先不詳細描述,後面章節會詳細解釋代金券的操作。
結語
以上為 微信支付結果通知介面
的接收方式,它是一個微信服務自身控制的非同步呼叫方法,在自身商戶系統中需要處理很多異常,如網路抖動和服務異常等問題,所以儘量在商戶系統中儲存微信結果通知資料和增加失敗重試機制,保證資料的正確性。
預告:下一篇文章 查詢訂單和關閉訂單
,敬請期待!!!
如果想要提前一覽原始碼的小夥伴,可以先看看我的 github,地址如下: https://github.com/YClimb/wxpay-sdk/blob/master/README.md
加作者私人微信,作者微訊號如下 yclimb
,標明 微信支付
可拉入微信支付討論群與小夥伴一起探討哦,一定要標明 微信支付
哦~
到此本文就結束了,關注公眾號檢視更多推送!!!