Files
YwxAppThink/addon/mqpay/docs/payment_hooks.md
T

109 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 支付钩子调用机制与时序图
本文档说明 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` 中注册,与支付插件解耦。