Flutter in_app_purchase插件iOS恢复购买先识别为新购买的问题咨询
iOS端恢复内购出现重复状态的原因及解决方案
原因分析
iOS App Store的内购恢复机制和Android存在本质差异:当调用restorePurchases()时,系统会先将已购买的非消耗品交易以PurchaseStatus.purchased状态推送给观察者,随后再将这些交易更新为PurchaseStatus.restored状态。这是iOS平台的原生行为,in_app_purchase插件只是如实转发了系统的状态变化事件,因此会出现先收到purchased再收到restored的情况。
同时你的代码存在两个放大问题的细节:
- 在处理
purchased状态时调用了completePurchase(),这会让iOS系统误判该交易为主动完成的购买,干扰后续恢复状态的推送逻辑。 - 未区分主动购买和恢复操作的事件流,导致两种场景的状态被无差别处理,引发重复逻辑执行。
解决方案
1. 增加恢复状态标记,区分事件场景
在类中添加布尔变量标记当前是否处于恢复流程,调用restorePurchases()时设为true,恢复完成后重置为false,以此跳过恢复流程中的purchased状态处理:
bool _isRestoring = false; // 恢复购买的方法 Future<void> restorePurchases() async { _isRestoring = true; await iap.restorePurchases(); // 恢复完成后重置标记 _isRestoring = false; } Future<void> listenForPurchases() async { print("Called listenForPurchases method"); iap.purchaseStream.listen((List<PurchaseDetails> purchases) async { print("Listened"); bool purchaseStatus = await purchaseProducts().handlePurchase(purchases); if (purchaseStatus == true) { for (var purchase in purchases) { print("User has ${purchase.status.toString()} ${purchase.productID}"); if (purchase.status == PurchaseStatus.purchased) { // 仅在非恢复流程时处理主动购买逻辑 if (!_isRestoring) { print("NEW Purchase was made"); // 执行主动购买的业务逻辑 await iap.completePurchase(purchase); } } else if (purchase.status == PurchaseStatus.restored) { print("Restore was made"); // 执行恢复购买的业务逻辑 // iOS恢复的交易无需调用completePurchase,系统会自动处理 } } } }); }
2. 避免在恢复流程中调用completePurchase
iOS平台的恢复交易由系统自动完成收尾操作,只有用户主动发起的购买才需要手动调用completePurchase()确认交易完成。
3. 过滤重复交易事件
通过PurchaseDetails的transactionID过滤已处理过的交易,避免因状态重复推送导致逻辑重复执行:
final Set<String> _processedTransactionIds = {}; Future<void> listenForPurchases() async { print("Called listenForPurchases method"); iap.purchaseStream.listen((List<PurchaseDetails> purchases) async { print("Listened"); bool purchaseStatus = await purchaseProducts().handlePurchase(purchases); if (purchaseStatus == true) { for (var purchase in purchases) { // 跳过已处理的交易 if (_processedTransactionIds.contains(purchase.transactionID)) continue; print("User has ${purchase.status.toString()} ${purchase.productID}"); if (purchase.status == PurchaseStatus.purchased) { if (!_isRestoring) { print("NEW Purchase was made"); // 业务逻辑 await iap.completePurchase(purchase); _processedTransactionIds.add(purchase.transactionID!); } } else if (purchase.status == PurchaseStatus.restored) { print("Restore was made"); // 业务逻辑 _processedTransactionIds.add(purchase.transactionID!); } } } }); }
内容的提问来源于stack exchange,提问作者jscodes
相关产品推荐
相关产品推荐

