flutter IAP蘋果內(nèi)購(gòu)

2023-10-08 16:38:50 最新的文章修正,請(qǐng)查看
flutter IAP蘋果內(nèi)購(gòu)總結(jié) - 掘金 (juejin.cn)

這里介紹的是蘋果的內(nèi)購(gòu),目前最常用的兩個(gè)內(nèi)購(gòu)
1.谷歌官方出的: in_app_purchase
pub.dev 1151 likes
github.com 這是flutter插件集里的插件,無(wú)法看出真正的stars
2.先于谷歌官方出的民間插件 flutter_inapp_purchase
pub.dev 308 likes
github.com 492stars

從長(zhǎng)遠(yuǎn)來(lái)看,in_app_purchase獲得的支持力度更大,畢竟是官方自己出的,而flutter_inapp_purchase是在那個(gè)沒(méi)有官方插件的年代時(shí),大眾們能依靠的最好內(nèi)購(gòu)插件.本文,基于項(xiàng)目考慮,還是采用了官方的in_app_purchase插件

使用(這里只使用消耗形的商品購(gòu)買)

友情提示一下各位,在使用這些有三方的庫(kù)時(shí),最好都自己封裝一層,方便后續(xù)替換成其他庫(kù).

使用前準(zhǔn)備

絕大部分文章都已經(jīng)介紹了如何配置消耗型商品欄目,以及測(cè)試人員配置,這里不再贅述,這里只提示幾點(diǎn),測(cè)試人員是賬號(hào)級(jí)別的,同一賬號(hào)下的測(cè)試人員可以直接參與到APP測(cè)試,消耗形商品的productID不允許與其他APP相同.

使用

in_app_purchase的使用demo也很簡(jiǎn)單.我們只需要知道流程順序:

  1. 后臺(tái)展示出可購(gòu)買的商品清單(這一步可以自己查詢出所有清單,但是正常的APP不可能只有系統(tǒng)配置的那些商品信息,所以,商品清單肯定是由后端提供的.
  2. 選擇購(gòu)買商品id,查詢蘋果商品詳情
if (productDetails.isEmpty) {
  ProductDetailsResponse res = await _inAppPurchase.queryProductDetails(IAPProduct.allProducts);
  productDetails = res.productDetails;
}
ProductDetails? productDetail = productDetails.firstWhereOrNull((element) => element.id == productId);

3.發(fā)起交易請(qǐng)求

final purchaseParam = PurchaseParam(
        productDetails: productDetail,
        applicationUserName: "${ServerTime.millisecond}");
    _inAppPurchase.buyConsumable(
      purchaseParam: purchaseParam,
      autoConsume: true,
    );

4.等待回調(diào)
其中有各種狀態(tài)判斷,消耗型只需要注意
pending:
交易中,用于展示loading等信息
error:
交易錯(cuò)誤,根據(jù)錯(cuò)誤碼展示相應(yīng)信息,如未能連接到蘋果服務(wù)器等等
canceled:
交易取消,吐絲,取消loading等
purchased:
交易成功, 驗(yàn)單,完成交易等

注意點(diǎn)

前面都是大同小異的內(nèi)容,其他文章也都有.既然我要寫(xiě),那我就寫(xiě)點(diǎn)別的文章沒(méi)有提到的,iap要解決的永遠(yuǎn)不是這種正向邏輯,而是一大堆稀奇古怪的騷操作,比如APP閃退,強(qiáng)殺APP等行為帶來(lái)的掉單,驗(yàn)單失敗問(wèn)題.

交易清單信息變更

在非正常交易邏輯中,在用戶收到交易訂單完成后,如果驗(yàn)單未成功,APP閃退或者我們強(qiáng)制殺死APP等其他操作,導(dǎo)致交易中斷,那么下次進(jìn)來(lái)的交易信息會(huì)發(fā)生變更.
下圖是第一次交易成功信息回調(diào)時(shí)的訂單,交易id為2000000111028232


下圖是強(qiáng)制殺死APP后,重新監(jiān)聽(tīng)交易隊(duì)列時(shí)獲取到的訂單信息,可以看到,除了交易id仍然為2000000111028232之外,serverVerificationData,transactionDate都為新的交易信息,并且我塞進(jìn)去的applicationUsername也只在第一次交易回調(diào)起作用,后續(xù)的交易信息直接為null,但是,沒(méi)關(guān)系,驗(yàn)單任然是能成功的.
.
這也從側(cè)面說(shuō)明了,僅靠in_app_purchase交易隊(duì)列的機(jī)制,是無(wú)法實(shí)現(xiàn)我們的額外參數(shù)填充,去輔助訂單校驗(yàn).我一開(kāi)始是想僅依靠消息隊(duì)列,在完完全全從服務(wù)器驗(yàn)完單后,再完成這筆交易.本來(lái),這種方法可以從隊(duì)列機(jī)制上解決掉掉單問(wèn)題,但是上述的問(wèn)題,打消了我的想法.而下面的這個(gè)問(wèn)題,也促使我去實(shí)現(xiàn)鑰匙串自存儲(chǔ)訂單來(lái)解決調(diào)單問(wèn)題.

隊(duì)列監(jiān)聽(tīng)時(shí)機(jī)問(wèn)題

_inAppPurchase.purchaseStream是用來(lái)監(jiān)聽(tīng)消息隊(duì)列的回調(diào)的,也就是所有訂單的狀態(tài)以及信息回調(diào),我們當(dāng)然是希望,在用戶登錄完成后,再開(kāi)啟訂單的監(jiān)聽(tīng)隊(duì)列,這樣,在驗(yàn)單的時(shí)候,就能夠及時(shí)獲取到訂單的所有信息,但顯然,in_app_purchase并不是這么做的,這個(gè)屬性的文檔中這么說(shuō)到:

IMPORTANT! You must subscribe to this stream as soon as your app launches,
preferably before returning your main App Widget in main(). Otherwise you
will miss purchase updated made before this stream is subscribed to.
重要!你必須在應(yīng)用程序啟動(dòng)后立即訂閱此流,
最好在main()中返回主應(yīng)用程序小部件之前。否則你
將錯(cuò)過(guò)訂閱此流之前更新的購(gòu)買。

文檔的意思十分清楚,希望我們?cè)赼pp啟動(dòng)的時(shí)候,就開(kāi)啟消息隊(duì)列的監(jiān)聽(tīng),為什么必須要這樣做呢,我查看了其源碼

發(fā)現(xiàn)在InAppPurchase的單例初始化方法里,當(dāng)streamcontroller建立起與原生的通信后,直接發(fā)起了隊(duì)列的監(jiān)聽(tīng),這也就導(dǎo)致我們需要在使用到調(diào)用到單例屬性的之前,先建立purchaseStream的監(jiān)聽(tīng)
這與我的業(yè)務(wù)需求嚴(yán)重不符!
這與我的業(yè)務(wù)需求嚴(yán)重不符!
這與我的業(yè)務(wù)需求嚴(yán)重不符!
我希望能自己掌控到隊(duì)列的監(jiān)聽(tīng)時(shí)機(jī),可in_app_purchase并不給我這個(gè)機(jī)會(huì).

productId發(fā)起異常

如果未能完成上一次的訂單,調(diào)用請(qǐng)求下一次的交易,會(huì)報(bào)錯(cuò)
Unhandled Exception: PlatformException(storekit_duplicate_product_object, There is a pending transaction for the same product identifier. Please either wait for it to be finished or finish it manually using completePurchase to avoid edge cases., {applicationUsername: 1658383960024, requestData: null, quantity: 1, productIdentifier: an_coin_6, simulatesAskToBuyInSandbox: false}, null)

如果你的APP不需要那么嚴(yán)格訂單的校驗(yàn)機(jī)制,比如服務(wù)器有預(yù)請(qǐng)求接口,驗(yàn)單時(shí)需要提交預(yù)請(qǐng)求的參數(shù),又或者你的APP允許A支付B賬戶充值,那么你可以僅依靠交易隊(duì)列去實(shí)現(xiàn),但我的APP不行.
綜上所述,使用谷歌的in_app_purchase插件,在僅靠交易隊(duì)列機(jī)制,我們是無(wú)法實(shí)現(xiàn)一個(gè)精準(zhǔn)驗(yàn)單的,所以必須要自己手寫(xiě)驗(yàn)單邏輯.這就跟我原生寫(xiě)iap的邏輯基本完全一致了.
這里僅提幾個(gè)小點(diǎn)
1.交易完成必須在把訂單手寫(xiě)入鑰匙串成功后再完成交易.
2.指數(shù)時(shí)間重復(fù)校驗(yàn)這是必不可少的,一般校驗(yàn)失敗個(gè)兩三次后,就可以走上傳日志邏輯了,如果掉單行為不能完全抹除,一個(gè)好的報(bào)錯(cuò)接口可以解決很多問(wèn)題.
3.后端最好有預(yù)創(chuàng)建訂單api接口,蘋果的iap十分霸道,創(chuàng)建訂單,獲取訂單的serverVerificationData完全是前端自己的行為,這一點(diǎn)其實(shí)很不安全,因?yàn)橛唵瓮瓿珊脡姆?wù)器一點(diǎn)都不能知曉,所以最好是讓后端先建立一筆自己的訂單記錄,然后驗(yàn)單時(shí)順便校驗(yàn)這個(gè)訂單信息,保證前后對(duì)照行為一致.

下一篇立個(gè)flag,寫(xiě)點(diǎn)啥呢,overlay層級(jí)控制?flutter異步任務(wù)隊(duì)列?upyun文件上傳?東西其實(shí)都解決過(guò)了,只不過(guò)寫(xiě)blog真是有點(diǎn)懶啊~唉,好了,其他話沒(méi)什么好說(shuō)的,祝大家生活愉快

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容