直接 composer require wechatpay/wechatpay 会失败,因其强制依赖 ext-json 和 ext-openssl 且最低要求 php 7.2,而本地环境常缺失这些扩展或版本过低;需检查扩展启用状态、php 版本,并避免使用 --ignore-platform-reqs。

为什么直接 composer require wechatpay/wechatpay 会失败?
因为微信官方 SDK 的 PHP 版本(wechatpay/wechatpay)**不依赖 ext-curl,但强制要求 ext-json 和 ext-openssl**,且最低 PHP 版本为 7.2。很多本地开发环境(尤其是 macOS 自带 PHP 或旧版 Docker 镜像)默认没开 openssl 扩展,或者 json 被禁用——此时 Composer 安装会卡在「requirement not met」或直接报 Class 'JsonException' not found。
实操建议:
- 运行
php -m | grep -E 'json|openssl'确认两个扩展已启用;若缺失,在php.ini中取消对应;extension=行的注释,并重启 PHP-FPM 或 Apache - 检查 PHP 版本:
php -v,低于 7.2 的必须升级,wechatpay/wechatpay不兼容 PHP 7.1 及更早版本 - 不要用
--ignore-platform-reqs强行跳过校验——SDK 内部大量使用json_encode($data, JSON_THROW_ON_ERROR),PHP 7.2+ 才支持该 flag
如何正确初始化 WeChatPayHttpClient 并避免签名失败?
签名失败是集成中最常见的问题,根源往往不在代码逻辑,而在证书路径、时间戳、或请求体格式。SDK 要求商户私钥、平台证书、APIv3 密钥三者严格匹配,且所有请求必须携带 Authorization 头。
实操建议:
- 证书文件必须是 PEM 格式,且私钥不能加密(即不能有
DEK-Info行),可用openssl rsa -in apiclient_key.pem -out apiclient_key_unencrypted.pem解密 -
WeChatPayHttpClient构造时传入的$merchantId是「微信支付商户号」(10 位纯数字),不是 APPID;$merchantSerial是平台证书序列号(从证书中提取,非文件名) - 调用
post()前,确保请求体是原始 JSON 字符串(非数组),且不含空格或换行——SDK 内部会用trim($body)计算签名,多一个空格就失败 - 时间戳必须用
date('Y-m-d\TH:i:sP')格式(注意\T是字面量 T),且服务器时间与微信服务器偏差不能超过 9 分钟
沙箱环境调用 /v3/pay/transactions/native 返回 401 怎么排查?
401 表示签名验证失败或授权头无效,和正式环境错误码一致,但沙箱有额外限制:它**不校验平台证书,但强制要求使用沙箱 API 域名和沙箱密钥**。
微信公众号推文写作与发布助手。支持深度文章撰写(1500+ 字)、智能配图搜索、API 配置引导、草稿箱上传、一键排版等全流程功能。 每篇文章默认 1500 字以上,配备 1 张相关配图(放在第一段后),包含清晰的分段标题结构。
实操建议:
- 确认请求 URL 是
https://api.mch.weixin.qq.com/v3/...(正式)还是https://api.sandbox.mch.weixin.qq.com/v3/...(沙箱)——漏掉sandbox.就 401 - 沙箱模式下,
$apiV3Key必须是从微信支付平台「沙箱」页面获取的「APIv3 密钥」,不是商户后台设置的「API 密钥」(v2) - 用 SDK 的
WeChatPayHttpClient::buildAuthorization()方法生成头时,传入的$method必须全大写(如'POST'),路径必须以/v3/...开头且不带查询参数 - 开启 SDK 日志:
new WeChatPayHttpClient(..., new Psr\Log\NullLogger())换成new Monolog\Logger('wxpay'),查看实际发出的 Authorization 头内容,比对微信文档中的签名算法步骤
回调通知验证 notify 时为何 decryptNotify 报错 Invalid payload?
这个错误几乎都源于「解密前没做原始字符串校验」。微信回调是 POST raw body,但很多框架(Laravel、ThinkPHP)会自动解析 JSON 或覆盖 php://input,导致拿到的是空或已 decode 的数组,而非原始密文。
实操建议:
- 绕过框架中间件,直接读取原始输入:
$raw = file_get_contents('php://input');,并确保该行在任何 JSON 解析、表单解析之前执行 - 验证
$raw是否为空:微信可能因网络问题重发空请求,需先if (!$raw) { http_response_code(400); exit; } -
decryptNotify()接收的是完整原始 body 字符串,不是$_POST或json_decode($raw)后的结果;传入数组或 null 会直接抛Invalid payload - 解密后得到的是 JSON 字符串,需再
json_decode($decrypted, true)——SDK 不负责二次解析
证书序列号、APIv3 密钥、时间戳精度、原始 body 读取时机,这四点漏掉任何一个,调试就会卡住半天。别信“配置没问题”,每次改完立刻用 curl -v 对比请求头和微信文档里的示例。










