支付宝 APP 支付中两类支付宝密钥的区别及公钥选择
明确结论
这两处密钥分属不同的接口体系,不能因为名称相同就混用:
- “应用设置 → 开发设置 → 接口加签方式”用于支付宝开放平台 OpenAPI,例如
alipay.trade.app.pay。 - “mapi 网关产品密钥”用于旧版 MAPI 接口,也就是请求地址、参数规范和签名规则均属于
mapi网关的产品。 - 通过
alipay.trade.app.pay拉起支付宝 APP 支付时,服务端应使用应用私钥签名;验签时则使用”接口加签方式”对应的支付宝公钥。 - 拉起支付时,通常不需要把支付宝公钥作为业务参数传给支付宝客户端。商户需要用它验证支付宝的响应和异步通知。
- MAPI 页面中的支付宝公钥只适用于对应的 MAPI 请求链路,不能用来验证 OpenAPI 的支付通知。
判断该用哪套密钥,要看实际调用的接口和网关,而不是看它们是否都用于 APP 支付。
两套密钥分别做什么
接口加签方式中的密钥
这套配置以开放平台应用为边界,通常包括:
- 应用私钥:由商户保存,用于对请求参数签名。
- 应用公钥:由商户上传给支付宝,支付宝用它验证商户请求。
- 支付宝公钥:由支付宝提供,商户用它验证支付宝的返回结果、异步通知等数据。
调用以下接口时,应使用这套配置:
alipay.trade.app.pay
alipay.trade.query
alipay.trade.refund
alipay.trade.close
常见网关地址为:
https://openapi.alipay.com/gateway.do
mapi 网关产品密钥
MAPI 是另一套接口体系,通常与历史签约产品、旧版网关及其账号标识绑定,有独立的密钥配置、请求参数和验签规则。
一般可以通过以下特征识别:
- 请求发送到 MAPI 网关;
- 参数中可能有
service、partner、seller_id等旧接口字段; - 接口文档明确要求使用 MAPI 产品密钥;
- 签名算法和字符编码需遵循相应的 MAPI 接口文档。
MAPI 页面中的支付宝公钥用于验证该产品链路中由支付宝签名的数据。即使它也是 RSA 公钥,也不代表它可以和开放平台应用下的支付宝公钥互换。
APP 支付到底使用哪个公钥
如果服务端调用的是:
alipay.trade.app.pay
各环节应使用以下密钥:
| 操作 | 使用的密钥 |
|---|---|
| 服务端生成 APP 支付订单并签名 | 应用私钥 |
| 支付宝验证商户提交的订单 | 已上传的应用公钥 |
| 商户验证支付宝异步通知 | 接口加签方式中的支付宝公钥 |
| 商户验证支付宝同步返回数据 | 接口加签方式中的支付宝公钥 |
| APP 客户端调用支付 SDK | 使用服务端返回的已签名订单字符串,不传支付宝公钥 |
因此,对于”拉起支付宝 APP 付款时应该传哪个支付宝公钥”这个问题,准确答案是:通常一个都不传。
客户端从商户服务端取得签名后的订单字符串,再交给支付宝客户端 SDK。应用私钥也不能放在 Android 或 iOS 客户端中,否则可能被反编译和提取,进而被用于伪造订单。
正确的调用流程
1. 确认正在使用的接口体系
先检查服务端实际调用的接口名称和网关地址。
如果代码使用:
alipay.trade.app.pay
https://openapi.alipay.com/gateway.do
就应使用”接口加签方式”中的整套密钥。
只有接口文档明确指定 MAPI 网关时,才使用”mapi 网关产品密钥”。
2. 在服务端配置开放平台密钥
以普通公钥模式为例,服务端需要保存:
APP_ID
APP_PRIVATE_KEY
ALIPAY_PUBLIC_KEY
其中:
APP_PRIVATE_KEY用于给请求签名;ALIPAY_PUBLIC_KEY用于验证支付宝签名;- 两者用途相反,不能互换。
应用公钥也不能误当作支付宝公钥。前者属于商户密钥对,后者属于支付宝密钥对。
3. 服务端生成签名订单
下面以 Java SDK 的普通公钥模式为例。SDK 版本不同,类名和方法可能有所变化,请以当前使用的支付宝官方 SDK 文档为准。
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipay.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2"
);
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
request.setNotifyUrl("https://example.com/alipay/notify");
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setSubject("订单支付");
model.setTotalAmount("99.00");
model.setProductCode("QUICK_MSECURITY_PAY");
request.setBizModel(model);
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
String orderString = response.getBody();
这里的 orderString 已经包含服务端生成的签名。客户端直接将它交给支付宝支付 SDK 即可,无需再次签名,也不用追加支付宝公钥。
4. 客户端拉起支付宝
Android 端通常按以下方式调用:
PayTask payTask = new PayTask(activity);
Map<String, String> result = payTask.payV2(orderString, true);
orderString 必须由商户服务端生成。不要在客户端使用应用私钥拼装或签名订单。
客户端返回的结果只能用于界面展示。最终支付状态应以服务端收到且验签成功的异步通知为准,也可以由服务端主动调用订单查询接口确认。
5. 服务端验证异步通知
在普通公钥模式下,可以使用开放平台配置中的支付宝公钥验签:
boolean verified = AlipaySignature.rsaCheckV1(
requestParams,
ALIPAY_PUBLIC_KEY,
"UTF-8",
"RSA2"
);
验签通过后,还要检查以下业务字段:
app_id
out_trade_no
total_amount
seller_id
trade_status
通知处理也要具备幂等性,避免同一通知被重复执行。
容易混淆的三个概念
应用公钥
应用公钥由商户生成并上传给支付宝,与商户保存的应用私钥成对。支付宝用它验证商户请求。
支付宝公钥
支付宝公钥由支付宝提供,与支付宝持有的私钥成对。商户用它验证支付宝签名。
MAPI 中的支付宝公钥
这个公钥只用于对应的 MAPI 产品和接口链路。不能因为名称同为”支付宝公钥”,就把它复制到 OpenAPI SDK 的配置中。
即使两个页面显示的公钥内容碰巧一致,也应按照接口归属分别配置,不能假设它们永远相同。后续更换密钥或迁移产品后,这种假设可能导致验签失败。
如何快速排查选错密钥
如果遇到 isv.invalid-signature、验签失败或通知无法通过校验,可以按以下顺序检查:
- 实际调用的是 OpenAPI 还是 MAPI。
- SDK 中配置的是支付宝公钥,还是误用了应用公钥。
- 请求签名是否使用了应用私钥。
- 签名算法是否一致,例如是否统一使用
RSA2。 - 公钥是否属于当前
app_id和当前运行环境。 - 是否混用了沙箱密钥和正式环境密钥。
- 公钥内容是否完整,头尾标记或换行是否被错误处理。
- 验签时是否使用了支付宝发送的原始通知参数,是否在验签前修改、解码或重新拼装过字段。
证书模式需要单独处理
如果”接口加签方式”选择的是证书模式,就不能继续按照普通公钥模式只配置一个 ALIPAY_PUBLIC_KEY。通常需要配置:
- 应用公钥证书;
- 支付宝公钥证书;
- 支付宝根证书。
证书模式应使用 SDK 对应的证书初始化和验签方法,不能从证书文件中截取一段公钥,再把它当作普通支付宝公钥使用。
选择密钥时,应以调用的网关和接口为准。对于当前的 OpenAPI APP 支付,服务端用应用私钥签名,并使用开放平台接口加签配置对应的支付宝公钥或证书验签。客户端不能携带任何私钥,通常也不需要传入支付宝公钥。
备注:内容仅供参考。
