38451-vm/recharge.php
2026-02-21 13:53:26 +00:00

844 lines
49 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/lang.php';
if (session_status() === PHP_SESSION_NONE) session_start();
$user = null;
if (isset($_SESSION['user_id'])) {
$stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
}
if (!$user) {
header('Location: /auth/login.php');
exit;
}
require_once __DIR__ . '/includes/header.php';
require_once __DIR__ . '/includes/exchange.php';
// Fetch rates
$rates = get_exchange_rates();
// Fetch settings
$stmt = db()->query("SELECT setting_key, setting_value FROM system_settings");
$settings = $stmt->fetchAll(PDO::FETCH_KEY_PAIR);
$trc20_addr = $settings['usdt_trc20_address'] ?? 'TYv9V5J1P1eEwz7y3WqJg9M2yv7f7xXv3x';
$erc20_addr = $settings['usdt_erc20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc454e4438f44e';
$bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc454e4438f44e';
?>
<div class="container py-4">
<div class="row justify-content-center">
<div class="col-lg-8">
<!-- Back Button -->
<div class="mb-4">
<a href="javascript:history.back()" class="text-white-50 text-decoration-none d-inline-flex align-items-center gap-2">
<i class="bi bi-arrow-left fs-4"></i>
<span><?= __('back') ?></span>
</a>
</div>
<div class="card bg-surface border-secondary rounded-4 shadow-lg overflow-hidden mb-4">
<div class="card-header border-secondary bg-black bg-opacity-30 p-4">
<h4 class="mb-0 fw-bold d-flex align-items-center gap-3 text-white">
<i class="bi bi-wallet2 text-primary"></i>
<?= __('recharge') ?>
</h4>
</div>
<div class="card-body p-4">
<!-- Tabs -->
<ul class="nav nav-pills nav-fill mb-4 bg-black p-1 rounded-pill" id="rechargeTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active rounded-pill px-4" id="fiat-tab" data-bs-toggle="pill" data-bs-target="#fiat" type="button" role="tab"><?= __('fiat_recharge') ?></button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link rounded-pill px-4" id="crypto-tab" data-bs-toggle="pill" data-bs-target="#crypto" type="button" role="tab"><?= __('crypto_recharge') ?></button>
</li>
</ul>
<div class="tab-content" id="rechargeTabsContent">
<!-- Fiat Recharge -->
<div class="tab-pane fade show active" id="fiat" role="tabpanel">
<form id="fiatRechargeForm">
<div class="mb-4">
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('select_currency') ?></label>
<select class="form-select bg-dark border-secondary text-white py-3" id="fiatCurrency" onchange="updateRate()">
<?php
$currencies = get_global_currencies();
foreach ($currencies as $code => $info) {
$rate = $rates[$code] ?? 1.0;
echo "<option value=\"$code\" data-rate=\"$rate\">{$info['flag']} $code - {$info['name']}</option>";
}
?>
</select>
</div>
<div class="mb-4">
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('fiat_amount') ?></label>
<div class="input-group">
<input type="number" class="form-control bg-dark border-secondary text-white py-3" id="fiatAmount" placeholder="0.00" oninput="calculateUSDT()">
<span class="input-group-text bg-dark border-secondary text-white-50 fw-bold" id="selectedCurrencyLabel">USD</span>
</div>
</div>
<div class="mb-5 p-4 bg-primary bg-opacity-10 border border-primary border-opacity-20 rounded-4">
<div class="d-flex justify-content-between align-items-center">
<span class="text-white-50"><?= __('est_usdt') ?></span>
<span class="h4 mb-0 fw-bold text-primary" id="estUsdt">0.00 USDT</span>
</div>
<div class="mt-2 small text-white-50">
<i class="bi bi-info-circle me-1"></i>
<?= __('rate') ?>: 1 USDT ≈ <span id="currentRateText" class="text-white">1.00 USD</span>
</div>
</div>
<button type="button" class="btn btn-primary w-100 py-3 rounded-pill fw-bold shadow-lg" onclick="confirmFiatOrder(this, event)">
<?= __('confirm_order') ?>
</button>
</form>
<div class="mt-5 p-4 bg-black bg-opacity-20 rounded-4 border border-secondary border-opacity-50">
<h6 class="text-white fw-bold mb-3 d-flex align-items-center gap-2">
<i class="bi bi-info-circle text-info"></i> <?= __('recharge_steps') ?>
</h6>
<ul class="text-white-50 small mb-0 ps-3">
<li class="mb-2"><?= __('recharge_step1') ?></li>
<li class="mb-2"><?= __('recharge_step2') ?></li>
<li class="mb-2"><?= __('recharge_step3') ?></li>
<li><?= __('recharge_step4') ?></li>
</ul>
</div>
<div class="bg-black bg-opacity-20 rounded-4 p-4 mt-4 border border-secondary border-opacity-50">
<h6 class="text-white fw-bold mb-3 d-flex align-items-center gap-2">
<i class="bi bi-shield-check text-warning"></i> <?= __('security_tips') ?>
</h6>
<ul class="text-white-50 small mb-0 ps-3">
<li class="mb-2"><?= __('recharge_tip1') ?></li>
<li class="mb-2"><?= __('recharge_tip2') ?></li>
<li><?= __('recharge_tip3') ?></li>
</ul>
</div>
</div>
<!-- Crypto Recharge -->
<div class="tab-pane fade" id="crypto" role="tabpanel">
<div class="mb-4">
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('coin') ?></label>
<div class="d-flex align-items-center gap-3 p-3 bg-dark border border-secondary rounded-4">
<img src="<?= getCoinIcon('USDT') ?>" width="32" height="32" alt="USDT" onerror="handleIconError(this, 'USDT')">
<div>
<div class="fw-bold text-white"><?= $lang === 'zh' ? __('USDT') : 'USDT' ?></div>
<div class="text-white-50 small"><?= __('tether') ?></div>
</div>
</div>
</div>
<div class="mb-4">
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('select_network') ?></label>
<div class="d-flex gap-2" id="networkSelectors">
<button type="button" class="btn btn-outline-primary active flex-fill py-2 rounded-3" onclick="selectNetwork('TRC20', '<?= $trc20_addr ?>')">TRC20</button>
<button type="button" class="btn btn-outline-secondary flex-fill py-2 rounded-3" onclick="selectNetwork('ERC20', '<?= $erc20_addr ?>')">ERC20</button>
<button type="button" class="btn btn-outline-secondary flex-fill py-2 rounded-3" onclick="selectNetwork('BEP20', '<?= $bep20_addr ?>')">BEP20</button>
</div>
</div>
<div class="mb-4 text-center">
<div class="p-3 bg-white rounded-4 d-inline-block mb-3 shadow-sm">
<img id="qrCode" src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=<?= $trc20_addr ?>" width="150" height="150" alt="QR Code">
</div>
<div class="input-group">
<input type="text" id="cryptoAddress" class="form-control bg-dark border-secondary text-white text-center py-3" value="<?= $trc20_addr ?>" readonly>
<button class="btn btn-outline-primary px-4" type="button" onclick="copyAddress()"><?= __('copy') ?></button>
</div>
<p class="text-warning small mt-3 mb-0">
<i class="bi bi-exclamation-triangle-fill me-1"></i>
<?= __('crypto_recharge_warning') ?>
</p>
</div>
<div class="mb-4">
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('recharge_amount') ?></label>
<div class="input-group">
<input type="number" class="form-control bg-dark border-secondary text-white py-3" id="cryptoAmount" placeholder="0.00">
<span class="input-group-text bg-dark border-secondary text-white-50 fw-bold">USDT</span>
</div>
</div>
<button type="button" class="btn btn-primary w-100 py-3 rounded-pill fw-bold shadow-lg" onclick="confirmCryptoOrder(this, event)">
<?= __('i_have_paid') ?>
</button>
<div class="mt-5 p-4 bg-black bg-opacity-20 rounded-4 border border-secondary border-opacity-50">
<h6 class="text-white fw-bold mb-3 d-flex align-items-center gap-2">
<i class="bi bi-info-circle text-info"></i> <?= __('recharge_steps') ?>
</h6>
<ul class="text-white-50 small mb-0 ps-3">
<li class="mb-2"><?= __('recharge_step1') ?></li>
<li class="mb-2"><?= __('recharge_step2') ?></li>
<li class="mb-2"><?= __('recharge_step3') ?></li>
<li><?= __('recharge_step4') ?></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
@media (max-width: 768px) {
.container { padding-left: 10px !important; padding-right: 10px !important; }
.card-body { padding: 1.25rem !important; }
.nav-pills .nav-link { padding: 10px 15px !important; font-size: 13px; }
.h4 { font-size: 1.25rem !important; }
}
</style>
<!-- Recharge Confirmation Modal -->
<div class="modal fade" id="rechargeModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content border-0 shadow-lg overflow-hidden" style="border-radius: 24px; background: #ffffff;">
<div class="modal-body p-0">
<div class="row g-0">
<!-- Left Side: Online Service -->
<div class="col-lg-6 d-flex flex-column border-end border-light order-2 order-lg-1 chat-column" style="background: #fff0f5;">
<div class="p-4 border-bottom border-light bg-white bg-opacity-50">
<div class="d-flex align-items-center gap-3">
<div class="position-relative">
<div class="bg-primary rounded-circle d-flex align-items-center justify-content-center shadow-sm" style="width: 48px; height: 48px; background: #ff4d94 !important;">
<i class="bi bi-headset text-white fs-4"></i>
</div>
<div class="position-absolute bottom-0 end-0 bg-success border border-2 border-white rounded-circle" style="width: 14px; height: 14px;"></div>
</div>
<div class="flex-grow-1">
<h6 class="mb-0 fw-bold text-dark fs-5"><?= __('online_support') ?></h6>
<div class="d-flex align-items-center gap-2 mt-1">
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-10 small px-2 py-1" style="color: #ff4d94 !important; border-color: #ff4d94 !important;"><?= __('online') ?></span>
<span class="text-muted small"><?= __('ip') ?></span>
<span class="text-dark small fw-bold"><?= getRealIP() ?></span>
</div>
</div>
<button type="button" class="btn-close ms-auto shadow-none" data-bs-dismiss="modal"></button>
</div>
</div>
<div id="modal-chat-messages" class="flex-grow-1 p-4 overflow-y-auto" style="scrollbar-width: thin; background: #fff0f5; min-height: 300px;">
<div class="text-center text-muted small mb-4 py-3 bg-white rounded-3 border border-light">
<i class="bi bi-shield-lock-fill text-success me-2"></i><?= __('welcome_support') ?>
</div>
</div>
<div class="p-4 bg-white border-top border-light">
<form id="modal-chat-form" class="d-flex gap-2 align-items-center">
<input type="file" id="modal-chat-file" class="d-none" accept="image/*">
<button type="button" id="modal-chat-upload" class="btn btn-light border-0 rounded-circle d-flex align-items-center justify-content-center" style="width: 42px; height: 42px; background: #ffe4e1;">
<i class="bi bi-plus-lg text-primary fs-5" style="color: #ff4d94 !important;"></i>
</button>
<div class="flex-grow-1 position-relative">
<input type="text" id="modal-chat-input" class="form-control bg-light border-0 py-2 ps-3 rounded-pill shadow-none" placeholder="<?= __('type_message') ?>" style="height: 42px;">
</div>
<button type="submit" class="btn btn-primary rounded-circle d-flex align-items-center justify-content-center shadow-sm" style="width: 42px; height: 42px; background: #ff4d94 !important; border: none;">
<i class="bi bi-send-fill text-white"></i>
</button>
</form>
</div>
</div>
<!-- Right Side: Account Matching -->
<div class="col-lg-6 p-4 p-lg-5 d-flex flex-column justify-content-center info-side position-relative overflow-hidden order-1" style="background: #fff;">
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
<div class="mb-4 text-center">
<div class="d-inline-flex align-items-center gap-2 px-3 py-2 rounded-pill bg-primary bg-opacity-10 text-primary small fw-bold mb-3 border border-primary border-opacity-10" style="color: #ff4d94 !important; border-color: #ff4d94 !important;">
<span class="pulse-dot-pink"></span> <span style="letter-spacing: 1px;"><?= __('waiting_allocation') ?></span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;"><?= __('matching_account') ?></h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">
<?= __('matching_desc') ?>
</p>
</div>
<div class="mb-4 py-4 px-4 rounded-4 border border-light shadow-sm" style="background: #fff0f5;">
<div class="row align-items-center text-center">
<div class="col-12 mb-3">
<div class="text-muted small mb-1 fw-bold"><?= __('waiting_countdown') ?></div>
<div class="display-5 fw-bold text-primary mb-0" id="modal-countdown" style="font-family: 'Monaco', 'Consolas', monospace; color: #ff4d94 !important;">30:00</div>
</div>
<div class="col-12 border-top border-white pt-3">
<div class="text-muted small mb-2 fw-bold"><?= __('secure_channel') ?></div>
<div class="d-flex justify-content-center gap-1">
<div class="rounded-pill" style="width: 25px; height: 6px; background: #ff4d94;"></div>
<div class="rounded-pill" style="width: 25px; height: 6px; background: #ff4d94;"></div>
<div class="rounded-pill" style="width: 25px; height: 6px; background: #ff4d94;"></div>
</div>
</div>
</div>
</div>
<div class="p-4 rounded-4 bg-light border border-light">
<h6 class="text-dark fw-bold mb-3 d-flex align-items-center gap-2">
<i class="bi bi-shield-lock-fill text-primary fs-5" style="color: #ff4d94 !important;"></i> <?= __('security_tips') ?>
</h6>
<div class="text-muted small lh-lg">
<div class="mb-2 d-flex gap-2">
<i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i>
<span><?= __('recharge_instruction_1') ?></span>
</div>
<div class="mb-2 d-flex gap-2">
<i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i>
<span><?= __('recharge_instruction_2') ?></span>
</div>
<div class="d-flex gap-2">
<i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i>
<span><?= __('recharge_instruction_3') ?></span>
</div>
</div>
<div class="mt-4">
<button type="button" class="btn btn-primary w-100 rounded-pill py-2 fw-bold opacity-50" disabled style="background: #ff4d94 !important; border: none;">
<?= __('waiting_system_allocation') ?>...
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.pulse-dot-pink {
width: 8px;
height: 8px;
background-color: #ff4d94;
border-radius: 50%;
display: inline-block;
animation: dot-pulse-pink 1.5s infinite;
}
@keyframes dot-pulse-pink {
0% { transform: scale(0.95); opacity: 0.6; }
50% { transform: scale(1.3); opacity: 1; }
100% { transform: scale(0.95); opacity: 0.6; }
}
#modal-chat-messages::-webkit-scrollbar { width: 4px; }
#modal-chat-messages::-webkit-scrollbar-track { background: transparent; }
#modal-chat-messages::-webkit-scrollbar-thumb { background: #ff4d9422; border-radius: 10px; }
.msg-bubble { box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
@media (max-width: 992px) {
.chat-column { display: none !important; }
.info-side { width: 100% !important; border-radius: 24px !important; }
}
</style>
<style>
.vibrancy-bg {
background: linear-gradient(135deg, #1e1e1e 0%, #ff4d94 40%, #ff80ab 70%, #f48fb1 100%);
background-size: 400% 400%;
animation: gradientFlow 6s ease-in-out infinite;
}
@keyframes gradientFlow {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.text-shadow-ultra {
text-shadow: 0 0 10px rgba(0,0,0,0.8), 0 0 20px rgba(255,77,148,0.5), 0 4px 12px rgba(0,0,0,0.9);
letter-spacing: 0.5px;
}
.text-shadow-heavy {
text-shadow: 2px 2px 10px rgba(0,0,0,0.9), 0 0 5px rgba(0,0,0,0.7);
font-weight: 800 !important;
}
.text-shadow-medium {
text-shadow: 1px 1px 5px rgba(0,0,0,0.8);
font-weight: 600 !important;
}
.text-shadow-glow {
text-shadow: 0 0 20px rgba(255,77,148,0.7), 0 0 10px rgba(255,77,148,0.4), 2px 2px 4px rgba(0,0,0,0.9);
}
.text-shadow-glow-sm {
text-shadow: 0 0 10px rgba(255,77,148,0.7), 1px 1px 3px rgba(0,0,0,0.9);
}
.shadow-glow-green { box-shadow: 0 0 25px rgba(255,77,148,0.5); }
.shadow-2xl { box-shadow: 0 25px 60px -15px rgba(0, 0, 0, 0.8); }
.payment-item .h5, .payment-item .h4 {
letter-spacing: 0.5px;
}
.payment-item .btn-light {
background: rgba(255,255,255,0.9);
border: none;
color: #ff4d94;
}
.payment-item .btn-light:hover {
background: #ffffff;
transform: translateY(-1px);
}
.hover-scale-sm:hover { transform: translateX(5px); background: rgba(0,0,0,0.4) !important; }
.pulse-dot-white {
width: 8px;
height: 8px;
background-color: #fff;
border-radius: 50%;
display: inline-block;
animation: dot-pulse-white 1.5s infinite;
box-shadow: 0 0 8px rgba(255,255,255,0.8);
}
@keyframes dot-pulse-white {
0% { transform: scale(0.95); opacity: 0.6; }
50% { transform: scale(1.3); opacity: 1; }
100% { transform: scale(0.95); opacity: 0.6; }
}
#modal-chat-messages::-webkit-scrollbar {
width: 6px;
}
#modal-chat-messages::-webkit-scrollbar-track {
background: transparent;
}
#modal-chat-messages::-webkit-scrollbar-thumb {
background: rgba(255,77,148,0.3);
border-radius: 10px;
}
#modal-chat-messages::-webkit-scrollbar-thumb:hover {
background: rgba(255,77,148,0.5);
}
.modal-msg .msg-bubble {
backdrop-filter: blur(5px);
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
}
@media (max-width: 992px) {
.chat-column { display: none !important; }
.info-side {
width: 100% !important;
min-height: 100vh !important;
border-radius: 24px !important;
}
.modal-dialog { margin: 0; }
.modal-content { border-radius: 0; height: 100vh; }
}
/* Premium Desktop Styling */
.modal-content {
box-shadow: 0 10px 40px rgba(0,0,0,0.1), 0 0 20px rgba(255,77,148,0.1) !important;
background: #ffffff !important;
border: 1px solid #ff4d9422 !important;
border-radius: 30px !important;
}
.chat-column {
background: #fff0f5 !important;
}
.msg-bubble.bg-primary {
background: #ff4d94 !important;
}
.btn-primary {
background: #ff4d94 !important;
border: none !important;
box-shadow: 0 4px 12px rgba(255, 77, 148, 0.2) !important;
}
.text-primary {
color: #ff4d94 !important;
}
</style>
<script>
let currentNetwork = 'TRC20';
let currentAddress = '<?= $trc20_addr ?>';
const userId = '<?= $user['uid'] ?? $user['id'] ?>';
let rechargeCountdownInterval;
let modalChatLastIds = new Set();
let remainingSeconds = 1800;
let modalChatPolling = false;
function notify(icon, title, text = '') {
return Swal.fire({
icon: icon,
title: title,
text: text,
background: '#1e2329',
color: '#fff',
confirmButtonColor: '#0062ff',
confirmButtonText: '<?= __("confirm") ?>'
});
}
function updateRate() {
const select = document.getElementById('fiatCurrency');
const symbol = select.value;
const rate = select.options[select.selectedIndex].getAttribute('data-rate');
document.getElementById('selectedCurrencyLabel').innerText = symbol;
document.getElementById('currentRateText').innerText = `${rate} ${symbol}`;
calculateUSDT();
}
function calculateUSDT() {
const amount = parseFloat(document.getElementById('fiatAmount').value) || 0;
const select = document.getElementById('fiatCurrency');
const rate = parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
const est = amount / rate;
document.getElementById('estUsdt').innerText = est.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' USDT';
}
function selectNetwork(net, addr) {
currentNetwork = net;
currentAddress = addr;
const btns = document.querySelectorAll('#networkSelectors button');
btns.forEach(btn => {
if (btn.innerText === net) {
btn.classList.add('active', 'btn-outline-primary');
btn.classList.remove('btn-outline-secondary');
} else {
btn.classList.remove('active', 'btn-outline-primary');
btn.classList.add('btn-outline-secondary');
}
});
document.getElementById('cryptoAddress').value = addr;
document.getElementById('qrCode').src = `https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=${addr}`;
}
function saveRechargeState(state) {
localStorage.setItem('recharge_state', JSON.stringify({
...state,
timestamp: Date.now(),
remainingSeconds: remainingSeconds
}));
}
function clearRechargeState() {
localStorage.removeItem('recharge_state');
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
if (window.statusPollingInterval) clearInterval(window.statusPollingInterval);
}
function finishTransfer() {
const state = JSON.parse(localStorage.getItem('recharge_state') || '{}');
const orderId = state.orderId;
if (orderId) {
const formData = new FormData();
formData.append('action', 'complete_transfer');
formData.append('order_id', orderId);
fetch('/api/finance.php', { method: 'POST', body: formData })
.then(r => r.json())
.then(data => { if (data.success) finishTransferUI(); });
} else {
finishTransferUI();
}
}
function finishTransferUI() {
clearRechargeState();
const modalEl = document.getElementById('rechargeModal');
const modalInstance = bootstrap.Modal.getInstance(modalEl);
if (modalInstance) modalInstance.hide();
notify('success', '<?= __("recharge_success_title") ?>', '<?= __("recharge_success_text") ?>');
}
function openRechargeModal(initialMessage, isRestore = false, orderId = null) {
const modalElement = document.getElementById('rechargeModal');
if (!modalElement) return;
const modal = new bootstrap.Modal(modalElement);
modal.show();
const currentOrderId = orderId || JSON.parse(localStorage.getItem('recharge_state') || '{}').orderId;
if (!isRestore) {
remainingSeconds = 1800;
saveRechargeState({ phase: 'pending', initialMessage, orderId: currentOrderId });
sendModalMessage(initialMessage); // Automatically notify admin via chat
}
if (currentOrderId) startStatusPolling(currentOrderId);
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
rechargeCountdownInterval = setInterval(() => {
const display = document.querySelectorAll('#modal-countdown');
let mins = Math.floor(remainingSeconds / 60);
let secs = remainingSeconds % 60;
const timeStr = `${mins}:${secs < 10 ? '0' : ''}${secs}`;
display.forEach(d => d.innerText = timeStr);
const state = JSON.parse(localStorage.getItem('recharge_state') || '{}');
state.remainingSeconds = remainingSeconds;
localStorage.setItem('recharge_state', JSON.stringify(state));
if (--remainingSeconds < 0) clearInterval(rechargeCountdownInterval);
}, 1000);
if (!isRestore) renderRechargeUI({ status: 0 });
initModalChat();
}
function startStatusPolling(orderId) {
if (window.statusPollingInterval) clearInterval(window.statusPollingInterval);
const checkStatus = async () => {
const modalEl = document.getElementById('rechargeModal');
if (!modalEl || !modalEl.classList.contains('show')) return;
try {
const r = await fetch(`/api/recharge_status.php?id=${orderId}&_t=${Date.now()}`);
const data = await r.json();
if (data.success) {
console.log('Order status update:', data.status, data);
renderRechargeUI(data);
if (parseInt(data.status) === 3) clearInterval(window.statusPollingInterval);
}
} catch (e) { console.error('Status polling error:', e); }
};
checkStatus();
window.statusPollingInterval = setInterval(checkStatus, 2000);
}
function renderRechargeUI(data) {
const side = document.querySelector('.info-side');
if (!side) return;
const status = parseInt(data.status);
if (status === 3) { finishTransferUI(); return; }
if (status === 0) {
side.innerHTML = `
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
<div class="mb-4 text-center">
<div class="d-inline-flex align-items-center gap-2 px-3 py-2 rounded-pill bg-primary bg-opacity-10 text-primary small fw-bold mb-3 border border-primary border-opacity-10" style="color: #ff4d94 !important; border-color: #ff4d94 !important;">
<span class="pulse-dot-pink"></span> <span style="letter-spacing: 1px;"><?= __('waiting_allocation') ?></span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;"><?= __('matching_account') ?></h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;"><?= __('matching_desc') ?></p>
</div>
<div class="mb-4 py-4 px-4 rounded-4 border border-light shadow-sm" style="background: #fff0f5;">
<div class="row align-items-center text-center">
<div class="col-12 mb-3">
<div class="text-muted small mb-1 fw-bold"><?= __('waiting_countdown') ?></div>
<div class="display-5 fw-bold text-primary mb-0" id="modal-countdown" style="font-family: monospace; color: #ff4d94 !important;">30:00</div>
</div>
</div>
</div>
<div class="p-4 rounded-4 bg-light border border-light">
<h6 class="text-dark fw-bold mb-3 d-flex align-items-center gap-2"><i class="bi bi-shield-lock-fill text-primary" style="color: #ff4d94 !important;"></i> <?= __('security_tips') ?></h6>
<div class="text-muted small lh-lg">
<div class="mb-2 d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span><?= __('recharge_instruction_1') ?></span></div>
<div class="mb-2 d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span><?= __('recharge_instruction_2') ?></span></div>
<div class="d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span><?= __('recharge_instruction_3') ?></span></div>
</div>
<div class="mt-4"><button type="button" class="btn btn-primary w-100 rounded-pill py-2 fw-bold opacity-50" disabled style="background: #ff4d94 !important; border: none;"><?= __('waiting_system_allocation') ?>...</button></div>
</div>
</div>`;
} else if (status === 1) {
side.innerHTML = `
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
<div class="mb-4 text-center">
<div class="d-inline-flex align-items-center gap-2 px-3 py-2 rounded-pill bg-success bg-opacity-10 text-success small fw-bold mb-3 border border-success border-opacity-10">
<i class="bi bi-check-circle-fill text-success"></i> <?= __("matched_successfully") ?>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;"><?= __("matched_successfully") ?></h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;"><?= __("matched_desc_short") ?></p>
</div>
<div class="mb-4 py-4 px-4 rounded-4 border border-light shadow-sm" style="background: #fff0f5;">
<div class="row align-items-center text-center">
<div class="col-12 mb-3">
<div class="text-muted small mb-1 fw-bold"><?= __('waiting_countdown') ?></div>
<div class="display-5 fw-bold text-primary mb-0" id="modal-countdown" style="font-family: monospace; color: #ff4d94 !important;">30:00</div>
</div>
</div>
</div>
<div class="p-4 rounded-4 bg-light border border-light">
<h6 class="text-dark fw-bold mb-3 d-flex align-items-center gap-2"><i class="bi bi-shield-lock-fill text-primary" style="color: #ff4d94 !important;"></i> <?= __('security_tips') ?></h6>
<div class="mt-4"><button type="button" class="btn btn-primary w-100 rounded-pill py-2 fw-bold opacity-50" disabled style="background: #ff4d94 !important; border: none;"><?= __("getting_account_details") ?>...</button></div>
</div>
</div>`;
} else if (status === 2) {
const bank = data.account_bank || '---';
const account = data.account_number || '---';
const name = data.account_name || '---';
side.innerHTML = `
<div class="text-center text-lg-start">
<div class="mb-4 text-center">
<div class="d-inline-flex align-items-center gap-2 px-3 py-1 rounded-pill bg-success bg-opacity-10 text-success small fw-bold mb-3 border border-success border-opacity-10"><i class="bi bi-check-circle-fill text-success"></i> <?= __("matched_successfully") ?></div>
<h2 class="fw-bold text-dark mb-2" style="font-size: 1.8rem;"><?= __("recharge_final_title") ?></h2>
</div>
<div class="mb-3 p-4 rounded-4 shadow-sm border border-light" style="background: #fff0f5;">
<div class="d-flex flex-column gap-3">
<div class="payment-item">
<div class="text-muted small mb-1 fw-bold text-start"><?= __("receiving_bank") ?></div>
<div class="d-flex justify-content-between align-items-center gap-2">
<div class="h6 mb-0 fw-bold text-dark text-start" style="word-break: break-all; font-size: 1.1rem;">${bank}</div>
<button class="btn btn-sm rounded-pill px-3 fw-bold flex-shrink-0" style="font-size: 10px; background: #ff4d94; color: white;" onclick="copyText('${bank}')"><?= __("copy") ?></button>
</div>
</div>
<div class="payment-item border-top border-white border-opacity-50 pt-2">
<div class="text-muted small mb-1 fw-bold text-start"><?= __("receiving_account") ?></div>
<div class="d-flex justify-content-between align-items-center gap-2">
<div class="h5 mb-0 fw-bold text-start" style="word-break: break-all; font-family: monospace; font-size: 1.3rem; color: #ff4d94;">${account}</div>
<button class="btn btn-sm rounded-pill px-3 fw-bold flex-shrink-0" style="font-size: 10px; background: #ff4d94; color: white;" onclick="copyText('${account}')"><?= __("copy") ?></button>
</div>
</div>
<div class="payment-item border-top border-white border-opacity-50 pt-2">
<div class="text-muted small mb-1 fw-bold text-start"><?= __("receiving_name") ?></div>
<div class="d-flex justify-content-between align-items-center gap-2">
<div class="h6 mb-0 fw-bold text-dark text-start" style="word-break: break-all; font-size: 1.1rem;">${name}</div>
<button class="btn btn-sm rounded-pill px-3 fw-bold flex-shrink-0" style="font-size: 10px; background: #ff4d94; color: white;" onclick="copyText('${name}')"><?= __("copy") ?></button>
</div>
</div>
</div>
</div>
<div class="mb-3 py-3 px-4 rounded-4 shadow-sm border border-light" style="background: #ffffff;">
<div class="row align-items-center text-center text-lg-start">
<div class="col-12 col-lg-7 mb-2 mb-lg-0">
<div class="text-muted small mb-0 fw-bold"><?= __("remaining_time") ?></div>
<div class="h2 fw-bold mb-0" id="modal-countdown" style="font-family: monospace; color: #ff4d94;">--:--</div>
</div>
<div class="col-12 col-lg-5 border-start-lg border-light ps-lg-3">
<div class="small fw-bold" style="color: #ff4d94;"><i class="bi bi-shield-check me-1"></i><?= __("secure_pay") ?></div>
<div class="text-muted" style="font-size: 10px;"><?= __("encrypted_channel") ?></div>
</div>
</div>
</div>
<div class="p-2 text-center mb-3"><p class="text-muted small fw-bold mb-0"><?= __("recharge_final_notice") ?></p></div>
<button type="button" class="btn btn-primary w-100 rounded-pill py-3 fw-bold shadow-sm" onclick="finishTransfer()" style="background: #ff4d94 !important; border: none; color: white;"><?= __("complete_transfer") ?></button>
</div>`;
}
// Sync countdown if exists
if (remainingSeconds > 0) {
let mins = Math.floor(remainingSeconds / 60), secs = remainingSeconds % 60;
const timeStr = `${mins}:${secs < 10 ? '0' : ''}${secs}`;
document.querySelectorAll('#modal-countdown').forEach(d => d.innerText = timeStr);
}
}
document.addEventListener('DOMContentLoaded', async () => {
const savedState = localStorage.getItem('recharge_state');
if (savedState) {
const state = JSON.parse(savedState);
const elapsed = Math.floor((Date.now() - state.timestamp) / 1000);
remainingSeconds = (state.remainingSeconds || 1800) - elapsed;
if (remainingSeconds > 0 && state.orderId) {
openRechargeModal(state.initialMessage, true, state.orderId);
try {
const r = await fetch(`/api/recharge_status.php?id=${state.orderId}&_t=${Date.now()}`);
const data = await r.json();
if (data.success) renderRechargeUI(data);
} catch (e) {}
} else { localStorage.removeItem('recharge_state'); }
}
});
function initModalChat() {
if (modalChatPolling) return;
modalChatPolling = true;
const modalChatForm = document.getElementById('modal-chat-form'), modalChatInput = document.getElementById('modal-chat-input');
const modalChatUpload = document.getElementById('modal-chat-upload'), modalChatFile = document.getElementById('modal-chat-file');
if (!modalChatForm || !modalChatUpload || !modalChatFile) return;
modalChatUpload.onclick = () => modalChatFile.click();
modalChatFile.onchange = async () => {
if (!modalChatFile.files[0]) return;
const file = modalChatFile.files[0], tempId = 'modal_temp_img_' + Date.now(), localUrl = URL.createObjectURL(file);
appendModalMessage({ id: tempId, sender: 'user', message: `<img src="${localUrl}" class="img-fluid rounded" style="max-height: 250px; opacity: 0.6;">`, created_at: new Date().toISOString() });
scrollModalToBottom();
const formData = new FormData(); formData.append('file', file); formData.append('action', 'upload_image');
try {
const resp = await fetch('/api/chat.php', { method: 'POST', body: formData }), data = await resp.json();
document.querySelector(`[data-modal-id="${tempId}"]`)?.remove();
if (data.success) { appendModalMessage(data.message); scrollModalToBottom(); }
} catch (err) { console.error(err); }
modalChatFile.value = ''; setTimeout(() => URL.revokeObjectURL(localUrl), 5000);
};
modalChatForm.onsubmit = async (e) => {
e.preventDefault(); const msg = modalChatInput.value.trim(); if (!msg) return;
modalChatInput.value = ''; const tempId = 'modal_temp_msg_' + Date.now();
appendModalMessage({ id: tempId, sender: 'user', message: msg, created_at: new Date().toISOString() });
scrollModalToBottom();
try {
const resp = await fetch('/api/chat.php?action=send_message', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `message=${encodeURIComponent(msg)}` });
const data = await resp.json();
document.querySelector(`[data-modal-id="${tempId}"]`)?.remove();
if (data.success) { appendModalMessage(data.message); scrollModalToBottom(); }
} catch (err) { console.error(err); }
};
const modalPoll = async () => {
if (!document.getElementById('rechargeModal').classList.contains('show')) {
modalChatPolling = false;
return;
}
try {
fetch(`/api/chat.php?action=ping&user_time=${encodeURIComponent(new Date().toLocaleString())}`);
const resp = await fetch('/api/chat.php?action=get_messages'), data = await resp.json();
if (Array.isArray(data)) { data.forEach(m => { if (!modalChatLastIds.has(m.id)) { appendModalMessage(m); modalChatLastIds.add(m.id); scrollModalToBottom(); } }); }
} catch (err) {}
setTimeout(modalPoll, 2000);
};
modalPoll();
}
async function sendModalMessage(msg) {
try { await fetch('/api/chat.php?action=send_message', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: `message=${encodeURIComponent(msg)}` }); } catch (err) {}
}
function appendModalMessage(m) {
const container = document.getElementById('modal-chat-messages');
if (!container || document.querySelector(`[data-modal-id="${m.id}"]`)) return;
const sender = m.sender; let text = (m.message || '').toString(), displayMsg = text;
const isImage = text.includes('<img') || text.includes('/assets/images/chat/') || text.includes('data:image');
if (isImage && !displayMsg.includes('<img')) displayMsg = `<img src="${displayMsg.includes('assets/') ? '/'+displayMsg : displayMsg}" class="img-fluid rounded" style="max-height: 250px; cursor: zoom-in;" onclick="window.open(this.src)">`;
let timeStr = ''; try { const d = m.created_at ? (m.created_at.includes('-') ? new Date(m.created_at.replace(/-/g, "/")) : new Date(m.created_at)) : new Date(); timeStr = d.toLocaleTimeString('zh-CN', {hour:'2-digit', minute:'2-digit'}); } catch(e) { timeStr = '--:--'; }
const html = `
<div class="mb-3 d-flex flex-column ${sender === 'user' ? 'align-items-end' : 'align-items-start'} modal-msg" data-modal-id="${m.id}">
<div class="msg-bubble p-2 px-3 rounded-4 small ${sender === 'user' ? 'bg-primary text-white' : 'bg-dark text-white border border-secondary border-opacity-30'}" style="max-width: 85%; position: relative; padding-bottom: 22px !important;">
<div class="message-content">${displayMsg}</div>
<div style="font-size: 9px; opacity: 0.6; position: absolute; bottom: 4px; ${sender === 'user' ? 'right: 12px;' : 'left: 12px;'}">${timeStr}</div>
</div>
</div>`;
container.insertAdjacentHTML('beforeend', html); modalChatLastIds.add(m.id);
}
function copyText(text) {
const el = document.createElement('textarea'); el.value = text; document.body.appendChild(el); el.select(); document.execCommand('copy'); document.body.removeChild(el);
Swal.fire({ icon: 'success', title: '<?= __("copy_success") ?>', toast: true, position: 'top-end', showConfirmButton: false, timer: 2000 });
}
function scrollModalToBottom() { const c = document.getElementById('modal-chat-messages'); if (c) c.scrollTop = c.scrollHeight; }
function confirmFiatOrder(btn, event) {
if (event) event.preventDefault();
const amount = parseFloat(document.getElementById('fiatAmount').value), select = document.getElementById('fiatCurrency'), currency = select.value, rate = parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
if (isNaN(amount) || amount <= 0) { notify('warning', '<?= __("enter_amount") ?>'); return; }
const originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = `<span class="spinner-border spinner-border-sm me-2"></span>${originalText}`;
const formData = new FormData(); formData.append('action', 'recharge'); formData.append('amount', amount / rate); formData.append('symbol', 'USDT'); formData.append('fiat_amount', amount); formData.append('fiat_currency', currency); formData.append('method', '<?= __("fiat_recharge") ?> (' + currency + ')');
fetch('/api/finance.php', { method: 'POST', body: formData }).then(r => r.json()).then(data => {
btn.disabled = false; btn.innerHTML = originalText;
if (data.success) {
let msg = `<?= __("recharge_msg_fiat") ?>`; msg = msg.replace('%uid%', userId).replace('%amount%', amount).replace('%currency%', currency).replace('%rate%', rate).replace('%res%', (amount / rate).toFixed(4));
openRechargeModal(msg, false, data.id);
} else notify('error', data.error || '<?= __("request_failed") ?>');
}).catch(err => { btn.disabled = false; btn.innerHTML = originalText; notify('error', err.message); });
}
function confirmCryptoOrder(btn, event) {
if (event) event.preventDefault();
const amountInput = document.getElementById('cryptoAmount'), amount = parseFloat(amountInput.value);
if (isNaN(amount) || amount <= 0) { notify('warning', '<?= __("enter_amount") ?>'); return; }
const originalText = btn.innerHTML; btn.disabled = true; btn.innerHTML = `<span class="spinner-border spinner-border-sm me-2"></span>${originalText}`;
const formData = new FormData(); formData.append('action', 'recharge'); formData.append('amount', amount); formData.append('symbol', 'USDT'); formData.append('method', currentNetwork);
fetch('/api/finance.php', { method: 'POST', body: formData }).then(r => r.json()).then(data => {
btn.disabled = false; btn.innerHTML = originalText;
if (data.success) { Swal.fire({ icon: 'success', title: '<?= __("recharge_request_submitted") ?>', text: '<?= __("recharge_request_submitted_text") ?>', toast: true, position: 'top-end', showConfirmButton: false, timer: 3000 }); amountInput.value = ''; }
else notify('error', data.error || '<?= __("request_failed") ?>');
}).catch(err => { btn.disabled = false; btn.innerHTML = originalText; notify('error', err.message); });
}
function copyAddress() {
const addr = document.getElementById('cryptoAddress'); addr.select(); document.execCommand('copy');
Swal.fire({ icon: 'success', title: '<?= __("copy_success") ?>', toast: true, position: 'top-end', showConfirmButton: false, timer: 3000, background: '#1e2329', color: '#fff' });
}
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>