PAYATHON 2026

支付宝PC端网页扫码支付提示invalid-format如何解决?

支付老李

结论

invalid-format 表示支付宝网关收到的请求中,至少有一个参数不符合接口格式要求。错误页不会指出具体字段。在 PC 网站支付场景中,建议先检查以下内容:

  • biz_content 是否为合法 JSON,是否发生了重复转义或重复 URL 编码
  • total_amount、timestamp、timeout_express 等字段的格式是否正确
  • method、product_code、charset、version 是否符合电脑网站支付接口的要求
  • SDK 返回的 <form> 是否被模板引擎、JSON 序列化或前端代码改写
  • 修改参数后是否重新生成了签名

使用官方 SDK 时,建议直接输出 pageExecute() 返回的完整 HTML,不要自行拆解、拼接或再次编码表单。

常见的无效数据格式

1. biz_content 不是合法 JSON

alipay.trade.page.pay 的业务参数都放在 biz_content 中。以下情况可能引发格式错误:

  • 使用单引号拼接 JSON
  • JSON 末尾带有多余逗号
  • 金额等字符串没有正确加引号
  • 中文、双引号或反斜杠转义错误
  • JSON 被 HTML 转义为 &quot;
  • 整段 JSON 又经过一次 URL 编码
  • 把 JSON 字符串再次序列化,变成双层字符串

正确结构示例:

{
  "out_trade_no": "ORDER202609090001",
  "product_code": "FAST_INSTANT_TRADE_PAY",
  "total_amount": "99.00",
  "subject": "订单支付"
}

不要手工拼接 JSON。可以使用 SDK 提供的业务模型,也可以通过成熟的 JSON 序列化库生成。

2. total_amount 格式错误

金额应使用十进制字符串,例如:

0.01
99.00
128.50

以下写法通常不符合要求:

¥99.00
99元
1,299.00
99.001
NaN
null

业务代码中也不要先用 double 计算金额,再直接转成字符串,否则可能得到 99.00000000003 之类的结果。Java 可以使用 BigDecimal:

BigDecimal amount = new BigDecimal("99.00");
model.setTotalAmount(amount.setScale(2, RoundingMode.HALF_UP).toPlainString());

金额的实际精度和取值范围应以当前接入接口的参数文档为准。

3. 时间参数格式不正确

公共参数 timestamp 通常使用以下格式:

yyyy-MM-dd HH:mm:ss

例如:

2026-09-09 14:30:00

不要传入 Unix 时间戳、ISO 8601 时间或带毫秒的时间,例如:

1788935400
2026-09-09T14:30:00+08:00
2026-09-09 14:30:00.123

设置 timeout_express 时,应使用接口支持的相对时间格式,例如:

30m
1h

不要写成 "30分钟"、"1800" 或自定义日期字符串。具体可用格式以当前接口文档为准。

4. 混用了其他支付产品的参数

电脑网站支付通常使用:

method=alipay.trade.page.pay
product_code=FAST_INSTANT_TRADE_PAY
version=1.0
format=json
charset=UTF-8
sign_type=RSA2

如果项目还接入了手机网站支付、当面付或旧版即时到账接口,需要确认代码没有复用其他接口的 method、product_code 或业务参数结构。不同支付产品的参数不能直接混用。

5. 表单被二次处理

SDK 返回的通常是一段可以直接提交的 HTML 表单。以下操作容易破坏其中的隐藏字段:

  • 把表单放入 JSON 响应,再交给前端取出
  • 对整个表单进行 HTML 转义
  • 使用模板语法重新渲染 <input> 的值
  • 对参数再次调用 encodeURIComponent
  • 由前端读取隐藏字段并重新组装请求
  • 修改字段值后继续使用原来的 sign

服务端可以直接返回表单:

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

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);

AlipayTradePagePayModel model = new AlipayTradePagePayModel();
model.setOutTradeNo("ORDER202609090001");
model.setProductCode("FAST_INSTANT_TRADE_PAY");
model.setTotalAmount("99.00");
model.setSubject("订单支付");

request.setBizModel(model);

String form = alipayClient.pageExecute(request).getBody();

response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(form);

不要把 form 包装成下面这样的 JSON:

{
  "code": 0,
  "data": "<form>...</form>"
}

如果业务架构要求由前端发起跳转,可以让后端提供一个专门的支付入口。浏览器访问该入口后,由后端直接输出支付宝表单。

排查步骤

1. 检查浏览器实际提交的请求

打开浏览器开发者工具,在 Network 中找到提交给支付宝网关的请求,查看最终发送的表单字段,重点检查:

app_id
method
format
charset
sign_type
timestamp
version
sign
biz_content

应检查浏览器最终发出的数据,不能只看后端日志中的原始对象。

2. 单独校验 biz_content

将请求中的 biz_content 正常 URL 解码一次,再交给 JSON 解析器验证。解码结果应该是一个 JSON 对象:

{"out_trade_no":"ORDER202609090001","product_code":"FAST_INSTANT_TRADE_PAY","total_amount":"99.00","subject":"订单支付"}

如果得到下面这种形式,通常说明 JSON 被重复序列化了:

"{\"out_trade_no\":\"ORDER202609090001\",...}"

如果内容中出现大量 %22、%257B 或 &quot;,应检查 URL 编码和 HTML 转义分别执行了多少次。

3. 使用最小参数重新测试

先只保留必需的业务参数:

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

最小请求成功后,再逐项加入 body、timeout_express、passback_params 等可选字段。这样更容易确定是哪个字段导致了错误。

passback_params 的处理比较特殊。使用该字段时,应按接口要求编码,并确保参与签名的值与最终提交的值完全一致。

4. 确认签名前后的参数完全一致

签名生成后,所有参与签名的参数都不能再修改,包括:

  • JSON 中的空格和转义形式
  • 字符编码
  • 金额表示
  • 回调地址
  • 订单标题
  • biz_content 的完整字符串

只要参数发生变化,就必须重新生成 sign。如果字符集声明为 UTF-8,生成签名和发送请求时也应使用 UTF-8。

5. 核对环境配置

确认沙箱环境和正式环境没有混用,检查:

  • 网关地址
  • app_id
  • 应用私钥
  • 支付宝公钥
  • 应用签约产品

环境或密钥配置错误通常会表现为验签失败、权限异常或应用状态错误。不过在排查 invalid-format 时,也应同时核对这些配置,避免多个问题叠加。

仍然无法定位时需要保留的信息

提交技术支持前,可以记录以下内容:

  • 支付宝返回的错误代码和请求时间
  • 网关响应中的请求标识,例如页面提供的 request_id 或类似字段
  • 脱敏后的公共参数
  • 正常解码一次后的 biz_content
  • SDK 名称和实际使用版本
  • 当前使用的是沙箱环境还是正式环境
  • 浏览器实际发送的请求参数

不要提交应用私钥,也不要在公开日志中暴露完整签名、用户信息或敏感的回调参数。错误页只能说明请求中的数据格式不符合要求,具体原因仍要根据实际提交到网关的参数判断。

备注:内容仅供参考。