PHP WebShell
Текущая директория: /var/www/bitcardoApp/models/crypto
Просмотр файла: swap.php
<?php
session_start();
require_once "../../config/db_config.php";
if (!isset($_SESSION['user_id'])) { header("Location: login.php"); exit(); }
$user_id = (int)$_SESSION['user_id'];
$from = strtoupper(trim($_POST['currencyFrom'] ?? ''));
$to = strtoupper(trim($_POST['currencyTo'] ?? ''));
$input = (float)($_POST['fromAmount'] ?? 0);
if (!$from || !$to || $input <= 0 || $from === $to) exit("Invalid swap input.");
if (!empty($_SESSION['swap_lock'])) exit("Please wait, a swap is already in progress.");
$_SESSION['swap_lock'] = true;
function table_has_column(mysqli $conn, string $table, string $column): bool {
$sql = "SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = ?
AND COLUMN_NAME = ?
LIMIT 1";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ss", $table, $column);
$stmt->execute();
$res = $stmt->get_result();
$ok = $res && $res->num_rows > 0;
$stmt->close();
return $ok;
}
function get_setting(mysqli $conn, string $key, $default = null) {
$sql = "SELECT setting_value FROM site_settings WHERE setting_key=? LIMIT 1";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $key);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();
return $row['setting_value'] ?? $default;
}
function variants(string $coin): array {
$c = strtoupper(trim($coin));
$set = [$c => true];
if ($c === 'USDT') $set['USDT-TRC20'] = true;
if ($c === 'USDT-TRC20') $set['USDT'] = true;
if ($c === 'TRON') $set['TRX'] = true;
if ($c === 'TRX') $set['TRON'] = true;
return array_keys($set);
}
function norm_coin(string $coin): string {
$coin = strtoupper(trim($coin));
$map = [
'USDT-TRC20' => 'USDT',
'TRON' => 'TRX',
];
return $map[$coin] ?? $coin;
}
function usd_price(mysqli $conn, string $coin): float {
$coin = norm_coin($coin);
$stmt = $conn->prepare("SELECT rate FROM online_coin_rates WHERE UPPER(coin)=? LIMIT 1");
$stmt->bind_param("s", $coin);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();
$usd = (float)($row['rate'] ?? 0.0);
if ($usd > 0) return $usd;
if (table_has_column($conn,'coin_rates','usd_price')) {
$stmt = $conn->prepare("SELECT usd_price FROM coin_rates WHERE UPPER(coin)=? LIMIT 1");
$stmt->bind_param("s", $coin);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();
return (float)($row['usd_price'] ?? 0.0);
}
return 0.0;
}
function ngn_per_usd(mysqli $conn, string $coin, string $side): float {
$col = ($side === 'buy') ? 'buy_rate' : 'sell_rate';
$coin = norm_coin($coin);
$stmt = $conn->prepare("SELECT {$col} FROM coin_rates WHERE UPPER(coin)=? LIMIT 1");
$stmt->bind_param("s", $coin);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();
return (float)($row[$col] ?? 0.0);
}
function min_swap(mysqli $conn, string $coin): float {
$coin = norm_coin($coin);
$stmt = $conn->prepare("SELECT COALESCE(min_swap,0) AS m FROM coin_rates WHERE UPPER(coin)=? LIMIT 1");
$stmt->bind_param("s", $coin);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();
return (float)($row['m'] ?? 0.0);
}
function direction_allowed(mysqli $conn, string $from, string $to): bool {
$fromVars = variants($from);
$toVars = variants($to);
$fromPlace = implode(',', array_fill(0, count($fromVars), '?'));
$toPlace = implode(',', array_fill(0, count($toVars), '?'));
$sql = "SELECT 1 FROM swap_directions
WHERE UPPER(from_coin) IN ($fromPlace)
AND UPPER(to_coin) IN ($toPlace)
AND is_active=1
LIMIT 1";
$stmt = $conn->prepare($sql);
$types = str_repeat('s', count($fromVars) + count($toVars));
$params = array_merge($fromVars, $toVars);
$bind = [];
$bind[] = $types;
foreach ($params as $k => $v) { $bind[] = &$params[$k]; }
call_user_func_array([$stmt, 'bind_param'], $bind);
$stmt->execute();
$ok = $stmt->get_result()->num_rows > 0;
$stmt->close();
return $ok;
}
function load_wallet(mysqli $conn, int $user_id, string $coin) {
foreach (variants($coin) as $try) {
$stmt = $conn->prepare("SELECT wallet_id, wallet_add, balance FROM user_wallets WHERE user_id=? AND UPPER(coin)=? LIMIT 1");
$stmt->bind_param("is", $user_id, $try);
$stmt->execute();
$row = $stmt->get_result()->fetch_assoc();
$stmt->close();
if ($row) return $row;
}
return null;
}
function compute_swap(mysqli $conn, string $from, string $to, float $input, float &$rate_used, float &$debit_amount): float {
$from = strtoupper($from);
$to = strtoupper($to);
$rate_used = 0.0;
$to_is_usdt = in_array($to, ['USDT','USDT-TRC20'], true);
// Factor for COIN->USDT profitability
$factor = (float) get_setting($conn, 'swap_coin_to_usdt_factor', '0.8000');
if ($factor < 0.10) $factor = 0.10;
if ($factor > 1.00) $factor = 1.00;
// NGN -> COIN: input NGN
if ($from === 'NGN' && $to !== 'NGN') {
$buy = ngn_per_usd($conn, $to, 'buy');
if ($buy <= 0) return 0.0;
$usd = $input / $buy;
if ($to_is_usdt) {
// NGN->USDT: no factor here unless you explicitly want it (currently keep fair)
$debit_amount = $input;
$rate_used = 1.0 / $buy;
return $usd; // ~USDT
}
$p = usd_price($conn, $to);
if ($p <= 0) return 0.0;
$to_coin = $usd / $p;
$debit_amount = $input;
$rate_used = $to_coin / $input;
return $to_coin;
}
// COIN -> NGN: input USD, debit in coin units
if ($from !== 'NGN' && $to === 'NGN') {
$p_from = usd_price($conn, $from);
if ($p_from <= 0) return 0.0;
$coin_debit = $input / $p_from;
$sell = ngn_per_usd($conn, $from, 'sell');
if ($sell <= 0) return 0.0;
$ngn = $input * $sell;
$debit_amount = $coin_debit;
$rate_used = $ngn / $input;
return $ngn;
}
// COIN -> USDT (PROFITABLE): input USD, credit USDT = USD * factor, debit coin = USD / usd_price(from)
if ($from !== 'NGN' && $to_is_usdt) {
$p_from = usd_price($conn, $from);
if ($p_from <= 0) return 0.0;
$coin_debit = $input / $p_from;
$usdt_credit = $input * $factor;
$debit_amount = $coin_debit;
$rate_used = $factor; // USD->USDT payout factor
return $usdt_credit;
}
// COIN -> COIN: input USD, debit from coin, credit to coin
if ($from !== 'NGN' && $to !== 'NGN') {
$p_from = usd_price($conn, $from);
$p_to = usd_price($conn, $to);
if ($p_from <= 0 || $p_to <= 0) return 0.0;
$coin_debit = $input / $p_from;
$to_coin = $input / $p_to;
$debit_amount = $coin_debit;
$rate_used = $to_coin / $input;
return $to_coin;
}
return 0.0;
}
/* direction */
if (!direction_allowed($conn, $from, $to)) { $_SESSION['swap_lock'] = false; exit("Swap direction not allowed."); }
/* min swap */
$min_cfg = min_swap($conn, $from);
if ($min_cfg > 0) {
if ($from === 'NGN') {
if ($input < $min_cfg) { $_SESSION['swap_lock'] = false; exit("Minimum swap for NGN is ₦".number_format($min_cfg,0)); }
} else {
if ($input < $min_cfg) { $_SESSION['swap_lock'] = false; exit("Minimum swap for $from is $".number_format($min_cfg,2)); }
}
}
/* wallets */
$fromWallet = load_wallet($conn, $user_id, $from);
if (!$fromWallet) { $_SESSION['swap_lock'] = false; exit("Source wallet not found."); }
$toWallet = load_wallet($conn, $user_id, $to);
if (!$toWallet) { $_SESSION['swap_lock'] = false; exit("Destination wallet not found."); }
$from_wallet_id = (int)$fromWallet['wallet_id'];
$from_address = $fromWallet['wallet_add'];
$to_wallet_id = (int)$toWallet['wallet_id'];
$to_address = $toWallet['wallet_add'];
$rate_used = 0.0;
$debit_amount = 0.0;
$credit_amount = compute_swap($conn, $from, $to, $input, $rate_used, $debit_amount);
if ($credit_amount <= 0 || $debit_amount <= 0) {
$_SESSION['swap_lock'] = false;
exit("Swap failed: Rate unavailable. Ensure cron is updating online prices.");
}
if ((float)$fromWallet['balance'] < $debit_amount) {
$_SESSION['swap_lock'] = false;
exit("Insufficient balance.");
}
$swap_id = uniqid("", true);
$conn->begin_transaction();
try {
$stmt = $conn->prepare("UPDATE user_wallets SET balance = balance - ? WHERE wallet_id = ? AND balance >= ?");
$stmt->bind_param("did", $debit_amount, $from_wallet_id, $debit_amount);
$stmt->execute();
if ($stmt->affected_rows !== 1) { $stmt->close(); throw new Exception("Debit failed."); }
$stmt->close();
$stmt = $conn->prepare("UPDATE user_wallets SET balance = balance + ? WHERE wallet_id = ?");
$stmt->bind_param("di", $credit_amount, $to_wallet_id);
$stmt->execute();
$stmt->close();
$stmt = $conn->prepare("INSERT INTO swap_transactions
(swap_id, user_id, from_coin, to_coin, from_amount, to_amount, rate_used, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, NOW())");
$stmt->bind_param("sissddd", $swap_id, $user_id, $from, $to, $input, $credit_amount, $rate_used);
$stmt->execute();
$stmt->close();
$conf_col = table_has_column($conn, 'transactions', 'confirmations') ? 'confirmations' : 'confirmation';
$txid1 = uniqid("txid_", true);
$sql1 = "INSERT INTO transactions
(coin, wallet_id, sender_address, receiver_address, amount, type, txid, $conf_col, status, applied, swap_id, note, created_at)
VALUES (?, ?, ?, ?, ?, 'send', ?, 0, 'completed', 1, ?, 'Swap out', NOW())";
$stmt = $conn->prepare($sql1);
$stmt->bind_param("sissdss", $from, $from_wallet_id, $from_address, $to_address, $debit_amount, $txid1, $swap_id);
$stmt->execute();
$stmt->close();
$txid2 = uniqid("txid_", true);
$sql2 = "INSERT INTO transactions
(coin, wallet_id, sender_address, receiver_address, amount, type, txid, $conf_col, status, applied, swap_id, note, created_at)
VALUES (?, ?, ?, ?, ?, 'receive', ?, 0, 'completed', 1, ?, 'Swap in', NOW())";
$stmt = $conn->prepare($sql2);
$stmt->bind_param("sissdss", $to, $to_wallet_id, $from_address, $to_address, $credit_amount, $txid2, $swap_id);
$stmt->execute();
$stmt->close();
$conn->commit();
$_SESSION['swap_lock'] = false;
$_SESSION['swap_success'] = [
'amount' => $input,
'from' => $from,
'to_amount' => $credit_amount,
'to' => $to,
'rate' => $rate_used,
'swap_id' => $swap_id,
'from_address' => $from_address,
'to_address' => $to_address,
'timestamp' => date('M j, Y • h:i A'),
];
header("Location: ../../user/wallets/swap_success.php");
exit;
} catch (Exception $e) {
$conn->rollback();
$_SESSION['swap_lock'] = false;
exit("Swap failed: " . $e->getMessage());
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!