PAYATHON 2026

Flutter 使用 tobias插件接入支付宝国际支付的方案

支付阿杰

结论

tobias 不能直接当作“支付宝国际支付插件”使用。

它的主要作用是在 Flutter 端调用支付宝 App 的支付能力,不负责国际商户签约、跨境收单、币种换算、订单签名或异步通知。能否继续使用,取决于支付宝国际业务服务端创建订单后,返回的 orderString 是否仍能由当前支付宝客户端 SDK 执行。

常见的处理方式有三种:

  1. 如果支付宝国际产品仍会返回兼容支付宝 App SDK 的订单字符串,可以保留 tobias,但服务端需要改接相应的国际支付接口。
  2. 如果接入 Antom、Alipay+ 或 Hosted Checkout,支付通常通过收银台 URL、WebView 或系统浏览器完成,不再由 tobias 发起。
  3. 如果需要支持海外电子钱包、银行卡等多种支付方式,应接入支付宝国际产品提供的原生 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 在返回类型、方法签名或平台配置上可能存在差异,应以项目实际使用版本的接口为准。

服务端需要完成以下工作:

  1. 通过国际支付产品对应的网关创建订单。
  2. 按产品要求传递币种、金额和商户订单号等信息。
  3. 使用保存在服务端的密钥或证书完成签名。
  4. 将支付宝客户端可以执行的订单字符串返回给 Flutter。
  5. 接收并验证支付宝的异步通知。
  6. 主动查询订单状态,处理通知丢失、重复通知和支付超时。

只有支付宝或 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 的客户端调用层,但无法把国内支付宝支付自动转换成国际支付。能否用于国际支付,最终取决于商户签约的产品、服务端接口,以及官方规定的客户端接入方式。

备注:内容仅供参考。