基于vue的h5项目之支付宝支付与微信支付
[ 2018/08/05, Vue , 17227阅, 2评 ]

本文仅记录基于vue开发h5项目过程中使用支付宝和微信支付过程中的重点与槽点,仅为前端部分,如有疏漏不正之处,请于文末评论探讨。注意:标红部分灰常重要,仔细阅读官方文档非常重要,耐心非常重要,细心非常重要。

一、支付宝h5支付

支付宝h5支付操作起来是超级简单的,前端关键代码如下:

this.$http.getTradeNo(lastParams).then(res=>{
    this.$http.doPayAlipay({
        oid: res.data, // 取到的交易订单号
        url: yourBackUrl // 成功后的回调地址
    }).then(resAlipay=>{
        // 避免时间间隙造成的用户误操作,尽管拿到数据了仍然显示loading...直到跳转到支付宝的提供的页面
        this.$toast.loading({
            mask: true,
            message: '加载中...'
        });
        // 取回来的是支付宝提供的一段自执行的form表单代码
        // 这里我把这段代码插入页面中,并手动触发
        const div = document.createElement('div');
        div.innerHTML = resAlipay.data.form;
        document.body.appendChild(div);
        document.forms[0].submit();
    })
})

由于安卓APP也调用了h5的支付页面,所以为了避免一些“尴尬的情况”,我们采用了支付宝官方提供的“手机网站支付转APP支付”方案,非常贴心的方案呢。

二、微信支付

微信的wap端支付分两种,一种是微信内的公众号支付,一种是微信外的H5支付。

首先:只有通过微信认证的服务号才具有支付接口的权限。

微信支付接口开通条件.jpg

其次:设置正确的支付目录。确保实际支付时的请求目录与后台配置的目录一致,否则将无法成功唤起微信支付。在微信商户平台(pay.weixin.qq.com)设置您的公众号支付支付目录,设置路径:商户平台-->产品中心-->开发配置,如下图所示。公众号支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。

然后:设置授权域名。开发公众号支付时,在统一下单接口中要求必传用户openid,而获取openid则需要您在公众平台设置获取openid的域名,只有被设置过的域名才是一个有效的获取openid的域名,否则将获取失败。

ps:iOS版微信和安卓版微信在发起微信支付时,两者被获取到的发起页面链接不一致:安卓正常;iOS版被获取到的是你进入该应用时的着陆页面链接,从而调用支付失败。在“不能修改支付授权目录、不能修改路由结构”等可操作权限极低的情况下,可以在授权的路由下使用window.location.reload()来修正着陆页链接(如果有附带影响,可以尝试使用return阻断多余操作,或将reload放到setTimeout中)。

1、公众号支付

公众号支付是用户在微信中打开商户的H5页面,商户在H5页面通过调用微信支付提供的JSAPI接口调起微信支付模块完成支付。

公众号支付开发者文档https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6

openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户,同时也是微信公众号支付的必传参数。网页授权获取用户openid接口文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842,要获取openid的第一步便是需要用户同意授权,获取code,所以需要前端来处理,官方文档描述如下:

在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开页面:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect(若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限)尤其注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问;跳转回调redirect_uri,应当(必须)使用https链接来确保授权code的安全性。

参数 是否必须 说明
appid 公众号的唯一标识
redirect_uri 授权后重定向的回调链接地址, 请使用 urlEncode 对链接进行处理
response_type 返回类型,请填写code
scope 应用授权作用域,snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
state 重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
#wechat_redirect 无论直接打开还是做页面302重定向时候,必须带此参数

如果用户同意授权,页面将跳转至redirect_uri/?code=CODE&state=STATE

我们可以把获取code的那个链接直接绑定到公众号的菜单上,只要用户点击菜单,就能跳到我们的应用url并且带上了code和state,那么我们就可以把这个code传给后端然后拿到openid存储到本地,最后需要用到时的时候再传过去就中了。基于此,我把获取openid的接口逻辑直接写到了路由的beforeEach钩子中(因为来路页面我并不能掌控)

// ...
router.beforeEach(function (to, from, next) {
    // ...
    if( checkClient.wechat() && !storage.get('openId') ){
        if( to.query.code && to.query.state=='eqifei' ){
            getOpenId({
                code: to.query.code
            }).then(res=>{
                if(res.code==0){
                    storage.set('openId',res.data)
                }
            })
        }else{
            window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?'+
                'appid=服务号的appid&'+
                'redirect_uri=授权后重定向的回调链接地址&'+
                'response_type=code&scope=snsapi_base&state=eqifei#wechat_redirect'
        }
    }
    // ...
})
// ...

发起支付的关键代码如下:

methods:{
    doPaySubmit(){
        // ...
        this.$http.doWechatPay({
            oid: '...',
            trade_type: 'JSAPI',
            openid: this.$tools.storage.get('openId')
        }).then(resWechat=>{
            this.weixinPay(resWechat.data)
        })
        // ...
    },
    weixinPay: function(data) {
        var vm = this;
        if (typeof WeixinJSBridge == "undefined") {
            if (document.addEventListener) {
                document.addEventListener("WeixinJSBridgeReady",vm.onBridgeReady(data),false);
            } else if (document.attachEvent) {
                document.attachEvent("WeixinJSBridgeReady",vm.onBridgeReady(data));
                document.attachEvent("onWeixinJSBridgeReady",vm.onBridgeReady(data));
            }
        } else {
            vm.onBridgeReady(data);
        }
    },
    onBridgeReady: function(data){
        var vm = this;
        WeixinJSBridge.invoke(
            "getBrandWCPayRequest",
            {
                appId: data.appid, //公众号名称,由商户传入
                timeStamp: data.timestamp, //时间戳,自1970年以来的秒数
                nonceStr: data.nonce_str, //随机串
                package: data.prepay_id, //订单详情扩展字符串
                signType: data.signType, //微信签名方式:
                paySign: data.paySign, //微信签名
            },
            function(res) {
                if(res.err_msg == "get_brand_wcpay_request:ok"){
                    // ...
                }else{
                    alert("支付失败!");
                }
            }
        );
    }
}

2、H5支付

H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前述页面确认使用微信支付时,商户发起本服务呼起微信客户端进行支付。主要用于触屏版的手机浏览器请求微信支付的场景。可以方便的从外部浏览器唤起微信支付。注意:H5支付不建议在APP端使用,如需要在APP中使用微信支付,请接APP支付。

H5支付开发文档https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4

微信外的h5支付需要前端做的就简单多了,关键代码如下:

doPaySubmit(){
    // ...
    this.$http.doWechatPay({
        oid: '',
        trade_type: 'MWEB',
    }).then(resWechat=>{
        window.location.replace(resWechat.data.mweb_url+'&redirect_url='+encodeURIComponent(window.location.href+'&tip=yes'))
    })
    // ...
}

注意有坑:

1、需对redirect_url进行urlencode处理

2、由于设置redirect_url后,回跳指定页面的操作可能发生在:a.微信支付中间页调起微信收银台后超过5秒 b.用户点击“取消支付“或支付完成后点“完成”按钮。因此无法保证页面回跳时,支付流程已结束,所以商户设置的redirect_url地址不能自动执行查单操作,应让用户去点击按钮触发查单操作。

三、相关文档汇总

有朋自远方来...评论一下呗O(∩_∩)O

  • 评论(2)

    That year 1997 [ 回复 ]
    2019-07-25 13:37

    看看

    hl [ 回复 ]
    2019-03-20 09:14

    不错