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