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

