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

Flutter中Apple订阅服务器端验证:如何区分新旧购买?

区分Flutter订阅新购买与已验证旧购买的解决方案

针对Apple每次启动应用推送所有历史订阅交易的问题,核心思路是追踪已处理的交易ID,结合本地或后端存储实现过滤,以下是具体可行方案:

1. 本地存储记录已处理交易ID

利用SharedPreferences或本地数据库(如Hive)保存所有已验证过的交易ID,每次收到交易时先检查是否已处理,避免重复操作:

核心代码示例

import 'package:shared_preferences/shared_preferences.dart';
import 'package:in_app_purchase/in_app_purchase.dart';

void _listenToPurchaseUpdated(List<PurchaseDetails> purchases) {
  for (var purchase in purchases) {
    if (purchase.status == PurchaseStatus.purchased || purchase.status == PurchaseStatus.restored) {
      // 先校验交易是否已处理
      _checkTransactionProcessed(purchase.transactionId!).then((isProcessed) async {
        if (!isProcessed) {
          // 调用后端验证购买有效性
          final isValid = await _verifyPurchase(purchase);
          if (isValid) {
            // 更新用户订阅状态等业务逻辑
            _updateUserSubscription(purchase);
            // 标记该交易为已处理
            _markTransactionProcessed(purchase.transactionId!);
          }
        }
        // 关键:务必调用completePurchase,告知Apple交易已处理,避免重复推送
        await InAppPurchase.instance.completePurchase(purchase);
      });
    }
  }
}

// 检查交易ID是否已存在于本地存储
Future<bool> _checkTransactionProcessed(String transactionId) async {
  final prefs = await SharedPreferences.getInstance();
  final processedIds = prefs.getStringList('processed_sub_transactions') ?? [];
  return processedIds.contains(transactionId);
}

// 将已处理的交易ID存入本地
Future<void> _markTransactionProcessed(String transactionId) async {
  final prefs = await SharedPreferences.getInstance();
  final processedIds = prefs.getStringList('processed_sub_transactions') ?? [];
  if (!processedIds.contains(transactionId)) {
    processedIds.add(transactionId);
    await prefs.setStringList('processed_sub_transactions', processedIds);
  }
}

2. 后端维护用户交易记录(更可靠)

由于本身依赖后端验证购买,可直接在后端存储用户所有已验证的交易ID,移动端每次收到交易时先调用后端接口查询是否已处理:

核心逻辑

  • 后端新增接口:接收transactionId和userId,返回该交易是否已验证过
  • 移动端收到交易后,先调用此接口,未处理的再执行验证流程
  • 验证通过后,后端自动将该transactionId关联到用户记录中

这种方案不受设备更换影响,数据一致性更强,适合多设备场景。

关键注意事项

  • 必须调用completePurchase:无论交易是否需要处理,完成后都要调用此方法,否则Apple会持续推送该交易
  • 清理冗余记录:本地存储可定期清理超过一定期限(如1年)的交易ID,避免存储膨胀
  • 处理恢复购买:PurchaseStatus.restored状态的交易同样需要用交易ID过滤,避免重复恢复

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 03:37:36