Autosave: 20260218-074654
This commit is contained in:
parent
e78b6820eb
commit
bb3dee89dd
@ -137,7 +137,7 @@ ob_start();
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<div class="card p-4 border-0 shadow-sm">
|
<div class="card p-4 border-0 shadow-sm card-dismissible" data-card-id="settings_instructions">
|
||||||
<h6 class="fw-bold mb-3">使用说明</h6>
|
<h6 class="fw-bold mb-3">使用说明</h6>
|
||||||
<ul class="small text-muted ps-3 mb-0">
|
<ul class="small text-muted ps-3 mb-0">
|
||||||
<li class="mb-2"><strong>网站名称:</strong>影响浏览器标签页标题。</li>
|
<li class="mb-2"><strong>网站名称:</strong>影响浏览器标签页标题。</li>
|
||||||
|
|||||||
@ -9,6 +9,28 @@ if (!hasPermission('view_orders')) {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-settle expired orders
|
||||||
|
$db->beginTransaction();
|
||||||
|
try {
|
||||||
|
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
|
||||||
|
$stmt->execute();
|
||||||
|
$expired = $stmt->fetchAll();
|
||||||
|
foreach ($expired as $o) {
|
||||||
|
$result = ($o['control_status'] == 1 || $o['user_control'] == 1) ? 'won' : (($o['control_status'] == 2 || $o['user_control'] == 2) ? 'lost' : ((rand(0, 100) > 50) ? 'won' : 'lost'));
|
||||||
|
$db->prepare("UPDATE binary_orders SET status = ?, settled_at = NOW() WHERE id = ?")->execute([$result, $o['id']]);
|
||||||
|
if ($result === 'won') {
|
||||||
|
$win_amount = $o['amount'] + ($o['amount'] * $o['profit_rate'] / 100);
|
||||||
|
$db->prepare("UPDATE user_balances SET available = available + ? WHERE user_id = ? AND symbol = 'USDT'")->execute([$win_amount, $o['user_id']]);
|
||||||
|
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'binary_win', ?, 'USDT', 'completed')")->execute([$o['user_id'], $win_amount]);
|
||||||
|
} else {
|
||||||
|
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'binary_loss', ?, 'USDT', 'completed')")->execute([$o['user_id'], $o['amount']]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$db->commit();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Control Update
|
// Handle Control Update
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||||
if ($_POST['action'] === 'set_control') {
|
if ($_POST['action'] === 'set_control') {
|
||||||
@ -78,8 +100,11 @@ $orders = $stmt->fetchAll();
|
|||||||
<td>
|
<td>
|
||||||
<span class="fw-bold"><?= $o['symbol'] ?></span>
|
<span class="fw-bold"><?= $o['symbol'] ?></span>
|
||||||
<br>
|
<br>
|
||||||
<span class="badge <?= $o['direction'] === 'buy' ? 'bg-success' : 'bg-danger' ?>">
|
<?php
|
||||||
<?= $o['direction'] === 'buy' ? '买涨 ↑' : '买跌 ↓' ?>
|
$is_up = in_array($o['direction'], ['buy', 'up', '涨']);
|
||||||
|
?>
|
||||||
|
<span class="badge <?= $is_up ? 'bg-success' : 'bg-danger' ?>">
|
||||||
|
<?= $is_up ? '买涨 ↑' : '买跌 ↓' ?>
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@ -151,6 +151,11 @@ $requests = $stmt->fetchAll();
|
|||||||
<span class="fw-bold <?= $r['type'] === 'recharge' ? 'text-success' : 'text-danger' ?>">
|
<span class="fw-bold <?= $r['type'] === 'recharge' ? 'text-success' : 'text-danger' ?>">
|
||||||
<?= $r['type'] === 'recharge' ? '+' : '-' ?> <?= number_format($r['amount'], 2) ?> <?= $r['symbol'] ?>
|
<?= $r['type'] === 'recharge' ? '+' : '-' ?> <?= number_format($r['amount'], 2) ?> <?= $r['symbol'] ?>
|
||||||
</span>
|
</span>
|
||||||
|
<?php if ($r['fiat_amount']): ?>
|
||||||
|
<div class="text-muted small">
|
||||||
|
≈ <?= number_format($r['fiat_amount'], 2) ?> <?= $r['fiat_currency'] ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php if ($r['type'] === 'recharge'): ?>
|
<?php if ($r['type'] === 'recharge'): ?>
|
||||||
|
|||||||
117
admin/layout.php
117
admin/layout.php
@ -160,17 +160,29 @@ function renderAdminPage($content, $title = '后台管理') {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (hasPermission('audit_finance')): ?>
|
<?php if (hasPermission('audit_finance')): ?>
|
||||||
<a href="/admin/finance.php" class="nav-link <?= $current_page == 'finance.php' ? 'active' : '' ?>"><i class="bi bi-wallet2"></i> 充提管理</a>
|
<a href="/admin/finance.php" class="nav-link <?= $current_page == 'finance.php' ? 'active' : '' ?>">
|
||||||
|
<i class="bi bi-wallet2"></i> 充提管理
|
||||||
|
<span class="badge bg-danger rounded-pill ms-auto d-none" id="finance-badge">0</span>
|
||||||
|
</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (hasPermission('view_orders')): ?>
|
<?php if (hasPermission('view_orders')): ?>
|
||||||
<a href="/admin/binary.php" class="nav-link <?= $current_page == 'binary.php' ? 'active' : '' ?>"><i class="bi bi-clock"></i> 秒合约管理</a>
|
<a href="/admin/transactions.php" class="nav-link <?= $current_page == 'transactions.php' ? 'active' : '' ?>">
|
||||||
|
<i class="bi bi-list-ul"></i> 财务明细
|
||||||
|
</a>
|
||||||
|
<a href="/admin/binary.php" class="nav-link <?= $current_page == 'binary.php' ? 'active' : '' ?>">
|
||||||
|
<i class="bi bi-clock"></i> 秒合约管理
|
||||||
|
<span class="badge bg-info rounded-pill ms-auto d-none" id="binary-badge">0</span>
|
||||||
|
</a>
|
||||||
<a href="/admin/contract.php" class="nav-link <?= $current_page == 'contract.php' ? 'active' : '' ?>"><i class="bi bi-layers"></i> 永续合约</a>
|
<a href="/admin/contract.php" class="nav-link <?= $current_page == 'contract.php' ? 'active' : '' ?>"><i class="bi bi-layers"></i> 永续合约</a>
|
||||||
<a href="/admin/spot.php" class="nav-link <?= $current_page == 'spot.php' ? 'active' : '' ?>"><i class="bi bi-currency-exchange"></i> 币币交易</a>
|
<a href="/admin/spot.php" class="nav-link <?= $current_page == 'spot.php' ? 'active' : '' ?>"><i class="bi bi-currency-exchange"></i> 币币交易</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (hasPermission('manage_kyc')): ?>
|
<?php if (hasPermission('manage_kyc')): ?>
|
||||||
<a href="/admin/kyc.php" class="nav-link <?= $current_page == 'kyc.php' ? 'active' : '' ?>"><i class="bi bi-person-vcard"></i> 实名认证</a>
|
<a href="/admin/kyc.php" class="nav-link <?= $current_page == 'kyc.php' ? 'active' : '' ?>">
|
||||||
|
<i class="bi bi-person-vcard"></i> 实名认证
|
||||||
|
<span class="badge bg-danger rounded-pill ms-auto d-none" id="kyc-badge">0</span>
|
||||||
|
</a>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (!$admin['is_agent']): ?>
|
<?php if (!$admin['is_agent']): ?>
|
||||||
@ -191,6 +203,12 @@ function renderAdminPage($content, $title = '后台管理') {
|
|||||||
</div>
|
</div>
|
||||||
<div class="d-flex align-items-center gap-3">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<div class="text-muted small">欢迎您, <?= htmlspecialchars($admin['username']) ?></div>
|
<div class="text-muted small">欢迎您, <?= htmlspecialchars($admin['username']) ?></div>
|
||||||
|
<div class="position-relative me-2">
|
||||||
|
<i class="bi bi-bell fs-5"></i>
|
||||||
|
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger d-none" id="total-badge">
|
||||||
|
0
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<a href="/" class="btn btn-sm btn-outline-primary">返回首页</a>
|
<a href="/" class="btn btn-sm btn-outline-primary">返回首页</a>
|
||||||
<a href="/auth/logout.php" class="btn btn-sm btn-outline-danger">登出</a>
|
<a href="/auth/logout.php" class="btn btn-sm btn-outline-danger">登出</a>
|
||||||
</div>
|
</div>
|
||||||
@ -200,6 +218,99 @@ function renderAdminPage($content, $title = '后台管理') {
|
|||||||
<?= $content ?>
|
<?= $content ?>
|
||||||
</div>
|
</div>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Handle dismissible cards
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const dismissedCards = JSON.parse(localStorage.getItem('dismissed_admin_cards') || '[]');
|
||||||
|
|
||||||
|
document.querySelectorAll('.card-dismissible').forEach(card => {
|
||||||
|
const cardId = card.getAttribute('data-card-id') || window.location.pathname;
|
||||||
|
if (dismissedCards.includes(cardId)) {
|
||||||
|
card.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeBtn = document.createElement('button');
|
||||||
|
closeBtn.className = 'btn-close position-absolute top-0 end-0 m-2';
|
||||||
|
closeBtn.style.zIndex = '10';
|
||||||
|
closeBtn.onclick = function() {
|
||||||
|
card.style.display = 'none';
|
||||||
|
dismissedCards.push(cardId);
|
||||||
|
localStorage.setItem('dismissed_admin_cards', JSON.stringify(dismissedCards));
|
||||||
|
};
|
||||||
|
card.style.position = 'relative';
|
||||||
|
card.appendChild(closeBtn);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let lastTotal = 0;
|
||||||
|
const notificationSound = new Audio('https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3');
|
||||||
|
|
||||||
|
function checkNotifications() {
|
||||||
|
fetch('/api/admin_notifications.php')
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success) {
|
||||||
|
const counts = data.counts;
|
||||||
|
const total = counts.total;
|
||||||
|
|
||||||
|
// Finance badge
|
||||||
|
const financeBadge = document.getElementById('finance-badge');
|
||||||
|
if (financeBadge) {
|
||||||
|
const fCount = counts.recharge + counts.withdrawal;
|
||||||
|
if (fCount > 0) {
|
||||||
|
financeBadge.innerText = fCount;
|
||||||
|
financeBadge.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
financeBadge.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KYC badge
|
||||||
|
const kycBadge = document.getElementById('kyc-badge');
|
||||||
|
if (kycBadge) {
|
||||||
|
if (counts.kyc > 0) {
|
||||||
|
kycBadge.innerText = counts.kyc;
|
||||||
|
kycBadge.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
kycBadge.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary badge
|
||||||
|
const binaryBadge = document.getElementById('binary-badge');
|
||||||
|
if (binaryBadge) {
|
||||||
|
if (counts.binary > 0) {
|
||||||
|
binaryBadge.innerText = counts.binary;
|
||||||
|
binaryBadge.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
binaryBadge.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total badge
|
||||||
|
const totalBadge = document.getElementById('total-badge');
|
||||||
|
if (totalBadge) {
|
||||||
|
if (total > 0) {
|
||||||
|
totalBadge.innerText = total;
|
||||||
|
totalBadge.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
totalBadge.classList.add('d-none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total > lastTotal) {
|
||||||
|
notificationSound.play().catch(e => console.log('Audio play failed:', e));
|
||||||
|
}
|
||||||
|
lastTotal = total;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(e => console.error('Notification check failed:', e));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check every 10 seconds
|
||||||
|
setInterval(checkNotifications, 10000);
|
||||||
|
checkNotifications();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
<?php
|
<?php
|
||||||
|
|||||||
122
admin/transactions.php
Normal file
122
admin/transactions.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/layout.php';
|
||||||
|
|
||||||
|
$db = db();
|
||||||
|
|
||||||
|
if (!hasPermission('view_orders')) {
|
||||||
|
echo "权限不足";
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = '财务明细';
|
||||||
|
ob_start();
|
||||||
|
|
||||||
|
$user_id = isset($_GET['user_id']) ? (int)$_GET['user_id'] : null;
|
||||||
|
$type = $_GET['type'] ?? '';
|
||||||
|
|
||||||
|
$sql = "SELECT t.*, u.username, u.uid FROM transactions t JOIN users u ON t.user_id = u.id";
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
if ($admin['is_agent']) {
|
||||||
|
$sql .= " WHERE u.agent_id = ?";
|
||||||
|
$params[] = $admin['id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($user_id) {
|
||||||
|
$sql .= (strpos($sql, 'WHERE') === false ? " WHERE" : " AND") . " t.user_id = ?";
|
||||||
|
$params[] = $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type) {
|
||||||
|
$sql .= (strpos($sql, 'WHERE') === false ? " WHERE" : " AND") . " t.type = ?";
|
||||||
|
$params[] = $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql .= " ORDER BY t.created_at DESC";
|
||||||
|
|
||||||
|
$stmt = $db->prepare($sql);
|
||||||
|
$stmt->execute($params);
|
||||||
|
$transactions = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$types = [
|
||||||
|
'recharge' => '充值',
|
||||||
|
'withdrawal' => '提现',
|
||||||
|
'binary_win' => '秒合约盈利',
|
||||||
|
'binary_loss' => '秒合约亏损',
|
||||||
|
'contract_profit' => '合约盈利',
|
||||||
|
'contract_loss' => '合约亏损',
|
||||||
|
'spot_buy' => '币币买入',
|
||||||
|
'spot_sell' => '币币卖出'
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<div class="d-flex align-items-center gap-3">
|
||||||
|
<a href="<?= $user_id ? 'users.php' : 'index.php' ?>" class="btn btn-outline-secondary btn-sm"><i class="bi bi-arrow-left"></i> 返回</a>
|
||||||
|
<h4 class="mb-0">账变记录 <?= $user_id ? "(用户ID: $user_id)" : "" ?></h4>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex gap-2">
|
||||||
|
<select class="form-select form-select-sm" onchange="location.href='?type=' + this.value + '<?= $user_id ? "&user_id=$user_id" : "" ?>'">
|
||||||
|
<option value="">所有类型</option>
|
||||||
|
<?php foreach($types as $k => $v): ?>
|
||||||
|
<option value="<?= $k ?>" <?= $type === $k ? 'selected' : '' ?>><?= $v ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="table table-hover align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr class="text-muted small">
|
||||||
|
<th>ID</th>
|
||||||
|
<th>用户信息</th>
|
||||||
|
<th>类型</th>
|
||||||
|
<th>币种</th>
|
||||||
|
<th>金额</th>
|
||||||
|
<th>时间</th>
|
||||||
|
<th>状态</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($transactions as $t): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= $t['id'] ?></td>
|
||||||
|
<td>
|
||||||
|
<div><?= htmlspecialchars($t['username']) ?></div>
|
||||||
|
<code class="small"><?= $t['uid'] ?></code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php
|
||||||
|
$badgeClass = 'bg-secondary';
|
||||||
|
if (strpos($t['type'], 'win') !== false || strpos($t['type'], 'profit') !== false || $t['type'] === 'recharge') $badgeClass = 'bg-success';
|
||||||
|
if (strpos($t['type'], 'loss') !== false || $t['type'] === 'withdrawal') $badgeClass = 'bg-danger';
|
||||||
|
?>
|
||||||
|
<span class="badge <?= $badgeClass ?>"><?= $types[$t['type']] ?? $t['type'] ?></span>
|
||||||
|
</td>
|
||||||
|
<td><span class="fw-bold"><?= $t['symbol'] ?></span></td>
|
||||||
|
<td>
|
||||||
|
<span class="fw-bold <?= (strpos($t['type'], 'win') !== false || strpos($t['type'], 'profit') !== false || $t['type'] === 'recharge') ? 'text-success' : 'text-danger' ?>">
|
||||||
|
<?= (strpos($t['type'], 'win') !== false || strpos($t['type'], 'profit') !== false || $t['type'] === 'recharge') ? '+' : '-' ?>
|
||||||
|
<?= number_format($t['amount'], 2) ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td><small class="text-muted"><?= $t['created_at'] ?></small></td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-opacity-10 <?= $t['status'] === 'completed' ? 'bg-success text-success' : 'bg-warning text-warning' ?>">
|
||||||
|
<?= $t['status'] === 'completed' ? '已完成' : '待处理' ?>
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if (empty($transactions)): ?>
|
||||||
|
<tr><td colspan="7" class="text-center p-5 text-muted">暂无账变记录</td></tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
$content = ob_get_clean();
|
||||||
|
renderAdminPage($content, $title);
|
||||||
|
?>
|
||||||
57
api/admin_notifications.php
Normal file
57
api/admin_notifications.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/../db/config.php';
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if (!isset($_SESSION['admin_id'])) {
|
||||||
|
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = db();
|
||||||
|
$admin_id = $_SESSION['admin_id'];
|
||||||
|
|
||||||
|
// Get admin info for agent check
|
||||||
|
$stmt = $db->prepare("SELECT is_agent FROM admins WHERE id = ?");
|
||||||
|
$stmt->execute([$admin_id]);
|
||||||
|
$admin = $stmt->fetch();
|
||||||
|
|
||||||
|
$pending_recharge = 0;
|
||||||
|
$pending_withdrawal = 0;
|
||||||
|
$pending_kyc = 0;
|
||||||
|
$active_binary = 0;
|
||||||
|
|
||||||
|
if ($admin['is_agent']) {
|
||||||
|
$pending_recharge = $db->prepare("SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'recharge' AND r.status = 'pending' AND u.agent_id = ?");
|
||||||
|
$pending_recharge->execute([$admin_id]);
|
||||||
|
$pending_recharge = $pending_recharge->fetchColumn();
|
||||||
|
|
||||||
|
$pending_withdrawal = $db->prepare("SELECT COUNT(*) FROM finance_requests r JOIN users u ON r.user_id = u.id WHERE r.type = 'withdrawal' AND r.status = 'pending' AND u.agent_id = ?");
|
||||||
|
$pending_withdrawal->execute([$admin_id]);
|
||||||
|
$pending_withdrawal = $pending_withdrawal->fetchColumn();
|
||||||
|
|
||||||
|
$pending_kyc = $db->prepare("SELECT COUNT(*) FROM users WHERE kyc_status = 'pending' AND agent_id = ?");
|
||||||
|
$pending_kyc->execute([$admin_id]);
|
||||||
|
$pending_kyc = $pending_kyc->fetchColumn();
|
||||||
|
|
||||||
|
$active_binary = $db->prepare("SELECT COUNT(*) FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND u.agent_id = ?");
|
||||||
|
$active_binary->execute([$admin_id]);
|
||||||
|
$active_binary = $active_binary->fetchColumn();
|
||||||
|
} else {
|
||||||
|
$pending_recharge = $db->query("SELECT COUNT(*) FROM finance_requests WHERE type = 'recharge' AND status = 'pending'")->fetchColumn();
|
||||||
|
$pending_withdrawal = $db->query("SELECT COUNT(*) FROM finance_requests WHERE type = 'withdrawal' AND status = 'pending'")->fetchColumn();
|
||||||
|
$pending_kyc = $db->query("SELECT COUNT(*) FROM users WHERE kyc_status = 'pending'")->fetchColumn();
|
||||||
|
$active_binary = $db->query("SELECT COUNT(*) FROM binary_orders WHERE status = 'pending'")->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'counts' => [
|
||||||
|
'recharge' => (int)$pending_recharge,
|
||||||
|
'withdrawal' => (int)$pending_withdrawal,
|
||||||
|
'kyc' => (int)$pending_kyc,
|
||||||
|
'binary' => (int)$active_binary,
|
||||||
|
'total' => (int)$pending_recharge + (int)$pending_withdrawal + (int)$pending_kyc + (int)$active_binary
|
||||||
|
]
|
||||||
|
]);
|
||||||
@ -103,6 +103,11 @@ if ($action === 'settle_order') {
|
|||||||
} else {
|
} else {
|
||||||
$result = ($close_price < $order['entry_price']) ? 'won' : 'lost';
|
$result = ($close_price < $order['entry_price']) ? 'won' : 'lost';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Tie (usually loss in binary options, or can be refund)
|
||||||
|
if ($close_price == $order['entry_price']) {
|
||||||
|
$result = 'lost';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force "Control Loss means deducting the order amount"
|
// Force "Control Loss means deducting the order amount"
|
||||||
|
|||||||
@ -30,6 +30,38 @@ if ($action === 'get_orders') {
|
|||||||
$settlement = [];
|
$settlement = [];
|
||||||
|
|
||||||
if ($tab === 'binary') {
|
if ($tab === 'binary') {
|
||||||
|
// Auto-settle expired orders
|
||||||
|
$stmt = $db->prepare("SELECT o.*, u.win_loss_control as user_control FROM binary_orders o JOIN users u ON o.user_id = u.id WHERE o.user_id = ? AND o.status = 'pending' AND DATE_ADD(o.created_at, INTERVAL o.duration SECOND) <= NOW()");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$expired = $stmt->fetchAll();
|
||||||
|
|
||||||
|
foreach ($expired as $o) {
|
||||||
|
$order_id = $o['id'];
|
||||||
|
$result = '';
|
||||||
|
// Simple settlement if we missed the real-time event
|
||||||
|
if ($o['control_status'] == 1 || $o['user_control'] == 1) $result = 'won';
|
||||||
|
elseif ($o['control_status'] == 2 || $o['user_control'] == 2) $result = 'lost';
|
||||||
|
else {
|
||||||
|
// Natural result fallback (randomized or tie-breaker if price history unavailable)
|
||||||
|
$result = (rand(0, 100) > 50) ? 'won' : 'lost';
|
||||||
|
}
|
||||||
|
|
||||||
|
$db->beginTransaction();
|
||||||
|
try {
|
||||||
|
$db->prepare("UPDATE binary_orders SET status = ?, settled_at = NOW() WHERE id = ?")->execute([$result, $order_id]);
|
||||||
|
if ($result === 'won') {
|
||||||
|
$win_amount = $o['amount'] + ($o['amount'] * $o['profit_rate'] / 100);
|
||||||
|
$db->prepare("UPDATE user_balances SET available = available + ? WHERE user_id = ? AND symbol = 'USDT'")->execute([$win_amount, $user_id]);
|
||||||
|
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'binary_win', ?, 'USDT', 'completed')")->execute([$user_id, $win_amount]);
|
||||||
|
} else {
|
||||||
|
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'binary_loss', ?, 'USDT', 'completed')")->execute([$user_id, $o['amount']]);
|
||||||
|
}
|
||||||
|
$db->commit();
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$db->rollBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$stmt = $db->prepare("SELECT * FROM binary_orders WHERE user_id = ? ORDER BY created_at DESC");
|
$stmt = $db->prepare("SELECT * FROM binary_orders WHERE user_id = ? ORDER BY created_at DESC");
|
||||||
$stmt->execute([$user_id]);
|
$stmt->execute([$user_id]);
|
||||||
$orders = $stmt->fetchAll();
|
$orders = $stmt->fetchAll();
|
||||||
@ -45,13 +77,12 @@ if ($action === 'get_orders') {
|
|||||||
'amount' => $o['amount'],
|
'amount' => $o['amount'],
|
||||||
'pnl' => $o['status'] === 'won' ? ($o['amount'] * $o['profit_rate'] / 100) : ($o['status'] === 'lost' ? -$o['amount'] : 0),
|
'pnl' => $o['status'] === 'won' ? ($o['amount'] * $o['profit_rate'] / 100) : ($o['status'] === 'lost' ? -$o['amount'] : 0),
|
||||||
'total' => $o['status'] === 'won' ? ($o['amount'] + ($o['amount'] * $o['profit_rate'] / 100)) : ($o['status'] === 'lost' ? '0.00' : '---'),
|
'total' => $o['status'] === 'won' ? ($o['amount'] + ($o['amount'] * $o['profit_rate'] / 100)) : ($o['status'] === 'lost' ? '0.00' : '---'),
|
||||||
'status' => ucfirst($o['status']),
|
'status' => ($o['status'] === 'won' ? 'Profit' : ($o['status'] === 'lost' ? 'Loss' : 'Executing')),
|
||||||
'profitRate' => $o['profit_rate']
|
'profitRate' => $o['profit_rate']
|
||||||
];
|
];
|
||||||
if ($o['status'] === 'pending') {
|
if ($o['status'] === 'pending') {
|
||||||
$row['status'] = 'Executing';
|
$row['status'] = 'Executing';
|
||||||
$row['totalSeconds'] = $o['duration'];
|
$row['totalSeconds'] = $o['duration'];
|
||||||
// Calculate seconds left
|
|
||||||
$elapsed = time() - strtotime($o['created_at']);
|
$elapsed = time() - strtotime($o['created_at']);
|
||||||
$row['secondsLeft'] = max(0, $o['duration'] - $elapsed);
|
$row['secondsLeft'] = max(0, $o['duration'] - $elapsed);
|
||||||
if ($row['secondsLeft'] > 0) $open[] = $row;
|
if ($row['secondsLeft'] > 0) $open[] = $row;
|
||||||
@ -112,14 +143,16 @@ if ($action === 'recharge') {
|
|||||||
$symbol = $_POST['symbol'] ?? 'USDT';
|
$symbol = $_POST['symbol'] ?? 'USDT';
|
||||||
$method = $_POST['method'] ?? 'Crypto';
|
$method = $_POST['method'] ?? 'Crypto';
|
||||||
$tx_hash = $_POST['tx_hash'] ?? '';
|
$tx_hash = $_POST['tx_hash'] ?? '';
|
||||||
|
$fiat_amount = isset($_POST['fiat_amount']) ? (float)$_POST['fiat_amount'] : null;
|
||||||
|
$fiat_currency = $_POST['fiat_currency'] ?? null;
|
||||||
|
|
||||||
if ($amount <= 0) {
|
if ($amount <= 0) {
|
||||||
echo json_encode(['success' => false, 'error' => 'Invalid amount']);
|
echo json_encode(['success' => false, 'error' => 'Invalid amount']);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_method, tx_hash, status) VALUES (?, 'recharge', ?, ?, ?, ?, 'pending')");
|
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_method, tx_hash, fiat_amount, fiat_currency, status) VALUES (?, 'recharge', ?, ?, ?, ?, ?, ?, 'pending')");
|
||||||
$stmt->execute([$user_id, $amount, $symbol, $method, $tx_hash]);
|
$stmt->execute([$user_id, $amount, $symbol, $method, $tx_hash, $fiat_amount, $fiat_currency]);
|
||||||
|
|
||||||
echo json_encode(['success' => true]);
|
echo json_encode(['success' => true]);
|
||||||
exit;
|
exit;
|
||||||
@ -130,6 +163,8 @@ if ($action === 'withdraw') {
|
|||||||
$symbol = $_POST['symbol'] ?? 'USDT';
|
$symbol = $_POST['symbol'] ?? 'USDT';
|
||||||
$address = $_POST['address'] ?? '';
|
$address = $_POST['address'] ?? '';
|
||||||
$password = $_POST['password'] ?? '';
|
$password = $_POST['password'] ?? '';
|
||||||
|
$fiat_amount = isset($_POST['fiat_amount']) ? (float)$_POST['fiat_amount'] : null;
|
||||||
|
$fiat_currency = $_POST['fiat_currency'] ?? null;
|
||||||
|
|
||||||
// Validate balance
|
// Validate balance
|
||||||
$stmt = $db->prepare("SELECT available FROM user_balances WHERE user_id = ? AND symbol = ?");
|
$stmt = $db->prepare("SELECT available FROM user_balances WHERE user_id = ? AND symbol = ?");
|
||||||
@ -151,8 +186,8 @@ 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, status) VALUES (?, 'withdrawal', ?, ?, ?, 'pending')");
|
$stmt = $db->prepare("INSERT INTO finance_requests (user_id, type, amount, symbol, payment_details, fiat_amount, fiat_currency, status) VALUES (?, 'withdrawal', ?, ?, ?, ?, ?, 'pending')");
|
||||||
$stmt->execute([$user_id, $amount, $symbol, $address]);
|
$stmt->execute([$user_id, $amount, $symbol, $address, $fiat_amount, $fiat_currency]);
|
||||||
|
|
||||||
// Add to transactions as pending
|
// Add to transactions as pending
|
||||||
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'withdrawal', ?, ?, 'pending')")
|
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'withdrawal', ?, ?, 'pending')")
|
||||||
|
|||||||
@ -676,48 +676,99 @@
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
.order-popup {
|
.order-popup {
|
||||||
background: #1e2329;
|
background: rgba(30, 35, 41, 0.95);
|
||||||
width: 360px;
|
width: 420px;
|
||||||
padding: 30px;
|
padding: 40px;
|
||||||
border-radius: 24px;
|
border-radius: 36px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border: 1px solid rgba(255,255,255,0.1);
|
border: 1px solid rgba(255,255,255,0.15);
|
||||||
box-shadow: 0 20px 50px rgba(0,0,0,0.5);
|
box-shadow: 0 40px 100px rgba(0,0,0,0.9);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
backdrop-filter: blur(25px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.order-popup::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 6px;
|
||||||
|
background: linear-gradient(90deg, #0062ff, #00d2ff, #0062ff);
|
||||||
|
background-size: 200% 100%;
|
||||||
|
animation: gradientMove 3s linear infinite;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes gradientMove {
|
||||||
|
0% { background-position: 100% 0; }
|
||||||
|
100% { background-position: -100% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
.order-popup h5 {
|
.order-popup h5 {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-weight: 700;
|
font-weight: 800;
|
||||||
margin-bottom: 25px;
|
margin-bottom: 35px;
|
||||||
|
font-size: 22px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-circle {
|
.countdown-circle {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 120px;
|
width: 200px;
|
||||||
height: 120px;
|
height: 200px;
|
||||||
margin: 0 auto 30px;
|
margin: 0 auto 35px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: radial-gradient(circle, rgba(0, 98, 255, 0.1) 0%, rgba(0,0,0,0) 70%);
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-circle svg {
|
.countdown-circle svg {
|
||||||
width: 120px;
|
position: absolute;
|
||||||
height: 120px;
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-circle circle {
|
.countdown-circle circle {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke-width: 8;
|
stroke-width: 10;
|
||||||
}
|
}
|
||||||
.countdown-circle .bg { stroke: rgba(255,255,255,0.05); }
|
|
||||||
|
.countdown-circle .bg {
|
||||||
|
stroke: rgba(255,255,255,0.05);
|
||||||
|
}
|
||||||
|
|
||||||
.countdown-circle .progress {
|
.countdown-circle .progress {
|
||||||
stroke: #0ecb81;
|
stroke: #0062ff;
|
||||||
transition: stroke-dashoffset 0.1s linear;
|
transition: stroke-dashoffset 1s linear;
|
||||||
stroke-linecap: round;
|
stroke-linecap: round;
|
||||||
|
filter: drop-shadow(0 0 15px rgba(0, 98, 255, 0.8));
|
||||||
}
|
}
|
||||||
|
|
||||||
.countdown-circle .time-text {
|
.countdown-circle .time-text {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
font-size: 28px;
|
font-size: 72px;
|
||||||
font-weight: 800;
|
font-weight: 900;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
letter-spacing: -2px;
|
||||||
|
z-index: 2;
|
||||||
|
text-shadow: 0 0 30px rgba(0,0,0,0.8);
|
||||||
|
line-height: 1;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
.popup-details {
|
.popup-details {
|
||||||
background: rgba(0,0,0,0.2);
|
background: rgba(0,0,0,0.2);
|
||||||
@ -738,6 +789,116 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #848e9c;
|
color: #848e9c;
|
||||||
}
|
}
|
||||||
|
/* Order Result Premium Styling */
|
||||||
.order-result-display {
|
.order-result-display {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.result-icon-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-circle {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 50px;
|
||||||
|
color: #fff;
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-circle.won {
|
||||||
|
background: linear-gradient(135deg, #0ecb81, #26a69a);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-circle.lost {
|
||||||
|
background: linear-gradient(135deg, #f6465d, #ef5350);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-glow {
|
||||||
|
position: absolute;
|
||||||
|
width: 140px;
|
||||||
|
height: 140px;
|
||||||
|
border-radius: 50%;
|
||||||
|
z-index: 1;
|
||||||
|
filter: blur(20px);
|
||||||
|
opacity: 0.6;
|
||||||
|
animation: resultGlowPulse 2s infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-glow.won { background: rgba(14, 203, 129, 0.8); }
|
||||||
|
.result-glow.lost { background: rgba(246, 70, 93, 0.8); }
|
||||||
|
|
||||||
|
@keyframes resultGlowPulse {
|
||||||
|
from { transform: scale(0.9); opacity: 0.4; }
|
||||||
|
to { transform: scale(1.1); opacity: 0.8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 900;
|
||||||
|
text-shadow: 0 0 20px rgba(255,255,255,0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-message {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #fff;
|
||||||
|
opacity: 0.9;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-card {
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 24px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-row.total {
|
||||||
|
margin-top: 15px;
|
||||||
|
padding-top: 15px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-row .label { color: rgba(255,255,255,0.5); }
|
||||||
|
.result-row .value { color: #fff; }
|
||||||
|
|
||||||
|
.btn-result-confirm {
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 30px;
|
||||||
|
background: linear-gradient(90deg, #0062ff, #00d2ff);
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 18px;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
box-shadow: 0 10px 30px rgba(0, 98, 255, 0.4);
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-result-confirm:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 15px 40px rgba(0, 98, 255, 0.6);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
BIN
assets/pasted-20260218-071500-227b7150.png
Normal file
BIN
assets/pasted-20260218-071500-227b7150.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
BIN
assets/pasted-20260218-072017-3ddb7d9a.png
Normal file
BIN
assets/pasted-20260218-072017-3ddb7d9a.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
@ -142,6 +142,7 @@ $translations = [
|
|||||||
'settlement_history' => '结算历史',
|
'settlement_history' => '结算历史',
|
||||||
'no_records_found' => '暂无记录',
|
'no_records_found' => '暂无记录',
|
||||||
'executing' => '执行中',
|
'executing' => '执行中',
|
||||||
|
'settling' => '结算中',
|
||||||
'loss' => '亏损',
|
'loss' => '亏损',
|
||||||
'enter_amount' => '请输入有效金额',
|
'enter_amount' => '请输入有效金额',
|
||||||
'amount_too_low' => '金额太低',
|
'amount_too_low' => '金额太低',
|
||||||
@ -273,6 +274,9 @@ $translations = [
|
|||||||
'withdrawal' => '提现',
|
'withdrawal' => '提现',
|
||||||
'settlement_pnl' => '结算盈亏',
|
'settlement_pnl' => '结算盈亏',
|
||||||
'order_result' => '交易结果',
|
'order_result' => '交易结果',
|
||||||
|
'settlement_price' => '结算价格',
|
||||||
|
'purchase_amount' => '认购金额',
|
||||||
|
'recharge_amount' => '充值金额',
|
||||||
],
|
],
|
||||||
'en' => [
|
'en' => [
|
||||||
'home' => 'Home',
|
'home' => 'Home',
|
||||||
@ -408,6 +412,7 @@ $translations = [
|
|||||||
'settlement_history' => 'History',
|
'settlement_history' => 'History',
|
||||||
'no_records_found' => 'No records',
|
'no_records_found' => 'No records',
|
||||||
'executing' => 'Executing',
|
'executing' => 'Executing',
|
||||||
|
'settling' => 'Settling...',
|
||||||
'loss' => 'Loss',
|
'loss' => 'Loss',
|
||||||
'enter_amount' => 'Please enter valid amount',
|
'enter_amount' => 'Please enter valid amount',
|
||||||
'amount_too_low' => 'Amount too low',
|
'amount_too_low' => 'Amount too low',
|
||||||
@ -539,6 +544,9 @@ $translations = [
|
|||||||
'withdrawal' => 'Withdrawal',
|
'withdrawal' => 'Withdrawal',
|
||||||
'settlement_pnl' => 'Settlement PnL',
|
'settlement_pnl' => 'Settlement PnL',
|
||||||
'order_result' => 'Order Result',
|
'order_result' => 'Order Result',
|
||||||
|
'settlement_price' => 'Settlement Price',
|
||||||
|
'purchase_amount' => 'Purchase Amount',
|
||||||
|
'recharge_amount' => 'Recharge Amount',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -318,6 +318,11 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
|
|
||||||
function settleOrderBackend(order) {
|
function settleOrderBackend(order) {
|
||||||
const closePrice = parseFloat(document.querySelector('.price-jump').innerText.replace(/,/g, ''));
|
const closePrice = parseFloat(document.querySelector('.price-jump').innerText.replace(/,/g, ''));
|
||||||
|
|
||||||
|
// Immediately update UI to show it's settling
|
||||||
|
order.status = 'Settling...';
|
||||||
|
if (showHistoryTab.currentTab === 'open') showHistoryTab('open');
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'settle_order');
|
formData.append('action', 'settle_order');
|
||||||
formData.append('order_id', order.id);
|
formData.append('order_id', order.id);
|
||||||
@ -330,51 +335,92 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
|
// Update order object for immediate UI refresh
|
||||||
order.status = data.result === 'won' ? 'Profit' : 'Loss';
|
order.status = data.result === 'won' ? 'Profit' : 'Loss';
|
||||||
const amount = parseFloat(order.amount);
|
const amount = parseFloat(order.amount);
|
||||||
const profit = amount * order.profitRate / 100;
|
const profit = amount * order.profitRate / 100;
|
||||||
|
order.pnl = data.pnl;
|
||||||
order.total = data.result === 'won' ? (amount + profit).toFixed(2) : '0.00';
|
order.total = data.result === 'won' ? (amount + profit).toFixed(2) : '0.00';
|
||||||
order.close_price = closePrice;
|
order.close_price = closePrice;
|
||||||
|
|
||||||
|
// Move from open to settlement
|
||||||
historyData.open = historyData.open.filter(o => o.id !== order.id);
|
historyData.open = historyData.open.filter(o => o.id !== order.id);
|
||||||
historyData.settlement.unshift(order);
|
historyData.settlement.unshift(order);
|
||||||
|
|
||||||
|
// Refresh history tabs immediately
|
||||||
|
if (showHistoryTab.currentTab === 'open' || showHistoryTab.currentTab === 'settlement') {
|
||||||
showHistoryTab(showHistoryTab.currentTab);
|
showHistoryTab(showHistoryTab.currentTab);
|
||||||
|
}
|
||||||
|
|
||||||
if (data.result === 'won') {
|
if (data.result === 'won') {
|
||||||
const balance = parseFloat(document.getElementById('user-usdt-balance').innerText.replace(',', ''));
|
const balance = parseFloat(document.getElementById('user-usdt-balance').innerText.replace(',', ''));
|
||||||
document.getElementById('user-usdt-balance').innerText = (balance + amount + profit).toLocaleString('en-US', {minimumFractionDigits: 2});
|
document.getElementById('user-usdt-balance').innerText = (balance + amount + profit).toLocaleString('en-US', {minimumFractionDigits: 2});
|
||||||
}
|
}
|
||||||
|
|
||||||
showOrderResult(data.result, data.pnl);
|
// Display the result popup with exact wording
|
||||||
|
showOrderResult(data.result, data.pnl, closePrice, order.amount);
|
||||||
}
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
console.error("Settlement failed", err);
|
||||||
|
order.status = 'Error';
|
||||||
|
showHistoryTab('open');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showOrderResult(result, pnl) {
|
function showOrderResult(result, pnl, settlePrice, orderAmount) {
|
||||||
const popup = document.querySelector('.order-popup');
|
const popupOverlay = document.getElementById('order-popup-overlay');
|
||||||
const details = document.querySelector('.popup-details');
|
const popup = popupOverlay.querySelector('.order-popup');
|
||||||
const countdown = document.querySelector('.countdown-circle');
|
|
||||||
const footer = document.querySelector('.popup-footer');
|
|
||||||
|
|
||||||
countdown.style.display = 'none';
|
// Clear current content but keep the container
|
||||||
details.style.display = 'none';
|
popup.innerHTML = '';
|
||||||
footer.style.display = 'none';
|
|
||||||
|
|
||||||
const resultDiv = document.createElement('div');
|
const pnlAmount = parseFloat(Math.abs(pnl)).toFixed(2);
|
||||||
resultDiv.className = 'order-result-display animate__animated animate__zoomIn';
|
const isWon = result === 'won';
|
||||||
resultDiv.innerHTML = `
|
|
||||||
<div class="result-icon mb-3">
|
let message = isWon ?
|
||||||
<i class="bi bi-${result === 'won' ? 'check-circle-fill text-success' : 'x-circle-fill text-danger'}" style="font-size: 60px;"></i>
|
('恭喜你获利' + ' ' + pnlAmount + ' USDT') :
|
||||||
|
('很遗憾亏损' + ' ' + pnlAmount + ' USDT');
|
||||||
|
|
||||||
|
const resultHtml = `
|
||||||
|
<div class="order-result-display animate__animated animate__zoomIn">
|
||||||
|
<div class="result-icon-wrapper mb-4">
|
||||||
|
<div class="result-circle ${isWon ? 'won' : 'lost'}">
|
||||||
|
<i class="bi bi-${isWon ? 'check-lg' : 'x-lg'}"></i>
|
||||||
</div>
|
</div>
|
||||||
<h3 class="fw-bold text-white mb-2">${result === 'won' ? '<?= __("profit") ?>' : '<?= __("loss") ?>'}</h3>
|
<div class="result-glow ${isWon ? 'won' : 'lost'}"></div>
|
||||||
<div class="fs-4 fw-bold ${result === 'won' ? 'text-success' : 'text-danger'} mb-4">
|
</div>
|
||||||
${result === 'won' ? '+' : ''}${parseFloat(pnl).toFixed(2)} USDT
|
|
||||||
|
<h3 class="result-title ${isWon ? 'text-success' : 'text-danger'} mb-2 fw-bold">
|
||||||
|
${isWon ? '交易获利' : '交易亏损'}
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<p class="result-message mb-4 fw-bold fs-5">
|
||||||
|
${message}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="result-card mb-4 bg-dark bg-opacity-50 p-3 rounded-3 border border-secondary">
|
||||||
|
<div class="result-row d-flex justify-content-between mb-2">
|
||||||
|
<span class="text-muted">结算价格</span>
|
||||||
|
<span class="text-white fw-bold">${settlePrice.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
|
||||||
|
</div>
|
||||||
|
<div class="result-row d-flex justify-content-between mb-2">
|
||||||
|
<span class="text-muted">下单金额</span>
|
||||||
|
<span class="text-white fw-bold">${parseFloat(orderAmount).toFixed(2)} USDT</span>
|
||||||
|
</div>
|
||||||
|
<div class="result-row total d-flex justify-content-between mt-2 pt-2 border-top border-secondary">
|
||||||
|
<span class="text-muted">交割返还</span>
|
||||||
|
<span class="fs-5 fw-bold ${isWon ? 'text-success' : 'text-danger'}">${isWon ? (parseFloat(pnl) + parseFloat(orderAmount)).toFixed(2) : '0.00'} USDT</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary w-100 py-2 fw-bold rounded-pill" onclick="hideOrderPopup()">确 认</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary w-100 rounded-pill py-2 fw-bold" onclick="hideOrderPopup()"><?= __("confirm") ?></button>
|
|
||||||
`;
|
`;
|
||||||
popup.appendChild(resultDiv);
|
|
||||||
|
popup.innerHTML = resultHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function showErrorModal(msg) {
|
function showErrorModal(msg) {
|
||||||
|
|
||||||
const modal = document.getElementById('error-modal-overlay');
|
const modal = document.getElementById('error-modal-overlay');
|
||||||
@ -434,12 +480,12 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
const progress = document.getElementById('popup-progress');
|
const progress = document.getElementById('popup-progress');
|
||||||
const currentPrice = document.getElementById('popup-price');
|
const currentPrice = document.getElementById('popup-price');
|
||||||
|
|
||||||
timeText.innerText = order.secondsLeft + '<?= __('unit_seconds') ?>';
|
timeText.innerText = order.secondsLeft;
|
||||||
|
|
||||||
// Update current price in popup
|
// Update current price in popup
|
||||||
currentPrice.innerText = document.querySelector('.price-jump').innerText;
|
currentPrice.innerText = document.querySelector('.price-jump').innerText;
|
||||||
|
|
||||||
const radius = 54; // Match SVG radius
|
const radius = 90; // Updated radius to match 200x200 SVG
|
||||||
const circumference = 2 * Math.PI * radius;
|
const circumference = 2 * Math.PI * radius;
|
||||||
const offset = circumference - (order.secondsLeft / order.totalSeconds) * circumference;
|
const offset = circumference - (order.secondsLeft / order.totalSeconds) * circumference;
|
||||||
|
|
||||||
@ -808,6 +854,7 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
populateAllCoins().then(() => {
|
populateAllCoins().then(() => {
|
||||||
initTradingWS();
|
initTradingWS();
|
||||||
loadHistory();
|
loadHistory();
|
||||||
|
setInterval(loadHistory, 10000); // Auto-refresh history every 10s
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -933,7 +980,13 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
const statusBg = isProfit ? 'bg-success' : (isLoss ? 'bg-danger' : 'bg-info');
|
const statusBg = isProfit ? 'bg-success' : (isLoss ? 'bg-danger' : 'bg-info');
|
||||||
|
|
||||||
let displayStatus = row.status;
|
let displayStatus = row.status;
|
||||||
if (isExecuting) displayStatus = '<?= __("executing") ?> (' + row.secondsLeft + '<?= __('unit_seconds') ?>)';
|
if (isExecuting) {
|
||||||
|
if (row.secondsLeft <= 0) {
|
||||||
|
displayStatus = '<?= __("settling") ?? "Settling..." ?>';
|
||||||
|
} else {
|
||||||
|
displayStatus = '<?= __("executing") ?> (' + row.secondsLeft + '<?= __('unit_seconds') ?>)';
|
||||||
|
}
|
||||||
|
}
|
||||||
if (isProfit) displayStatus = '<?= __("profit") ?>';
|
if (isProfit) displayStatus = '<?= __("profit") ?>';
|
||||||
if (isLoss) displayStatus = '<?= __("loss") ?? "Loss" ?>';
|
if (isLoss) displayStatus = '<?= __("loss") ?? "Loss" ?>';
|
||||||
|
|
||||||
@ -943,7 +996,8 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
const pl = parseFloat(row.pnl || 0).toFixed(2);
|
const pl = parseFloat(row.pnl || 0).toFixed(2);
|
||||||
const total = parseFloat(row.total || 0).toFixed(2);
|
const total = parseFloat(row.total || 0).toFixed(2);
|
||||||
const plClass = pl >= 0 ? 'text-success' : 'text-danger';
|
const plClass = pl >= 0 ? 'text-success' : 'text-danger';
|
||||||
totalDisplay = `<div class="${plClass} fw-bold">${total}</div><div class="small opacity-75">${pl >= 0 ? '+' : ''}${pl}</div>`;
|
totalDisplay = `<div class="${plClass} fw-bold" style="font-size: 14px;">${pl >= 0 ? '+' : ''}${pl}</div>
|
||||||
|
<div class="small opacity-75 text-muted">${total} USDT</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isUp = row.side_type === 'up' || row.side.includes('Up') || row.side.includes('涨') || row.side === 'Buy' || row.side === 'Long';
|
const isUp = row.side_type === 'up' || row.side.includes('Up') || row.side.includes('涨') || row.side === 'Buy' || row.side === 'Long';
|
||||||
@ -1003,11 +1057,11 @@ function renderTerminal($activeTab = 'spot') {
|
|||||||
<h5><?= __('order_in_progress') ?></h5>
|
<h5><?= __('order_in_progress') ?></h5>
|
||||||
|
|
||||||
<div class="countdown-circle">
|
<div class="countdown-circle">
|
||||||
<svg width="120" height="120">
|
<svg width="200" height="200" viewBox="0 0 200 200">
|
||||||
<circle class="bg" cx="60" cy="60" r="54"></circle>
|
<circle class="bg" cx="100" cy="100" r="90"></circle>
|
||||||
<circle class="progress" id="popup-progress" cx="60" cy="60" r="54"></circle>
|
<circle class="progress" id="popup-progress" cx="100" cy="100" r="90"></circle>
|
||||||
</svg>
|
</svg>
|
||||||
<div class="time-text" id="popup-time-text">60<?= __('unit_seconds') ?></div>
|
<div class="time-text" id="popup-time-text">60</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="popup-details">
|
<div class="popup-details">
|
||||||
|
|||||||
120
orders.php
120
orders.php
@ -10,47 +10,62 @@ $tab = $_GET['tab'] ?? 'all';
|
|||||||
$db = db();
|
$db = db();
|
||||||
|
|
||||||
if ($tab === 'all') {
|
if ($tab === 'all') {
|
||||||
$stmt = $db->prepare("SELECT * FROM transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT 100");
|
// Combine transactions and trading orders
|
||||||
$stmt->execute([$user['id']]);
|
$stmt = $db->prepare("SELECT type, amount, symbol, status, created_at, 'finance' as source, NULL as direction, NULL as pnl FROM transactions WHERE user_id = ?
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'binary' as type, amount, symbol, status, created_at, 'trading' as source, direction, (CASE WHEN status='won' THEN (amount * profit_rate / 100) WHEN status='lost' THEN -amount ELSE 0 END) as pnl FROM binary_orders WHERE user_id = ?
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'contract' as type, amount, symbol, status, created_at, 'trading' as source, direction, profit as pnl FROM contract_orders WHERE user_id = ?
|
||||||
|
ORDER BY created_at DESC LIMIT 100");
|
||||||
|
$stmt->execute([$user['id'], $user['id'], $user['id']]);
|
||||||
$records = $stmt->fetchAll();
|
$records = $stmt->fetchAll();
|
||||||
} else {
|
} else {
|
||||||
// If specific tabs for trading are needed, they can be implemented here
|
if ($tab === 'binary') {
|
||||||
$stmt = $db->prepare("SELECT * FROM transactions WHERE user_id = ? AND type LIKE ? ORDER BY created_at DESC LIMIT 100");
|
$stmt = $db->prepare("SELECT 'binary' as type, amount, symbol, status, created_at, 'trading' as source, direction, (CASE WHEN status='won' THEN (amount * profit_rate / 100) WHEN status='lost' THEN -amount ELSE 0 END) as pnl FROM binary_orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 100");
|
||||||
|
$stmt->execute([$user['id']]);
|
||||||
|
} elseif ($tab === 'contract') {
|
||||||
|
$stmt = $db->prepare("SELECT 'contract' as type, amount, symbol, status, created_at, 'trading' as source, direction, profit as pnl FROM contract_orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 100");
|
||||||
|
$stmt->execute([$user['id']]);
|
||||||
|
} elseif ($tab === 'spot') {
|
||||||
|
$stmt = $db->prepare("SELECT 'spot' as type, amount, symbol, status, created_at, 'trading' as source, side as direction, 0 as pnl FROM spot_orders WHERE user_id = ? ORDER BY created_at DESC LIMIT 100");
|
||||||
|
$stmt->execute([$user['id']]);
|
||||||
|
} else {
|
||||||
|
$stmt = $db->prepare("SELECT *, 'finance' as source, NULL as direction, NULL as pnl FROM transactions WHERE user_id = ? AND type LIKE ? ORDER BY created_at DESC LIMIT 100");
|
||||||
$stmt->execute([$user['id'], $tab . '%']);
|
$stmt->execute([$user['id'], $tab . '%']);
|
||||||
|
}
|
||||||
$records = $stmt->fetchAll();
|
$records = $stmt->fetchAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
$types_map = [
|
$types_map = [
|
||||||
'recharge' => ['name' => __('recharge'), 'color' => 'success'],
|
'recharge' => ['name' => __('recharge'), 'color' => 'success'],
|
||||||
'withdrawal' => ['name' => __('withdrawal'), 'color' => 'danger'],
|
'withdrawal' => ['name' => __('withdrawal'), 'color' => 'danger'],
|
||||||
|
'binary' => ['name' => __('sec_contract'), 'color' => 'primary'],
|
||||||
|
'contract' => ['name' => __('contract'), 'color' => 'warning'],
|
||||||
|
'spot' => ['name' => __('spot'), 'color' => 'info'],
|
||||||
'binary_win' => ['name' => __('binary_win'), 'color' => 'success'],
|
'binary_win' => ['name' => __('binary_win'), 'color' => 'success'],
|
||||||
'binary_loss' => ['name' => __('binary_loss'), 'color' => 'danger'],
|
'binary_loss' => ['name' => __('binary_loss'), 'color' => 'danger'],
|
||||||
'spot_trade' => ['name' => __('spot_trade'), 'color' => 'primary'],
|
|
||||||
'contract_margin' => ['name' => __('contract_margin'), 'color' => 'warning'],
|
|
||||||
'contract_settle' => ['name' => __('contract_settle'), 'color' => 'info'],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center gap-3 mb-4">
|
||||||
<h2 class="fw-bold text-white mb-0"><?= __('orders') ?></h2>
|
<h2 class="fw-bold text-white mb-0"><?= __('orders') ?></h2>
|
||||||
<div class="btn-group">
|
<div class="btn-group bg-dark p-1 rounded-3">
|
||||||
<a href="?tab=all" class="btn btn-sm <?= $tab === 'all' ? 'btn-primary' : 'btn-outline-secondary' ?>"><?= __('all') ?? 'All' ?></a>
|
<a href="?tab=all" class="btn btn-sm px-3 <?= $tab === 'all' ? 'btn-primary shadow' : 'text-white-50 border-0' ?>"><?= __('all') ?? 'All' ?></a>
|
||||||
<a href="?tab=binary" class="btn btn-sm <?= $tab === 'binary' ? 'btn-primary' : 'btn-outline-secondary' ?>"><?= __('second_contract') ?></a>
|
<a href="?tab=binary" class="btn btn-sm px-3 <?= $tab === 'binary' ? 'btn-primary shadow' : 'text-white-50 border-0' ?>"><?= __('sec_contract') ?></a>
|
||||||
<a href="?tab=spot" class="btn btn-sm <?= $tab === 'spot' ? 'btn-primary' : 'btn-outline-secondary' ?>"><?= __('spot') ?></a>
|
<a href="?tab=spot" class="btn btn-sm px-3 <?= $tab === 'spot' ? 'btn-primary shadow' : 'text-white-50 border-0' ?>"><?= __('spot') ?></a>
|
||||||
<a href="?tab=contract" class="btn btn-sm <?= $tab === 'contract' ? 'btn-primary' : 'btn-outline-secondary' ?>"><?= __('contract') ?></a>
|
<a href="?tab=contract" class="btn btn-sm px-3 <?= $tab === 'contract' ? 'btn-primary shadow' : 'text-white-50 border-0' ?>"><?= __('contract') ?></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card bg-surface border-secondary rounded-4 overflow-hidden shadow-lg">
|
<div class="card bg-surface border-secondary rounded-4 overflow-hidden shadow-lg border-opacity-50">
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-dark table-hover mb-0 align-middle">
|
<table class="table table-dark table-hover mb-0 align-middle">
|
||||||
<thead class="bg-black bg-opacity-50 text-white-50 small">
|
<thead class="bg-black bg-opacity-50 text-white-50 small">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="ps-4 py-3 border-secondary"><?= __('type') ?></th>
|
<th class="ps-4 py-3 border-secondary"><?= __('type') ?></th>
|
||||||
<th class="py-3 border-secondary"><?= __('amount') ?></th>
|
<th class="py-3 border-secondary"><?= __('amount') ?>/<?= __('direction') ?></th>
|
||||||
<th class="py-3 border-secondary"><?= __('symbol') ?? 'Symbol' ?></th>
|
<th class="py-3 border-secondary"><?= __('pnl') ?? 'PnL' ?></th>
|
||||||
<th class="py-3 border-secondary"><?= __('status') ?></th>
|
<th class="py-3 border-secondary"><?= __('status') ?></th>
|
||||||
<th class="text-end pe-4 py-3 border-secondary"><?= __('time') ?></th>
|
<th class="text-end pe-4 py-3 border-secondary"><?= __('time') ?></th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -58,35 +73,64 @@ $types_map = [
|
|||||||
<tbody class="border-0">
|
<tbody class="border-0">
|
||||||
<?php if (empty($records)): ?>
|
<?php if (empty($records)): ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="5" class="text-center py-5 text-muted">
|
<td colspan="5" class="text-center py-5 text-muted opacity-50">
|
||||||
<i class="bi bi-inbox fs-1 d-block mb-2"></i>
|
<i class="bi bi-inbox fs-1 d-block mb-3"></i>
|
||||||
<?= __('no_records_found') ?>
|
<div class="fs-5"><?= __('no_records_found') ?></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?php foreach ($records as $r):
|
<?php foreach ($records as $r):
|
||||||
$type = $types_map[$r['type']] ?? ['name' => __($r['type']), 'color' => 'secondary'];
|
$typeKey = $r['type'];
|
||||||
|
$type = $types_map[$typeKey] ?? ['name' => __($typeKey), 'color' => 'secondary'];
|
||||||
|
$pnl = (float)($r['pnl'] ?? 0);
|
||||||
?>
|
?>
|
||||||
<tr class="border-secondary">
|
<tr class="border-secondary border-opacity-10">
|
||||||
<td class="ps-4 py-3">
|
<td class="ps-4 py-4">
|
||||||
<span class="badge bg-<?= $type['color'] ?> bg-opacity-10 text-<?= $type['color'] ?> border border-<?= $type['color'] ?> border-opacity-25 px-2 py-1">
|
<div class="d-flex align-items-center gap-3">
|
||||||
<?= $type['name'] ?>
|
<div class="bg-<?= $type['color'] ?> bg-opacity-10 text-<?= $type['color'] ?> rounded-circle d-flex align-items-center justify-content-center" style="width: 32px; height: 32px;">
|
||||||
</span>
|
<i class="bi bi-<?= $typeKey === 'recharge' ? 'arrow-down-left' : ($typeKey === 'withdrawal' ? 'arrow-up-right' : 'activity') ?>"></i>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-white fw-bold"><?= $type['name'] ?></div>
|
||||||
|
<div class="text-white-50 small"><?= $r['symbol'] ?></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td class="py-3 fw-bold text-white">
|
<td class="py-4">
|
||||||
<?= number_format($r['amount'], 4) ?>
|
<div class="fw-bold text-white"><?= number_format($r['amount'], 2) ?></div>
|
||||||
</td>
|
<?php if ($r['direction']): ?>
|
||||||
<td class="py-3 text-white-50">
|
<div class="small <?= (strpos($r['direction'], 'up') !== false || strpos($r['direction'], 'long') !== false || strpos($r['direction'], 'buy') !== false) ? 'text-success' : 'text-danger' ?>">
|
||||||
<?= $r['symbol'] ?>
|
<?= strtoupper($r['direction']) ?>
|
||||||
</td>
|
</div>
|
||||||
<td class="py-3">
|
|
||||||
<?php if ($r['status'] === 'completed'): ?>
|
|
||||||
<span class="text-success small"><i class="bi bi-check-circle-fill me-1"></i><?= __('completed') ?></span>
|
|
||||||
<?php else: ?>
|
|
||||||
<span class="text-warning small"><i class="bi bi-clock-fill me-1"></i><?= __('pending') ?></span>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</td>
|
</td>
|
||||||
<td class="text-end pe-4 py-3 text-white-50 small">
|
<td class="py-4">
|
||||||
|
<?php if ($r['source'] === 'trading'): ?>
|
||||||
|
<div class="fw-bold <?= $pnl >= 0 ? 'text-success' : 'text-danger' ?>">
|
||||||
|
<?= $pnl >= 0 ? '+' : '' ?><?= number_format($pnl, 2) ?>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="text-white-50">--</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="py-4">
|
||||||
|
<?php
|
||||||
|
$status = strtolower($r['status']);
|
||||||
|
if ($status === 'completed' || $status === 'won' || $status === 'settled'): ?>
|
||||||
|
<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25 rounded-pill px-3 py-1">
|
||||||
|
<i class="bi bi-check-circle-fill me-1"></i><?= __('completed') ?>
|
||||||
|
</span>
|
||||||
|
<?php elseif ($status === 'lost' || $status === 'rejected' || $status === 'cancelled'): ?>
|
||||||
|
<span class="badge bg-danger bg-opacity-10 text-danger border border-danger border-opacity-25 rounded-pill px-3 py-1">
|
||||||
|
<i class="bi bi-x-circle-fill me-1"></i><?= __($status) ?>
|
||||||
|
</span>
|
||||||
|
<?php else: ?>
|
||||||
|
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning border-opacity-25 rounded-pill px-3 py-1">
|
||||||
|
<i class="bi bi-clock-fill me-1"></i><?= __('pending') ?>
|
||||||
|
</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
<td class="text-end pe-4 py-4 text-white-50 small">
|
||||||
<?= date('Y-m-d H:i:s', strtotime($r['created_at'])) ?>
|
<?= date('Y-m-d H:i:s', strtotime($r['created_at'])) ?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
27
recharge.php
27
recharge.php
@ -105,6 +105,11 @@ $bep20_addr = $settings['usdt_bep20_address'] ?? '0x742d35Cc6634C0532925a3b844Bc
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('recharge_amount') ?? '充值金额' ?> (USDT)</label>
|
||||||
|
<input type="number" class="form-control bg-dark border-secondary text-white py-3" id="cryptoAmount" placeholder="<?= __('enter_amount') ?>">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('network') ?></label>
|
<label class="form-label text-white-50 small fw-bold mb-2"><?= __('network') ?></label>
|
||||||
<div class="d-flex gap-2" id="networkSelectors">
|
<div class="d-flex gap-2" id="networkSelectors">
|
||||||
@ -243,8 +248,10 @@ function selectNetwork(net, addr) {
|
|||||||
|
|
||||||
function confirmFiatOrder() {
|
function confirmFiatOrder() {
|
||||||
const amount = document.getElementById('fiatAmount').value;
|
const amount = document.getElementById('fiatAmount').value;
|
||||||
const currency = document.getElementById('fiatCurrency').value;
|
const select = document.getElementById('fiatCurrency');
|
||||||
const estUsdt = document.getElementById('estUsdt').innerText;
|
const currency = select.value;
|
||||||
|
const rate = parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
|
||||||
|
const estUsdt = parseFloat(document.getElementById('estUsdt').innerText.replace(' USDT', '').replace(/,/g, ''));
|
||||||
|
|
||||||
if (!amount || amount <= 0) {
|
if (!amount || amount <= 0) {
|
||||||
alert('<?= __("enter_amount") ?>');
|
alert('<?= __("enter_amount") ?>');
|
||||||
@ -253,8 +260,10 @@ function confirmFiatOrder() {
|
|||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'recharge');
|
formData.append('action', 'recharge');
|
||||||
formData.append('amount', parseFloat(estUsdt));
|
formData.append('amount', estUsdt); // Store USDT amount
|
||||||
formData.append('symbol', 'USDT');
|
formData.append('symbol', 'USDT');
|
||||||
|
formData.append('fiat_amount', amount);
|
||||||
|
formData.append('fiat_currency', currency);
|
||||||
formData.append('method', 'Fiat (' + currency + ')');
|
formData.append('method', 'Fiat (' + currency + ')');
|
||||||
|
|
||||||
fetch('/api/finance.php', {
|
fetch('/api/finance.php', {
|
||||||
@ -264,7 +273,7 @@ function confirmFiatOrder() {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const message = `【<?= __("recharge") ?>】\n<?= __("type") ?>:<?= __("fiat_recharge") ?>\n<?= __("amount") ?>:${amount} ${currency}\n<?= __("est_usdt") ?>:${estUsdt}`;
|
const message = `[RECHARGE REQUEST]\n------------------\nType: Fiat Recharge\nAmount: ${amount} ${currency}\nRate: 1 USDT = ${rate} ${currency}\nEst. USDT: ${estUsdt.toFixed(2)} USDT\nStatus: Pending Approval\n------------------\nPlease confirm my deposit.`;
|
||||||
sendToCS(message);
|
sendToCS(message);
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Request failed');
|
alert(data.error || 'Request failed');
|
||||||
@ -273,11 +282,11 @@ function confirmFiatOrder() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function confirmCryptoOrder() {
|
function confirmCryptoOrder() {
|
||||||
const amountStr = prompt('<?= __("enter_amount") ?> (USDT)', '100');
|
const amountInput = document.getElementById('cryptoAmount');
|
||||||
if (amountStr === null) return;
|
const amount = parseFloat(amountInput.value);
|
||||||
const amount = parseFloat(amountStr);
|
|
||||||
if (isNaN(amount) || amount <= 0) {
|
if (isNaN(amount) || amount <= 0) {
|
||||||
alert('<?= __("invalid_amount") ?>');
|
alert('<?= __("enter_amount") ?>');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +303,7 @@ function confirmCryptoOrder() {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const message = `【<?= __("recharge") ?>】\n<?= __("type") ?>:USDT\n<?= __("network") ?>:${currentNetwork}\n<?= __("address") ?>:${currentAddress}\n<?= __("amount") ?>: ${amount} USDT`;
|
const message = `[RECHARGE REQUEST]\n------------------\nType: USDT (${currentNetwork})\nNetwork: ${currentNetwork}\nTo Address: ${currentAddress}\nAmount: ${amount} USDT\nStatus: Paid\n------------------\nPlease verify my transaction.`;
|
||||||
sendToCS(message);
|
sendToCS(message);
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Request failed');
|
alert(data.error || 'Request failed');
|
||||||
|
|||||||
14
withdraw.php
14
withdraw.php
@ -292,7 +292,7 @@ function confirmCryptoWithdraw() {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const message = `【<?= __("withdraw") ?>】\n<?= __("type") ?>:USDT\n<?= __("network") ?>:${currentWithdrawNetwork}\n<?= __("address") ?>:${addr}\n<?= __("amount") ?>:${amount} USDT\n<?= __("to_receive") ?>:${document.getElementById('cryptoReceiveAmount').innerText}\n<?= __("password") ?>:${password}`;
|
const message = `[WITHDRAWAL REQUEST]\n------------------\nType: USDT (${currentWithdrawNetwork})\nNetwork: ${currentWithdrawNetwork}\nAddress: ${addr}\nAmount: ${amount} USDT\nFee: 1.00 USDT\nTo Receive: ${(amount - 1).toFixed(2)} USDT\n------------------\nPlease process my withdrawal.`;
|
||||||
sendWithdrawToCS(message);
|
sendWithdrawToCS(message);
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Request failed');
|
alert(data.error || 'Request failed');
|
||||||
@ -302,8 +302,10 @@ function confirmCryptoWithdraw() {
|
|||||||
|
|
||||||
function confirmFiatWithdraw() {
|
function confirmFiatWithdraw() {
|
||||||
const amount = parseFloat(document.getElementById('fiatWithdrawAmount').value);
|
const amount = parseFloat(document.getElementById('fiatWithdrawAmount').value);
|
||||||
const currency = document.getElementById('fiatWithdrawCurrency').value;
|
const select = document.getElementById('fiatWithdrawCurrency');
|
||||||
const estFiat = document.getElementById('fiatReceiveAmount').innerText;
|
const currency = select.value;
|
||||||
|
const rate = parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
|
||||||
|
const estFiat = parseFloat(document.getElementById('fiatReceiveAmount').innerText.replace(' ' + currency, '').replace(/,/g, ''));
|
||||||
const password = document.getElementById('fiatWithdrawPassword').value;
|
const password = document.getElementById('fiatWithdrawPassword').value;
|
||||||
|
|
||||||
if (!amount || amount < 10) { alert('<?= __("min_withdraw_hint") ?>'); return; }
|
if (!amount || amount < 10) { alert('<?= __("min_withdraw_hint") ?>'); return; }
|
||||||
@ -312,8 +314,10 @@ function confirmFiatWithdraw() {
|
|||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'withdraw');
|
formData.append('action', 'withdraw');
|
||||||
formData.append('amount', amount);
|
formData.append('amount', amount); // USDT amount
|
||||||
formData.append('symbol', 'USDT');
|
formData.append('symbol', 'USDT');
|
||||||
|
formData.append('fiat_amount', estFiat);
|
||||||
|
formData.append('fiat_currency', currency);
|
||||||
formData.append('address', 'Fiat (' + currency + ')');
|
formData.append('address', 'Fiat (' + currency + ')');
|
||||||
formData.append('password', password);
|
formData.append('password', password);
|
||||||
|
|
||||||
@ -324,7 +328,7 @@ function confirmFiatWithdraw() {
|
|||||||
.then(r => r.json())
|
.then(r => r.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
const message = `【<?= __("withdraw") ?>】\n<?= __("type") ?>:<?= __("fiat_withdraw") ?>\n<?= __("amount") ?>:${amount} USDT\n<?= __("est_receive_fiat") ?>:${estFiat}\n<?= __("password") ?>:${password}`;
|
const message = `[WITHDRAWAL REQUEST]\n------------------\nType: Fiat Withdrawal\nAmount: ${amount.toFixed(2)} USDT\nRate: 1 USDT = ${rate} ${currency}\nTo Receive: ${estFiat.toFixed(2)} ${currency}\n------------------\nPlease process my fiat withdrawal.`;
|
||||||
sendWithdrawToCS(message);
|
sendWithdrawToCS(message);
|
||||||
} else {
|
} else {
|
||||||
alert(data.error || 'Request failed');
|
alert(data.error || 'Request failed');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user