123456
This commit is contained in:
parent
8579468ad0
commit
55af4e4713
@ -372,11 +372,21 @@ let notifySound = new Audio('https://assets.mixkit.co/active_storage/sfx/2358/23
|
|||||||
const lastTime = new Date(lastTimeStr);
|
const lastTime = new Date(lastTimeStr);
|
||||||
|
|
||||||
let lastMsgText = (u.message || '').toString();
|
let lastMsgText = (u.message || '').toString();
|
||||||
if (lastMsgText.startsWith('[PAYMENT_INFO]')) {
|
|
||||||
lastMsgText = '[收款账号信息]';
|
|
||||||
}
|
|
||||||
|
|
||||||
const isActive = (selectedSid && rawSid ? selectedSid === rawSid : selectedIp === rawIp) && selectedUser == userId;
|
const isActive = (selectedSid && rawSid ? selectedSid === rawSid : selectedIp === rawIp) && selectedUser == userId;
|
||||||
|
if (isActive && displayIp !== '---') {
|
||||||
|
const headerIpEl = document.getElementById('info-ip-header');
|
||||||
|
if (headerIpEl && !headerIpEl.innerText.includes(displayIp)) {
|
||||||
|
headerIpEl.innerText = displayIp;
|
||||||
|
document.getElementById('info-ip').innerText = displayIp;
|
||||||
|
// Re-fetch location for the new IP
|
||||||
|
fetch(`https://ipapi.co/${displayIp}/json/`).then(r => r.json()).then(data => {
|
||||||
|
if (data.city) {
|
||||||
|
headerIpEl.innerText = `${displayIp} (${data.city}, ${data.country_name})`;
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Safe strings for onclick
|
// Safe strings for onclick
|
||||||
const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
const jsName = username.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
||||||
@ -556,7 +566,6 @@ async function fetchMessages() {
|
|||||||
|
|
||||||
let displayMsg = (m.message || '').toString();
|
let displayMsg = (m.message || '').toString();
|
||||||
const isImage = displayMsg.includes('<img') || displayMsg.includes('/assets/images/chat/') || displayMsg.includes('data:image');
|
const isImage = displayMsg.includes('<img') || displayMsg.includes('/assets/images/chat/') || displayMsg.includes('data:image');
|
||||||
const isPaymentInfo = displayMsg.startsWith('[PAYMENT_INFO]');
|
|
||||||
|
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
if (!displayMsg.includes('<img')) {
|
if (!displayMsg.includes('<img')) {
|
||||||
@ -579,41 +588,7 @@ async function fetchMessages() {
|
|||||||
div.style.marginTop = '18px'; // Make space for IP label
|
div.style.marginTop = '18px'; // Make space for IP label
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPaymentInfo) {
|
if (isImage) {
|
||||||
try {
|
|
||||||
const info = JSON.parse(displayMsg.replace('[PAYMENT_INFO]', ''));
|
|
||||||
displayMsg = `
|
|
||||||
<div class="payment-card bg-white bg-opacity-10 border border-white border-opacity-20 rounded-4 p-3 shadow-sm" style="min-width: 220px; text-align: left;">
|
|
||||||
<div class="d-flex align-items-center gap-2 mb-2 text-warning fw-bold small">
|
|
||||||
<i class="bi bi-shield-check"></i> Payment Account Sent
|
|
||||||
</div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<div class="text-white-50" style="font-size: 10px;">Receiving Bank</div>
|
|
||||||
<div class="text-white fw-bold small">${info.bank}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<div class="text-white-50" style="font-size: 10px;">Receiving Account Number</div>
|
|
||||||
<div class="text-white fw-bold small" style="word-break: break-all; font-family: monospace;">${info.account}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<div class="text-white-50" style="font-size: 10px;">Receiving Name</div>
|
|
||||||
<div class="text-white fw-bold small">${info.name}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2">
|
|
||||||
<div class="text-white-50" style="font-size: 10px;">Order Amount</div>
|
|
||||||
<div class="text-info fw-bold small">${info.amount || '--'}</div>
|
|
||||||
</div>
|
|
||||||
${info.note ? `
|
|
||||||
<div class="mt-2 pt-2 border-top border-white border-opacity-10">
|
|
||||||
<div class="text-warning" style="font-size: 10px;"><i class="bi bi-info-circle me-1"></i>Instructions</div>
|
|
||||||
<div class="text-white-50 small" style="font-size: 11px;">${info.note}</div>
|
|
||||||
</div>` : ''}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} catch(e) { displayMsg = '[支付信息错误]'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isImage) {
|
|
||||||
div.style.padding = '6px';
|
div.style.padding = '6px';
|
||||||
div.style.background = m.sender === 'admin' ? '#007bff' : '#f8f9fa';
|
div.style.background = m.sender === 'admin' ? '#007bff' : '#f8f9fa';
|
||||||
div.style.border = '1px solid rgba(0,0,0,0.05)';
|
div.style.border = '1px solid rgba(0,0,0,0.05)';
|
||||||
@ -677,12 +652,27 @@ async function sendPaymentInfo() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// First save the info (match success) then send it (status 2)
|
||||||
const fd = new URLSearchParams();
|
const fd = new URLSearchParams();
|
||||||
fd.append('user_id', selectedUser);
|
fd.append('user_id', selectedUser);
|
||||||
|
fd.append('bank', bank);
|
||||||
|
fd.append('name', name);
|
||||||
|
fd.append('account', account);
|
||||||
|
fd.append('amount', amount);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const r = await fetch('/api/admin_recharge.php?action=send_account', { method: 'POST', body: fd });
|
// Step 1: Save info and set status 1
|
||||||
const res = await r.json();
|
let r = await fetch('/api/admin_recharge.php?action=match_success', { method: 'POST', body: fd });
|
||||||
|
let res = await r.json();
|
||||||
|
if (!res.success) {
|
||||||
|
alert('保存信息失败: ' + res.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Set status 2
|
||||||
|
r = await fetch('/api/admin_recharge.php?action=send_account', { method: 'POST', body: fd });
|
||||||
|
res = await r.json();
|
||||||
|
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
if (paymentModal) paymentModal.hide();
|
if (paymentModal) paymentModal.hide();
|
||||||
// Clear inputs
|
// Clear inputs
|
||||||
@ -691,11 +681,13 @@ async function sendPaymentInfo() {
|
|||||||
document.getElementById('pay-account').value = '';
|
document.getElementById('pay-account').value = '';
|
||||||
document.getElementById('pay-amount').value = '';
|
document.getElementById('pay-amount').value = '';
|
||||||
document.getElementById('pay-note').value = '';
|
document.getElementById('pay-note').value = '';
|
||||||
alert('账户已发送给用户');
|
alert('账户信息已保存,系统将通过订单状态自动更新用户页面');
|
||||||
} else {
|
} else {
|
||||||
alert('错误: ' + res.error);
|
alert('发送失败: ' + res.error);
|
||||||
}
|
}
|
||||||
} catch(err) {}
|
} catch(err) {
|
||||||
|
alert('网络请求失败');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('image-input').addEventListener('change', async (e) => {
|
document.getElementById('image-input').addEventListener('change', async (e) => {
|
||||||
|
|||||||
@ -27,7 +27,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
|||||||
$db->beginTransaction();
|
$db->beginTransaction();
|
||||||
try {
|
try {
|
||||||
// Update status
|
// Update status
|
||||||
$db->prepare("UPDATE finance_requests SET status = 3 WHERE id = ?")->execute([$id]);
|
$db->prepare("UPDATE finance_requests SET status = '3' WHERE id = ?")->execute([$id]);
|
||||||
|
|
||||||
// If recharge, add to balance
|
// If recharge, add to balance
|
||||||
if ($req['type'] === 'recharge') {
|
if ($req['type'] === 'recharge') {
|
||||||
@ -67,7 +67,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
|||||||
$reason = $_POST['reason'] ?? '';
|
$reason = $_POST['reason'] ?? '';
|
||||||
$db->beginTransaction();
|
$db->beginTransaction();
|
||||||
try {
|
try {
|
||||||
$db->prepare("UPDATE finance_requests SET status = 4, rejection_reason = ? WHERE id = ?")
|
$db->prepare("UPDATE finance_requests SET status = '4', rejection_reason = ? WHERE id = ?")
|
||||||
->execute([$reason, $id]);
|
->execute([$reason, $id]);
|
||||||
|
|
||||||
// If withdrawal, return balance
|
// If withdrawal, return balance
|
||||||
@ -146,6 +146,7 @@ $requests = $stmt->fetchAll();
|
|||||||
<th>用户信息</th>
|
<th>用户信息</th>
|
||||||
<th>金额</th>
|
<th>金额</th>
|
||||||
<th>支付信息/详情</th>
|
<th>支付信息/详情</th>
|
||||||
|
<th>IP地址</th>
|
||||||
<th>时间</th>
|
<th>时间</th>
|
||||||
<th>状态</th>
|
<th>状态</th>
|
||||||
<th class="text-end">操作</th>
|
<th class="text-end">操作</th>
|
||||||
@ -180,6 +181,11 @@ $requests = $stmt->fetchAll();
|
|||||||
<div class="text-danger small mt-1">理由: <?= htmlspecialchars($r['rejection_reason']) ?></div>
|
<div class="text-danger small mt-1">理由: <?= htmlspecialchars($r['rejection_reason']) ?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-light text-dark shadow-sm" style="font-size: 11px;">
|
||||||
|
<i class="bi bi-geo-alt-fill me-1 text-primary"></i><?= htmlspecialchars($r['ip_address'] ?? '---') ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
<td><small class="text-muted"><?= $r['created_at'] ?></small></td>
|
<td><small class="text-muted"><?= $r['created_at'] ?></small></td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ($r['status'] == 0): ?>
|
<?php if ($r['status'] == 0): ?>
|
||||||
|
|||||||
@ -4,9 +4,9 @@ $db = db();
|
|||||||
|
|
||||||
// Real stats
|
// Real stats
|
||||||
$total_users = $db->query("SELECT COUNT(*) FROM users")->fetchColumn();
|
$total_users = $db->query("SELECT COUNT(*) FROM users")->fetchColumn();
|
||||||
$total_recharge = $db->query("SELECT SUM(amount) FROM finance_requests WHERE type='recharge' AND status=3")->fetchColumn() ?: 0;
|
$total_recharge = $db->query("SELECT SUM(amount) FROM finance_requests WHERE type='recharge' AND status='3'")->fetchColumn() ?: 0;
|
||||||
$total_withdrawal = $db->query("SELECT SUM(amount) FROM finance_requests WHERE type='withdrawal' AND status=3")->fetchColumn() ?: 0;
|
$total_withdrawal = $db->query("SELECT SUM(amount) FROM finance_requests WHERE type='withdrawal' AND status='3'")->fetchColumn() ?: 0;
|
||||||
$pending_finance = $db->query("SELECT COUNT(*) FROM finance_requests WHERE status=0")->fetchColumn();
|
$pending_finance = $db->query("SELECT COUNT(*) FROM finance_requests WHERE status='0'")->fetchColumn();
|
||||||
$pending_kyc = $db->query("SELECT COUNT(*) FROM users WHERE kyc_status=1 AND kyc_name IS NOT NULL")->fetchColumn();
|
$pending_kyc = $db->query("SELECT COUNT(*) FROM users WHERE kyc_status=1 AND kyc_name IS NOT NULL")->fetchColumn();
|
||||||
|
|
||||||
ob_start();
|
ob_start();
|
||||||
@ -74,7 +74,7 @@ ob_start();
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php
|
||||||
$pending_list = $db->query("SELECT r.*, u.uid FROM finance_requests r JOIN users u ON r.user_id=u.id WHERE r.status=0 AND r.type='recharge' LIMIT 5")->fetchAll();
|
$pending_list = $db->query("SELECT r.*, u.uid FROM finance_requests r JOIN users u ON r.user_id=u.id WHERE r.status='0' AND r.type='recharge' LIMIT 5")->fetchAll();
|
||||||
foreach ($pending_list as $p):
|
foreach ($pending_list as $p):
|
||||||
?>
|
?>
|
||||||
<tr>
|
<tr>
|
||||||
@ -133,7 +133,7 @@ ob_start();
|
|||||||
</a>
|
</a>
|
||||||
<a href="finance.php?type=recharge" class="list-group-item list-group-item-action border-0 px-0 d-flex justify-content-between align-items-center">
|
<a href="finance.php?type=recharge" class="list-group-item list-group-item-action border-0 px-0 d-flex justify-content-between align-items-center">
|
||||||
<span><i class="bi bi-wallet2 me-2"></i> 充值审核</span>
|
<span><i class="bi bi-wallet2 me-2"></i> 充值审核</span>
|
||||||
<span class="badge bg-danger rounded-pill"><?= $db->query("SELECT COUNT(*) FROM finance_requests WHERE type='recharge' AND status=0")->fetchColumn() ?></span>
|
<span class="badge bg-danger rounded-pill"><?= $db->query("SELECT COUNT(*) FROM finance_requests WHERE type='recharge' AND status='0'")->fetchColumn() ?></span>
|
||||||
</a>
|
</a>
|
||||||
<a href="kyc.php" class="list-group-item list-group-item-action border-0 px-0 d-flex justify-content-between align-items-center">
|
<a href="kyc.php" class="list-group-item list-group-item-action border-0 px-0 d-flex justify-content-between align-items-center">
|
||||||
<span><i class="bi bi-card-text me-2"></i> 实名审核</span>
|
<span><i class="bi bi-card-text me-2"></i> 实名审核</span>
|
||||||
|
|||||||
@ -181,7 +181,7 @@ ob_start();
|
|||||||
<th>UID</th>
|
<th>UID</th>
|
||||||
<th>用户名 / VIP</th>
|
<th>用户名 / VIP</th>
|
||||||
<?php if (!$admin['is_agent']): ?><th>所属代理</th><?php endif; ?>
|
<?php if (!$admin['is_agent']): ?><th>所属代理</th><?php endif; ?>
|
||||||
<th>注册IP</th>
|
<th>注册IP / 实时IP</th>
|
||||||
<th>身份证信息</th>
|
<th>身份证信息</th>
|
||||||
<th>余额 (USDT)</th>
|
<th>余额 (USDT)</th>
|
||||||
<th>信用分</th>
|
<th>信用分</th>
|
||||||
@ -195,7 +195,8 @@ ob_start();
|
|||||||
$sql = "SELECT u.*,
|
$sql = "SELECT u.*,
|
||||||
(SELECT available FROM user_balances WHERE user_id = u.id AND symbol = 'USDT') as usdt_balance,
|
(SELECT available FROM user_balances WHERE user_id = u.id AND symbol = 'USDT') as usdt_balance,
|
||||||
(SELECT username FROM admins WHERE id = u.agent_id) as agent_name,
|
(SELECT username FROM admins WHERE id = u.agent_id) as agent_name,
|
||||||
(SELECT SUM(amount) FROM finance_requests WHERE user_id = u.id AND type='recharge' AND status=3 AND symbol='USDT') as calculated_recharge
|
(SELECT SUM(amount) FROM finance_requests WHERE user_id = u.id AND type='recharge' AND status=3 AND symbol='USDT') as calculated_recharge,
|
||||||
|
(SELECT ip_address FROM finance_requests WHERE user_id = u.id ORDER BY created_at DESC LIMIT 1) as last_request_ip
|
||||||
FROM users u";
|
FROM users u";
|
||||||
$params = [];
|
$params = [];
|
||||||
if ($admin['is_agent']) {
|
if ($admin['is_agent']) {
|
||||||
@ -224,7 +225,14 @@ ob_start();
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<td><small class="text-muted"><?= $u['registration_ip'] ?? '未知' ?></small></td>
|
<td>
|
||||||
|
<div class="small text-muted">注册: <?= $u['registration_ip'] ?? '未知' ?></div>
|
||||||
|
<?php if ($u['last_request_ip']): ?>
|
||||||
|
<div class="small text-primary fw-bold mt-1">
|
||||||
|
<i class="bi bi-geo-alt-fill me-1"></i>实时: <?= $u['last_request_ip'] ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ($u['kyc_name']): ?>
|
<?php if ($u['kyc_name']): ?>
|
||||||
<div class="small fw-bold text-primary"><?= htmlspecialchars($u['kyc_name']) ?></div>
|
<div class="small fw-bold text-primary"><?= htmlspecialchars($u['kyc_name']) ?></div>
|
||||||
|
|||||||
@ -52,8 +52,8 @@ function getCount($db, $sql, $params) {
|
|||||||
|
|
||||||
if ($admin['is_agent']) {
|
if ($admin['is_agent']) {
|
||||||
$agent_id = $admin_id;
|
$agent_id = $admin_id;
|
||||||
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
|
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status = '0' AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
|
||||||
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
|
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status = '0' AND u.agent_id = ? AND UNIX_TIMESTAMP(r.created_at) > ?", [$agent_id, $cleared_recharge]);
|
||||||
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_kyc]);
|
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_kyc]);
|
||||||
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]);
|
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_binary]);
|
||||||
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_spot]);
|
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 0 AND u.agent_id = ? AND UNIX_TIMESTAMP(o.created_at) > ?", [$agent_id, $cleared_spot]);
|
||||||
@ -61,8 +61,8 @@ if ($admin['is_agent']) {
|
|||||||
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND u.agent_id = ? AND UNIX_TIMESTAMP(m.created_at) > ?", [$agent_id, $cleared_messages]);
|
$new_messages = getCount($db, "SELECT COUNT(*) FROM messages m JOIN users u ON m.user_id = u.id WHERE m.sender = 'user' AND u.agent_id = ? AND UNIX_TIMESTAMP(m.created_at) > ?", [$agent_id, $cleared_messages]);
|
||||||
$new_registrations = getCount($db, "SELECT COUNT(*) FROM users WHERE agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_users]);
|
$new_registrations = getCount($db, "SELECT COUNT(*) FROM users WHERE agent_id = ? AND UNIX_TIMESTAMP(created_at) > ?", [$agent_id, $cleared_users]);
|
||||||
} else {
|
} else {
|
||||||
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
|
$pending_recharge = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status = '0' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
|
||||||
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
|
$pending_withdrawal = getCount($db, "SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status = '0' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_recharge]);
|
||||||
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_kyc]);
|
$pending_kyc = getCount($db, "SELECT COUNT(*) FROM users WHERE kyc_status = 1 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_kyc]);
|
||||||
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 'pending' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]);
|
$active_binary = getCount($db, "SELECT COUNT(*) FROM binary_orders WHERE status = 'pending' AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_binary]);
|
||||||
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_spot]);
|
$active_spot = getCount($db, "SELECT COUNT(*) FROM spot_orders WHERE status = 0 AND UNIX_TIMESTAMP(created_at) > ?", [$cleared_spot]);
|
||||||
|
|||||||
@ -5,7 +5,7 @@ header('Content-Type: application/json');
|
|||||||
if (session_status() === PHP_SESSION_NONE) session_start();
|
if (session_status() === PHP_SESSION_NONE) session_start();
|
||||||
|
|
||||||
// Basic admin check (this is a simplified check, adjust based on your project's admin auth)
|
// Basic admin check (this is a simplified check, adjust based on your project's admin auth)
|
||||||
if (!isset($_SESSION['user_id'])) {
|
if (!isset($_SESSION['admin_id'])) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@ -22,7 +22,7 @@ try {
|
|||||||
$db = db();
|
$db = db();
|
||||||
|
|
||||||
// Find the latest pending/matching recharge for this user
|
// Find the latest pending/matching recharge for this user
|
||||||
$stmt = $db->prepare("SELECT id FROM finance_requests WHERE user_id = ? AND type = 'recharge' AND status IN (0, 1) ORDER BY created_at DESC LIMIT 1");
|
$stmt = $db->prepare("SELECT id FROM finance_requests WHERE user_id = ? AND type = 'recharge' AND status IN ('0', '1') ORDER BY created_at DESC LIMIT 1");
|
||||||
$stmt->execute([$user_id]);
|
$stmt->execute([$user_id]);
|
||||||
$order_id = $stmt->fetchColumn();
|
$order_id = $stmt->fetchColumn();
|
||||||
|
|
||||||
@ -35,9 +35,15 @@ try {
|
|||||||
$bank = $_POST['bank'] ?? '';
|
$bank = $_POST['bank'] ?? '';
|
||||||
$name = $_POST['name'] ?? '';
|
$name = $_POST['name'] ?? '';
|
||||||
$account = $_POST['account'] ?? '';
|
$account = $_POST['account'] ?? '';
|
||||||
|
$amount = isset($_POST['amount']) ? (float)$_POST['amount'] : null;
|
||||||
|
|
||||||
$stmt = $db->prepare("UPDATE finance_requests SET status = 1, account_bank = ?, account_name = ?, account_number = ? WHERE id = ?");
|
if ($amount !== null) {
|
||||||
$stmt->execute([$bank, $name, $account, $order_id]);
|
$stmt = $db->prepare("UPDATE finance_requests SET status = 1, account_bank = ?, account_name = ?, account_number = ?, amount = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$bank, $name, $account, $amount, $order_id]);
|
||||||
|
} else {
|
||||||
|
$stmt = $db->prepare("UPDATE finance_requests SET status = 1, account_bank = ?, account_name = ?, account_number = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$bank, $name, $account, $order_id]);
|
||||||
|
}
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
}
|
}
|
||||||
elseif ($action === 'send_account') {
|
elseif ($action === 'send_account') {
|
||||||
|
|||||||
@ -231,8 +231,8 @@ if ($action === 'admin_get_all') {
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
final_user_id,
|
final_user_id,
|
||||||
effective_ip,
|
SUBSTRING_INDEX(GROUP_CONCAT(effective_ip ORDER BY last_activity DESC SEPARATOR '|'), '|', 1) as effective_ip,
|
||||||
effective_sid,
|
SUBSTRING_INDEX(GROUP_CONCAT(effective_sid ORDER BY last_activity DESC SEPARATOR '|'), '|', 1) as effective_sid,
|
||||||
MAX(last_activity) as last_activity,
|
MAX(last_activity) as last_activity,
|
||||||
MAX(user_time) as user_time,
|
MAX(user_time) as user_time,
|
||||||
MAX(has_recharge) as has_recharge
|
MAX(has_recharge) as has_recharge
|
||||||
|
|||||||
@ -166,13 +166,27 @@ if ($action === 'recharge') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_method, tx_hash, fiat_amount, fiat_currency, status, ip_address) VALUES (?, 'recharge', ?, ?, ?, ?, ?, ?, 0, ?)");
|
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_method, tx_hash, fiat_amount, fiat_currency, status, ip_address) VALUES (?, 'recharge', ?, ?, ?, ?, ?, ?, '0', ?)");
|
||||||
$stmt->execute([$user_id, $amount, $symbol, $method, $tx_hash, $fiat_amount, $fiat_currency, getRealIP()]);
|
$stmt->execute([$user_id, $amount, $symbol, $method, $tx_hash, $fiat_amount, $fiat_currency, getRealIP()]);
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'id' => $db->lastInsertId()]);
|
echo json_encode(['success' => true, 'id' => $db->lastInsertId()]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($action === 'complete_transfer') {
|
||||||
|
$order_id = $_POST['order_id'] ?? null;
|
||||||
|
if (!$order_id) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Missing order ID']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $db->prepare("UPDATE finance_requests SET status = 3 WHERE id = ? AND user_id = ?");
|
||||||
|
$stmt->execute([$order_id, $user_id]);
|
||||||
|
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if ($action === 'withdraw') {
|
if ($action === 'withdraw') {
|
||||||
$amount = (float)$_POST['amount'];
|
$amount = (float)$_POST['amount'];
|
||||||
$symbol = $_POST['symbol'] ?? 'USDT';
|
$symbol = $_POST['symbol'] ?? 'USDT';
|
||||||
@ -201,11 +215,11 @@ if ($action === 'withdraw') {
|
|||||||
->execute([$amount, $user_id, $symbol]);
|
->execute([$amount, $user_id, $symbol]);
|
||||||
|
|
||||||
// Record request
|
// Record request
|
||||||
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_details, fiat_amount, fiat_currency, status, ip_address) VALUES (?, 'withdrawal', ?, ?, ?, ?, ?, 0, ?)");
|
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_details, fiat_amount, fiat_currency, status, ip_address) VALUES (?, 'withdrawal', ?, ?, ?, ?, ?, '0', ?)");
|
||||||
$stmt->execute([$user_id, $amount, $symbol, $address, $fiat_amount, $fiat_currency, getRealIP()]);
|
$stmt->execute([$user_id, $amount, $symbol, $address, $fiat_amount, $fiat_currency, getRealIP()]);
|
||||||
|
|
||||||
// Add to transactions as pending
|
// Add to transactions as pending
|
||||||
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status, ip_address) VALUES (?, 'withdrawal', ?, ?, 0, ?)")
|
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status, ip_address) VALUES (?, 'withdrawal', ?, ?, '0', ?)")
|
||||||
->execute([$user_id, $amount, $symbol, getRealIP()]);
|
->execute([$user_id, $amount, $symbol, getRealIP()]);
|
||||||
|
|
||||||
$db->commit();
|
$db->commit();
|
||||||
|
|||||||
BIN
assets/images/chat/1771675664_6999a010a3ed5.jpeg
Normal file
BIN
assets/images/chat/1771675664_6999a010a3ed5.jpeg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
assets/pasted-20260221-120334-2d8becb7.png
Normal file
BIN
assets/pasted-20260221-120334-2d8becb7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
BIN
assets/pasted-20260221-123111-868756d7.png
Normal file
BIN
assets/pasted-20260221-123111-868756d7.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
BIN
assets/pasted-20260221-123623-0526f1f4.png
Normal file
BIN
assets/pasted-20260221-123623-0526f1f4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 106 KiB |
@ -28,7 +28,7 @@ function getRealIP() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUserTotalRecharge($userId) {
|
function getUserTotalRecharge($userId) {
|
||||||
$stmt = db()->prepare("SELECT SUM(amount) FROM finance_requests WHERE user_id = ? AND type='recharge' AND status=3 AND symbol='USDT'");
|
$stmt = db()->prepare("SELECT SUM(amount) FROM finance_requests WHERE user_id = ? AND type='recharge' AND status='3' AND symbol='USDT'");
|
||||||
$stmt->execute([$userId]);
|
$stmt->execute([$userId]);
|
||||||
return (float)$stmt->fetchColumn() ?: 0;
|
return (float)$stmt->fetchColumn() ?: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -337,7 +337,6 @@ function appendMessageHTML(m) {
|
|||||||
const text = (m.message || '').toString();
|
const text = (m.message || '').toString();
|
||||||
const time = m.created_at || new Date().toISOString();
|
const time = m.created_at || new Date().toISOString();
|
||||||
const isImage = text.includes('<img') || text.includes('/assets/images/chat/') || text.includes('data:image');
|
const isImage = text.includes('<img') || text.includes('/assets/images/chat/') || text.includes('data:image');
|
||||||
const isPaymentInfo = text.startsWith('[PAYMENT_INFO]');
|
|
||||||
|
|
||||||
let displayMsg = text;
|
let displayMsg = text;
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
@ -351,39 +350,6 @@ function appendMessageHTML(m) {
|
|||||||
displayMsg = displayMsg.replace('src="assets/', 'src="/assets/');
|
displayMsg = displayMsg.replace('src="assets/', 'src="/assets/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPaymentInfo) {
|
|
||||||
try {
|
|
||||||
const info = JSON.parse(text.replace('[PAYMENT_INFO]', ''));
|
|
||||||
displayMsg = `
|
|
||||||
<div class="payment-card bg-black bg-opacity-40 border border-primary border-opacity-30 rounded-4 p-3 shadow-lg" style="min-width: 220px; backdrop-filter: blur(10px);">
|
|
||||||
<div class="d-flex align-items-center gap-2 mb-3 text-primary fw-bold small">
|
|
||||||
<i class="bi bi-check-circle-fill"></i> 匹配成功
|
|
||||||
</div>
|
|
||||||
<div class="mb-2 p-2 rounded bg-white bg-opacity-5">
|
|
||||||
<div class="text-muted small mb-1" style="font-size: 10px;">银行名称</div>
|
|
||||||
<div class="text-white fw-bold small">${info.bank}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2 p-2 rounded bg-white bg-opacity-5">
|
|
||||||
<div class="text-muted small mb-1" style="font-size: 10px;">收款账户</div>
|
|
||||||
<div class="text-white fw-bold small" style="word-break: break-all; font-family: monospace;">${info.account}</div>
|
|
||||||
</div>
|
|
||||||
<div class="mb-2 p-2 rounded bg-white bg-opacity-5">
|
|
||||||
<div class="text-muted small mb-1" style="font-size: 10px;">收款姓名</div>
|
|
||||||
<div class="text-white fw-bold small">${info.name}</div>
|
|
||||||
</div>
|
|
||||||
${info.note ? `
|
|
||||||
<div class="mt-2 p-2 rounded bg-warning bg-opacity-10 border-top border-warning border-opacity-20">
|
|
||||||
<div class="text-warning fw-bold mb-1" style="font-size: 10px;"><i class="bi bi-info-circle me-1"></i>备注</div>
|
|
||||||
<div class="text-white-50 small" style="font-size: 11px;">${info.note}</div>
|
|
||||||
</div>` : ''}
|
|
||||||
<div class="mt-2 pt-2 border-top border-white border-opacity-10">
|
|
||||||
<div class="text-white fw-bold" style="font-size: 10px;">温馨提示</div>
|
|
||||||
<div class="text-white-50 small" style="font-size: 9px;">请严格按照页面展示的账户信息进行转账,转账金额需与订单金额保持一致。</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
} catch(e) { displayMsg = '[支付信息错误]'; }
|
|
||||||
}
|
|
||||||
|
|
||||||
let dateObj;
|
let dateObj;
|
||||||
if (typeof time === 'string' && time.includes('-')) {
|
if (typeof time === 'string' && time.includes('-')) {
|
||||||
|
|||||||
@ -549,8 +549,8 @@ $translations = [
|
|||||||
'swap_success_desc' => '您的兑换已成功完成!',
|
'swap_success_desc' => '您的兑换已成功完成!',
|
||||||
'unknown_error' => '发生未知错误',
|
'unknown_error' => '发生未知错误',
|
||||||
'rate_fetch_failed' => '获取汇率失败(服务商问题)',
|
'rate_fetch_failed' => '获取汇率失败(服务商问题)',
|
||||||
'matching_account' => '正在分配专属收款账户',
|
'matching_account' => '正在为您匹配充值账户',
|
||||||
'matching_desc' => '系统正在为您分配专属收款账户,请耐心等待 匹配期间请勿刷新页面或重复提交订单 若超过倒计时仍未匹配成功,请及时联系在线客服',
|
'matching_desc' => '系统正在为您匹配充值账户,请耐心等待 匹配期间请勿刷新页面或重复提交订单 若超过倒计时仍未匹配成功,请及时联系在线客服',
|
||||||
'security_instructions' => '安全说明',
|
'security_instructions' => '安全说明',
|
||||||
'security_tip_1' => '请在倒计时结束前完成充值',
|
'security_tip_1' => '请在倒计时结束前完成充值',
|
||||||
'security_tip_2' => '转账时请务必备注您的用户ID',
|
'security_tip_2' => '转账时请务必备注您的用户ID',
|
||||||
@ -609,11 +609,12 @@ $translations = [
|
|||||||
'contact_sup_title' => '联系支持',
|
'contact_sup_title' => '联系支持',
|
||||||
'contact_sup_desc' => '如果您遇到问题,我们的团队将全天候为您服务。',
|
'contact_sup_desc' => '如果您遇到问题,我们的团队将全天候为您服务。',
|
||||||
'matched_successfully' => '匹配成功',
|
'matched_successfully' => '匹配成功',
|
||||||
|
'account_received' => '已接收到收款账户',
|
||||||
'getting_account_details' => '正在获取账户详情',
|
'getting_account_details' => '正在获取账户详情',
|
||||||
'receiving_bank' => '收款银行',
|
'receiving_bank' => '收款银行',
|
||||||
'receiving_account' => '收款账号',
|
'receiving_account' => '收款账号',
|
||||||
'receiving_name' => '收款姓名',
|
'receiving_name' => '收款姓名',
|
||||||
'recharge_final_notice' => '匹配成功,请严格按照页面展示的账户信息进行转账,转账金额需与订单金额保持一致,请勿分笔转账或修改金额。转账完成后请点击下方按钮,并将凭证提供给客服。',
|
'recharge_final_notice' => "匹配成功,请严格按照页面展示的账户信息进行转账\n转账金额需与订单金额保持一致\n请勿分笔转账或修改金额",
|
||||||
'remaining_time' => '剩余时间',
|
'remaining_time' => '剩余时间',
|
||||||
'secure_pay' => '安全支付',
|
'secure_pay' => '安全支付',
|
||||||
'encrypted_channel' => '加密通道',
|
'encrypted_channel' => '加密通道',
|
||||||
@ -1137,11 +1138,12 @@ $translations = [
|
|||||||
'security_step2' => 'Please keep your login and trade passwords safe and never disclose them to others.',
|
'security_step2' => 'Please keep your login and trade passwords safe and never disclose them to others.',
|
||||||
'trade_password' => 'Trade Password',
|
'trade_password' => 'Trade Password',
|
||||||
'matched_successfully' => 'Matched Successfully',
|
'matched_successfully' => 'Matched Successfully',
|
||||||
|
'account_received' => 'Payment Account Received',
|
||||||
'getting_account_details' => 'Getting Account Details',
|
'getting_account_details' => 'Getting Account Details',
|
||||||
'receiving_bank' => 'Receiving Bank',
|
'receiving_bank' => 'Receiving Bank',
|
||||||
'receiving_account' => 'Receiving Account',
|
'receiving_account' => 'Receiving Account',
|
||||||
'receiving_name' => 'Receiving Name',
|
'receiving_name' => 'Receiving Name',
|
||||||
'recharge_final_notice' => 'Matched successfully. Please strictly follow the account information displayed on the page for transfer. The transfer amount must be consistent with the order amount. Do not split transfers or modify amounts. After transfer, click the button below and provide the voucher to support.',
|
'recharge_final_notice' => "Matched successfully. Please strictly follow the account information displayed on the page for transfer.\nThe transfer amount must be consistent with the order amount.\nDo not split transfers or modify amounts.",
|
||||||
'remaining_time' => 'Remaining Time',
|
'remaining_time' => 'Remaining Time',
|
||||||
'secure_pay' => 'Secure Pay',
|
'secure_pay' => 'Secure Pay',
|
||||||
'encrypted_channel' => 'Encrypted Channel',
|
'encrypted_channel' => 'Encrypted Channel',
|
||||||
|
|||||||
113
recharge.php
113
recharge.php
@ -523,9 +523,28 @@ function clearRechargeState() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function finishTransfer() {
|
function finishTransfer() {
|
||||||
clearRechargeState();
|
const state = JSON.parse(localStorage.getItem('recharge_state') || '{}');
|
||||||
bootstrap.Modal.getInstance(document.getElementById('rechargeModal'))?.hide();
|
const orderId = state.orderId;
|
||||||
notify('success', '<?= __("recharge_success_title") ?>', '<?= __("recharge_success_text") ?>');
|
|
||||||
|
if (orderId) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('action', 'complete_transfer');
|
||||||
|
formData.append('order_id', orderId);
|
||||||
|
|
||||||
|
fetch('/api/finance.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
}).then(r => r.json()).then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
clearRechargeState();
|
||||||
|
bootstrap.Modal.getInstance(document.getElementById('rechargeModal'))?.hide();
|
||||||
|
notify('success', '<?= __("recharge_success_title") ?>', '<?= __("recharge_success_text") ?>');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
clearRechargeState();
|
||||||
|
bootstrap.Modal.getInstance(document.getElementById('rechargeModal'))?.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function openRechargeModal(initialMessage, isRestore = false, orderId = null) {
|
function openRechargeModal(initialMessage, isRestore = false, orderId = null) {
|
||||||
@ -545,9 +564,9 @@ function openRechargeModal(initialMessage, isRestore = false, orderId = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start countdown
|
// Start countdown
|
||||||
const display = document.getElementById('modal-countdown');
|
|
||||||
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
|
if (rechargeCountdownInterval) clearInterval(rechargeCountdownInterval);
|
||||||
rechargeCountdownInterval = setInterval(() => {
|
rechargeCountdownInterval = setInterval(() => {
|
||||||
|
const display = document.getElementById('modal-countdown');
|
||||||
let mins = Math.floor(remainingSeconds / 60);
|
let mins = Math.floor(remainingSeconds / 60);
|
||||||
let secs = remainingSeconds % 60;
|
let secs = remainingSeconds % 60;
|
||||||
if (display) display.innerText = `${mins}:${secs < 10 ? '0' : ''}${secs}`;
|
if (display) display.innerText = `${mins}:${secs < 10 ? '0' : ''}${secs}`;
|
||||||
@ -602,6 +621,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (state.phase === 'matched') {
|
if (state.phase === 'matched') {
|
||||||
openRechargeModal(state.initialMessage, true);
|
openRechargeModal(state.initialMessage, true);
|
||||||
updateMatchingSide(state.info, true);
|
updateMatchingSide(state.info, true);
|
||||||
|
} else if (state.phase === 'matched_status') {
|
||||||
|
openRechargeModal(state.initialMessage, true);
|
||||||
|
updateMatchingStatus('matched');
|
||||||
} else {
|
} else {
|
||||||
openRechargeModal(state.initialMessage, true);
|
openRechargeModal(state.initialMessage, true);
|
||||||
}
|
}
|
||||||
@ -726,25 +748,40 @@ function startStatusPolling(orderId) {
|
|||||||
const r = await fetch(`/api/recharge_status.php?id=${orderId}`);
|
const r = await fetch(`/api/recharge_status.php?id=${orderId}`);
|
||||||
const data = await r.json();
|
const data = await r.json();
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
if (data.status == 1) {
|
const state = JSON.parse(localStorage.getItem('recharge_state') || '{}');
|
||||||
|
|
||||||
|
if (data.status == 1 && state.phase !== 'matched_status' && state.phase !== 'matched') {
|
||||||
updateMatchingStatus('matched');
|
updateMatchingStatus('matched');
|
||||||
} else if (data.status == 2) {
|
saveRechargeState({ ...state, phase: 'matched_status', orderId: orderId });
|
||||||
|
} else if (data.status == 2 && state.phase !== 'matched') {
|
||||||
updateMatchingSide({
|
updateMatchingSide({
|
||||||
bank: data.account_bank,
|
bank: data.account_bank,
|
||||||
account: data.account_number,
|
account: data.account_number,
|
||||||
name: data.account_name,
|
name: data.account_name
|
||||||
amount: data.amount
|
});
|
||||||
|
saveRechargeState({
|
||||||
|
...state,
|
||||||
|
phase: 'matched',
|
||||||
|
orderId: orderId,
|
||||||
|
info: {
|
||||||
|
bank: data.account_bank,
|
||||||
|
account: data.account_number,
|
||||||
|
name: data.account_name
|
||||||
|
}
|
||||||
});
|
});
|
||||||
// Keep polling in case it gets approved while modal is open?
|
|
||||||
// Or stop if side is already updated? User said "automatic update".
|
|
||||||
// Let's keep polling for status 3.
|
|
||||||
} else if (data.status == 3) {
|
} else if (data.status == 3) {
|
||||||
finishTransfer();
|
finishTransferUI();
|
||||||
clearInterval(window.statusPollingInterval);
|
clearInterval(window.statusPollingInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
}, 2000);
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finishTransferUI() {
|
||||||
|
clearRechargeState();
|
||||||
|
bootstrap.Modal.getInstance(document.getElementById('rechargeModal'))?.hide();
|
||||||
|
notify('success', '<?= __("recharge_success_title") ?>', '<?= __("recharge_success_text") ?>');
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendModalMessage(m) {
|
function appendModalMessage(m) {
|
||||||
@ -752,7 +789,10 @@ function appendModalMessage(m) {
|
|||||||
if (!container || document.querySelector(`[data-modal-id="${m.id}"]`)) return;
|
if (!container || document.querySelector(`[data-modal-id="${m.id}"]`)) return;
|
||||||
|
|
||||||
const sender = m.sender;
|
const sender = m.sender;
|
||||||
const text = (m.message || '').toString();
|
let text = (m.message || '').toString();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let displayMsg = text;
|
let displayMsg = text;
|
||||||
const isImage = text.includes('<img') || text.includes('/assets/images/chat/') || text.includes('data:image');
|
const isImage = text.includes('<img') || text.includes('/assets/images/chat/') || text.includes('data:image');
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
@ -819,13 +859,9 @@ function updateMatchingSide(info, isRestore = false) {
|
|||||||
const side = document.querySelector('.info-side');
|
const side = document.querySelector('.info-side');
|
||||||
if (!side) return;
|
if (!side) return;
|
||||||
|
|
||||||
if (!isRestore) {
|
|
||||||
const state = JSON.parse(localStorage.getItem('recharge_state') || '{}');
|
|
||||||
saveRechargeState({ ...state, phase: 'matched', info });
|
|
||||||
}
|
|
||||||
|
|
||||||
side.innerHTML = `
|
side.innerHTML = `
|
||||||
<div class="text-center text-lg-start fade-in">
|
<div class="text-center text-lg-start fade-in">
|
||||||
|
<!-- 1. Status Badge & Title -->
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<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">
|
<div class="d-inline-flex align-items-center gap-2 px-3 py-1 rounded-pill bg-success bg-opacity-10 text-success small fw-bold mb-3 border border-success border-opacity-10">
|
||||||
<i class="bi bi-check-circle-fill text-success"></i> <?= __("matched_successfully") ?>
|
<i class="bi bi-check-circle-fill text-success"></i> <?= __("matched_successfully") ?>
|
||||||
@ -833,24 +869,25 @@ function updateMatchingSide(info, isRestore = false) {
|
|||||||
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;"><?= __("matched_successfully") ?></h2>
|
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;"><?= __("matched_successfully") ?></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 2. Account Details (ABOVE Countdown) -->
|
||||||
<div class="mb-4 p-4 rounded-4 shadow-sm border border-light" style="background: #fff0f5;">
|
<div class="mb-4 p-4 rounded-4 shadow-sm border border-light" style="background: #fff0f5;">
|
||||||
<div class="d-flex flex-column gap-3">
|
<div class="d-flex flex-column gap-3">
|
||||||
<div class="payment-item">
|
<div class="payment-item">
|
||||||
<div class="text-muted small mb-1 fw-bold"><?= __("receiving_bank") ?>:</div>
|
<div class="text-muted small mb-1 fw-bold"><?= __("receiving_bank") ?>:</div>
|
||||||
<div class="d-flex justify-content-between align-items-center gap-2">
|
<div class="d-flex justify-content-between align-items-center gap-2">
|
||||||
<div class="h6 mb-0 fw-bold text-dark" style="word-break: break-all; font-size: 1.1rem;">${info.bank}</div>
|
<div class="h6 mb-0 fw-bold text-dark" style="word-break: break-all; font-size: 1.1rem;">${info.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('${info.bank}')"><?= __("copy") ?></button>
|
<button class="btn btn-sm rounded-pill px-3 fw-bold flex-shrink-0" style="font-size: 10px; background: #ff4d94; color: white;" onclick="copyText('${info.bank}')"><?= __("copy") ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payment-item border-top border-white border-opacity-50 pt-2">
|
<div class="payment-item border-top border-white border-opacity-50 pt-2">
|
||||||
<div class="text-muted small mb-1 fw-bold"><?= __("receiving_account") ?>:</div>
|
<div class="text-muted small mb-1 fw-bold"><?= __("receiving_account") ?>:</div>
|
||||||
<div class="d-flex justify-content-between align-items-center gap-2">
|
<div class="d-flex justify-content-between align-items-center gap-2">
|
||||||
<div class="h5 mb-0 fw-bold" style="word-break: break-all; font-family: monospace; font-size: 1.3rem; color: #ff4d94;">${info.account}</div>
|
<div class="h5 mb-0 fw-bold" style="word-break: break-all; font-family: monospace; font-size: 1.3rem; color: #ff4d94;">${info.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('${info.account}')"><?= __("copy") ?></button>
|
<button class="btn btn-sm rounded-pill px-3 fw-bold flex-shrink-0" style="font-size: 10px; background: #ff4d94; color: white;" onclick="copyText('${info.account}')"><?= __("copy") ?></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="payment-item border-top border-white border-opacity-50 pt-2">
|
<div class="payment-item border-top border-white border-opacity-50 pt-2">
|
||||||
<div class="text-muted small mb-1 fw-bold"><?= __("receiving_name") ?>:</div>
|
<div class="text-muted small mb-1 fw-bold"><?= __("receiving_name") ?>:</div>
|
||||||
<div class="d-flex justify-content-between align-items-center gap-2">
|
<div class="d-flex justify-content-between align-items-center gap-2">
|
||||||
<div class="h6 mb-0 fw-bold text-dark" style="word-break: break-all; font-size: 1.1rem;">${info.name}</div>
|
<div class="h6 mb-0 fw-bold text-dark" style="word-break: break-all; font-size: 1.1rem;">${info.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('${info.name}')"><?= __("copy") ?></button>
|
<button class="btn btn-sm rounded-pill px-3 fw-bold flex-shrink-0" style="font-size: 10px; background: #ff4d94; color: white;" onclick="copyText('${info.name}')"><?= __("copy") ?></button>
|
||||||
@ -859,12 +896,7 @@ function updateMatchingSide(info, isRestore = false) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-3 rounded-4 border border-light mb-4" style="background: rgba(255,77,148,0.05);">
|
<!-- 3. Remaining Time Box (BELOW Account Details) -->
|
||||||
<p class="text-dark fw-bold mb-0" style="line-height: 1.6; font-size: 14px; opacity: 0.9;">
|
|
||||||
<?= __("recharge_final_notice") ?>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-4 py-3 px-4 rounded-4 shadow-sm border border-light" style="background: #ffffff;">
|
<div class="mb-4 py-3 px-4 rounded-4 shadow-sm border border-light" style="background: #ffffff;">
|
||||||
<div class="row align-items-center">
|
<div class="row align-items-center">
|
||||||
<div class="col-7">
|
<div class="col-7">
|
||||||
@ -878,6 +910,13 @@ function updateMatchingSide(info, isRestore = false) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 4. Final Notice Tip Text (BELOW Countdown) -->
|
||||||
|
<div class="p-3 rounded-4 border border-light mb-4" style="background: rgba(255,77,148,0.05);">
|
||||||
|
<p class="text-dark fw-bold mb-0" style="line-height: 1.6; font-size: 14px; opacity: 0.9; white-space: pre-line;">
|
||||||
|
<?= __("recharge_final_notice") ?>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-primary w-100 rounded-pill py-3 fw-bold shadow-sm" onclick="finishTransfer()" style="background: #ff4d94; border: none; color: white;">
|
<button type="button" class="btn btn-primary w-100 rounded-pill py-3 fw-bold shadow-sm" onclick="finishTransfer()" style="background: #ff4d94; border: none; color: white;">
|
||||||
<?= __("complete_transfer") ?>
|
<?= __("complete_transfer") ?>
|
||||||
</button>
|
</button>
|
||||||
@ -949,7 +988,10 @@ function confirmFiatOrder(btn, event) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => {
|
||||||
|
if (!r.ok) throw new Error('HTTP error ' + r.status);
|
||||||
|
return r.json();
|
||||||
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@ -966,9 +1008,11 @@ function confirmFiatOrder(btn, event) {
|
|||||||
notify('error', data.error || '<?= __("request_failed") ?>');
|
notify('error', data.error || '<?= __("request_failed") ?>');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
|
notify('error', '<?= __("request_failed") ?>: ' + err.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -996,7 +1040,10 @@ function confirmCryptoOrder(btn, event) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => {
|
||||||
|
if (!r.ok) throw new Error('HTTP error ' + r.status);
|
||||||
|
return r.json();
|
||||||
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@ -1019,9 +1066,11 @@ function confirmCryptoOrder(btn, event) {
|
|||||||
notify('error', data.error || '<?= __("request_failed") ?>');
|
notify('error', data.error || '<?= __("request_failed") ?>');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
|
notify('error', '<?= __("request_failed") ?>: ' + err.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
withdraw.php
18
withdraw.php
@ -280,7 +280,10 @@ function confirmCryptoWithdraw(btn, event) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => {
|
||||||
|
if (!r.ok) throw new Error('HTTP error ' + r.status);
|
||||||
|
return r.json();
|
||||||
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@ -294,9 +297,11 @@ function confirmCryptoWithdraw(btn, event) {
|
|||||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
|
notify('error', '<?= __('request_failed') ?>: ' + err.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,7 +337,10 @@ function confirmFiatWithdraw(btn, event) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
})
|
})
|
||||||
.then(r => r.json())
|
.then(r => {
|
||||||
|
if (!r.ok) throw new Error('HTTP error ' + r.status);
|
||||||
|
return r.json();
|
||||||
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
@ -349,9 +357,11 @@ function confirmFiatWithdraw(btn, event) {
|
|||||||
notify('error', data.error || '<?= __('request_failed') ?>');
|
notify('error', data.error || '<?= __('request_failed') ?>');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
btn.disabled = false;
|
btn.disabled = false;
|
||||||
btn.innerHTML = originalText;
|
btn.innerHTML = originalText;
|
||||||
|
notify('error', '<?= __('request_failed') ?>: ' + err.message);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user