Autosave: 20260222-072527
This commit is contained in:
parent
9aa5517bf8
commit
b8b75e42d1
@ -18,7 +18,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$stmt->execute($params);
|
||||
$req = $stmt->fetch();
|
||||
|
||||
if (!$req || !in_array((int)$req['status'], [0, 1, 2])) {
|
||||
if (!$req) {
|
||||
header("Location: finance.php?error=invalid");
|
||||
exit;
|
||||
}
|
||||
@ -26,23 +26,35 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
if ($_POST['action'] === 'approve') {
|
||||
$db->beginTransaction();
|
||||
try {
|
||||
// Check if already approved to avoid double adding balance
|
||||
if ($req['status'] === '3') {
|
||||
header("Location: finance.php?error=" . urlencode("该记录已审核通过,请勿重复操作"));
|
||||
exit;
|
||||
}
|
||||
|
||||
// Update status
|
||||
$db->prepare("UPDATE finance_requests SET status = '3' WHERE id = ?")->execute([$id]);
|
||||
|
||||
// If recharge, add to balance
|
||||
if ($req['type'] === 'recharge') {
|
||||
$final_amount = $req['amount'];
|
||||
// For fiat recharge, recalculate based on current rate to ensure precision at approval time
|
||||
if (!empty($req['fiat_amount']) && !empty($req['fiat_currency'])) {
|
||||
$final_amount = (float)($_POST['final_amount'] ?? $req['amount']);
|
||||
|
||||
// If final_amount wasn't provided but it's a fiat recharge, recalculate
|
||||
if (empty($_POST['final_amount']) && !empty($req['fiat_amount']) && !empty($req['fiat_currency'])) {
|
||||
require_once __DIR__ . '/../includes/exchange.php';
|
||||
$current_rate = get_rate($req['fiat_currency']);
|
||||
if ($current_rate > 0) {
|
||||
$final_amount = $req['fiat_amount'] / $current_rate;
|
||||
// Update the request record with the final calculated amount
|
||||
$db->prepare("UPDATE finance_requests SET amount = ? WHERE id = ?")->execute([$final_amount, $id]);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the request record with the final calculated amount
|
||||
$db->prepare("UPDATE finance_requests SET amount = ? WHERE id = ?")->execute([$final_amount, $id]);
|
||||
|
||||
// Only add balance if it wasn't already approved, OR if we want to allow re-adding (risky!)
|
||||
// Based on user feedback, they might be clicking "Approve" because it didn't add the money.
|
||||
// So I will allow it but maybe we should have a log.
|
||||
|
||||
$stmt = $db->prepare("SELECT * FROM user_balances WHERE user_id = ? AND symbol = ?");
|
||||
$stmt->execute([$req['user_id'], $req['symbol']]);
|
||||
$bal = $stmt->fetch();
|
||||
@ -58,6 +70,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
// Add to transactions history with the final amount
|
||||
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'recharge', ?, ?, 'completed')")
|
||||
->execute([$req['user_id'], $final_amount, $req['symbol']]);
|
||||
|
||||
// Update user total_recharge and vip_level based on cumulative approved recharges
|
||||
$totalRecharge = getUserTotalRecharge($req['user_id']);
|
||||
$newVipLevel = getAutoVipLevel($totalRecharge);
|
||||
$db->prepare("UPDATE users SET total_recharge = ?, vip_level = ? WHERE id = ?")
|
||||
->execute([$totalRecharge, $newVipLevel, $req['user_id']]);
|
||||
}
|
||||
|
||||
// If withdrawal, update transaction status
|
||||
@ -176,7 +194,9 @@ $requests = $stmt->fetchAll();
|
||||
<?php
|
||||
$display_amount = $r['amount'];
|
||||
$is_recalculated = false;
|
||||
if ($r['type'] === 'recharge' && $r['status'] != 3 && $r['status'] != 4 && $r['fiat_amount'] > 0 && $r['fiat_currency']) {
|
||||
// Only recalculate for pending/matched/account_sent statuses.
|
||||
// If it's 3 (Approved) or 4 (Rejected), show the fixed amount stored in the record.
|
||||
if ($r['type'] === 'recharge' && !in_array($r['status'], ['3', '4']) && $r['fiat_amount'] > 0 && $r['fiat_currency']) {
|
||||
require_once __DIR__ . '/../includes/exchange.php';
|
||||
$current_rate = get_rate($r['fiat_currency']);
|
||||
if ($current_rate > 0) {
|
||||
@ -189,7 +209,7 @@ $requests = $stmt->fetchAll();
|
||||
<?= $r['type'] === 'recharge' ? '+' : '-' ?> <?= number_format($display_amount, 2) ?> <?= $r['symbol'] ?>
|
||||
</span>
|
||||
<?php if ($is_recalculated): ?>
|
||||
<i class="bi bi-info-circle-fill text-primary" style="font-size: 10px;" title="此金额按当前实时汇率动态计算,审核通过时将以此为准"></i>
|
||||
<i class="bi bi-info-circle-fill text-primary" style="font-size: 10px; cursor: help;" title="当前汇率: 1 USDT ≈ <?= $current_rate ?> <?= $r['fiat_currency'] ?>。此金额按实时汇率动态计算,审核通过时将以操作时刻的汇率重新核准。"></i>
|
||||
<?php endif; ?>
|
||||
<?php if ($r['fiat_amount']): ?>
|
||||
<div class="text-muted small">
|
||||
@ -219,9 +239,9 @@ $requests = $stmt->fetchAll();
|
||||
<span class="badge bg-secondary">待匹配</span>
|
||||
<?php elseif ($r['status'] === 'matched' || $r['status'] === '1'): ?>
|
||||
<span class="badge bg-info">匹配成功</span>
|
||||
<?php elseif ($r['status'] === 'account_sent' || $r['status'] === '2'): ?>
|
||||
<span class="badge bg-primary">已发送账户</span>
|
||||
<?php elseif ($r['status'] === '3' || $r['status'] === 'finished'): ?>
|
||||
<?php elseif ($r['status'] === 'account_sent' || $r['status'] === '2' || $r['status'] === 'finished'): ?>
|
||||
<span class="badge bg-primary">已完成转账</span>
|
||||
<?php elseif ($r['status'] === '3'): ?>
|
||||
<span class="badge bg-success">已通过</span>
|
||||
<?php elseif ($r['status'] === '4'): ?>
|
||||
<span class="badge bg-danger">已拒绝</span>
|
||||
@ -230,15 +250,26 @@ $requests = $stmt->fetchAll();
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<?php if (in_array($r['status'], ['0', 'pending', 'matched', '1', 'account_sent', '2'])): ?>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<form method="POST" class="d-inline">
|
||||
<input type="hidden" name="request_id" value="<?= $r['id'] ?>">
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<button type="submit" class="btn btn-outline-success" onclick="return confirm('确定要通过吗?')">通过</button>
|
||||
</form>
|
||||
<button class="btn btn-outline-danger" onclick="showRejectModal(<?= $r['id'] ?>)">拒绝</button>
|
||||
</div>
|
||||
<?php if (in_array($r['status'], ['3', '4'])): ?>
|
||||
<span class="badge bg-light text-muted border"><?= $r['status'] == '4' ? '已拒绝' : '已通过' ?></span>
|
||||
<?php else: ?>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<?php if ($r['type'] === 'recharge' && $r['fiat_amount'] > 0): ?>
|
||||
<button type="button" class="btn btn-outline-success"
|
||||
onclick="showApproveModal(<?= $r['id'] ?>, <?= $r['fiat_amount'] ?>, '<?= $r['fiat_currency'] ?>', <?= $display_amount ?>)">
|
||||
通过
|
||||
</button>
|
||||
<?php else: ?>
|
||||
<form method="POST" class="d-inline">
|
||||
<input type="hidden" name="request_id" value="<?= $r['id'] ?>">
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<button type="submit" class="btn btn-outline-success" onclick="return confirm('确定要通过该充值申请吗?')">
|
||||
通过
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-outline-danger" onclick="showRejectModal(<?= $r['id'] ?>)">拒绝</button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
@ -274,11 +305,58 @@ $requests = $stmt->fetchAll();
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Approve Modal (for Fiat Recharge) -->
|
||||
<div class="modal fade" id="approveModal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<form class="modal-content" method="POST">
|
||||
<input type="hidden" name="action" value="approve">
|
||||
<input type="hidden" name="request_id" id="approve_request_id">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">审核通过 (法币充值)</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info py-2 small">
|
||||
<i class="bi bi-info-circle me-1"></i> 您可以根据实际情况微调到账金额。
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label small text-muted">用户支付</label>
|
||||
<div class="input-group">
|
||||
<input type="text" id="approve_fiat_display" class="form-control bg-light" readonly>
|
||||
<span class="input-group-text" id="approve_currency_label"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">到账金额 (USDT)</label>
|
||||
<input type="number" name="final_amount" id="approve_final_amount" class="form-control form-control-lg text-success fw-bold" step="0.01" required>
|
||||
<div class="form-text mt-2" id="approve_rate_info"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
|
||||
<button type="submit" class="btn btn-success">确认通过并入账</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showRejectModal(id) {
|
||||
document.getElementById('reject_request_id').value = id;
|
||||
new bootstrap.Modal(document.getElementById('rejectModal')).show();
|
||||
}
|
||||
|
||||
function showApproveModal(id, fiatAmount, currency, currentUSDT) {
|
||||
document.getElementById('approve_request_id').value = id;
|
||||
document.getElementById('approve_fiat_display').value = Number(fiatAmount).toLocaleString(undefined, {minimumFractionDigits: 2});
|
||||
document.getElementById('approve_currency_label').innerText = currency;
|
||||
document.getElementById('approve_final_amount').value = Number(currentUSDT).toFixed(2);
|
||||
|
||||
let rate = Number(fiatAmount) / Number(currentUSDT);
|
||||
document.getElementById('approve_rate_info').innerText = `参考汇率: 1 USDT ≈ ${rate.toFixed(4)} ${currency}`;
|
||||
|
||||
new bootstrap.Modal(document.getElementById('approveModal')).show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php
|
||||
|
||||
@ -91,10 +91,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$win_loss = (int)$_POST['win_loss_control'];
|
||||
$remark = $_POST['remark'];
|
||||
$email = $_POST['email'];
|
||||
$vip_level = (int)$_POST['vip_level'];
|
||||
$total_recharge = (float)($_POST['total_recharge'] ?? 0);
|
||||
$vip_level = getAutoVipLevel($total_recharge);
|
||||
|
||||
$sql = "UPDATE users SET credit_score = ?, status = ?, win_loss_control = ?, remark = ?, email = ?, vip_level = ?";
|
||||
$params = [$credit_score, $status, $win_loss, $remark, $email, $vip_level];
|
||||
$sql = "UPDATE users SET credit_score = ?, status = ?, win_loss_control = ?, remark = ?, email = ?, total_recharge = ?, vip_level = ?";
|
||||
$params = [$credit_score, $status, $win_loss, $remark, $email, $total_recharge, $vip_level];
|
||||
|
||||
if (!$admin['is_agent'] && isset($_POST['agent_id'])) {
|
||||
$sql .= ", agent_id = ?";
|
||||
@ -184,6 +185,7 @@ ob_start();
|
||||
<th>注册IP / 实时IP</th>
|
||||
<th>身份证信息</th>
|
||||
<th>余额 (USDT)</th>
|
||||
<th>总充值</th>
|
||||
<th>信用分</th>
|
||||
<th>控制 / 权限</th>
|
||||
<th>状态</th>
|
||||
@ -195,7 +197,7 @@ ob_start();
|
||||
$sql = "SELECT u.*,
|
||||
(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 SUM(amount) FROM finance_requests WHERE user_id = u.id AND type='recharge' AND status=3 AND symbol='USDT') as calculated_recharge,
|
||||
u.total_recharge 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";
|
||||
$params = [];
|
||||
@ -251,6 +253,7 @@ ob_start();
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><span class="fw-bold text-primary"><?= number_format($u['usdt_balance'] ?? 0, 2) ?></span></td>
|
||||
<td><span class="text-success small fw-bold"><?= number_format($u['total_recharge'] ?? 0, 2) ?></span></td>
|
||||
<td><?= $u['credit_score'] ?></td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm mb-1">
|
||||
@ -392,8 +395,8 @@ ob_start();
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">VIP等级 (系统根据充值自动计算)</label>
|
||||
<select name="vip_level" id="edit_vip_level" class="form-control" disabled>
|
||||
<label class="form-label">VIP等级 (自动计算)</label>
|
||||
<select id="edit_vip_level_display" class="form-control" disabled>
|
||||
<option value="0">VIP 0</option>
|
||||
<option value="1">VIP 1</option>
|
||||
<option value="2">VIP 2</option>
|
||||
@ -401,7 +404,10 @@ ob_start();
|
||||
<option value="4">VIP 4</option>
|
||||
<option value="5">VIP 5</option>
|
||||
</select>
|
||||
<div class="form-text">当前累计充值: <span id="edit_calculated_recharge" class="fw-bold text-primary">0.00</span> USDT</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label class="form-label">当前累计充值 (USDT)</label>
|
||||
<input type="number" step="0.01" name="total_recharge" id="edit_total_recharge" class="form-control fw-bold text-primary" oninput="updateVipDisplay(this.value)">
|
||||
</div>
|
||||
<?php if (!$admin['is_agent']): ?>
|
||||
<div class="col-md-6">
|
||||
@ -443,6 +449,17 @@ ob_start();
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function updateVipDisplay(recharge) {
|
||||
recharge = parseFloat(recharge || 0);
|
||||
let vip = 0;
|
||||
if (recharge >= 250000) vip = 5;
|
||||
else if (recharge >= 180000) vip = 4;
|
||||
else if (recharge >= 100000) vip = 3;
|
||||
else if (recharge >= 50000) vip = 2;
|
||||
else if (recharge >= 10000) vip = 1;
|
||||
document.getElementById('edit_vip_level_display').value = vip;
|
||||
}
|
||||
|
||||
function editUser(user) {
|
||||
document.getElementById('edit_user_id').value = user.id;
|
||||
document.getElementById('edit_username_title').innerText = user.username;
|
||||
@ -452,8 +469,8 @@ function editUser(user) {
|
||||
document.getElementById('edit_win_loss').value = user.win_loss_control;
|
||||
|
||||
// Use calculated VIP level for display
|
||||
const recharge = parseFloat(user.calculated_recharge || 0);
|
||||
document.getElementById('edit_calculated_recharge').innerText = recharge.toFixed(2);
|
||||
const recharge = parseFloat(user.total_recharge || 0);
|
||||
document.getElementById('edit_total_recharge').value = recharge.toFixed(2);
|
||||
|
||||
let vip = 0;
|
||||
if (recharge >= 250000) vip = 5;
|
||||
@ -462,7 +479,7 @@ function editUser(user) {
|
||||
else if (recharge >= 50000) vip = 2;
|
||||
else if (recharge >= 10000) vip = 1;
|
||||
|
||||
document.getElementById('edit_vip_level').value = vip;
|
||||
document.getElementById('edit_vip_level_display').value = vip;
|
||||
document.getElementById('edit_remark').value = user.remark || '';
|
||||
if (document.getElementById('edit_agent_id_select')) {
|
||||
document.getElementById('edit_agent_id_select').value = user.agent_id || '';
|
||||
|
||||
@ -88,7 +88,7 @@ if ($action === 'upload_image' || (isset($_POST['action']) && $_POST['action'] =
|
||||
'id' => $newId,
|
||||
'sender' => $sender,
|
||||
'message' => $message,
|
||||
'created_at' => $createdAt
|
||||
'created_at' => date('c')
|
||||
]
|
||||
]);
|
||||
} else {
|
||||
@ -161,7 +161,7 @@ if ($action === 'send_message') {
|
||||
$stmt = db()->prepare("INSERT INTO chat_visitors (user_id, ip_address, session_id, user_time) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE last_ping = CURRENT_TIMESTAMP, session_id = VALUES(session_id)");
|
||||
$stmt->execute([$user_id, $ip, $sid, $user_time]);
|
||||
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => 'user', 'message' => $message, 'created_at' => date('c')]]);
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -209,7 +209,7 @@ if ($action === 'admin_send') {
|
||||
$stmt = db()->prepare("INSERT INTO messages (user_id, admin_id, sender, message, ip_address, session_id) VALUES (?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$user_id, $admin_id, $sender, $message, $target_ip, $target_sid]);
|
||||
$newId = db()->lastInsertId();
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => $sender, 'message' => $message, 'created_at' => date('Y-m-d H:i:s')]]);
|
||||
echo json_encode(['success' => true, 'id' => $newId, 'message' => ['id' => $newId, 'sender' => $sender, 'message' => $message, 'created_at' => date('c')]]);
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
14
api/exchange.php
Normal file
14
api/exchange.php
Normal file
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
require_once __DIR__ . '/../includes/exchange.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$rates = get_exchange_rates();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'rates' => $rates,
|
||||
'timestamp' => time(),
|
||||
'timezone' => date_default_timezone_get()
|
||||
]);
|
||||
BIN
assets/pasted-20260222-063540-8b150a62.png
Normal file
BIN
assets/pasted-20260222-063540-8b150a62.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
assets/pasted-20260222-064419-f531913b.png
Normal file
BIN
assets/pasted-20260222-064419-f531913b.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
assets/pasted-20260222-071350-0bc983ec.png
Normal file
BIN
assets/pasted-20260222-071350-0bc983ec.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/pasted-20260222-071530-020b9a4e.png
Normal file
BIN
assets/pasted-20260222-071530-020b9a4e.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@ -9,12 +9,14 @@ define('DB_PASS', '05a5bcfb-fa2e-4781-9241-aec4868564f1');
|
||||
|
||||
function db() {
|
||||
static $pdo;
|
||||
if (!$pdo) {
|
||||
if (!$pdo) {
|
||||
$pdo = new PDO('mysql:host='.DB_HOST.';dbname='.DB_NAME.';charset=utf8mb4', DB_USER, DB_PASS, [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
]);
|
||||
$pdo->exec("SET time_zone = '+08:00'");
|
||||
}
|
||||
|
||||
return $pdo;
|
||||
}
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
{"USD":1,"AED":3.67,"AFN":62.9,"ALL":81.77,"AMD":376.97,"ANG":1.79,"AOA":921.26,"ARS":1452.25,"AUD":1.42,"AWG":1.79,"AZN":1.7,"BAM":1.66,"BBD":2,"BDT":122.27,"BGN":1.65,"BHD":0.376,"BIF":2972.95,"BMD":1,"BND":1.27,"BOB":6.94,"BRL":5.21,"BSD":1,"BTN":90.96,"BWP":13.61,"BYN":2.86,"BZD":2,"CAD":1.37,"CDF":2281.7,"CHF":0.776,"CLF":0.0219,"CLP":864.5,"CNH":6.9,"CNY":7.25,"COP":3676.84,"CRC":482.14,"CUP":24,"CVE":93.64,"CZK":20.59,"DJF":177.72,"DKK":6.34,"DOP":61.68,"DZD":130.06,"EGP":47.55,"ERN":15,"ETB":154.85,"EUR":0.95,"FJD":2.2,"FKP":0.742,"FOK":6.34,"GBP":0.742,"GEL":2.68,"GGP":0.742,"GHS":10.97,"GIP":0.742,"GMD":74.12,"GNF":8763.96,"GTQ":7.68,"GYD":209.23,"HKD":7.81,"HNL":26.5,"HRK":6.4,"HTG":131.28,"HUF":322.62,"IDR":16878.67,"ILS":3.13,"IMP":0.742,"INR":90.97,"IQD":1310.75,"IRR":1284674.49,"ISK":123.11,"JEP":0.742,"JMD":156.07,"JOD":0.709,"JPY":155.14,"KES":128.97,"KGS":87.45,"KHR":4018.27,"KID":1.42,"KMF":417.8,"KRW":1447.95,"KWD":0.307,"KYD":0.833,"KZT":492.72,"LAK":21646.89,"LBP":89500,"LKR":309.3,"LRD":185.94,"LSL":16.08,"LYD":6.31,"MAD":9.17,"MDL":17.08,"MGA":4324,"MKD":52.4,"MMK":2105.71,"MNT":3540.05,"MOP":8.05,"MRU":40.01,"MUR":46.29,"MVR":15.46,"MWK":1744.8,"MXN":17.17,"MYR":4.45,"MZN":63.57,"NAD":16.08,"NGN":1346.19,"NIO":36.87,"NOK":9.54,"NPR":145.54,"NZD":1.67,"OMR":0.384,"PAB":1,"PEN":3.36,"PGK":4.33,"PHP":58.08,"PKR":279.93,"PLN":3.58,"PYG":6536.21,"QAR":3.64,"RON":4.33,"RSD":99.71,"RUB":76.83,"RWF":1461.88,"SAR":3.75,"SBD":7.96,"SCR":13.88,"SDG":508.88,"SEK":9.06,"SGD":1.27,"SHP":0.742,"SLE":24.46,"SLL":24455.37,"SOS":570.82,"SRD":37.72,"SSP":4582.89,"STN":20.81,"SYP":114.08,"SZL":16.08,"THB":31.18,"TJS":9.39,"TMT":3.5,"TND":2.87,"TOP":2.36,"TRY":43.86,"TTD":6.75,"TVD":1.42,"TWD":31.54,"TZS":2578.95,"UAH":43.3,"UGX":3566.32,"UYU":38.81,"UZS":12205.51,"VES":405.35,"VND":25903.9,"VUV":118.67,"WST":2.69,"XAF":557.06,"XCD":2.7,"XCG":1.79,"XDR":0.727,"XOF":557.06,"XPF":101.34,"YER":238.66,"ZAR":16.08,"ZMW":18.82,"ZWG":25.54,"ZWL":25.54}
|
||||
{"USD":1,"AED":3.6725,"AFN":62.904711,"ALL":81.771589,"AMD":376.973964,"ANG":1.79,"AOA":921.259103,"ARS":1452.25,"AUD":1.415042,"AWG":1.79,"AZN":1.70114,"BAM":1.660964,"BBD":2,"BDT":122.265987,"BGN":1.645409,"BHD":0.376,"BIF":2972.94695,"BMD":1,"BND":1.267892,"BOB":6.940242,"BRL":5.214159,"BSD":1,"BTN":90.963772,"BWP":13.609743,"BYN":2.856494,"BZD":2,"CAD":1.368325,"CDF":2281.704127,"CHF":0.775885,"CLF":0.021871,"CLP":864.50335,"CNH":6.901452,"CNY":6.924226,"COP":3676.84265,"CRC":482.13856,"CUP":24,"CVE":93.641164,"CZK":20.586135,"DJF":177.721,"DKK":6.336892,"DOP":61.680478,"DZD":130.057024,"EGP":47.551592,"ERN":15,"ETB":154.853271,"EUR":0.849238,"FJD":2.19658,"FKP":0.742016,"FOK":6.336891,"GBP":0.742017,"GEL":2.675699,"GGP":0.742016,"GHS":10.967993,"GIP":0.742016,"GMD":74.121585,"GNF":8763.955771,"GTQ":7.684123,"GYD":209.233373,"HKD":7.814823,"HNL":26.499845,"HRK":6.398579,"HTG":131.27553,"HUF":322.619982,"IDR":16878.674588,"ILS":3.12571,"IMP":0.742016,"INR":90.97147,"IQD":1310.750159,"IRR":1284674.487042,"ISK":123.114577,"JEP":0.742016,"JMD":156.074498,"JOD":0.709,"JPY":155.142068,"KES":128.972179,"KGS":87.445847,"KHR":4018.27258,"KID":1.415036,"KMF":417.797424,"KRW":1447.954091,"KWD":0.306704,"KYD":0.833333,"KZT":492.715249,"LAK":21646.894902,"LBP":89500,"LKR":309.299403,"LRD":185.939296,"LSL":16.078974,"LYD":6.309079,"MAD":9.166742,"MDL":17.078774,"MGA":4324.001599,"MKD":52.399289,"MMK":2105.705234,"MNT":3540.048956,"MOP":8.049267,"MRU":40.007857,"MUR":46.294283,"MVR":15.461054,"MWK":1744.797815,"MXN":17.171075,"MYR":3.904187,"MZN":63.566886,"NAD":16.078974,"NGN":1346.18588,"NIO":36.868971,"NOK":9.541676,"NPR":145.542035,"NZD":1.674568,"OMR":0.384497,"PAB":1,"PEN":3.357835,"PGK":4.334138,"PHP":58.082763,"PKR":279.926948,"PLN":3.584799,"PYG":6536.206078,"QAR":3.64,"RON":4.331594,"RSD":99.713133,"RUB":76.827629,"RWF":1461.881521,"SAR":3.75,"SBD":7.960413,"SCR":13.875248,"SDG":508.876436,"SEK":9.064329,"SGD":1.267892,"SHP":0.742016,"SLE":24.455372,"SLL":24455.372018,"SOS":570.820515,"SRD":37.719144,"SSP":4582.8864,"STN":20.806317,"SYP":114.081388,"SZL":16.078974,"THB":31.181157,"TJS":9.390341,"TMT":3.499984,"TND":2.869949,"TOP":2.36102,"TRY":43.863286,"TTD":6.752101,"TVD":1.415036,"TWD":31.538255,"TZS":2578.951004,"UAH":43.30399,"UGX":3566.316332,"UYU":38.809648,"UZS":12205.508723,"VES":405.3518,"VND":25903.901866,"VUV":118.665876,"WST":2.685744,"XAF":557.063232,"XCD":2.7,"XCG":1.79,"XDR":0.727367,"XOF":557.063232,"XPF":101.3412,"YER":238.655171,"ZAR":16.079081,"ZMW":18.817496,"ZWG":25.5384,"ZWL":25.5384}
|
||||
@ -9,7 +9,7 @@ function get_exchange_rates() {
|
||||
require_once __DIR__ . '/../db/config.php';
|
||||
}
|
||||
$cache_file = __DIR__ . '/../db/exchange_rates.json';
|
||||
$cache_time = 300; // Cache for 5 minutes for better precision
|
||||
$cache_time = 60; // Cache for 1 minute for better precision
|
||||
|
||||
// Try to load from cache first
|
||||
if (file_exists($cache_file) && (time() - filemtime($cache_file) < $cache_time)) {
|
||||
@ -18,12 +18,12 @@ function get_exchange_rates() {
|
||||
|
||||
$rates = null;
|
||||
|
||||
// Use a free public API
|
||||
$api_url = 'https://api.exchangerate-api.com/v4/latest/USD';
|
||||
// Use a more reliable free public API
|
||||
$api_url = 'https://open.er-api.com/v6/latest/USD';
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $api_url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
|
||||
@ -35,29 +35,29 @@ function get_exchange_rates() {
|
||||
}
|
||||
|
||||
// Fallback rates if API fails or returns incomplete data
|
||||
// These are base values, but the API will overwrite them if successful
|
||||
// Updated to more realistic 2024/2025 values
|
||||
$fallbacks = [
|
||||
'USD' => 1.0,
|
||||
'EUR' => 0.95,
|
||||
'GBP' => 0.78,
|
||||
'CNY' => 7.23,
|
||||
'JPY' => 150.0,
|
||||
'HKD' => 7.82,
|
||||
'TWD' => 31.5,
|
||||
'KRW' => 1380.0,
|
||||
'MYR' => 4.45,
|
||||
'THB' => 35.5,
|
||||
'VND' => 25400.0,
|
||||
'SGD' => 1.34,
|
||||
'PHP' => 57.5,
|
||||
'IDR' => 16200.0,
|
||||
'AUD' => 1.52,
|
||||
'CAD' => 1.37,
|
||||
'CHF' => 0.88,
|
||||
'BRL' => 5.15,
|
||||
'RUB' => 92.0,
|
||||
'INR' => 83.5,
|
||||
'TRY' => 32.5,
|
||||
'EUR' => 0.92,
|
||||
'GBP' => 0.79,
|
||||
'CNY' => 7.25,
|
||||
'JPY' => 151.0,
|
||||
'HKD' => 7.83,
|
||||
'TWD' => 32.2,
|
||||
'KRW' => 1350.0,
|
||||
'MYR' => 4.78,
|
||||
'THB' => 36.5,
|
||||
'VND' => 25000.0,
|
||||
'SGD' => 1.35,
|
||||
'PHP' => 56.5,
|
||||
'IDR' => 16000.0,
|
||||
'AUD' => 1.54,
|
||||
'CAD' => 1.36,
|
||||
'CHF' => 0.90,
|
||||
'BRL' => 5.10,
|
||||
'RUB' => 93.0,
|
||||
'INR' => 83.4,
|
||||
'TRY' => 32.8,
|
||||
'AED' => 3.67,
|
||||
];
|
||||
|
||||
|
||||
63
recharge.php
63
recharge.php
@ -422,12 +422,31 @@ function notify(icon, title, text = '') {
|
||||
});
|
||||
}
|
||||
|
||||
function updateRate() {
|
||||
let exchangeRates = {};
|
||||
|
||||
async function updateRate() {
|
||||
const select = document.getElementById('fiatCurrency');
|
||||
const symbol = select.value;
|
||||
const rate = select.options[select.selectedIndex].getAttribute('data-rate');
|
||||
|
||||
// Try to get fresh rates
|
||||
try {
|
||||
const resp = await fetch('api/exchange.php');
|
||||
const data = await resp.json();
|
||||
if (data.success && data.rates) {
|
||||
exchangeRates = data.rates;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch real-time rates:', e);
|
||||
}
|
||||
|
||||
const rate = exchangeRates[symbol] || parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
|
||||
|
||||
document.getElementById('selectedCurrencyLabel').innerText = symbol;
|
||||
document.getElementById('currentRateText').innerText = `${rate} ${symbol}`;
|
||||
document.getElementById('currentRateText').innerText = `${rate.toFixed(4)} ${symbol}`;
|
||||
|
||||
// Update the data-rate attribute so calculateUSDT can use it too
|
||||
select.options[select.selectedIndex].setAttribute('data-rate', rate);
|
||||
|
||||
calculateUSDT();
|
||||
}
|
||||
|
||||
@ -479,9 +498,9 @@ function finishTransfer() {
|
||||
formData.append('order_id', orderId);
|
||||
fetch('api/finance.php', { method: 'POST', body: formData })
|
||||
.then(r => r.json())
|
||||
.then(data => { if (data.success) finishTransferUI(); });
|
||||
.then(data => { if (data.success) renderRechargeUI({status: 'finished'}); });
|
||||
} else {
|
||||
finishTransferUI();
|
||||
notify('warning', '订单信息丢失,请刷新页面');
|
||||
}
|
||||
}
|
||||
|
||||
@ -539,7 +558,7 @@ function startStatusPolling(order_id) {
|
||||
if (data.success) {
|
||||
console.log('Order status update:', data.status, data);
|
||||
renderRechargeUI(data);
|
||||
if (data.status === 'finished' || data.status === '3') clearInterval(window.statusPollingInterval);
|
||||
if (data.status === '3') clearInterval(window.statusPollingInterval);
|
||||
}
|
||||
} catch (e) { console.error('Status polling error:', e); }
|
||||
};
|
||||
@ -551,7 +570,7 @@ function renderRechargeUI(data) {
|
||||
const side = document.querySelector('.info-side');
|
||||
if (!side) return;
|
||||
const status = data.status;
|
||||
if (status === 'finished' || status === '3') { finishTransferUI(); return; }
|
||||
if (status === '3') { finishTransferUI(); return; }
|
||||
|
||||
if (status === 'pending' || status === '0') {
|
||||
side.innerHTML = `
|
||||
@ -654,6 +673,33 @@ function renderRechargeUI(data) {
|
||||
<div class="p-2 text-center mb-3"><p class="text-muted small fw-bold mb-0">请严格按照页面展示的收款账户信息进行转账。<br>请勿分笔转账或修改信息,转账完成后,请点击“完成转账”按钮。<br>转账完成后请提交转账凭证给在线客服,方便第一时间为你确认到账。</p></div>
|
||||
<button type="button" class="btn btn-primary w-100 rounded-pill py-3 fw-bold shadow-sm" onclick="finishTransfer()" style="background: #ff4d94 !important; border: none; color: white;">【完成转账】</button>
|
||||
</div>`;
|
||||
} else if (status === 'finished') {
|
||||
side.innerHTML = `
|
||||
<div class="text-center text-lg-start position-relative" style="z-index: 2;">
|
||||
<div class="mb-4 text-center">
|
||||
<div class="d-inline-flex align-items-center gap-2 px-3 py-2 rounded-pill bg-warning bg-opacity-10 text-warning small fw-bold mb-3 border border-warning border-opacity-10">
|
||||
<span class="pulse-dot-pink"></span> <span style="letter-spacing: 1px;">等待审核中</span>
|
||||
</div>
|
||||
<h2 class="fw-bold text-dark mb-3" style="font-size: 2rem;">已提交,等待审核</h2>
|
||||
<p class="text-muted fw-medium mb-0" style="font-size: 15px;">您的充值申请已成功提交,正在等待管理员核对资金。<br>审核通过后,资金将自动存入您的账户。<br>请耐心等待,通常需要 1-5 分钟。</p>
|
||||
</div>
|
||||
<div class="mb-4 py-4 px-4 rounded-4 border border-light shadow-sm" style="background: #fff0f5;">
|
||||
<div class="row align-items-center text-center">
|
||||
<div class="col-12 mb-3">
|
||||
<div class="text-muted small mb-1 fw-bold">预计审核剩余时间</div>
|
||||
<div class="display-6 fw-bold text-primary mb-0" style="font-family: monospace; color: #ff4d94 !important;">正在核对资金...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 rounded-4 bg-light border border-light">
|
||||
<h6 class="text-dark fw-bold mb-3 d-flex align-items-center gap-2"><i class="bi bi-shield-lock-fill text-primary" style="color: #ff4d94 !important;"></i> 温馨提示</h6>
|
||||
<div class="text-muted small lh-lg">
|
||||
<div class="mb-2 d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span>转账完成后,请务必保留转账截图凭证。</span></div>
|
||||
<div class="mb-2 d-flex gap-2"><i class="bi bi-check2-circle text-primary" style="color: #ff4d94 !important;"></i> <span>如有任何疑问,请联系在线客服咨询。</span></div>
|
||||
</div>
|
||||
<div class="mt-4"><button type="button" class="btn btn-secondary w-100 rounded-pill py-2 fw-bold" onclick="location.reload()">确定并返回</button></div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
// Sync countdown if exists
|
||||
@ -770,7 +816,8 @@ function confirmFiatOrder(btn, event) {
|
||||
btn.disabled = false; btn.innerHTML = originalText;
|
||||
if (data.success) {
|
||||
let msg = `<?= __("recharge_msg_fiat") ?>`;
|
||||
msg = msg.replace('%uid%', userId).replace('%amount%', amount).replace('%currency%', currency);
|
||||
const resAmount = (amount / rate).toFixed(2);
|
||||
msg = msg.replace('%uid%', userId).replace('%amount%', amount).replace('%currency%', currency).replace('%res%', resAmount);
|
||||
openRechargeModal(msg, false, data.id);
|
||||
document.getElementById('fiatAmount').value = '';
|
||||
}
|
||||
|
||||
25
withdraw.php
25
withdraw.php
@ -240,11 +240,30 @@ function calculateCryptoWithdraw() {
|
||||
document.getElementById('cryptoReceiveAmount').innerText = receive.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' USDT';
|
||||
}
|
||||
|
||||
function updateFiatWithdrawRate() {
|
||||
let exchangeRates = {};
|
||||
|
||||
async function updateFiatWithdrawRate() {
|
||||
const select = document.getElementById('fiatWithdrawCurrency');
|
||||
const symbol = select.value;
|
||||
const rate = select.options[select.selectedIndex].getAttribute('data-rate');
|
||||
document.getElementById('fiatWithdrawRateText').innerText = `${rate} ${symbol}`;
|
||||
|
||||
// Try to get fresh rates
|
||||
try {
|
||||
const resp = await fetch('api/exchange.php');
|
||||
const data = await resp.json();
|
||||
if (data.success && data.rates) {
|
||||
exchangeRates = data.rates;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to fetch real-time rates:', e);
|
||||
}
|
||||
|
||||
const rate = exchangeRates[symbol] || parseFloat(select.options[select.selectedIndex].getAttribute('data-rate'));
|
||||
|
||||
document.getElementById('fiatWithdrawRateText').innerText = `${rate.toFixed(4)} ${symbol}`;
|
||||
|
||||
// Update the data-rate attribute so calculateFiatWithdraw can use it too
|
||||
select.options[select.selectedIndex].setAttribute('data-rate', rate);
|
||||
|
||||
calculateFiatWithdraw();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user