PAYATHON 2026

手机网站支付提示订单参数异常,如何解决?

支付阿杰

结论

出现“订单参数异常,请重新下单后再发起付款”,一般说明支付平台没有通过订单参数校验。此时需要重新生成订单,并检查必填参数、金额格式、订单号、时间、签名和请求编码。

不要反复提交原支付链接。旧链接可能已经过期,参数可能被修改,对应订单也可能已经关闭。具体原因要结合支付接口的同步响应、异步通知和平台日志中的错误码判断,不能只看页面提示。

常见原因

订单参数缺失或格式错误

手机网站支付要求业务参数完整,格式也要符合接口规范。以支付宝手机网站支付接口 alipay.trade.wap.pay 为例,常见参数包括:

  • out_trade_no:商户订单号,在同一商户下应保持唯一。
  • total_amount:订单金额,应使用合法的十进制金额格式。
  • subject:订单标题,不能为空。
  • product_code:手机网站支付通常填写 QUICK_WAP_WAY。

接口的必填项和字段长度限制可能不同,请以当前接入接口的官方文档为准。

金额不合法

常见情况有:

  • 金额为空、为零或小于零。
  • 金额带有千位分隔符,例如 1,000.00。
  • 使用全角小数点或混入其他非数字字符。
  • 浮点数计算造成精度误差。
  • 前端展示的金额与服务端提交的金额不同。

金额应由服务端计算,并使用十进制类型处理。累计商品金额时,不要直接使用二进制浮点数。

商户订单号重复或订单状态异常

如果 out_trade_no 已对应一笔支付成功、已关闭或参数不同的订单,再次使用可能产生冲突。重新发起付款前,应先查询原订单状态:

  • 已支付:不要再次创建支付。
  • 支付中:让用户等待,或继续查询支付结果。
  • 已关闭或已失效:生成新的商户订单号。
  • 未创建成功:修正参数后重新发起。

不要只在前端刷新页面,然后重复提交同一订单。

签名或字符编码不一致

签名问题有时会直接显示为验签错误,有时只表现为支付页面无法识别订单。排查时应检查:

  • 参与签名的参数是否与实际发送的参数完全一致。
  • 参数是否在签名完成后又被修改。
  • 应用私钥和支付平台公钥是否正确。
  • sign_type 是否与 SDK 配置一致。
  • 字符编码是否统一为 UTF-8。
  • 参数是否被重复进行 URL 编码。
  • JSON 序列化结果是否发生二次转义。

建议通过官方 SDK 生成请求,避免手动拼接签名字符串和支付 URL。

时间或请求已过期

订单设置了超时时间,或者支付链接经过缓存、转发后隔了很久才打开,都可能导致支付平台拒绝继续付款。服务器时间严重不准,也可能使请求无法通过时间校验。

重新下单时,需要重新生成请求参数,不要复用之前生成的完整支付表单或 URL。

应用与商户配置不匹配

以下配置必须属于同一套环境和账号:

  • app_id
  • 商户账号或签约主体
  • 应用私钥
  • 支付平台公钥
  • 网关地址
  • 沙箱或正式环境
  • 已签约的支付产品

例如,将正式环境的 app_id 用于沙箱网关,或者误用了另一应用的密钥,都会造成请求异常。

排查步骤

1. 获取接口的原始响应

先记录支付请求返回的这些字段:

code
msg
sub_code
sub_msg
out_trade_no
trade_no

页面上的中文提示通常经过统一处理,能提供的信息不多。定位具体字段时,sub_code 和 sub_msg 更有用。

日志中不要写入完整私钥、用户敏感信息或未经脱敏的支付凭证。

2. 打印服务端实际提交的参数

应检查服务端最终发送的值,而不只是前端传入的值。至少要确认:

method
app_id
charset
sign_type
timestamp
biz_content.out_trade_no
biz_content.total_amount
biz_content.subject
biz_content.product_code

使用其他支付渠道时,应按照相应接口文档核对必填字段。

3. 校验订单数据

服务端应在调用支付接口前校验订单数据,例如:

import java.math.BigDecimal;

public void validateOrder(String outTradeNo, BigDecimal totalAmount, String subject) {
    if (outTradeNo == null || outTradeNo.isBlank()) {
        throw new IllegalArgumentException("out_trade_no 不能为空");
    }

    if (totalAmount == null || totalAmount.compareTo(BigDecimal.ZERO) <= 0) {
        throw new IllegalArgumentException("total_amount 必须大于 0");
    }

    if (subject == null || subject.isBlank()) {
        throw new IllegalArgumentException("subject 不能为空");
    }
}

提交金额时,可以统一保留两位小数:

String amount = totalAmount
        .setScale(2, java.math.RoundingMode.HALF_UP)
        .toPlainString();

金额是否必须保留两位小数,以及允许的金额范围和舍入规则,都应以所接支付接口的要求为准。

4. 使用官方 SDK 构造请求

下面的示例只展示支付宝手机网站支付中常见的参数结构。SDK 类名和初始化方式可能随版本变化,请以当前版本的官方 SDK 文档为准:

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

Map<String, Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no", orderNo);
bizContent.put("total_amount", amount);
bizContent.put("subject", subject);
bizContent.put("product_code", "QUICK_WAP_WAY");

request.setBizContent(objectMapper.writeValueAsString(bizContent));

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);

if (!response.isSuccess()) {
    log.error(
        "创建支付失败,code={}, msg={}, subCode={}, subMsg={}, orderNo={}",
        response.getCode(),
        response.getMsg(),
        response.getSubCode(),
        response.getSubMsg(),
        orderNo
    );
    throw new IllegalStateException("创建支付失败");
}

return response.getBody();

接口返回的表单内容应直接输出到浏览器,不要在前端拆开后重新拼接参数。

5. 检查 URL 与编码

如果必须手动拼接跳转地址,应避免重复编码。每个参数只能按照接口规范编码一次,例如:

String encodedReturnUrl = URLEncoder.encode(returnUrl, StandardCharsets.UTF_8);

如果官方 SDK 已经完成参数编码,就不要再对整个请求结果调用 URLEncoder.encode(),否则可能破坏签名或回跳地址。

同时检查反向代理、网关和前端框架是否改写了 +、%、&、= 等字符。

6. 重新生成订单并发起支付

确认原订单尚未支付后,创建新的商户订单号,并重新生成支付请求和跳转页面。不要使用浏览器历史记录、缓存页面或旧二维码中的支付地址。

注意事项

  • 支付金额、订单号和商品信息应以服务端数据为准,不能直接采用前端传值。
  • 同一个 out_trade_no 不能对应不同金额或不同商品。
  • 支付结果应以服务端异步通知和主动查询为准,不能只根据前端跳转结果判断。
  • 收到异步通知后必须验签,同时校验商户号、应用 ID、订单号、金额和订单状态。
  • 排查生产环境问题时,应保留请求时间、订单号、接口错误码和平台请求流水号,以便与支付平台技术支持核对。
  • 如果参数均符合文档要求但仍然报错,请携带脱敏后的请求参数、code、sub_code、请求时间和订单号联系支付平台技术支持。不要提供应用私钥或完整签名密钥。

备注:内容仅供参考。