109 lines
8.1 KiB
Markdown
109 lines
8.1 KiB
Markdown
# 支付钩子调用机制与时序图
|
||
|
||
本文档说明 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 步)
|
||
|
||
1. **声明订阅器** — `addon/mqpay/info.php` 的 `events.subscribe`:
|
||
```php
|
||
'events' => [
|
||
'subscribe' => [
|
||
'addon\\mqpay\\subscribe\\Payment',
|
||
],
|
||
],
|
||
```
|
||
2. **自动注册** — `ywxapp/service/AppService.php::loadAddonRelevant()`
|
||
遍历启用插件,调用 `app()->loadEvent($info['events'])`,把订阅器注册进事件系统。
|
||
3. **订阅方法即钩子** — `subscribe/Payment.php` 中方法名约定 `on + 事件名`:
|
||
- `onPaymentMethods` ↔ `PaymentMethods`
|
||
- `onPaymentOrderCreate` ↔ `PaymentOrderCreate`
|
||
- `onPaymentCreate` ↔ `PaymentCreate`
|
||
- `onPaymentNotify` ↔ `PaymentNotify`
|
||
- `onPaymentFreeActive` ↔ `PaymentFreeActive`
|
||
4. **业务点火** — `app/member/controller/Payment.php`:
|
||
```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
|
||
```
|
||
5. **返回值消费**:
|
||
- `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.php`
|
||
- `handle($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` 中注册,与支付插件解耦。
|