You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter in_app_purchase包升级后无法恢复已购项目求助

in_app_purchase升级后Android恢复购买失败问题修复

问题根源

你的代码存在几个关键问题,导致恢复购买时purchaseStream始终返回空列表:

  1. 订阅过早取消:在getPastPurchases的listen回调里立刻调用_subscription.cancel(),此时restorePurchases的异步结果还未返回,直接终止了流监听,无法接收到恢复的购买数据。
  2. Android平台处理缺失:仅在iOS分支处理了completePurchase,Android平台恢复购买后同样需要完成购买确认,否则系统会认为购买未完成,后续恢复请求无法正确返回数据。
  3. 监听逻辑不合理:仅在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 21:45:34