淺析微信支付:支付結果通知

本文是【淺析微信支付】系列文章的第六篇,主要講解支付成功後,微信回調商戶支付結果通知的處理。

淺析微信支付系列已經更新五篇了喲~,沒有看過的朋友們能夠看一下哦。php

淺析微信支付:統一下單接口前端

淺析微信支付:微信公衆號網頁受權git

淺析微信支付:開發前的準備github

前面一章已經講了如何調用統一下單接口和調起微信支付窗口,在調用下單接口時,咱們會傳入 異步接收微信支付結果通知的回調地址,顧名思義這個地址做用就是用來接收支付結果通知,當用戶在前端支付成功後,微信服務器會自動調用此地址,而後商戶再進行處理。api

一、支付結果通知

如下爲接口官方解釋:服務器

支付完成後,微信會把相關支付結果和用戶信息發送給商戶,商戶須要接收處理,並返回應答。

對後臺通知交互時,若是微信收到商戶的應答不是成功或超時,微信認爲通知失敗,微信會經過必定的策略按期從新發起通知,儘量提升通知的成功率,但微信不保證通知最終能成功。 (通知頻率爲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:推薦的作法是,當收到通知進行處理時,首先檢查對應業務數據的狀態,判斷該通知是否已經處理過,若是沒有處理過再進行處理,若是處理過直接返回結果成功。在對業務數據進行狀態檢查和處理以前,要採用數據鎖進行併發控制,以免函數重入形成的數據混亂。併發

關於具體的簽名和接收通知代碼以下:app

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,上面方法已經實現這個過程,有現成的代碼可供參考,無需再從新編寫代碼。

須要注意的是,不論成功或者失敗,必須向微信返回對應的返回值,如上方代碼中的resSuccessXmlresFailXml,不然會引發重複調用的問題,在代碼中咱們應該規避風險。

二、對於支付單的處理

在收到支付結果後,咱們會對系統中的支付單進行狀態修改等操做,此時須要注意,若是微信返回失敗,接口直接返回失敗便可;

若是微信通知付款成功,返回時有一個 out_trade_no 參數,此參數爲調用 統一下單接口 時傳入微信的支付單號,可根據此參數取得對應的支付單,而後進行修改操做,若操做時出現異常,必定要向微信返回錯誤的xml代碼,用微信的重試機制來二次回調,不然就須要記錄通知信息,在本系統自動重試來解決。

在處理微信驗證數據時,還須要注意微信最終返回的結果中是否使用了代金券,若是使用了代金券,還須要另行處理,此處先不詳細描述,後面章節會詳細解釋代金券的操做。

結語

以上爲 微信支付結果通知接口 的接收方式,它是一個微信服務自身控制的異步調用方法,在自身商戶系統中須要處理不少異常,如網絡抖動和服務異常等問題,因此儘可能在商戶系統中保存微信結果通知數據和增長失敗重試機制,保證數據的正確性。

預告:下一篇文章 查詢訂單和關閉訂單,敬請期待!!!

​若是想要提早一覽源碼的小夥伴,能夠先看看個人 github,地址以下: https://github.com/YClimb/wxpay-sdk/blob/master/README.md

加做者私人微信,做者微信號以下 yclimb,標明 微信支付 可拉入微信支付討論羣與小夥伴一塊兒探討哦,必定要標明 微信支付 哦~

到此本文就結束了,關注公衆號查看更多推送!!!


關注個人公衆號

相關文章
相關標籤/搜索