Flutter/Google Play中in_app_purchase恢复购买功能无响应问题
问题描述
我按照in_app_purchase包官方示例实现了应用内购买(IAP)功能,目前可正常购买商店内配置的唯一商品,但在测试「用户更换新手机」场景时,恢复购买功能无响应,也未抛出任何错误。
我采用Provider模式实现相关逻辑,各部分代码如下:
商店连接初始化代码
_isStoreAvailable = await _inAppPurchase.isAvailable();
购买流程实现代码
Future<void> initializePurchase() async { ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(_productIds); if (response.notFoundIDs.isNotEmpty) { // Handle the error. print("not found product in store!)"); } else { PurchaseParam purchaseParam = PurchaseParam( productDetails: response.productDetails.first, ); _inAppPurchase.buyNonConsumable(purchaseParam: purchaseParam); } }
购买状态监听代码
final Stream<List<PurchaseDetails>> purchaseUpdated = _inAppPurchase.purchaseStream; _subscription = purchaseUpdated.listen((List<PurchaseDetails> purchaseDetailsList) { _listenToPurchaseUpdated(purchaseDetailsList); }, onDone: () { _subscription.cancel(); }, onError: (error) { print("listen error: $error"); });
状态监听回调处理逻辑
Future<void> _listenToPurchaseUpdated( List<PurchaseDetails> purchaseDetailsList) async { for (final PurchaseDetails purchaseDetails in purchaseDetailsList) { if (purchaseDetails.status != PurchaseStatus.pending) { if (purchaseDetails.status == PurchaseStatus.error) { //handle error } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { setLicense(StoreLicenseStatus.paid); notifyListeners(); if (purchaseDetails.pendingCompletePurchase) { await _inAppPurchase.completePurchase(purchaseDetails); } } } } }
恢复购买触发代码
Future<void> restorePurchases() async { await _inAppPurchase.restorePurchases(); }
目前除恢复购买功能外其余逻辑均运行正常:购买商品后清空应用数据,再次启动应用尝试购买时,商店会弹出「你已拥有该商品」的提示,说明商店侧可正常识别历史购买记录,但点击恢复按钮后无任何响应,该问题在Pixel 4 API 32模拟器和真机环境下均可复现。
问题原因与修复方案
你的实现遗漏了Android平台恢复购买的必填逻辑,以及状态处理的边界case,具体问题点和修复方式如下:
- 核心问题:Android端调用
restorePurchases前,必须先调用queryProductDetails拉取商品信息,否则Google Play不会返回任何历史购买记录,也不会抛出错误。你现在的恢复购买方法直接调用restorePurchases,没有提前查询商品,是Android端无响应的直接原因。
修复后的恢复购买代码:Future<void> restorePurchases() async { // 必须在调用restorePurchases前完成商品查询 ProductDetailsResponse response = await _inAppPurchase.queryProductDetails(_productIds); if (response.error != null || response.notFoundIDs.isNotEmpty) { print("查询商品失败,无法恢复购买"); return; } await _inAppPurchase.restorePurchases(); } - 状态处理缺失:你的状态监听回调没有处理
PurchaseStatus.canceled状态,恢复过程中如果出现用户取消、鉴权失败的情况,现有代码会直接跳过,没有任何日志提示,不利于排查问题。
修复方式:在状态判断分支里补充canceled状态的日志打印:if (purchaseDetails.status == PurchaseStatus.error) { print("购买错误: ${purchaseDetails.error?.message}"); } else if (purchaseDetails.status == PurchaseStatus.canceled) { print("操作取消"); } else if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) { // 保留原有权益发放逻辑 } - 测试环境校验:测试恢复购买时,必须使用已经成功完成过该非消耗品购买的测试账号登录Google Play,且测试账号已经被加入应用的内测/封闭测试名单,否则也会出现无返回的情况。
- 商品配置校验:确认你在商店后台配置商品时,商品类型设置为非消耗型,如果错设为消耗型商品,调用恢复购买接口也不会返回历史订单。
修复完成后,先冷启动应用,确保purchaseStream的监听已经完成注册,再点击恢复购买按钮,就能在监听回调里收到PurchaseStatus.restored状态的订单,完成权益发放。
内容的提问来源于stack exchange,提问作者under
相关产品推荐
相关产品推荐

