'failure', 'message' => 'Database connection failed']); exit; } // ========================================================= // backend/bot/worker.php // Endpoint for the standalone Android Bot (Worker Node) // Handles Wallet Payments (ShamCash) and Competitor Pricing // ========================================================= // 1. Security & Configuration $SECRET_KEY = getenv('BOT_SECRET_KEY') ?: 'SIRO_BOT_SUPER_SECRET_123'; $ALLOWED_DEVICES = ['SHAM_CASH_BOT_01', 'PRICE_SCRAPER_BOT_01']; $method = $_SERVER['REQUEST_METHOD']; // 2. Validate Security (HMAC-SHA256 Signature) function validateSignature($device_id, $ts, $sig, $secret_key) { // Prevent replay attacks (valid for 5 minutes) if (abs(time() - $ts) > 300) { return false; } // Generate the expected signature $expected_sig = hash_hmac('sha256', $device_id . $ts, $secret_key); // Secure comparison to prevent timing attacks return hash_equals($expected_sig, $sig); } if ($method === 'GET') { // --------------------------------------------------------- // GET: Fetch Pending Task for the Bot // --------------------------------------------------------- $device_id = filterRequest('device_id'); $ts = filterRequest('ts'); $sig = filterRequest('sig'); if (!$device_id || !$ts || !$sig) { jsonError("Missing security parameters"); exit; } if (!in_array($device_id, $ALLOWED_DEVICES)) { jsonError("Device not authorized: " . $device_id); exit; } if (!validateSignature($device_id, $ts, $sig, $SECRET_KEY)) { jsonError("Invalid signature or expired timestamp"); exit; } // Check Redis Queue for tasks // Queue Name: queue:bot:tasks (Using Main Redis) $taskJson = $redis->rpop('queue:bot:tasks'); if ($taskJson) { $task = json_decode($taskJson, true); echo json_encode([ "status" => "success", "has_task" => true, "task" => $task ]); } else { echo json_encode([ "status" => "success", "has_task" => false ]); } exit; } elseif ($method === 'POST') { // --------------------------------------------------------- // POST: Submit Result from the Bot // --------------------------------------------------------- // Read raw JSON body $input = json_decode(file_get_contents('php://input'), true); if (!$input) { jsonError("Invalid JSON body"); exit; } $device_id = $input['device_id'] ?? null; $ts = $input['ts'] ?? null; $sig = $input['sig'] ?? null; $task_id = $input['task_id'] ?? null; $task_status = $input['status'] ?? null; // 'success' or 'failed' $result_data = $input['result_data'] ?? []; if (!$device_id || !$ts || !$sig || !$task_id || !$task_status) { jsonError("Missing required parameters"); exit; } if (!in_array($device_id, $ALLOWED_DEVICES)) { jsonError("Device not authorized"); exit; } if (!validateSignature($device_id, $ts, $sig, $SECRET_KEY)) { jsonError("Invalid signature or expired timestamp"); exit; } $task_type = $input['type'] ?? 'payment'; // Process the result based on task type if ($task_status === 'success') { if ($task_type === 'price_check') { $app_name = $result_data['app'] ?? 'unknown'; $price = (float)($result_data['price'] ?? 0); $distance_km = (float)($result_data['distance_km'] ?? 0); $start_lat = (float)($result_data['start_lat'] ?? 0); $start_lng = (float)($result_data['start_lng'] ?? 0); $end_lat = (float)($result_data['end_lat'] ?? 0); $end_lng = (float)($result_data['end_lng'] ?? 0); // 1. Save to MySQL $stmt = $con->prepare(" INSERT INTO competitor_prices (app_name, start_lat, start_lng, end_lat, end_lng, distance_km, price) VALUES (?, ?, ?, ?, ?, ?, ?) "); $stmt->execute([$app_name, $start_lat, $start_lng, $end_lat, $end_lng, $distance_km, $price]); // 2. Save to Redis (Calculate Price Per KM) if ($distance_km > 0 && $price > 0) { $pricePerKm = $price / $distance_km; // Store in Redis (Main) to be used by Pricing Engine // Store recent 50 prices for the app $redis->lpush("competitor:price_history:$app_name", $pricePerKm); $redis->ltrim("competitor:price_history:$app_name", 0, 49); error_log("[Bot Worker] Price Check $app_name: Dist $distance_km, Price $price"); } } else { // It's a payment task $transaction_id = $result_data['transaction_id'] ?? 'N/A'; // TODO: Update MySQL driver balance/payout status // $stmt = $con->prepare("UPDATE payouts SET status = 'paid', transaction_ref = ? WHERE task_id = ?"); // $stmt->execute([$transaction_id, $task_id]); error_log("[Bot Worker] Task $task_id SUCCESS on $device_id. Ref: $transaction_id"); } } else { $error_msg = $result_data['error'] ?? 'Unknown Error'; error_log("[Bot Worker] Task $task_id FAILED on $device_id. Reason: $error_msg"); // Optional: Re-queue the task if it failed due to a temporary issue } echo json_encode(["status" => "success", "message" => "Result recorded successfully"]); exit; } else { http_response_code(405); jsonError("Method Not Allowed"); }