Flutter 使用 tobias插件接入支付宝国际支付的方案
结论
tobias 不能直接当作“支付宝国际支付插件”使用。
它的主要作用是在 Flutter 端调用支付宝 App 的支付能力,不负责国际商户签约、跨境收单、币种换算、订单签名或异步通知。能否继续使用,取决于支付宝国际业务服务端创建订单后,返回的 orderString 是否仍能由当前支付宝客户端 SDK 执行。
常见的处理方式有三种:
- 如果支付宝国际产品仍会返回兼容支付宝 App SDK 的订单字符串,可以保留
tobias,但服务端需要改接相应的国际支付接口。 - 如果接入 Antom、Alipay+ 或 Hosted Checkout,支付通常通过收银台 URL、WebView 或系统浏览器完成,不再由
tobias发起。 - 如果需要支持海外电子钱包、银行卡等多种支付方式,应接入支付宝国际产品提供的原生 SDK、托管收银台,或选择支持这些渠道的支付服务商。
不建议只修改 Flutter 端参数就直接上线。应先根据商户主体、目标国家、付款币种和支付产品,向支付宝或 Antom 确认客户端接入方式。
为什么不能直接复用国内支付流程
国内支付宝 App 支付通常采用以下流程:
Flutter App
→ 业务服务端创建并签名订单
→ 返回 orderString
→ tobias 调用支付宝客户端
→ 支付宝异步通知业务服务端
在这套流程中,tobias 只负责“调用支付宝客户端”。国际支付还需要确认以下事项:
- 商户是否已签约相应的跨境或国际收单产品
- 商户主体所在的国家或地区
- 支持哪些交易币种和结算币种
- 使用支付宝中国钱包,还是 Alipay+ 合作钱包
- 国际接口返回订单字符串、支付 URL,还是要求调用原生 SDK
- 签名算法、证书、网关地址和异步通知规则
- iOS、Android SDK 是否支持目标地区和支付产品
即使 Flutter 端成功拉起支付宝,也不代表国际支付已经正确接入。订单创建、签名、汇率、结算和最终支付状态,仍由服务端和商户签约的产品决定。
方案一:国际接口返回兼容的 orderString
如果支付宝提供的国际支付接口明确返回可由支付宝 App SDK 执行的订单字符串,可以考虑保留 tobias。
Flutter 端的逻辑与国内支付基本一致:
import 'package:tobias/tobias.dart' as tobias;
Future<void> payWithAlipay() async {
// orderString 必须由业务服务端生成,不能在 App 中保存私钥并签名。
final String orderString = await requestOrderStringFromServer();
final result = await tobias.aliPay(orderString);
// 此结果只适合提示用户,不应作为最终入账依据。
print(result);
}
不同版本的 tobias 在返回类型、方法签名或平台配置上可能存在差异,应以项目实际使用版本的接口为准。
服务端需要完成以下工作:
- 通过国际支付产品对应的网关创建订单。
- 按产品要求传递币种、金额和商户订单号等信息。
- 使用保存在服务端的密钥或证书完成签名。
- 将支付宝客户端可以执行的订单字符串返回给 Flutter。
- 接收并验证支付宝的异步通知。
- 主动查询订单状态,处理通知丢失、重复通知和支付超时。
只有支付宝或 Antom 的产品文档明确说明订单可以交给支付宝 App SDK 执行,才适合继续采用这种方式。
方案二:使用国际托管收银台
国际支付也常通过托管收银台接入。服务端创建支付后取得收银台 URL,再由 App 打开该地址。
Flutter 可以调用系统浏览器:
import 'package:url_launcher/url_launcher.dart';
Future<void> openInternationalCheckout() async {
final String checkoutUrl = await createPaymentOnServer();
final Uri uri = Uri.parse(checkoutUrl);
final bool opened = await launchUrl(
uri,
mode: LaunchMode.externalApplication,
);
if (!opened) {
throw StateError('无法打开支付页面');
}
}
也可以使用 WebView,但需要先确认支付页面是否允许嵌入,以及登录、跳转、Cookie 和第三方钱包唤起等功能能否正常运行。部分支付页面要求通过系统浏览器打开,不能直接嵌入 WebView。
订单必须由业务服务端创建。下面仅展示代码结构,接口地址和字段需要替换为所签约产品的实际定义:
app.post('/api/payments', async (req, res) => {
const merchantOrderNo = createUniqueOrderNo();
const paymentRequest = {
merchantOrderNo,
amount: {
currency: req.body.currency,
value: req.body.amount
},
returnUrl: 'myapp://payment/return',
notifyUrl: 'https://api.example.com/payment/notify'
};
// signRequest 和 createInternationalPayment
// 应按照实际支付产品的官方文档实现。
const response = await createInternationalPayment(
paymentRequest,
signRequest(paymentRequest)
);
res.json({
checkoutUrl: response.checkoutUrl
});
});
支付完成后,可以通过 App Link、Universal Link 或自定义 URL Scheme 返回应用。不过,客户端收到的回跳结果仍不能作为最终支付凭证。
方案三:接入官方原生 SDK 或支付服务商
如果国际支付产品要求调用专用的 Android 或 iOS SDK,而现有 Flutter 插件没有相应封装,可以选择:
- 通过 Flutter Platform Channel 自行封装原生 SDK
- 寻找明确支持该国际支付产品的 Flutter 插件
- 使用支付宝或 Antom 提供的托管收银台
- 接入同时支持支付宝、海外钱包和银行卡的第三方支付服务商
选择第三方 Flutter 插件时,不能只看名称中是否包含 Alipay,还需要核对:
- 插件对应的是国内支付宝 App Pay,还是国际支付产品
- 内置的 Android、iOS SDK 版本
- 是否支持目标国家、地区和钱包
- 是否兼容当前 Flutter、Android 和 iOS 构建环境
- 插件近期是否仍在维护
- 回调、取消、失败和异常流程是否完整
- 是否需要额外配置原生项目
推荐的实施步骤
先在支付宝或 Antom 商户后台确认已经签约的产品,以及目标用户实际使用的支付方式。
接着向支付平台确认服务端创建订单后,客户端应该执行什么操作:
- 返回
orderString:评估能否继续使用tobias - 返回
checkoutUrl:使用浏览器或符合要求的 WebView - 要求调用专用 SDK:接入官方 SDK,并编写 Flutter 桥接
- 由服务端直接完成支付:Flutter 只负责展示支付方式和查询结果
接口联调完成后,还需要测试以下情况:
- 不同币种和金额精度
- 支付成功、失败和用户取消
- App 未安装支付宝
- 支付过程中切换应用或终止进程
- 网络超时和重复提交
- 异步通知重复到达或未到达
- 客户端回跳成功,但服务端尚未确认
- 退款、撤销和订单关闭
注意事项
支付私钥、证书和签名逻辑必须保存在业务服务端,不能打包进 Flutter 应用。
客户端返回的 resultStatus、回跳参数或“支付成功”页面只能用于交互提示。最终订单状态应以服务端收到并通过验签的异步通知为主,同时配合主动查单确认。
金额计算不要直接使用 double。可以使用最小货币单位的整数,也可以按支付接口要求提交十进制定点字符串:
final int amountInMinorUnit = 1099; // 例如 10.99,具体小数位由币种决定
不同币种使用的小数位数并不完全相同,不能默认所有币种都有两位小数。
tobias 可以继续充当兼容支付宝 App SDK 的客户端调用层,但无法把国内支付宝支付自动转换成国际支付。能否用于国际支付,最终取决于商户签约的产品、服务端接口,以及官方规定的客户端接入方式。
备注:内容仅供参考。
