PHP WebShell

Текущая директория: /var/www/bitcardoApp/user/crypto

Просмотр файла: swap_crypto.php

<?php
/**
 * user/crypto/swap_crypto_new.php
 *
 */

require_once "../../config/db_config.php";
if (!isset($_SESSION)) { session_start(); }
if (!isset($_SESSION['user_id'])) { header("Location: ../../auth/login.php"); exit(); }
$user_id = $_SESSION['user_id'];

/* ----------------- helpers ----------------- */

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 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();
  $exists = $res && $res->num_rows > 0;
  $stmt->close();
  return $exists;
}

/** coin_rates: USDT per COIN (crypto); NGN row may be absent */
function get_db_rates(mysqli $conn, string $coin): array {
  $sql = "SELECT buy_rate, sell_rate, COALESCE(use_online_rate,0) AS use_online_rate
          FROM coin_rates WHERE UPPER(coin)=?";
  $stmt = $conn->prepare($sql);
  $c = strtoupper($coin);
  $stmt->bind_param("s", $c);
  $stmt->execute();
  $row = $stmt->get_result()->fetch_assoc();
  $stmt->close();
  return [
    'buy'  => (float)($row['buy_rate'] ?? 0.0),   // USDT/COIN  (platform buys coin)
    'sell' => (float)($row['sell_rate'] ?? 0.0),  // USDT/COIN  (platform sells coin)
    'use_online_rate' => (int)($row['use_online_rate'] ?? 0)
  ];
}

/** online_coin_rates:
 *  - Crypto: rate = USDT per COIN → buy/sell around that
 *  - NGN   : rate = NGN per USDT   → buy/sell around that
 */
function get_online_rates(mysqli $conn, string $coin): array {
  $sql = "SELECT buy_rate, sell_rate FROM online_coin_rates WHERE UPPER(coin)=?";
  $stmt = $conn->prepare($sql);
  $c = strtoupper($coin);
  $stmt->bind_param("s", $c);
  $stmt->execute();
  $row = $stmt->get_result()->fetch_assoc();
  $stmt->close();
  return [
    'buy'  => (float)($row['buy_rate'] ?? 0.0),
    'sell' => (float)($row['sell_rate'] ?? 0.0)
  ];
}

/**
 * Decide effective rates for a coin:
 * - If enforce_swap_profit=1:
 *     BUY  (coin→USDT) : min(db.buy, online.buy)
 *     SELL (USDT→coin) : max(db.sell, online.sell)
 * - Else:
 *     If coin_rates.use_online_rate=1 and online present → online; else → db.
 *
 * NOTE: We DO NOT use this for NGN routes in this file per your instruction.
 */
function effective_rates(mysqli $conn, string $coin, bool $enforce_profit): array {
  $coin = strtoupper($coin);
  $db = get_db_rates($conn, $coin);
  $ol = get_online_rates($conn, $coin);

  if ($enforce_profit) {
    $candidates_buy  = [];
    $candidates_sell = [];
    if ($db['buy']  > 0) $candidates_buy[]  = $db['buy'];
    if ($ol['buy']  > 0) $candidates_buy[]  = $ol['buy'];
    if ($db['sell'] > 0) $candidates_sell[] = $db['sell'];
    if ($ol['sell'] > 0) $candidates_sell[] = $ol['sell'];
    return [
      'buy'  => $candidates_buy  ? min($candidates_buy)   : 0.0,
      'sell' => $candidates_sell ? max($candidates_sell)  : 0.0,
    ];
  }

  $useOnline = ($db['use_online_rate'] === 1) && ($ol['buy'] > 0 || $ol['sell'] > 0);
  return $useOnline ? ['buy'=>$ol['buy'], 'sell'=>$ol['sell']]
                    : ['buy'=>$db['buy'], 'sell'=>$db['sell']];
}

/** Convert an amount FROM coin to USDT for min-swap comparison (NGN special) */
function amount_to_usdt(mysqli $conn, string $from, float $amount, bool $enforce_profit): float {
  $from = strtoupper($from);
  if ($from === 'USDT') return $amount;

  if ($from === 'NGN') {
    // For min-swap only (kept), but quote logic below uses old-file math directly
    $stmt = $conn->prepare("SELECT buy_rate FROM coin_rates WHERE UPPER(coin)='NGN'");
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    $buy = (float)($row['buy_rate'] ?? 0.0); // NGN/USDT
    return $buy > 0 ? ($amount / $buy) : 0.0;
  }

  // Crypto: USDT per COIN (use BUY side)
  $r = effective_rates($conn, $from, $enforce_profit);
  return $amount * (float)$r['buy'];
}

/* ----------------- AJAX QUOTE ----------------- */
if (isset($_GET['ajax']) && $_GET['ajax'] === 'quote') {
  header('Content-Type: application/json');

  $from   = strtoupper($_GET['from'] ?? '');
  $to     = strtoupper($_GET['to'] ?? '');
  $amount = (float)($_GET['amount'] ?? 0);

  if (!$from || !$to || $amount <= 0) {
    echo json_encode(['error' => 'Invalid input']); exit;
  }

  $enforce_profit = get_setting($conn, 'enforce_swap_profit', '1') === '1';
  $enforce_min    = get_setting($conn, 'enforce_min_swap', '1') === '1';

  // Enforce minimum from coin_rates.min_swap only
  if ($enforce_min) {
    $stmt = $conn->prepare("SELECT COALESCE(min_swap,0) AS m FROM coin_rates WHERE UPPER(coin)=?");
    $stmt->bind_param("s", $from);
    $stmt->execute();
    $r = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    $min_cfg = (float)($r['m'] ?? 0);

    if ($min_cfg > 0) {
      if ($from === 'NGN') {
        if ($amount < $min_cfg) { echo json_encode(['error' => "Min ₦" . number_format($min_cfg, 0)]); exit; }
      } else {
        $amount_usdt = amount_to_usdt($conn, $from, $amount, $enforce_profit);
        if ($amount_usdt < $min_cfg) { echo json_encode(['error' => "Min $" . number_format($min_cfg, 2)]); exit; }
      }
    }
  }

  // Build quote
  $rate = 0.0;
  $to_amount = 0.0;

  // ======== NGN ROUTES (USING OLD-FILE LOGIC) ========
  if (($from === 'USDT' && $to === 'NGN') || ($from === 'NGN' && $to === 'USDT')) {
    // Direct NGN ↔ USDT
    if ($from === 'USDT') {
      // USDT → NGN : multiply by NGN.sell_rate
      $stmt = $conn->prepare("SELECT sell_rate FROM coin_rates WHERE UPPER(coin) = 'NGN'");
      $stmt->execute();
      $row = $stmt->get_result()->fetch_assoc();
      $stmt->close();
      $rate = (float)($row['sell_rate'] ?? 0);
      $to_amount = $rate > 0 ? $amount * $rate : 0.0;
    } else {
      // NGN → USDT : divide by NGN.buy_rate
      $stmt = $conn->prepare("SELECT buy_rate FROM coin_rates WHERE UPPER(coin) = 'NGN'");
      $stmt->execute();
      $row = $stmt->get_result()->fetch_assoc();
      $stmt->close();
      $rate = (float)($row['buy_rate'] ?? 0);
      $to_amount = $rate > 0 ? $amount / $rate : 0.0;
    }

  } elseif ($from === 'NGN' && in_array($to, ['BTC','SOL','ETH'])) {
    // NGN → COIN : (NGN / NGN.buy_rate) -> USDT, then (USDT / COIN.sell_rate) -> COIN
    // 1) NGN.buy_rate
    $stmt = $conn->prepare("SELECT buy_rate FROM coin_rates WHERE UPPER(coin) = 'NGN'");
    $stmt->execute();
    $ngnRow = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    $ngn_buy = (float)($ngnRow['buy_rate'] ?? 0.0); // NGN per 1 USDT
    $usdt = $ngn_buy > 0 ? ($amount / $ngn_buy) : 0.0;

    // 2) COIN.sell_rate (USDT per 1 COIN)
    $stmt = $conn->prepare("SELECT sell_rate FROM coin_rates WHERE UPPER(coin) = ?");
    $stmt->bind_param("s", $to);
    $stmt->execute();
    $coinRow = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    $coin_sell = (float)($coinRow['sell_rate'] ?? 0.0);

    $to_amount = ($coin_sell > 0) ? ($usdt / $coin_sell) : 0.0;

    // Composite rate (optional): COIN per 1 NGN
    $rate = ($ngn_buy > 0 && $coin_sell > 0) ? (1.0 / $ngn_buy) / $coin_sell : 0.0;

  } elseif (in_array($from, ['BTC','SOL','ETH']) && $to === 'NGN') {
    // COIN → NGN : (COIN * COIN.buy_rate) -> USDT, then (USDT * NGN.sell_rate) -> NGN
    // 1) COIN.buy_rate (USDT per 1 COIN)
    $stmt = $conn->prepare("SELECT buy_rate FROM coin_rates WHERE UPPER(coin) = ?");
    $stmt->bind_param("s", $from);
    $stmt->execute();
    $coinRow = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    $coin_buy = (float)($coinRow['buy_rate'] ?? 0.0);
    $usdt = $amount * $coin_buy;

    // 2) NGN.sell_rate (NGN per 1 USDT)
    $stmt = $conn->prepare("SELECT sell_rate FROM coin_rates WHERE UPPER(coin) = 'NGN'");
    $stmt->execute();
    $ngnRow = $stmt->get_result()->fetch_assoc();
    $stmt->close();
    $ngn_sell = (float)($ngnRow['sell_rate'] ?? 0.0);

    $to_amount = $usdt * $ngn_sell;

    // Composite rate (optional): NGN per 1 COIN
    $rate = $coin_buy * $ngn_sell;

  // ======== OTHER (UNCHANGED) ROUTES ========
  } elseif ($to === 'USDT') {
    // Coin → USDT: multiply by BUY (USDT/coin)
    $r = effective_rates($conn, $from, $enforce_profit);
    $rate = (float)$r['buy'];
    $to_amount = $amount * $rate;

  } elseif ($from === 'USDT') {
    // USDT → Coin: divide by SELL (USDT/coin)
    $r = effective_rates($conn, $to, $enforce_profit);
    $rate = (float)$r['sell'];
    $to_amount = $rate > 0 ? $amount / $rate : 0.0;

  } else {
    // Crypto → Crypto: (amount * FROM.buy USDT) / TO.sell
    $rf = effective_rates($conn, $from, $enforce_profit);
    $rt = effective_rates($conn, $to,   $enforce_profit);
    $buy  = (float)$rf['buy'];   // USDT / FROM
    $sell = (float)$rt['sell'];  // USDT / TO
    $tusdt = $amount * $buy;
    $to_amount = $sell > 0 ? $tusdt / $sell : 0.0;
    $rate = ($buy > 0 && $sell > 0) ? ($buy / $sell) : 0.0;
  }

  echo json_encode([
    'rate'      => $rate,
    'to_amount' => round($to_amount, 8)
  ]);
  exit;
}

/* ----------------- Build wallet picklists ----------------- */

/** Active filter (wallet_status can be 'Active' or 'active') */
$hasWalletStatus = table_has_column($conn, 'user_wallets', 'wallet_status');
$activeClause = $hasWalletStatus
  ? " AND (LOWER(uw.wallet_status) = 'active' OR uw.wallet_status IS NULL) "
  : "";

/** "You send" → active + balance > 0 */
$sendCoins = [];
$balances  = [];
$sqlSend = "SELECT uw.coin, uw.balance
            FROM user_wallets uw
            WHERE uw.user_id = ? $activeClause AND uw.balance > 0";
$stmt = $conn->prepare($sqlSend);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$rs = $stmt->get_result();
while ($row = $rs->fetch_assoc()) {
  $c = strtoupper($row['coin']);
  $sendCoins[]   = $c;
  $balances[$c]  = (float)$row['balance'];
}
$stmt->close();

/** "You get" → active (balance can be 0) */
$activeToCoins = [];
$sqlTo = "SELECT DISTINCT UPPER(uw.coin) AS coin
          FROM user_wallets uw
          WHERE uw.user_id = ? $activeClause";
$stmt = $conn->prepare($sqlTo);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$rt = $stmt->get_result();
while ($row = $rt->fetch_assoc()) {
  $activeToCoins[] = $row['coin'];
}
$stmt->close();

/** Allowed directions from swap_directions */
$directions = [];
$dirResult = $conn->query("SELECT from_coin, to_coin FROM swap_directions WHERE is_active = 1");
while ($row = $dirResult->fetch_assoc()) {
  $from = strtoupper($row['from_coin']);
  $to   = strtoupper($row['to_coin']);
  if (!isset($directions[$from])) $directions[$from] = [];
  $directions[$from][] = $to;
}
?>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

<div class="containerx py-2">
  <h4 class="mb-3">Asset Swap</h4>

  <form action="../../models/crypto/swap.php" method="POST">
    <div class="mb-3">
      <label class="form-label ms-2 d-flex justify-content-between align-items-center">
        <span>You send</span>
        <span id="balanceMsg" class="text-danger small d-none"></span>
      </label>

      <div class="input-group">
        <input type="number" name="fromAmount" id="amountFrom" class="form-control" placeholder="100.00" step="0.000001" required>
        <button class="btn btn-white-outline btn-sm pyx-0 border" type="button" id="useMaxBtn">Max</button>
        <select id="currencyFrom" name="currencyFrom" class="form-select" onchange="updateCurrencyTo()" style="max-width: 120px;" required>
          <?php foreach ($sendCoins as $index => $coin): ?>
            <option value="<?= $coin ?>" <?= $index === 0 ? 'selected' : '' ?>><?= $coin ?></option>
          <?php endforeach; ?>
        </select>
      </div>
      <!-- Removed the "Min ₦..." line per request -->
    </div>

    <div class="mb-3 mt-3">
      <label class="form-label ms-2">You Get</label>
      <div class="input-group">
        <input type="number" id="amountTo" name="toAmount" class="form-control" placeholder="0.00" readonly>
        <select id="currencyTo" name="currencyTo" class="form-select" style="max-width: 120px;" required>
          <!-- Populated via JS -->
        </select>
      </div>
    </div>

    <div class="d-grid mt-4">
      <button class="btn btn-primary" id="convertBtn">Convert</button>
    </div>
  </form>

  <script>
    const balances       = <?= json_encode($balances) ?>;     // coin -> balance
    const activeToCoins  = new Set(<?= json_encode($activeToCoins) ?>);
    const allowedDirs    = <?= json_encode($directions) ?>;

    function updateCurrencyTo() {
      const from  = document.getElementById('currencyFrom').value;
      const toSel = document.getElementById('currencyTo');
      toSel.innerHTML = '';

      // Only show directions that land on user's active wallets
      const allowed = (allowedDirs[from] || []).filter(c => activeToCoins.has(c));
      if (!allowed.length) {
        const o = document.createElement('option');
        o.value = '';
        o.text = 'Not available';
        o.disabled = true;
        o.selected = true;
        toSel.appendChild(o);
      } else {
        allowed.forEach(c => {
          const o = document.createElement('option');
          o.value = c;
          o.text  = c;
          toSel.appendChild(o);
        });
        toSel.value = allowed[0];
      }

      getSwapQuote();
    }

    function getSwapQuote() {
      const from   = document.getElementById('currencyFrom').value;
      const to     = document.getElementById('currencyTo').value;
      const amount = parseFloat(document.getElementById('amountFrom').value);
      const bal    = parseFloat(balances[from] || 0);
      const btn    = document.getElementById('convertBtn');
      const msg    = document.getElementById('balanceMsg');

      if (!from || !to || isNaN(amount) || amount <= 0) {
        document.getElementById('amountTo').value = '';
        btn.disabled = true;
        msg.classList.add('d-none');
        return;
      }

      if (amount > bal) {
        msg.textContent = 'Insufficient balance';
        msg.classList.remove('d-none');
        btn.disabled = true;
        document.getElementById('amountTo').value = '';
        return;
      } else {
        msg.classList.add('d-none');
        btn.disabled = false;
      }

      fetch(`../crypto/swap_crypto.php?ajax=quote&from=${from}&to=${to}&amount=${amount}`)
        .then(res => res.json())
        .then(data => {
          if (data.error) {
            btn.disabled = true;
            document.getElementById('amountTo').value = '';
            msg.textContent = data.error;
            msg.classList.remove('d-none');
            return;
          }
          document.getElementById('amountTo').value = data.to_amount ?? '0.00';
        })
        .catch(err => console.error('Fetch error:', err));
    }

    document.getElementById('amountFrom').addEventListener('input', getSwapQuote);
    document.getElementById('currencyFrom').addEventListener('change', updateCurrencyTo);
    document.getElementById('currencyTo').addEventListener('change', getSwapQuote);
    document.getElementById('useMaxBtn').addEventListener('click', () => {
      const from = document.getElementById('currencyFrom').value;
      const bal  = parseFloat(balances[from] || 0);
      document.getElementById('amountFrom').value = bal.toFixed(6);
      getSwapQuote();
    });
    document.addEventListener('DOMContentLoaded', () => setTimeout(updateCurrencyTo, 0));
  </script>
</div>

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


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