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());
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!