代码 微信支付 APIv3 上送敏感字段:Wechatpay-Serial error 与平台私钥解密失败的排查顺序

2026-10-17 09:02:10

微信支付 APIv3 上送敏感字段:Wechatpay-Serial error 与平台私钥解密失败的排查顺序

商家转账收款人姓名、银行卡号、实名字段这类敏感信息,上送前都要用 RSA 公钥加密。跑不通的时候,报错一般落在这两个上:请求直接被拒(PARAM_ERROR: Platform certificate serial number Wechatpay-Serial error),或者微信侧报平台私钥解密失败。绝大多数是密钥方向和 Wechatpay-Serial 声明对不上,不是代码写错了。

官方文档

方向:上送和下行用的是两把不同的密钥

这是最容易弄反的地方。

上送(商户 → 微信支付)敏感信息:用微信支付平台证书公钥或微信支付公钥加密。只有持有对应私钥的微信支付能解开。

下行(微信支付 → 商户)敏感信息:微信支付用商户证书公钥加密,商户用商户私钥解密。

所以别拿商户证书去加密上送内容,也不要用任何私钥加密上送。方向反了,接口不报加密格式错,而是直接在你说的那两个报错上翻车。

加密算法与填充

算法是 RSA 公钥加密,填充方案 RSAES-OAEP。

各语言里 OAEP 的写法:

  • OpenSSL:padding = RSA_PKCS1_OAEP_PADDING
  • Java:Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding")
  • PHP:padding = OPENSSL_PKCS1_OAEP_PADDING
  • .NET:fOAEP = true
  • Node.js:padding = crypto.constants.RSA_PKCS1_OAEP_PADDING
  • Go:EncryptOAEP

官方推荐使用 SHA-256 作为哈希算法,但 Java 示例给的是 OAEPWithSHA-1AndMGF1Padding。这里存在不一致,以你实际用的 SDK 和文档为准,核心约束是:你加密的方式,微信侧要能解开。

密文输出统一 Base64。

Wechatpay-Serial 声明的是本次用了哪把公钥

请求头 Wechatpay-Serial 的作用是告诉微信支付:这份密文是用哪把公钥加密的。两种模式传的值不一样。

平台证书模式:传平台证书序列号,例如 Wechatpay-Serial: 4A4EF961F12345D888A902944179F1F270FC68B3。这是示例值,每个商户号不同,必须传自己商户号对应的那本平台证书序列号。

微信支付公钥模式:传微信支付公钥 ID,例如 Wechatpay-Serial: PUB_KEY_ID_0116571234562024052000123400000000。同样是示例值。

微信支付会更新平台证书,更新期间可能同时有多本平台证书可用于加密,所以才要靠 Wechatpay-Serial 声明本次用的是哪一本,微信侧才能挑出对应私钥解出密文。

长度限制:RSA2048 + OAEP 约 214 字节

RSA2048 下,OAEP(SHA-1) 能加密的原文上限约 214 字节,Java 示例里对应的报错文案就是「加密原串的长度不能超过 214 字节」。

姓名、身份证号、银行卡号这类单字段都很短,没问题。要留意的是把多个字段拼成一个 JSON 一起加密的场景,拼完可能就超了。改用 SHA-256 时可用长度还会更短。

报错归因

PARAM_ERROR: Platform certificate serial number Wechatpay-Serial error

出现在「上传参数需要加密敏感信息」的接口,含义是证书序列号错误,或者与请求商户号对应的平台证书不匹配。按顺序查:

  1. 加密用的证书是不是「微信支付平台证书」。用成商户证书就会报这个。
  2. 是不是「请求方商户号对应」的平台证书,有没有误用别的商户号的平台证书。
  3. 商户证书 / 平台证书是否过期,按指引检查更新后重新部署。

平台私钥解密失败

微信侧拿到你的密文,用对应私钥解不开。三类常见原因:

  1. 加密敏感数据用的公钥不对,不是微信支付平台证书公钥或微信支付公钥。
  2. Wechatpay-Serial 声明的序列号,和实际用来加密的那把公钥不匹配。
  3. 加密方式不符合规范,需确保使用 RSAES-OAEP(推荐 SHA-256)。

关联:回调报 AEADBadTagException: Tag mismatch

这是另一条链路的问题——回调 / 平台证书下载报文用 AES-256-GCM,密钥是 APIv3 密钥。Tag mismatch 常见原因:APIv3 密钥用错(用了别的商户号的,或者误用 APIv2 的 APIKey);密文本身不对(报文里给的是 Base64);解密时漏传了 associated_data。

各语言加密示例要点

  • Java:Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding"),密文 Base64 编码。
  • PHP:openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)。
  • Go:EncryptOAEP(sha1.New(), rand.Reader, rsaPublicKey, msg, nil),再 Base64。

适用范围说明

上面讲的是商户直连 APIv3、上送敏感字段的场景。服务商模式下的证书归属,以及「平台证书模式」与「微信支付公钥模式」在下载、轮换流程上的差异,以官方文档为准,不要跨模式套用序列号。语言示例部分来自官方文档写法,未在全部语言环境逐一实测,接入时以你所用 SDK 的实现为准。

推荐文章

程序员茄子在线接单