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

如何通过Flutter in_app_purchase检测Google Play订阅取消状态

如何通过Flutter in_app_purchase检测Google Play订阅取消

核心问题说明

从Google Play商店手动取消订阅时,in_app_purchase的purchaseStream不会主动推送取消事件,因此必须主动查询用户的历史购买记录来同步订阅状态,不能仅依赖购买/恢复时的事件触发。

另外注意:你的代码中调用buyConsumable购买订阅是错误的——订阅属于非消耗品/订阅类产品,应使用buySubscription方法。

解决方案步骤

1. 修正订阅购买方法

将购买逻辑中的buyConsumable替换为buySubscription:

buySubscription(String id) async{
  PurchaseParam purchaseParam = PurchaseParam(
      productDetails: _products.firstWhere((element) => element.id == id));
  try {
    // 订阅需使用buySubscription,而非消耗品购买方法
    await _iap.buySubscription(purchaseParam: purchaseParam);
  } catch (e) {
    print('Error while buying ${e}');
  }
}

2. 添加主动查询订阅状态的方法

新增方法用于查询历史购买记录,校验目标订阅的有效性:

Future<void> checkSubscriptionStatus() async {
  if (!availble) return;
  
  // 查询所有历史购买记录
  final QueryPurchaseDetailsResponse response = await _iap.queryPastPurchases();
  
  if (response.error != null) {
    print('查询历史购买失败: ${response.error}');
    return;
  }
  
  // 检查目标订阅是否存在且有效
  bool isSubscribed = response.pastPurchases.any((purchase) {
    if (purchase.productID != subscriptionId) return false;
    
    // Google Play订阅需检查自动续费状态和到期时间
    if (purchase is GooglePlayPurchaseDetails) {
      // isAutoRenewing为false表示已取消订阅
      return purchase.isAutoRenewing && !_isSubscriptionExpired(purchase);
    }
    
    // 其他平台(如iOS)可补充对应逻辑
    return purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored;
  });
  
  // 更新应用内订阅状态
  setPurchases(fASubscribed: isSubscribed);
  notifyListeners();
}

// 辅助方法:判断订阅是否过期
bool _isSubscriptionExpired(GooglePlayPurchaseDetails purchase) {
  if (purchase.subscriptionPeriod == null || purchase.transactionDate == null) return true;
  
  final transactionDate = DateTime.parse(purchase.transactionDate!);
  final period = purchase.subscriptionPeriod!;
  
  DateTime expiryDate = transactionDate;
  switch (period.unit) {
    case GooglePlaySubscriptionPeriodUnit.day:
      expiryDate = expiryDate.add(Duration(days: period.value));
      break;
    case GooglePlaySubscriptionPeriodUnit.week:
      expiryDate = expiryDate.add(Duration(days: period.value * 7));
      break;
    case GooglePlaySubscriptionPeriodUnit.month:
      expiryDate = expiryDate.add(Duration(days: period.value * 30)); // 简化处理,可使用更精确的日期库优化
      break;
    case GooglePlaySubscriptionPeriodUnit.year:
      expiryDate = expiryDate.add(Duration(days: period.value * 365));
      break;
  }
  
  return expiryDate.isBefore(DateTime.now());
}

3. 在关键时机触发状态检查

  • 应用初始化完成后调用:
Future initiazlize() async {
  final Stream<List<PurchaseDetails>> purchaseUpdated = _iap.purchaseStream;

  _subscription = purchaseUpdated.listen((purchaseDetailsList) async {
    _purchases.addAll(purchaseDetailsList);
    _listenToPurchaseUpdated(purchaseDetailsList);
    notifyListeners();
  });
  
  availble = await _iap.isAvailable();
  notifyListeners();
  if (availble == true) {
    print("Is store availble ${availble}");
    await _getProducts();
    // 初始化时同步订阅状态
    await checkSubscriptionStatus();
  }
}
  • 应用从后台回到前台时调用(通过WidgetsBindingObserver实现):
// 混入WidgetsBindingObserver监听应用生命周期
class YourProvider with ChangeNotifier, WidgetsBindingObserver {
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed && availble) {
      // 回到前台时同步订阅状态
      checkSubscriptionStatus();
    }
  }
  
  // 初始化时注册观察者
  Future initiazlize() async {
    WidgetsBinding.instance.addObserver(this);
    // ... 原有初始化逻辑
  }
  
  // 销毁时移除观察者
  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    _subscription?.cancel();
    super.dispose();
  }
}

4. 优化购买事件监听逻辑

在购买状态更新后,主动同步订阅状态:

void _listenToPurchaseUpdated(List<PurchaseDetails> purchaseDetailsList) {
  purchaseDetailsList.forEach((PurchaseDetails purchaseDetails) async {
    switch (purchaseDetails.status) {
      case PurchaseStatus.pending:
        break;
      case PurchaseStatus.purchased:
      case PurchaseStatus.restored:
        if (purchaseDetails.productID == subscriptionId) {
          setPurchases(fASubscribed: true);
        }
        break;
      case PurchaseStatus.error:
        print(purchaseDetails.error);
        break;
      default:
        break;
    }

    if (purchaseDetails.pendingCompletePurchase) {
      await _iap.completePurchase(purchaseDetails);
    }
    
    // 每次购买事件后同步最新状态
    await checkSubscriptionStatus();
  });
}

关键注意事项

  • Google Play订阅取消状态同步存在延迟(通常几分钟),测试时需等待状态更新后验证。
  • 对于已过期的订阅,即使未主动取消,也需通过到期时间判断有效性。
  • 确保AndroidManifest配置了正确的Billing权限,且应用已关联Google Play开发者后台的订阅产品。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 10:15:58