448 lines
21 KiB
PHP
448 lines
21 KiB
PHP
<?php
|
|
session_start();
|
|
if (!isset($_SESSION['user_id'])) {
|
|
header('Location: index.php');
|
|
exit;
|
|
}
|
|
require_once __DIR__ . '/db/config.php';
|
|
|
|
$pdo = db();
|
|
$stmt = $pdo->prepare("SELECT username, balance FROM users WHERE id = ?");
|
|
$stmt->execute([$_SESSION['user_id']]);
|
|
$user = $stmt->fetch();
|
|
|
|
$settings = $pdo->query("SELECT setting_key, setting_value FROM settings")->fetchAll(PDO::FETCH_KEY_PAIR);
|
|
$trc20_address = $settings['usdt_trc20_address'] ?? 'TEm1B...TRC20_ADDRESS_HERE';
|
|
$erc20_address = $settings['usdt_erc20_address'] ?? '0x71C...ERC20_ADDRESS_HERE';
|
|
$site_name = $settings['site_name'] ?? '全球接码';
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>充值中心 - <?= htmlspecialchars($site_name) ?></title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<script src="https://cdn.jsdelivr.net/npm/qrcode-generator@1.4.4/qrcode.min.js"></script>
|
|
<style>
|
|
:root {
|
|
--primary: #3b82f6;
|
|
--primary-gradient: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);
|
|
--bg-body: #f1f5f9;
|
|
--surface: #ffffff;
|
|
--text-main: #1e293b;
|
|
--text-muted: #64748b;
|
|
--border-color: #e2e8f0;
|
|
--sidebar-width: 280px;
|
|
--radius-xl: 24px;
|
|
--radius-lg: 16px;
|
|
}
|
|
body {
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
background-color: var(--bg-body);
|
|
color: var(--text-main);
|
|
letter-spacing: -0.01em;
|
|
}
|
|
|
|
.main-content {
|
|
margin-left: var(--sidebar-width);
|
|
padding: 2.5rem;
|
|
min-height: 100vh;
|
|
}
|
|
|
|
.card-custom {
|
|
background: white;
|
|
border: 1px solid var(--border-color);
|
|
border-radius: var(--radius-xl);
|
|
padding: 2.5rem;
|
|
margin-bottom: 2rem;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
}
|
|
|
|
.network-btn {
|
|
background-color: #f8fafc;
|
|
border: 1.5px solid #e2e8f0;
|
|
color: var(--text-muted);
|
|
padding: 18px;
|
|
border-radius: var(--radius-lg);
|
|
cursor: pointer;
|
|
transition: all 0.2s ease;
|
|
text-align: center;
|
|
font-weight: 700;
|
|
flex: 1;
|
|
}
|
|
.network-btn.active {
|
|
border-color: var(--primary);
|
|
color: var(--primary);
|
|
background-color: #eff6ff;
|
|
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1);
|
|
}
|
|
|
|
.qr-card {
|
|
background: #fff;
|
|
padding: 24px;
|
|
border-radius: var(--radius-xl);
|
|
display: inline-block;
|
|
border: 1px solid #f1f5f9;
|
|
box-shadow: 0 10px 30px rgba(0,0,0,0.05);
|
|
}
|
|
|
|
.address-box-wrapper {
|
|
background-color: #f8fafc;
|
|
padding: 20px 24px;
|
|
border-radius: var(--radius-lg);
|
|
border: 2px dashed #cbd5e1;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 15px;
|
|
}
|
|
|
|
.address-text {
|
|
word-break: break-all;
|
|
font-family: 'JetBrains Mono', 'Courier New', monospace;
|
|
font-size: 1.1rem;
|
|
color: #1e293b;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.btn-copy {
|
|
background: var(--primary);
|
|
color: white;
|
|
border: none;
|
|
width: 48px; height: 48px;
|
|
border-radius: 14px;
|
|
display: flex; align-items: center; justify-content: center;
|
|
cursor: pointer; transition: all 0.2s;
|
|
}
|
|
.btn-copy:hover { transform: scale(1.05); background: #2563eb; }
|
|
|
|
.btn-primary-action {
|
|
background: var(--primary-gradient);
|
|
border: none;
|
|
padding: 20px;
|
|
border-radius: var(--radius-lg);
|
|
font-weight: 800;
|
|
color: white;
|
|
width: 100%;
|
|
transition: all 0.3s;
|
|
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.25);
|
|
}
|
|
.btn-primary-action:hover { transform: translateY(-2px); box-shadow: 0 12px 30px rgba(37, 99, 235, 0.35); }
|
|
|
|
.step-panel { display: none; }
|
|
.step-panel.active { display: block; animation: fadeInUp 0.4s ease-out; }
|
|
@keyframes fadeInUp { from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } }
|
|
|
|
.scanner-circle {
|
|
width: 100px; height: 100px;
|
|
border: 3px solid var(--primary);
|
|
border-radius: 50%;
|
|
display: flex; align-items: center; justify-content: center;
|
|
margin-bottom: 24px;
|
|
position: relative;
|
|
}
|
|
.scanner-circle::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: -10px; left: -10px; right: -10px; bottom: -10px;
|
|
border: 2px solid rgba(59, 130, 246, 0.2);
|
|
border-radius: 50%;
|
|
animation: ping 1.5s infinite;
|
|
}
|
|
@keyframes ping { 75%, 100% { transform: scale(1.4); opacity: 0; } }
|
|
|
|
.status-badge {
|
|
padding: 8px 16px;
|
|
border-radius: 100px;
|
|
font-weight: 700;
|
|
font-size: 12px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.status-sync { background: #dcfce7; color: #166534; }
|
|
|
|
.amt-btn {
|
|
background: #fff;
|
|
border: 1.5px solid #e2e8f0;
|
|
padding: 15px;
|
|
border-radius: var(--radius-lg);
|
|
font-weight: 800;
|
|
transition: all 0.2s;
|
|
}
|
|
.amt-btn:hover { border-color: var(--primary); color: var(--primary); background: #f8fafc; }
|
|
|
|
@media (max-width: 992px) {
|
|
.main-content { margin-left: 0; padding: 1.5rem; }
|
|
.sidebar { display: none; }
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<?php include 'includes/sidebar.php'; ?>
|
|
|
|
<div class="main-content">
|
|
<div class="d-flex justify-content-between align-items-center mb-5">
|
|
<div>
|
|
<h1 class="fw-bold mb-1" style="font-size: 1.5rem;">资产充值 <span class="text-muted fw-medium ms-2 fs-6">RECHARGE CENTER</span></h1>
|
|
<p class="text-muted small mb-0 fw-medium">USDT 全自动区块监听,即时入账安全有保障</p>
|
|
</div>
|
|
<div class="text-end bg-white border p-3 px-4 rounded-4 shadow-sm d-flex align-items-center gap-3">
|
|
<div class="text-end">
|
|
<div class="small text-muted fw-bold" style="font-size: 10px; letter-spacing: 0.5px;">BALANCE</div>
|
|
<div class="h4 fw-bold text-primary mb-0">$<?= number_format($user['balance'] ?? 0, 2) ?></div>
|
|
</div>
|
|
<div class="bg-primary bg-opacity-10 text-primary rounded-circle d-flex align-items-center justify-content-center" style="width: 40px; height: 40px;">
|
|
<i class="fas fa-wallet"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-xl-8">
|
|
<div class="card-custom">
|
|
<!-- Step 1 -->
|
|
<div id="step1" class="step-panel active">
|
|
<h5 class="fw-bold mb-4" style="color: #334155;">1. 输入充值数额 / AMOUNT</h5>
|
|
<div class="p-4 bg-primary bg-opacity-10 rounded-4 mb-4" style="border-left: 4px solid var(--primary);">
|
|
<p class="small text-primary fw-bold mb-0">
|
|
<i class="fas fa-info-circle me-1"></i> 为了极速识别您的支付,系统会生成一个唯一的<strong>随机小数</strong>。请务必支付<strong>包含小数点的准确金额</strong>。
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mb-5 text-center py-4">
|
|
<label class="form-label fw-bold text-muted small mb-3">充值金额 (USDT)</label>
|
|
<div class="d-flex align-items-center justify-content-center gap-2">
|
|
<span class="fs-1 fw-bold text-muted opacity-25">$</span>
|
|
<input type="number" id="inputAmount" class="form-control border-0 text-center fw-bold" style="font-size: 4rem; width: 300px; color: var(--primary);" placeholder="0" min="10" step="1">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-5">
|
|
<?php foreach([10, 50, 100, 200, 500, 1000] as $amt): ?>
|
|
<div class="col-4 col-md-2">
|
|
<button class="btn amt-btn w-100" onclick="setAmount(<?= $amt ?>)"><?= $amt ?></button>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<button class="btn-primary-action py-4 mt-4" onclick="confirmOrder()">
|
|
生成支付订单 / CONFIRM ORDER <i class="fas fa-chevron-right ms-2"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Step 2 -->
|
|
<div id="step2" class="step-panel">
|
|
<div class="d-flex justify-content-between align-items-center mb-5 pb-4 border-bottom">
|
|
<div>
|
|
<h5 class="fw-bold mb-1">2. 完成区块链支付 / PAYMENT</h5>
|
|
<div class="status-badge status-sync" id="orderIdDisplay">正在连接节点...</div>
|
|
</div>
|
|
<div class="text-end">
|
|
<div class="h2 fw-bold text-primary mb-0" id="displayAmount">0.00</div>
|
|
<div class="small text-muted fw-bold" style="font-size: 10px;">PRECISE AMOUNT (USDT)</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4 mb-5">
|
|
<div class="col-md-6">
|
|
<label class="form-label fw-bold text-muted mb-3 small">A. 选择支付网络 / NETWORK</label>
|
|
<div class="d-flex gap-3 mb-4">
|
|
<div class="network-btn active" id="btnTRC" onclick="selectNetwork('TRC20')">
|
|
<div class="fs-5">TRC20</div>
|
|
<div class="small opacity-50">波场 TRON</div>
|
|
</div>
|
|
<div class="network-btn" id="btnERC" onclick="selectNetwork('ERC20')">
|
|
<div class="fs-5">ERC20</div>
|
|
<div class="small opacity-50">以太坊 ETH</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-warning border-0 rounded-4 p-4" style="background: #fffbeb;">
|
|
<h6 class="fw-bold mb-2 small" style="color: #92400e;"><i class="fas fa-exclamation-triangle me-2"></i> 核心支付准则</h6>
|
|
<ol class="small mb-0 ps-3" style="color: #92400e;">
|
|
<li class="mb-1">转账金额必须<strong>精确到小数点后两位</strong>。</li>
|
|
<li class="mb-1">转账网络必须选择与您选择的选项一致。</li>
|
|
<li>支付后请不要关闭此页面,等待系统自动跳转。</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6 text-center">
|
|
<div class="qr-card" id="qrcode"></div>
|
|
<div class="mt-4">
|
|
<div class="badge bg-light text-primary border p-2 px-4 rounded-pill fw-bold">
|
|
<i class="fas fa-clock-rotate-left me-2"></i> 有效期: <span id="countdown">60:00</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-5">
|
|
<label class="text-muted small mb-2 d-block fw-bold px-1">B. 复制收款地址 / ADDRESS</label>
|
|
<div class="address-box-wrapper">
|
|
<div class="address-text" id="addressBox">正在加载...</div>
|
|
<button class="btn-copy" onclick="copyAddress()" title="点击复制"><i class="fas fa-copy"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex flex-column align-items-center py-4">
|
|
<div class="scanner-circle">
|
|
<i class="fas fa-satellite-dish text-primary fs-3"></i>
|
|
</div>
|
|
<h6 class="fw-bold mb-2">正在监听区块确认...</h6>
|
|
<p class="text-muted small text-center mb-5 px-5">我们正在 24/7 监听您的专属付款。一旦链上确认数达标,系统将瞬间为您入账。</p>
|
|
|
|
<div class="d-flex gap-3 w-100">
|
|
<button class="btn btn-light border flex-grow-1 py-3 rounded-4 fw-bold" style="color: #64748b;" onclick="goBackToStep1()">修改金额</button>
|
|
<button class="btn btn-primary flex-grow-1 py-3 rounded-4 fw-bold shadow-sm" onclick="checkStatusManual()">手动刷新状态</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-xl-4">
|
|
<div class="card-custom" style="padding: 2rem;">
|
|
<h6 class="fw-bold mb-4" style="color: #334155;"><i class="fas fa-lightbulb text-warning me-2"></i> 充值助手 / HELP</h6>
|
|
<div class="mb-4">
|
|
<div class="fw-bold mb-1 small">为什么要支付精确小数?</div>
|
|
<p class="small text-muted mb-0">金额是唯一的。精准支付后,系统可实现秒级自动识别入账。否则需要联系人工核实。</p>
|
|
</div>
|
|
<div class="mb-4">
|
|
<div class="fw-bold mb-1 small">多久到账?</div>
|
|
<p class="small text-muted mb-0">TRC20 网络通常在 1 分钟内。ERC20 视网络拥堵情况,通常在 5-10 分钟。</p>
|
|
</div>
|
|
<div class="mt-5 p-4 bg-primary rounded-4 text-white shadow-lg" style="background: var(--primary-gradient) !important;">
|
|
<h6 class="fw-bold mb-2">需要人工支持?</h6>
|
|
<p class="small opacity-75 mb-3">如果金额支付错误或长时间未到账,请立即联系在线客服。</p>
|
|
<a href="support.php" class="btn btn-white btn-sm w-100 fw-bold py-2 rounded-3" style="background: white; color: var(--primary);">发起咨询 / SUPPORT</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal fade" id="successModal" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 rounded-5 overflow-hidden text-center p-5">
|
|
<div class="mb-4">
|
|
<i class="fas fa-check-circle text-success" style="font-size: 5rem;"></i>
|
|
</div>
|
|
<h3 class="fw-bold">充值确认成功!</h3>
|
|
<p class="text-muted mb-4">您的余额已成功更新。欢迎回到工作台继续接码。</p>
|
|
<button class="btn btn-primary btn-lg w-100 py-3 rounded-4 fw-bold" onclick="window.location.href='dashboard.php'">立即前往工作台</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const addresses = { 'TRC20': '<?= $trc20_address ?>', 'ERC20': '<?= $erc20_address ?>' };
|
|
let currentNetwork = 'TRC20';
|
|
let timeLeft = 3600;
|
|
let timerInterval, pollInterval, currentRechargeId = null;
|
|
|
|
function setAmount(val) { document.getElementById('inputAmount').value = val; }
|
|
|
|
async function confirmOrder() {
|
|
const amt = document.getElementById('inputAmount').value;
|
|
if (!amt || amt < 10) { alert('最低充值金额为 10 USDT'); return; }
|
|
|
|
const btn = event.currentTarget;
|
|
const originalText = btn.innerHTML;
|
|
btn.disabled = true;
|
|
btn.innerHTML = '<i class="fas fa-circle-notch fa-spin me-2"></i>正在连接区块链网关...';
|
|
|
|
try {
|
|
const formData = new FormData();
|
|
formData.append('amount', amt);
|
|
const res = await fetch('ajax_handler.php?action=create_recharge', { method: 'POST', body: formData });
|
|
const data = await res.json();
|
|
if (data.code === 0) {
|
|
currentRechargeId = data.recharge_id;
|
|
document.getElementById('orderIdDisplay').textContent = 'ORDER ID: #RE' + currentRechargeId;
|
|
document.getElementById('displayAmount').textContent = parseFloat(data.amount).toFixed(2);
|
|
document.getElementById('step1').classList.remove('active');
|
|
document.getElementById('step2').classList.add('active');
|
|
updateDisplay();
|
|
startTimer();
|
|
startPollingStatus();
|
|
} else { alert(data.msg); btn.disabled = false; btn.innerHTML = originalText; }
|
|
} catch (e) { btn.disabled = false; btn.innerHTML = originalText; }
|
|
}
|
|
|
|
function startPollingStatus() {
|
|
pollInterval = setInterval(async () => {
|
|
try {
|
|
const res = await fetch(`ajax_handler.php?action=check_recharge_status&recharge_id=${currentRechargeId}`);
|
|
const data = await res.json();
|
|
if (data.code === 0 && data.status === 'completed') {
|
|
clearInterval(pollInterval);
|
|
new bootstrap.Modal(document.getElementById('successModal')).show();
|
|
setTimeout(() => window.location.href = 'dashboard.php', 3000);
|
|
}
|
|
} catch (e) {}
|
|
}, 3000);
|
|
}
|
|
|
|
async function checkStatusManual() {
|
|
const btn = event.currentTarget;
|
|
const old = btn.innerHTML;
|
|
btn.disabled = true; btn.innerHTML = '<i class="fas fa-circle-notch fa-spin"></i>';
|
|
try {
|
|
const res = await fetch(`ajax_handler.php?action=check_recharge_status&recharge_id=${currentRechargeId}`);
|
|
const data = await res.json();
|
|
if (data.code === 0 && data.status === 'completed') {
|
|
new bootstrap.Modal(document.getElementById('successModal')).show();
|
|
} else { setTimeout(() => { btn.disabled = false; btn.innerHTML = old; }, 1000); }
|
|
} catch (e) { btn.disabled = false; btn.innerHTML = old; }
|
|
}
|
|
|
|
function selectNetwork(net) {
|
|
currentNetwork = net;
|
|
document.getElementById('btnTRC').classList.toggle('active', net === 'TRC20');
|
|
document.getElementById('btnERC').classList.toggle('active', net === 'ERC20');
|
|
updateDisplay();
|
|
}
|
|
|
|
function updateDisplay() {
|
|
const addr = addresses[currentNetwork];
|
|
document.getElementById('addressBox').textContent = addr;
|
|
const qr = qrcode(0, 'M');
|
|
qr.addData(addr); qr.make();
|
|
document.getElementById('qrcode').innerHTML = qr.createImgTag(6, 0);
|
|
}
|
|
|
|
function copyAddress() {
|
|
const addr = document.getElementById('addressBox').textContent;
|
|
navigator.clipboard.writeText(addr).then(() => {
|
|
const btn = document.querySelector('.btn-copy');
|
|
const old = btn.innerHTML;
|
|
btn.innerHTML = '<i class="fas fa-check"></i>';
|
|
setTimeout(() => btn.innerHTML = old, 2000);
|
|
});
|
|
}
|
|
|
|
function startTimer() {
|
|
timerInterval = setInterval(() => {
|
|
timeLeft--;
|
|
const m = Math.floor(timeLeft / 60), s = timeLeft % 60;
|
|
document.getElementById('countdown').textContent = `${m}:${s.toString().padStart(2, '0')}`;
|
|
if (timeLeft <= 0) window.location.reload();
|
|
}, 1000);
|
|
}
|
|
|
|
function goBackToStep1() {
|
|
if (!confirm('确定返回修改金额?当前订单将作废。')) return;
|
|
document.getElementById('step2').classList.remove('active');
|
|
document.getElementById('step1').classList.add('active');
|
|
clearInterval(timerInterval); clearInterval(pollInterval);
|
|
document.querySelector('#step1 button.btn-primary-action').disabled = false;
|
|
document.querySelector('#step1 button.btn-primary-action').innerHTML = '生成支付订单 / CONFIRM ORDER <i class="fas fa-chevron-right ms-2"></i>';
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|