【支付方式】
我开发的项目中只对接了最基础的三项,微信h5,支付宝h5,以及由于微信内打开链接不可用微信h5支付而采取的微信公众号支付
【准备】
1. 申请微信公众号,配置域名并拿到appid
2. 了解微信获取code和openid的方式,和后端商量获取方案
这里我们采取的方案是,前端在点击支付按钮时获取code传递给后端,由后端去获取openid。
之所以在点击支付时才获取是因为code有五分钟的时效性,并且我这个项目的后端老师说他没有表可以让我在进入时获取完就存储一下code和openid,如果能够在进入页面时就获取code传递给后端来获取openid存起来的话,我觉得会更好。
【微信code】
微信code和openid是采取微信公众号支付所需要的信息,这里的微信公众号支付其实就是把h5挂在微信公众号下来获得拉起微信支付的能力
获取code其实就是利用window.location.href跳转一个按照微信的规则拼接的链接,然后微信页面一闪会自己跳回来,并在url后面拼接上我们需要的code
【支付流程】
1.判断环境
根据 navigator.userAgent.toLowerCase() 来判断当前h5所处的环境,我这里区分了微信内(包含企业微信)和钉钉内,因为微信内需要获取code采取jsapi支付,并且无法跳转支付宝,而钉钉内无法跳转微信支付,需要做相应的选项屏蔽并选择当前可选的支付方式。
let ua = navigator.userAgent.toLowerCase(); if (ua.match(/MicroMessenger/i) == "micromessenger") { this.weixinInner = true; //是微信内环境 this.dingInner = false; //不是钉钉内环境 this.radio = "1"; //支付radio选择微信 } else if (ua.match(/dingtalk/i) == "dingtalk") { this.weixinInner = false; this.dingInner = true; this.radio = "2"; }
micromessenger表示在微信内,dingtalk表示在钉钉内,如果需要适配更多环境,可以打印一下ua自己针对关键字做处理
2.判断是否是刚刚获取code回来
因为我是在点击支付的时候才获取的code,所以会导致进入页面时有两种情况,一种是刚刚获取完code回来应该直接继续支付了,一种是单纯的进入页面
if (urlHerf.indexOf("code") >= 0) { this.code = getParam("code"); //获取url里的code ... this.goPay(); //调起支付函数 } else ...
3.点击支付按钮时
首先,对按钮点击做节流处理
onSubmit() { if (this.stopClick) { return; } this.stopClick = true; setTimeout(() => { this.stopClick = false; }, 3000); //节流时间3s if (页面内一些是否可以发起支付的判断) { ...... } else { this.goPay(); } }
支付:
1. 如果是微信公众号支付且还没有code,去获取code
if ( ua.match(/MicroMessenger/i) == "micromessenger" && urlHerf.indexOf("code") < 0 ) { this.weixinInner = true; let urlCurrent = this.urlencode(urlHerf); let url_code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=你们的appid填这里&redirect_uri=" + urlCurrent + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; window.location.href = url_code; }
这里拼接的redirect_uri需要是转码后的,我用的转码函数也放在这里
urlencode(str) { str = (str + "").toString(); return encodeURIComponent(str) .replace(/!/g, "%21") .replace(/‘/g, "%27") .replace(/\(/g, "%28") .replace(/\)/g, "%29") .replace(/\*/g, "%2A") .replace(/%20/g, "+"); },
我们这里采取的支付是把支付方式对应的参数和需要的信息发给后端,由后端返回不同的extra数据来拉起支付,为了防止各种不同的手机或者用户操作过程的一些不可控跳转,这里还存储了this,避免拿不到this
如果采用的是支付宝h5,会返回一个表单,通过提交表单来调起支付宝支付
const form = res.data.extra.body; const div = document.createElement("div"); div.id = "alipay"; div.innerHTML = form; document.body.appendChild(div); document.querySelector("#alipay").children[0].submit();
如果采用的是微信h5支付,跳转后端提供的链接,可以拼上自己需要跳回来的页面
window.location.href = res.data.extra.mweb_url + "&redirect_url=" + _this.urlencode(window.location.href);
如果是采用微信支付,需要发起WeixinJSBridge进行支付
1.根据WeixinJSBridge不同状态来发起jsapi支付函数onBridgeReady
if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener( "WeixinJSBridgeReady", _this.onBridgeReady, false ); } else if (document.attachEvent) { document.attachEvent( "WeixinJSBridgeReady", _this.onBridgeReady ); document.attachEvent( "onWeixinJSBridgeReady", _this.onBridgeReady ); } } else { _this.onBridgeReady(); }
jsapi支付函数,这里一定要存储一下this,我们在测试环境没遇到问题,但是上线后this出现了丢了的情况
这里在微信jsapi文档下是采用 WeixinJSBridge.invoke 来调用支付的,但是vue下会报错没有WeixinJSBridge这个对象,挂载在window下就不会报错了
参考:微信jsapi文档 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
onBridgeReady() { const _this = this; //存储this
// 下面这些参数都是通过后端传回来的 window.WeixinJSBridge.invoke( "getBrandWCPayRequest", { appId: "...", timeStamp: this.extra.timeStamp, nonceStr: this.extra.nonceStr, package: this.extra.package, signType: this.extra.signType, paySign: this.extra.paySign }, function(res) {
//支付成功 if (res.err_msg == "get_brand_wcpay_request:ok") { 。。。 } else { 。。。 Toast("取消支付"); } } ); },
完整函数
goPay() { let ua = navigator.userAgent.toLowerCase(); let urlHerf = window.location.href; // 1001 微信H5(非微信app内环境下) // 1003 支付宝H5(非微信app内环境下) // 1201 微信公众号支付 if ( ua.match(/MicroMessenger/i) == "micromessenger" && urlHerf.indexOf("code") < 0 ) { this.weixinInner = true; let urlCurrent = this.urlencode(urlHerf); let url_code = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=。。。&redirect_uri=" + urlCurrent + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; window.location.href = url_code; } else { if (ua.match(/MicroMessenger/i) == "micromessenger") { this.method = "1201"; } else { this.method = this.radio == 1 ? "1001" : "1003"; } let _this = this; this.$store.dispatch("dealPay", { data: { href: window.location.href, payTool: this.method, code: this.code }, callback: res => { if (res == 0) { // 鉴权失败 _this.reStart(); } else { // 正常支付 。。。 _this.extra = res.data.extra; if (_this.method == "1201") { if (typeof WeixinJSBridge == "undefined") { if (document.addEventListener) { document.addEventListener( "WeixinJSBridgeReady", _this.onBridgeReady, false ); } else if (document.attachEvent) { document.attachEvent( "WeixinJSBridgeReady", _this.onBridgeReady ); document.attachEvent( "onWeixinJSBridgeReady", _this.onBridgeReady ); } } else { _this.onBridgeReady(); } } else if (_this.method == "1001") { window.location.href = res.data.extra.mweb_url + "&redirect_url=" + _this.urlencode(window.location.href); } else if (_this.method == "1003") { console.log(1003); const form = res.data.extra.body; const div = document.createElement("div"); div.id = "alipay"; div.innerHTML = form; document.body.appendChild(div); document.querySelector("#alipay").children[0].submit(); } } } }); } },
会出现的问题:
1. 企业微信内打开链接会被判定为微信环境,在获取code的时候会跳转到微信内继续支付,但是此时微信内浏览器并没有我们支付所需的订单号一类信息
解决办法:在app.vue的beforeMount阶段判断到是企业微信内就提示用户跳转到微信内,但由于微信风控,这种情况可以正常浏览页面但无法进行支付
beforeMount() { let ua = window.navigator.userAgent.toLowerCase(); if ( ua.match(/MicroMessenger/i) == "micromessenger" && ua.match(/wxwork/i) == "wxwork" ) { this.isWXwork = true; let currentHref = window.location.href; let currentUrl = this.urlencode(currentHref); let goWXurl = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=。。。&redirect_uri=" + currentUrl + "&response_type=code&scope=snsapi_base&state=1#wechat_redirect"; window.location.href = goWXurl; } }
2.ios手机使用chrome或者UC等非safari浏览器进行支付,支付完成后会跳转到safari,从而无法监控到支付完成