Laravel怎么集成微信支付_Laravel使用EasyWeChat插件【必备】

4次阅读

EasyWeChat v6与v5差异大,新项目必须用v6;需手动传配置、正确处理prepay_id路径、规范jsAPI参数、严格验签回调、结合异步通知与主动查单防丢,证书须无bom且为绝对路径。

Laravel怎么集成微信支付_Laravel使用EasyWeChat插件【必备】

EasyWeChat 版本选错直接导致支付失败

laravel 集成微信支付时,overtrue/wechat(即 EasyWeChat)的 v5 和 v6 行为差异极大,v6 强制要求使用 HttpClient 实现,且签名逻辑、参数结构、回调验签方式全变了。很多教程照搬旧版代码,结果 app.pay()->unifiedOrder()Invalid signature 或空响应。

实操建议:

  • v5 仍支持 Laravel 5.8–8.x,但已停止维护;新项目必须用 v6(overtrue/wechat:^6.0
  • v6 不再自动读取 config/wechat.php,需手动传入配置数组或使用 WeChat::officialAccount() 等工厂方法
  • 沙箱环境调试时,v6 的 useSandbox(true) 必须在实例化后调用,不能写在配置里
  • 微信返回的 prepay_id 在 v6 中藏在 $result['response']['prepay_id'] 下,不是顶层字段

Laravel 中如何安全传参给前端发起 JSAPI 支付

很多人把 appIdtimestampnonceStrpackagesignTypepaySign 全部从后端拼好再返回 json,结果前端WXJSSDK.chooseWXPay() 时提示“参数错误”——问题常出在 package 值没按微信规范转义,或 timeStamp字符串而非整数秒。

实操建议:

  • package 字段值必须是 prepay_id=xxx,不能带空格、换行或额外引号
  • timeStamp 必须是 (int) time(),不能是 Carbon::now()->timestamp(可能带毫秒)
  • paySign 生成前,所有参与签名的字段必须按字典序排序,且 package 的值不能 URL 编码(EasyWeChat v6 内部已处理,勿二次 encode)
  • 别把 key(商户 API 密钥)暴露到前端,签名必须 100% 在服务端完成

微信支付回调验签失败的三个高频原因

收到微信 POST 回调后,$app->payment->handleNotify() 返回空或抛出 InvalidSignatureException,不是证书问题就是数据解析错了。v6 默认只接受 application/xml,而有些 nginxcdn 会把 POST body 转成 application/x-www-form-urlencoded

实操建议:

  • Laravel 中需禁用默认的表单请求解析:在中间件里加 if (request()->isMethod('POST') && request()->header('Content-Type') === 'application/xml') { $content = file_get_contents('php://input'); }
  • v6 的 handleNotify() 默认会自动验签并更新订单状态,若需自定义逻辑,应传 false 关闭自动处理:$app->payment->handleNotify($callback, false)
  • 验签失败时先 dump file_get_contents('php://input'),确认是否为空或被截断——常见于 Nginx 的 client_max_body_size 过小
  • 证书路径必须是绝对路径,ssl_cerssl_key 要对应微信商户平台下载的 apiclient_cert.pemapiclient_key.pem,不能用 .p12

异步通知与主动查询订单状态怎么配合才不丢单

微信回调可能超时、重复或丢失,只依赖 handleNotify() 更新订单状态极易造成“用户付了钱,系统没记账”。但也不能每笔都轮询查,成本高还可能触发风控。

实操建议:

  • 回调成功后,立即记录原始 XML 到数据库(字段如 notify_rawnotify_at),哪怕只是存个日志表,方便事后对账
  • 对“通知未到达”的订单,启用延迟队列(如 Laravel Horizon)在 5 分钟后查一次 $app->payment->find($orderNo),最多重试 3 次
  • 查单接口返回 trade_state=NOTPAYCLOSED 时,不要直接标记失败——可能是用户中途取消,得结合 return_coderesult_code 综合判断
  • 微信侧的 out_trade_no 必须全局唯一,Laravel 中建议用 Str::uuid()->toString() 或时间戳+随机数生成,避免并发重复

最麻烦的其实是证书和签名链——微信的 SHA256withRSA 和 EasyWeChat v6 的 OpenSSL 调用方式耦合极深,稍有不慎就卡在 openssl_sign(): supplied key param cannot be coerced into a private key,这时候别急着换包,先检查 pem 文件有没有被编辑器悄悄转成 UTF-8 BOM。事情说清了就结束

text=ZqhQzanResources