PAYATHON 2026

支付宝 APP 支付参数异常,URL 编码及缺失参数如何排查?

支付小周

明确结论

传给支付宝客户端 SDK 的 orderInfo 通常是已完成签名和 URL 编码的订单字符串。字符串中出现 %7B、%22、%2B、%3D 等内容是正常的。

需要注意以下几点:

  • URL 编码的对象是各个参数的值。参数之间的 & 和键值之间的 = 必须保留。
  • 签名应使用编码前的参数值。生成签名后,再对包括 sign 在内的各个参数值进行 URL 编码。
  • 不要对完整的订单字符串再次编码,否则 % 会变成 %25,造成双重编码。
  • alipay.trade.app.pay 的 biz_content 通常需要包含固定值:
"product_code": "QUICK_MSECURITY_PAY"

题目中的 biz_content 缺少这个字段,应先补上。建议由服务端调用支付宝官方 SDK 的 sdkExecute() 生成完整订单字符串,APP 不再自行拼接、编码或签名。

URL 编码的正确形式

最终订单字符串大致如下:

app_id=...&biz_content=%7B...%7D&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=https%3A%2F%2Fexample.com%2Falipay%2Fnotify&sign=...&sign_type=RSA2&timestamp=2021-12-09%2017%3A17%3A03&version=1.0

其中:

  • biz_content 中的 JSON 经过 URL 编码是正常的。
  • sign 中的 +、/、= 分别编码为 %2B、%2F、%3D,这是必要的。
  • & 和 = 是订单参数的结构分隔符,不能随参数值一起编码。
  • APP 收到服务端返回的字符串后,应原样传给支付宝 SDK。

Android 端可以直接这样调用:

PayTask alipay = new PayTask(activity);
Map<String, String> result = alipay.payV2(orderInfo, true);

调用 payV2() 前不要再执行:

URLEncoder.encode(orderInfo, "UTF-8");

否则,原来的 %7B 会变成 %257B,支付宝解析时可能提示订单参数异常。

当前参数需要重点检查的问题

1. 补充 product_code

建议将 biz_content 调整为:

{
  "out_trade_no": "0622021120902000124",
  "total_amount": "8.00",
  "subject": "法式复古chic气质小众侧边不规则花边显瘦娃娃衫上衣短袖女",
  "product_code": "QUICK_MSECURITY_PAY"
}

APP 支付的 product_code 通常固定为:

QUICK_MSECURITY_PAY

如果使用支付宝官方服务端 SDK,应通过业务模型或请求对象设置这个字段,不要手工拼接。

2. 检查是否发生二次编码或提前解码

常见的二次编码如下:

正确:biz_content=%7B%22out_trade_no%22...
错误:biz_content=%257B%2522out_trade_no%2522...

第二种写法中,% 又被编码成了 %25。

还要检查 APP 或中间接口是否提前执行了 URL 解码。签名中的 %2B 解码后会变成 +,后续如果再按照表单规则解析,+ 可能被当成空格,最终导致验签失败。

建议分别记录以下三个位置的完整字符串,然后逐字比较:

  1. 服务端生成后的 orderInfo。
  2. APP 接收到的 orderInfo。
  3. 调用支付宝 SDK 前的 orderInfo。

这三个字符串应完全一致。

3. 确认签名顺序

正确流程如下:

  1. 准备所有待签名参数。
  2. 排除 sign 参数和空值参数。
  3. 按参数名的 ASCII 顺序排序。
  4. 使用编码前的参数值拼接待签名字符串。
  5. 使用应用私钥和 SHA256withRSA 生成 RSA2 签名。
  6. 将签名写入 sign。
  7. 分别对最终参数值进行 UTF-8 URL 编码。
  8. 使用 & 连接各项参数,再交给 APP。

待签名字符串类似:

app_id=应用ID&biz_content={"out_trade_no":"...","product_code":"QUICK_MSECURITY_PAY","subject":"...","total_amount":"8.00"}&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=https://example.com/alipay/notify&sign_type=RSA2&timestamp=2021-12-09 17:17:03&version=1.0

除非所使用的 SDK 明确要求,否则不要用已经包含 %7B、%22 的字符串计算签名。支付宝官方 SDK 通常会处理签名和最终编码,手工实现时最容易在这个环节出错。

4. 检查 notify_url

示例中的地址:

http://please-to-set-default-host.com/v1/notifies/alipay

看起来是占位地址。如果真实请求中使用的也是这个地址,需要将其替换为自己的异步通知地址,例如:

https://pay.example.com/v1/notifies/alipay

该地址应满足以下条件:

  • 可以从公网访问。
  • 不依赖登录状态。
  • 能接收支付宝发送的 POST 通知。
  • 不使用内网地址、localhost 或临时占位域名。
  • 收到通知后先验签,再更新订单状态。

notify_url 并非创建订单时所有场景下都绝对必填,但地址错误或无法访问会导致支付结果通知无法送达。它是否会直接触发当前提示,还要结合支付宝返回的具体错误码判断。

5. 检查时间和参数格式

timestamp 应使用以下格式:

yyyy-MM-dd HH:mm:ss

例如:

2021-12-09 17:17:03

最终传输时编码为:

2021-12-09%2017%3A17%3A03

同时还要确认:

  • 时间是当前请求时间,不是已经过期的固定值。
  • 服务器时钟准确。
  • charset 为 utf-8。
  • sign_type 为 RSA2。
  • version 为 1.0。
  • total_amount 是合法的金额字符串,如 "8.00"。
  • out_trade_no 在商户系统内唯一。
  • app_id、应用私钥和公钥配置属于同一个支付宝应用。

题目说明部分参数经过修改,因此无法只根据展示的 app_id、订单号和签名判断这些参数实际是否合法。

推荐的服务端生成方式

以支付宝 Java SDK 为例,服务端可以直接生成 APP 需要的订单字符串:

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi.alipay.com/gateway.do",
    APP_ID,
    APP_PRIVATE_KEY,
    "json",
    "UTF-8",
    ALIPAY_PUBLIC_KEY,
    "RSA2"
);

AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();

AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("0622021120902000124");
model.setTotalAmount("8.00");
model.setSubject("法式复古chic气质小众侧边不规则花边显瘦娃娃衫上衣短袖女");
model.setProductCode("QUICK_MSECURITY_PAY");

request.setBizModel(model);
request.setNotifyUrl("https://pay.example.com/v1/notifies/alipay");

AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

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

String orderInfo = response.getBody();

response.getBody() 就是应当返回给 APP 的内容。APP 收到后直接交给支付宝客户端 SDK,不要自行补充参数、重新排序、重新签名或再次执行 URL 编码。

建议的排查顺序

  1. 在 biz_content 中增加 product_code=QUICK_MSECURITY_PAY。
  2. 使用官方服务端 SDK 的 sdkExecute() 生成订单字符串。
  3. 确认 APP 没有对完整的 orderInfo 再次执行 URL 编码或解码。
  4. 检查服务端私钥、支付宝公钥和 app_id 是否对应同一个应用及环境。
  5. 确认使用 RSA2,也就是 SHA256withRSA。
  6. 将占位的 notify_url 替换为真实且能从公网访问的地址。
  7. 检查时间、金额、订单号和 JSON 格式。
  8. 获取支付宝 SDK 返回结果中的 resultStatus、memo 和 result,以及服务端接口返回的 code、sub_code、sub_msg,再根据具体错误码继续排查。

仅凭客户端显示的“商家订单参数异常”,无法确定唯一的故障点。缺少 product_code、签名原文不一致、订单字符串被二次编码,以及 app_id 与密钥不匹配,都是常见原因。

备注:内容仅供参考。