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

8.1 KiB
Raw Blame History

支付钩子调用机制与时序图

本文档说明 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.phpevents.subscribe
    'events' => [
        'subscribe' => [
            'addon\\mqpay\\subscribe\\Payment',
        ],
    ],
    
  2. 自动注册ywxapp/service/AppService.php::loadAddonRelevant() 遍历启用插件,调用 app()->loadEvent($info['events']),把订阅器注册进事件系统。
  3. 订阅方法即钩子subscribe/Payment.php 中方法名约定 on + 事件名
    • onPaymentMethodsPaymentMethods
    • onPaymentOrderCreatePaymentOrderCreate
    • onPaymentCreatePaymentCreate
    • onPaymentNotifyPaymentNotify
    • onPaymentFreeActivePaymentFreeActive
  4. 业务点火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
    
  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/giduser_group_accessuser_billuser_log
  • 注册:全局 app/event.phplisten 数组(插件 info.php)—— 与具体支付插件解耦。
  • 触发:本插件 confirmOrder()subscribe/Payment.php:326),两条路径殊途同归:
    • 安卓免签:POST /mqpay/pay/notify-appnotifyApp() 验签 → 按 real_amount 匹配 → confirmOrder()
    • 后台人工:后台 Confirm 接口 → Pay.phpconfirmOrder()

四、完整时序图(以"个人免签"为例)

用户浏览器        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.phpevents.subscribe 声明,框架启动时自动 loadEvent 注册。
  • 会员开通(最后 1 个):全局 app/event.phplisten 中注册,与支付插件解耦。