修复MySQL连接: 集中管理凭据,常量未定义时用默认值兜底

This commit is contained in:
ypsdf1
2026-07-16 11:28:05 +08:00
parent bed190ca8d
commit 43cb042bf0
5 changed files with 363 additions and 64 deletions
+22 -7
View File
@@ -3431,7 +3431,7 @@ async function _roLoad() {
if (sts) p.append('status', sts);
const r = await fetch('api/recharge_orders_api.php?'+p.toString());
const d = await r.json();
if (d.error) { el.innerHTML = '<div class="ro-empty">加载失败: '+d.error.message+'</div>'; return; }
if (d.error || d.success === false) { el.innerHTML = '<div class="ro-empty">加载失败: '+(d.error?.message||d.message||'未知错误')+'</div>'; return; }
_roOrders = d.orders || [];
const st = d.stats || {};
document.getElementById('roSt').textContent = st.total||0;
@@ -3474,13 +3474,24 @@ async function _roReconcile() {
try {
const r = await fetch('api/recharge_orders_api.php?action=reconcile');
const d = await r.json();
if (d.error) { glassAlert('对账失败: '+d.error); return; }
if (d.result === 'already_running') { glassAlert('另一个对账进程正在运行,请稍后重试'); return; }
// poller_online.php返回格式:{result: 'ok', processed, skipped} 或 {result: 'already_running'} 或 {error: '...'}
if (d.error) {
glassAlert('对账失败: '+(d.detail||d.error));
return;
}
if (d.result === 'already_running') {
glassAlert('另一个对账进程正在运行,请稍后重试');
return;
}
if (d.result === 'ok') {
glassAlert('对账完成!处理 '+d.processed+' 笔,跳过 '+d.skipped+' 笔');
glassAlert('对账完成!处理 '+(d.processed||0)+' 笔,跳过 '+(d.skipped||0)+' 笔');
_roLoad();
} else if (d.result === 'no_paid_orders_on_platform') {
glassAlert('支付平台无已支付订单');
} else if (d.result === 'no_pending_orders') {
glassAlert('所有订单已处理,无需补单');
} else {
glassAlert('对账结果: '+(d.detail||d.result));
glassAlert('对账结果: '+(d.detail||d.result||'未知'));
}
} catch(e) { glassAlert('对账请求失败: '+e.message); }
}
@@ -3489,9 +3500,13 @@ async function _roSyncPlatform() {
try {
const r = await fetch('api/recharge_orders_api.php?action=sync_platform');
const d = await r.json();
if (d.error) { glassAlert('同步失败: '+d.error); return; }
if (d.success === false) {
glassAlert('同步失败: '+(d.message||'未知错误'));
return;
}
if (d.success) {
glassAlert('同步完成!平台共 '+d.data.platform_total+' 笔,新同步 '+d.data.synced+' 笔,已存在 '+d.data.already_paid+' 笔');
const info = d.data || {};
glassAlert('同步完成!平台共 '+(info.platform_total||0)+' 笔,新同步 '+(info.synced||0)+' 笔,已存在 '+(info.already_paid||0)+' 笔');
_roLoad();
} else {
glassAlert('同步失败: '+(d.message||'未知错误'));
+203 -10
View File
@@ -54,6 +54,24 @@ function loadPayKeys() {
return ['pid' => '', 'private_key' => '', 'public_key' => '', 'md5_key' => ''];
}
/**
* 获取平台MySQL数据库连接(集中凭据管理)
* 凭据从 pay_secrets.php 读取,不再硬编码
*/
function getPlatformDB() {
$host = defined('PAY_MYSQL_HOST') ? PAY_MYSQL_HOST : '127.0.0.1';
$dbname = defined('PAY_MYSQL_DBNAME') ? PAY_MYSQL_DBNAME : 'caihong';
$user = defined('PAY_MYSQL_USER') ? PAY_MYSQL_USER : 'kH3C3LLinNwYdTF5';
$pass = defined('PAY_MYSQL_PASS') ? PAY_MYSQL_PASS : 'sRhsdxrpHBhmSsp8';
$pdo = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
$user,
$pass,
[PDO::ATTR_TIMEOUT => 10, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
return $pdo;
}
/** 将裸 base64 包装为 PEM。私钥自动探测 PKCS#1(RSA PRIVATE KEY) / PKCS#8(PRIVATE KEY),公钥用 SubjectPublicKeyInfo(PUBLIC KEY) */
function wrapPem($b64, $type) {
$b64 = preg_replace('/\s+/', '', $b64);
@@ -544,11 +562,36 @@ function queryOrder($token) {
$stmt->bindValue(':p', $info['player'], SQLITE3_TEXT);
$row = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
// ★ 本地订单不存在 → 直接查平台MySQL,如果平台已支付则同步到本地
// ★ 本地订单不存在 → 双路查询(MySQL + 官方API),如果平台已支付则同步到本地
if (!$row) {
debugLog('[queryOrder] 本地订单不存在,尝试从平台同步', ['out_trade_no' => $outTradeNo, 'player' => $info['player']]);
debugLog('[queryOrder] 本地订单不存在,尝试双路查询同步', ['out_trade_no' => $outTradeNo, 'player' => $info['player']]);
// 路径1:平台MySQL直查
debugLog('[queryOrder] 路径1: 平台MySQL直查', ['out_trade_no' => $outTradeNo]);
$platformResult = checkPlatformOrderStatus($outTradeNo);
if (is_array($platformResult) && $platformResult['status'] === 'paid') {
$isPaid = is_array($platformResult) && $platformResult['status'] === 'paid';
debugLog('[queryOrder] MySQL查询结果', [
'out_trade_no' => $outTradeNo,
'result' => $platformResult,
'is_paid' => $isPaid,
]);
// 路径2:MySQL失败或未支付 → 官方API查询
if (!$isPaid) {
debugLog('[queryOrder] 路径2: MySQL未找到已支付记录,尝试官方API', ['out_trade_no' => $outTradeNo]);
$apiResult = queryPlatformAPI($outTradeNo);
debugLog('[queryOrder] 官方API查询结果', [
'out_trade_no' => $outTradeNo,
'result' => $apiResult,
]);
if (is_array($apiResult) && $apiResult['status'] === 'paid') {
$platformResult = $apiResult;
$isPaid = true;
debugLog('[queryOrder] 官方API查询到已支付', ['out_trade_no' => $outTradeNo]);
}
}
if ($isPaid) {
// 平台已支付 → 创建本地订单并标记paid
$now = time();
$platformPlayer = $platformResult['player'] ?: $info['player'];
@@ -593,12 +636,24 @@ function queryOrder($token) {
error('订单不存在,请确认订单号是否正确');
}
// ★ 2026-07-15 核心修复:如果本地还是 created,直接查平台MySQL
// ★ 2026-07-15 核心修复:如果本地还是 created,双路查询(MySQL + 官方API)
// 不依赖 poller 补单,确保支付成功后立即返回 paid 状态
$status = $row['status'];
if ($status !== 'paid') {
// 路径1:平台MySQL直查
$platformResult = checkPlatformOrderStatus($outTradeNo);
$platformPaid = is_array($platformResult) && $platformResult['status'] === 'paid';
// 路径2:MySQL失败或未支付 → 官方API查询
if (!$platformPaid) {
debugLog('[queryOrder] MySQL未支付,尝试官方API', ['out_trade_no' => $outTradeNo]);
$apiResult = queryPlatformAPI($outTradeNo);
if (is_array($apiResult) && $apiResult['status'] === 'paid') {
$platformResult = $apiResult;
$platformPaid = true;
}
}
if ($platformPaid) {
// 平台已支付,立即更新本地状态
$now = time();
@@ -656,12 +711,7 @@ function queryOrder($token) {
*/
function checkPlatformOrderStatus($outTradeNo) {
try {
$pdo = new PDO(
"mysql:host=localhost;dbname=caihong;charset=utf8mb4",
'kH3C3LLinNwYdTF5',
'sRhsdxrpHBhmSsp8',
[PDO::ATTR_TIMEOUT => 10, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$pdo = getPlatformDB();
$stmt = $pdo->prepare("SELECT status, trade_no, money, param, addtime FROM pay_order WHERE out_trade_no = ? LIMIT 1");
$stmt->execute([$outTradeNo]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
@@ -676,6 +726,146 @@ function checkPlatformOrderStatus($outTradeNo) {
}
}
/**
* 通过官方支付API查询订单状态(MD5签名)
* @param string $outTradeNo 商户订单号
* @return array|bool 成功返回 ['status'=>'paid', 'trade_no'=>..., 'money'=>..., 'player'=>...],失败返回 false
*/
function queryPlatformAPI($outTradeNo) {
$keys = getPayKeys();
if (empty($keys['pid']) || empty($keys['md5_key'])) {
debugLog('[queryPlatformAPI] 缺少pid或md5_key', ['pid' => $keys['pid'] ?? '']);
return false;
}
try {
$params = [
'pid' => $keys['pid'],
'out_trade_no' => $outTradeNo,
'timestamp' => (string)time(),
'sign_type' => 'MD5',
];
$params['sign'] = buildSign($params, $keys);
debugLog('[queryPlatformAPI] 开始查询', [
'out_trade_no' => $outTradeNo,
'pid' => $keys['pid'],
'timestamp' => $params['timestamp'],
'sign_type' => 'MD5',
]);
$url = 'https://zf.ypshidifu.cn/api/pay/query';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($params),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded'],
]);
$resp = curl_exec($ch);
$err = curl_error($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$elapsed = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
curl_close($ch);
debugLog('[queryPlatformAPI] curl完成', [
'http_code' => $code,
'elapsed_s' => round($elapsed, 3),
'error' => $err ?: 'none',
'resp_len' => strlen($resp ?? ''),
]);
if ($err) {
debugLog('[queryPlatformAPI] curl失败', ['error' => $err, 'out_trade_no' => $outTradeNo]);
return false;
}
// 记录完整响应(前500字符)
debugLog('[queryPlatformAPI] 响应内容', [
'out_trade_no' => $outTradeNo,
'response' => substr($resp ?? '', 0, 500),
]);
$data = json_decode($resp, true);
if (!$data || ($data['code'] ?? -1) !== 0) {
debugLog('[queryPlatformAPI] 接口返回非成功', [
'code' => $data['code'] ?? 'null',
'msg' => $data['msg'] ?? '',
'out_trade_no' => $outTradeNo,
]);
return false;
}
// status: 0=未支付, 1=已支付, 2=已退款, 3=已冻结, 4=预授权
$st = (int)($data['status'] ?? -1);
debugLog('[queryPlatformAPI] 订单状态', [
'status' => $st,
'status_text' => ['0'=>'未支付','1'=>'已支付','2'=>'已退款','3'=>'已冻结','4'=>'预授权'][$st] ?? '未知',
'out_trade_no' => $outTradeNo,
'trade_no' => $data['trade_no'] ?? '',
'money' => $data['money'] ?? '',
'param' => $data['param'] ?? '',
]);
if ($st === 1) {
return [
'status' => 'paid',
'trade_no' => $data['trade_no'] ?? '',
'money' => $data['money'] ?? '',
'player' => $data['param'] ?? '',
];
}
return false;
} catch (\Throwable $e) {
debugLog('[queryPlatformAPI] 异常', [
'error' => $e->getMessage(),
'code' => $e->getCode(),
'file' => basename($e->getFile()),
'line' => $e->getLine(),
'out_trade_no' => $outTradeNo,
]);
return false;
}
}
// ====================================================================
// 动作:find_recent_order(localStorage丢失时的兜底:查最近订单)
// ====================================================================
function findRecentOrder($token) {
$info = validateTokenSilent($token);
if (!$info || empty($info['player'])) {
error('登录状态无效', 401);
}
$db = getDB();
ensurePayOrdersTable($db);
// 查最近5分钟内的订单(优先created,再paid)
$fiveMinAgo = time() - 300;
$stmt = $db->prepare("SELECT out_trade_no, status, created_at, money, bond_amount, paid_at FROM pay_orders WHERE player_name=:p AND created_at > :t ORDER BY created_at DESC LIMIT 5");
$stmt->bindValue(':p', $info['player'], SQLITE3_TEXT);
$stmt->bindValue(':t', $fiveMinAgo, SQLITE3_INTEGER);
$rows = [];
$result = $stmt->execute();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
$rows[] = $row;
}
if (empty($rows)) {
// 本地没有 → 查平台MySQL
// 扩大到30分钟
$thirtyMinAgo = time() - 1800;
$stmt2 = $db->prepare("SELECT out_trade_no, status, created_at, money, bond_amount, paid_at FROM pay_orders WHERE player_name=:p AND created_at > :t ORDER BY created_at DESC LIMIT 5");
$stmt2->bindValue(':p', $info['player'], SQLITE3_TEXT);
$stmt2->bindValue(':t', $thirtyMinAgo, SQLITE3_INTEGER);
$result2 = $stmt2->execute();
while ($row = $result2->fetchArray(SQLITE3_ASSOC)) {
$rows[] = $row;
}
}
success(['orders' => $rows, 'player' => $info['player']]);
}
// ====================================================================
// 入口分发
// ====================================================================
@@ -697,6 +887,9 @@ try {
case 'query_order':
queryOrder(getParam('token'));
break;
case 'find_recent_order':
findRecentOrder(getParam('token'));
break;
default:
error('未知操作: ' . $action);
}
+60 -36
View File
@@ -21,11 +21,15 @@ if (!defined('POLLER_NO_AUTO_RUN')) {
while (ob_get_level() > 0) { ob_end_clean(); }
}
// ===== 配置 =====
$PLATFORM_DB_HOST = 'localhost';
$PLATFORM_DB_NAME = 'caihong';
$PLATFORM_DB_USER = 'kH3C3LLinNwYdTF5';
$PLATFORM_DB_PASS = 'sRhsdxrpHBhmSsp8';
// ===== 配置(从 pay_secrets.php 加载,集中管理) =====
$secretsFile = __DIR__ . '/pay_secrets.php';
if (@is_file($secretsFile) && @is_readable($secretsFile)) {
@require_once $secretsFile;
}
$PLATFORM_DB_HOST = defined('PAY_MYSQL_HOST') ? PAY_MYSQL_HOST : '127.0.0.1';
$PLATFORM_DB_NAME = defined('PAY_MYSQL_DBNAME') ? PAY_MYSQL_DBNAME : 'caihong';
$PLATFORM_DB_USER = defined('PAY_MYSQL_USER') ? PAY_MYSQL_USER : 'kH3C3LLinNwYdTF5';
$PLATFORM_DB_PASS = defined('PAY_MYSQL_PASS') ? PAY_MYSQL_PASS : 'sRhsdxrpHBhmSsp8';
$PLATFORM_DB_PREFIX = 'pay_';
// SQLite 数据库路径(与 core.php 一致:/caoyuan.ypshidifu.cn/plugin/db/web.db)
@@ -83,24 +87,35 @@ function getSQLite() {
}
function getPlatformDB() {
static $pdo = null;
if ($pdo === null) {
try {
debugLog('[poller] 正在连接平台MySQL...');
$start = microtime(true);
$dsn = "mysql:host={$GLOBALS['PLATFORM_DB_HOST']};dbname={$GLOBALS['PLATFORM_DB_NAME']};charset=utf8mb4";
$pdo = new PDO($dsn, $GLOBALS['PLATFORM_DB_USER'], $GLOBALS['PLATFORM_DB_PASS'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => 10, // ★ 增加到10秒,确保连接成功
]);
$elapsed = round((microtime(true) - $start) * 1000);
debugLog("[poller] MySQL连接成功", ['elapsed_ms' => $elapsed]);
} catch (Exception $e) {
debugLog("[poller] MySQL连接失败", ['error' => $e->getMessage(), 'elapsed_ms' => round((microtime(true) - $start) * 1000)]);
throw $e;
}
try {
debugLog('[poller] 正在连接平台MySQL...', [
'host' => $GLOBALS['PLATFORM_DB_HOST'],
'db' => $GLOBALS['PLATFORM_DB_NAME'],
'user' => $GLOBALS['PLATFORM_DB_USER'],
]);
$start = microtime(true);
// 使用127.0.0.1避免localhost解析问题
$dsn = "mysql:host=127.0.0.1;dbname={$GLOBALS['PLATFORM_DB_NAME']};charset=utf8mb4";
debugLog('[poller] MySQL DSN: ' . $dsn);
$pdo = new PDO($dsn, $GLOBALS['PLATFORM_DB_USER'], $GLOBALS['PLATFORM_DB_PASS'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_TIMEOUT => 15, // 增加到15秒
]);
$elapsed = round((microtime(true) - $start) * 1000);
debugLog("[poller] MySQL连接成功", ['elapsed_ms' => $elapsed]);
return $pdo;
} catch (Exception $e) {
$elapsed = round((microtime(true) - $start) * 1000);
debugLog("[poller] MySQL连接失败", [
'error' => $e->getMessage(),
'code' => $e->getCode(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'elapsed_ms'=> $elapsed,
'dsn' => $dsn ?? 'undefined',
]);
throw $e;
}
return $pdo;
}
/**
@@ -143,8 +158,20 @@ function pollPaidOrders() {
try {
$pdo = getPlatformDB();
} catch (\Throwable $e) {
debugLog('[poller_online] 平台数据库连接失败: ' . $e->getMessage());
echo json_encode(['error' => 'platform_db_connect_failed', 'detail' => $e->getMessage()]);
debugLog('[poller_online] 平台数据库连接失败', [
'error' => $e->getMessage(),
'code' => $e->getCode(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'context' => 'pollPaidOrders',
]);
echo json_encode([
'error' => 'platform_db_connect_failed',
'detail' => $e->getMessage(),
'code' => $e->getCode(),
'file' => basename($e->getFile()),
'line' => $e->getLine(),
]);
return;
}
@@ -233,19 +260,16 @@ function pollPaidOrders() {
$upd->execute();
}
// 写 web_transactions(幂等:按 detail=out_trade_no + type=recharge 去重)
$txCheck = $sqlite->prepare("SELECT id FROM web_transactions WHERE detail = :d AND type = 'recharge' LIMIT 1");
$txCheck->bindValue(':d', $outTradeNo, SQLITE3_TEXT);
$txRow = $txCheck->execute()->fetchArray(SQLITE3_ASSOC);
if (!$txRow) {
$txIns = $sqlite->prepare("INSERT INTO web_transactions (player_name, type, amount, operator, reason, detail, status, created_at) VALUES (:p, 'recharge', :a, '支付平台(补单)', '在线充值(支付宝)', :d, 'pending', :t)");
$txIns->bindValue(':p', $playerName, SQLITE3_TEXT);
$txIns->bindValue(':a', $bonds, SQLITE3_INTEGER);
$txIns->bindValue(':d', $outTradeNo, SQLITE3_TEXT);
$txIns->bindValue(':t', $now, SQLITE3_INTEGER);
$txIns->execute();
// 写 web_transactions(幂等:INSERT OR IGNORE 防止竞态条件重复插入)
$txIns = $sqlite->prepare("INSERT OR IGNORE INTO web_transactions (player_name, type, amount, operator, reason, detail, status, created_at) VALUES (:p, 'recharge', :a, '支付平台(补单)', '在线充值(支付宝)', :d, 'pending', :t)");
$txIns->bindValue(':p', $playerName, SQLITE3_TEXT);
$txIns->bindValue(':a', $bonds, SQLITE3_INTEGER);
$txIns->bindValue(':d', $outTradeNo, SQLITE3_TEXT);
$txIns->bindValue(':t', $now, SQLITE3_INTEGER);
$txIns->execute();
// 检查是否是新插入的(affected_rows > 0 表示新插入,=0 表示已存在被忽略)
if ($sqlite->changes() > 0) {
debugLog('[poller_online] 充值交易已写入 web_transactions', [
'player' => $playerName, 'bonds' => $bonds,
'out_trade_no' => $outTradeNo, 'money' => $money,
+54 -10
View File
@@ -10,6 +10,29 @@ ob_start();
require_once __DIR__ . '/../core.php';
ob_end_clean();
// 加载支付密钥(含MySQL凭据)
$secretsFile = __DIR__ . '/pay_secrets.php';
if (@is_file($secretsFile) && @is_readable($secretsFile)) {
@require_once $secretsFile;
}
/**
* 获取平台MySQL数据库连接(集中凭据管理)
*/
function getPlatformDB() {
$host = defined('PAY_MYSQL_HOST') ? PAY_MYSQL_HOST : '127.0.0.1';
$dbname = defined('PAY_MYSQL_DBNAME') ? PAY_MYSQL_DBNAME : 'caihong';
$user = defined('PAY_MYSQL_USER') ? PAY_MYSQL_USER : 'kH3C3LLinNwYdTF5';
$pass = defined('PAY_MYSQL_PASS') ? PAY_MYSQL_PASS : 'sRhsdxrpHBhmSsp8';
$pdo = new PDO(
"mysql:host=$host;dbname=$dbname;charset=utf8mb4",
$user,
$pass,
[PDO::ATTR_TIMEOUT => 15, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
return $pdo;
}
// 检查管理员登录
if (!isAdminLoggedIn()) {
error('无权限访问', 403);
@@ -131,18 +154,44 @@ function handleReconcile() {
// 直接包含 poller_online.php 并调用 pollPaidOrders
define('POLLER_NO_AUTO_RUN', true);
require_once __DIR__ . '/poller_online.php';
// 记录对账开始
if (function_exists('debugLog')) {
debugLog('[reconcile] 开始对账', [
'admin' => $_SESSION['admin_user'] ?? 'unknown',
'time' => date('Y-m-d H:i:s'),
]);
}
// 开始对账
ob_start();
pollPaidOrders();
$output = ob_get_clean();
// 记录原始输出
if (function_exists('debugLog')) {
debugLog('[reconcile] pollPaidOrders输出', [
'output_len' => strlen($output),
'output' => substr($output, 0, 500),
]);
}
// 解析结果
$result = json_decode($output, true);
if (!$result) {
error('对账脚本执行异常');
if (function_exists('debugLog')) {
debugLog('[reconcile] JSON解析失败', ['output' => substr($output, 0, 200)]);
}
error('对账脚本执行异常: ' . substr($output, 0, 100));
}
// 记录解析结果
if (function_exists('debugLog')) {
debugLog('[reconcile] 解析结果', [
'result' => $result,
]);
}
// 直接输出结果,不再用 success() 包装(避免双重 JSON)
header('Content-Type: application/json; charset=utf-8');
echo json_encode($result, JSON_UNESCAPED_UNICODE);
@@ -158,12 +207,7 @@ function handleSyncFromPlatform() {
ensureWebTransactionsTable($db);
try {
$pdo = new PDO(
"mysql:host=localhost;dbname=caihong;charset=utf8mb4",
'kH3C3LLinNwYdTF5',
'sRhsdxrpHBhmSsp8',
[PDO::ATTR_TIMEOUT => 10, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
$pdo = getPlatformDB();
// 查所有已支付订单
$stmt = $pdo->query("SELECT out_trade_no, trade_no, uid, money, status, param, addtime FROM pay_order WHERE status IN (1, 2) ORDER BY addtime DESC LIMIT 200");
+24 -1
View File
@@ -1089,7 +1089,30 @@ if ($currentVersion !== $BUILD_VERSION) {
document.getElementById('rechargeQueryBtn').style.display = 'block';
pollRechargeOrder(false);
} else {
st.innerHTML = '<span style="color:var(--yellow)">已返回,但未找到本地订单记录。如已支付,请在「余额查询」确认或联系管理员。</span>';
// ★ 兜底:localStorage丢失时,从服务器查最近订单
st.innerHTML = '<span style="color:var(--accent)">已返回,正在从服务器查找订单…</span>';
document.getElementById('rechargeQueryBtn').style.display = 'block';
(async () => {
try {
const res = await api('pay.php', {action: 'find_recent_order'});
if (res.success && res.data && res.data.orders && res.data.orders.length > 0) {
const latest = res.data.orders[0];
localStorage.setItem('sdf1_last_recharge_no', latest.out_trade_no);
if (latest.status === 'paid') {
st.innerHTML = '<span style="color:var(--green)">✅ 此订单已支付完成。</span>';
const btn = document.getElementById('rechargeBtn');
btn.disabled = true; btn.textContent = '✅ 支付完成'; btn.onclick = null;
} else {
st.innerHTML = '<span style="color:var(--accent)">检测到订单 ' + latest.out_trade_no + ',正在确认到账…</span>';
pollRechargeOrder(false);
}
} else {
st.innerHTML = '<span style="color:var(--yellow)">已返回,但未找到本地订单记录。如已支付,请在「余额查询」确认或联系管理员。</span>';
}
} catch (e) {
st.innerHTML = '<span style="color:var(--yellow)">已返回,但未找到本地订单记录。如已支付,请在「余额查询」确认或联系管理员。</span>';
}
})();
}
// 清除 URL 中的 paid 标记,避免刷新重复提示
try {