better-staridc-MNBT
1---
2title: 支付插件系统
3description: MNBT 支付插件系统(V1.81 P3):概念数据流、关键约定、注册支付插件、订单上下文、回调结算、公共函数、设置页与完整示例
4---
5
6# 支付插件系统(V1.81 P3)
7
8本文是 [插件开发手册](./guide.md) §3.12 的拆分章节。把"发起支付 + 异步回调 + 订单结算"做成插件化架构。系统提供注册 API、订单结算函数、统一支付设置页;插件只负责构造网关请求与验签。
9
10## 3.12.1 概念与数据流
11
12```
13客户端 系统层 支付插件
14 │ │ │
15 │ POST /user/pay.php │ │
16 │ type=epay__alipay │ │
17 │ ───────────────────────>│ │
18 │ │ mnbt_pay_dispatch_gateway │
19 │ │ ──────────────────────────>│
20 │ │ │ build(method, order, cfg)
21 │ │ <──────────────────────────│ HTML 表单 / 扫码页
22 │ <───────────────────────│ │
23 │ │ │
24 │ 跳转第三方网关 → 支付 │
25 │ │ │
26 │ 异步回调 /pay/{slug}/notify │
27 │ ───────────────────────>│ mnbt_register_route │
28 │ │ ──────────────────────────>│ 验签
29 │ │ │ mnbt_pay_settle_order()
30 │ │ │ echo 'success'
31```
32
33## 3.12.2 关键约定
34
35| 项目 | 约定 |
36|------|------|
37| **支付方式 type** | 格式 `{plugin_id}__{method_id}`,如 `epay__alipay`、`alipay_official__pc` |
38| **已启用付款方式** | 存 `MN_config.pay_methods` 字段(JSON 数组),由 `admin/pay_settings.php` 维护 |
39| **插件 API 凭证** | 存 `MN_plugin_option` 表,由插件自身的设置页维护 |
40| **回调路径** | 推荐 `/pay/{slug}/notify`(异步)+ `/pay/{slug}/return`(同步),用 `mnbt_register_route` 注册 |
41| **订单结算** | 统一调 `mnbt_pay_settle_order($out_trade_no, $trade_status, $money)` |
42
43## 3.12.3 注册支付插件
44
45```php
46mnbt_register_payment('my_pay', [
47 'name' => '我的支付',
48 'description' => '一句话说明',
49 'icon' => 'mdi-credit-card',
50 'methods' => [
51 'alipay' => ['name' => '支付宝', 'icon' => 'mdi-alpha-a-circle'],
52 'wxpay' => ['name' => '微信', 'icon' => 'mdi-wechat'],
53 ],
54 'build' => function ($method, $order, $plugin_config) {
55 // $method: 'alipay' / 'wxpay'(来自 methods 的 key)
56 // $order: ['out_trade_no'=>..., 'name'=>..., 'money'=>..., 'type'=>..., 'siteurl'=>..., 'pay_lx'=>...]
57 // $plugin_config: 该插件所有 option(来自 MN_plugin_option)
58 // 返回 HTML 字符串(通常是自动提交的表单),或 false 表示不接管
59
60 $cfg = $plugin_config; // 读取 apiurl/key 等
61 // 构造表单...
62 return '<form>...</form>';
63 },
64]);
65```
66
67## 3.12.4 订单上下文 `$order`
68
69`user/pay.php` 在创建 `MN_dd` 订单记录后,构造以下数组传给 `mnbt_pay_dispatch_gateway`:
70
71| 字段 | 说明 |
72|------|------|
73| `out_trade_no` | 商户订单号(`MN_dd.ddh`,全局唯一) |
74| `name` | 订单标题(展示用) |
75| `money` | 金额(元,字符串) |
76| `type` | 支付方式 type,如 `epay__alipay` |
77| `siteurl` | 站点根 URL(带协议 + 末尾斜杠) |
78| `pay_lx` | 业务类型:`yjbs` 一键部署(核心结算内置);其他业务(如 `ymgm` 域名购买)由对应插件在 `order.paid` 钩子内自行处理 |
79
80插件用 `siteurl` 拼接 `notify_url` / `return_url`,例如:
81```php
82$notifyUrl = rtrim($order['siteurl'], '/') . '/pay/my_pay/notify';
83```
84
85## 3.12.5 回调路由与订单结算
86
87异步通知路由示例:
88
89```php
90mnbt_register_route('*', '/pay/my_pay/notify', function ($params, $ctx) {
91 @header('Content-Type: text/plain; charset=UTF-8');
92 $cfg = mnbt_plugin_option_all('my_pay');
93 // 1. 从 $_POST / $_GET 取回调数据
94 // 2. 用 $cfg 中的 key 验签
95 if (!验签通过) {
96 mnbt_pay_log('验签失败', '验签失败', $_POST['out_trade_no'] ?? '');
97 echo 'fail';
98 return;
99 }
100 // 3. 调用统一结算函数(处理 yjbs 业务、标记订单完成、触发 order.paid;
101 // 其他业务类型如 ymgm 由对应插件在 order.paid 钩子内自行处理)
102 $result = mnbt_pay_settle_order(
103 $_POST['out_trade_no'],
104 $_POST['trade_status'], // 支付宝系: TRADE_SUCCESS
105 $_POST['money']
106 );
107 echo !empty($result['ok']) ? 'success' : 'fail';
108});
109```
110
111同步返回路由示例(仅展示,不做业务处理):
112
113```php
114mnbt_register_route('*', '/pay/my_pay/return', function ($params, $ctx) {
115 $base = $ctx['base'] ?? '';
116 @header('Location: ' . $base . '/user');
117});
118```
119
120## 3.12.6 公共函数
121
122| 函数 | 说明 |
123|------|------|
124| `mnbt_register_payment($slug, $config)` | 注册支付插件 |
125| `mnbt_get_payment_plugins()` | 获取所有已注册的支付插件(用于支付设置页) |
126| `mnbt_pay_type($slug, $method)` | 构造 type 字符串 |
127| `mnbt_pay_parse_type($type)` | 解析 type,返回 `['plugin'=>..., 'method'=>...]` 或 false |
128| `mnbt_get_enabled_payment_methods()` | 读取已启用的付款方式(按 sort 排序) |
129| `mnbt_save_payment_methods($list)` | 保存付款方式列表 |
130| `mnbt_pay_dispatch_gateway($type, $order)` | 内部分发:根据 type 调用对应插件的 build 回调 |
131| `mnbt_pay_settle_order($no, $status, $money)` | **插件可调**:处理支付成功的订单,返回 `['ok'=>bool,'msg'=>string]` |
132| `mnbt_pay_log($content, $status, $orderNo)` | 记录支付日志到 `MN_log` |
133
134## 3.12.7 后台支付设置页
135
136系统内置 `admin/pay_settings.php`,自动列出所有已注册支付插件及其子方式,管理员可:
137
138- 勾选启用的子付款方式
139- 设置客户端显示名(默认取 `methods[m]['name']`)
140- 设置图标 class(默认取 `methods[m]['icon']`)
141- 设置排序(数字越小越靠前)
142
143**插件的 API 凭证不在支付设置页配置**,而是在插件自身的设置页(通过 `mnbt_register_page('admin', 'settings', ...)` 注册)。支付设置页会自动显示"插件设置"按钮跳转。
144
145## 3.12.8 客户端模板适配
146
147客户端支付方式选择已改为动态渲染:
148
149```php
150<?php $__methods = function_exists('mnbt_get_enabled_payment_methods')
151 ? mnbt_get_enabled_payment_methods() : []; ?>
152<?php foreach ($__methods as $__idx => $__m): ?>
153<?php $__type = $__m['plugin'] . '__' . $__m['method']; ?>
154<label class="lyear-radio radio-inline radio-primary col">
155 <input type="radio" name="type" value="<?=htmlspecialchars($__type)?>" <?=$__idx===0?'checked':''?>>
156 <i class="mdi <?=htmlspecialchars($__m['icon'] ?? 'mdi-payment')?>"></i>
157 <span><?=htmlspecialchars($__m['display_name'])?></span>
158</label>
159<?php endforeach; ?>
160```
161
162## 3.12.9 完整示例
163
164参考 `app_plugins/epay/`(易支付协议)与 `app_plugins/alipay_official/`(支付宝官方 API)。
165
166- **epay**:3 个子方式(alipay/wxpay/qqpay),MD5 签名,自动迁移旧 `MN_config.hxe/hxr/hxt` 配置
167- **alipay_official**:2 个子方式(pc 电脑网站支付 / qrcode 当面付),RSA2 签名,基于 dedemao/alipay SDK
168
169更多细节见内置插件文档:[epay](./builtin/epay.md)、[alipay_official](./builtin/alipay-official.md)。