仰望星辰工作室

better-staridc-MNBT

better-staridc-MNBT/ app_plugins/epay/bootstrap.php 7.8 KB · 217 行 原始文件
Z zfhsh first commit 1 天前
1<?php
2/**
3 * 易支付插件主入口
4 *
5 * V1.81 P3:从核心支付代码迁移而来,提供彩虹易支付协议的支付宝/微信/QQ 三个子付款方式。
6 *
7 * 配置项(保存在 MN_plugin_option 表):
8 * - apiurl 易支付接口地址(如 https://pay.example.com/)
9 * - pid 商户 ID
10 * - key 商户密钥
11 *
12 * 自动迁移:首次启动时若检测到 MN_config.hxe/hxr/hxt 旧值且本插件未配置,则自动迁移。
13 */
14
15if (!defined('IN_CRONLITE')) {
16 exit;
17}
18
19require_once __DIR__ . '/lib/Epay_Core.php';
20
21mnbt_plugin_register('epay', [
22 'name' => '易支付',
23 'description' => '彩虹易支付协议,支持支付宝/微信/QQ 钱包',
24 'icon' => 'mdi-credit-card-multiple',
25]);
26
27// ============================================================
28// 自动迁移旧配置(一次性)
29// ============================================================
30function epay_maybe_migrate_legacy()
31{
32 $migrated = mnbt_plugin_option_get('epay', '_migrated', '0');
33 if ($migrated === '1' || $migrated === 1) {
34 return;
35 }
36 global $DB, $siteid;
37 $siteid = isset($siteid) ? $siteid : 1;
38 $row = $DB->get_row_prepare("SELECT hxe,hxr,hxt FROM MN_config WHERE id=? LIMIT 1", [$siteid]);
39 if (!$row) {
40 mnbt_plugin_option_set('epay', '_migrated', '1');
41 return;
42 }
43 $apiurl = trim((string)($row['hxe'] ?? ''));
44 $pid = trim((string)($row['hxr'] ?? ''));
45 $key = trim((string)($row['hxt'] ?? ''));
46 if ($apiurl !== '' || $pid !== '' || $key !== '') {
47 if ($apiurl !== '') mnbt_plugin_option_set('epay', 'apiurl', $apiurl);
48 if ($pid !== '') mnbt_plugin_option_set('epay', 'pid', $pid);
49 if ($key !== '') mnbt_plugin_option_set('epay', 'key', $key);
50 error_log('[MNBT epay] 已自动迁移旧易支付配置到插件选项');
51 }
52 mnbt_plugin_option_set('epay', '_migrated', '1');
53}
54epay_maybe_migrate_legacy();
55
56// ============================================================
57// 读取本插件配置的辅助函数
58// ============================================================
59function epay_get_config()
60{
61 return [
62 'apiurl' => trim((string)mnbt_plugin_option_get('epay', 'apiurl', '')),
63 'pid' => trim((string)mnbt_plugin_option_get('epay', 'pid', '')),
64 'key' => trim((string)mnbt_plugin_option_get('epay', 'key', '')),
65 ];
66}
67
68function epay_is_configured()
69{
70 $c = epay_get_config();
71 return $c['apiurl'] !== '' && $c['pid'] !== '' && $c['key'] !== '';
72}
73
74// ============================================================
75// 注册支付插件
76// ============================================================
77mnbt_register_payment('epay', [
78 'name' => '易支付',
79 'description' => '彩虹易支付协议(pid/key/apiurl)',
80 'icon' => 'mdi-credit-card-multiple',
81 'methods' => [
82 'alipay' => ['name' => '支付宝', 'icon' => 'mdi-alpha-a-circle'],
83 'wxpay' => ['name' => '微信支付', 'icon' => 'mdi-wechat'],
84 'qqpay' => ['name' => 'QQ 钱包', 'icon' => 'mdi-qqchat'],
85 ],
86 'build' => function ($method, $order, $plugin_config) {
87 if (!epay_is_configured()) {
88 error_log('[MNBT epay] 未配置 apiurl/pid/key,无法发起支付');
89 return false;
90 }
91 $c = epay_get_config();
92 $siteurl = isset($order['siteurl']) ? $order['siteurl'] : '';
93 // 站点根 URL 末尾保证带斜杠
94 $siteurl = rtrim((string)$siteurl, '/') . '/';
95 // 回调地址统一用 index.php?_r= 兼容路由:不依赖服务器伪静态重写。
96 // 部分环境(未配置 rewrite 规则)下 /pay/epay/notify 会直接返回 nginx 404,
97 // 导致易支付网关异步通知收不到、订单无法结算(支付成功但余额不入账)。
98 $notifyUrl = $siteurl . 'index.php?_r=/pay/epay/notify';
99 $returnUrl = $siteurl . 'index.php?_r=/pay/epay/return';
100
101 $params = [
102 'type' => $method, // alipay / wxpay / qqpay
103 'out_trade_no' => $order['out_trade_no'],
104 'notify_url' => $notifyUrl,
105 'return_url' => $returnUrl,
106 'name' => $order['name'],
107 'money' => $order['money'],
108 ];
109 return Epay_Core::buildForm($c['apiurl'], $c['pid'], $c['key'], $params);
110 },
111]);
112
113// ============================================================
114// 注册回调路由:/pay/epay/notify(异步通知)
115// 注意:路由方式必须为 *(GET+POST 都接收)——
116// 部分易支付变体(如 pay1987)的自动通知/后台补发使用 GET 携带参数,
117// 只注册 POST 会导致通知命中不了路由、订单无法结算。
118// ============================================================
119mnbt_register_route('*', '/pay/epay/notify', function ($params, $ctx) {
120 @header('Content-Type: text/plain; charset=UTF-8');
121 if (!epay_is_configured()) {
122 echo 'fail';
123 return;
124 }
125 $c = epay_get_config();
126 // 兼容 POST / GET 两种通知方式
127 $data = $_POST;
128 if (empty($data)) {
129 $data = $_GET;
130 // 路由参数 _r 是我们拼接的,不参与网关签名,验签前必须剔除
131 unset($data['_r']);
132 }
133 if (empty($data) || empty($data['sign'])) {
134 mnbt_pay_log('易支付异步通知无数据或缺少 sign', '回调异常', $data['out_trade_no'] ?? '');
135 echo 'fail';
136 return;
137 }
138 $sign = (string)$data['sign'];
139 if (!Epay_Core::verifySign($data, $c['key'], $sign)) {
140 mnbt_pay_log('易支付异步通知验签失败', '验签失败', $data['out_trade_no'] ?? '');
141 echo 'fail';
142 return;
143 }
144 $out_trade_no = isset($data['out_trade_no']) ? (string)$data['out_trade_no'] : '';
145 $trade_status = isset($data['trade_status']) ? (string)$data['trade_status'] : '';
146 $money = isset($data['money']) ? (string)$data['money'] : '';
147 if ($out_trade_no === '') {
148 echo 'fail';
149 return;
150 }
151 $result = mnbt_pay_settle_order($out_trade_no, $trade_status, $money);
152 if (!empty($result['ok'])) {
153 echo 'success';
154 } else {
155 echo 'fail';
156 }
157}, 10, null, true);
158
159// ============================================================
160// 注册回调路由:/pay/epay/return(同步返回)
161// ============================================================
162mnbt_register_route('GET', '/pay/epay/return', function ($params, $ctx) {
163 if (!epay_is_configured()) {
164 @header('Location: ./');
165 return;
166 }
167 $c = epay_get_config();
168 $data = $_GET;
169 // 剔除路由参数 _r(不参与签名)
170 unset($data['_r']);
171 $ok = false;
172 if (!empty($data) && !empty($data['sign'])) {
173 $ok = Epay_Core::verifySign($data, $c['key'], (string)$data['sign']);
174 }
175 if (!$ok) {
176 mnbt_pay_log('易支付同步返回验签失败', '验签失败', $data['out_trade_no'] ?? '');
177 }
178 // 同步返回仅作展示,订单最终状态以异步通知为准
179 $base = isset($ctx['base']) ? $ctx['base'] : '';
180 @header('Location: ' . $base . '/user');
181});
182
183// ============================================================
184// 后台设置页
185// ============================================================
186mnbt_register_page('admin', 'settings', 'admin/settings.php', '易支付设置');
187
188mnbt_register_settings_tab([
189 'title' => '易支付设置',
190 'page' => 'settings',
191 'order' => 10,
192]);
193
194mnbt_register_menu('admin', [
195 'title' => '易支付设置',
196 'page' => 'settings',
197 'icon' => 'mdi-credit-card-multiple',
198 'order' => 60,
199 'multitabs' => true,
200]);
201
202// ============================================================
203// AJAX:保存配置
204// ============================================================
205mnbt_register_ajax('admin', 'epay_save', function () {
206 mnbt_plugin_require_admin();
207 $apiurl = isset($_POST['apiurl']) ? trim((string)$_POST['apiurl']) : '';
208 $pid = isset($_POST['pid']) ? trim((string)$_POST['pid']) : '';
209 $key = isset($_POST['key']) ? trim((string)$_POST['key']) : '';
210 if (mb_strlen($apiurl) > 500 || mb_strlen($pid) > 60 || mb_strlen($key) > 200) {
211 json_exit('参数过长');
212 }
213 mnbt_plugin_option_set('epay', 'apiurl', $apiurl);
214 mnbt_plugin_option_set('epay', 'pid', $pid);
215 mnbt_plugin_option_set('epay', 'key', $key);
216 json_exit('保存成功');
217});