38320-vm/recharge.php
2026-02-10 07:00:25 +00:00

566 lines
26 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
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();
$stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = 'usdt_trc20_address'");
$stmt->execute();
$trc20_address = $stmt->fetchColumn() ?: 'TEm1B...TRC20_ADDRESS_HERE';
$stmt = $pdo->prepare("SELECT setting_value FROM settings WHERE setting_key = 'usdt_erc20_address'");
$stmt->execute();
$erc20_address = $stmt->fetchColumn() ?: '0x71C...ERC20_ADDRESS_HERE';
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>充值中心 - 全球接码 (Recharge)</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-color: #ff4d94;
--secondary-color: #ff1a75;
--bg-light: #fff8f9;
--surface-light: #ffffff;
--text-main: #2d1a1e;
--text-muted: #8a6d71;
--border-color: #ffd1dc;
--sidebar-width: 280px;
}
body {
font-family: 'Plus Jakarta Sans', sans-serif;
background-color: var(--bg-light);
color: var(--text-main);
overflow-x: hidden;
}
.main-content {
margin-left: var(--sidebar-width);
padding: 2rem 3rem;
}
.card-neo {
background: white;
border: 2px solid var(--border-color);
border-radius: 32px;
padding: 40px;
margin-bottom: 24px;
box-shadow: 8px 8px 0px var(--border-color);
}
.network-btn {
background-color: #fff;
border: 2px solid var(--border-color);
color: var(--text-muted);
padding: 15px;
border-radius: 16px;
cursor: pointer;
transition: all 0.3s ease;
text-align: center;
font-weight: 700;
}
.network-btn.active {
border-color: var(--primary-color);
color: var(--primary-color);
background-color: rgba(255, 77, 148, 0.05);
box-shadow: 0 4px 12px rgba(255, 77, 148, 0.1);
}
.qr-container {
background: white;
padding: 20px;
border-radius: 24px;
display: inline-block;
margin: 20px 0;
border: 2px solid var(--border-color);
box-shadow: 0 10px 30px rgba(255, 77, 148, 0.1);
}
.address-container {
position: relative;
background-color: var(--bg-light);
padding: 24px;
border-radius: 20px;
border: 2px dashed var(--border-color);
transition: all 0.3s ease;
}
.address-box {
word-break: break-all;
font-family: 'Courier New', monospace;
font-size: 1.2rem;
color: var(--primary-color);
font-weight: 800;
margin-right: 60px;
}
.copy-btn {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
background: var(--primary-color);
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;
}
.copy-btn:hover { background: var(--secondary-color); transform: translateY(-50%) scale(1.05); }
.btn-primary {
background-color: var(--primary-color);
border: none;
padding: 18px 30px;
border-radius: 20px;
font-weight: 800;
color: white;
box-shadow: 0 8px 25px rgba(255, 77, 148, 0.3);
transition: all 0.3s;
}
.btn-primary:hover { transform: translateY(-3px); box-shadow: 0 12px 30px rgba(255, 77, 148, 0.4); }
.step-container { display: none; }
.step-container.active { display: block; animation: slideUp 0.6s cubic-bezier(0.16, 1, 0.3, 1); }
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
.status-overlay {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(255, 255, 255, 0.98);
backdrop-filter: blur(15px);
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
visibility: hidden;
opacity: 0;
transition: all 0.6s ease;
}
.status-overlay.show { visibility: visible; opacity: 1; }
.instruction-item {
display: flex;
gap: 15px;
margin-bottom: 15px;
}
.instruction-item:last-child { margin-bottom: 0; }
.instruction-icon {
width: 32px; height: 32px;
background: var(--bg-light);
color: var(--primary-color);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-weight: 800;
font-size: 0.8rem;
}
.pulse-scanner {
width: 100px; height: 100px;
border: 4px solid var(--primary-color);
border-radius: 50%;
position: relative;
animation: pulse 2s infinite;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 2rem;
}
@keyframes pulse {
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 77, 148, 0.7); }
70% { transform: scale(1); box-shadow: 0 0 0 20px rgba(255, 77, 148, 0); }
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(255, 77, 148, 0); }
}
.notice-box {
background: #fff5f8;
border-left: 4px solid var(--primary-color);
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
}
.accordion-button:not(.collapsed) {
background-color: rgba(255, 77, 148, 0.05);
color: var(--primary-color);
}
@media (max-width: 992px) {
.main-content { margin-left: 80px; padding: 1.5rem; }
}
</style>
</head>
<body>
<?php include 'includes/sidebar.php'; ?>
<div class="main-content">
<div class="mb-5 d-flex justify-content-between align-items-end">
<div>
<h2 class="fw-bold mb-1"><i class="fas fa-wallet me-2 text-primary"></i> 资产充值 (Asset Recharge)</h2>
<p class="text-muted mb-0">USDT 全自动到账,由区块链共识网络提供安全保障</p>
</div>
<div class="text-end">
<span class="small text-muted fw-bold d-block">当前可用余额</span>
<span class="h4 fw-bold text-primary mb-0">$<?= number_format($user['balance'], 2) ?></span>
</div>
</div>
<div class="row g-4">
<div class="col-xl-8">
<div class="card-neo">
<!-- Step 1: Input Amount -->
<div id="step1" class="step-container active">
<h4 class="fw-bold mb-4">1. 设定充值金额 (Set Amount)</h4>
<div class="notice-box">
<p class="small mb-0">
<i class="fas fa-info-circle me-1"></i> 为了识别您的充值订单,系统会自动为您的金额添加<strong>唯一的随机小数</strong>。请在支付时务必支付<strong>包含小数点的精确金额</strong>。
</p>
</div>
<div class="mb-4">
<label class="form-label fw-bold text-muted small">充值金额 (USDT)</label>
<div class="input-group">
<span class="input-group-text bg-white border-end-0 border-2" style="border-color: var(--border-color); color: var(--primary-color); font-weight: 800; padding: 0 25px;">$</span>
<input type="number" id="inputAmount" class="form-control border-start-0 border-2" style="border-color: var(--border-color); font-size: 1.8rem; font-weight: 800;" placeholder="10.00" 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 btn-outline-pink w-100 py-3 border-2 rounded-4" style="border-color: var(--border-color); color: var(--primary-color); font-weight: 800;" onclick="setAmount(<?= $amt ?>)"><?= $amt ?></button>
</div>
<?php endforeach; ?>
</div>
<button class="btn btn-primary w-100 py-4" onclick="confirmOrder()">
<i class="fas fa-arrow-right me-2"></i> 生成专属支付订单
</button>
</div>
<!-- Step 2: Payment Details -->
<div id="step2" class="step-container">
<div class="d-flex justify-content-between align-items-center mb-5">
<div>
<h4 class="fw-bold mb-1">2. 完成链上支付</h4>
<p class="text-muted small mb-0" id="orderIdDisplay">正在创建订单...</p>
</div>
<div class="text-end bg-light p-3 rounded-4 border">
<div class="h2 fw-bold text-primary mb-0" id="displayAmount">0.00</div>
<div class="small text-muted fw-bold">待支付精确金额 (USDT)</div>
</div>
</div>
<div class="row g-4 mb-4">
<div class="col-md-6">
<label class="form-label fw-bold text-muted mb-3">A. 选择支付网络</label>
<div class="row g-2">
<div class="col-6">
<div class="network-btn active" id="btnTRC" onclick="selectNetwork('TRC20')">
<div class="fw-bold">TRC20</div>
<div class="small opacity-50" style="font-size: 0.6rem;">波场/TRON</div>
</div>
</div>
<div class="col-6">
<div class="network-btn" id="btnERC" onclick="selectNetwork('ERC20')">
<div class="fw-bold">ERC20</div>
<div class="small opacity-50" style="font-size: 0.6rem;">以太坊/ETH</div>
</div>
</div>
</div>
<div class="mt-4 p-4 rounded-4" style="background: #fff9f0; border: 1px solid #ffeeba;">
<p class="small mb-0 text-dark">
<i class="fas fa-exclamation-triangle text-warning me-2"></i>
<strong>必看操作指南:</strong> <br>
1. 充值网络必须与您钱包发送的网络一致,否则资产将丢失。<br>
2. 支付金额必须<strong>精确到小数点后两位</strong>,这是识别您身份的唯一凭据。<br>
3. 支付完成后,请<strong>停留在本页面</strong>。系统检测到款项后会自动为您跳转。
</p>
</div>
</div>
<div class="col-md-6 text-center">
<div class="qr-container" id="qrcode"></div>
<div class="mt-2">
<span class="badge bg-light text-primary p-2 px-4 rounded-pill fw-bold border">
<i class="fas fa-hourglass-half me-2"></i> 订单监听剩余时间: <span id="countdown">60:00</span>
</span>
</div>
</div>
</div>
<div class="mb-5">
<label class="text-muted small mb-2 d-block fw-bold">B. 复制专属收款地址</label>
<div class="address-container">
<div class="address-box" id="addressBox">正在获取地址...</div>
<button class="copy-btn" onclick="copyAddress()" title="点击复制"><i class="fas fa-copy"></i></button>
</div>
</div>
<div class="d-flex flex-column align-items-center py-5 border-top">
<div class="pulse-scanner">
<i class="fas fa-broadcast-tower text-primary h3 mb-0"></i>
</div>
<h5 class="fw-bold mb-2">正在实时监控收款地址...</h5>
<p class="text-muted small mb-4 text-center">
我们正在 24/7 监听区块链节点数据。一旦检测到与您金额匹配的款项,将立即跳转。<br>
<span class="text-primary fw-bold" id="pollingStatus">正在连接区块链节点...</span>
</p>
<div class="d-flex gap-3 w-100">
<button class="btn btn-light flex-grow-1 py-3 rounded-4 fw-bold border" onclick="goBackToStep1()">
<i class="fas fa-edit me-1"></i> 修改金额
</button>
<button class="btn btn-primary flex-grow-1 py-3 rounded-4" onclick="checkStatusManual()">
<i class="fas fa-sync-alt me-1"></i> 手动刷新到账状态
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4">
<div class="card-neo">
<h5 class="fw-bold mb-4"><i class="fas fa-question-circle me-2 text-primary"></i> 常见问题 (FAQ)</h5>
<div class="accordion accordion-flush" id="faqAccordion">
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
<h2 class="accordion-header">
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq1">
为什么要支付包含随机小数的金额?
</button>
</h2>
<div id="faq1" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body small pt-0 opacity-75">
为了实现无人值守的自动化到账,系统需要区分不同用户的充值。通过为每个订单分配独特的随机小数(如 100.42),我们可以在收款地址检测到对应的精确数值时,瞬间识别出是您的充值并自动入账,无需您手动上传截图。
</div>
</div>
</div>
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
<h2 class="accordion-header">
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq2">
支付后多久可以到账?
</button>
</h2>
<div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body small pt-0 opacity-75">
到账时间取决于区块链网络的确认速度。TRC20 网络通常在 1-2 分钟内完成确认ERC20 网络视网络拥堵情况可能需要 5-10 分钟。一旦网络确认达到 1 个确认数,系统将立即为您增加余额。
</div>
</div>
</div>
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
<h2 class="accordion-header">
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq3">
如果金额支付错误(未支付小数)怎么办?
</button>
</h2>
<div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body small pt-0 opacity-75">
如果您支付的金额不包含指定的小数,系统将无法自动识别您的订单。这种情况下,请务必保留您的转账截图和 TXID并联系在线客服进行人工手动审核和上分。
</div>
</div>
</div>
<div class="accordion-item border-0 mb-3 bg-light rounded-4 overflow-hidden">
<h2 class="accordion-header">
<button class="accordion-button collapsed bg-transparent fw-bold small py-3" type="button" data-bs-toggle="collapse" data-bs-target="#faq4">
支持哪些代币充值?
</button>
</h2>
<div id="faq4" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body small pt-0 opacity-75">
目前系统仅支持 USDT (Tether) 充值。请确保您在转账时选择了正确的币种。请勿向收款地址发送非 USDT 资产,否则资产将永久丢失。
</div>
</div>
</div>
</div>
<div class="mt-4 p-4 bg-primary text-white rounded-4 shadow-sm">
<h6 class="fw-bold mb-2"><i class="fas fa-headset me-2"></i> 充值遇到问题?</h6>
<p class="small mb-3 opacity-75">我们提供 24/7 全天候技术支持。如果您的充值超过 30 分钟未到账,请通过工单联系我们。</p>
<a href="support.php" class="btn btn-white btn-sm w-100 fw-bold">联系在线客服</a>
</div>
</div>
<div class="card-neo mt-4 border-primary">
<h6 class="fw-bold mb-3">区块链监控节点状态</h6>
<div class="d-flex align-items-center mb-2">
<div class="spinner-grow spinner-grow-sm text-success me-2" style="animation-duration: 3s;"></div>
<span class="small text-muted">TRON (TRC20) 节点: <span class="text-success fw-bold">Active</span></span>
</div>
<div class="d-flex align-items-center mb-2">
<div class="spinner-grow spinner-grow-sm text-success me-2" style="animation-duration: 2.5s;"></div>
<span class="small text-muted">Ethereum (ERC20) 节点: <span class="text-success fw-bold">Active</span></span>
</div>
<div class="d-flex align-items-center">
<div class="spinner-grow spinner-grow-sm text-success me-2" style="animation-duration: 4s;"></div>
<span class="small text-muted">API 网关同步: <span class="text-success fw-bold">Synced</span></span>
</div>
</div>
</div>
</div>
</div>
<div class="status-overlay" id="successOverlay">
<div class="mb-4"><i class="fas fa-check-circle text-success" style="font-size: 8rem;"></i></div>
<h1 class="fw-bold mb-2">支付已确认!</h1>
<p class="text-muted h5">系统已识别您的充值并自动入账,正在跳转工作台...</p>
</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 < 10) { alert('最低充值金额为 10 USDT'); return; }
const btn = event.currentTarget;
const oldHtml = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner 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 = '支付流水号: #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 = oldHtml;
}
} catch (e) {
console.error(e);
btn.disabled = false;
btn.innerHTML = oldHtml;
}
}
function startPollingStatus() {
const statusEl = document.getElementById('pollingStatus');
let dots = '';
pollInterval = setInterval(async () => {
dots = dots.length >= 3 ? '' : dots + '.';
statusEl.textContent = '实时同步区块数据中' + dots;
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);
document.getElementById('successOverlay').classList.add('show');
setTimeout(() => window.location.href = 'dashboard.php', 3000);
}
} catch (e) {}
}, 3000);
}
async function checkStatusManual() {
const btn = event.currentTarget;
const oldHtml = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></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') {
document.getElementById('successOverlay').classList.add('show');
setTimeout(() => window.location.href = 'dashboard.php', 3000);
} else {
setTimeout(() => {
btn.disabled = false;
btn.innerHTML = oldHtml;
}, 1000);
}
} catch (e) {
btn.disabled = false;
btn.innerHTML = oldHtml;
}
}
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('.copy-btn');
const oldHtml = btn.innerHTML;
btn.innerHTML = '<i class="fas fa-check"></i>';
setTimeout(() => btn.innerHTML = oldHtml, 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);
const btn = document.querySelector('#step1 button.btn-primary');
btn.disabled = false;
}
</script>
</body>
</html>