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

Flutter iOS端IAP的serverVerificationData解码及交易列表获取求助

iOS App Store自动续期订阅收据解析方案

以下是可直接在Flutter中运行的代码,用于获取并解析iOS端App Store收据中的历史交易记录:

1. 依赖准备

确保pubspec.yaml中添加以下依赖:

dependencies:
  in_app_purchase: ^latest_version
  asn1lib: ^1.0.0
  convert: ^3.1.1

2. 完整实现代码

import 'dart:convert';
import 'dart:typed_data';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:asn1lib/asn1lib.dart';
import 'package:flutter/foundation.dart';

// 假设_inAppPurchase是已初始化的InAppPurchase实例
final InAppPurchase _inAppPurchase = InAppPurchase.instance;

Future<List<Map<String, dynamic>>> parseIOSReceipt() async {
  final List<Map<String, dynamic>> transactions = [];
  try {
    // 获取iOS平台扩展
    final iosPlatformAddition = _inAppPurchase.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
    
    // 刷新并获取收据数据
    final receiptData = await iosPlatformAddition.refreshPurchaseVerificationData();
    if (receiptData?.serverVerificationData == null || receiptData!.serverVerificationData.isEmpty) {
      debugPrint("未获取到有效收据");
      return transactions;
    }

    // Base64解码为字节数据
    final Uint8List binaryReceipt = base64.decode(receiptData.serverVerificationData);
    
    // 初始化ASN.1解析器
    final parser = ASN1Parser(binaryReceipt);
    final topLevelSeq = parser.nextObject() as ASN1Sequence;

    // 遍历顶级序列,定位交易记录集合(ASN.1类型标识为17)
    for (var element in topLevelSeq.elements) {
      if (element is ASN1Sequence) {
        final fieldType = (element.elements[0] as ASN1Integer).value;
        if (fieldType == 17) {
          // 提取交易记录集合
          final transactionSet = element.elements[1] as ASN1Set;
          
          // 解析每一条交易数据
          for (var transactionSeq in transactionSet.elements) {
            if (transactionSeq is ASN1Sequence) {
              final transactionMap = <String, dynamic>{};
              for (var field in transactionSeq.elements) {
                if (field is ASN1Sequence && field.elements.length >= 2) {
                  final subFieldType = (field.elements[0] as ASN1Integer).value;
                  final subFieldValue = field.elements[1];
                  
                  // 映射ASN.1字段到业务字段
                  switch (subFieldType) {
                    case 1: // 交易ID
                      transactionMap['transactionId'] = (subFieldValue as ASN1OctetString).stringValue;
                      break;
                    case 17: // 订阅过期时间(秒级时间戳)
                      if (subFieldValue is ASN1Integer) {
                        transactionMap['expiresDate'] = DateTime.fromMillisecondsSinceEpoch(subFieldValue.value * 1000);
                      }
                      break;
                    case 21: // 原始交易ID
                      transactionMap['originalTransactionId'] = (subFieldValue as ASN1OctetString).stringValue;
                      break;
                    case 24: // 产品ID
                      transactionMap['productId'] = (subFieldValue as ASN1OctetString).stringValue;
                      break;
                    // 可根据需求扩展更多字段映射
                  }
                }
              }
              if (transactionMap.isNotEmpty) transactions.add(transactionMap);
            }
          }
          break;
        }
      }
    }
  } catch (e) {
    debugPrint("收据解析失败: $e");
  }
  return transactions;
}

关键说明

  • App Store收据的ASN.1结构中,交易记录集中存储在类型标识为17的顶级字段内
  • 每条交易是独立的ASN1Sequence,内部通过整数类型标识区分不同字段(如1=交易ID、24=产品ID)
  • 时间戳字段为秒级格式,需转换为毫秒级后生成DateTime对象
  • 代码包含异常捕获逻辑,避免因收据格式异常导致崩溃

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 21:57:44