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; + } +} ?> -
- - - -