alipay.trade.query 提示支付宝公钥配置错误
结论
alipay.trade.query 已经收到了支付宝返回的交易数据,但 SDK 在验签时发现,alipay_public_key 中配置的是“应用公钥”,不是“支付宝公钥”,因此抛出以下异常:
AlipayApiException: 当前配置的支付宝公钥为应用公钥,请填写正确的支付宝公钥字段
请将客户端配置中的公钥替换为当前应用在支付宝开放平台页面显示的“支付宝公钥”。应用私钥仍用于请求签名,无需与支付宝公钥配对生成。
原因
支付宝开放平台的非证书密钥模式使用三类密钥:
- 应用私钥:由开发者保管,用于对请求签名。
- 应用公钥:上传到支付宝开放平台,供支付宝验证应用发出的请求。
- 支付宝公钥:由支付宝提供,供应用验证支付宝返回的数据。
它们的使用关系如下:
应用请求:
应用私钥签名 → 支付宝使用应用公钥验签
支付宝响应:
支付宝侧私钥签名 → 应用使用支付宝公钥验签
把应用公钥误填到支付宝公钥字段后,请求通常仍能正常发出,因为请求签名使用的是应用私钥。收到响应时,SDK 无法使用错误的公钥完成验签,便会抛出异常。
异常信息中仍能看到交易状态,是因为响应报文已经通过网络返回,错误出现在之后的签名验证环节。不要绕过异常,直接使用报文中的交易结果,否则无法确认数据是否真实、完整。
解决步骤
1. 确认当前使用的签名模式
先确认应用使用的是哪种模式:
- 普通公钥模式;或
- 公钥证书模式。
两种模式的初始化方式不同,不能混用。题目中的错误通常出现在普通公钥模式下。
2. 获取正确的支付宝公钥
进入当前 APPID 对应的支付宝开放平台应用,在接口签名或开发设置中查看密钥配置。
需要复制的是:
支付宝公钥
不要复制:
应用公钥
同时确认公钥与当前调用环境一致:
- 正式环境使用正式应用对应的支付宝公钥。
- 沙箱环境使用沙箱应用对应的支付宝公钥。
- 不同
APPID的密钥不能交叉使用。
3. 修正客户端配置
在普通公钥模式下,各字段的对应关系应为:
appId = 当前应用的 APPID
merchantPrivateKey = 当前应用的应用私钥
alipayPublicKey = 支付宝开放平台提供的支付宝公钥
修改配置后,重新创建 AlipayClient,再调用 alipay.trade.query。
Java 配置示例
以下示例使用普通公钥模式:
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
public class AlipayTradeQueryExample {
public static void main(String[] args) throws AlipayApiException {
String serverUrl = "https://openapi.alipay.com/gateway.do";
String appId = "your_app_id";
// 应用私钥:用于对应用发出的请求签名
String merchantPrivateKey = "your_merchant_private_key";
// 必须填写支付宝公钥,不能填写应用公钥
String alipayPublicKey = "your_alipay_public_key";
AlipayClient alipayClient = new DefaultAlipayClient(
serverUrl,
appId,
merchantPrivateKey,
"json",
"UTF-8",
alipayPublicKey,
"RSA2"
);
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo("your_out_trade_no");
request.setBizModel(model);
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println("交易状态:" + response.getTradeStatus());
} else {
System.out.println("查询失败:"
+ response.getCode() + " "
+ response.getSubCode() + " "
+ response.getSubMsg());
}
}
}
如果使用配置对象初始化,各字段的含义相同:
AlipayConfig alipayConfig = new AlipayConfig();
alipayConfig.setServerUrl("https://openapi.alipay.com/gateway.do");
alipayConfig.setAppId("your_app_id");
alipayConfig.setPrivateKey("your_merchant_private_key");
alipayConfig.setFormat("json");
alipayConfig.setCharset("UTF-8");
// 这里填写支付宝公钥,不是应用公钥
alipayConfig.setAlipayPublicKey("your_alipay_public_key");
alipayConfig.setSignType("RSA2");
AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
不同 SDK 版本使用的构造方法和配置类名称可能有所差异,但这些密钥的用途不变。
如果使用公钥证书模式
证书模式通常需要配置:
- 应用公钥证书;
- 支付宝公钥证书;
- 支付宝根证书;
- 应用私钥。
这时应按照当前 SDK 提供的证书模式初始化方式设置相应的证书路径,不能只在 alipayPublicKey 中填写一段公钥字符串。
以下两类配置不要混用:
普通公钥模式:支付宝公钥字符串
证书模式:支付宝公钥证书、支付宝根证书等证书文件
如果开放平台应用已经切换到证书模式,服务端代码也要同步修改,否则可能出现验签失败或证书序列号不匹配。
注意事项
- 完整复制公钥内容,不要漏行、截断或混入多余空格。
- 是否保留
-----BEGIN PUBLIC KEY-----等头尾标记,以所用 SDK 的配置要求为准。 signType必须与开放平台配置一致,常见配置为RSA2。charset应与请求配置一致,通常为UTF-8。- 支付宝公钥由支付宝提供,不能根据应用私钥自行推导。
- 重新生成并上传应用公钥后,需要检查开放平台显示的支付宝公钥或证书配置是否发生变化。
- 即使异常中能看到
trade_status,也不要直接解析并将其作为支付结果。应先修正公钥,等 SDK 验签成功后再处理交易状态。 - 支付结果应以验签成功的异步通知或主动查询结果为准,并结合商户订单号、金额、收款方等关键字段进行业务校验。
备注:内容仅供参考。
