微信支付踩坑

公司項目須要,須要接入第三方支付,前先後後也搞了好久。雖然微信公衆平臺文檔已經寫得很清楚了,可是仍是記錄一下整個接入的流程。php

openid

openid是微信用戶在公衆號appid下的惟一用戶標識(appid不一樣,則獲取到的openid就不一樣,因此不一樣的公衆號下有不一樣的openid,),可用於永久標記一個用戶,同時也是微信公衆號支付的必傳參數。html

code

微信支付,須要用戶受權,獲取code,經過code獲取網頁受權,實際上是要取得openid,須要注意的是,code只能使用一次,用戶每次受權帶上的code都是不同的,而且五分鐘內不使用的話會自動過時。前端

場景描述

咱們在進行支付時,點擊支付按鈕,首先會跳轉了頁面而後再跳轉回來,而後彈出框讓咱們輸入密碼,支付成功或者支付失敗。或者, 沒有跳轉頁面,直接就彈出密碼輸入框api

邏輯描述

上面也說了,咱們獲取code就是爲了獲取到openid而後利用openid這個必傳的參數才能成功喚起支付。瀏覽器

其實呢,讓用戶每次跳轉URL去獲取code,而後傳code參數給後臺也是能夠的。由於後臺的邏輯也是獲取了code以後再利用code去獲取openid,而後進行進一步的操做。如今只是保存openid這一操做,從後臺放到了前端。 只要咱們前端判斷openid存在,咱們就沒必要再跳轉URL進行受權從新獲取code,能夠直接進行支付了。微信

因此能夠得出下圖:
圖片描述app

具體代碼

邏輯圖寫的雖然並不具體,可是很簡要。微信公衆平臺

接下來咱們就走一遍流程異步

1.獲取code

點擊支付,第一次支付確定是不存在openid的,那麼就要跳轉URL獲取code
官方也已經寫得很明白 微信支付網頁受權函數

咱們須要拼接此連接:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

其中 APPID是商戶的APPID,REDIRECT_URI是頁面跳轉後跳回來的URL,state能夠放一些咱們自定義的內容,會帶在REDIRECT_URI上。

例如:我要重定向的地址是https://baidu.com/testIndex.html/?#index
那麼,我要拼接的地址應該是https://open.weixin.qq.com/connect/oauth2/authorize?appi=XXXXX&redirect_uri=https://baidu.com/testIndex.html/?#index&response_type=code&scope=SCOPE&state={name:guo}#wechat_redirect

URL要進行編碼encodeURIComponent,這裏演示我就省略了)

那麼,重點來了,地址根據咱們拼接的地址跳轉過去,實際跳轉回來的地址會變成:
https://baidu.com/testIndex.html?code=XXXX&state={name:guo}/?#index(一樣須要解碼decodeURIComponent)

code=XXXX&state={name:guo}這一坨東西跑到中間去了,那咱們嘗試把它取出來放到後面去
變成這樣子https://baidu.com/testIndex.html/?#index?code=XXXX&state={name:guo}
這樣一來,咱們就能用$route.query取到參數了

//跳轉回來的URL修正,在地址跳轉回來的時候調用進行修正
var payUrlReplace = function() {
    //修正前: http://XXXX/?code=XXX&state=XXX/?#/XXX
    var codeReg = /(?:[?|&]code=)([\w\d]+)&{0,1}/;
    var statePayInfoReg= /(?:[?|&]*state=)({{1}.+}{1})&{0,1}/;
    var url = decodeURIComponent(window.location.href)

    //這裏是爲了確保#前有?
    if (url.indexOf('?#') < 0) {
        url = url.replace('#','?#');
    }
    var urlReg = /\?#(.+)\?+/


    //判斷URL掛載參數
    if(    codeReg.test(url) &&
        statePayInfoReg.test(url) && 
        url.match(statePayInfoReg).length > 0 &&
        url.match(codeReg).length > 0
    ){
        var code = url.match(codeReg)[1]
        var state = url.match(statePayInfoReg)[1]
        url = url.replace(codeReg, '')
        url = url.replace(statePayInfoReg, '')


        if (urlReg.test(url)) {
            url = `${url}&payCode=${code}`
        }else {
            url = `${url}?payCode=${code}`
        }

        url = `${url}&payInfo=${state}`

        //修正後變成 http://XXX/?#/XXXX?payCode=XXX&payInfo=XXX,這裏我換了字段名
        window.location.href = url
    }
}

這樣,payCodepayInfo都能取到了。

2.獲取openid

有了code,就能夠獲取openid了,而後存在本地(這裏咱們的後臺專門寫了傳codeopenid的接口,這是異步操做)
圖片描述
上圖就說明了咱們傳code給後臺後,後臺再去獲取openid, 後臺在傳給咱們前端。

而咱們要使用openid請求後臺接口獲取支付參數就要保證openid必須存在,獲取openid是異步操做,咱們必須保證前後順序。值得注意的是,openid一旦獲取保存在本地,以後支付就再也不須要獲取code了。

//檢查是否使用了微信支付
var payIsUse = function(query,callback){
    //檢查這裏是否攜帶須要支付的參數
    //?payCode=XXX&payInfo=XXX
    
    if(query && query.payCode){
        var code = query.payCode
        if(query.payInfo){
            var payInfo = JSON.parse(query.payInfo)
        }
        //判斷是否本地存在openID 若是存在則取消回調,由pay方法手動喚起支付
        var openID = tokenServer.getOpenId();
        if(openID){
            return false;
        }
        //不存在openID用code去接口請求openID,在觸發回調去支付
        payModel.saveOpenid({
            code:code
        }).then(function(data){
                if( tokenServer.setOpenId(data.data) ){
                    if(callback){
                        //將支付參數傳給回調函數 具體用的時候是傳給pay
                        callback(payInfo)
                    }
                }
            }
        })
    }
}

3.獲取支付參數

後臺接口,傳參得到的。

4.喚起微信支付

https://pay.weixin.qq.com/wik...

//微信JSDK喚起支付
var WeChatJSDKpay = function(config,onSuceess,onError){
    var pay = function(){
        WeixinJSBridge.invoke('getBrandWCPayRequest',{
                "appId":config.appId,
                "timeStamp": config.timeStamp,
                "nonceStr": config.nonceStr,
                "package": config.package,
                "signType": config.signType,
                "paySign": config.paySign
            },function(res){
                if(res.err_msg == "get_brand_wcpay_request:ok"){   
                    //支付成功
                    onSuceess()
                }else{
                      //支付失敗
                      onError('支付失敗')
                }
        });
    }
    if(typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function"){
        pay()
    }else{
        if (document.addEventListener) {
            document.addEventListener("WeixinJSBridgeReady", pay, false);
        } else if (document.attachEvent) {
            document.attachEvent("WeixinJSBridgeReady", pay);
            document.attachEvent("onWeixinJSBridgeReady", pay);
           }
    }
}

支付封裝的函數

/*
*onSuccess 支付成功的回調
*onError 支付失敗的回調
*payInfo 支付參數
*/
var pay = function(onSuceess,onError,payInfo) {
    var codeReg = /(?:[?|&]code=)([\w\d]+)&{0,1}/;
    var statePayInfoReg= /(?:[?|&]*state=)({{1}.+}{1})&{0,1}/;
    var payCodeReg = /(?:[?|&]payCode=)([\w\d]+)&{0,1}/;
    var payInfoReg = /(?:[?|&]*payInfo=)({{1}.+}{1})&{0,1}/;

    var url = decodeURIComponent(window.location.href)

    var openId = tokenServer.getOpenId()
    //openid存在
    if (openId) {
        if (!payInfo){
               return false;
        }
        //整理獲取微信支付參數的傳參
        var defaultParams = {
            type: 1, //微信瀏覽器的標誌
            openId: openId
        }
        
        var _payInfo = tool.extend(defaultParams, payInfo)
        (第三步驟-請求後臺接口獲取支付參數)
        payRequest(_payInfo).then(function(data) {
            modalLoadingServer.unload();
            if(data.appId && data.status === 200){
                //第四步驟-調起微信支付JSDK
                WeChatJSDKpay(data,function(){
                    //微信JSDK支付成功 返回驗證參數-流水號進行驗證
                    onSuceess(data.outTradeNo)
                },onError)
            }else{
                console.error('獲取微信支付參數錯誤', _payInfo)
                onError('獲取微信支付參數錯誤,請稍候再試')
            }
        })
        return false;
    }
    //若是已經存在
    if(codeReg.test(url) && statePayInfoReg.test(url)){
        window.location.reload();
        return false;
    }
    //重複請求的話
    if(payCodeReg.test(url) && payInfoReg.test(url)){
        //處理下url從新請求
        url = url.replace(payCodeReg,'');
        url = url.replace(payInfoReg,'');
        url = encodeURIComponent(url);
    }else{
        //跳轉獲取code 回調url
        url  = encodeURIComponent(window.location.href);
    }
    var statePay = {};
    for(var key in payInfo){
        //微信內支付不存在回調URL
        if(payInfo.hasOwnProperty(key)  && payInfo[key] && key !== 'returnUrl'){
            statePay[key] = payInfo[key]
        }
    }
    var statePay = encodeURIComponent(JSON.stringify(statePay));
    var getCodeUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxd9242c6d1e249d25&redirect_uri='+url+'&response_type=code&scope=snsapi_base&state='+statePay+'&connect_redirect=1#wechat_redirect';
    //獲取`code`的跳轉連接
    window.location.href = getCodeUrl
}

以上是主要的代碼邏輯,也不是特別完整可以直接使用,可是可以獲得啓發。主要仍是要看具體的業務和接口怎麼給。有問題歡迎一塊兒交流~

相關文章
相關標籤/搜索