微信小程式 iOS支付
微信小程式運營支付規範
微信官方針對有虛擬支付功能的小程式下發了整改通知.截至5月8號,平臺將對賬號遮蔽iOS系統的支付介面呼叫.我去查了<<微信小程式運營規則>>,如上圖.發現小程式的支付規範中已經做了明示.
這次整改主要含2方面內容:
1.虛擬支付
不是所有提供支付功能的小程式都要整改,僅僅只有涉及”虛擬支付”的小程式需要調整.什麼是虛擬支付呢?說白了就是購買非實物,摸不到的物品.比如:VIP會員,充值,線上課程,虛擬物品等等.
2.iOS系統
本次整改,只涉及iOS系統內執行的小程式,安卓系統不受影響,開發者們依然可以在安卓版本內呼叫微信支付.另外小遊戲內的安卓購功能,也不受影響.
針對規則,開發者們可以如何繞過呢?我整理後發現主要有4種方式.
1.小程式跳轉APP或者引導使用者去APP支付(例如得到小程式)
2.引導至公眾號支付或者利用公眾號支付(例如樂樂國學)
3.將虛擬變成實體
4.收費變免費
最後公司決定選用第二種支付邏輯,也就是利用公眾號支付來實現繞過小程式支付.
前端操作流程如下圖:其本質還是利用公眾號支付來實現支付功能
接下來我們來一步一步看程式碼是如何實現這套流程的.
第一步:現在WXML中引入按鈕組和蘋果支付的元件
<!-- 螢幕下方的功能按鈕 拼團 原價購買 集贊 -->
<course_actionBtn id="course-actionBtn" bindDialogEvent="onDialogEvent" bindApplePayEvent="onApplePayEvent" bindAndroidPayEvent="onAndroidPayEvent" bindPriseEvent="onPriseEvent" data-mode=" {{data}}"> </course_actionBtn>
<!--蘋果手機支付驗證框 -->
<payment-dialog id="payment-dialog" data-mode="{{isGroup}}" payArgumentStr="{{payArgumentStr}}"></payment-dialog>
第二步:在頁面的js裡在點選拼團按鈕的triggerEvent方法裡彈出蘋果支付的元件,因為專欄詳情頁下方的入口按鈕組也封裝成了元件,為了減少專欄詳情頁的程式碼數量.也是基於面向物件的考慮,單獨的業務邏輯抽取出來,方便其他頁面如果使用類似程式碼的話,方便移植.比如產品新加入的集贊入口,這樣就可以直接修改按鈕組元件的邏輯就好,不需要大範圍的修改專欄詳情頁的程式碼邏輯.又比如將來如果有一個新的業務功能也需要這套按鈕入口,直接引入元件就可以了.利於擴充套件.可以看到這裡按鈕組是一個元件,當點選拼團支付或者原價購買時,因為區分了蘋果支付和安卓支付,我在這裡將蘋果支付也抽成了一個元件.這樣可以在安卓系統上走小程式原生支付,在蘋果系統上走我們特殊處理的蘋果支付.目的也是為了將來方便擴充套件.這裡有一個隱藏的坑.這裡記錄下來防止遺忘.
官方給的tip:
注意:必須在兩個元件定義中都加入relations定義,否則不會生效。下圖是官方文件給出的relations支援的type型別.
綜上,我的理解是微信目前只支援類似這種包含關係的元件巢狀元件.它必須滿足的是一種UI層面的巢狀,而我現在實現的是一種邏輯意義上的巢狀,所以不滿足.這裡我選用了第二套方案.利用元件的事件機制,將所有的元件都引入page物件中,然後監聽每個元件的點選事件,並將觸發結果從子元件中全部傳到page物件中進行回撥處理.
我們重新回到蘋果支付這裡.彈出蘋果支付的元件後(就是流程圖的第一幅圖).
//彈出蘋果支付
onApplePayEvent: function (e) {
var isGroup = e.detail.isGroup
var payArgumentStr = e.detail.payArgumentStr
this.setData({ isGroup: isGroup, payArgumentStr: payArgumentStr})
this.paymentDialog.showPopup()
},
// 三人拼團
tapPuzzle: function(e) {
var result = this.dataset.mode
var courseID = result.id
var that = this
//防止按鈕重複點選
if (util.isReClick() || this.data.clickStatus == 1) return
//前置驗證是否需要彈出收集手機號的驗證彈框
util.checkoutShowMessageCodeDialog(this, function(e) {
if (e == 1) {
that.setData({
clickStatus: 1
})
pm = new PayManager()
pm.groupBuyPayment(courseID, {
iosCallback: res => {
var payArgumentStr = res
//調起iOS支付邏輯, payArgumentStr也是在這裡通過元件事件給傳遞到page物件裡面,PayManager()是專案裡我封裝的支付管理類,一種es6的寫法------------這裡最重要
that.triggerEvent("ApplePayEvent", {
isGroup: 1,
payArgumentStr: payArgumentStr
})
that.setData({
clickStatus: 0
})
},
androidCallback: res => {
var code = res.code
var result = res.data
if (code == -1) {
that.setData({
clickStatus: 0
})
console.log("調起拼團介面失敗");
util.toast(result)
} else if (code == -2) {
that.setData({
clickStatus: 0
})
console.log("拼團調起微信支付失敗");
// util.toast(result.errMsg)
} else if (code == 0) {
that.setData({
clickStatus: 0
})
console.log("拼團調起微信支付成功");
var result = pm.getGroupResult();
//跳轉到拼團詳情頁面
wx.navigateTo({
url: '../group-payment/group-payment?groupBuyId=' + result.groupBuyId,
})
}
}
})
} else if (e == -1) {
console.log("手機號驗證失敗")
} else if (e == 2) {
that.triggerEvent("DialogEvent")
}
})
},
下面這段程式碼是支付管理類裡關於拼團購買的實現
//拼團購買
groupBuyPayment(courseId, callBack) {
var that = this
//獲取當前使用者手機型別
var isIOS = util.isIOSPlatform(app.data)
if (isIOS == true) {
//組裝sessionFrom,帶到客服會話列表裡面
if (courseId) {
var object = {};
object.courseId = courseId;
object.isGroupBuy = "1";//1是拼團購買 0是原價購買
object.groupBuyId = "";//groupBuyId如果有值就是參與拼團 沒有值是發起拼團
var argument = JSON.stringify(object);
} else {
console.log("courseId為空")
}
//這裡這個argument就是上面那個payArgumentStr
callBack.iosCallback(argument)
} else {
//安卓支付
var courseID = courseId;
//1.發起拼團,獲取支付的前置資訊獲取
network.POST("/groupBuy/startGroupBuy", {
params: { courseId: courseID },
success: function (result) {
// success
var r = result.data.data;
that.groupResult = r
var groupId = r.groupBuyId;
var timestamp = r.timestamp;
var nonceStr = r.nonceStr;
var pack = r.package;
var paysign = r.paySign;
//2.調起微信支付
util.requestWXPayment(timestamp, nonceStr, pack, paysign, function (res) {
callBack.androidCallback({ code: 0, data: res })
}, function (res) {
callBack.androidCallback({ code: -2, data: res })
})
},
fail: function (error) {
console.log("error:")
console.log(error)
callBack.androidCallback({ code: -1, data: error })
}
})
}
}
第三步:
<button class='popup_ok' open-type='contact' session-from='{{payArgumentStr}}'>確定</button>
開啟客服訊息時,將這個payArgumentStr傳給後臺.它是怎麼傳給後臺的呢?看上圖,是我在小程式API中擷取的圖片.我們先看第一幅圖紅框位置.它的意思就是當用戶在小程式中點選一個openType=”contact”的按鈕時就會產生一個數據包,這個資料包裡有一個SessionFrom欄位.我們的payArgumentStr就是通過這個欄位傳給後臺的.在看第二副圖,就是微信伺服器會將訊息(或事件)的資料包(JSON或者XML格式)POST請求開發者填寫的URL。開發者收到請求後可以使用傳送客服訊息介面進行非同步回覆。簡單解釋就是從使用者點選客服會話按鈕,就會產生第一個資料包,可以利用這個資料包從外面帶一些自定義的引數到會話列表中,這個資料包會先發到微信伺服器,微信伺服器收到這個資料包後,會將這個資料包發給我們自己的伺服器,因為小程式後臺配置的開發者URL就是我們自己的伺服器地址,伺服器收到微信伺服器的請求後獲取到這個payArgumentStr,也就是拿到了訂單資訊.再根據這些資訊生成支付連結,當監聽到使用者傳送客服訊息時將支付連結回調回去.顯示在客服會話列表中.
現在我們再來看看這個payArgumentStr到底是什麼?它其實是我和後臺約定好的一個json物件轉成的字串.這個json物件會帶一些必要資訊到後臺,讓後臺知道我當前發起的什麼樣的支付請求,是原價購買,是我要拼團,還是參與拼團.如果是我要拼團,需要課程ID,如果是參與拼團的話,需要拼團ID.這些引數都是我在開啟客服列表會話時,傳給後臺,將來後臺拿到這些引數,處理後,把這些資訊拼在支付連結後面返回給我.就是流程圖第3幅圖裡面的點選立即購買,它其實後臺返回的一條帶有訂單資訊的支付url連結.(這裡是有安全隱患的).這個url連結是我自己寫的併發布好的一個web頁面,在這個頁面裡我會先拿到訂單資訊,然後調起公眾號支付完成對該訂單的付款.後臺監聽到這個單子付過款後,就會更新相關欄位,這樣我跳回小程式後,來到專欄詳情頁就可以去伺服器更新專欄課程的最新狀態,就專欄解鎖.
function wxConfig() {
//通過config介面注入許可權驗證設定
$.ajax(
{
type: 'GET',
url: app.url.config_url + 'wechat-js-config/xcx',
data: {
url: window.location.href //window.location.href指的永遠是訪問該網頁時用的URL.
},
success:function (msg,req) {
if(req == 'success') {
console.log("=====獲取成功======");
var data = msg.data.js_config;
wx.config({
debug: false, // 開啟除錯模式,呼叫的所有api的返回值會在客戶端alert出來,若要檢視傳入的引數,可以在pc端開啟,引數資訊會通過log打出,僅在pc端時才會列印。
appId: data.appid, // 必填,企業號的唯一標識,此處填寫企業號corpid
timestamp:data.timestamp , // 必填,生成簽名的時間戳
nonceStr: data.nonceStr, // 必填,生成簽名的隨機串
signature: data.signature,// 必填,簽名,見附錄1
jsApiList: ['chooseWXPay'] // 必填,需要使用的JS介面列表,所有JS介面列表見附錄2
});
// config資訊驗證後會執行ready方法,所有介面呼叫都必須在config介面獲得結果之後
wx.ready(function(){
//獲取頁面URL連結的查詢引數
var args = app.getQueryStringArgs();
console.log(args);
//1.獲取支付型別
var paymentType = args.isGroupBuy;
var courseId = args.courseId;
var groupBuyId = args.groupBuyId;
if (paymentType == 1) {
if (groupBuyId != "null") {
//參團人獲取拼團預付單資訊
joinGroupBuy(groupBuyId);
}else {
//拼團發起人獲取拼團預付單資訊
startGroupBuy(courseId);
}
}else {
//原價購買的預付單資訊
unlockCourse(courseId);
}
});
}else {
app.showTip('請求超時');
}
},
error:function (msg) {
app.showTip(JSON.stringify(msg));
}
})
}
//支付成功後跳轉到公眾號文章頁面,就是流程圖中第6幅圖.這個頁面是在公眾號後臺配置生成的公眾號文章,在編輯的時候,可以在文章裡面關聯小程式的圖文訊息.這樣就可以通過點選公眾號裡的小程式圖文訊息實現從公眾號向小程式的跳轉,完成閉環.這裡有個小坑,就是生成的文章連結是一個臨時連結,是有時效的,可以百度一下如何獲取公眾號文章的永久連結拿到永久連結,很簡單,這裡就不給傳送門了.這裡還有一個小需求,就是在web頁面調起支付後,如果取消支付,就關閉當前頁面,回到客服會話列表.我的思路是在支付的封裝方法裡,監聽到支付取消後跳回處理.
//跳轉到公眾號文章頁面
function nextArticePage() {
window.open("https://mp.weixin.qq.com/s/IxGJRB5oUtE8sP292KATOw");
}
if(res.errMsg == "chooseWXPay:cancel" ) {
WeixinJSBridge.call('closeWindow');
}
綜上,就是我前端的整體實現邏輯,可能會有很多不準確的地方,感興趣的朋友看到後可以給我指出來.提前謝謝了.
參考連線:
2小時後,小程式“虛擬支付”更改規則,這有4種“曲線救國”的方式!