• 微信支付踩坑


    公司项目需要,需要接入第三方支付,前前后后也搞了很久。虽然微信公众平台文档已经写得很清楚了,但是还是记录一下整个接入的流程。

    openid

    openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同,所以不同的公众号下有不同的openid,),可用于永久标记一个用户,同时也是微信公众号支付的必传参数。

    code

    微信支付,需要用户授权,获取code,通过code获取网页授权,其实是要取得openid,需要注意的是,code只能使用一次,用户每次授权带上的code都是不一样的,并且五分钟内不使用的话会自动过期。

    场景描述

    我们在进行支付时,点击支付按钮,首先会跳转了页面然后再跳转回来,然后弹出框让我们输入密码,支付成功或者支付失败。或者, 没有跳转页面,直接就弹出密码输入框

    逻辑描述

    上面也说了,我们获取code就是为了获取到openid然后利用openid这个必传的参数才能成功唤起支付。

    其实呢,让用户每次跳转URL去获取code,然后传code参数给后台也是可以的。因为后台的逻辑也是获取了code之后再利用code去获取openid,然后进行进一步的操作。现在只是保存openid这一操作,从后台放到了前端。 只要我们前端判断openid存在,我们就不必再跳转URL进行授权重新获取code,可以直接进行支付了。

    所以可以得出下图:

    在这里插入图片描述

    具体代码

    逻辑图写的虽然并不具体,但是很简要。

    接下来我们就走一遍流程

    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=)([wd]+)&{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=)([wd]+)&{0,1}/;
        var statePayInfoReg= /(?:[?|&]*state=)({{1}.+}{1})&{0,1}/;
        var payCodeReg = /(?:[?|&]payCode=)([wd]+)&{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
    }
    
    
    
    

    以上是主要的代码逻辑,也不是特别完整能够直接使用,但是能够得到启发。主要还是要看具体的业务和接口怎么给。有问题欢迎一起交流~

    来源:https://segmentfault.com/a/1190000016200719

  • 相关阅读:
    -_-#【缓存】Content-Type 错误
    ♫【事件】事件处理程序
    -_-#【Node】Express 400 Error: ENOENT, open
    【JS】定位
    【JS】壹零零壹
    洛谷—— P1018 乘积最大
    洛谷—— P1017 进制转换
    洛谷——P1011 车站
    洛谷——P2241 统计方形(数据加强版)
    洛谷——P1548 棋盘问题
  • 原文地址:https://www.cnblogs.com/datiangou/p/10144898.html
Copyright © 2020-2023  润新知