From 767a9892f7d9d4781b99773b67a75112241dc398 Mon Sep 17 00:00:00 2001 From: ypsdf1 Date: Wed, 15 Jul 2026 20:24:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20query=5Forder=E7=9B=B4?= =?UTF-8?q?=E6=9F=A5=E5=B9=B3=E5=8F=B0MySQL+poller=E8=AF=A6=E7=BB=86?= =?UTF-8?q?=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - pay.php: query_order直接查平台MySQL,不依赖poller补单 - poller_online.php: MySQL连接超时增加到10秒+详细耗时日志 - 解决: 支付成功后前端按钮不锁定+游戏无回调 --- api/pay.php | 67 +++++++++++++++++++++++++++++++++++++++++-- api/poller_online.php | 17 +++++++++-- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/api/pay.php b/api/pay.php index ea53561..10f6e76 100644 --- a/api/pay.php +++ b/api/pay.php @@ -493,9 +493,6 @@ function triggerPollerAsync() { // 动作:query_order(前端补单 / 轮询) // ==================================================================== function queryOrder($token) { - // 后台触发轮询器(非阻塞),处理 Cloudflare WAF 拦截导致的未通知订单 - @triggerPollerAsync(); - $info = validateTokenSilent($token); if (!$info || empty($info['player'])) { error('登录状态无效', 401); @@ -513,10 +510,52 @@ function queryOrder($token) { if (!$row) { error('订单不存在'); } + + // ★ 2026-07-15 核心修复:如果本地还是 created,直接查平台MySQL + // 不依赖 poller 补单,确保支付成功后立即返回 paid 状态 $status = $row['status']; + if ($status !== 'paid') { + $platformStatus = checkPlatformOrderStatus($outTradeNo); + if ($platformStatus === 'paid') { + // 平台已支付,立即更新本地状态 + $now = time(); + $upd = $db->prepare("UPDATE pay_orders SET status='paid', paid_at=:t WHERE out_trade_no=:no AND status != 'paid'"); + $upd->bindValue(':t', $now, SQLITE3_INTEGER); + $upd->bindValue(':no', $outTradeNo, SQLITE3_TEXT); + $upd->execute(); + + // 写 web_transactions(幂等) + $txCheck = $db->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 = $db->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', $info['player'], SQLITE3_TEXT); + $txIns->bindValue(':a', (int)$row['bond_amount'], SQLITE3_INTEGER); + $txIns->bindValue(':d', $outTradeNo, SQLITE3_TEXT); + $txIns->bindValue(':t', $now, SQLITE3_INTEGER); + $txIns->execute(); + } + + $status = 'paid'; + debugLog('[queryOrder] 直查平台补单成功', ['out_trade_no' => $outTradeNo, 'player' => $info['player']]); + } else { + // 检查是否已过期(5分钟) + $nowSec = time(); + if ($row['created_at'] && ($nowSec - $row['created_at']) > 300) { + $upd = $db->prepare("UPDATE pay_orders SET status='expired' WHERE out_trade_no=:no AND status='created'"); + $upd->bindValue(':no', $outTradeNo, SQLITE3_TEXT); + $upd->execute(); + $status = 'expired'; + } + } + } + $message = '订单创建中'; if ($status === 'paid') { $message = '已支付,游戏内债券发放中(若未到账请稍候或联系管理员)'; + } elseif ($status === 'expired') { + $message = '订单已过期(超过5分钟),请重新下单'; } success([ 'out_trade_no' => $outTradeNo, @@ -528,6 +567,28 @@ function queryOrder($token) { ], $message); } +/** + * 直接查平台MySQL检查订单状态(不依赖poller) + * @return string 'paid'|'created'|'not_found' + */ +function checkPlatformOrderStatus($outTradeNo) { + try { + $pdo = new PDO( + "mysql:host=localhost;dbname=caihong;charset=utf8mb4", + 'kH3C3LLinNwYdTF5', + 'sRhsdxrpHBhmSsp8', + [PDO::ATTR_TIMEOUT => 3, PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION] + ); + $stmt = $pdo->prepare("SELECT status FROM pay_order WHERE out_trade_no = ? AND status IN (1, 2) LIMIT 1"); + $stmt->execute([$outTradeNo]); + $row = $stmt->fetch(PDO::FETCH_ASSOC); + return $row ? 'paid' : 'not_found'; + } catch (\Throwable $e) { + debugLog('[checkPlatformOrderStatus] 查询失败', ['error' => $e->getMessage(), 'out_trade_no' => $outTradeNo]); + return 'not_found'; + } +} + // ==================================================================== // 入口分发 // ==================================================================== diff --git a/api/poller_online.php b/api/poller_online.php index 4b66ca6..c8ca1b6 100644 --- a/api/poller_online.php +++ b/api/poller_online.php @@ -86,14 +86,18 @@ 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 => 5, + PDO::ATTR_TIMEOUT => 10, // ★ 增加到10秒,确保连接成功 ]); + $elapsed = round((microtime(true) - $start) * 1000); + debugLog("[poller] MySQL连接成功", ['elapsed_ms' => $elapsed]); } catch (Exception $e) { - debugLog("Platform DB connection failed", ['error' => $e->getMessage()]); - throw $e; // re-throw to be caught by pollPaidOrders + debugLog("[poller] MySQL连接失败", ['error' => $e->getMessage(), 'elapsed_ms' => round((microtime(true) - $start) * 1000)]); + throw $e; } } return $pdo; @@ -125,6 +129,9 @@ function pollerAcquireLock() { } function pollPaidOrders() { + $startTime = microtime(true); + debugLog('[poller] 补单开始'); + // 并发保护:已有补单进程在跑则直接跳过,杜绝重复补单 if (!pollerAcquireLock()) { echo json_encode(['result' => 'already_running', 'detail' => '另一个补单进程正在运行,本次跳过']); @@ -249,11 +256,15 @@ function pollPaidOrders() { $processed++; } + $elapsed = round((microtime(true) - $startTime) * 1000); + debugLog('[poller] 补单完成', ['processed' => $processed, 'skipped' => $skipped, 'elapsed_ms' => $elapsed]); + echo json_encode([ 'result' => 'ok', 'processed' => $processed, 'skipped' => $skipped, 'total' => count($orders), + 'elapsed_ms' => $elapsed, ]); }