HyperPay 信用卡支付在 iOS live environment 中失败
结论
这个错误通常与信用卡或 OTP 页面无关。更常见的原因是:服务端创建 checkoutId 的环境,与 iOS SDK 查询支付会话的环境不一致。
例如:
- 服务端通过 HyperPay live 接口创建
checkoutId; - Android SDK 使用 live 模式,可以继续支付;
- iOS SDK 仍处于 test/sandbox 模式,因而会到测试环境查询 live
checkoutId; - 测试环境找不到该会话,于是返回:
No payment session found for the requested id
此外,checkoutId 已过期、被重复使用,或者 iOS 收到的 ID 并非刚创建的 ID,也可能导致同样的错误。只有先解决会话查询问题,支付流程才会进入 3-D Secure/OTP 验证。
重点检查项
1. 确认 iOS SDK 使用 live 模式
检查 Flutter 插件的 iOS 原生实现。HyperPay iOS SDK 初始化支付 Provider 时,需要明确指定 live 模式。不同 SDK 或插件版本的类名可能有所区别,常见写法如下:
let provider = OPPaymentProvider(mode: .live)
有些版本可能使用:
let provider = OPPaymentProvider(mode: OPPProviderMode.live)
如果代码使用 .test,或者插件默认采用测试模式,需要将其改为 .live。
只修改 Dart 层的环境变量可能不够。部分 Flutter HyperPay 插件会在 Android 和 iOS 端分别初始化原生 SDK。Android 已切换到 live,并不表示 iOS 也会自动使用 live。
2. 保证 checkoutId 与 SDK 环境一致
checkoutId 应由后端创建。Flutter 客户端不应直接携带 HyperPay access token 请求支付接口。
调用逻辑可以写成:
final checkoutId = await backend.createCheckout(
amount: '100.00',
currency: 'SAR',
);
await hyperPay.pay(
checkoutId: checkoutId,
paymentBrand: 'VISA',
);
后端创建 checkout 时,以下配置必须来自同一环境:
- 请求地址;
- access token;
entityId;- 商户账户及支付品牌配置;
- iOS SDK 的 Provider mode。
示意代码:
const isLive = process.env.HYPERPAY_ENV === 'live';
const checkoutBaseUrl = isLive
? process.env.HYPERPAY_LIVE_BASE_URL
: process.env.HYPERPAY_TEST_BASE_URL;
const response = await fetch(`${checkoutBaseUrl}/v1/checkouts`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HYPERPAY_ACCESS_TOKEN}`,
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
entityId: process.env.HYPERPAY_ENTITY_ID,
amount: '100.00',
currency: 'SAR',
paymentType: 'DB',
}),
});
const data = await response.json();
return data.id;
实际使用的生产域名、请求参数和认证方式,应以商户所在区域及当前 HyperPay 接入文档为准。不要直接沿用测试环境的 URL 或凭据。
3. 每次支付都创建新的 checkoutId
不要缓存、复用或持久化旧的 checkoutId。用户点击支付时,应由后端即时创建新会话:
Future<void> startPayment() async {
final checkoutId = await paymentApi.createCheckout();
if (checkoutId.isEmpty) {
throw StateError('Backend did not return a checkoutId');
}
await hyperPay.pay(
checkoutId: checkoutId,
paymentBrand: 'VISA',
);
}
以下情况都可能触发相同的错误:
checkoutId已超过有效期;- 使用同一个
checkoutId重复发起支付; - Flutter 状态中仍保留上一次支付的 ID;
- 创建 checkout 后等待时间过长;
- iOS 传递参数时取错字段或传入空值。
错误信息中的 “more than 30min ago” 是网关提供的排查提示。具体有效期取决于当前接入配置和官方文档,但实际处理时,仍应在每次支付前创建新的 ID。
4. 检查 iOS 与 Android 实际收到的值
可以记录以下信息,但不要输出 token、完整卡号等敏感内容:
debugPrint('paymentEnv=live');
debugPrint('checkoutId=$checkoutId');
debugPrint('platform=${Platform.operatingSystem}');
检查以下问题:
- iOS 收到的
checkoutId是否为空或被截断; - iOS 是否误读了测试环境配置;
- Android 和 iOS 是否请求同一个后端;
- Release、Debug、Scheme 或 Flavor 是否加载了不同配置;
- 服务端是否成功创建 checkout,而不是误将响应中的其他字段作为
id。
生产环境不宜长期记录完整的 checkoutId。排查时可以只保留首尾几位,或改用请求追踪 ID。
OTP 页面还需要检查的配置
OTP 页面通常属于 3-D Secure 验证流程。SDK 成功找到 checkout 会话并提交银行卡支付后,如果发卡行要求验证,才会显示该页面。当前错误发生在 OTP 流程之前,直接打开 OTP 页面无法解决问题。
会话环境修复后,如果 iOS 仍然无法从验证页面返回应用,再检查:
shopperResultUrl是否正确;- iOS URL Scheme 或 Universal Link 是否已经配置;
Info.plist中的回调 Scheme 是否与支付请求一致;AppDelegate或SceneDelegate是否将回调 URL 交给 HyperPay SDK;- live 商户账户是否已启用相应的支付品牌和 3-D Secure;
- iOS App 的 bundle identifier、回调地址等是否符合商户后台配置。
不同版本的 HyperPay iOS SDK 可能采用不同的回调处理方式。应按照当前插件依赖的 SDK 文档进行配置,不要直接套用其他版本的 AppDelegate 示例。
推荐的排查顺序
- 在 iOS 原生代码中确认 Provider mode 为
.live。 - 确认服务端使用 live URL、live token 和 live
entityId创建 checkout。 - 清除客户端缓存,并在每次支付前重新获取
checkoutId。 - 比较 Android 和 iOS 实际访问的后端地址及环境标识。
- 确认 iOS Release Scheme 没有加载测试配置。
- 会话错误消失后,再检查 3-D Secure 回调和 URL Scheme。
如果 Android 使用同一后端返回的全新 checkoutId 可以完成支付,而 iOS 收到该 ID 后立即报 No payment session found,应优先检查 iOS 是否仍连接到 test 环境。
备注:内容仅供参考。
