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
相关产品推荐
相关产品推荐

