8.8 KiB
Mqpay — 示例支付插件(真实微信 / 支付宝)
通过监听 app/member/controller/Payment.php 暴露的 Payment* 事件 接入真实微信/支付宝支付,
核心控制器零改动,所有支付能力由本插件注入(基于 Yansongda Pay v3)。
前置依赖
composer require yansongda/pay # ^3.0
在 .env 中配置真实密钥(对应 config/pay.php):
# 微信支付
PAY_WECHAT_MCH_ID=你的商户号
PAY_WECHAT_APP_ID=你的AppID
PAY_WECHAT_SECRET_KEY=APIv3密钥
# PAY_WECHAT_CERT_CLIENT=/path/apiclient_cert.pem
# PAY_WECHAT_CERT_KEY=/path/apiclient_key.pem
# 支付宝
PAY_ALIPAY_APP_ID=你的AppID
PAY_ALIPAY_PUBLIC_KEY=支付宝公钥
PAY_ALIPAY_PRIVATE_KEY=应用私钥
未安装 SDK 或未配置密钥时,插件会优雅降级:
methods仍返回通道,create返回友好错误提示,不会致命报错。
监听的事件契约
| 事件 | 订阅方法 | 入参 | 返回值 |
|---|---|---|---|
PaymentMethods |
onPaymentMethods |
[] |
[['code'=>'wechat'|'alipay','name','desc','icon','sort']] |
PaymentOrderCreate |
onPaymentOrderCreate |
['order'=>[...]] |
无(落库 Mqpay_order) |
PaymentCreate |
onPaymentCreate |
['order'=>[...],'params'=>[...]] |
['type'=>'qrcode','qrcode_url','order_sn'] |
PaymentNotify |
onPaymentNotify |
['method','post','get'] |
'success' / 'fail'(网关要求) |
PaymentFreeActive |
onPaymentFreeActive |
['user','plan'] |
无 |
前端流程
- 用户选择套餐 → 弹窗列出
wechat/alipay两个通道; - 确认后
POST /user/payment/create→ 触发PaymentOrderCreate+PaymentCreate; - 插件调用 Yansongda 统一下单(扫码),前端用返回的
qrcode_url渲染二维码; - 前端每 2 秒轮询
POST /Mqpay/pay/query(带order_sn)检测支付结果; - 微信/支付宝异步通知
POST /Mqpay/pay/notify?method=xxx→ 插件验签、标记订单、触发UserMemberActivate事件,返回success。
会员开通:UserMemberActivate 监听器
支付成功后本插件仅完成订单激活(把 wxapp_mqpay_order.status 置 1),
真正的「会员权益写入」(更新用户组 / 会员有效期 / 资金流水 / 操作日志)通过触发
UserMemberActivate 事件交给业务层处理,保证插件与业务解耦。
监听器位置
-
实现:
app/member/listener/UserMemberActivate.phphandle($event)读取$event['uid']、$event['plan_id']、$event['order']、$event['method'];- 依据
config('member.plans')取套餐的duration/level/gid; - 在原未过期时间基础上顺延会员期限(避免重复购买覆盖);
- 写入 4 处:用户主表
vip_expire/vip_level/gid、用户组关联user_group_access、 资金流水user_bill、操作日志user_log。
-
注册:
app/event.php的listen数组(app/event.php:27-29):'UserMemberActivate' => [ app\member\listener\UserMemberActivate::class, ],注:该监听器注册在全局
app/event.php(非插件info.php),因此与具体支付插件解耦—— 任何触发UserMemberActivate事件的插件都能复用同一套会员开通逻辑。 (app/event.php注释里残留的paydemo字样是重命名前身,可忽略。)
触发点
本插件在两处触发该事件:
| 触发路径 | 调用链 |
|---|---|
| 安卓免签自动确认 | POST /mqpay/pay/notify-app → notifyApp() 验签 → 按 real_amount 精确匹配 → confirmOrder() |
| 后台人工确认 | 后台 Confirm 接口 → Pay.php → confirmOrder() |
confirmOrder() 触发代码示例(subscribe/Payment.php:326):
Event::trigger('UserMemberActivate', [
'uid' => $order['uid'],
'plan_id' => $order['plan_id'],
'order' => $order,
'method' => 'personal',
]);
支付钩子完整调用链路(时序图)
用户浏览器 app/member/controller/Payment addon/mqpay/subscribe/Payment 安卓监听App 会员监听器
│ │ │ │ │
│─ GET methods ───────▶│ │ │ │
│ │ Event::trigger('PaymentMethods') │ │
│ │────────────────────────────▶│ onPaymentMethods() │ │
│◀─ 方式列表 ──────────│◀─────────────────────────────│ │ │
│ │ │ │ │
│─ POST create(personal)▶│ │ │ │
│ │ 生成 order │ │ │
│ │ Event::trigger('PaymentOrderCreate') │ │
│ │────────────────────────────▶│ onPaymentOrderCreate() │ │
│ │ Event::trigger('PaymentCreate') │ │
│ │────────────────────────────▶│ onPaymentCreate() │ │
│ │ │ personalQrcode()+jitter │ │
│◀─ 收款码+精确金额 ───│◀─────────────────────────────│ │ │
│ │ │ ║ 用户扫码转账到账 ║ │ │
│ │ │ │ 监听通知栏 │
│ │ │ │─ POST /mqpay/pay/notify-app(amount)▶│
│ │ │ notifyApp() 验签 md5(key+amount+sn+key) │
│ │ │ 按 real_amount 精确匹配 │ │
│ │ │ confirmOrder(order_sn) │ │
│ │ │ status=1 │ │
│ │ │ Event::trigger('UserMemberActivate') │
│ │ │────────────────────────── 开通会员 ──────▶│
│◀─ 轮询/刷新看到已开通─│ │ │ │
网关支付(微信/支付宝)走
onPaymentNotify异步回调;免签(personal)无网关回调, 由notify-app(安卓)或后台Confirm触发confirmOrder(),殊途同归到UserMemberActivate。
钩子 / 事件速查表
| 事件名 | 订阅方法 | 订阅位置 | 触发位置(Payment.php) | 返回值 |
|---|---|---|---|---|
PaymentMethods |
onPaymentMethods |
subscribe/Payment.php |
methods() |
array 方式列表 |
PaymentOrderCreate |
onPaymentOrderCreate |
subscribe/Payment.php |
create() |
无(写库) |
PaymentCreate |
onPaymentCreate |
subscribe/Payment.php |
create() |
array/null 支付参数 |
PaymentNotify |
onPaymentNotify |
subscribe/Payment.php |
notify() |
'success'/'fail' |
PaymentFreeActive |
onPaymentFreeActive |
subscribe/Payment.php |
create() |
无 |
UserMemberActivate |
handle() |
app/member/listener/UserMemberActivate.php |
confirmOrder()(subscribe/Payment.php) |
无(写库/日志) |
注册方式对照
- 支付钩子(前 5 个):在
addon/mqpay/info.php的events.subscribe声明订阅器类, 框架AppService::loadAddonRelevant()启动时自动loadEvent注册。 - 会员开通(最后 1 个):在全局
app/event.php的listen中注册,与支付插件解耦。
相关文档
- 支付钩子调用机制与时序图 — 事件系统、订阅器注册、会员监听器链路详解
- 安卓到账监听 App 接入指南 — 免签监听 App 对接
notify-app接口说明