前言
Hello!又是很長時間沒有寫博客了,因為最近又開始從事新項目,也是第一次接觸關于uniapp開發(fā)原生IOS應用的項目,在這里做一些關于我在項目中使用蘋果內購支付所實現(xiàn)的方式以及要注意的事項,希望能給正在做uniapp開發(fā)ios應用需要使用蘋果內購支付的小伙伴一些幫助!
問題
為什么開發(fā)ios應用需要使用蘋果內購支付?
原因在于,蘋果要求所有開發(fā)者在上架Appstore中的應用,如果應用中出現(xiàn)了虛擬商品
的購買,必須使用蘋果內購支付,并且絕對不能出現(xiàn)其他支付方式,例如微信、支付寶等支付方面的sdk,當然,如果你不怕被蘋果下架的風險,你可以嘗試使用webview跳轉的方式,但是如果你的代碼中使用了其他支付方式的sdk或者代碼,是很大可能無法通過蘋果嚴格的審核的。
ios內購為什么要專門拿出來說,對比其他支付方式有什么區(qū)別嗎?
首先,他與微信、支付寶等都屬于支付渠道的一種,本質上沒有區(qū)別,但是由于蘋果服務器的原因,導致一些非常特殊的問題,例如:回調時間長甚至沒有回調、掉單、回調異常等情況,這種情況對比其他支付方式真的很雞肋,特別是在uniapp的開發(fā)環(huán)境下,居然沒有超時的回調,簡直是大坑,不過這個在后面我會提到解決方案。
ios內購,事務
蘋果支付走的是事務列表,每生成一筆訂單就會走一筆訂單,如果已經(jīng)完成的訂單需要使用蘋果提供的關閉訂單的api來進行關閉訂單,否則會出現(xiàn)回調有誤的情況。
前期準備
開通內購
你需要擁有ios開發(fā)者平臺的開發(fā)者賬號,申請開通內購支付,并配置相應的內購檔位名稱和參數(shù)。
內購檔位需要配置ios后臺有的檔位,ios檔位是有規(guī)定指定的金額,不是自定義檔位金額的。
注意:此處的檔位id需要配置到后端返回給前端,需要跟蘋果后臺配置的一一對應,否則也會拉起失敗!
自定義基座包
注意,測試支付需要制作自定義基座包,需要配置相應的證書,證書可以參考uniapp的文檔進行配置。測試證書制作正式包無法測試支付,只能用于自定義基座包。
獲取內購證書
正式上線
正式上線需要將證書替換為正式證書,通過testfight軟件進行進一步測試!
實現(xiàn)步驟
Unipay(不常用)
由于我使用的是uniapp開發(fā)原生應用,本身uniapp對于支付方式就有專門的封裝,如果你沒有后端,那你可以嘗試使用uniPay,下面是文檔的鏈接
Unipay官方文檔
基本用法(常用)
使用uniapp的uni.requestPayment
來實現(xiàn)是比較常用的方式,下面是支付的文檔,不過看看就好,還是有挺多坑的,具體的支付流程可以參考一下官方文檔,不過邏輯還有代碼的正確性需要自己考量,下面我會介紹我的方式
蘋果支付
開啟內購模塊
在manifest.json文件中勾選Payment(支付)
中的Apple應用內
支付。
注意:不要勾選其他支付模塊,如果你開發(fā)ios原生應用的話。
獲取iap通道
獲取iap通道是判斷當前設備是否支持蘋果內購支付的必要條件,所以一定要先判斷是否含有iap支付通道,如果含有支付通道,才可以走支付邏輯,否則直接return
即可,不需要任何邏輯。
export function Init() {
return new Promise((resolve, reject) => {
//使用uni.getProvider來獲取通道
uni.getProvider({
service: 'payment',
success: (res) => {
let iapChannel = res.providers.find((channel) => {
return (channel.id === 'appleiap')
})
//成功之后會返回通道
resolve(iapChannel)
},
})
})
}
返回示例
如果你獲取到的iap通道為null,那么你可以直接return,因為當前環(huán)境是不支持蘋果內購支付的,也就不用走其他邏輯了。
獲取已完成但未支付的訂單
由于蘋果服務器的原因,導致某些情況會出現(xiàn)回調時間長甚至沒有回調的情況,因此,這一步必須要做,因為如果不做這一步操作,會導致下一次的支付回調了上一次的事務這種異常情況。
其中,獲取訂單和關閉訂單是一起操作的,所以我把他們整合在了一起。
獲取訂單
export function restore(iapChannel) {
console.log("獲取蘋果服務器已支付且未關閉的交易列表")
return new Promise((resolve, reject) => {
iapChannel.restoreCompletedTransactions({
manualFinishTransaction: true,
username: ''
}, (res) => {
resolve(res)
}, (err) => {
reject(err);
})
});
}
關閉訂單
export function finishTransaction(transaction, iapChannel) {
console.log("關閉訂單")
return new Promise((resolve, reject) => {
iapChannel.finishTransaction(transaction, (res) => {
console.log("關閉訂單成功", res)
resolve(res);
}, (err) => {
reject(err);
});
});
}
整合:
export function getReview(iapChannel, token, dev) {
//請求是否有已完成未關閉的訂單
restore(iapChannel).then(res => {
//如果有并且狀態(tài)為已支付則請求關閉并回調給后端
console.log(res)
if (res.length > 0) {
//輪詢關閉訂單
res.map(item => {
finishTransaction(item, iapChannel)
//如果狀態(tài)為已完成的狀態(tài)
if (item.transactionState == '1') {
//后端邏輯,此處省略,通常是完成上報憑證的操作,來完成補單
//請求后端接口上傳支付憑證
submitMisson(PayBack, productId, iapChannel).then(res => {
uni.showToast({
icon: 'none',
title: '上一筆訂單已支付成功,請稍后留意余額'
})
console.log(res)
})
}
})
}
})
}
注意事項
這里可以選擇在合適的時機進行調用,可以選擇靜默處理,因為在支付的過程中,是不會允許移除事務的,所以如果調用獲取訂單的回調時間長,也可以不用處理,但一定要做這一步操作。
請求蘋果檔位列表
這一步一定要做,否則無法拉起內購支付,目的就是判斷當前的內購檔位信息是否有配置在蘋果后臺中。
/**
* 調用ID為“appleiap”的PaymentChannel對象的requestOrder方法,像Appstore請求有效的商品詳情。
* 注意:IAP支付必須在調用payment.request方法之前,調用requestOrder方法,否則調用payment.request將會報錯。
*/
export function requestOrder(iapChannel, productIds) {
uni.showLoading({
title: '初始化中~',
mask: true
})
return new Promise((resolve, reject) => {
iapChannel.requestOrder(productIds, (orderList) => { //必須調用此方法才能進行 iap 支付
console.log('requestOrder success: ' + JSON.stringify(orderList));
resolve(orderList)
uni.hideLoading()
}, (e) => {
console.log('requestOrder failed: ' + JSON.stringify(e));
uni.hideLoading()
uni.showToast({
icon: 'none',
title: '當前環(huán)境不支持內購支付'
})
reject(e)
});
})
}
拉起支付
這里建議將manualFinishTransaction設置為true,手動關閉訂單,否則自動關閉訂單可能出現(xiàn)訂單關閉失效的情況。
uni.requestPayment({
provider: 'appleiap',
orderInfo: {
manualFinishTransaction: true, //true為手動關閉訂單,false為自動關閉訂單
username: res.data.osn, //透傳參數(shù)
productid: productId, //檔位id
},
success: (e) => {
// e 類型為 Transaction, 詳見下面的描述
//后端邏輯省略
輪詢訂單情況
}
})
踩過的坑
回調時間長,導致掉單
如果你的應用有客服反饋的功能,那么可以申請客服反饋查詢后端訂單情況,進行補單的操作。
如果沒有,那么你就只能手動補單,一般來說,補單需要提供訂單號和票據(jù)信息。
但是由于用戶手動關閉應用,導致訂單號丟失,票據(jù)信息和訂單號對應起來,因此我們要做一個手動隊列的處理。
解決方案:在用戶下單時候,將訂單號和檔位id關聯(lián)起來做一個隊列
也就是key:檔位id,value: 訂單號數(shù)組
原因是用戶可以關閉應用之后,重新點擊支付,生成了一筆新的訂單號,但是回調是上一筆的票據(jù),因此需要做一個訂單號數(shù)組。
每次支付的時候獲取緩存中的隊列數(shù)據(jù),如果該檔位存在訂單號,說明上一筆訂單并沒有上報成功,因此取隊列中的第一個訂單號作為上報訂單,上報成功之后將這筆訂單移除,這樣就不會影響用戶的正常支付,獲取到上一筆訂單的回調問題,影響頁面邏輯。
例如:支付成功跳轉成功落地頁,但是回到的信息是上一筆訂單這種現(xiàn)象。
主動關閉訂單
由于上一步操作雖然正常上報,但是并沒有將已完成的訂單移除,所以我們還需要做一個隊列,用來移除已完成的訂單。
上報成功之后,將票據(jù)和osn作為隊列,放入緩存中,這一步其實是為了判斷訂單是否已經(jīng)關閉。
由于蘋果服務器的原因,很可能你主動調用關閉訂單,沒有立即關閉,所以你需要在進入應用的時候重新主動關閉。
蘋果回調了上一筆訂單的票據(jù)
這也算是一個比較奇怪的問題,不過回想也是可以理解的,由于蘋果服務器回調時間長的問題,不僅僅是支付回調慢,就連關閉訂單的回調也是非常慢,這就導致了用戶在支付下一筆訂單時,上一筆訂單并沒有完全關閉,結果就是用戶在支付第二筆訂單時回調的結果是上一筆訂單的票據(jù),因此推薦手動關閉訂單,自己選擇合適的時機去關閉訂單。
解決方案:
我們可以在本地做一個本地隊列,在有回調的時候,檢查本地隊列是否含有本次票據(jù)對應的訂單號,如果存在,就將osn拿出來進行上報,并刪除對應的隊列,如果不存在,就將票據(jù)和訂單號進行關聯(lián)存放,然后進行上報和關閉訂單的操作,這里主要是考慮到長時間沒有回調用戶主動關閉app的情況,在用戶下一次點擊的時候能夠主動幫他進行上報
總體流程圖
注意事項
校驗格式不正確
如果上報蘋果憑證發(fā)現(xiàn)蘋果返回憑證格式不正確的問題:
請注意查看官方文檔:
https://developer.apple.com/documentation/appstorereceipts/verifyreceipt
這里的憑證需要經(jīng)過base64,但是我們通過uniapp官方api調用的支付方式,返回的憑證是已經(jīng)經(jīng)過了base64
的,請不要再base64,否則就會校驗失效!
21003錯誤
如果之前后端校驗憑據(jù)無誤,但是突然沙箱校驗報21003的錯誤,很大可能是由于蘋果后臺配置了續(xù)費類型的產品
,那么你需要在校驗的時候多傳一個password字段
文章來源:http://www.zghlxwxcb.cn/news/detail-424453.html
消耗類型的傳這個參數(shù):{
"receipt-data": "交易憑證"
}
訂閱類型的要傳多一個參:
{
"receipt-data": "交易憑證",
"password" : "將生成的專用秘鑰填入"
}
注意:只要存在不屬于消耗型的產品,就可能需要加上密鑰校驗參數(shù)(使用蘋果原生不傳也能校驗通過,作者對此表示非常疑惑)文章來源地址http://www.zghlxwxcb.cn/news/detail-424453.html
到了這里,關于【iOS內購支付】Uniapp拉起蘋果內購支付注意事項、實現(xiàn)步驟以及踩過的坑(手把手教程)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!