8.1 KiB
8.1 KiB
支付钩子调用机制与时序图
本文档说明 mqpay 支付插件内部基于 ThinkPHP 事件系统(Event)+ 订阅器(Subscriber) 的钩子调用方式,
以及与会员开通(UserMemberActivate)的解耦关系。完整插件说明见 ../README.md。
一、整体机制
支付钩子不是传统 Hook::add/Hook::listen,而是 ThinkPHP 的事件系统:
- 业务核心
app/member/controller/Payment.php只负责「触发事件」(Event::trigger); - 支付插件
addon/mqpay/subscribe/Payment.php通过「事件订阅器」监听并注入实现; - 两者解耦,框架在启动阶段按
info.php声明自动注册,无需手动绑定。
二、调用链路(5 步)
- 声明订阅器 —
addon/mqpay/info.php的events.subscribe:'events' => [ 'subscribe' => [ 'addon\\mqpay\\subscribe\\Payment', ], ], - 自动注册 —
ywxapp/service/AppService.php::loadAddonRelevant()遍历启用插件,调用app()->loadEvent($info['events']),把订阅器注册进事件系统。 - 订阅方法即钩子 —
subscribe/Payment.php中方法名约定on + 事件名:onPaymentMethods↔PaymentMethodsonPaymentOrderCreate↔PaymentOrderCreateonPaymentCreate↔PaymentCreateonPaymentNotify↔PaymentNotifyonPaymentFreeActive↔PaymentFreeActive
- 业务点火 —
app/member/controller/Payment.php:$results = Event::trigger('PaymentMethods', []); // Payment.php:77 Event::trigger('PaymentOrderCreate', ['order' => $order]); // Payment.php:153 $results = Event::trigger('PaymentCreate', [...]); // Payment.php:156 $results = Event::trigger('PaymentNotify', [...]); // Payment.php:180 Event::trigger('PaymentFreeActive', [...]); // Payment.php:134 - 返回值消费:
PaymentMethods→ 返回array,合并去重后返回前端(按sort排序)。PaymentCreate→ 返回array(如['type'=>'qrcode','qrcode_url'=>...])或null(不处理);create()取第一个非空作为支付参数。PaymentNotify→ 返回'success'/'fail',notify()直接作为网关响应体输出。PaymentOrderCreate/PaymentFreeActive→ 副作用型(写库/日志),返回值忽略。
三、会员开通:UserMemberActivate 监听器
支付成功后本插件仅完成订单激活(wxapp_mqpay_order.status=1),真正的「会员权益写入」
(用户组 / 会员有效期 / 资金流水 / 操作日志)通过触发 UserMemberActivate 事件交给业务层处理。
- 实现:
app/member/listener/UserMemberActivate.phphandle($event)读取uid/plan_id/order/method;- 按
config('member.plans')取套餐duration/level/gid; - 在原未过期时间上顺延期限(防重复购买覆盖);
- 写 4 处:用户主表
vip_expire/vip_level/gid、user_group_access、user_bill、user_log。
- 注册:全局
app/event.php的listen数组(非插件info.php)—— 与具体支付插件解耦。 - 触发:本插件
confirmOrder()(subscribe/Payment.php:326),两条路径殊途同归:- 安卓免签:
POST /mqpay/pay/notify-app→notifyApp()验签 → 按real_amount匹配 →confirmOrder() - 后台人工:后台
Confirm接口 →Pay.php→confirmOrder()
- 安卓免签:
四、完整时序图(以"个人免签"为例)
用户浏览器 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。
五、钩子 / 事件速查表
| 事件名 | 订阅方法 | 订阅位置 | 触发位置 | 返回值 |
|---|---|---|---|---|
PaymentMethods |
onPaymentMethods |
subscribe/Payment.php |
Payment.php::methods() |
array 方式列表 |
PaymentOrderCreate |
onPaymentOrderCreate |
subscribe/Payment.php |
Payment.php::create() |
无(写库) |
PaymentCreate |
onPaymentCreate |
subscribe/Payment.php |
Payment.php::create() |
array/null 支付参数 |
PaymentNotify |
onPaymentNotify |
subscribe/Payment.php |
Payment.php::notify() |
'success'/'fail' |
PaymentFreeActive |
onPaymentFreeActive |
subscribe/Payment.php |
Payment.php::create() |
无 |
UserMemberActivate |
handle() |
app/member/listener/UserMemberActivate.php |
confirmOrder() |
无(写库/日志) |
注册方式对照
- 支付钩子(前 5 个):
addon/mqpay/info.php的events.subscribe声明,框架启动时自动loadEvent注册。 - 会员开通(最后 1 个):全局
app/event.php的listen中注册,与支付插件解耦。