Yansongda与EasyWeChat实战:多端支付集成开发指南
1. 为什么选择Yansongda和EasyWeChat在开发多端支付功能时选择合适的SDK能让你事半功倍。Yansongda/pay和EasyWeChat这两个PHP扩展包可以说是目前国内最受欢迎的支付集成解决方案。我去年接手一个电商项目需要在Web、H5和App三个端同时接入支付宝和微信支付当时对比了市面上各种方案最终选择了这对黄金组合。Yansongda/pay最大的特点是对支付宝支付的支持非常完善。它封装了支付宝的各种支付场景从电脑网站支付到手机网站支付再到APP支付一个扩展包全搞定。而且它的API设计非常符合Laravel开发者的习惯用起来特别顺手。记得第一次使用时我只用了不到半小时就完成了基础支付功能的接入。EasyWeChat则是微信生态开发的瑞士军刀。它不仅支持微信支付还能处理公众号、小程序等各种微信生态开发需求。我在实际项目中发现它的微信支付模块特别稳定各种支付场景都考虑得很周到比如JSAPI支付、Native支付、APP支付等。2. 环境准备与安装配置2.1 安装依赖包首先需要通过Composer安装这两个扩展包。建议在项目根目录下执行以下命令composer require yansongda/pay -vvv composer require w7corp/easywechat安装完成后我们需要进行基础配置。对于Laravel项目Yansongda/pay提供了开箱即用的ServiceProvider只需要在config/app.php中添加providers [ // ... Yansongda\LaravelPay\PayServiceProvider::class, ], aliases [ // ... Pay Yansongda\LaravelPay\Facades\Pay::class, ]2.2 配置文件设置接下来创建支付相关的配置文件。在config目录下新建pay.php和wechat.php// config/pay.php return [ alipay [ app_id env(ALIPAY_APP_ID), notify_url env(ALIPAY_NOTIFY_URL), return_url env(ALIPAY_RETURN_URL), ali_public_key env(ALIPAY_PUBLIC_KEY), private_key env(ALIPAY_PRIVATE_KEY), log [ file storage_path(logs/alipay.log), ], ], ]; // config/wechat.php return [ payment [ default [ app_id env(WECHAT_APP_ID), mch_id env(WECHAT_MCH_ID), key env(WECHAT_KEY), cert_path env(WECHAT_CERT_PATH), key_path env(WECHAT_KEY_PATH), notify_url env(WECHAT_NOTIFY_URL), ], ], ];记得在.env文件中配置相应的参数。这里有个小技巧微信支付的证书路径建议使用绝对路径避免在不同环境下出现路径解析问题。3. 多端支付实现方案3.1 Web端支付集成Web端的支付场景主要分为两种PC网站支付和手机浏览器支付。对于支付宝我们使用Yansongda的web和wap方法对于微信支付则使用EasyWeChat的Native支付方式。// 支付宝Web支付 public function alipayWeb(Request $request) { $order [ subject VIP会员充值, out_trade_no time(), total_amount 9.9, ]; return Pay::alipay()-web($order); } // 微信Native支付 public function wechatNative(Request $request) { $app Factory::payment(config(wechat.payment.default)); $result $app-order-unify([ body VIP会员充值, out_trade_no time(), total_fee 990, trade_type NATIVE, ]); return view(pay.qrcode, [code_url $result[code_url]]); }在实际项目中我发现微信Native支付有个需要注意的地方code_url生成的二维码需要在页面中定期检查支付状态通常每5秒轮询一次后端接口。3.2 H5端支付实现H5端的支付主要面向手机浏览器场景。支付宝使用wap支付微信则根据是否在微信浏览器内决定使用JSAPI支付还是H5支付。// 支付宝WAP支付 public function alipayWap(Request $request) { $order [ subject VIP会员充值, out_trade_no time(), total_amount 9.9, ]; return Pay::alipay()-wap($order); } // 微信H5/JSAPI支付 public function wechatH5(Request $request) { $app Factory::payment(config(wechat.payment.default)); $params [ body VIP会员充值, out_trade_no time(), total_fee 990, ]; if (isWechatBrowser()) { $params[trade_type] JSAPI; $params[openid] getWechatOpenid(); } else { $params[trade_type] MWEB; $params[scene_info] json_encode([ h5_info [type Wap] ]); } $result $app-order-unify($params); // 处理返回结果... }这里有个坑我踩过微信H5支付需要配置支付域名而且必须是通过ICP备案的域名否则会报错。建议在开发初期就配置好这些信息。4. App端支付集成4.1 支付宝App支付对于App端的支付宝支付Yansongda提供了非常简洁的APIpublic function alipayApp(Request $request) { $order [ subject VIP会员充值, out_trade_no time(), total_amount 9.9, ]; return Pay::alipay()-app($order); }在移动端接收到返回的orderString后需要调用支付宝SDK的支付方法。Android和iOS的调用方式略有不同但核心都是把这个orderString传给SDK。4.2 微信App支付微信App支付稍微复杂一些需要先统一下单然后获取prepay_idpublic function wechatApp(Request $request) { $app Factory::payment(config(wechat.payment.default)); $result $app-order-unify([ body VIP会员充值, out_trade_no time(), total_fee 990, trade_type APP, ]); $config $app-jssdk-appConfig($result[prepay_id]); return response()-json($config); }在App端你需要使用微信SDK的sendReq方法发起支付。这里有个经验分享Android平台需要特别注意包名和签名必须与微信开放平台填写的一致否则会调不起支付。5. 支付回调处理5.1 支付宝回调处理支付回调是支付流程中最关键的部分之一。Yansongda让支付宝回调处理变得非常简单public function alipayNotify(Request $request) { $alipay Pay::alipay(config(pay.alipay)); try { $data $alipay-verify(); $order Order::where(order_sn, $data-out_trade_no)-first(); if (!$order || $order-paid_at) { return $alipay-success(); } // 处理业务逻辑 $order-update([ paid_at now(), status paid ]); return $alipay-success(); } catch (\Exception $e) { Log::error(支付宝回调异常, [error $e]); return $alipay-success(); } }这里有个重要原则回调处理一定要做幂等设计。因为支付宝可能会多次回调确保同一笔订单不会重复处理。5.2 微信支付回调微信支付的回调处理稍微复杂一些需要使用EasyWeChat提供的闭包方式public function wechatNotify() { $app Factory::payment(config(wechat.payment.default)); $response $app-handlePaidNotify(function($message, $fail) { $order Order::where(order_sn, $message[out_trade_no])-first(); if (!$order || $order-paid_at) { return true; } if ($message[return_code] SUCCESS) { if ($message[result_code] SUCCESS) { $order-update([ paid_at now(), status paid ]); } elseif ($message[result_code] FAIL) { $order-update([status failed]); } } else { return $fail(通信失败请稍后再通知我); } return true; }); return $response; }在实际项目中我建议在回调处理中加入订单查询作为二次验证确保支付状态的真实性。微信支付的回调URL必须支持HTTPS这点需要特别注意。6. 常见问题与调试技巧6.1 支付参数配置问题在集成过程中最常见的错误就是配置不正确。以下是一些检查清单支付宝确保alipay_public_key是正确的支付宝公钥不是应用公钥private_key需要保留-----BEGIN PRIVATE KEY-----和-----END PRIVATE KEY-----标记notify_url和return_url需要正确配置微信支付app_id、mch_id和key必须与商户平台一致证书路径必须正确且文件可读支付域名必须在商户平台配置6.2 调试与日志记录两个扩展包都提供了日志功能建议在开发阶段开启// Yansongda日志配置 log [ file storage_path(logs/alipay.log), level debug ], // EasyWeChat日志配置 log [ level debug, file storage_path(logs/wechat.log), ]当遇到问题时首先检查这些日志文件。我习惯在支付关键节点加入自定义日志比如Log::debug(支付宝支付请求参数, $order); $response Pay::alipay()-web($order); Log::debug(支付宝支付响应, [response $response]);6.3 跨平台适配技巧在多端项目中一个常见的需求是根据不同平台返回不同的支付方式。我的做法是在控制器中做一个简单的路由分发public function pay(Request $request) { $platform $request-header(X-Platform, web); switch ($platform) { case app: return $this-appPay($request); case h5: return $this-h5Pay($request); default: return $this-webPay($request); } }对于微信内的支付还需要特别注意获取openid的问题。我通常会做一个中间件来处理微信授权public function handle($request, Closure $next) { if (isWechatBrowser() !$request-session()-has(wechat_openid)) { $app Factory::officialAccount(config(wechat.official_account)); $oauth $app-oauth; return $oauth-redirect(); } return $next($request); }7. 安全与性能优化7.1 支付安全实践支付环节的安全至关重要以下是我总结的几个关键点金额校验后端必须校验订单金额防止前端篡改$order Order::find($request-order_id); if ($order-amount ! $request-amount) { abort(400, 订单金额不匹配); }防重放攻击订单号必须唯一可以使用Redis做幂等控制$key pay_order: . $orderSn; if (Redis::exists($key)) { abort(400, 请勿重复支付); } Redis::setex($key, 3600, 1);敏感信息保护日志中不要记录完整的支付参数7.2 性能优化建议在高并发场景下支付系统需要特别注意性能异步处理支付成功后的业务逻辑尽量异步处理// 在回调处理中 dispatch(new ProcessPaidOrder($order)); return $alipay-success();数据库优化为order_sn字段添加索引提高查询速度缓存利用频繁访问的支付配置可以缓存起来$config Cache::remember(wechat_payment_config, 3600, function() { return config(wechat.payment.default); });连接池管理数据库和Redis连接使用连接池避免频繁创建连接8. 扩展与进阶用法8.1 退款功能实现两个扩展包都提供了完善的退款接口。以支付宝为例public function refund(Request $request) { $order Order::find($request-order_id); $result Pay::alipay()-refund([ out_trade_no $order-order_sn, refund_amount $order-amount, out_request_no time() ]); if ($result-code 10000) { $order-update([status refunded]); return response()-json([message 退款成功]); } return response()-json([message 退款失败], 400); }微信退款类似但需要特别注意证书的配置public function wechatRefund(Request $request) { $app Factory::payment(config(wechat.payment.default)); $result $app-refund-byOutTradeNumber($orderSn, $refundSn, $totalFee, $refundFee); if ($result[return_code] SUCCESS) { // 处理退款成功逻辑 } }8.2 订单查询与对账定期查询订单状态和对账是支付系统的重要功能// 支付宝订单查询 public function queryAlipayOrder($orderSn) { $result Pay::alipay()-find([ out_trade_no $orderSn ]); return $result-trade_status; } // 微信订单查询 public function queryWechatOrder($orderSn) { $app Factory::payment(config(wechat.payment.default)); $result $app-order-queryByOutTradeNumber($orderSn); return $result[trade_state]; }对于对账功能建议使用官方提供的对账单接口每天定时下载对账单进行比对// 下载支付宝对账单 public function downloadAlipayBill($date) { $result Pay::alipay()-download([ bill_type trade, bill_date $date ]); file_put_contents(storage_path(bills/alipay-{$date}.csv), $result); } // 下载微信对账单 public function downloadWechatBill($date) { $app Factory::payment(config(wechat.payment.default)); $stream $app-bill-get($date, ALL); file_put_contents(storage_path(bills/wechat-{$date}.csv), $stream); }在实际项目中我通常会把这些功能做成Artisan命令然后通过定时任务每天自动执行。