【支付方式】
我開發的項目中只對接了最基礎的三項,微信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,從而無法監控到支付完成