Skip to content

微信支付 v3 的证书和密钥,四类东西别再配混了 ​

接微信支付 v3,商户平台上要拿四类东西:APIv3 密钥、API 证书、商户证书序列号、微信支付公钥。

落到配置文件里,这四类会摊成八个字段。网上大部分教程只讲了其中两三个,剩下的等你签名失败的时候自己撞。

踩错的代价 ​

这几样配错,报错长得都一样:SIGN_ERROR,HTTP 401,「签名验证失败」。

它不会告诉你是私钥错了、序列号错了、还是公钥拿反了。八个变量,一个错误码。

更难受的是它不是必现。下单接口能通,回调解密失败——因为下单用的是商户私钥签名,回调用的是 APIv3 密钥解密,两套东西。你以为配好了,钱收不到才发现。

别逐个字段试,按环节反推 ​

八个字段一个个换着试,是最慢的排查方式。按失败发生在哪个环节,能直接圈到某一类上:

走到哪一步说明这几个是对的问题只可能在
下单请求就被拒(401)——商户私钥、证书序列号、商户号
下单成功,拿到 prepay_id私钥、序列号、商户号都对——
收到回调但解密失败签名那套全对只可能是 APIv3 密钥
解密出来了但验签不过密钥也对微信支付公钥 / 公钥 ID

下单成功这件事本身就是一个强信号:它证明签名那一套(私钥 + 序列号 + 商户号)全对,剩下的问题不可能在那三个上面。

很多人卡住是因为一看到「签名失败」就把八个字段全怀疑一遍。先看走到哪一步断的,一次能排掉一半。

先分清方向:签名和验签不是同一把钥匙 ​

配之前先把方向理清楚,后面八个字段就不会乱。

签名和验签是两个相反的方向,用的是两套不同的钥匙:

我方 → 微信微信 → 我方
动作给请求签名验证响应和回调的签名
用什么商户 API 私钥(apiclient_key.pem)微信支付公钥
标识商户证书序列号公钥 ID

回调报文的解密是第三件事,跟上面两个方向都无关,用 APIv3 密钥。

三件事、三套东西,混在一起就是各种「签名失败」。

四类东西分别是什么 ​

一、APIv3 密钥(32 个字符) ​

在商户平台的「API 安全」页面里设置(账户中心下面),自己定的 32 个字符。

用途只有两个:解密回调报文(AES-256-GCM)、下载平台证书。

最常见的坑:跟 v2 的「API 密钥」搞混。 商户平台上这是两个独立入口,都是 32 位,都叫密钥。v2 那个做 MD5/HMAC 签名,v3 这个做解密,互相不通用。设置页面还挨在一起。

这个坑不是「不小心」,是结构性的——后面配置那节你会看到,代码里 mchKey 和 apiV3Key 就是两个独立字段,摆着两个坑位等你填错。

二、API 证书(两个文件) ​

同一个页面申请下载,解压后你要的是两个:

  • apiclient_key.pem —— 商户 API 私钥,签名用它
  • apiclient_cert.pem —— 商户证书,序列号从它里面读

这个只能下载一次,重新申请会作废旧的。生产环境的证书丢了就是重新申请,中间这段时间收不到钱。

路径用相对路径或从配置注入,不要写绝对路径。 本地跑通了传到服务器上找不到文件。

三、商户证书序列号 ​

从证书里读:

bash
openssl x509 -noout -serial \
        -in apiclient_cert.pem

输出形如 serial=1A2B3C...,去掉 serial= 前缀,转成大写填进配置。

「API 安全」页面上也能直接看到,两边应该一致。不一致说明你手里的证书不是当前生效的那张——大概率是重新申请过,本地还留着旧的。

四、微信支付公钥 —— 这里 2024 年之后变了 ​

这是全文最容易过时的一节,也是最多人配错的一节。

新申请的商户号:直接在商户平台下载「微信支付公钥」,配一个公钥文件加一个公钥 ID(形如 PUB_KEY_ID_...)。静态的,不会变。

老商户号:两种模式并存。平台证书那套还能用,但公钥模式同样可以申请——
在「账户中心 → API 安全」里,官方也推荐迁过去(平台证书五年一换,公钥不用换)。

如果你还在用平台证书:它不能在页面上直接下载,要用工具或调接口拿,
拿的时候还得用 APIv3 密钥解密。

平台证书模式有个隐藏的坑:证书会轮换。微信提前一段时间下发新证书,旧的到期作废。如果你把平台证书硬编码进代码、或者只在启动时加载一次,某天会突然全线验签失败,而你什么都没改。

官方 SDK 里有自动更新平台证书的实现,别自己写。

落到配置里,实际是八个字段 ​

前面四类摊开是六个字段,加上 appId 和 mchId 一共八个。这张表是我配的时候最想要、但网上找不到的东西:

(先说个数:一个真实项目的微信支付配置类,78 行、12 个字段。
12 个里 4 个是身份——AppID、商户号、子商户 AppID、子商户号;
8 个是凭据。下面这张表是那 8 个里属于 v3 的 6 个,加上 2 个身份字段。
剩下那两个凭据字段属于 v2,本文最后一节单说。)

配置字段是什么去哪拿
appId小程序或公众号 AppID小程序/公众号后台
mchId商户号商户平台首页
apiV3KeyAPIv3 密钥(32 位)「API 安全」页面,自己设
certSerialNo商户证书序列号openssl 从证书读,或「API 安全」页看
privateCertPathapiclient_cert.pem 路径申请证书时下载
privateKeyPathapiclient_key.pem 路径同上,同一个压缩包
publicKeyPath微信支付公钥文件路径商户平台下载
publicKeyId公钥 ID(PUB_KEY_ID_...)跟公钥一起给的

配置写出来是这样:

yaml
wxpay:
  # 小程序或公众号的 AppID —— 注意不是商户号
  app-id: wx**********
  mch-id: 16**********
  # APIv3 密钥,32 位
  # 不是 v2 的那个 API 密钥
  api-v3-key: ****************************
  # 商户证书序列号,大写
  # 从 apiclient_cert.pem 里读出来
  cert-serial-no: 1A2B3C**********
  # 相对路径,别写绝对路径
  private-cert-path: wx/apiclient_cert.pem
  private-key-path: wx/apiclient_key.pem
  # 微信支付公钥模式(新商户号)
  public-key-path: wx/pub_key.pem
  public-key-id: PUB_KEY_ID_**********

配完自己核一遍:证书序列号和商户平台页面上显示的一致,这一条能挡掉一半的签名失败。

还有四个字段不属于 v3,但最容易顺手填错 ​

翻配置的时候你还会看到这几个,它们不是 v3 要的:

  • mchKey —— v2 的 API 密钥。跟 apiV3Key 长得一样都是 32 位,填串了就是签名失败
  • keyPath —— v2 的 p12 证书,老版本退款用的
  • subAppId / subMchId —— 服务商模式才用。你是普通商户就留空,填了反而报错

也就是说,那 12 个字段实际分属三套体系:

体系字段数
v2(mchKey + p12)2
v3 证书模式4
v3 公钥模式(2024 年 8 月后的新商户)2
身份(两套都要)4

一个配置类里同时躺着三代凭据方案,这本身就是最容易配混的原因。
字段名不会告诉你哪个属于哪一代,注释才会——
那两个公钥字段的注释里明写着「2024.08 后的新商户验签需要用此公钥」。

看到一个字段不知道要不要填的时候,先去看它的注释,不要看名字。

最后这对我单独说一句:服务商模式是另一套玩法(分账、子商户),跟普通商户号的配置不通用。看到配置里有这两个字段不代表你需要填。 什么时候才该上服务商模式,我另外写。

与其配完靠试,不如让它启动就炸 ​

上面那张八字段表,配完之后怎么知道对不对?

大部分人的答案是「发一笔一分钱的单试试」。这个答案的问题在于它只能试出一半——
前面说过,下单成功只证明签名那三个字段对,回调解密和验签要等真有人付款才暴露。

更好的做法是:把能在启动时查的都查掉。

我在一个项目里见过这么干的,一个 158 行的配置校验器,跑在启动阶段。
它查这几样:

查什么判据
APIv3 密钥有值,且长度必须是 32 位
商户号、证书序列号有值
商户私钥、商户证书路径有值,且文件真的读得到
微信支付公钥、公钥 ID两个都有值
回调地址四个地址(支付、合单、分账、退款)逐个查

八个字段里,有六个能在启动时查出问题:长度不对、文件不存在、值是空的。
剩下两个(序列号跟证书对不对得上、公钥 ID 跟公钥文件配不配套)
才真的要发一笔单才知道。

这个校验器最值得抄的不是它查什么,是它怎么报。

它不是查到第一个错就抛,而是把所有错攒起来,编号列全,最后一次性抛,
消息里带一句「共 N 项」。

差别在返工次数:一次抛一个错,你改一个、重启一次、再发现下一个,
八个字段能让你重启五六次。一次抛全,改一轮就完事。

配置错误天生适合攒起来一起报,因为它们之间没有依赖——
不像业务校验,第一步没过后面根本没法算。

顺带一个值得抄的细节:那个校验器把「致命」和「告警」分开。
凭据缺失是致命,直接抛;回调地址没配只是告警,写条日志继续跑——
因为回调地址在有些部署方式下是运行时才拼出来的。

分清这两档,校验器才不会变成大家争着注释掉的东西。

常见报错对照表 ​

报错真正的原因怎么查
SIGN_ERROR / 401 签名验证失败certSerialNo 或商户私钥不对openssl 重新读一次序列号,跟商户平台核对,注意大写
下单成功,回调解密报错apiV3Key 填成了 v2 的 mchKey回「API 安全」页确认是哪个入口的密钥
验签失败,但签名没问题公钥拿反了,或用商户证书去验签确认验签用的是微信那边的公钥,不是自己的
昨天还好好的,今天全部验签失败平台证书轮换了换成 SDK 的自动更新,或迁到公钥模式
本地通,服务器 500,找不到文件证书用了绝对路径改相对路径或从配置注入
提示子商户号错误普通商户填了 subMchId服务商模式才用,普通商户留空
NOAUTH不是签名问题,是商户号没签约这个产品商户平台的「产品中心」确认 JSAPI/APP 支付已开通

签名-A

大粽子