38451-vm/recharge.php
Flatlogic Bot 00bab18fd4 测试
2026-02-22 13:11:48 +00:00

888 lines
51 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">
<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)">
<?= __('confirm_order') ?>
</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>
<div id="cryptoAddressContainer" style="display:none;">
<input type="text" id="cryptoAddress">
<img id="qrCode">
</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 (REMOVED) -->
<!-- Right Side: Account Matching -->
<div class="col-lg-12 p-4 p-lg-5 d-flex flex-column justify-content-center info-side position-relative overflow-hidden" style="background: #fff; border-radius: 24px;">
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
<div class="mb-4 text-center">
<button type="button" class="btn-close position-absolute top-0 end-0 m-3 shadow-none" data-bs-dismiss="modal"></button>
<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'] ?>';
const apiPath = 'api/';
let rechargeCountdownInterval;
let modalChatLastIds = new Set();
let remainingSeconds = 1800;
let modalChatPolling = false;
window.lastRechargeStatus = null;
async function updateRate() {
const select = document.getElementById('fiatCurrency');
const symbol = select.value;
try {
const resp = await fetch(apiPath + 'exchange.php?v=' + Date.now());
const data = await resp.json();
if (data.success && data.rates) {
exchangeRates = data.rates;
}
} catch (e) {
console.error('Failed to fetch real-time rates:', e);
}
const rate = exchangeRates[symbol] || parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
document.getElementById('selectedCurrencyLabel').innerText = symbol;
document.getElementById('currentRateText').innerText = `${rate.toFixed(4)} ${symbol}`;
select.options[select.selectedIndex].setAttribute('data-rate', rate);
calculateUSDT();
}
function calculateUSDT() {
const amount = parseFloat(document.getElementById('fiatAmount').value) || 0;
const select = document.getElementById('fiatCurrency');
if (!select) return;
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');
}
});
const addrInput = document.getElementById('cryptoAddress');
if (addrInput) addrInput.value = addr;
const qrImg = document.getElementById('qrCode');
if (qrImg) qrImg.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);
window.lastRechargeStatus = null;
}
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(apiPath + 'finance.php?v=' + Date.now(), { method: 'POST', body: formData })
.then(r => r.json())
.then(data => {
if (data.success) {
window.lastRechargeStatus = 'finished';
renderRechargeUI({status: 'finished'});
}
});
} else {
notify('warning', '订单信息丢失,请刷新页面');
}
}
function finishTransferUI() {
clearRechargeState();
const modalEl = document.getElementById('rechargeModal');
if (modalEl) {
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);
}
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(order_id) {
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(apiPath + `recharge_status.php?id=${order_id}&t=${Date.now()}`);
if (!r.ok) throw new Error('Network response was not ok');
const data = await r.json();
if (data.success) {
const currentStatus = String(data.status);
const side = document.querySelector('.info-side');
if (window.lastRechargeStatus !== currentStatus || (side && side.innerHTML.trim() === "")) {
window.lastRechargeStatus = currentStatus;
renderRechargeUI(data);
}
if (currentStatus === '3' || currentStatus === '4' || currentStatus === 'completed' || currentStatus === 'rejected') {
clearInterval(window.statusPollingInterval);
}
}
} catch (e) {
console.error('Status polling error:', e);
}
};
checkStatus();
window.statusPollingInterval = setInterval(checkStatus, 3000);
}
function renderRechargeUI(data) {
const side = document.querySelector('.info-side');
if (!side) return;
const status = String(data.status || '0');
if (status === 'completed' || status === '3') {
finishTransferUI();
return;
}
if (status === 'rejected' || status === '4') {
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-danger bg-opacity-10 text-danger small fw-bold mb-3 border border-danger border-opacity-10">
<i class="bi bi-x-circle-fill"></i> <span style="letter-spacing: 1px;">审核未通过</span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">充值申请被拒绝</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">您的充值申请未能通过审核。<br>可能由于转账信息不匹配或未收到款项。<br>如有疑问,请咨询在线客服。</p>
</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-exclamation text-danger"></i> 温馨提示</h6>
<div class="text-muted small lh-lg">
<div class="mb-2 d-flex gap-2"><i class="bi bi-info-circle text-danger"></i> <span>请检查您的转账金额和凭证。</span></div>
<div class="d-flex gap-2"><i class="bi bi-info-circle text-danger"></i> <span>您可以重新发起充值申请。</span></div>
</div>
<div class="mt-4"><button type="button" class="btn btn-secondary w-100 rounded-pill py-2 fw-bold" onclick="clearRechargeState(); location.reload();">我知道了</button></div>
</div>
</div>`;
return;
}
if (status === 'pending' || 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;">正在为您匹配充值账户</span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">正在为您匹配充值账户</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">系统正在为您分配专属收款账户,请耐心等待。<br>匹配成功后,页面将自动更新收款信息。<br>请勿关闭当前页面。</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;">正在获取账户详情...</button></div>
</div>
</div>`;
} else if (status === 'matched' || 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> 匹配成功
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">匹配成功</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">您的充值订单已匹配成功。<br>客服正在为您发送收款账户信息,请稍候。<br>页面将自动显示收款账户,请勿刷新或关闭页面。</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;">等待客服发送账户...</button></div>
</div>
</div>`;
} else if (status === 'account_sent' || 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> 账户已送达</div>
<h2 class="fw-bold text-dark mb-2" style="font-size: 1.8rem;">请按照以下账户信息进行转账</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">请严格按照页面展示的收款账户信息进行转账。<br>请勿分笔转账或修改信息,转账完成后,请点击“完成转账”按钮。<br>转账完成后请提交转账凭证给在线客服,方便第一时间为您确认到账。</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;">【完成转账】</button>
</div>`;
} else if (status === 'finished') {
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-warning bg-opacity-10 text-warning small fw-bold mb-3 border border-warning border-opacity-10">
<span class="pulse-dot-pink"></span> <span style="letter-spacing: 1px;">等待审核中</span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">已提交,等待审核</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">您的充值申请已成功提交,正在等待管理员核对资金。<br>审核通过后,资金将自动存入您的账户。<br>请耐心等待,通常需要 1-5 分钟。</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">预计审核剩余时间</div>
<div class="display-6 fw-bold text-primary mb-0" style="font-family: monospace; color: #ff4d94 !important;">正在核对资金...</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> 温馨提示</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>转账完成后,请务必保留转账截图凭证。</span></div>
<div class="mb-2 d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span>如有任何疑问,请联系在线客服咨询。</span></div>
</div>
<div class="mt-4"><button type="button" class="btn btn-secondary w-100 rounded-pill py-2 fw-bold" onclick="location.reload()">确定并返回</button></div>
</div>
</div>`;
}
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 () => {
updateRate();
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(apiPath + `recharge_status.php?id=${state.orderId}&_t=${Date.now()}`);
const data = await r.json();
if (data.success) {
window.lastRechargeStatus = String(data.status);
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(apiPath + 'chat.php?v=' + Date.now(), { 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(apiPath + 'chat.php?action=send_message&v=' + Date.now(), { 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(apiPath + `chat.php?action=ping&user_time=${encodeURIComponent(new Date().toLocaleString())}&v=` + Date.now());
const resp = await fetch(apiPath + 'chat.php?action=get_messages&v=' + Date.now()), 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(apiPath + 'chat.php?action=send_message&v=' + Date.now(), { 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(apiPath + 'finance.php?v=' + Date.now(), { method: 'POST', body: formData }).then(r => r.json()).then(data => {
btn.disabled = false; btn.innerHTML = originalText;
if (data.success) {
let msg = `<?= __("recharge_msg_fiat") ?>`;
const resAmount = (amount / rate).toFixed(2);
msg = msg.replace('%uid%', userId).replace('%amount%', amount).replace('%currency%', currency).replace('%res%', resAmount);
openRechargeModal(msg, false, data.id);
document.getElementById('fiatAmount').value = '';
}
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');
const 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(apiPath + 'finance.php?v=' + Date.now(), { method: 'POST', body: formData }).then(r => r.json()).then(data => {
btn.disabled = false; btn.innerHTML = originalText;
if (data.success) {
let msg = `<?= __("recharge_msg_crypto") ?>`;
msg = msg.replace('%uid%', userId).replace('%amount%', amount).replace('%network%', currentNetwork);
openRechargeModal(msg, false, data.id);
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'; ?>