2020微信支付v3版本對接詳細流程

打工人1993發表於2020-11-11

都0202年,我似乎翻遍了百度,都沒找到最新版微信支付v3的對接相關的詳細部落格,我都納悶了,只有自己摸索。還有就是竟然還有人用一些v3對接的假程式碼,來騙積分,我真的服了,感同身受,以下是我對接的過程,免費分享給大家,歡迎小夥伴一起探討。

~首先吐槽下騰訊的文件,自己根據文件看,對於沒有對接經驗的來說,根本看不懂,什麼亂起八糟的,心裡一萬個草泥馬。
其次,特別是對接的資料加密解密,傳遞格式那些是最讓人想瘋的東西。所以已經有大佬把這些基礎的資料對接做了整合,就在gitee上,ijPay。ijPay我們只需要關注的只有給物件設定引數,發起請求,處理響應資料,就完事,很方便。此篇文章就基於此展開對接的講解。

1.gitee開源支付對接原始碼(ijpay)地址
2.ijpay官方文件地址

ps:ijpay中可以自己讀程式碼,再根據騰訊的文件,摸索(ijpay註釋較少).也可以花錢讓ijpay的作者給你線上幫助

整體對接流程概括如下

  1. 肯定是先下載ijpay原始碼到本地
    ijpay整合了許多支付,這裡我們只講解微信支付v3的對接,那麼我自己是另外新建了一個springboot專案,然後把原始碼裡面的微信v3支付的程式碼拷貝到新專案裡面做測試的,缺什麼依賴,根據報紅的提示,自己引入,這裡不做詳細說明.
    在這裡插入圖片描述

2.【 微信支付v3版本證照下載】和【配置配置檔案】

這裡先說下公眾號和商戶平臺的關係,公眾號的支付依附於商戶平臺,所以公眾號和商戶平臺要做關聯處理:
登陸商戶平臺–>產品中心–>AppID賬號管理
關聯過程,自行百度咯,不做過多講解

1).證照的下載
登陸商戶平臺–>賬戶中心–>api安全–>API安全
然後生成證照,最終會生成3個檔案
在這裡插入圖片描述
生成流程:
自行檢視官方文件

2).證照copy到【新專案】的資料夾中
我這邊是放在了src\main\resources\cert目錄下
在這裡插入圖片描述

3).設定api祕鑰和apiv3祕鑰
登陸商戶平臺–>賬戶中心–>api安全–>設定api祕鑰/設定apiv3祕鑰
儲存好,後面要用到

4).設定配置檔案 wxpay_v3.properties
在這裡插入圖片描述
在這裡插入圖片描述
為了方便你們copy

#服務商/直連商戶平臺 關聯的 公眾號appid
v3.appId=?
#祕鑰
v3.keyPath=?
#CA證照 格式.pem
v3.certPath=?
#CA證照 格式.p12
v3.certP12Path=?
#平臺證照路徑
v3.platformCertPath=?
#服務商id/商戶id
v3.mchId=?
#自定義 apiv3 祕鑰
v3.apiKey3=?
#自定義 api 祕鑰
v3.apiKey=?
#專案域名
v3.domain=?

ps:這裡講下配置檔案的引數如果獲取
appId:登陸微信公眾平臺–>開發–>基本配置–>開發者ID(AppID)
keyPath: 對應apiclient_key.pem所在路徑
certPath: 對應apiclient_cert.pem所在路徑
certP12Path: 對應apiclient_cert.p12所在路徑
platformCertPath: 【平臺證照】訪問v3支付提供的介面獲取,下面會講
mchId: 登陸商戶平臺–>賬戶中心–>商戶資訊–>微信支付商戶號
apiKey3: 參考上面的設定api祕鑰和apiv3祕鑰
apiKey: 參考上面的設定api祕鑰和apiv3祕鑰
domain: 專案域名
關於專案域名,我這邊用的natapp做的本地內網對映,可以直接在本地做支付測試,因為natapp代理的域名都是備案了的,非常方便,這裡推薦下,不然去伺服器上測試,太麻煩了.
natapp官方連結地址 自己看natapp的文件或者幫助,這裡不做過多講解

5).獲取平臺證照,也就是上圖的platformCert.pem檔案
啟動服務,本地訪問介面: localhost/v3/get
這裡會請求騰訊介面,拿到平臺證照,並儲存到配置檔案所配置的路徑下(注意檔名在配置檔案一開始就要配好)
配置檔案到這裡就配好了

  1. 支付對接(直連商戶模式)

ps:v3微信支付官方文件
基礎支付–>【直連模式】和【服務商模式】的區別?
1.介面對接的角度來說,就訪問的地址不同,和傳遞的引數有差別,實現的效果是一樣的,響應的引數的處理方式是一樣的
2.從現實邏輯來講,
直連模式是公眾號直接對接商戶平臺,發起支付,
關係為: 公眾號–>商戶平臺
服務商模式是基於直連,商戶平臺又把支付授權給服務商,
關係為: 公眾號–>商戶平臺–>服務商
用服務商模式,貌似有返點啥的,沒有深入研究,有興趣自行百度,兩者對接方式差不多,只是傳遞的引數有些許差別.但相應引數的處理是一樣的,此篇部落格只講直連方式,服務商模式可以自行舉一反三.

一.電腦生成二維碼,手機掃碼支付(nativePay)

用大佬的寫好的程式碼,根本不用關心什麼加密解密什麼的,配置檔案配好,調介面就完事了QAQ
不同的支付的應用場景:
1.nativePay(電腦生成二維碼,手機掃碼支付)
1.jsApiPay(微信自帶瀏覽器中或者說公眾號裡面,喚起微信支付)
1.h5Pay(手機普通瀏覽器中,喚起微信支付)
注意:
1.傳遞引數根據官方文件來看,ijpay原始碼可能在服務商和直連商戶兩種模式的程式碼只提供了其一,靈活斟酌
2.登陸商戶平臺–>產品中心–>我的產品–>開通nativePay
其它的支付看需要開通,具體操作,百度啊QAQ,後面就不提示開通支付這個事情了,自己可以先提前開通了都
大概流程:
請求介面,拿到二維碼生成連結–>用生成二維碼的js生成支付碼–>掃碼支付

官方文件:
在這裡插入圖片描述
在這裡插入圖片描述
響應引數示例

{	
"code_url": "weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00"
}

1).發起支付請求,獲取二維碼連結地址
請求介面(com.example.wxpay.controller.wxpay.WxPayV3Controller#nativePay):

http://localhost/v3/nativePay

2).響應引數

{	
"code_url": "weixin://wxpay/bizpayurl/up?pr=NwY5Mz9&groupid=00"
}

3).生成二維碼(qrcode.min.js)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
    <title>Javascript 二維碼生成庫:QRCode</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
    <script type="text/javascript" src="//cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
    <script type="text/javascript" src="//static.runoob.com/assets/qrcode/qrcode.min.js"></script>
</head>
<body>
<input id="text" type="text" value="http://www.runoob.com" style="width:80%" /><br />
<div id="qrcode" style="width:100px; height:100px; margin-top:15px;"></div>

<script type="text/javascript">
    var qrcode = new QRCode(document.getElementById("qrcode"), {
        width : 100,
        height : 100
    });

    function makeCode () {
        var elText = document.getElementById("text");

        if (!elText.value) {
            alert("Input a text");
            elText.focus();
            return;
        }

        qrcode.makeCode(elText.value);
    }

    makeCode();

    $("#text").
    on("blur", function () {
        makeCode();
    }).
    on("keydown", function (e) {
        if (e.keyCode == 13) {
            makeCode();
        }
    });
</script>
</body>
</html>

在這裡插入圖片描述
支付成功後會有一個回撥通知,在一開始傳遞的引數裡面
在這裡插入圖片描述
ijpay裡面也是寫好了的
在這裡插入圖片描述
通知的對接自行看ijpay列印的引數,做自己的邏輯處理
com.example.wxpay.controller.wxpay.WxPayV3Controller#payNotify

二.微信自帶瀏覽器中或者說公眾號裡面,喚起微信支付(jsApiPay)

注意:
配置jsApiPay的支付目錄,我配置的 本地對映的代理域名+’/’
登陸直連商戶平臺–>產品中心–>開發配置–>支付配置–>JSAPI支付

大概流程:
拿到微信使用者的openId–>呼叫ijpay介面(傳入openId)–>響應 喚起微信支付的json資料–>基於響應json,前端js二次請求騰訊介面–>喚起支付

官方文件(jsApiPay下單)
在這裡插入圖片描述
在這裡插入圖片描述
官方文件(jsApiPay喚起支付)
在這裡插入圖片描述
在這裡插入圖片描述
1).拿到微信使用者的openId

參考自部落格:java-微信公眾號選單跳轉網頁獲取openid

就拿openId這一步就挺麻煩
大概流程:
公眾號選單點選–>自定義請求介面1(請求騰訊拿到code)–>重定向自到定義介面2(根據code請求騰訊拿到openId)–>重定向到自定義html頁面,拿到微信使用者openId,初始化呼叫上述介面…

直接上自定義介面的程式碼

WxGZHController.java

package com.example.wxpay.controller.wxpay;

import com.alibaba.fastjson.JSONObject;
import com.example.wxpay.domain.WxPayV3Bean;
import com.ijpay.core.kit.HttpKit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;

/**
 * @ClassName WxGZHController
 * 微信公眾號對接
 * @Author ZhangYong
 * @Date 2020/11/10 15:46
 * @Version 1.0
 **/
@RequestMapping("/wxgzh")
@Controller
public class WxGZHController {

    private static final Logger log = LoggerFactory.getLogger(WxGZHController.class);

    @Resource
    WxPayV3Bean wxPayV3Bean;

    private static final String serverSuffixUrl = "/wxgzh/weixinoauth";//查詢到code後重定向的目錄
    private static final String stateCashout = "cashOut";
    private static final String weixinGzhSecret = "785yuiyddsc76f115cd3fa86746";//開發者密碼(AppSecret)
    private static final String jsApiPayUrl = "/jsApiPay.html";//使用openId的html頁面


    /*獲取微信瀏覽器使用者openId,並跳轉頁面傳遞openId*/
    //1.先查詢code
    @RequestMapping("/redirecttocashout")
    public String redirectToCashout() {
        log.info("準備獲取code");
        return "redirect:https://open.weixin.qq.com/connect/oauth2/authorize?appid="
                + wxPayV3Bean.getAppId() + "&redirect_uri=" + wxPayV3Bean.getDomain()
                +"/"+serverSuffixUrl+"?response_type=code&scope=snsapi_base&state=" + stateCashout + "#wechat_redirect";
    }

    //2.根據code獲取openId
    @RequestMapping("/weixinoauth")
    public String weixinOauth(@RequestParam String code,@RequestParam String state) throws Exception {
        log.info("獲取code:{}",code);
        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="
                + wxPayV3Bean.getAppId() + "&secret=" + weixinGzhSecret + "&code=" + code + "&grant_type=authorization_code";
        String res = HttpKit.getDelegate().get(url, null);
        System.out.println(res);
        String openid = JSONObject.parseObject(res).getString("openid");
        log.info("根據code查詢得到openId:{}",openid);
        String redirect = "";
        switch (state){
            case stateCashout:
                redirect =jsApiPayUrl + "?openId=" + openid;
                break;
        }
        log.info("準備調起jsApi支付,url:{}",redirect);
        return "redirect:" + redirect;//重定向到jsApiPay.html並傳遞openId
    }


}

jsApiPay.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>jsApi支付測試</title>
    <script src="./js/jquery-3.3.1.min.js"></script>
    <script src="./js/vue.min.js"></script>
    <script src="./js/http.js"></script>
</head>
<body>

<script>

    new Vue({
        el:'app',
        data(){
            return {
                callPayParam:{
                    appId:'',
                    timeStamp:'',
                    nonceStr:'',
                    package:'',
                    signType:'RSA',
                    paySign:'',
                }
            }
        },
        methods:{
            prePay(openId){
                http.get('/v3/jsApiPay?openId='+openId,{},res=>{
                    console.log(res)
                    //alert(res)
                    this.callPayParam = JSON.parse(res)
                    this.onBridgeReady()//喚起支付
                })
            },
            onBridgeReady() {
                alert(this.callPayParam)
                alert(this.callPayParam.appId)
                WeixinJSBridge.invoke('getBrandWCPayRequest', {
                        "appId": this.callPayParam.appId,//公眾號名稱,由商戶傳入
                        "timeStamp": this.callPayParam.timeStamp,//時間戳,自1970年以來的秒數
                        "nonceStr": this.callPayParam.nonceStr,//隨機串
                        "package": this.callPayParam.package,//預支付返回資料
                        "signType": this.callPayParam.signType,//微信簽名方式:
                        "paySign":this.callPayParam.paySign //微信簽名
                    },
                    function(res) {
                        if (res.err_msg == "get_brand_wcpay_request:ok") {
                            // 使用以上方式判斷前端返回,微信團隊鄭重提示:
                            //res.err_msg將在使用者支付成功後返回ok,但並不保證它絕對可靠。
                        }
                    });
            }
        },
        mounted() {
            let openId = getQueryVariable('openId')
            console.log('openId:'+openId)
            this.prePay(openId)
        }
    })
    function onBridgeReady() {
        WeixinJSBridge.invoke('getBrandWCPayRequest', {
                "appId": "wx2421b1c4370ec43b",
                //公眾號名稱,由商戶傳入
                "timeStamp": "1395712654",
                //時間戳,自1970年以來的秒數
                "nonceStr": "e61463f8efa94090b1f366cccfbbb444",
                //隨機串
                "package": "prepay_id=up_wx21201855730335ac86f8c43d1889123400",
                "signType": "RSA",
                //微信簽名方式:
                "paySign": "oR9d8PuhnIc+YZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/X+QBhcCYL21N7cHCTUxbQ+EAt6Uy+lwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuT+Cdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZV+JSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8Fu+LozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2+AehHvz+n64GDmXxbX++IOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg==" //微信簽名
            },
            function(res) {
                if (res.err_msg == "get_brand_wcpay_request:ok") {
                    // 使用以上方式判斷前端返回,微信團隊鄭重提示:
                    //res.err_msg將在使用者支付成功後返回ok,但並不保證它絕對可靠。
                }
            });
    }
</script>
</body>
</html>

公眾號選單配置

在這裡插入圖片描述
請求的介面為
http://域名/wxgzh/redirecttocashout
對應控制器:com.example.wxpay.controller.wxpay.WxGZHController#redirectToCashout

開發者密碼(AppSecret)
公眾號後臺–>開發–>基本配置–>開發者密碼(AppSecret)

公眾號網頁授權設定
參考上述的參考部落格↑↑

通知處理同上

三.手機普通瀏覽器,喚起微信支付(h5Pay)

後續補全

ps:以上皆為自己的對接經驗,有理解的不夠深刻的地方,多多包涵.如果部落格還有不詳細或者錯誤的地方,歡迎評論告訴我

相關文章