微信支付v3在thinkphp中签名失败、验签不过、解密报invalid aead tag,主因是证书路径配置错误、验签顺序颠倒(须先平台证书验签再解密)、gcm参数未正确base64_decode且tag长度不为16字节。

微信支付V3在ThinkPHP里签名失败、回调验签不过、解密报Invalid AEAD tag——基本都是证书路径、验签顺序、GCM参数拆分这三处出问题,不是SDK写得烂,是你没踩对点。
ThinkPHP里加载apiclient_cert.pem和apiclient_key.pem总报file_get_contents() failed
证书不能放public/或runtime/下被Web服务器拦截;也不能用__DIR__ . '/../cert/'这种硬编码路径,Docker或宝塔部署时绝对路径一变就崩。
- 把证书放进
config/cert/(推荐)或runtime/cert/(确保目录可写) - 在
config/wechat.php里统一配置:'cert_path' => app()->getRuntimePath() . 'cert/' - 加载前必须显式校验:
is_readable($certPath . 'apiclient_cert.pem')和is_readable($certPath . 'apiclient_key.pem') -
apiclient_key.pem必须带密码(微信后台导出时设的),调用openssl_pkey_get_private()时第2个参数不能传空
wechatpay-php SDK回调验签总提示Invalid signature
不是私钥错了,是验签顺序反了:微信要求「先用平台证书验签成功,再解密」。你要是跳过验签直接解密,哪怕密文是对的,也白忙活。
- 从
Wechatpay-Serial请求头取序列号,匹配你本地缓存的平台证书(不是商户证书) - 用该平台证书公钥 +
Wechatpay-Signature+Wechatpay-Timestamp+ 原始JSON体,调openssl_verify()验签 - 验签失败直接
return response('', 401),别往下走 - 验签通过后,才提取
resource.nonce、resource.ciphertext、resource.associated_data、resource.tag四部分
PHP openssl_decrypt()解密微信AES-GCM总抛Invalid AEAD tag
微信的ciphertext和tag都是base64编码过的,但openssl_decrypt()第5个参数$tag要的是原始二进制,且长度必须严格为16字节——少一个字节、多一个空格都不行。
- 必须分别对
ciphertext和tag做base64_decode(),不能只解ciphertext -
$tag = base64_decode($rawTag)之后,立刻检查strlen($tag) === 16,不等就拒收 -
openssl_decrypt()第4个参数是$iv(即nonce),第5个是$tag,第6个是OPENSSL_ZERO_PADDING(GCM固定) - 别漏掉
associated_data,它要作为第7个参数传入,值是base64_decode($rawAssociatedData)
回调控制器里混写验签+业务逻辑导致难维护
微信回调不是普通接口,它是外部系统主动推数据,字段结构、签名规则、响应格式全由微信定死。你把验签、解密、查库、改状态全塞进一个notify()方法里,下次加支付宝回调就得复制粘贴再改一堆if。
- 定义适配器接口
PaymentNotifyAdapter,强制实现verify()、getOrderId()、buildSuccessResponse() - 微信V3实现类只管:从
php://input读原始JSON、验签、解密、提取resource.original_type === 'transaction'字段 - 控制器只做三件事:
$adapter->verify()→ 查订单是否存在 →$adapter->buildSuccessResponse() -
buildSuccessResponse()必须返回纯字符串'success',不能带\n、\r、BOM头,否则微信会重试
最常被忽略的其实是幂等控制——resource.id才是微信回调唯一ID,不是订单号,也不是out_trade_no。你拿它去查是否已处理,比任何时间戳或随机串都可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











