PAYATHON 2026

服务端生成的orderinfo无法唤起支付宝支付

支付阿杰

结论

客户端使用本地配置的支付参数能够完成支付,说明支付宝客户端、应用跳转配置和客户端支付代码基本正常。问题大多出在服务端生成的 orderInfo,例如参数缺失、签名方式不一致、编码顺序错误,或者字符串在返回过程中被二次编码、转义或截断。

建议服务端通过支付宝官方 SDK 生成完整的订单字符串。客户端收到后不要拆分、拼接或再次进行 URL 编码,直接传给支付接口即可。

检查 orderInfo 是否完整

App 支付使用的 orderInfo 不是普通订单号,而是一整段已经签名的请求参数,通常包含:

  • app_id
  • method
  • format
  • charset
  • sign_type
  • timestamp
  • version
  • notify_url
  • biz_content
  • sign

其中,biz_content 一般包含:

  • out_trade_no
  • total_amount
  • subject
  • product_code,App 支付通常使用 QUICK_MSECURITY_PAY

如果服务端只返回 biz_content、订单号或待签名字符串,客户端无法直接唤起支付。

可以先记录服务端生成的原始 orderInfo,确认它不是 null、空字符串,也没有被截断,同时检查其中是否包含 sign= 和 biz_content=。

日志可能包含签名、订单号等敏感信息,只能在安全的测试环境中使用。线上环境不要完整打印或长期保存这些内容。

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

以 Java 服务端为例,核心流程如下:

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;

public class AlipayOrderService {

    public String createOrderInfo() throws AlipayApiException {
        AlipayClient alipayClient = new DefaultAlipayClient(
                "https://openapi.alipay.com/gateway.do",
                "YOUR_APP_ID",
                "YOUR_APP_PRIVATE_KEY",
                "json",
                "UTF-8",
                "ALIPAY_PUBLIC_KEY",
                "RSA2"
        );

        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setSubject("测试商品");
        model.setOutTradeNo("ORDER_202609130001");
        model.setTotalAmount("0.01");
        model.setProductCode("QUICK_MSECURITY_PAY");

        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        request.setBizModel(model);
        request.setNotifyUrl("https://example.com/alipay/notify");

        AlipayTradeAppPayResponse response =
                alipayClient.sdkExecute(request);

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

        return response.getBody();
    }
}

应当返回给客户端的是:

response.getBody()

不要误传:

response.toString()

也不要只返回:

model.toString()

具体类名和调用方式可能因 SDK 版本而异,应以当前依赖对应的官方接口定义为准。处理原则不变,即由 SDK 完成参数组装、签名和必要的编码。

服务端接口原样返回订单字符串

可以将 orderInfo 放在 JSON 字段中返回:

{
  "code": 0,
  "data": {
    "orderInfo": "app_id=...&biz_content=...&sign=...&sign_type=RSA2..."
  }
}

JSON 中出现 \"、\uXXXX 等转义形式不一定有问题,客户端 JSON 解析器会将其正常还原。需要避免的是业务代码再次处理这个字符串,例如:

  • 对整个 orderInfo 进行 URL 编码;
  • URL 解码后重新编码;
  • 按 & 拆分,再重新排序或拼接;
  • 将 + 替换为空格;
  • 手动删除引号、斜杠或百分号;
  • 通过 URL 查询参数传递,导致网关重新编码内容;
  • 数据库字段或接口网关的长度不足,导致字符串被截断。

客户端取出解析后的字符串后,应直接调用支付宝 SDK。以 Android 为例:

String orderInfo = response.getData().getOrderInfo();

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

不要进行下面这样的处理:

String encoded = URLEncoder.encode(orderInfo, "UTF-8");
payTask.payV2(encoded, true);

检查签名和编码顺序

如果没有使用官方 SDK,而是自行拼接参数,需要逐项确认:

  1. 按支付宝接口要求生成待签名内容。
  2. 使用应用私钥签名,而不是支付宝公钥。
  3. sign_type 必须与实际使用的签名算法一致,例如 RSA2。
  4. 待签名内容的字符集必须与请求中的 charset 一致,通常为 UTF-8。
  5. 先对原始待签名内容进行签名,再对需要作为请求参数传输的值进行必要编码。
  6. 不要再次编码已经编码过的 sign 或 biz_content。

常见错误包括先对整个参数字符串进行 URL 编码,再对编码后的字符串签名;或者 SDK 已经完成编码,业务代码又处理了一次。这些操作都会导致最终参数与签名内容不一致。

还要注意两种密钥的用途不同:

  • 服务端使用应用私钥生成签名;
  • 验证支付宝异步通知时使用支付宝公钥。

即使密钥内容没有问题,只要应用或环境配置不匹配,支付仍然会失败。例如,沙箱应用的密钥和 app_id 不能与正式环境网关混用。

对比客户端可用参数和服务端参数

既然客户端本地生成的参数可以正常支付,可以在测试环境中比较两份订单字符串的结构,主要检查:

  • app_id 是否相同;
  • method 是否为对应的 App 支付接口;
  • product_code 是否正确;
  • sign_type 是否一致;
  • charset 是否一致;
  • biz_content 是否为合法 JSON;
  • 金额是否为字符串格式的合法数值;
  • timestamp 格式是否正确;
  • 是否包含完整的 sign;
  • 服务端参数中是否出现 %25、%252F 等可能由重复编码产生的内容。

不要直接比较两份 sign 是否相同。订单号、时间戳或业务参数只要有一项不同,签名结果就可能不同。排查时应比较参数结构和编码层级。

检查客户端实际收到的内容

排查时不能只看服务端生成前的变量,还要检查三个位置:

  1. 服务端 SDK 刚生成的 orderInfo;
  2. HTTP 接口实际返回的响应体;
  3. 客户端完成 JSON 解析后、调用 payV2 前的字符串。

如果第一个位置正常,第三个位置异常,问题通常发生在接口序列化、网关传输或客户端解析环节。此时应检查:

  • 是否将订单字符串放进了 URL 查询参数;
  • 是否先使用表单编码,又将内容嵌入 JSON;
  • 特殊字符是否被网关过滤;
  • 客户端字段类型是否为 String;
  • 是否错误读取了外层 JSON,而不是 orderInfo 字段;
  • 字符串首尾是否多出一对引号;
  • 是否包含换行、不可见字符或多余空格。

无法唤起支付时如何定位

支付宝 SDK 的调用结果和客户端日志通常比”没有反应”更有助于定位问题。应捕获并记录支付调用返回的状态字段,但不要记录完整签名或私钥。

排查时可先区分两种情况:

  • 完全没有跳转到支付宝时,优先检查传入值是否为空、是否传错字段,以及客户端调用线程和 SDK 接入方式是否正确。
  • 能够跳转,但提示订单或签名错误时,优先检查签名算法、密钥所属应用、网关环境、编码和订单参数。

如果客户端本地参数与服务端参数使用的 app_id、网关环境或签名方案不同,不能因为本地参数能够支付,就认定服务端的密钥配置没有问题。需要确认 app_id、应用私钥、支付宝公钥、网关地址和应用环境属于同一套配置。

较稳妥的处理方式是让服务端使用官方 SDK 的 App 支付请求对象生成 response.getBody(),接口将其作为普通字符串返回。客户端解析 JSON 后不再加工,直接把该字符串交给支付宝支付 SDK。

备注:内容仅供参考。