flutter_inapp_purchase 支付报 ASDErrorDomain Code=825
结论
ASDErrorDomain Code=825 不是 flutter_inapp_purchase 定义的业务错误,而是 iOS App Store/StoreKit 返回的内部错误。No transactions in response 表示支付请求已经发出,但商店响应中没有可交付的 SKPaymentTransaction,插件只能将其映射为:
SKPaymentTransactionStateFailed
code: E_UNKNOWN
问题通常出在沙盒账号、商品配置、App Store Connect 协议状态或苹果沙盒服务。error callback 不能视为支付成功,需要先检查商店配置和沙盒环境。
优先排查步骤
1. 确认商品能够正常查询
发起购买前,先调用 getProducts。如果查询结果为空或不包含目标商品,就不要继续调用 requestPurchase。
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
Future<void> loadProducts() async {
const productIds = <String>[
'com.example.app.product1',
];
final products =
await FlutterInappPurchase.instance.getProducts(productIds);
for (final product in products) {
print(
'IAP product: id=${product.productId}, '
'title=${product.title}, price=${product.localizedPrice}',
);
}
if (!products.any((item) => item.productId == productIds.first)) {
throw StateError('App Store 未返回目标商品,停止发起支付');
}
}
如果查不到商品,重点检查以下内容:
- Bundle ID 是否与 App Store Connect 中的 App 完全一致。
- Product ID 是否逐字符一致,包括大小写。
- In-App Purchase 商品是否已完整创建,价格和销售地区等信息是否有效。
- 商品是否处于可测试状态,是否缺少必要的元数据。
- Xcode 工程是否启用了
In-App Purchasecapability。 - 当前安装的 App 是否使用与 App Store Connect 对应的签名和 Bundle ID。
2. 检查 App Store Connect 协议状态
进入 App Store Connect,检查付费应用协议、税务信息和收款信息是否还有待处理项目。协议尚未生效或账户资料不完整时,沙盒购买也可能收到异常响应。
但 Code=825 并不只对应某一种协议问题。协议状态只是常见检查项,不能仅凭这个错误码判断具体缺少了哪一项。
3. 重建沙盒测试环境
沙盒账号或设备上的商店会话异常,也可能造成 No transactions in response。可以依次执行以下操作:
- 退出当前 Sandbox Apple Account。
- 删除测试 App。
- 重启设备。
- 重新安装开发包或 TestFlight 包。
- 新建一个 Sandbox Tester 并用它测试。
- 在系统弹出的购买界面中登录沙盒账号。
不同 iOS 版本的沙盒账号入口可能位于“设置 → 开发者 → Sandbox Apple Account”,也可能位于 App Store 的相关设置中。不要用正式 Apple ID 代替 Sandbox Tester。
还要确认设备满足以下条件:
- 系统日期和时区正确。
- 网络可以正常访问 App Store。
- 没有代理、VPN、DNS 过滤或企业网络策略干扰 App Store 请求。
- “屏幕使用时间”没有禁止 App 内购买。
如果多个沙盒账号和多台设备同时失败,也要考虑苹果沙盒服务暂时异常。遇到这种情况,稍后再试通常比在客户端连续重试更有效。
正确初始化并监听交易
应在查询商品和发起支付之前注册交易监听,否则可能漏掉 StoreKit 回调。
import 'dart:async';
import 'package:flutter_inapp_purchase/flutter_inapp_purchase.dart';
StreamSubscription? _purchaseUpdatedSubscription;
StreamSubscription? _purchaseErrorSubscription;
Future<void> initializeIap() async {
await FlutterInappPurchase.instance.initialize();
_purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen((purchase) async {
print(
'purchase-updated: '
'productId=${purchase.productId}, '
'transactionId=${purchase.transactionId}, '
'transactionStateIOS=${purchase.transactionStateIOS}',
);
// 先把收据发送到服务端,通过 Apple 验证后再发放权益。
final verified = await verifyPurchaseOnServer(purchase);
if (!verified) {
print('Receipt verification failed');
return;
}
await deliverEntitlement(purchase.productId);
// 收据已保存、权益已正确处理后再结束交易。
await FlutterInappPurchase.instance.finishTransaction(purchase);
});
_purchaseErrorSubscription =
FlutterInappPurchase.purchaseError.listen((error) {
print(
'purchase-error: '
'responseCode=${error.responseCode}, '
'code=${error.code}, '
'message=${error.message}, '
'debugMessage=${error.debugMessage}',
);
// 这里只记录并提示失败,不能发放商品。
});
}
Future<void> buy(String productId) async {
try {
await FlutterInappPurchase.instance.requestPurchase(productId);
} catch (error, stackTrace) {
print('requestPurchase failed: $error');
print(stackTrace);
}
}
Future<bool> verifyPurchaseOnServer(dynamic purchase) async {
// 将 transactionReceipt 等凭据提交到服务端验证。
return false;
}
Future<void> deliverEntitlement(String? productId) async {
// 根据业务逻辑发放权益,并保证重复调用不会重复发放。
}
Future<void> disposeIap() async {
await _purchaseUpdatedSubscription?.cancel();
await _purchaseErrorSubscription?.cancel();
await FlutterInappPurchase.instance.finalize();
}
不同小版本访问这些流的方式可能稍有不同。如果使用 5.2.0 时 IDE 提示 purchaseUpdated 或 purchaseError 不是静态成员,应按照该版本的声明,从 FlutterInappPurchase.instance 获取。交易处理原则不受影响。
检查未完成交易
StoreKit 交易必须在业务处理完成后调用 finishTransaction。如果此前购买成功后没有结束交易,StoreKit 可能会在应用下次启动时重新投递。
处理时遵循以下原则:
purchased:验证收据并发放权益,随后结束交易。restored:恢复权益并确认无误,随后结束交易。failed:记录具体错误,不发放权益;底层应结束失败交易。- 不要刚收到回调就结束成功交易,否则应用崩溃或网络中断时可能失去发货机会。
- 发货逻辑必须使用
transactionId等信息保证幂等,避免重复发放。
反复调用 requestPurchase 无法清理旧交易,还可能让测试状态更难判断。
如何缩小问题范围
可以根据排查结果判断问题所在:
getProducts返回空:优先检查 Product ID、Bundle ID、商品状态和 App Store Connect 配置。- 商品可以查询,但所有沙盒账号都报
Code=825:检查协议状态、网络环境和苹果沙盒服务。 - 只有一个账号失败:很可能是 Sandbox Tester 状态异常,可以换新账号验证。
- 只有一台设备失败:清理沙盒登录状态,重装 App,并检查设备限制和网络。
- 已在支付面板确认购买,但仍进入
SKPaymentTransactionStateFailed:按失败处理,不能仅凭用户看到扣款界面就发放权益。 - 收到
purchaseUpdated,但服务端验证失败:这是收据验证问题,与当前Code=825不在同一个处理阶段。
如果检查配置、更换 Sandbox Tester、重装应用并跨设备测试后仍能稳定复现,请保存发生时间、时区、Bundle ID、Product ID、iOS 版本和完整设备日志,再向 Apple Developer Support 提交问题。ASDErrorDomain 是苹果的内部错误域,flutter_inapp_purchase 无法在客户端修复 825,也不能将其转换为成功交易。
备注:内容仅供参考。
