38451-vm/api/swap.php
2026-02-20 05:37:33 +00:00

127 lines
4.2 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" => "Failed to fetch exchange rate (Provider issue)"]);
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) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$user_id, $from_coin, $to_coin, $amount, $target_amount, $rate]);
// Record transaction
$db->prepare("INSERT INTO transactions (user_id, type, amount, symbol, status) VALUES (?, 'swap', ?, ?, 'completed')")
->execute([$user_id, $amount, $from_coin]);
$db->commit();
echo json_encode(["success" => true, "target_amount" => $target_amount]);
} catch (Exception $e) {
$db->rollBack();
echo json_encode(["success" => false, "error" => $e->getMessage()]);
}