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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:24:24