flutter IAP蘋果內(nèi)購

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

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

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

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

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

使用前準備

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

使用

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

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

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

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

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

注意點

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

交易清單信息變更

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


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

隊列監(jiān)聽時機問題

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

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.
重要!你必須在應用程序啟動后立即訂閱此流,
最好在main()中返回主應用程序小部件之前。否則你
將錯過訂閱此流之前更新的購買。

文檔的意思十分清楚,希望我們在app啟動的時候,就開啟消息隊列的監(jiān)聽,為什么必須要這樣做呢,我查看了其源碼

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

productId發(fā)起異常

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

下一篇立個flag,寫點啥呢,overlay層級控制?flutter異步任務隊列?upyun文件上傳?東西其實都解決過了,只不過寫blog真是有點懶啊~唉,好了,其他話沒什么好說的,祝大家生活愉快

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容