微信支付v3 api必须用curl发起https请求并配置双向证书认证,禁用file_get_contents和stream_context_create;签名需用原始json字符串,回调验签须动态获取证书;沙箱与正式环境完全隔离。

微信支付 v3 API 必须用 curl 发起 HTTPS 请求,不能靠 file_get_contents 或 stream_context_create
微信支付 v3 接口强制要求双向证书认证(客户端证书 + 服务器证书校验),且必须携带 Authorization 签名头。PHP 原生的 file_get_contents 不支持传入 client cert/key,stream_context_create 虽能配 ssl 选项但极难正确设置证书链和密钥格式,极易报 SSL certificate problem: unable to get local issuer certificate 或 Peer's Certificate issuer is not recognized。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 用
curl,且必须显式调用curl_setopt($ch, CURLOPT_SSLCERT, 'apiclient_cert.pem')和curl_setopt($ch, CURLOPT_SSLKEY, 'apiclient_key.pem') - 证书文件必须是 PEM 格式,且私钥不能带密码(微信不接受加密私钥);可用
openssl rsa -in apiclient_key.pem -out apiclient_key_nopass.pem去密 - 务必设置
curl_setopt($ch, CURLOPT_CAINFO, 'cacert.pem')—— 这个不是可选的,微信 v3 接口会拒绝无 CA 校验的请求 - 别用系统自带的
cacert.pem,直接从 curl 官网下载最新版,否则可能因根证书过期导致SSL connect error
generateSign 签名函数里,body 必须是原始 JSON 字符串,不能是 json_encode 后再 trim 或 str_replace
微信 v3 签名规则要求:对 HTTP 请求体(即 body)做 SHA256-HMAC 签名时,必须使用「未加工」的 UTF-8 编码字节流。常见错误是先 json_encode($data),再手动去掉空格、换行或引号转义 —— 这会导致签名值和微信服务端计算结果不一致,返回 {"code":"SIGN_ERROR","message":"签名验证失败"}。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
-
$body = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);—— 仅用这两个 flag,其他任何处理都不要加 - 确保
$data中字符串字段本身不含非法控制字符(如 \x00、\r\n 混入文本内容),否则json_encode输出不可预测 - 签名前检查
mb_strlen($body, '8bit'),确认长度和实际发送的 body 字节数一致(尤其注意 BOM、UTF-8 变长编码)
回调通知验签必须用 openssl_verify,且公钥要从微信 /v3/certificates 接口动态获取
微信不再提供固定 RSA 公钥,所有回调通知(notify_url)的签名都由平台当前有效证书签发,证书每三个月轮换一次。硬编码公钥或用旧证书验签,会在某天突然全部失败,错误日志里只看到 "failed to verify signature",毫无上下文。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 首次收到回调前,先调用
GET /v3/certificates获取所有有效证书,并缓存到本地(如wechat_certs.json),含serial_no和encrypt_certificate.ciphertext解密后的 PEM 公钥 - 回调到达时,从
WECHAT-REQUEST-SIGNATURE头取签名,从WECHAT-REQUEST-TIMESTAMP和WECHAT-REQUEST-NONCE构造待验数据,再用匹配WECHAT-REQUEST-SERIAL的证书公钥调用openssl_verify - 别用
hash_hmac或自写 RSA 验签逻辑 —— 微信用的是 SHA256withRSA,必须用openssl_verify($data, $signature, $pubkey, OPENSSL_ALGO_SHA256)
沙箱环境和正式环境的 appid、mchid、证书、API 基础路径全部不同,切勿共用配置
微信沙箱不是“开关一开就切换”,而是完全独立的一套环境:沙箱 appid 和正式 appid 不同,mchid 也不同,证书需单独申请,API 地址是 https://api.mch.weixin.qq.com/v3/sandbox/...。混用会导致「参数错误」「商户号不存在」或静默失败(比如统一下单成功但查不到订单)。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- 把沙箱和正式环境拆成两套完整配置,包括
appid、mchid、cert_path、key_path、api_base_url - 在代码里用
if (ENV === 'sandbox') { ... }显式分叉,而不是靠某个开关变量动态拼接 URL - 沙箱下单后,必须用沙箱
mchid调用沙箱查询接口,正式环境订单绝不能拿去沙箱查 —— 错误示例:GET /v3/pay/transactions/id/{transaction_id}在沙箱环境必须带/sandbox/前缀
最麻烦的其实是证书更新和验签逻辑——它不像参数填错那样立刻报错,而是在证书过期后某天凌晨三点开始批量失败,日志里只有空响应或 401。盯住 /v3/certificates 返回的 effective_time 和 expire_time,比写业务逻辑还重要。











