38451-vm/recharge.php
2026-02-24 13:02:17 +00:00

911 lines
55 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;"><?= $lang === 'zh' ? '账户匹配中…' : 'Account Matching...' ?></span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;"><?= $lang === 'zh' ? '充值订单已生成' : 'Recharge Order Generated' ?></h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px; line-height: 1.6;">
<?= $lang === 'zh' ? '您的充值申请已成功提交,系统正在为您智能匹配本次订单的专属收款账户。<br>为保障资金安全及订单唯一性,每笔充值均采用独立账户匹配机制。<br>请您耐心等待匹配完成,请勿刷新或关闭当前页面,以免影响订单状态同步。' : 'Your recharge request has been submitted. The system is matching an exclusive receiving account for you.<br>To ensure fund security and order uniqueness, each recharge uses an independent matching mechanism.<br>Please wait patiently and do not refresh or close this page.' ?>
</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"><?= $lang === 'zh' ? '预计匹配剩余时间' : 'Estimated Matching Time' ?></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"><?= $lang === 'zh' ? '当前状态:账户匹配中…' : 'Current Status: Account Matching...' ?></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> <?= $lang === 'zh' ? '温馨提示' : 'Safety 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><?= $lang === 'zh' ? '请使用您本人实名账户进行转账。' : 'Please use your own real-name account.' ?></span>
</div>
<div class="mb-2 d-flex gap-2">
<i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i>
<span><?= $lang === 'zh' ? '转账金额必须与订单金额一致。' : 'Transfer amount must match order.' ?></span>
</div>
<div class="d-flex gap-2">
<i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i>
<span><?= $lang === 'zh' ? '请勿关闭当前页面,系统正在为您匹配。' : 'Do not close this page, system is matching.' ?></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;">
<?= $lang === 'zh' ? '等待系统分配账户' : 'Waiting for 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: auto !important;
border-radius: 24px !important;
padding: 1.25rem !important;
justify-content: flex-start !important;
}
.modal-dialog {
margin: 10px !important;
min-height: calc(100vh - 20px);
display: flex;
align-items: center;
}
.modal-content {
border-radius: 24px !important;
height: auto !important;
max-height: 85vh !important;
overflow-y: auto !important;
}
.display-5 { font-size: 2.2rem !important; }
h2 { font-size: 1.4rem !important; }
}
/* 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 exchangeRates = <?= json_encode($rates) ?>;
let rechargeCountdownInterval;
let modalChatLastIds = new Set();
let remainingSeconds = 1800;
let modalChatPolling = false;
window.lastRechargeStatus = null;
const getApiPath = () => {
// Determine the path to the api directory relative to current location
const loc = window.location.pathname;
const isRoot = loc.endsWith('/') || loc.endsWith('index.php') || loc.endsWith('recharge.php');
return isRoot ? 'api/' : '../api/';
};
async function updateRate() {
const select = document.getElementById('fiatCurrency');
if (!select) return;
const symbol = select.value;
try {
const resp = await fetch(getApiPath() + 'exchange.php?v=' + Date.now(), {
headers: { 'Cache-Control': 'no-cache', 'Pragma': 'no-cache' }
});
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(getApiPath() + '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(getApiPath() + `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);
if (window.lastRechargeStatus !== currentStatus) {
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');
const isZh = '<?= $lang ?>' === 'zh';
if (status === 'completed' || status === '3') {
setTimeout(() => finishTransferUI(), 500);
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;">${isZh ? '审核未通过' : 'Audit Failed'}</span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">${isZh ? '充值申请被拒绝' : 'Recharge Request Rejected'}</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">${isZh ? '您的充值申请未能通过审核。<br>可能由于转账信息不匹配或未收到款项。<br>如有疑问,请咨询在线客服。' : 'Your recharge request failed audit.<br>Possible mismatch in transfer info or payment not received.<br>Please contact support if you have questions.'}</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> ${isZh ? '温馨提示' : 'Safety Tips'}</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>${isZh ? '请检查您的转账金额和凭证。' : 'Please check your transfer amount and receipt.'}</span></div>
<div class="d-flex gap-2"><i class="bi bi-info-circle text-danger"></i> <span>${isZh ? '您可以重新发起充值申请。' : 'You can initiate a new recharge request.'}</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();">${isZh ? '我知道了' : 'I Understand'}</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;">${isZh ? '账户匹配中…' : 'Account Matching...'}</span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">${isZh ? '充值订单已生成' : 'Recharge Order Generated'}</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px; line-height: 1.6;">
${isZh ? '您的充值申请已成功提交,系统正在为您智能匹配本次订单的专属收款账户。<br>为保障资金安全及订单唯一性,每笔充值均采用独立账户匹配机制。<br>请您耐心等待匹配完成,请勿刷新或关闭当前页面,以免影响订单状态同步。' : 'Your recharge request has been submitted. The system is matching an exclusive receiving account for you.<br>To ensure fund security and order uniqueness, each recharge uses an independent matching mechanism.<br>Please wait patiently and do not refresh or close this page.'}
</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">${isZh ? '预计匹配剩余时间' : 'Estimated Matching Time'}</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 class="col-12 border-top border-white pt-3">
<div class="text-muted small mb-2 fw-bold">${isZh ? '当前状态:账户匹配中…' : 'Current Status: Account Matching...'}</div>
</div>
</div>
</div>
<div class="p-4 rounded-4 bg-light border border-light text-start">
<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> ${isZh ? '温馨提示' : 'Safety 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>${isZh ? '请使用您本人实名账户进行转账。' : 'Please use your own real-name account for transfer.'}</span></div>
<div class="mb-2 d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span>${isZh ? '转账金额必须与订单金额一致。' : 'Transfer amount must match order amount.'}</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;">${isZh ? '正在获取账户详情...' : 'Getting Account Details...'}</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> ${isZh ? '匹配成功' : 'Matched Successfully'}
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">${isZh ? '专属充值账户已匹配成功' : 'Exclusive Account Matched'}</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px; line-height: 1.6;">
${isZh ? '系统已成功为您分配本次订单的专属收款账户。<br>请严格按照下方显示的账户信息及金额进行转账操作。<br>本账户仅限本次订单使用,请勿重复转账或向其他账户付款。' : 'The system has assigned an exclusive receiving account for this order.<br>Please strictly follow the account info and amount shown below for transfer.<br>This account is for this order only. Do not pay multiple times.'}
</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">${isZh ? '等待支付剩余时间' : 'Payment Time Remaining'}</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 class="col-12 border-top border-white pt-3">
<div class="text-muted small mb-2 fw-bold">${isZh ? '当前状态:匹配成功,等待分配转账账户' : 'Status: Matched, waiting for account details'}</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> ${isZh ? '温馨提示' : 'Safety 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;">${isZh ? '等待客服发送账户...' : 'Waiting for Account Details...'}</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> ${isZh ? '账户已送达' : 'Account Received'}</div>
<h2 class="fw-bold text-dark mb-2" style="font-size: 1.8rem;">${isZh ? '转账操作说明' : 'Transfer Instructions'}</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">${isZh ? '收款银行' : '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}')">${isZh ? '复制' : '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">${isZh ? '收款账号' : 'Account Number'}</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}')">${isZh ? '复制' : '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">${isZh ? '收款姓名' : 'Receiver 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}')">${isZh ? '复制' : 'Copy'}</button>
</div>
</div>
</div>
</div>
<div class="p-4 rounded-4 bg-light border border-light text-start mb-3">
<h6 class="text-dark fw-bold mb-3 d-flex align-items-center gap-2"><i class="bi bi-info-circle-fill text-primary" style="color: #ff4d94 !important;"></i> ${isZh ? '操作须知' : 'Operating Instructions'}</h6>
<div class="text-muted small lh-lg">
<p class="mb-2 fw-bold text-dark">${isZh ? '为确保资金顺利到账,请注意以下事项:' : 'To ensure successful deposit, please note:'}</p>
<div class="mb-1">1⃣ ${isZh ? '请使用您本人实名账户进行转账。' : 'Use your own real-name account.'}</div>
<div class="mb-1">2⃣ ${isZh ? '转账金额必须与订单金额完全一致,不可多转或少转。' : 'Amount must exactly match order amount.'}</div>
<div class="mb-1">3⃣ ${isZh ? '请勿修改订单备注信息(如系统有指定备注,请严格填写)。' : 'Do not modify order remarks (if any).'}</div>
<div class="mb-2">4⃣ ${isZh ? '转账完成后,请保留转账凭证需要提交凭证平台核查。' : 'Keep transfer receipt for verification.'}</div>
<p class="mb-0 mt-2 text-primary fw-bold">${isZh ? '系统将在收到银行到账后自动进行匹配确认。' : 'System will auto-confirm after bank arrival.'}</p>
</div>
</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;">${isZh ? '【完成转账】' : '[Finish Transfer]'}</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;">${isZh ? '等待审核中' : 'Awaiting Review'}</span>
</div>
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">${isZh ? '已提交,等待审核' : 'Submitted, Awaiting Review'}</h2>
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">${isZh ? '您的充值申请已成功提交,正在等待管理员核对资金。<br>审核通过后,资金将自动存入您的账户。<br>请耐心等待,通常需要 1-5 分钟。' : 'Your application is submitted. Waiting for admin to verify funds.<br>Funds will be deposited automatically after approval.<br>Please wait, usually takes 1-5 mins.'}</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-lock-fill text-primary" style="color: #ff4d94 !important;"></i> ${isZh ? '温馨提示' : 'Safety 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>${isZh ? '转账完成后,请务必保留转账截图凭证。' : 'Keep your transfer screenshot/receipt.'}</span></div>
<div class="d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span>${isZh ? '如有任何疑问,请联系在线客服咨询。' : 'Contact support for any questions.'}</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()">${isZh ? '确定并返回' : 'Confirm & Back'}</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(getApiPath() + `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(getApiPath() + '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(getApiPath() + '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(getApiPath() + `chat.php?action=ping&user_time=${encodeURIComponent(new Date().toLocaleString())}&v=` + Date.now());
const resp = await fetch(getApiPath() + '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(getApiPath() + '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(getApiPath() + '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', 'Crypto (USDT-' + currentNetwork + ')');
fetch(getApiPath() + '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).replace('%addr%', currentAddress);
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'; ?>