修复: query_order直查平台MySQL+poller详细日志

- pay.php: query_order直接查平台MySQL,不依赖poller补单
- poller_online.php: MySQL连接超时增加到10秒+详细耗时日志
- 解决: 支付成功后前端按钮不锁定+游戏无回调
This commit is contained in:
ypsdf1
2026-07-15 20:24:47 +08:00
parent cb949f5b16
commit 767a9892f7
2 changed files with 78 additions and 6 deletions
+64 -3
View File
@@ -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';
}
}
// ====================================================================
// 入口分发
// ====================================================================
+14 -3
View File
@@ -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,
]);
}