Flutter 使用 alipay_kit 调起支付提示系统繁忙
结论
能够跳转到支付宝,说明 alipay_kit 的客户端接入和应用唤起流程基本正常。支付宝提示“系统繁忙”,通常不是 Flutter 插件本身的问题,而是传入的支付订单字符串不合法。常见情况包括签名错误、参数被二次编码、缺少必填字段,或客户端传入的内容并不是完整的 orderInfo。
仅凭“系统繁忙”无法判断具体哪个字段有误。还需要查看插件返回的 resultStatus、memo,并结合服务端生成订单时的日志排查。
检查服务端返回值
支付宝 App 支付要求服务端生成并签名完整的订单参数。客户端收到的内容通常类似:
app_id=2021xxxxxxxxxxxx
&biz_content=%7B%22subject%22%3A%22...%22%2C%22out_trade_no%22%3A%22...%22%2C%22total_amount%22%3A%220.01%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%7D
&charset=utf-8
&method=alipay.trade.app.pay
&sign_type=RSA2
×tamp=2026-09-09%2012%3A00%3A00
&version=1.0
&sign=...
实际返回的内容一般是一整行。客户端不能只传下面这样的值:
2026090900000000000000
这类支付宝交易号或商户订单号不是 orderInfo,不能直接用来调起支付。
常见原因
1. 签名后修改了参数
服务端完成 RSA2 签名后,如果客户端或网关又修改了金额、订单号、空格、字符编码或参数顺序,支付宝验签就会失败。
正确的处理流程如下:
- 服务端组装业务参数。
- 服务端生成待签名字符串。
- 使用应用私钥完成 RSA2 签名。
- 按照支付宝的要求,对签名值及相关参数进行编码。
- 将完整的订单字符串原样返回客户端。
- Flutter 不再拼接或修改该字符串,直接将其传给插件。
2. 二次 URL 编码或错误解码
如果服务端已经对 sign 和 biz_content 做过编码,Flutter 又执行:
Uri.encodeComponent(orderInfo)
字符串中的 % 会被再次编码为 %25,支付宝因此无法正确解析参数。
也不要随意执行:
Uri.decodeComponent(orderInfo)
解码后,签名中的 + 可能被错误地处理成空格,导致验签失败。
3. 返回值带有多余的 JSON 包装
假设接口响应如下:
{
"code": 0,
"data": {
"orderInfo": "app_id=...&biz_content=...&sign=..."
}
}
传给插件的应当是 orderInfo 字段,而不是整个 JSON:
final orderInfo = response.data['data']['orderInfo'] as String;
同时检查字符串两端是否有额外的引号、换行或转义字符,例如:
"app_id=...&sign=..."
最外层的双引号不属于订单字符串,不能一并传入。
4. 应用配置与签名密钥不匹配
下面这些配置必须属于同一个支付宝开放平台应用:
app_id- 应用私钥
- 配置到支付宝开放平台的应用公钥或支付宝公钥证书
sign_type- 网关环境
如果服务端用 A 应用的私钥签名,却在参数中填写 B 应用的 app_id,客户端仍有可能打开支付宝,但支付请求无法通过校验。
5. 混用沙箱与正式环境
沙箱应用、沙箱账号、沙箱网关不能与正式环境混用。服务端使用沙箱配置时,客户端测试环境也必须与其对应。
6. App 支付参数不完整
biz_content 中通常需要正确提供以下参数:
{
"subject": "订单标题",
"out_trade_no": "唯一商户订单号",
"total_amount": "0.01",
"product_code": "QUICK_MSECURITY_PAY"
}
服务端需要检查金额格式、订单号唯一性和 product_code。具体哪些参数必填,应以当前的支付宝 App 支付接口文档为准。
Flutter 调用示例
不同版本的 alipay_kit API 可能略有差异。下面以常见的平台接口形式为例:
Future<void> startAlipay(String orderInfo) async {
if (orderInfo.trim().isEmpty) {
throw ArgumentError('orderInfo 不能为空');
}
final result = await AlipayKitPlatform.instance.pay(
orderInfo: orderInfo,
);
debugPrint('resultStatus: ${result.resultStatus}');
debugPrint('memo: ${result.memo}');
debugPrint('result: ${result.result}');
}
客户端应直接传入服务端返回的完整字符串:
final orderInfo = response.data['data']['orderInfo'] as String;
await AlipayKitPlatform.instance.pay(
orderInfo: orderInfo,
);
不要再对它进行编码、解码或重新拼接:
// 不建议
final encoded = Uri.encodeComponent(orderInfo);
// 不建议
final decoded = Uri.decodeComponent(orderInfo);
// 不建议
final modified = '$orderInfo&extra=value';
排查步骤
- 打印插件返回的
resultStatus和memo,不要只看支付宝页面上的“系统繁忙”。 - 确认传入的是完整的 App 支付订单字符串,而不是订单号、预下单编号或整个接口响应。
- 对比服务端最终生成的字符串和 Flutter 实际收到的字符串,确认两者完全一致。
- 检查字符串中是否有
%25、多余引号、换行、反斜杠,或+被替换为空格等情况。 - 在服务端核对 RSA2 签名过程,确认签名所用的原始参数与最终发送的参数一致。
- 检查
app_id、密钥、证书以及沙箱或正式环境是否匹配。 - 查看支付宝服务端接口返回的错误码和日志,不要只根据客户端的通用提示判断问题。
注意事项
应用私钥必须保存在服务端,不能放进 Flutter 工程。订单金额、订单号和签名也应由服务端生成。
客户端返回支付成功,只能用于界面提示。最终支付状态应由服务端通过支付宝异步通知或主动查询接口确认,不能仅凭客户端结果执行发货、充值到账等业务操作。
备注:内容仅供参考。
