PAYATHON 2026

前端无法调起支付宝支付并提示订单参数异常

支付阿杰

结论

支付宝后台把 sign 显示为 ***,通常只是对敏感信息做了脱敏处理,不能据此判断签名有误。

目前后台只显示 - , - , -,问题中也没有提供实际请求参数,暂时无法确定究竟是哪个参数传错了。“订单参数异常,请重新下单”一般不是前端页面本身导致的,常见原因包括:

  • 前端传给支付宝 SDK 的内容不是服务端生成的完整订单字符串。
  • 订单字符串经过了 URL 解码、截断、重新拼接或转义。
  • biz_content 的字段类型、必填字段或 JSON 格式有误。
  • 支付产品、method 与前端调用方式不匹配。
  • 参与签名的参数与最终提交的参数不一致。
  • app_id、应用私钥、支付宝公钥或签名算法配置不匹配。

建议先检查前端实际传入支付宝 SDK 的原始订单字符串,并在服务端使用支付宝官方 SDK 重新生成支付参数。

sign 显示为 *** 是否正常

支付宝后台、日志平台或问题诊断页面通常会对签名、密钥等敏感字段做脱敏,因此 sign 可能显示为:

***

这只说明页面没有展示签名原文,不代表 sign 为空,也不代表签名错误。

要判断签名是否有问题,应查看支付宝返回的明确错误码,例如:

isv.invalid-signature

也可以检查服务端 SDK 是否抛出了签名或验签异常。仅凭 *** 无法认定签名错误。

首先确认支付产品是否匹配

不同支付产品对应不同的接口和前端调用方式,不能混用。

支付场景常见接口
App 支付alipay.trade.app.pay
手机网站支付alipay.trade.wap.pay
电脑网站支付alipay.trade.page.pay
当面付二维码alipay.trade.precreate

例如,前端使用支付宝 App SDK 时,服务端通常需要通过 alipay.trade.app.pay 生成完整的 orderString。如果服务端生成的是手机网站支付表单或电脑网站支付 URL,却交给 App SDK 调起,就可能出现订单参数异常。

检查前端传参方式

在 App 支付场景中,前端应将服务端返回的完整订单字符串直接传给支付宝 SDK:

const orderString = response.data.orderString;

// 不要解析、解码或重新拼接 orderString
Alipay.pay(orderString);

下面这些处理容易破坏签名:

// 错误示例:不要对订单字符串进行解码
const orderString = decodeURIComponent(response.data.orderString);

// 错误示例:不要拆成对象后再重新排序、拼接
const params = parseQueryString(response.data.orderString);
const rebuiltOrderString = buildQueryString(params);

// 错误示例:不要只传 biz_content
Alipay.pay(response.data.biz_content);

检查时要特别留意:

  • orderString 是否经过 decodeURIComponent() 处理。
  • + 是否被转换成空格。
  • %2B、%2F、%3D 等编码是否被重复解码。
  • 字符串中是否混入换行、引号或多余空格。
  • 网络层是否把订单字符串当作 URL 再次编码。
  • 前端是否漏传 sign、sign_type 或其他公共参数。

签名是根据一组确定的参数计算出来的。完成签名后,只要参与签名的内容发生变化,即使只是编码方式或空格不同,也可能导致订单不可用。

检查服务端生成的公共参数

以 App 支付为例,请求通常包含以下公共参数:

app_id
method
format
charset
sign_type
timestamp
version
notify_url
biz_content
sign

其中需要核对:

method=alipay.trade.app.pay
charset=utf-8
sign_type=RSA2
version=1.0

同时检查以下配置:

  1. app_id 是否属于当前使用的支付宝应用。
  2. 应用是否已经开通对应的支付产品。
  3. 沙箱应用、沙箱网关和沙箱账号是否配套使用。
  4. 正式环境是否误用了沙箱密钥或沙箱网关。
  5. 签名所用的应用私钥,是否与该 app_id 配置的应用公钥匹配。
  6. SDK 中配置的是支付宝公钥还是应用公钥,避免混淆两者。
  7. timestamp 格式是否符合接口要求,服务器时间是否明显异常。

检查 biz_content

App 支付的业务参数通常类似:

{
  "out_trade_no": "ORDER_202609090001",
  "total_amount": "99.00",
  "subject": "商品订单",
  "product_code": "QUICK_MSECURITY_PAY"
}

以下字段需要逐项检查。

out_trade_no

商户订单号不能为空,并且要符合当前接口对长度和字符范围的要求。订单号中不要包含换行、制表符或不可见字符。

如果同一订单号已经支付或关闭,或者此前曾用不同金额重复提交,也可能导致交易创建失败。具体原因应以支付宝返回的 sub_code 和 sub_msg 为准。

total_amount

金额建议用字符串传递,并保留合法的小数位:

"total_amount": "99.00"

不要传入以下值:

"total_amount": ""
"total_amount": "0"
"total_amount": "99元"

也不要直接用浮点运算拼接金额,以免出现精度问题:

// 可能产生浮点精度问题
const amount = 0.1 + 0.2;

可以先按“分”计算,再进行格式化:

const amountInFen = 9900;
const totalAmount = (amountInFen / 100).toFixed(2);

subject

订单标题不能为空,也应避免控制字符、过长内容和未经处理的特殊字符。

"subject": "会员充值"

product_code

App 支付通常使用:

"product_code": "QUICK_MSECURITY_PAY"

如果使用的不是 App 支付,就不能直接照用这个值,需要按对应接口的要求设置。

使用官方 SDK 生成订单字符串

不要自行排序、拼接和 URL 编码请求参数。下面以 Java 为例:

AlipayClient alipayClient = new DefaultAlipayClient(
    gatewayUrl,
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl(notifyUrl);

AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setTotalAmount("99.00");
model.setSubject("商品订单");
model.setProductCode("QUICK_MSECURITY_PAY");

request.setBizModel(model);

AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

if (!response.isSuccess()) {
    throw new IllegalStateException(
        "生成支付宝订单失败:" +
        response.getCode() + ", " +
        response.getSubCode() + ", " +
        response.getSubMsg()
    );
}

String orderString = response.getBody();

服务端应将 response.getBody() 原样返回给前端:

{
  "orderString": "app_id=...&biz_content=...&charset=utf-8&method=alipay.trade.app.pay&sign=...&sign_type=RSA2&timestamp=...&version=1.0"
}

前端收到后直接调用支付 SDK,不再改动其中的任何字符。

建议的排查步骤

1. 保存三份原始数据

分别记录:

  • 服务端完成签名后生成的完整订单字符串。
  • HTTP 响应中返回给前端的订单字符串。
  • 前端调用支付宝 SDK 前的订单字符串。

然后比较三者是否完全一致。日志不必记录完整的 sign,可以只保留长度和摘要,避免泄露签名:

function safeSignInfo(sign) {
  return {
    present: typeof sign === "string" && sign.length > 0,
    length: typeof sign === "string" ? sign.length : 0
  };
}

不要在公开页面发布应用私钥、完整签名或用户敏感信息。

2. 检查字符串是否被破坏

重点查找以下变化:

+ 变成空格
%2B 变成 +
%3D 变成 =
%25 被重复解码
中文或 JSON 被重复编码
字符串首尾增加引号
参数被重新排序

如果服务端生成的字符串与前端最终传入的值不完全一致,应先修复传输过程和前端处理逻辑。

3. 记录完整的支付宝响应

不要只记录 code,至少要保留:

code
msg
sub_code
sub_msg
request_id
out_trade_no

如果后台只显示 - , - , -,可能是请求没有有效形成、查询条件不完整,或者日志页面未能解析对应的错误字段。此时需要结合客户端 SDK 的同步返回值、服务端异常日志和支付宝返回的 request_id 继续排查。

4. 用最小参数重新测试

先只保留接口必需的字段:

{
  "out_trade_no": "唯一订单号",
  "total_amount": "0.01",
  "subject": "支付测试",
  "product_code": "QUICK_MSECURITY_PAY"
}

确认最小请求可以正常调起支付后,再逐项加入扩展参数。这样更容易判断问题是否来自超时时间、业务扩展参数、分账参数或其他可选字段。

注意事项

  • sign 显示为 *** 本身不能证明发生了故障。
  • 签名必须在服务端生成,应用私钥不能放在前端。
  • 不要通过截图公开完整订单字符串、用户信息、私钥或其他敏感数据。
  • 支付宝公钥与应用公钥用途不同,配置时不要混淆。
  • 参数完成签名后必须原样提交,不能再次修改、重新排序或重复编码。
  • 前端插件或跨端框架可能会隐式转换参数,需要查看最终调用原生支付宝 SDK 时传入的真实值。
  • 只有取得实际请求参数,以及支付宝返回的 code、sub_code 和 sub_msg,才能确定具体是哪个字段有误。当前的 - , - , - 无法定位到单一参数。

备注:内容仅供参考。