微信支付的“商户密钥”是商户在微信商户平台手动设置的32位纯字母数字字符串,PHP仅用于签名和AES-256-GCM解密,不可生成、不可混淆APIv2密钥、不可泄露或硬编码。

微信支付的“商户密钥”不是 PHP 代码里写出来的,而是你在微信商户平台手动配置、生成并下载的固定字符串——PHP 只负责在调用 API 时正确使用它(比如参与签名、解密回调)。写错、混淆类型、硬编码泄露、或用错加密方式,是绝大多数失败的根源。
商户密钥在哪配?别在 PHP 里“生成”
微信支付没有提供任何 PHP 函数(如 openssl_pkey_new 或 random_bytes)来“生成”商户密钥。它必须通过以下路径获取:
- 登录 微信商户平台 →「账户中心」→「API安全」→「APIv3密钥」
- 点击「设置APIv3密钥」,输入一个 32 字符长度的纯数字/字母组合(不能含符号、不能有空格),提交后即生效
- 这个密钥仅用于 APIv3 接口的
sign签名和aes-256-gcm解密通知,与旧版 APIv2 的key(32位MD5密钥)完全无关
PHP 怎么用 APIv3 密钥做签名?关键在拼接和哈希
APIv3 要求对请求体做 SHA256 with RSA 签名,但密钥本身不直接参与 RSA 运算——它只用于构造签名原文中的 resource.sign 字段(即解密后的回调响应体签名)。真正要用密钥的地方是:
- 构造
Authorization请求头时,需用商户私钥(apiclient_key.pem)对摘要签名,而该摘要中包含时间戳、随机串、请求路径、以及请求体的 SHA256 值 - 解密支付结果通知时,必须用你配置的 APIv3 密钥(32 字符字符串)作为 AES-GCM 的 key,配合 nonce 和 associated_data 才能正确解密
resource.encrypted_message - 示例解密片段(省略异常处理):
use openssl_decrypt; $associated_data = $notify['resource']['associated_data']; $nonce = $notify['resource']['nonce']; $ciphertext = base64_decode($notify['resource']['ciphertext']); $key = 'your_32_char_api_v3_key_here'; // 必须严格32字节,UTF-8编码下中文会超长 $plaintext = openssl_decrypt($ciphertext, 'aes-256-gcm', $key, OPENSSL_RAW_DATA, $nonce, $associated_data);
常见密钥相关错误和避坑点
90% 的“验签失败”“解密为空”问题都源于密钥误用:
bee餐饮点餐外卖小程序是针对餐饮行业推出的一套完整的餐饮解决方案,实现了用户在线点餐下单、外卖、叫号排队、支付、配送等功能,完美的使餐饮行业更高效便捷!功能演示:1、桌号管理登录后台,左侧菜单 “桌号管理”,添加并管理你的桌号信息,添加以后在列表你将可以看到 ID 和 密钥,这两个数据用来生成桌子的二维码2、生成桌子二维码例如上面的ID为 308,密钥为 d3PiIY,那么现在去左侧菜单微信设置
立即学习“PHP免费学习笔记(深入)”;
-
openssl_decrypt返回 false?检查$key是否恰好 32 字节:用strlen($key) === 32验证,不要用mb_strlen;避免从配置文件读取时带 BOM 或换行 - 把 APIv2 的 MD5 密钥(如
abcd1234efgh5678ijkl9012mnop3456)直接当 APIv3 密钥用 → 绝对不行,二者算法、用途、长度约束完全不同 - 在日志或调试输出中打印
$key或$_ENV['WECHAT_V3_KEY']→ 密钥一旦泄露,攻击者可伪造回调、篡改订单状态 - 用
file_get_contents('key.txt')直接加载密钥 → 若文件权限为 644 且 Web 目录可访问,极易被下载
最易被忽略的是:APIv3 密钥虽是字符串,但它在 AES-GCM 中被当作原始字节序列使用。PHP 不会自动填充或截断——少一位、多一个空格、混入全角字符,解密必然失败,且错误信息极其模糊(只返回 false 或空字符串)。










