false, "error" => __("unauthorized")]); exit; } $from_coin = $_POST["from_coin"] ?? ""; $to_coin = $_POST["to_coin"] ?? ""; $amount = (float)($_POST["amount"] ?? 0); if ($amount <= 0 || !$from_coin || !$to_coin || $from_coin === $to_coin) { echo json_encode(["success" => false, "error" => __("invalid_amount")]); exit; } /** * Robust price fetching using OKX as primary and CoinCap as fallback */ function getPrice($symbol) { if ($symbol === "USDT") return 1.0; // Primary: OKX $pair = strtoupper($symbol) . "-USDT"; $url = "https://www.okx.com/api/v5/market/ticker?instId=" . $pair; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 5); curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0"); // OKX might require UA $response = curl_exec($ch); curl_close($ch); if ($response) { $data = json_decode($response, true); if (isset($data["data"][0]["last"])) { return (float)$data["data"][0]["last"]; } } // Fallback: CoinCap $slugMap = [ "BTC" => "bitcoin", "ETH" => "ethereum", "BNB" => "binance-coin", "SOL" => "solana", "XRP" => "ripple", "ADA" => "cardano", "DOGE" => "dogecoin", "DOT" => "polkadot", "MATIC" => "polygon", "AVAX" => "avalanche", "LINK" => "chainlink", "SHIB" => "shiba-inu", "TRX" => "tron", "BCH" => "bitcoin-cash", "LTC" => "litecoin", "UNI" => "uniswap" ]; $slug = $slugMap[strtoupper($symbol)] ?? strtolower($symbol); $url = "https://api.coincap.io/v2/assets/" . $slug; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $response = curl_exec($ch); curl_close($ch); if ($response) { $data = json_decode($response, true); if (isset($data["data"]["priceUsd"])) { return (float)$data["data"]["priceUsd"]; } } return null; } $from_price = getPrice($from_coin); $to_price = getPrice($to_coin); if ($from_price === null || $to_price === null) { echo json_encode(["success" => false, "error" => __("rate_fetch_failed")]); exit; } $rate = $from_price / $to_price; $target_amount = $amount * $rate; $db->beginTransaction(); try { // Check balance of from_coin $stmt = $db->prepare("SELECT available FROM user_balances WHERE user_id = ? AND symbol = ?"); $stmt->execute([$user_id, $from_coin]); $from_balance = $stmt->fetchColumn() ?: 0; if ($from_balance < $amount) { throw new Exception(__("insufficient_balance")); } // Deduct from_coin $db->prepare("UPDATE user_balances SET available = available - ? WHERE user_id = ? AND symbol = ?") ->execute([$amount, $user_id, $from_coin]); // Add to_coin $stmt = $db->prepare("SELECT id FROM user_balances WHERE user_id = ? AND symbol = ?"); $stmt->execute([$user_id, $to_coin]); if (!$stmt->fetch()) { $db->prepare("INSERT INTO user_balances (user_id, symbol, available) VALUES (?, ?, 0)") ->execute([$user_id, $to_coin]); } $db->prepare("UPDATE user_balances SET available = available + ? WHERE user_id = ? AND symbol = ?") ->execute([$target_amount, $user_id, $to_coin]); // Record exchange $stmt = $db->prepare("INSERT INTO exchange_records (user_id, from_symbol, to_symbol, from_amount, to_amount, rate, ip_address) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([$user_id, $from_coin, $to_coin, $amount, $target_amount, $rate, getRealIP()]); // Record transaction $db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status, ip_address) VALUES (?, 'swap', ?, ?, 'completed', ?)") ->execute([$user_id, $amount, $from_coin, getRealIP()]); $db->commit(); echo json_encode(["success" => true, "target_amount" => $target_amount]); } catch (Exception $e) { $db->rollBack(); echo json_encode(["success" => false, "error" => $e->getMessage()]); }