支付宝小程序唤起支付报错 isv.invalid-signature 如何解决?
结论
isv.invalid-signature 表示支付宝网关无法使用当前应用配置的商户公钥验证请求签名。问题通常不在小程序前端的 my.tradePay,而在服务端调用 alipay.trade.create 等接口时,使用的 app_id、商户私钥、签名方式或证书配置不匹配。
App 支付、WAP 支付和 Web 支付正常,只能说明对应应用及密钥配置正确,不能说明小程序使用的应用配置也没有问题。排查时应先确认小程序支付请求实际使用的 app_id,再核对该应用在支付宝开放平台配置的接口加签方式。
常见原因
1. 小程序 app_id 与商户私钥不属于同一应用
同一个支付宝账号下可能有多个应用,App、网站和小程序支付也可能分别使用不同的 app_id。
常见的错误配置是:
- 请求参数使用小程序的
app_id - 签名使用 App 支付应用的商户私钥
- 小程序应用后台配置的是另一套商户公钥
在这种情况下,即使其他支付方式都正常,小程序相关接口仍会返回 isv.invalid-signature。
2. 混淆商户私钥和支付宝公钥
这几种密钥的用途不同:
- 商户私钥:商户请求支付宝接口时用于生成签名
- 商户公钥:配置在支付宝开放平台,供支付宝验证商户请求
- 支付宝公钥:商户用于验证支付宝响应或异步通知
调用支付宝接口时,必须用商户私钥签名,不能使用支付宝公钥或商户公钥。
3. 普通公钥模式与证书模式混用
如果应用采用证书模式,服务端还要正确配置应用公钥证书、支付宝公钥证书和支付宝根证书,并携带对应的证书序列号。
常见问题有:
- 开放平台配置为证书模式,代码仍按普通公钥模式初始化
- 使用了其他应用的证书
- 更新证书后,服务器仍在加载旧证书
- 只替换商户私钥,没有同步更新应用公钥证书
- 混用了沙箱证书和正式环境证书
同一套配置只能选择一种模式,不能混用。
4. sign_type 或字符集不一致
通常应使用:
sign_type=RSA2
charset=utf-8
如果请求声明使用 RSA2,实际却通过 SHA1withRSA 签名,或者签名和发送请求时采用了不同的字符集,都会导致验签失败。
5. 签名原文在发送前后发生变化
手工拼接签名时,容易出现这些问题:
- 参数没有按要求排序
- 把
sign参数本身加入签名 - 签名前对参数做了 URL 编码
biz_content签名后又被重新序列化- JSON 中的空格、转义符或字段顺序发生变化
- 金额、中文、换行符或特殊字符在传输时被修改
- 网关收到的请求参数与生成签名时使用的参数不是同一份数据
不能先对一份 biz_content 生成签名,再重新构造另一份 JSON 发送。
6. 环境配置混用
以下配置必须来自同一个环境:
- 支付宝网关地址
app_id- 商户私钥
- 商户公钥或应用公钥证书
- 支付宝公钥或支付宝公钥证书
沙箱应用的密钥不能用于正式网关,正式应用的密钥也不能用于沙箱网关。
推荐排查步骤
第一步:确定报错发生在哪个接口
先记录服务端调用支付宝时使用的接口名称。小程序支付的常见流程是:
- 服务端调用
alipay.trade.create - 支付宝返回
trade_no - 小程序调用
my.tradePay - 将
trade_no作为tradeNO传入
如果服务端调用 alipay.trade.create 时就返回了 isv.invalid-signature,应检查服务端的加签配置,不需要修改小程序前端代码。
第二步:核对实际发送的 app_id
不要只看配置文件。应在不输出私钥的前提下,记录程序运行时实际使用的 app_id,然后进入该应用的支付宝开放平台配置页面,核对接口加签方式。
三者必须互相对应:
请求中的 app_id
↕ 必须对应
开放平台应用中配置的商户公钥或应用公钥证书
↕ 必须对应
服务端用于签名的商户私钥
第三步:重新验证密钥对应关系
可以使用商户私钥生成一段测试签名,再用准备上传或已经配置的商户公钥在本地验签。
如果本地验签失败,说明公私钥不配对。此时应重新生成密钥,并把正确的商户公钥配置到对应应用中。
不要把商户私钥、完整签名原文或支付敏感信息写入公开日志。
第四步:检查 SDK 初始化
以 Java SDK 的普通公钥模式为例:
AlipayConfig config = new AlipayConfig();
config.setServerUrl("https://openapi.alipay.com/gateway.do");
config.setAppId("小程序对应的APP_ID");
config.setPrivateKey("与该应用商户公钥配对的商户私钥");
config.setFormat("json");
config.setCharset("UTF-8");
config.setSignType("RSA2");
config.setAlipayPublicKey("该应用对应的支付宝公钥");
AlipayClient alipayClient = new DefaultAlipayClient(config);
证书模式需要使用证书路径或证书内容初始化,具体写法取决于 SDK 版本:
AlipayConfig config = new AlipayConfig();
config.setServerUrl("https://openapi.alipay.com/gateway.do");
config.setAppId("小程序对应的APP_ID");
config.setPrivateKey("商户私钥");
config.setFormat("json");
config.setCharset("UTF-8");
config.setSignType("RSA2");
config.setAppCertPath("/path/appCertPublicKey.crt");
config.setAlipayCertPath("/path/alipayCertPublicKey_RSA2.crt");
config.setRootCertPath("/path/alipayRootCert.crt");
AlipayClient alipayClient = new DefaultAlipayClient(config);
普通公钥模式和证书模式只能选择一种,初始化参数不要混用。
第五步:使用官方 SDK 创建交易
尽量由 SDK 处理参数排序、编码和签名,避免手工拼接网关参数:
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
AlipayTradeCreateModel model = new AlipayTradeCreateModel();
model.setOutTradeNo("ORDER_202609080001");
model.setTotalAmount("0.01");
model.setSubject("订单支付");
model.setBuyerId("支付宝用户标识");
request.setBizModel(model);
AlipayTradeCreateResponse response = alipayClient.execute(request);
if (!response.isSuccess()) {
throw new IllegalStateException(
response.getCode() + " / "
+ response.getSubCode() + " / "
+ response.getSubMsg()
);
}
String tradeNo = response.getTradeNo();
buyerId 的获取方式取决于小程序登录和用户授权流程,不能用小程序 app_id、商户 UID 或其他标识代替。
第六步:小程序端只传支付宝交易号
服务端成功创建交易后,小程序端可以这样调用:
my.tradePay({
tradeNO: tradeNo,
success: (result) => {
console.log('tradePay result:', result.resultCode);
},
fail: (error) => {
console.error('tradePay failed:', error);
}
});
参数名是 tradeNO,其中 NO 必须大写。tradeNo 应是服务端调用 alipay.trade.create 后取得的支付宝交易号,不是商户订单号 out_trade_no。
如果必须手工签名
签名原文通常需要按照支付宝开放平台的规则处理公共请求参数:排除 sign,按参数名排序,再以 key=value 的形式连接。具体处理方式应以当前接口文档和所用加签模式为准。
RSA2 的核心操作类似:
Signature signature = Signature.getInstance("SHA256withRSA");
signature.initSign(merchantPrivateKey);
signature.update(contentToSign.getBytes(StandardCharsets.UTF_8));
String sign = Base64.getEncoder().encodeToString(signature.sign());
手工实现时必须满足:
签名使用的参数值 = 最终发送给支付宝的参数值
尤其不能在 biz_content 完成签名后再次序列化或修改。
注意事项
isv.invalid-signature和“签名验证支付宝异步通知失败”属于两个方向的问题。前者通常表示支付宝无法验证商户请求。- 排查时可以记录
app_id、接口名、网关环境、sign_type、字符集,以及支付宝返回的code、sub_code和sub_msg,但不要记录商户私钥。 - 修改开放平台的公钥或证书后,需要确认配置已经生效,并重启可能缓存旧配置的应用实例。
- 如果项目封装了统一支付网关,需要检查小程序渠道是否错误复用了 App、WAP 或其他商户配置。
- 如果以上配置均一致,可以保存支付宝返回的请求流水号或
trace_id,再通过支付宝开放平台的诊断功能或技术支持继续定位。
备注:内容仅供参考。
