PAYATHON 2026

支付宝当面扫支付报错 isv.invalid-signature 如何排查?

支付阿杰

结论

isv.invalid-signature 表示支付宝无法使用当前应用的公钥验证请求签名。

在线密钥匹配成功,只能说明私钥与某个公钥配对,不能说明以下配置都没有问题:

  • 请求中的 app_id 是否与密钥所属应用一致
  • 使用的是否为应用私钥,而非支付宝公钥或支付宝私钥
  • 支付宝开放平台保存的应用公钥是否与本地私钥匹配
  • sign_type、字符集和签名算法是否一致
  • 待签名字符串在签名后是否发生变化
  • 是否混用了普通公钥模式和公钥证书模式

所以,这类问题通常与支付业务参数无关,更可能出在应用身份、密钥配置或签名过程上。

建议按以下顺序排查

1. 核对 app_id 和运行环境

先记录实际发送到支付宝网关的参数,不要只看配置文件:

app_id
method
sign_type
charset
timestamp
version
sign

逐项确认:

  • app_id 是当前开放平台应用的 AppID
  • 私钥属于这个应用
  • 应用后台配置的公钥与该私钥配对
  • 没有混用沙箱应用、正式应用及其密钥
  • 请求地址与所用环境一致

常见网关为:

正式环境:https://openapi.alipay.com/gateway.do
沙箱环境:https://openapi-sandbox.dl.alipaydev.com/gateway.do

即使两组密钥分别都能通过在线匹配,只要请求使用的是 A 应用的 app_id 和 B 应用的私钥,支付宝仍会返回 isv.invalid-signature。

2. 确认公私钥的用途

在普通公钥模式下:

  • 商户系统保存并使用“应用私钥”生成请求签名
  • 支付宝开放平台配置“应用公钥”
  • 商户系统使用“支付宝公钥”验证支付宝返回结果或异步通知

这三者的用途不要混淆:

应用私钥:商户请求支付宝时签名
应用公钥:上传到支付宝开放平台
支付宝公钥:验证支付宝响应和通知

在线工具应当匹配“本地应用私钥”和“开放平台已配置的应用公钥”。如果本地私钥能与另一份本地公钥匹配,但上传到开放平台的还是旧公钥,线上请求依然会验签失败。

修改应用公钥后,还要检查服务器上的私钥是否已同步更新,以及旧进程、配置缓存和容器环境变量是否还在使用旧值。

3. 核对 sign_type

目前常见的配置是 RSA2,对应 SHA256WithRSA:

sign_type=RSA2

如果请求参数声明使用 RSA2,代码实际却按 SHA1WithRSA 签名,验签就会失败。

对应关系如下:

sign_typeJava 算法名
RSA2SHA256withRSA
RSASHA1withRSA

除非现有应用明确配置为旧的 RSA 模式,否则通常应使用 RSA2。不能只修改请求中的 sign_type,签名实现和开放平台配置也要保持一致。

4. 检查私钥格式

不同语言、不同版本的 SDK 对私钥格式的要求可能不同。常见问题有:

  • 把公钥填入私钥配置项
  • 复制私钥时缺行、缺字符,或者带入不可见字符
  • 环境变量中的换行变成字面量 \n
  • PEM 头尾重复,或者格式不受当前 SDK 支持
  • 使用了加密私钥,但程序没有提供解密口令
  • PKCS#1 或 PKCS#8 格式不符合当前 SDK 的要求

PEM 私钥通常类似:

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

如果私钥保存在环境变量中,要确认程序读取后包含的是真实换行。例如,Java 可以根据实际存储方式处理:

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

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

是否保留 PEM 头尾,要看所用 SDK 及其版本,不能一概而论。传入密钥时,应遵循对应 SDK 构造方法的要求。

5. 检查待签名字符串

如果签名没有完全交给官方 SDK 处理,需要逐项确认以下规则:

  1. 排除 sign 参数
  2. 排除值为空的参数
  3. 按参数名的 ASCII 顺序排序
  4. 按 key=value 的形式拼接
  5. 参数之间使用 & 连接
  6. 使用声明的字符集生成字节
  7. 完成签名后,再对 sign 进行 URL 编码

示意代码:

Map<String, String> params = new TreeMap<>();
params.put("app_id", appId);
params.put("method", "alipay.trade.precreate");
params.put("charset", "utf-8");
params.put("sign_type", "RSA2");
params.put("timestamp", "2026-09-08 10:30:00");
params.put("version", "1.0");
params.put("biz_content", bizContent);

String contentToSign = params.entrySet().stream()
    .filter(entry -> entry.getValue() != null && !entry.getValue().isEmpty())
    .map(entry -> entry.getKey() + "=" + entry.getValue())
    .collect(java.util.stream.Collectors.joining("&"));

签名生成后,以下任一变化都会让签名失效:

  • 修改 biz_content
  • 改变 JSON 中的空格、转义或字段顺序
  • 重复对参数进行 URL 编码
  • 签名时使用原始值,发送时却使用另一份编码后的值
  • 网关、中间件或 HTTP 客户端重新组装参数
  • 签名时使用 UTF-8,发送时声明其他字符集

排查时应同时记录:

最终待签名字符串
签名结果
最终实际发送的请求参数

日志中的私钥必须脱敏,最好完全禁止输出。

6. 优先使用官方 SDK

以 Java 普通公钥模式为例,配置结构通常如下:

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

AlipayTradePrecreateRequest request =
    new AlipayTradePrecreateRequest();

request.setBizContent("""
{
  "out_trade_no": "ORDER_202609080001",
  "total_amount": "0.01",
  "subject": "测试订单"
}
""");

AlipayTradePrecreateResponse response =
    alipayClient.execute(request);

System.out.println(response.getCode());
System.out.println(response.getSubCode());
System.out.println(response.getSubMsg());

需要注意:

  • 第三个密钥参数是应用私钥
  • alipayPublicKey 是支付宝公钥,不是应用公钥
  • appId、应用私钥和开放平台中的应用公钥必须属于同一个应用
  • signType 要与应用配置一致

这段示例只用于说明配置关系。具体构造方法应以项目实际使用的支付宝 SDK 版本为准。

证书模式需要单独检查

如果应用使用公钥证书模式,就不能继续按普通公钥模式配置。通常需要正确加载:

应用公钥证书
支付宝公钥证书
支付宝根证书

同时还要确认,SDK 根据请求计算出的证书序列号与当前证书一致。常见错误有:

  • 更新证书后,服务器仍在加载旧文件
  • 把普通“支付宝公钥”填入证书模式配置
  • 多台服务器使用的证书版本不一致
  • 容器挂载了错误的证书目录

普通公钥模式和证书模式只能选择一种,并使用与之对应的 SDK 初始化方式。

快速定位办法

可以创建一个最小化测试程序,只保留:

  • 固定的正式或沙箱网关
  • 明确的 app_id
  • 当前应用私钥
  • 对应的支付宝公钥或证书
  • 一个金额为 0.01 的测试请求
  • 官方 SDK 自动签名

如果最小程序能够成功调用,而业务系统仍然失败,问题通常出在以下位置:

  • 配置中心读取了旧密钥
  • 请求参数在签名后被修改
  • HTTP 客户端进行了二次编码
  • 多实例部署中有部分节点没有更新配置
  • 第三方支付封装错误处理了 sign_type 或字符集

如果最小程序仍然失败,就需要重新到开放平台核对 AppID、公钥配置、密钥模式和运行环境是否一一对应。

最常见的根因

实际排查时,可以优先检查以下几项:

  1. app_id 和应用私钥不属于同一个应用。
  2. 开放平台保存的还是旧应用公钥。
  3. 请求声明使用 RSA2,代码实际却按 RSA 签名。
  4. 混用了沙箱和正式环境的 AppID、密钥或网关。
  5. 私钥通过环境变量传递后,换行格式被破坏。
  6. 自行拼接参数时,签名内容与最终发送内容不一致。
  7. 混用了普通公钥模式和公钥证书模式。

“密钥匹配成功”只能排除密钥对本身不匹配,无法排除上述配置和请求构造问题。排查时,最直接的办法是打印脱敏后的实际运行配置和完整待签名字符串,再与最终发出的参数逐项比较。

备注:内容仅供参考。