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

Flutter中in_app_purchase包内购状态读取及退款状态获取咨询

关于Flutter in_app_purchase的两个问题解答

我来帮你一步步解决这两个问题,结合你的代码细节来说明:

1. 如何在Flutter中使用in_app_purchase包读取应用内购买状态

读取购买状态主要靠两个核心操作:监听实时购买更新流和查询历史购买记录,结合你的代码来优化:

(1)监听实时购买流

你代码里已经用到了purchaseUpdatedStream,但注意不要把订阅逻辑放在deliverProduct里——这个函数是用来交付商品的,应该在页面初始化时就启动监听,确保不会错过任何状态变化:

StreamSubscription<List<PurchaseDetails>>? _subscription;

@override
void initState() {
  super.initState();
  _startPurchaseListener();
  _checkPastPurchases(); // 初始化时同时查询历史
}

void _startPurchaseListener() {
  _subscription = InAppPurchaseConnection.instance.purchaseUpdatedStream.listen(
    (List<PurchaseDetails> purchaseDetailsList) {
      _handlePurchaseUpdates(purchaseDetailsList);
    },
    onDone: () => _subscription?.cancel(),
    onError: (error) {
      // 处理监听错误,比如网络问题
      print('购买流监听错误: $error');
    },
  );
}

void _handlePurchaseUpdates(List<PurchaseDetails> purchaseDetailsList) {
  for (var purchaseDetails in purchaseDetailsList) {
    switch (purchaseDetails.status) {
      case PurchaseStatus.pending:
        // 购买中,显示加载状态
        setState(() => _purchasePending = true);
        break;
      case PurchaseStatus.purchased:
        // 购买成功,交付商品
        deliverProduct(purchaseDetails);
        break;
      case PurchaseStatus.error:
        // 购买失败,提示用户
        setState(() => _purchasePending = false);
        _showError(purchaseDetails.error!);
        break;
      case PurchaseStatus.canceled:
        // 用户取消购买
        setState(() => _purchasePending = false);
        break;
    }
  }
}

(2)查询历史购买记录

不管用户是首次打开APP还是重新进入,都需要主动查询历史购买来恢复状态,比如用户卸载重装后,或者退款后需要更新权限:

Future<void> _checkPastPurchases() async {
  final QueryPurchaseDetailsResponse response = await InAppPurchaseConnection.instance.queryPastPurchases();
  
  if (response.error != null) {
    print('查询历史购买失败: ${response.error}');
    return;
  }

  setState(() {
    // 过滤掉无效的购买(比如已退款、已撤销的)
    _purchases = response.pastPurchases.where((purchase) {
      // 核心判断:只有状态为已购买且未被撤销的才有效
      return purchase.status == PurchaseStatus.purchased && !purchase.isRevoked;
    }).toList();

    // 同步你的专业版状态
    bool hasProAccess = _purchases.any((p) => p.productID == _kProId);
    if (!hasProAccess) {
      // 如果没有有效购买,移除专业版权限
      _revokeProAccess();
    }
  });
}

void _revokeProAccess() {
  // 这里执行降级逻辑,比如清除本地存储的专业版标记
  save_achat_deblocage(false); // 假设你的save_achat_deblocage支持传入状态
  setState(() {
    // 更新UI状态
  });
}

2. 处理退款后状态同步问题

你提到退款后插件无法获取信息,导致用户仍保留专业版——这是因为in_app_purchase的实时流不会主动推送退款通知,苹果/谷歌的退款事件需要主动查询或通过服务器端接收:

解决方案1:定期主动查询历史购买

在关键节点触发_checkPastPurchases函数,比如:

  • APP启动时
  • 用户进入付费功能页面时
  • 后台切换回前台时

这样能及时发现已退款的购买,同步本地权限状态。

解决方案2:服务器端验证(推荐)

客户端查询可能有延迟,更可靠的方式是通过服务器接收官方通知:

  • 苹果:配置App Store Server Notifications,当用户退款时,苹果会主动给你的服务器发送通知
  • 谷歌:配置Real-Time Developer Notifications,同样会实时推送退款事件

服务器收到通知后更新用户状态,客户端每次打开APP或访问付费功能时,从服务器拉取最新权限状态,这样就能彻底解决退款不同步的问题。

针对你现有代码的修改建议

你的deliverProduct函数可以保留,但要确保它只负责交付有效购买,同时把订阅逻辑移到初始化阶段,并且添加定期查询的逻辑,避免退款后状态残留。

内容的提问来源于stack exchange,提问作者Nitneuq

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:42:00