401, 'msg' => 'Unauthorized']); exit; } function check_trc20_payment($address, $target_amount, $order_time) { if (!$address || $address == 'TEm1B...TRC20_ADDRESS_HERE') return false; // TronScan API to check transactions $url = "https://apilist.tronscan.org/api/token_trc20/transfers?limit=20&start=0&direction=1&address=" . urlencode($address); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); $response = curl_exec($ch); curl_close($ch); if (!$response) return false; $data = json_decode($response, true); if (!isset($data['token_transfers'])) return false; foreach ($data['token_transfers'] as $tx) { if ($tx['symbol'] !== 'USDT') continue; $amount = (float)$tx['quant'] / pow(10, $tx['tokenInfo']['tokenDecimal']); $tx_time = (int)($tx['block_ts'] / 1000); $order_ts = strtotime($order_time); // Match amount (with small tolerance for floating point) and time (must be after order) if (abs($amount - $target_amount) < 0.001 && $tx_time > $order_ts) { return $tx['transaction_id']; } } return false; } switch ($action) { case 'get_balance': $stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $balance = $stmt->fetchColumn(); echo json_encode(['code' => 0, 'balance' => number_format($balance, 2)]); break; case 'get_countries': $res = $api->getCountries(); if ($res && (int)$res['code'] === 0) { $data = $res['msg'] ?? $res['data'] ?? []; echo json_encode(['code' => 0, 'data' => $data]); } else { echo json_encode($res ?: ['code' => 500, 'msg' => 'Unknown API error']); } break; case 'get_services': $country = $_GET['country'] ?? ''; $service = $_GET['service'] ?? ''; $res = $api->getServices($country, $service); if ($res && (int)$res['code'] === 0) { $data = $res['msg'] ?? $res['data'] ?? []; foreach ($data as &$item) { if (isset($item['cost'])) { $item['cost'] = round((float)$item['cost'] * PRICE_MULTIPLIER, 2); } } echo json_encode(['code' => 0, 'data' => $data]); } else { echo json_encode($res ?: ['code' => 500, 'msg' => 'Unknown API error']); } break; case 'get_number': $service_id = $_GET['service_id'] ?? ''; $country_name = $_GET['country_name'] ?? '未知国家'; $service_name = $_GET['service_name'] ?? '未知项目'; $price = (float)($_GET['price'] ?? 0); if (!$service_id) { echo json_encode(['code' => 400, 'msg' => 'Service ID is required']); break; } $stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $balance = $stmt->fetchColumn(); if ($balance < $price) { echo json_encode(['code' => 400, 'msg' => '余额不足,请先充值']); break; } $res = $api->getNumber($service_id); if ($res && (int)$res['code'] === 0) { $pdo->beginTransaction(); try { $stmt = $pdo->prepare("UPDATE users SET balance = balance - ? WHERE id = ?"); $stmt->execute([$price, $_SESSION['user_id']]); $stmt = $pdo->prepare("INSERT INTO sms_orders (user_id, request_id, number, service_name, country_name, cost, status, expire_at) VALUES (?, ?, ?, ?, ?, ?, 'pending', DATE_ADD(NOW(), INTERVAL 10 MINUTE))"); $stmt->execute([$_SESSION['user_id'], $res['request_id'], $res['number'], $service_name, $country_name, $price]); $pdo->commit(); echo json_encode($res); } catch (Exception $e) { $pdo->rollBack(); echo json_encode(['code' => 500, 'msg' => 'Database error: ' . $e->getMessage()]); } } else { echo json_encode($res ?: ['code' => 500, 'msg' => 'API Error']); } break; case 'check_sms': $request_id = $_GET['request_id'] ?? ''; if (!$request_id) { echo json_encode(['code' => 400, 'msg' => 'Request ID is required']); break; } $res = $api->getSms($request_id); if ($res && (int)$res['code'] === 0 && ($res['msg'] ?? '') == 'success') { $stmt = $pdo->prepare("UPDATE sms_orders SET sms_content = ?, status = 'received' WHERE request_id = ?"); $stmt->execute([$res['sms_code'], $request_id]); } echo json_encode($res ?: ['code' => 500, 'msg' => 'API Error']); break; case 'release_number': $request_id = $_GET['request_id'] ?? ''; $stmt = $pdo->prepare("SELECT created_at, status FROM sms_orders WHERE request_id = ? AND user_id = ?"); $stmt->execute([$request_id, $_SESSION['user_id']]); $order = $stmt->fetch(); if (!$order) { echo json_encode(['code' => 404, 'msg' => 'Order not found']); break; } if ($order['status'] !== 'pending') { echo json_encode(['code' => 400, 'msg' => 'Invalid order status']); break; } $createdAt = strtotime($order['created_at']); if (time() - $createdAt < 120) { echo json_encode(['code' => 400, 'msg' => '获取号码不足2分钟,暂时无法手动释放。']); break; } $res = $api->setStatus($request_id, 'reject'); if ($res && (int)$res['code'] === 0) { $stmt = $pdo->prepare("UPDATE sms_orders SET status = 'canceled' WHERE request_id = ?"); $stmt->execute([$request_id]); } echo json_encode($res ?: ['code' => 500, 'msg' => 'API Error']); break; case 'get_active_orders': $stmt = $pdo->prepare("UPDATE sms_orders SET status = 'expired' WHERE status = 'pending' AND expire_at < NOW()"); $stmt->execute(); $stmt = $pdo->prepare("SELECT * FROM sms_orders WHERE user_id = ? AND status = 'pending' ORDER BY created_at DESC"); $stmt->execute([$_SESSION['user_id']]); echo json_encode(['code' => 0, 'data' => $stmt->fetchAll()]); break; case 'create_recharge': $amount = (float)($_POST['amount'] ?? 0); if ($amount < 10) { echo json_encode(['code' => 400, 'msg' => '最低充值金额为 10 USDT']); break; } $base = floor($amount); $random_decimal = rand(1, 99) / 100; $final_amount = $base + $random_decimal; $stmt = $pdo->prepare("INSERT INTO recharges (user_id, amount, txid, status) VALUES (?, ?, 'Auto-Detect', 'pending')"); $stmt->execute([$_SESSION['user_id'], $final_amount]); echo json_encode(['code' => 0, 'recharge_id' => $pdo->lastInsertId(), 'amount' => $final_amount]); break; case 'check_recharge_status': $recharge_id = $_GET['recharge_id'] ?? ''; if (!$recharge_id) { echo json_encode(['code' => 400, 'msg' => 'Recharge ID is required']); break; } $stmt = $pdo->prepare("SELECT * FROM recharges WHERE id = ? AND user_id = ?"); $stmt->execute([$recharge_id, $_SESSION['user_id']]); $recharge = $stmt->fetch(); if (!$recharge) { echo json_encode(['code' => 404, 'msg' => 'Order not found']); break; } if ($recharge['status'] === 'completed') { echo json_encode(['code' => 0, 'status' => 'completed']); exit; } // Try Auto-Detection $settings = $pdo->query("SELECT setting_key, setting_value FROM settings")->fetchAll(PDO::FETCH_KEY_PAIR); $trc20_address = $settings['usdt_trc20_address'] ?? ''; $txid = check_trc20_payment($trc20_address, $recharge['amount'], $recharge['created_at']); if ($txid) { $pdo->beginTransaction(); try { $stmt = $pdo->prepare("UPDATE recharges SET status = 'completed', txid = ? WHERE id = ?"); $stmt->execute([$txid, $recharge_id]); $stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?"); $stmt->execute([$recharge['amount'], $recharge['user_id']]); $pdo->commit(); echo json_encode(['code' => 0, 'status' => 'completed']); } catch (Exception $e) { $pdo->rollBack(); echo json_encode(['code' => 500, 'msg' => 'Detection error']); } } else { echo json_encode(['code' => 0, 'status' => 'pending']); } break; // --- Chat Actions --- case 'send_message': $message = trim($_POST['message'] ?? ''); $target_user_id = $_POST['user_id'] ?? $_SESSION['user_id']; if (!$message) { echo json_encode(['code' => 400, 'msg' => 'Message is empty']); break; } $sender = 'user'; // Check if current user is admin $stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $currentUser = $stmt->fetch(); if ($currentUser['role'] === 'admin') { $sender = 'admin'; } $stmt = $pdo->prepare("INSERT INTO support_messages (user_id, sender, message, is_read) VALUES (?, ?, ?, 0)"); $stmt->execute([$target_user_id, $sender, $message]); echo json_encode(['code' => 0, 'msg' => 'Sent']); break; case 'get_messages': $target_user_id = $_GET['user_id'] ?? $_SESSION['user_id']; // Security: non-admins can only see their own messages $stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $currentUser = $stmt->fetch(); $isAdmin = ($currentUser['role'] === 'admin'); if (!$isAdmin && $target_user_id != $_SESSION['user_id']) { echo json_encode(['code' => 403, 'msg' => 'Forbidden']); break; } // Mark as read logic if ($isAdmin && $target_user_id != $_SESSION['user_id']) { // Admin is reading user messages $stmt = $pdo->prepare("UPDATE support_messages SET is_read = 1 WHERE user_id = ? AND sender = 'user'"); $stmt->execute([$target_user_id]); } else if (!$isAdmin && $target_user_id == $_SESSION['user_id']) { // User is reading admin messages $stmt = $pdo->prepare("UPDATE support_messages SET is_read = 1 WHERE user_id = ? AND sender = 'admin'"); $stmt->execute([$target_user_id]); } $stmt = $pdo->prepare("SELECT * FROM support_messages WHERE user_id = ? ORDER BY created_at ASC"); $stmt->execute([$target_user_id]); echo json_encode(['code' => 0, 'data' => $stmt->fetchAll()]); break; case 'get_chat_users': // Admin only $stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $currentUser = $stmt->fetch(); if ($currentUser['role'] !== 'admin') { echo json_encode(['code' => 403, 'msg' => 'Forbidden']); break; } $stmt = $pdo->query(" SELECT u.id, u.username, m.message as last_message, m.created_at as last_time, (SELECT COUNT(*) FROM support_messages WHERE user_id = u.id AND sender = 'user' AND is_read = 0) as unread_count FROM users u JOIN ( SELECT user_id, MAX(created_at) as max_time FROM support_messages GROUP BY user_id ) last_msg ON u.id = last_msg.user_id JOIN support_messages m ON m.user_id = u.id AND m.created_at = last_msg.max_time ORDER BY m.created_at DESC "); echo json_encode(['code' => 0, 'data' => $stmt->fetchAll()]); break; case 'check_new_messages': // Admin only $stmt = $pdo->prepare("SELECT role FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $currentUser = $stmt->fetch(); if ($currentUser['role'] !== 'admin') { echo json_encode(['code' => 403, 'msg' => 'Forbidden']); break; } $stmt = $pdo->query("SELECT COUNT(*) FROM support_messages WHERE sender = 'user' AND is_read = 0"); $count = $stmt->fetchColumn(); echo json_encode(['code' => 0, 'unread_total' => $count]); break; default: echo json_encode(['code' => 404, 'msg' => 'Action not found']); break; }