不用官方SDK而选yansongda/pay,因其文档清晰、命名统一、验签逻辑明确,且深度适配Laravel容器与异常机制;yansongda/pay支持自动绑定、门面调用及预置中间件,安装需指定版本并手动发布配置,配置项default须为wechat、notify_url须为公网完整URL、证书路径推荐storage_path。

为什么不用官方 SDK 而选 yansongda/pay
微信官方 PHP SDK 文档混乱、命名不统一、回调验签逻辑藏得深,且不兼容 Laravel 的服务容器和异常处理机制。而 yansongda/pay 是社区维护最活跃的封装,已适配 Laravel 8–11,自动绑定 Pay::class 到容器,支持门面调用,关键路径(如异步通知、退款回调)都预置了 Laravel 风格的中间件和响应结构。
安装与基础配置要注意哪些坑
直接 composer require yansongda/pay:~4.0(Laravel 9+ 推荐 4.x;Laravel 8 用 3.x)。安装后必须手动发布配置:php artisan vendor:publish --provider="Yansongda\LaravelPay\ServiceProvider",否则 config/pay.php 不会生成,后续所有调用都会抛出 InvalidArgumentException: No supported gateway。
配置项里最容易填错的是:
-
default必须设为wechat(不是weixin或wxpay) -
wechat下的notify_url必须是**可公网访问的完整 URL**(如https://your.app/wechat/notify),不能写/wechat/notify或本地地址 -
cert_client和cert_key路径要写对,推荐用storage_path('app/certs/apiclient_cert.pem'),别用相对路径或public_path()
扫码支付(Native)怎么生成二维码并监听结果
扫码支付本质是后端调用微信统一下单接口,拿到 code_url,再由前端转成二维码展示。用户扫码后,微信服务器会主动调用你配置的 notify_url,你必须在该路由里完成验签、处理业务、返回成功响应——漏掉任一环节,微信会持续重试。
Route::post('/wechat/notify', function () {
$pay = Pay::wechat();
try {
$data = $pay->verify(); // 自动验签 + 解析 XML
if ($data->return_code === 'SUCCESS' && $data->result_code === 'SUCCESS') {
// 这里更新订单状态、发货等
Order::where('out_trade_no', $data->out_trade_no)->update(['status' => 'paid']);
}
return $pay->success(); // 必须返回此响应,否则微信不停重发
} catch (\Exception $e) {
\Log::error('WeChat notify failed: '.$e->getMessage());
return $pay->fail(); // 失败也要返回 fail(),不能 throw
}
});
下单生成二维码示例:
$order = [
'out_trade_no' => 'ORD'.date('ymdHis').rand(1000, 9999),
'description' => '商品描述',
'amount' => ['total' => 100], // 单位:分
'notify_url' => config('pay.wechat.notify_url'),
];
$result = Pay::wechat()->scan($order);
$codeUrl = $result->code_url; // 拿到这个 URL,用 qrcode.js 或后端生成图片
回调验签失败常见原因和调试方法
最常见的错误是 Signature verification failed,基本都源于三类问题:
- 微信传来的原始 XML 数据被 Laravel 中间件(如
TrimStrings、ConvertEmptyStringsToNull)提前解析或修改,导致验签原文不一致 → 必须给/wechat/notify路由加middleware('web')并确保没有其他中间件干扰原始输入 - 证书路径不对,或
cert_client和cert_key文件权限不是 600,PHP 读不到 → 用file_exists()和is_readable()手动检查 - 配置里的
app_id、mch_id、key混用了测试环境和正式环境值,尤其key是商户平台「APIv3 密钥」,不是「API 密钥」
调试时可在 verify() 前加日志输出原始输入:file_get_contents('php://input'),比看微信后台日志更直接。











