127 lines
4.3 KiB
PHP
127 lines
4.3 KiB
PHP
<?php
|
|
require_once __DIR__ . "/../db/config.php";
|
|
require_once __DIR__ . "/../includes/lang.php";
|
|
session_start();
|
|
|
|
header("Content-Type: application/json");
|
|
|
|
$db = db();
|
|
$user_id = $_SESSION["user_id"] ?? null;
|
|
|
|
if (!$user_id) {
|
|
echo json_encode(["success" => 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()]);
|
|
}
|