PAYATHON 2026

支付宝支付生成的sign值为空并提示商家订单参数异常

支付阿杰

结论

sign 为空,通常说明商户端生成签名时失败,不一定是支付宝公钥有问题。建议重点检查:

  • 生成签名时使用的是否为应用私钥,而不是支付宝公钥、应用公钥或证书公钥。
  • 私钥格式、换行和头尾标记是否符合当前 SDK 的要求。
  • sign_type 是否与密钥类型一致,通常应使用 RSA2。
  • 待签名参数是否完整,biz_content 是否为合法 JSON。
  • 代码是否捕获并忽略了签名异常,随后把空字符串传给支付宝。
  • 参数是否在签名后被修改、重复编码或遗漏。

“商家订单参数异常”是支付宝收银台的通用提示,不能据此认定公钥配置错误。先确认本地已经成功生成签名,再核对实际提交给支付宝的完整请求。

优先检查私钥是否正确

商户向支付宝发起请求时,需要使用应用私钥生成 sign;支付宝公钥则用于验证支付宝返回结果的签名。两者不能混用:

密钥用途
应用私钥商户请求支付宝时生成 sign
应用公钥配置到支付宝开放平台
支付宝公钥商户验证支付宝返回的签名
应用公钥证书证书模式下标识应用公钥
支付宝公钥证书证书模式下验证支付宝签名

普通公钥模式和证书模式混用,也可能造成签名或验签异常。例如,客户端按证书模式初始化,却传入普通支付宝公钥;接口使用普通公钥模式,配置中却只有证书路径。

还要确认私钥属于当前 app_id 对应的应用。私钥即使格式正确,只要与开放平台中配置的应用公钥不成对,支付宝仍会拒绝请求。

检查私钥格式

不同语言和 SDK 版本接受的私钥格式可能不同。常见问题有:

  • 配置中带有多余的引号、空格或不可见字符。
  • 私钥复制不完整。
  • PEM 头尾与 SDK 的要求不一致。
  • 环境变量将换行保存成了字面量 \n。
  • RSA 私钥格式不符合 PKCS#8 要求。
  • 配置文件转义导致 +、/、= 等字符发生变化。

私钥可能按以下形式保存:

-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

如果 SDK 要求传入纯私钥内容,需要去掉头尾标记和换行;如果 SDK 接收 PEM,则应保留完整格式。应以项目实际使用的官方 SDK 接口要求为准,不要在没有确认的情况下自行转换。

通过环境变量加载 PEM 私钥时,可以检查是否需要恢复换行:

String privateKey = System.getenv("ALIPAY_APP_PRIVATE_KEY");

if (privateKey != null) {
    privateKey = privateKey.replace("\\n", "\n").trim();
}

不要把完整私钥写入日志。排查时最多记录私钥是否为空、字符长度和安全的指纹信息。

确认签名算法一致

如果请求参数声明:

sign_type=RSA2

代码就必须使用 RSA2 对应的算法生成签名。RSA2 通常对应:

SHA256withRSA

旧的 RSA 签名一般对应:

SHA1withRSA

不能让请求中的 sign_type 写成 RSA2,代码却使用 SHA1withRSA。字符集也要统一,通常使用:

charset=utf-8

签名使用的字符集、请求声明的 charset 和实际发送请求的编码必须一致。

检查待签名参数

签名前,可以记录脱敏后的参数和最终待签名字符串,重点核对:

  • app_id 是否正确。
  • method 是否符合当前支付场景。
  • timestamp 格式是否正确。
  • version 是否符合接口要求。
  • charset 是否为 utf-8。
  • sign_type 是否为 RSA2。
  • biz_content 是否为合法 JSON。
  • out_trade_no 是否为空。
  • total_amount 是否符合接口要求。
  • subject 是否为空或含有异常字符。
  • 签名完成后,参数是否又被修改。

典型的公共参数如下:

app_id=应用ID
method=alipay.trade.app.pay
format=JSON
charset=utf-8
sign_type=RSA2
timestamp=2026-09-08 15:30:00
version=1.0
biz_content={"out_trade_no":"ORDER_001","total_amount":"0.01","subject":"测试订单","product_code":"QUICK_MSECURITY_PAY"}

不同支付产品对 method、product_code 和业务参数的要求不同。App 支付、手机网站支付和电脑网站支付不能直接共用同一套业务参数。

自行拼接待签名字符串时,很容易在参数过滤、排序或编码规则上出错。应优先使用支付宝官方 SDK,不建议手写签名逻辑。

使用官方 SDK 生成支付参数

以 Java App 支付为例,可以由 SDK 生成包含签名的订单字符串:

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 createOrderString(
            String gateway,
            String appId,
            String appPrivateKey,
            String alipayPublicKey,
            String notifyUrl) throws AlipayApiException {

        AlipayClient alipayClient = new DefaultAlipayClient(
                gateway,
                appId,
                appPrivateKey,
                "JSON",
                "UTF-8",
                alipayPublicKey,
                "RSA2"
        );

        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setOutTradeNo("ORDER_001");
        model.setTotalAmount("0.01");
        model.setSubject("测试订单");
        model.setProductCode("QUICK_MSECURITY_PAY");

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

        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);

        String orderString = response.getBody();
        if (orderString == null || orderString.isBlank()) {
            throw new IllegalStateException(
                    "支付宝支付参数生成失败,subCode=" + response.getSubCode()
                            + ", subMsg=" + response.getSubMsg()
            );
        }

        return orderString;
    }
}

这里要检查的是 response.getBody(),不能只读取自行维护的某个 sign 变量。App 支付的订单字符串包含多个参数,sign 只是其中之一。

电脑网站支付或手机网站支付需要调用相应接口,例如 pageExecute,不能直接照搬 App 支付的 sdkExecute 调用方式。

不要吞掉签名异常

有些项目之所以出现 sign 为空,是因为代码捕获异常后直接返回了空字符串:

try {
    return createAlipaySign(params);
} catch (Exception e) {
    return "";
}

这样会掩盖签名失败的真正原因。代码应保留异常类型和堆栈,并阻止带有空签名的请求继续发送:

try {
    String sign = createAlipaySign(params);

    if (sign == null || sign.isBlank()) {
        throw new IllegalStateException("支付宝签名结果为空");
    }

    return sign;
} catch (Exception e) {
    throw new IllegalStateException("生成支付宝签名失败", e);
}

日志中可以记录:

  • 使用的 app_id。
  • sign_type 和 charset。
  • SDK 返回的错误码和错误信息。
  • 脱敏后的业务参数。
  • 私钥是否加载成功及其安全指纹。

不要记录应用私钥、完整身份证号、手机号或其他敏感数据。

检查 URL 编码时机

签名值通常是 Base64 字符串,其中可能包含 +、/ 和 =。这些字符通过表单或 URL 传输时需要正确编码,但不能在签名前对参数进行不一致的 URL 编码。

常见错误包括:

  • 对 sign 编码两次。
  • 将 + 解析为空格。
  • 签名时使用编码后的值,发送时却使用原始值。
  • 签名完成后重新序列化 biz_content,导致内容发生变化。
  • 客户端收到订单字符串后,再次拆分、排序和拼接。

正确的处理顺序是先确定最终业务参数,再交给 SDK 签名并生成请求内容,最后将 SDK 的返回结果原样提交。不要自行解析后重新拼装。

建议的排查顺序

  1. 确认 sign 由服务端生成,并且结果不为空。
  2. 查看签名函数或 SDK 调用抛出的原始异常,不要只看收银台提示。
  3. 确认生成签名时使用的是应用私钥,并且该私钥与当前 app_id 配置的应用公钥成对。
  4. 检查是否混用了普通公钥模式和证书模式。
  5. 确认 sign_type=RSA2,字符集和 SDK 初始化参数保持一致。
  6. 校验 biz_content、金额、订单号、商品标题和 product_code。
  7. 对比签名前的参数和实际发送的参数,确认中途没有修改。
  8. 检查客户端、网关或前端是否对订单字符串做了二次编码。
  9. 使用支付宝官方 SDK 的最小示例生成一笔新的测试订单。
  10. 如果本地签名正常,但支付宝仍然拒绝请求,再结合网关返回的 code、sub_code、sub_msg 和请求日志定位问题。

注意事项

不要只通过”重新配置公钥和私钥”来排查问题。重新配置后,还要确认应用配置到了正确的环境,并检查已部署的服务是否加载了新配置。测试环境和正式环境的网关、应用信息及密钥也不能混用。

订单参数异常也不一定完全由签名导致。即使 sign 已经成功生成,接口名称、支付产品、金额格式、必填参数或应用权限不匹配,仍可能出现类似提示。最终应以支付宝接口返回的结构化错误信息为准,不能只依据收银台页面上的提示文字。

备注:内容仅供参考。