better-staridc-MNBT
1<?php
2/**
3 * qmzl_domain 插件入口
4 *
5 * 对接启明智联平台域名注册 API(https://cloud.qimingidc.cn/console/v1):
6 * - 前置依赖 user_info 插件,用户维度 = user_info 用户(MN_plugin_user)
7 *
8 * 两种运营模式(后台设置切换):
9 * 1. client 客户自注册:每个客户绑定自己的启明智联账号,查询/下单/支付均在客户账号内完成;
10 * 用户端路径:/qmzl
11 * 2. agent 代理商模式:管理员配置代理商账号,客户下单走 MNBT 内置支付系统
12 * (MN_dd + 支付插件),支付成功后插件自动用代理商账号余额代注册;
13 * 用户端路径:/qmzl_domain
14 */
15if (!defined('IN_CRONLITE')) exit;
16
17require_once __DIR__ . '/lib/api.php';
18require_once __DIR__ . '/lib/helper.php';
19
20// 表结构自动升级(兼容已安装站点)
21qmzl_schema_upgrade();
22
23mnbt_plugin_register('qmzl_domain', ['name' => '启明智联域名注册']);
24
25/* ============================================================
26 * 1. 后台菜单
27 * ============================================================ */
28mnbt_register_menu('admin', [
29 'title' => '启明智联域名',
30 'icon' => 'mdi-web',
31 'order' => 35,
32 'children' => [
33 [
34 'title' => '插件设置',
35 'page' => 'settings',
36 'icon' => 'mdi-web',
37 'order' => 10,
38 'multitabs' => true,
39 ],
40 [
41 'title' => '订单记录',
42 'page' => 'orders',
43 'icon' => 'mdi-receipt',
44 'order' => 20,
45 'multitabs' => true,
46 ],
47 [
48 'title' => '用户云账号',
49 'page' => 'accounts',
50 'icon' => 'mdi-account-key',
51 'order' => 30,
52 'multitabs' => true,
53 ],
54 ],
55]);
56
57mnbt_register_page('admin', 'settings', 'admin/settings.php', '启明智联域名设置');
58mnbt_register_page('admin', 'orders', 'admin/orders.php', '域名订单记录');
59mnbt_register_page('admin', 'accounts', 'admin/accounts.php', '用户云账号');
60
61// 插件管理页快捷入口
62mnbt_register_settings_tab([
63 'title' => '启明智联域名',
64 'page' => 'settings',
65 'order' => 30,
66]);
67
68/* ============================================================
69 * 2. 后台 AJAX
70 * ============================================================ */
71
72/** 保存插件设置(开关 + 模式 + 后缀溢价) */
73mnbt_register_ajax('admin', 'p_qmzl_setting_save', function () {
74 mnbt_plugin_require_admin();
75 $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : true;
76 $mode = $_POST['mode'] ?? 'client';
77 $mode = ($mode === 'agent') ? 'agent' : 'client';
78 qmzl_setting_set('enabled', $enabled ? 'true' : 'false');
79 qmzl_setting_set('mode', $mode);
80
81 // 后缀溢价表 {"suffix": "溢价金额"}
82 $markup = [];
83 $raw = (string)($_POST['markup'] ?? '');
84 $arr = json_decode($raw, true);
85 if (is_array($arr)) {
86 foreach ($arr as $suffix => $val) {
87 $suffix = trim((string)$suffix);
88 $f = round((float)$val, 2);
89 if ($suffix !== '' && $f > 0) {
90 $markup[$suffix] = (string)$f;
91 }
92 }
93 }
94 qmzl_setting_set('agent_markup', json_encode($markup, JSON_UNESCAPED_SLASHES));
95 json_exit_success('已保存');
96});
97
98/** 保存并验证代理商开放接口凭证(平台账号 + API 密钥) */
99mnbt_register_ajax('admin', 'p_qmzl_agent_test', function () {
100 mnbt_plugin_require_admin();
101 $username = trim((string)($_POST['username'] ?? ''));
102 $apiToken = trim((string)($_POST['api_token'] ?? ''));
103 if ($username === '' || $apiToken === '') {
104 json_exit_error('请填写平台账号和 API 密钥');
105 }
106 $login = qmzl_agent_save($username, $apiToken);
107 if (!$login['ok']) {
108 json_exit_error($login['msg']);
109 }
110 json_exit_success('验证成功,代理商凭证已保存', ['exp' => $login['data']['exp']]);
111});
112
113/** 后台:用户云账号列表 */
114mnbt_register_ajax('admin', 'p_qmzl_admin_accounts', function () {
115 mnbt_plugin_require_admin();
116 $page = max(1, (int)($_POST['page'] ?? 1));
117 $limit = min(1000, max(1, (int)($_POST['limit'] ?? 200)));
118 $keyword = trim((string)($_POST['keyword'] ?? ''));
119 $data = qmzl_account_list($page, $limit, $keyword);
120 json_exit_success('ok', $data);
121});
122
123/** 后台:解绑某用户云账号 */
124mnbt_register_ajax('admin', 'p_qmzl_admin_account_unbind', function () {
125 mnbt_plugin_require_admin();
126 $user_id = (int)($_POST['user_id'] ?? 0);
127 if ($user_id <= 0) json_exit_error('参数错误');
128 if (!qmzl_account_delete($user_id)) json_exit_error('解绑失败');
129 json_exit_success('已解绑');
130});
131
132/** 后台:订单列表 */
133mnbt_register_ajax('admin', 'p_qmzl_admin_orders', function () {
134 mnbt_plugin_require_admin();
135 $page = max(1, (int)($_POST['page'] ?? 1));
136 $limit = min(200, max(1, (int)($_POST['limit'] ?? 20)));
137 $status = trim((string)($_POST['status'] ?? ''));
138 $keyword = trim((string)($_POST['keyword'] ?? ''));
139 $data = qmzl_order_admin_list($page, $limit, $status, $keyword);
140 json_exit_success('ok', $data);
141});
142
143/** 后台:重试失败的代理商注册订单 */
144mnbt_register_ajax('admin', 'p_qmzl_admin_order_retry', function () {
145 mnbt_plugin_require_admin();
146 $id = (int)($_POST['id'] ?? 0);
147 $order = qmzl_order_get_by_id($id);
148 if (!$order) json_exit_error('订单不存在');
149 if (qmzl_mode() !== 'agent') json_exit_error('仅代理商模式支持重试注册');
150 $r = qmzl_agent_register_domain($id);
151 if (!$r['ok']) json_exit_error($r['msg']);
152 json_exit_success($r['msg']);
153});
154
155/* ============================================================
156 * 3. 钩子:代理商模式支付成功后自动注册
157 * ============================================================ */
158mnbt_add_action('order.paid', function ($order_row, $ctx = []) {
159 if (!is_array($order_row)) return;
160 if (qmzl_mode() !== 'agent') return;
161 if (($order_row['lx'] ?? '') !== 'qmzl_domain') return;
162 $ddh = (string)($order_row['ddh'] ?? '');
163 if ($ddh === '') return;
164 $local = qmzl_order_get_by_ddh($ddh);
165 if (!$local) return;
166 if (($local['status'] ?? '') === 'Paid') return; // 已注册
167 $r = qmzl_agent_register_domain((int)$local['id']);
168 if (function_exists('mnbt_log')) {
169 mnbt_log('系统', '插件-qmzl_domain', '代理商注册域名 ' . $local['domain'] . ':' . ($r['msg'] ?? ''), $r['ok'] ? '成功' : '失败', $GLOBALS['DB']);
170 }
171}, 10);
172
173/* ============================================================
174 * 4. 用户端(受后台「用户端功能开关」控制,依赖 user_info)
175 * ============================================================ */
176if (qmzl_setting_get('enabled', 'true') === 'true') {
177
178$qzRoute = qmzl_route_prefix(); // client → qmzl / agent → qmzl_domain
179
180/* ---------- 4.1 页面路由 ---------- */
181
182// 域名注册(查询/价格/下单)
183mnbt_register_route('GET', '/' . $qzRoute, function ($params, $ctx) {
184 $user = qmzl_require_user();
185 qmzl_render('index', ['current_user' => $user, 'mode' => qmzl_mode()]);
186});
187
188// 信息模板
189mnbt_register_route('GET', '/' . $qzRoute . '/templates', function ($params, $ctx) {
190 $user = qmzl_require_user();
191 qmzl_render('templates', ['current_user' => $user, 'mode' => qmzl_mode()]);
192});
193
194// 我的域名
195mnbt_register_route('GET', '/' . $qzRoute . '/domains', function ($params, $ctx) {
196 $user = qmzl_require_user();
197 qmzl_render('domains', ['current_user' => $user, 'mode' => qmzl_mode()]);
198});
199
200// 云账号绑定(仅 client 模式)
201if (qmzl_mode() === 'client') {
202 mnbt_register_route('GET', '/' . $qzRoute . '/account', function ($params, $ctx) {
203 $user = qmzl_require_user();
204 qmzl_render('account', ['current_user' => $user, 'mode' => 'client']);
205 });
206
207 // 支付页(?order=上游订单号,仅 client 模式)
208 mnbt_register_route('GET', '/' . $qzRoute . '/pay', function ($params, $ctx) {
209 $user = qmzl_require_user();
210 $orderId = isset($_GET['order']) ? (int)$_GET['order'] : 0;
211 $order = $orderId > 0 ? qmzl_order_get($orderId) : false;
212 $valid = $order && (int)$order['user_id'] === (int)$user['id'];
213 qmzl_render('pay', [
214 'current_user' => $user,
215 'order' => $order ?: null,
216 'order_id' => $orderId,
217 'order_valid' => $valid,
218 'mode' => 'client',
219 ]);
220 });
221}
222
223/* ---------- 4.2 用户端 API 路由 ---------- */
224
225/** 当前绑定信息 / 模式 */
226mnbt_register_route('POST', '/' . $qzRoute . '/api/account_info', function ($params, $ctx) {
227 $user = qmzl_require_user(true);
228 if (qmzl_mode() === 'agent') {
229 json_exit_success('ok', ['bound' => true, 'mode' => 'agent']);
230 }
231 $row = qmzl_account_get((int)$user['id']);
232 if (!$row) {
233 json_exit_success('未绑定', ['bound' => false, 'mode' => 'client']);
234 }
235 $account = (string)$row['account'];
236 $masked = strlen($account) > 4 ? mb_substr($account, 0, 2) . '***' . mb_substr($account, -2) : '***';
237 json_exit_success('ok', [
238 'bound' => true,
239 'mode' => 'client',
240 'account' => $account,
241 'masked' => $masked,
242 'status' => $row['status'],
243 'last_msg' => (string)$row['last_msg'],
244 'updated_at' => (string)$row['updated_at'],
245 ]);
246});
247
248/** 获取人机验证码图片(代理上游 QmCaptcha) */
249mnbt_register_route('POST', '/' . $qzRoute . '/api/captcha_refresh', function ($params, $ctx) {
250 qmzl_require_user(true);
251 $res = qmzl_captcha_refresh();
252 if (!$res['ok']) json_exit_error($res['msg'] ?: '获取验证码失败');
253 json_exit_success('ok', $res['data']);
254});
255
256/** 校验人机验证码(代理上游) */
257mnbt_register_route('POST', '/' . $qzRoute . '/api/captcha_verify', function ($params, $ctx) {
258 qmzl_require_user(true);
259 $captcha = trim((string)($_POST['captcha'] ?? ''));
260 $token = trim((string)($_POST['token'] ?? ''));
261 if ($captcha === '' || $token === '') json_exit_error('参数错误');
262 $res = qmzl_captcha_verify($captcha, $token);
263 if (!$res['ok']) json_exit_error($res['msg'] ?: '验证失败');
264 json_exit_success('验证通过');
265});
266
267/** 绑定/更新云账号并测试登录(仅 client 模式,需先过人机验证) */
268mnbt_register_route('POST', '/' . $qzRoute . '/api/save_account', function ($params, $ctx) {
269 $user = qmzl_require_user(true);
270 $userId = (int)$user['id'];
271 if (qmzl_mode() !== 'client') json_exit_error('代理商模式下无需绑定账号');
272 $account = trim((string)($_POST['account'] ?? ''));
273 $password = (string)($_POST['password'] ?? '');
274 $captcha = trim((string)($_POST['captcha'] ?? ''));
275 $captchaToken = trim((string)($_POST['captcha_token'] ?? ''));
276
277 if ($account === '' || $password === '') {
278 json_exit_error('账号和密码不能为空');
279 }
280 if (mb_strlen($account) > 200) {
281 json_exit_error('账号过长');
282 }
283 if (strlen($password) > 100) {
284 json_exit_error('密码过长');
285 }
286 if ($captcha === '' || $captchaToken === '') {
287 json_exit_error('请先完成人机验证');
288 }
289
290 // 先登录测试(密码经 AES 加密,携带人机验证结果)
291 $login = qmzl_login($account, $password, $captcha, $captchaToken);
292 if (!$login['ok']) {
293 json_exit_error('登录失败:' . $login['msg']);
294 }
295
296 if (!qmzl_account_save_cred($userId, (string)$user['username'], $account, $password)) {
297 json_exit_error('保存失败,请重试');
298 }
299 qmzl_account_set_token($userId, $login['data']['jwt'], $login['data']['exp']);
300 qmzl_account_update_status($userId, 'ok', '');
301 json_exit_success('绑定成功', ['exp' => $login['data']['exp']]);
302});
303
304/** 解绑(仅 client 模式) */
305mnbt_register_route('POST', '/' . $qzRoute . '/api/unbind', function ($params, $ctx) {
306 $user = qmzl_require_user(true);
307 if (qmzl_mode() !== 'client') json_exit_error('代理商模式下无需绑定账号');
308 if (!qmzl_account_delete((int)$user['id'])) json_exit_error('解绑失败');
309 json_exit_success('已解绑');
310});
311
312/** 域名配置(后缀/协议) */
313mnbt_register_route('POST', '/' . $qzRoute . '/api/config', function ($params, $ctx) {
314 $user = qmzl_require_user(true);
315 $tok = qmzl_require_token($user);
316 if (!$tok['ok']) json_exit_error($tok['msg']);
317
318 $config = qmzl_config($tok['data']['jwt']);
319 if (!$config['ok']) json_exit_error($config['msg'] ?: '获取配置失败');
320
321 // 兜底后缀列表
322 $suffixes = [];
323 $d = $config['data'] ?: [];
324 if (!empty($d['specify_search_domain']) && is_array($d['specify_search_domain'])) {
325 foreach ($d['specify_search_domain'] as $s) {
326 $suffixes[] = ['suffix' => (string)$s];
327 }
328 }
329 if (empty($suffixes)) {
330 $sp = qmzl_suffixes($tok['data']['jwt']);
331 if ($sp['ok'] && !empty($sp['data']) && is_array($sp['data'])) {
332 $suffixes = $sp['data'];
333 }
334 }
335 if (empty($suffixes)) {
336 $suffixes = [['suffix' => '.com'], ['suffix' => '.cn'], ['suffix' => '.net']];
337 }
338 json_exit_success('ok', [
339 'config' => $d,
340 'suffixes' => $suffixes,
341 ]);
342});
343
344/** 域名可用性查询 */
345mnbt_register_route('POST', '/' . $qzRoute . '/api/check_domain', function ($params, $ctx) {
346 $user = qmzl_require_user(true);
347 $domain = trim((string)($_POST['domain'] ?? ''));
348 $suffix = trim((string)($_POST['suffix'] ?? '.com'));
349
350 if (!preg_match('/^[a-z0-9]([a-z0-9\-]{0,62}[a-z0-9])?$/i', $domain)) {
351 json_exit_error('域名前缀格式不正确');
352 }
353 if (!preg_match('/^\.[a-z0-9\-.]+$/i', $suffix)) {
354 json_exit_error('后缀格式不正确');
355 }
356
357 $tok = qmzl_require_token($user);
358 if (!$tok['ok']) json_exit_error($tok['msg']);
359
360 $res = qmzl_check($tok['data']['jwt'], strtolower($domain), strtolower($suffix));
361 if (!$res['ok']) json_exit_error($res['msg'] ?: '查询失败');
362 $list = $res['data'];
363 if (isset($list['list'])) $list = $list['list'];
364 if (!is_array($list)) $list = [];
365 json_exit_success('ok', ['list' => $list]);
366});
367
368/** 域名价格(agent 模式自动加上该后缀溢价) */
369mnbt_register_route('POST', '/' . $qzRoute . '/api/price', function ($params, $ctx) {
370 $user = qmzl_require_user(true);
371 $name = trim((string)($_POST['name'] ?? ''));
372 if (!preg_match('/^[a-z0-9]([a-z0-9\-]*[a-z0-9])?\.[a-z0-9\-.]+$/i', $name)) {
373 json_exit_error('域名格式不正确');
374 }
375 $tok = qmzl_require_token($user);
376 if (!$tok['ok']) json_exit_error($tok['msg']);
377 $res = qmzl_price($tok['data']['jwt'], strtolower($name));
378 if (!$res['ok']) json_exit_error($res['msg'] ?: '获取价格失败');
379 $list = $res['data'];
380 if (isset($list['list'])) $list = $list['list'];
381 if (!is_array($list)) $list = [];
382 $markup = qmzl_apply_markup($list, strtolower($name));
383 json_exit_success('ok', ['list' => $list, 'markup' => $markup]);
384});
385
386/** 支付方式:agent 模式用 MNBT 内置支付,client 模式用上游网关 */
387mnbt_register_route('POST', '/' . $qzRoute . '/api/gateways', function ($params, $ctx) {
388 $user = qmzl_require_user(true);
389 if (qmzl_mode() === 'agent') {
390 $methods = function_exists('mnbt_get_enabled_payment_methods') ? mnbt_get_enabled_payment_methods() : [];
391 $list = [];
392 foreach ($methods as $m) {
393 $list[] = [
394 'name' => (string)($m['plugin'] ?? '') . '__' . (string)($m['method'] ?? ''),
395 'title' => (string)($m['display_name'] ?? ($m['method'] ?? '')),
396 ];
397 }
398 json_exit_success('ok', ['list' => $list]);
399 }
400 $res = qmzl_gateways();
401 if (!$res['ok']) json_exit_error($res['msg'] ?: '获取支付方式失败');
402 $list = is_array($res['data']['list'] ?? null) ? $res['data']['list'] : [];
403 json_exit_success('ok', ['list' => $list]);
404});
405
406/** 信息模板列表(agent 模式仅显示当前用户创建的模板) */
407mnbt_register_route('POST', '/' . $qzRoute . '/api/templates', function ($params, $ctx) {
408 $user = qmzl_require_user(true);
409 $tok = qmzl_require_token($user);
410 if (!$tok['ok']) json_exit_error($tok['msg']);
411 $res = qmzl_templates($tok['data']['jwt']);
412 if (!$res['ok']) json_exit_error($res['msg'] ?: '获取模板失败');
413 $list = [];
414 if (isset($res['data']['list'])) {
415 $list = $res['data']['list'];
416 } elseif (is_array($res['data'])) {
417 $list = $res['data'];
418 }
419 if (!is_array($list)) $list = [];
420 if (qmzl_mode() === 'agent') {
421 $mine = qmzl_template_ids_by_user((int)$user['id']);
422 $list = array_values(array_filter($list, function ($t) use ($mine) {
423 return isset($mine[(int)($t['id'] ?? 0)]);
424 }));
425 }
426 json_exit_success('ok', ['list' => $list]);
427});
428
429/** 创建/更新模板(含证件照片上传) */
430mnbt_register_route('POST', '/' . $qzRoute . '/api/template_save', function ($params, $ctx) {
431 $user = qmzl_require_user(true);
432 $id = (int)($_POST['id'] ?? 0);
433 $newId = 0;
434
435 $fields = [];
436 foreach ([
437 'type', 'zh_owner', 'zh_all_name', 'zh_last_name', 'zh_first_name',
438 'en_owner', 'en_all_name', 'en_last_name', 'en_first_name',
439 'email', 'phone', 'zh_address', 'en_address', 'postal_code',
440 'country', 'idtype', 'idnum',
441 ] as $k) {
442 if (isset($_POST[$k])) {
443 $fields[$k] = trim((string)$_POST[$k]);
444 }
445 }
446 if (empty($fields['type']) || !in_array($fields['type'], ['personal', 'enterprise'], true)) {
447 json_exit_error('请选择模板类型');
448 }
449 foreach (['zh_owner', 'en_owner', 'email', 'phone', 'zh_address', 'en_address', 'idtype', 'idnum'] as $k) {
450 if (($fields[$k] ?? '') === '') {
451 json_exit_error('必填项缺失:' . $k);
452 }
453 }
454 if (empty($fields['country'])) $fields['country'] = 'CN';
455
456 $tok = qmzl_require_token($user);
457 if (!$tok['ok']) json_exit_error($tok['msg']);
458
459 // agent 模式:仅允许操作本人创建的模板
460 if (qmzl_mode() === 'agent' && $id > 0 && !qmzl_template_owned($id, (int)$user['id'])) {
461 json_exit_error('无权操作该模板');
462 }
463
464 $files = ['img_front' => $_FILES['img_front'] ?? null, 'img_back' => $_FILES['img_back'] ?? null];
465 foreach ($files as $k => $f) {
466 if (!empty($f['tmp_name']) && $f['size'] > 5 * 1024 * 1024) {
467 json_exit_error('证件照片不能超过 5MB');
468 }
469 }
470 if ($id > 0) {
471 $res = qmzl_template_update($tok['data']['jwt'], $id, $fields, $files);
472 } else {
473 if (empty($files['img_front']['tmp_name'])) {
474 json_exit_error('证件正面照片为必传项');
475 }
476 $res = qmzl_template_create($tok['data']['jwt'], $fields, $files);
477 }
478 if (!$res['ok']) json_exit_error($res['msg'] ?: '保存失败');
479 // agent 模式:记录新模板归属
480 if (qmzl_mode() === 'agent' && $id <= 0) {
481 $newId = 0;
482 $d = $res['data'];
483 if (is_array($d)) {
484 if (!empty($d['id'])) {
485 $newId = (int)$d['id'];
486 } elseif (!empty($d['template_id'])) {
487 $newId = (int)$d['template_id'];
488 } elseif (is_array($d['info_template'] ?? null) && !empty($d['info_template']['id'])) {
489 $newId = (int)$d['info_template']['id'];
490 }
491 }
492 if ($newId > 0) {
493 qmzl_template_record($newId, (int)$user['id'], (string)$user['username']);
494 }
495 }
496 json_exit_success($id > 0 ? '模板已更新' : '模板已创建', ['id' => $id > 0 ? $id : $newId]);
497});
498
499/** 删除模板 */
500mnbt_register_route('POST', '/' . $qzRoute . '/api/template_delete', function ($params, $ctx) {
501 $user = qmzl_require_user(true);
502 $id = (int)($_POST['id'] ?? 0);
503 if ($id <= 0) json_exit_error('参数错误');
504 if (qmzl_mode() === 'agent' && !qmzl_template_owned($id, (int)$user['id'])) {
505 json_exit_error('无权操作该模板');
506 }
507 $tok = qmzl_require_token($user);
508 if (!$tok['ok']) json_exit_error($tok['msg']);
509 $res = qmzl_template_delete($tok['data']['jwt'], $id);
510 if (!$res['ok']) json_exit_error($res['msg'] ?: '删除失败');
511 json_exit_success('已删除');
512});
513
514/** 提交实名认证 */
515mnbt_register_route('POST', '/' . $qzRoute . '/api/template_certify', function ($params, $ctx) {
516 $user = qmzl_require_user(true);
517 $id = (int)($_POST['id'] ?? 0);
518 if ($id <= 0) json_exit_error('参数错误');
519 if (qmzl_mode() === 'agent' && !qmzl_template_owned($id, (int)$user['id'])) {
520 json_exit_error('无权操作该模板');
521 }
522 $files = ['img_front' => $_FILES['img_front'] ?? null, 'img_back' => $_FILES['img_back'] ?? null];
523 if (empty($files['img_front']['tmp_name'])) {
524 json_exit_error('请上传证件正面照片');
525 }
526 foreach ($files as $k => $f) {
527 if (!empty($f['tmp_name']) && $f['size'] > 5 * 1024 * 1024) {
528 json_exit_error('证件照片不能超过 5MB');
529 }
530 }
531 $tok = qmzl_require_token($user);
532 if (!$tok['ok']) json_exit_error($tok['msg']);
533 $res = qmzl_template_certify($tok['data']['jwt'], $id, $files);
534 if (!$res['ok']) json_exit_error($res['msg'] ?: '提交失败');
535 json_exit_success('已提交实名认证,请等待审核');
536});
537
538/** 下单:agent 模式走 MNBT 支付,client 模式走上游购物车结算 */
539mnbt_register_route('POST', '/' . $qzRoute . '/api/place_order', function ($params, $ctx) {
540 global $DB, $date, $siteurl;
541 $user = qmzl_require_user(true);
542 $userId = (int)$user['id'];
543 $domain = strtolower(trim((string)($_POST['domain'] ?? '')));
544 $year = (int)($_POST['year'] ?? 1);
545 $templateId = (int)($_POST['template_id'] ?? 0);
546 $gateway = trim((string)($_POST['gateway'] ?? ''));
547 $autoRenew = isset($_POST['auto_renew']) ? (int)$_POST['auto_renew'] : 1;
548 $lockStatus = isset($_POST['lock_status']) ? (int)$_POST['lock_status'] : 1;
549
550 if (!preg_match('/^[a-z0-9]([a-z0-9\-]*[a-z0-9])?\.[a-z0-9\-.]+$/i', $domain)) {
551 json_exit_error('域名格式不正确');
552 }
553 if ($year < 1 || $year > 10) {
554 json_exit_error('购买年限不正确');
555 }
556 if ($templateId <= 0) {
557 json_exit_error('请选择信息模板');
558 }
559 if (qmzl_mode() === 'agent' && !qmzl_template_owned($templateId, $userId)) {
560 json_exit_error('信息模板不存在或不属于你,请先创建并完成实名认证');
561 }
562 if ($gateway === '') {
563 json_exit_error('请选择支付方式');
564 }
565
566 $tok = qmzl_require_token($user);
567 if (!$tok['ok']) json_exit_error($tok['msg']);
568 $jwt = $tok['data']['jwt'];
569
570 // 1) 取价格(以当次查询为准)
571 $priceRes = qmzl_price($jwt, $domain);
572 if (!$priceRes['ok']) json_exit_error($priceRes['msg'] ?: '获取价格失败');
573 $priceList = $priceRes['data'];
574 if (isset($priceList['list'])) $priceList = $priceList['list'];
575 if (!is_array($priceList)) $priceList = [];
576 $amount = '';
577 foreach ($priceList as $p) {
578 if ((int)($p['buyyear'] ?? 0) === $year) {
579 $amount = (string)($p['buyprice'] ?? '');
580 break;
581 }
582 }
583 if ($amount === '') {
584 json_exit_error('未获取到该域名 ' . $year . ' 年价格');
585 }
586 // agent 模式:加上该后缀溢价
587 if (qmzl_mode() === 'agent') {
588 $markup = qmzl_apply_markup($priceList, $domain);
589 if ($markup > 0) {
590 $amount = (string)round((float)$amount + $markup, 2);
591 }
592 }
593
594 /* ---- agent 模式:创建 MN_dd 订单走系统支付 ---- */
595 if (qmzl_mode() === 'agent') {
596 $out_trade_no = date("YmdHis") . mt_rand(100, 999);
597 $cs = json_encode([
598 'user_id' => $userId,
599 'username' => (string)$user['username'],
600 'domain' => $domain,
601 'year' => $year,
602 'template_id' => $templateId,
603 'auto_renew' => $autoRenew,
604 'lock_status' => $lockStatus,
605 'amount' => $amount,
606 'gateway' => $gateway,
607 ], 256);
608 $ip = $_SERVER["REMOTE_ADDR"] ?? '127.0.0.1';
609 $row1 = $DB->get_row_prepare("SELECT * FROM MN_dd WHERE 1 order by id desc limit 1");
610 $dd_id = $row1 ? ((int)$row1['id'] + 1) : 1;
611 $ok = $DB->query_prepare(
612 "INSERT INTO MN_dd (id, cs, date, zffs, je, ddh, lx, qk, ip) VALUES (?,?,?,?,?,?,?,?,?)",
613 [$dd_id, $cs, $date, $gateway, $amount, $out_trade_no, 'qmzl_domain', 'false', $ip]
614 );
615 if (!$ok) json_exit_error('创建订单失败,请稍后重试');
616
617 // 本地订单记录(ddh 关联,待支付)
618 qmzl_order_create($userId, (string)$user['username'], $out_trade_no, $domain, $year, $amount, $templateId, '', $gateway, '等待支付');
619
620 // 分发到 MNBT 支付插件
621 $order_context = [
622 'out_trade_no' => $out_trade_no,
623 'name' => '域名注册:' . $domain,
624 'money' => $amount,
625 'type' => $gateway,
626 'siteurl' => $siteurl,
627 'pay_lx' => 'qmzl_domain',
628 ];
629 $html = function_exists('mnbt_pay_dispatch_gateway') ? mnbt_pay_dispatch_gateway($gateway, $order_context) : false;
630 if ($html === false) {
631 json_exit_error('支付方式不可用,请检查支付插件是否已启用');
632 }
633 json_exit_success('下单成功,请完成支付', ['mode' => 'agent', 'html' => $html, 'order_no' => $out_trade_no]);
634 }
635
636 /* ---- client 模式:上游购物车 + 结算 ---- */
637 $productId = qmzl_product_id($jwt);
638 if ($productId <= 0) {
639 json_exit_error('未找到域名产品,请联系平台确认');
640 }
641 $cart = qmzl_cart_add($jwt, $productId, $domain, $year);
642 if (!$cart['ok']) json_exit_error('加入购物车失败:' . $cart['msg']);
643
644 $settle = qmzl_cart_settle($jwt, $templateId, $autoRenew, $lockStatus);
645 if (!$settle['ok']) json_exit_error('下单失败:' . $settle['msg']);
646 $orderId = 0;
647 if (isset($settle['data']['order_id'])) {
648 $orderId = (int)$settle['data']['order_id'];
649 } elseif (!empty($settle['data']['ids']) && is_array($settle['data']['ids'])) {
650 $orderId = (int)$settle['data']['ids'][0];
651 }
652 if ($orderId <= 0) json_exit_error('下单失败:未返回订单号');
653
654 qmzl_order_create($userId, (string)$user['username'], '', $domain, $year, $amount, $templateId, $orderId, $gateway, '等待支付');
655
656 json_exit_success('下单成功,请前往支付', [
657 'mode' => 'client',
658 'order_id' => $orderId,
659 'amount' => $amount,
660 'gateway' => $gateway,
661 'cloud_pay_url' => QMZL_SITE . '/console/payment?orderId=' . $orderId . '&amount=' . rawurlencode($amount) . '&gateway=' . rawurlencode($gateway),
662 ]);
663});
664
665/** 发起支付(仅 client 模式,返回三方 HTML) */
666mnbt_register_route('POST', '/' . $qzRoute . '/api/pay', function ($params, $ctx) {
667 $user = qmzl_require_user(true);
668 if (qmzl_mode() !== 'client') json_exit_error('代理商模式无需此操作');
669 $userId = (int)$user['id'];
670 $orderId = (int)($_POST['order_id'] ?? 0);
671 $gateway = trim((string)($_POST['gateway'] ?? ''));
672 if ($orderId <= 0 || $gateway === '') json_exit_error('参数错误');
673 $rec = qmzl_order_get($orderId);
674 if ($rec && (int)$rec['user_id'] !== $userId) json_exit_error('订单不存在');
675 $tok = qmzl_require_token($user);
676 if (!$tok['ok']) json_exit_error($tok['msg']);
677 $res = qmzl_pay($tok['data']['jwt'], $orderId, $gateway);
678 if (!$res['ok']) json_exit_error($res['msg'] ?: '发起支付失败');
679 if (!empty($res['paid'])) {
680 qmzl_order_update_status($orderId, 'Paid');
681 json_exit_success('该订单已支付', ['code' => 'Paid']);
682 }
683 json_exit_success('ok', ['code' => '', 'html' => $res['pay_html']]);
684});
685
686/** 支付状态轮询(仅 client 模式) */
687mnbt_register_route('POST', '/' . $qzRoute . '/api/pay_status', function ($params, $ctx) {
688 $user = qmzl_require_user(true);
689 if (qmzl_mode() !== 'client') json_exit_error('代理商模式无需此操作');
690 $userId = (int)$user['id'];
691 $orderId = (int)($_POST['order_id'] ?? 0);
692 if ($orderId <= 0) json_exit_error('参数错误');
693 $rec = qmzl_order_get($orderId);
694 if ($rec && (int)$rec['user_id'] !== $userId) json_exit_error('订单不存在');
695 $tok = qmzl_require_token($user);
696 if (!$tok['ok']) json_exit_error($tok['msg']);
697 $res = qmzl_pay_status($tok['data']['jwt'], $orderId);
698 if (!$res['ok']) json_exit_error($res['msg'] ?: '查询失败');
699 if (!empty($res['paid'])) {
700 qmzl_order_update_status($orderId, 'Paid');
701 }
702 json_exit_success('ok', ['paid' => !empty($res['paid'])]);
703});
704
705/** 我的域名 */
706mnbt_register_route('POST', '/' . $qzRoute . '/api/domains', function ($params, $ctx) {
707 $user = qmzl_require_user(true);
708 $data = qmzl_my_domains($user);
709 if (!$data['ok']) json_exit_error($data['msg']);
710 json_exit_success('ok', ['list' => $data['list']]);
711});
712
713/** 本地订单记录 */
714mnbt_register_route('POST', '/' . $qzRoute . '/api/orders', function ($params, $ctx) {
715 $user = qmzl_require_user(true);
716 $page = max(1, (int)($_POST['page'] ?? 1));
717 $data = qmzl_order_list((int)$user['id'], $page, 20);
718 json_exit_success('ok', $data);
719});
720
721} // end 用户端开关