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

Flutter Android:如何用in_app_purchase检查订阅状态并解决错误

Flutter 订阅活跃状态检查解决方案

核心问题分析

你遇到的PurchaserInfo未定义错误,是因为新版in_app_purchase库已移除PurchaserInfo类,queryPastPurchases()方法现在返回QueryPurchaseDetailsResponse而非PurchaserInfo。另外,仅通过PurchaseStatus.purchased判断订阅活跃性并不准确——订阅是周期性服务,即使购买记录存在,过期后也不再处于活跃状态,必须结合订阅过期时间验证。

修复步骤与代码修正

1. 修正历史购买查询逻辑

替换_checkSubscriptionStatus方法,适配新版API并验证订阅过期时间:

Future<void> _checkSubscriptionStatus() async {
  try {
    // 新版API返回QueryPurchaseDetailsResponse
    final QueryPurchaseDetailsResponse response = await InAppPurchase.instance.queryPastPurchases();
    
    if (response.error != null) {
      Fluttertoast.showToast(msg: '查询失败: ${response.error!.message}');
      return;
    }

    bool isActive = false;
    final now = DateTime.now();

    for (var purchase in response.pastPurchases) {
      // 过滤目标订阅商品
      if (purchase.productID == 's2424') {
        // 订阅需满足:已购买/已恢复 + 未过期
        if (purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored) {
          // 获取订阅过期时间(跨平台字段一致)
          final expirationDate = purchase.subscriptionExpirationDate;
          if (expirationDate != null && expirationDate.isAfter(now)) {
            isActive = true;
            break;
          }
        }
      }
    }

    setState(() {
      _isSubscriptionActive = isActive;
    });
  } catch (e) {
    Fluttertoast.showToast(
      msg: 'Error: $e',
      gravity: ToastGravity.BOTTOM,
      timeInSecForIosWeb: 1,
      backgroundColor: Colors.red,
      textColor: Colors.white,
    );
  }
}

2. 完善订阅购买与恢复逻辑

在UpdatePlan中,修正订阅购买方法(订阅需用buySubscription),并在恢复购买后更新状态:

// 替换purchaseProduct方法
Future<void> purchaseProduct() async {
  try {
    final ProductDetails product = await _getProductDetails();
    final PurchaseParam purchaseParam = PurchaseParam(productDetails: product);
    // 订阅需调用buySubscription,而非buyNonConsumable
    await _inAppPurchase.buySubscription(purchaseParam: purchaseParam);
  } catch (error) {
    Fluttertoast.showToast(
      msg: 'Error al iniciar la compra: $error',
      gravity: ToastGravity.BOTTOM,
      timeInSecForIosWeb: 1,
      backgroundColor: Colors.red,
      textColor: Colors.white,
    );
  }
}

// 完善_handlePurchases中的订阅状态处理
void _handlePurchases(List<PurchaseDetails> purchaseDetailsList) {
  for (var purchaseDetails in purchaseDetailsList) {
    if (purchaseDetails.status == PurchaseStatus.purchased || purchaseDetails.status == PurchaseStatus.restored) {
      if (purchaseDetails.productID == _productId) {
        // 验证订阅是否过期
        final expirationDate = purchaseDetails.subscriptionExpirationDate;
        final isActive = expirationDate != null && expirationDate.isAfter(DateTime.now());
        setState(() {
          _isPlanPremiumActive = isActive;
        });
        // 更新Firebase时存储过期时间,方便后续验证
        if (_username != null) {
          _firestore.collection('users').doc(_username).update({
            'subscriptionActive': isActive,
            'subscriptionExpires': expirationDate,
          });
        }
      }
    } else if (purchaseDetails.status == PurchaseStatus.error) {
      Fluttertoast.showToast(
        msg: 'Error al realizar la compra: ${purchaseDetails.error}',
        gravity: ToastGravity.BOTTOM,
        timeInSecForIosWeb: 1,
        backgroundColor: Colors.red,
        textColor: Colors.white,
      );
    }
  }
  _purchaseUpdatedController.add(purchaseDetailsList);
}

3. 初始化时主动检查订阅状态

在UpdatePlan的initialize方法中,恢复购买后直接触发状态检查:

void initialize() async {
  final bool available = await _inAppPurchase.isAvailable();
  if (!available) {
    return;
  }

  // 恢复购买后主动处理状态
  final restoreResponse = await _inAppPurchase.restorePurchases();
  if (restoreResponse.pastPurchases.isNotEmpty) {
    _handlePurchases(restoreResponse.pastPurchases);
  }

  _subscription = _inAppPurchase.purchaseStream.listen((purchaseDetailsList) {
    _handlePurchases(purchaseDetailsList);
  }, onDone: () {
    _subscription.cancel();
  }, onError: (error) {
    // 处理流错误
  });
}

关键注意事项

  • 必须验证过期时间:PurchaseStatus.purchased仅代表用户曾购买订阅,不代表当前仍有效,必须通过subscriptionExpirationDate判断。
  • 服务端验证(推荐):客户端状态易被篡改,建议将订阅凭证(purchaseDetails.verificationData.serverVerificationData)发送到后端,调用Google Play/App Store官方API验证有效性。
  • 跨平台测试:iOS和Android的订阅回调逻辑一致,但需确保商品ID在双平台后台配置一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 08:10:58