從前端的(de)角度來(lái)梳理微信支付(小程序、H5、JSAPI)的(de)流程
發表時(shí)間:2021-1-5
發布人(rén):融晨科技
浏覽次數:152
因業務需要(yào / yāo),開發微信支付功能,涉及三種支付方式:
- JSAPI 支付:微信内網頁支付,需要(yào / yāo)開通微信服務号
- 小程序支付:在(zài)小程序中支付,需要(yào / yāo)開通小程序
- H5 支付:在(zài)手機浏覽器(出(chū)微信内網爺)中網頁支付
使用微信支付的(de)前提必開通微信商戶号,要(yào / yāo)使用到(dào)那種的(de)支付方式要(yào / yāo)前需在(zài)商戶平台開通(要(yào / yāo)審核)。
支付的(de)錢最終都會到(dào)商戶号裏(一般由公司财務開通)。
開發微信支付的(de)過程中大(dà)大(dà)小小坑還是(shì)踩了(le/liǎo)不(bù)少,終于(yú)做完了(le/liǎo),整理下開發流程。
參考:
- 微信支付-接入指引
- 微信支付-開發文檔
小程序支付
開發流程
- 小程序端請求創建訂單接口,後端統一下單獲取
orderId
并返回 - 小程序端獲取通過wx.login()獲取
code
- 小程序端拿這(zhè)
code
和(hé / huò)orderId
請求後端接口,獲取支付所需數據 - 獲取支付所需數據之(zhī)後,小程序端調用wx.requestPayment()接口,直接調用起支付頁面
- 判斷是(shì)否支付成功後的(de)邏輯
僞代碼
async function wxPay(goodId) {
// 1. 創建訂單 獲取orderId
let orderId = await ajax("POST", "/api/OrderProgram/CreateTheOrder", {
goodId, // 商品id
});
// 2. 獲得 code
let code = await wxlogin(); // 基于(yú)pr封裝的(de)wx.login()方法
// 3. 獲取支付的(de)數據
let payData = http://www.wxapp-union.com/await ajax("POST", "/api/OrderProgram/WxXcxPay", {
orderId,
code,
});
// 4. 發起支付
let res = await payment(payData); // 基于(yú)pr封裝的(de)wx.requestPayment()方法
// 5. 判斷是(shì)否支付成功
let payResult = res.errMsg;
if (payResult == "requestPayment:ok") {
console.log("支付成功");
} else if (payResult == "requestPayment:fail cancel") {
console.log("用戶取消支付");
} else {
console.log("支付失敗");
}
}
注意事項
- 申請微信小程序賬号
申請成功可拿到(dào) AppID(小程序 id)和(hé / huò) AppSecret(小程序密鑰)
申請類型爲(wéi / wèi)企業性質,否則無法接入微信支付 - 微信小程序認證
通過認證的(de)小程序才能接入微信支付和(hé / huò)綁定商戶平台 - 申請商戶平台賬号
需要(yào / yāo)第一步申請的(de) AppID
申請成功可拿到(dào) MchID(商戶 id)和(hé / huò) MchKey(商戶密鑰) - 信小程序關聯商戶号
微信和(hé / huò)商戶都認證成功後,在(zài)微信後台微信支付菜單中進行關聯 - 接入微信支付
在(zài)微信後台微信支付菜單中進行接入
參考
- 小程序支付文檔
- 小程序開發文檔
H5 支付
開發流程
- 前端端請求創建訂單接口,後端統一下單獲取
orderId
并返回 - 前端帶着
orderId
請求支付接口,獲得mweb_url
, - 然後跳轉
mweb_url
會跳轉微信自動調用微信支付 - 支付後返回支付頁,判斷是(shì)否支付成功(需發送請求後端查詢)
4.1 刷新頁面,獲取最新的(de)支付(訂單)狀态。
4.2 設置一個(gè)的(de)按鈕"我已支付",讓用戶點擊自動查詢狀态。
僞代碼
async function wxH5Pay(goodId) {
// 1. 創建訂單 獲取orderId
let orderId = await ajax("POST", "/api/OrderProgram/CreateTheOrder", {
goodId, // 商品id
});
// 2. 獲取支付跳轉的(de)URL
let mweb_url = await ajax("POST", "/api/OrderProgram/WxH5Pay", { orderId });
// 3. 跳轉URL去微信支付
if (mweb_url) {
location.href = http://www.wxapp-union.com/mweb_url;
} else {
console.log("回調地(dì / de)址出(chū)錯");
}
// 4. 支付後返回支付頁,判斷是(shì)否支付成功
// 4.1 刷新頁面,獲取最新的(de)訂單(商品)狀态。
// 4.2 設置一個(gè)"我已支付"的(de)按鈕,讓用戶點擊之(zhī)後查詢狀态。
}
注意事項
- 在(zài)商戶平台設置正确的(de)支付域名
- 調試需要(yào / yāo)在(zài)線上(shàng),如果嫌麻煩可以(yǐ)使用内網穿透(Ngrok 或花生殼)
- 需對
redirect_url
進行urlencode
處理 - H5 支付不(bù)能直接在(zài)微信客戶端内調起,請在(zài)外部浏覽器調起。
參考
- 微信支付-H5 支付-開發步驟
JSAPI 支付(微信内網頁支付)
開發流程
- 商品頁
- 前端商品頁創建訂單,在(zài)後端統一下單後獲取
orderId
- 前端帶着
orderId
跳轉到(dào)支付頁,
- 支付頁
獲取
code
- 第一次進入頁面,判斷是(shì)否路徑中有
code
- 沒有
code
,請求數據跳轉授權頁面,code
會通過回調地(dì / de)址一起返回回來(lái) - 拿到(dào)
code
,發送給後端,後端解析到(dào)openid
,保存好。
- 第一次進入頁面,判斷是(shì)否路徑中有
點擊确定支付按鈕,觸發
wxPay()
方法- 發送
orderId
給後端,獲取wxData
wxData
中包含wx.config
和(hé / huò)wx.chooseWXPay
兩個(gè)接口的(de)數據。- 先調用
wx.config()
然後在(zài)調用wx.chooseWXPay()
,如果一切正常,支付頁面就(jiù)會彈出(chū)。
- 發送
- 支付狀态通過後端去查詢
僞代碼
- 商品頁
// 1. 創建訂單 獲取orderId
let orderId = await ajax("POST", "/api/OrderProgram/CreateTheOrder", {
goodId, // 商品id
});
// 2. 攜帶id 跳轉到(dào)支付頁
this.$router.push({ name: "wx_pay_page", params: { orderId: id } });
- 入口文件(
main.js
)
// main.js 引入 js-sdk
import wx from "weixin-js-sdk";
- 支付頁 HTML
<template>
<div>
<button @click="wxPay">點擊支付button>
div>
template>
支付頁 JS
// Vue
data(){
return {
orderId: this.$route.params.orderId, // 訂單id
url: '',// 獲取code的(de)url
wxData: null,// js-sdk接口所需的(de)數據
}
},
mounted(){
// 判斷是(shì)否有code
this.getCode()
}
methods: {
getCode() {
var code = this.getUrlPram("code");
if (code != null) {
this.code = code;
// 拿到(dào) code 發給 後端
this.sendCode(code);
} else {
// 去拿code
this.getUrl();
}
},
getUrl() {
// 請求後端拿到(dào)url所需數據,然後跳轉頁面在(zài)通過回調地(dì / de)址返回,獲取code.
this.axios
.post("/api/OrderProgram/GetOpenidAndAccessToken", {
orderId: this.orderId,
})
.then((data) => {
this.url = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${data.appId}&redirect_uri=${data.redirect_uri}&response_type=${data.response_type}&scope=${data.scope}&state=${data.state}`;
window.location.href = http://www.wxapp-union.com/this.url;
})
.catch((err) => {
console.log(err);
});
},
sendCode(code) {
// 發送code給後端 後端解析出(chū)openid
this.axios
.post("/api/OrderProgram/GetOpenidAndAccessTokenFromCode", {
code: code,
})
.then((res) => {
console.log(res);
})
.catch((err) => {
console.log(err);
});
},
wxPay: async function() {
// 發送orderid,獲取wx.chooseWXPay和(hé / huò)wx.config所需的(de)參數
this.wxData = http://www.wxapp-union.com/await this.axios.post(
"/api/OrderProgram/WxJSAPIPay",
{ orderId: this.orderId }
);
let wxConfigData = http://www.wxapp-union.com/this.wxData.wxConfigData // 獲取wx.chooseWXPay()所需數據
let wxPayData = http://www.wxapp-union.com/this.wxData.wxPayData;// 獲取wx.config()所需數據
this.$wx.config({
debug: false, // 開啓調試模式,調用的(de)所有api的(de)返回值會在(zài)客戶端alert出(chū)來(lái),若要(yào / yāo)查看傳入的(de)參數,可以(yǐ)在(zài)pc端打開,參數信息會通過log打出(chū),僅在(zài)pc端時(shí)才會打印。
appId: wxConfigData.appId, // 必填,公衆号的(de)唯一标識
timestamp: wxConfigData.timeStamp, // 必填,生成簽名的(de)時(shí)間戳
nonceStr: wxConfigData.nonceStr, // 必填,生成簽名的(de)随機串
signature: wxConfigData.paySign, // 必填,簽名
jsApiList: [
"chooseWXPay",
],
});
// 執行支付
this.$wx.chooseWXPay({
timestamp: wxPayData.timeStamp, // 支付簽名時(shí)間戳,注意微信jssdk中的(de)所有使用timestamp字段均爲(wéi / wèi)小寫。但最新版的(de)支付後台生成簽名使用的(de)timeStamp字段名需大(dà)寫其中的(de)S字符
nonceStr: wxPayData.nonceStr, // 支付簽名随機串,不(bù)長于(yú) 32 位
package: wxPayData.package, // 統一支付接口返回的(de)prepay_id參數值,提交格式如:prepay_id=\*\*\*)
signType: wxPayData.signType, // 簽名方式,默認爲(wéi / wèi)'SHA1',使用新版支付需傳入'MD5'
paySign: wxPayData.paySign, // 支付簽名
success: (res) => {
this.$toast("支付成功");
},
fail: (err) => {
this.$toast("支付失敗");
},
});
},
}
同時(shí)支持 H5 支付和(hé / huò) JSAPI 支付
// 在(zài)創建訂單之(zhī)後,就(jiù)判斷環境使用哪種方法支付。
if (isWx()) {
this.WXPay(orderId); // 帶着orderId跳轉到(dào)支付頁邏輯
} else {
this.H5Pay(orderId); // 執行上(shàng)面H5支付中的(de)創建訂單之(zhī)後的(de)邏輯
}
// 判斷是(shì)否是(shì)微信浏覽器
function isWx() {
let uAgent = navigator.userAgent.toLowerCase();
reutrn(/micromessenger/.test(uAgent)) ? true : false;
}
注意事項
- 開通微信商戶号 - 設置支付目錄(如果是(shì) Vue 這(zhè)類 SPA 頁面,到(dào)根目錄即可,也(yě)就(jiù)是(shì)#号之(zhī)前的(de)地(dì / de)址)
- 開通微信公衆号(服務号) - 設置安全域名、設置授權域名
- 收集參數:appId 和(hé / huò) AppSecret
- 添加 Web 開發工具開發者(需要(yào / yāo)開發者同時(shí)開發者關注開發的(de)微信公衆号和(hé / huò)微信公衆賬号安全助手)
[圖片上(shàng)傳失敗...(image-b07878-1605777597831)] - 設置回調域名(例如:
www.xx.com/pay
,最後獲取的(de) code 會拼在(zài)此回調地(dì / de)址後返回,返回後如www.xx.com/pay?code=xxxx
) 獲取 code
- 參考獲取 code 文檔
- 在(zài)微信客戶端網頁打開授權地(dì / de)址,跳轉之(zhī)後,在(zài)返回的(de)回調地(dì / de)址之(zhī)後拿到(dào)
code
:
https://open.weixin.qq.com/connect/oauth2/authorize
?appid=你的(de)appid
&redirect_uri=你的(de)回調地(dì / de)址(拿到(dào)code後返回)
&response_type=code(返回類型,默認code)
&scope=snsapi_base(授權範圍,靜默授權拿到(dào)openid)
&state=STATE(自定義狀态,非必填)
#wechat_redirect(重定向使用必須攜帶)
redirect_uri
參數要(yào / yāo)和(hé / huò)你在(zài)微信公衆号裏設置的(de)回調域名一緻(例如:www.xx.com/pay
),需要(yào / yāo)注意的(de)是(shì)這(zhè) url 需要(yào / yāo)urlEncode
。
請求這(zhè)個(gè)地(dì / de)址之(zhī)後,code
會以(yǐ)你設置的(de)redirect_uri
地(dì / de)址裏的(de)參數帶回來(lái),拿到(dào)之(zhī)後傳給後端就(jiù)行了(le/liǎo)。
前端引入 js-skd
- 使用
script
引入js-sdk - 下載使用
npm
包weixin-js-sdk
- 使用
參考
- 微信支付-JSAPI
- 微信公衆号-網頁授權
- JS-SDK 開發文檔
總結
整個(gè)流程走下來(lái),給我的(de)體驗是(shì):小程序支付最方面(因爲(wéi / wèi)配置少),其次是(shì) H5,JSAPI 支付最麻煩(文章一多半都在(zài)寫它)
在(zài)微信支付功能開發過程中,其實最麻煩的(de)不(bù)是(shì)開發流程,而(ér)是(shì)他(tā)的(de)各種配置和(hé / huò)授權流程,爲(wéi / wèi)了(le/liǎo)拿到(dào)所需的(de)參數而(ér)來(lái)回折騰。
開發過程中的(de)一些參數是(shì)經常用到(dào)的(de),如 appid、openid、orderId
支付流程大(dà)徑相同,先獲取到(dào)用戶的(de) openid,知道(dào)你是(shì)誰,然後統一下單拿到(dào) orderId 再去處理不(bù)同平台的(de)支付方式
開發時(shí)候用到(dào)的(de)相關文檔,一定要(yào / yāo)仔細閱讀二遍以(yǐ)上(shàng)爲(wéi / wèi)止!!