diff --git a/api/get_option_orders.php b/api/get_option_orders.php index e0a8d8c..28e42c7 100644 --- a/api/get_option_orders.php +++ b/api/get_option_orders.php @@ -1,70 +1,93 @@ false, 'error' => '未登录']); +if (!isset($_SESSION['user_id'])) { + echo json_encode([]); // Return empty array if not logged in exit; } -$status = $_GET['status'] ?? 'pending'; -$pdo = db(); +$user_id = $_SESSION['user_id']; +$status_filter = $_GET['status'] ?? 'pending'; -// Auto-settle orders that are due -$now = date('Y-m-d H:i:s'); -// Fetch orders that are pending and due, joined with user win_loss_control -$stmt = $pdo->prepare("SELECT o.*, u.win_loss_control as user_control FROM option_orders o JOIN users u ON o.user_id = u.id WHERE o.status = 'pending' AND o.settle_at <= ?"); -$stmt->execute([$now]); -$due_orders = $stmt->fetchAll(); - -foreach ($due_orders as $order) { - $result = 'loss'; - $profit = 0; - - // Win/Loss Control Logic: Order-level control overrides User-level control - $final_control = 'none'; - if ($order['control'] !== 'none') { - $final_control = $order['control']; - } elseif ($order['user_control'] !== 'none') { - $final_control = $order['user_control']; - } - - if ($final_control === 'win') { - $result = 'win'; - } elseif ($final_control === 'loss') { - $result = 'loss'; - } else { - // Default behavior if no control is set: 50/50 chance - $result = (rand(0, 100) > 50) ? 'win' : 'loss'; - } - - if ($result === 'win') { - $profit = $order['amount'] * $order['profit_rate']; - $total_return = $order['amount'] + $profit; - - // Add balance to user - $stmt_bal = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?"); - $stmt_bal->execute([$total_return, $order['user_id']]); - - // Set closing price slightly higher or lower than opening price to match result - $variation = (float)($order['opening_price'] * 0.0001 * rand(1, 10)); - $closing_price = ($order['direction'] === 'up') ? $order['opening_price'] + $variation : $order['opening_price'] - $variation; - } else { - $profit = -$order['amount']; - $variation = (float)($order['opening_price'] * 0.0001 * rand(1, 10)); - $closing_price = ($order['direction'] === 'up') ? $order['opening_price'] - $variation : $order['opening_price'] + $variation; - } - - $stmt_update = $pdo->prepare("UPDATE option_orders SET status = 'completed', result = ?, profit = ?, closing_price = ? WHERE id = ?"); - $stmt_update->execute([$result, $profit, $closing_price, $order['id']]); +if (!in_array($status_filter, ['pending', 'completed'])) { + $status_filter = 'pending'; } -// Fetch current orders for the user to return to frontend -$stmt = $pdo->prepare("SELECT * FROM option_orders WHERE user_id = ? AND status = ? ORDER BY created_at DESC"); -$stmt->execute([$user_id, $status]); -$orders = $stmt->fetchAll(); +$pdo = db(); -echo json_encode(['success' => true, 'data' => $orders]); \ No newline at end of file +// --- Settlement Logic for Due Orders --- +try { + $now = date('Y-m-d H:i:s'); + // Fetch orders that are pending and due for settlement, and join with users to get control settings. + $stmt = $pdo->prepare( + "SELECT o.*, u.win_loss_control FROM option_orders o " . + "JOIN users u ON o.user_id = u.id " . + "WHERE o.status = 'pending' AND o.settle_at <= ?" + ); + $stmt->execute([$now]); + $due_orders = $stmt->fetchAll(PDO::FETCH_ASSOC); + + if (count($due_orders) > 0) { + $pdo->beginTransaction(); + + foreach ($due_orders as $order) { + $result = 'loss'; // Default to loss + + // Determine final control setting (user's setting is the only one we consider as per request) + $final_control = $order['win_loss_control']; + + if ($final_control == 'win') { + $result = 'win'; + } elseif ($final_control == 'loss') { + $result = 'loss'; + } else { // No control set, default to random + $result = (rand(0, 100) < 51) ? 'loss' : 'win'; // 51% chance to lose to have a house edge + } + + $profit = 0; + // Calculate profit and update user balance + if ($result === 'win') { + $profit = $order['amount'] * $order['profit_rate']; + $total_return = $order['amount'] + $profit; + + // Credit the user's balance with the principal + profit + $bal_stmt = $pdo->prepare("UPDATE users SET balance = balance + ? WHERE id = ?"); + $bal_stmt->execute([$total_return, $order['user_id']]); + } else { + // Loss means the wagered amount is lost. No balance update needed as it was deducted at placement. + $profit = -$order['amount']; + } + + // Fake a closing price that realistically matches the outcome + $price_variation = (float)($order['open_price'] * 0.0001 * rand(1, 5)); + if (($order['direction'] === 'up' && $result === 'win') || ($order['direction'] === 'down' && $result === 'loss')) { + $closing_price = $order['open_price'] + $price_variation; + } else { + $closing_price = $order['open_price'] - $price_variation; + } + + // Update the order to settled status + $update_stmt = $pdo->prepare( + "UPDATE option_orders SET status = 'completed', result = ?, profit = ?, close_price = ? WHERE id = ?" + ); + $update_stmt->execute([$result, $profit, $closing_price, $order['id']]); + } + $pdo->commit(); + } +} catch (Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + error_log("Option settlement failed: " . $e->getMessage()); + // Don't exit, still try to fetch orders for the user +} + +// --- Fetch and Return Orders for Frontend --- +$stmt = $pdo->prepare("SELECT * FROM option_orders WHERE user_id = ? AND status = ? ORDER BY start_time DESC LIMIT 50"); +$stmt->execute([$user_id, $status_filter]); +$orders = $stmt->fetchAll(PDO::FETCH_ASSOC); + +echo json_encode($orders); +?> diff --git a/api/place_option_order.php b/api/place_option_order.php index 7efa3aa..c9f04c6 100644 --- a/api/place_option_order.php +++ b/api/place_option_order.php @@ -4,65 +4,117 @@ require_once '../db/config.php'; session_start(); if (!isset($_SESSION['user_id'])) { - echo json_encode(['success' => false, 'error' => 'Please login first']); + echo json_encode(['success' => false, 'error' => 'User not logged in']); exit; } $user_id = $_SESSION['user_id']; $data = json_decode(file_get_contents('php://input'), true); -$symbol = $data['symbol'] ?? ''; +// --- Input Validation --- +$pair = $data['pair'] ?? null; $amount = (float)($data['amount'] ?? 0); -$direction = $data['direction'] ?? ''; -$duration = (int)($data['duration'] ?? 60); -$opening_price = (float)($data['opening_price'] ?? 0); +$direction = $data['direction'] ?? null; +$duration = (int)($data['duration'] ?? 0); +$rate = (float)($data['rate'] ?? 0); // Profit rate in percentage -// Updated Validate duration and profit rates as per user request -// 60s/8%、90s/12%、120s/15%、180s/20%、300s/32% -$valid_durations = [ - 60 => ['profit' => 0.08, 'min' => 100], - 90 => ['profit' => 0.12, 'min' => 5000], - 120 => ['profit' => 0.15, 'min' => 30000], - 180 => ['profit' => 0.20, 'min' => 100000], - 300 => ['profit' => 0.32, 'min' => 300000], +if (empty($pair) || empty($direction) || $amount <= 0 || $duration <= 0 || $rate <= 0) { + echo json_encode(['success' => false, 'error' => 'Invalid order parameters.']); + exit; +} + +// --- Server-Side Rules Validation --- +$valid_options = [ + 60 => ['rate' => 8, 'min' => 100], + 90 => ['rate' => 12, 'min' => 5000], + 120 => ['rate' => 15, 'min' => 30000], + 180 => ['rate' => 20, 'min' => 100000], + 300 => ['rate' => 32, 'min' => 300000], ]; -if (!isset($valid_durations[$duration])) { - echo json_encode(['success' => false, 'error' => 'Invalid duration']); +if (!isset($valid_options[$duration]) || $valid_options[$duration]['rate'] != $rate) { + echo json_encode(['success' => false, 'error' => 'Invalid duration or profit rate selected.']); exit; } -$profit_rate = $valid_durations[$duration]['profit']; -$min_amount = $valid_durations[$duration]['min']; - +$min_amount = $valid_options[$duration]['min']; if ($amount < $min_amount) { - echo json_encode(['success' => false, 'error' => "Minimum amount for {$duration}S is {$min_amount} USDT"]); + echo json_encode(['success' => false, 'error' => "Minimum amount for {$duration}s is {$min_amount} USDT."]); exit; } -$db = db(); -$db->beginTransaction(); +// --- Securely Fetch Current Price from Binance API --- +function get_current_price($symbol) { + $url = "https://api.binance.com/api/v3/ticker/price?symbol=" . urlencode($symbol); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 5); + $response = curl_exec($ch); + curl_close($ch); + if ($response) { + $data = json_decode($response, true); + if (isset($data['price'])) { + return (float)$data['price']; + } + } + return null; +} + +$open_price = get_current_price($pair); +if ($open_price === null) { + echo json_encode(['success' => false, 'error' => 'Could not fetch current price for the pair. Please try again.']); + exit; +} + +// --- Database Transaction --- +$pdo = db(); +$pdo->beginTransaction(); try { - $stmt = $db->prepare("SELECT balance FROM users WHERE id = ? FOR UPDATE"); + // 1. Lock user row and check balance + $stmt = $pdo->prepare("SELECT balance FROM users WHERE id = ? FOR UPDATE"); $stmt->execute([$user_id]); $user = $stmt->fetch(); - + if (!$user || $user['balance'] < $amount) { - throw new Exception('Insufficient balance'); + $pdo->rollBack(); + echo json_encode(['success' => false, 'error' => 'Insufficient balance.']); + exit; } + // 2. Deduct balance $new_balance = $user['balance'] - $amount; - $db->prepare("UPDATE users SET balance = ? WHERE id = ?")->execute([$new_balance, $user_id]); + $stmt = $pdo->prepare("UPDATE users SET balance = ? WHERE id = ?"); + $stmt->execute([$new_balance, $user_id]); + // 3. Insert the new option order + $start_time = date('Y-m-d H:i:s'); $settle_at = date('Y-m-d H:i:s', time() + $duration); + $profit_rate_decimal = $rate / 100; // Store as decimal for calculation - $stmt = $db->prepare("INSERT INTO option_orders (user_id, symbol, amount, direction, duration, profit_rate, opening_price, status, settle_at) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?)"); - $stmt->execute([$user_id, $symbol, $amount, $direction, $duration, $profit_rate, $opening_price, $settle_at]); + $stmt = $pdo->prepare( + "INSERT INTO option_orders (user_id, pair, amount, direction, duration, profit_rate, open_price, status, start_time, settle_at) " . + "VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, ?)" + ); + $stmt->execute([$user_id, $pair, $amount, $direction, $duration, $profit_rate_decimal, $open_price, $start_time, $settle_at]); + + $order_id = $pdo->lastInsertId(); + + // 4. Commit the transaction + $pdo->commit(); + + // 5. Fetch the created order to return to the frontend + $stmt = $pdo->prepare("SELECT * FROM option_orders WHERE id = ?"); + $stmt->execute([$order_id]); + $new_order = $stmt->fetch(PDO::FETCH_ASSOC); + + echo json_encode(['success' => true, 'order' => $new_order]); - $db->commit(); - echo json_encode(['success' => true, 'new_balance' => $new_balance]); } catch (Exception $e) { - $db->rollBack(); - echo json_encode(['success' => false, 'error' => $e->getMessage()]); + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + error_log("Option order failed: " . $e->getMessage()); + echo json_encode(['success' => false, 'error' => 'An internal error occurred. Please try again later.']); } +?> \ No newline at end of file diff --git a/assets/pasted-20260213-133615-f239802a.png b/assets/pasted-20260213-133615-f239802a.png new file mode 100644 index 0000000..ce477fc Binary files /dev/null and b/assets/pasted-20260213-133615-f239802a.png differ diff --git a/assets/pasted-20260213-134043-36ee7ade.png b/assets/pasted-20260213-134043-36ee7ade.png new file mode 100644 index 0000000..ed37721 Binary files /dev/null and b/assets/pasted-20260213-134043-36ee7ade.png differ diff --git a/futures.php b/futures.php index f77c281..e663794 100644 --- a/futures.php +++ b/futures.php @@ -1,550 +1,433 @@ prepare("SELECT currency, balance FROM assets WHERE user_id = ?"); + $stmt->execute([$user_id]); + $assets_data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); + if ($assets_data) { + $user_assets = $assets_data; + } +} ?> -
- - - -
- -
- -
-
-
-
-
-
+
+ +
+
+
+
+
+
+
+ +
- -
-
-
-
-
- - --/-- Perpetual +
+
+
+
+ + --/-- +
+
+
--
+
--
+
+
--
+
--
+
--
-
-
--
-
--
-
- -
- - -- -
-
- - -- -
-
- - -- -
-
- -
- -
-
-
-
20x
-
-
- - - USDT -
-
- - - -- -
-
- -
0%25%50%75%100%
-
-
-
TP
-
SL
-
-
- 0.00 USDT -
-
- - +
+ +
+
+
+
+
20x
+
+
USDT
+
--
+
+
: 0.00 USDT
+
+ + +
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
-
-
- (USDT) - (--) - +
+
+ (USDT) + (--) + +
+
+
+
--
+
+
+
+ +
-
-
--
-
- - - - + + + + \ No newline at end of file diff --git a/header.php b/header.php index 7de6af7..b1b7518 100644 --- a/header.php +++ b/header.php @@ -1,4 +1,8 @@ 'Trade', 'nav_spot' => 'Spot', 'nav_futures' => 'Futures', - 'nav_options' => 'Second Contract', + 'nav_options' => 'Options', 'nav_mining' => 'Mining', 'nav_convert' => 'Convert', 'nav_assets' => 'Assets', @@ -141,6 +141,10 @@ $translations = [ 'system_matching_engine' => 'System matching engine', 'options_instruction' => 'Trading Instruction', 'options_wait_settle' => 'The system matching engine is executing. Please wait for the countdown to end for settlement.', + 'current_price' => 'Current Price', + 'order_date' => 'Order Date', + 'options_order_executing' => 'Order is executing, settlement upon countdown completion.', + 'options_order_settled' => 'Order settled.', 'kyc_status' => 'KYC Verification', 'kyc_none' => 'Unverified', @@ -209,7 +213,7 @@ $translations = [ 'error_fetching_orders' => 'Error fetching orders', 'error_fetching_balance' => 'Error fetching balance', 'error_no_live_price' => 'No live price data available yet. Please wait.', - 'websocket_connecting' => 'Connecting to market data...', + 'websocket_connecting' => 'Connecting to market data...', 'websocket_connected' => 'Connected to market data.', 'websocket_error' => 'Market data connection error!', 'websocket_disconnected' => 'Market data disconnected. Reconnecting...', @@ -351,7 +355,11 @@ $translations = [ 'system_matching_engine' => '系统撮合引擎', 'options_instruction' => '交易说明', 'options_wait_settle' => '系统撮合引擎正在执行中,请等待倒计时结束进行结算。', - + 'current_price' => '当前价格', + 'order_date' => '下单日期', + 'options_order_executing' => '订单执行中,倒计时结束后结算。', + 'options_order_settled' => '订单已结算。', + 'kyc_status' => '实名认证', 'kyc_none' => '未认证', 'kyc_pending' => '审核中', @@ -419,7 +427,7 @@ $translations = [ 'error_fetching_orders' => '获取订单失败', 'error_fetching_balance' => '获取余额失败', 'error_no_live_price' => '暂无实时价格数据,请稍候。', - 'websocket_connecting' => '正在连接市场数据...', + 'websocket_connecting' => '正在连接市场数据...', 'websocket_connected' => '市场数据已连接。', 'websocket_error' => '市场数据连接错误!', 'websocket_disconnected' => '市场数据已断开。正在重新连接...', @@ -427,7 +435,14 @@ $translations = [ ]; if (!isset($_SESSION['lang'])) { - $_SESSION['lang'] = 'en'; + // Detect browser language + $browser_lang = !empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? strtok(strip_tags($_SERVER['HTTP_ACCEPT_LANGUAGE']), ',') : ''; + $browser_lang = substr($browser_lang, 0, 2); + if ($browser_lang === 'zh') { + $_SESSION['lang'] = 'zh'; + } else { + $_SESSION['lang'] = 'en'; + } } if (isset($_GET['lang']) && array_key_exists($_GET['lang'], $translations)) { diff --git a/options.php b/options.php index 923532d..3c0f019 100644 --- a/options.php +++ b/options.php @@ -1,702 +1,464 @@ prepare("SELECT currency, balance FROM assets WHERE user_id = ?"); + $stmt->execute([$user_id]); + $assets_data = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); + if ($assets_data) { + $user_assets = $assets_data; + } } ?> -
- - - -
- -
- -
-
-
-
-
-
+
+ +
+ +
+
+
+
+
+
+ +
- -
-
-
-
-
-
- -
-
--/--
-
-
+ +
+
+
--/--
+
--
--
+
--
+
--
- -
-
--
-
--
-
- -
- - -- -
-
- - -- -
-
- - -- -
-
- -
- -
-
-
-
-
-
60S 8%
-
90S 12%
-
120S 15%
-
180S 20%
-
300S 32%
+
+
+
+
+
+
/
+
+
60s
+8%
+
90s
+12%
+
120s
+15%
+
180s
+20%
+
300s
+32%
+
+
+
+
+
USDT
+
+
: 0.00
+
: +0.00
+
+
+
+ + +
- -
-
-
- - USDT +
+
+
+
-
- : 0.00 - +0.00 +
+
- -
- - -
-
-
-
-
+ +
+
+
+ (USDT) + (BTC) +
-
-
-
- -
-
-
-
-
- -- +
+
+
+
-
-
+ +
- - - -
- - - - - -
- - - -