Flutter in_app_purchase包升级后无法恢复已购项目求助
in_app_purchase升级后Android恢复购买失败问题修复
问题根源
你的代码存在几个关键问题,导致恢复购买时purchaseStream始终返回空列表:
- 订阅过早取消:在
getPastPurchases的listen回调里立刻调用_subscription.cancel(),此时restorePurchases的异步结果还未返回,直接终止了流监听,无法接收到恢复的购买数据。 - Android平台处理缺失:仅在iOS分支处理了
completePurchase,Android平台恢复购买后同样需要完成购买确认,否则系统会认为购买未完成,后续恢复请求无法正确返回数据。 - 监听逻辑不合理:仅在
purchases为空时才创建订阅,没有持续监听购买流,而restorePurchases的结果可能延迟返回。
修复方案
1. 初始化时持久化订阅
在initStore中初始化全局订阅,持续监听购买事件,不要在单次恢复请求后取消:
Future<void> initStore() async { final bool isAvailableTemp = await inAppPurchase.isAvailable(); isAvailable = isAvailableTemp; if (!isAvailable) { products = []; purchases = []; notFoundIds = []; purchasePending = false; loading = false; return; } if (Platform.isIOS) { final InAppPurchaseStoreKitPlatformAddition iosPlatformAddition = inAppPurchase.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>(); await iosPlatformAddition.setDelegate(PaymentQueueDelegate()); } // 初始化持久订阅,页面销毁时再取消 _subscription = inAppPurchase.purchaseStream.listen( (purchaseDetailsList) { listenToPurchaseUpdated(context, purchaseDetailsList); // 统一处理所有平台的购买完成确认 for (var purchase in purchaseDetailsList) { if (purchase.pendingCompletePurchase) { inAppPurchase.completePurchase(purchase); } } }, onError: (error) { // 此处添加错误处理逻辑 }, ); final ProductDetailsResponse productDetailResponse = await inAppPurchase.queryProductDetails(_androidProductIds.toSet()); if (productDetailResponse.error != null) { queryProductError = productDetailResponse.error!.message; products = productDetailResponse.productDetails; purchases = <PurchaseDetails>[]; notFoundIds = productDetailResponse.notFoundIDs; purchasePending = false; loading = false; return; } products = productDetailResponse.productDetails; }
2. 修改getPastPurchases逻辑
移除内部的订阅创建,直接调用restorePurchases,依赖全局持久订阅接收结果:
Future<List<PurchaseDetails>> getPastPurchases(BuildContext context) async { if (purchases.isEmpty) { await inAppPurchase.restorePurchases(); // 短暂等待流返回结果(可选,或通过listenToPurchaseUpdated更新purchases后再返回) await Future.delayed(const Duration(milliseconds: 500)); } // 统一处理广告状态,无需区分平台 if (purchases.isEmpty) { Provider.of<AdState>(context, listen: false).toggleAds(context, true); } else { Provider.of<AdState>(context, listen: false).toggleAds(context, false); } return purchases; }
3. 完善listenToPurchaseUpdated方法
确保该方法正确将恢复的购买项添加到purchases列表:
void listenToPurchaseUpdated(BuildContext context, List<PurchaseDetails> purchaseDetailsList) { for (var purchase in purchaseDetailsList) { if (purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored) { // 避免重复添加相同商品的购买记录 if (!purchases.any((p) => p.productID == purchase.productID)) { purchases.add(purchase); } detectProVideo(context); } } }
4. 页面销毁时取消订阅
在State类的dispose方法中取消订阅,避免内存泄漏:
@override void dispose() { _subscription?.cancel(); super.dispose(); }
额外注意事项
- Android模拟器测试时,必须使用Google Play服务模拟器,普通Android模拟器无法正确处理内购恢复逻辑。
- 确认商品ID已在Google Play控制台正确配置,且测试账号已添加为授权测试账号。
内容的提问来源于stack exchange,提问作者Dani
相关产品推荐
相关产品推荐

