1. 程式人生 > >支付篇—微信掃碼支付

支付篇—微信掃碼支付

應用場景:
商戶根據商品資訊,生成商品二維碼,使用者通過微信掃一掃功能掃描該二維碼,完成支付。
支付模式:
現在微信掃碼支付支援兩種模式。
模式一需要商戶必須先在公眾平臺後臺設定支付回撥URL。URL實現的功能:接收使用者掃碼後微信支付系統回撥的productid和openid。
模式二與模式一相比,流程更為簡單,不依賴設定的回撥支付URL。商戶後臺系統先呼叫微信支付的統一下單介面,微信後臺系統返回連結引數code_url,商戶後臺系統將code_url值生成二維碼圖片,使用者使用微信客戶端掃碼後發起支付。注意:code_url有效期為2小時,過期後掃碼不能再發起支付。
我使用的模式二,也就是微信的統一下單介面。該模式的業務流程時序圖如下:
這裡寫圖片描述


業務流程說明:
(1)商戶後臺系統根據使用者選購的商品生成訂單。
(2)使用者確認支付後呼叫微信支付【統一下單API】生成預支付交易;
(3)微信支付系統收到請求後生成預支付交易單,並返回交易會話的二維碼連結code_url。
(4)商戶後臺系統根據返回的code_url生成二維碼。
(5)使用者開啟微信“掃一掃”掃描二維碼,微信客戶端將掃碼內容傳送到微信支付系統。
(6)微信支付系統收到客戶端請求,驗證連結有效性後發起使用者支付,要求使用者授權。
(7)使用者在微信客戶端輸入密碼,確認支付後,微信客戶端提交授權。
(8)微信支付系統根據使用者授權完成支付交易。
(9)微信支付系統完成支付交易後給微信客戶端返回交易結果,並將交易結果通過簡訊、微信訊息提示使用者。微信客戶端展示支付交易結果頁面。
(10)微信支付系統通過傳送非同步訊息通知商戶後臺系統支付結果。商戶後臺系統需回覆接收情況,通知微信後臺系統不再發送該單的支付通知。
(11)未收到支付通知的情況,商戶後臺系統呼叫【查詢訂單API】。
(12)商戶確認訂單已支付後給使用者發貨。

整個支付流程精簡下來主要就是2點:
①呼叫介面獲取支付二維碼並顯示出來
②微信回撥介面,通知支付結果
針對這2點我寫了兩個方法,一個是獲取二維碼,一個是提供給微信的支付結果回撥介面。
開發過程中需要用到微信提供的jar包,maven地址如下:

        <dependency>
            <groupId>com.github.wxpay</groupId>
            <artifactId>wxpay-sdk</artifactId>
            <version>
0.0.3</version> </dependency>
/**
 * 該類為配置類,主要是關於公眾號和商戶的一些配置資訊抽離出來
 * @author 
 *
 * 2017年11月17日
 */
public class MyConfig implements WXPayConfig{

    //公眾賬號ID
    public String getAppID() {
        return "wx11bd61834b0d57ef";
    }

    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    //商戶祕鑰
    public String getKey() {
        return "1234567890";
    }

    //商戶號
    public String getMchID() {
        return "1234567890";
    }

}
    /**
     * 獲得微信支付二維碼
     * @param req
     * @param resp
     * @throws IOException
     */
    @RequestMapping(value = "/getWxPayCode")
    public void getWxPayCode(HttpServletRequest req, HttpServletResponse resp)
            throws IOException{
        MyConfig config = new MyConfig();
        WXPay wxpay = new WXPay(config);

        String out_trade_no = DateUtil.dateToStr(new Date(), "yyyyMMddHHmmss");

        Map<String, String> data = new HashMap<String, String>();
        data.put("body", "填寫商品名稱"); //商品描述
        data.put("out_trade_no", out_trade_no); //商戶訂單號,不可重複
        data.put("device_info", "");    //裝置號
        data.put("fee_type", "CNY");    //標價幣種(預設人民幣)
        data.put("total_fee", "1");     //標價金額,單位:分
        data.put("spbill_create_ip", "127.0.0.1");  //終端IP
        data.put("notify_url", "https://www.baidu.com/getWxPayNotify.action");  //通知地址,必須是外網能訪問的地址
        data.put("trade_type", "NATIVE");  // 此處指定為掃碼支付
        data.put("product_id", "12");   //商品ID

        Map<String, String> respnoe = null;
        try {
            respnoe = wxpay.unifiedOrder(data);
            String codeUrl = respnoe.get("code_url");
            System.out.println("返回的二維碼url:" + codeUrl);
        } catch (Exception e) {
            e.printStackTrace();
        }

        resp.setContentType("text/html;charset=UTF-8");
        OutputStream os = resp.getOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(os);
        oos.writeObject(respnoe.toString());
        oos.flush();
        oos.close();
        os.close();
    }

這樣就完成了微信支付的二維碼請求。在進行請求的時候,微信是以xml格式的資料進行的請求,不過微信已經在提供的jar包裡進行了封裝,所以我們就不必再進行資料封裝了。
商戶後臺將該二維碼地址返回給手機端,手機端通過zxing工具包,將該地址轉換成二維碼供使用者掃碼支付。當用戶掃碼支付完成後,微信後臺會回撥我們提供給微信後臺的介面,就是剛才我們設定的notify_url引數,這裡一定要記得是外網能夠訪問到的地址!
下面是處理微信回撥的方法:

/**
     * 獲得微信支付通知回撥結果
     * @param req
     * @param resp
     * @throws Exception 
     * @throws IOException
     */
    @RequestMapping(value = "/getWxPayNotify")
    public void getWxPayNotify(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        String notifyData = "";
        try {
            InputStream is = req.getInputStream();
            StringBuffer sb = new StringBuffer();  
            String s;
            BufferedReader in = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            while ((s = in.readLine()) != null){  
                sb.append(s);  
            }  
            in.close();  
            is.close();

            notifyData = sb.toString();
            MyConfig config = new MyConfig();
            WXPay wxpay = new WXPay(config);
            Map<String, String> notifyMap = WXPayUtil.xmlToMap(notifyData);  // 轉換成map
            if (wxpay.isPayResultNotifySignatureValid(notifyMap)) {
                logger.info("支付成功");
                // 簽名正確
                // 進行處理。
                // 注意特殊情況:訂單已經退款,但收到了支付結果成功的通知,不應把商戶側訂單狀態從退款改成支付成功
            }
            else {
                // 簽名錯誤,如果資料裡沒有sign欄位,也認為是簽名錯誤
                logger.error("支付失敗");
            }

            logger.info("微信支付返回的通知為:" + notifyMap);

            resp.setContentType("text/html;charset=UTF-8");
            OutputStream os = resp.getOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(os);
            oos.writeObject(notifyData);
            oos.flush();
            oos.close();
            os.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

具體的處理方法這裡我就不寫了,微信回撥的時候需要驗證簽名是否正確,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止資料洩漏導致出現“假通知”,造成資金損失。另外當微信回撥我們的介面的時候,我們需要給微信後臺返回應答,已經接收到回撥,不然微信將會重新發起通知(通知頻率為15/15/30/180/1800/1800/1800/1800/3600,單位:秒)

另外當客戶端一定時間內沒有接收到微信後臺的回撥時,也可以主動查詢訂單狀態,跟統一下單差不多,只是把下單改為查詢

Map<String, String> resp = wxpay.orderQuery(data);

如需轉載,請註明出處!