prepare(" UPDATE `ride` SET `status` = ?, `driver_id` = ?, `rideTimeStart` = NOW() WHERE `id` = ? AND `status` IN ('waiting', 'wait') "); $stmtRemote->execute([$status, $driverId, $rideId]); // Check if the update actually changed a row. // If rowCount > 0, IT MEANS SUCCESS! This driver won the ride. if ($stmtRemote->rowCount() > 0) { // 4. Synchronization: Update Local Database // Now that we secured the ride, we update the main server's DB ($con) to match. if (isset($con)) { $stmtLocal = $con->prepare("UPDATE `ride` SET `driver_id` = ?, `status` = ?, `rideTimeStart` = NOW() WHERE id = ?"); $stmtLocal->execute([$driverId, $status, $rideId]); } // 5. Update/Insert Driver Orders Table // This tracks the driver's history or active orders. $checkSql = "SELECT `order_id` FROM `driver_orders` WHERE `order_id` = ?"; $checkStmt = $con->prepare($checkSql); $checkStmt->execute([$rideId]); if ($checkStmt->rowCount() > 0) { // If entry exists, update it $updateSql = "UPDATE `driver_orders` SET `driver_id` = ?, `status` = ?, `created_at` = NOW() WHERE `order_id` = ?"; $con->prepare($updateSql)->execute([$driverId, $status, $rideId]); } else { // If not, insert new record $insertSql = "INSERT INTO `driver_orders` (`driver_id`, `order_id`, `created_at`, `status`) VALUES (?, ?, NOW(), ?)"; $con->prepare($insertSql)->execute([$driverId, $rideId, $status]); } // ================================================================= // 6. ๐Ÿ‘ค GET DRIVER INFO (For the Passenger) // We need to fetch driver details (Car, Name, Rating) to show to the passenger. // ================================================================= $driverInfo = []; $sqlDetails = "SELECT d.id as driver_id, d.first_name, d.last_name, d.gender, d.phone, c.make, c.model, c.car_plate, c.year, c.color, c.color_hex, (SELECT ROUND(AVG(rating), 2) FROM ratingDriver WHERE driver_id = d.id) AS ratingDriver, dt.token FROM driver d LEFT JOIN CarRegistration c ON c.driverID = d.id LEFT JOIN driverToken dt ON dt.captain_id = d.id WHERE d.id = ?"; $stmtDetails = $con->prepare($sqlDetails); $stmtDetails->execute([$driverId]); $driverRawData = $stmtDetails->fetch(PDO::FETCH_ASSOC); if ($driverRawData) { // List of encrypted fields that need decryption $fieldsToDecrypt = ['first_name', 'last_name', 'gender', 'phone', 'car_plate', 'token']; foreach ($driverRawData as $key => $value) { if (in_array($key, $fieldsToDecrypt) && !empty($value)) { // Decrypt sensitive data $driverInfo[$key] = $encryptionHelper->decryptData($value); } else { $driverInfo[$key] = $value; } } // Format Full Name $driverInfo['driverName'] = trim(($driverInfo['first_name'] ?? '') . ' ' . ($driverInfo['last_name'] ?? '')); // Default rating if null if (empty($driverInfo['ratingDriver'])) { $driverInfo['ratingDriver'] = "5.0"; } } // ================================================================= // 7. ๐Ÿ”” NOTIFY PASSENGER (Socket + FCM) // Inform the passenger that a driver has been found. // ================================================================= // Fetch Passenger ID based on Ride ID $stmtPas = $con->prepare("SELECT passenger_id FROM ride WHERE id = ?"); $stmtPas->execute([$rideId]); $passenger_id = $stmtPas->fetchColumn(); if ($passenger_id) { // A. Send Socket Notification (Real-time update on map) if (function_exists('notifyPassengerOnRideServer')) { notifyPassengerOnRideServer($passenger_id, [ 'status' => 'accepted', 'ride_id' => $rideId, 'driver_id' => $driverId, 'driver_info' => $driverInfo ]); } // B. Send FCM Notification (Push Notification) if (!empty($passengerToken)) { // Using the standardized FCM function sendFCM_Internal( $passengerToken, "Ride Accepted ๐Ÿš–", // Title "Captain " . ($driverInfo['driverName'] ?? 'Driver') . " is coming to you.", // Body ['ride_id' => (string)$rideId, 'driver_info' => $driverInfo], // Data Payload "Accepted Ride", // Category false // Not a topic ); } } // ================================================================= // 8. ๐Ÿงน MARKETPLACE CLEANUP (Notify Location Server) // Crucial Step: We tell the Location Server that this ride is taken. // The Location Server will: // 1. Remove the ride from Redis (geo:rides:waiting). // 2. Broadcast 'ride_taken' to other drivers to remove it from their screens. // ================================================================= sendToLocationServer('ride_taken_event', [ 'ride_id' => $rideId, 'taken_by_driver_id' => $driverId ]); // 9. Final Response to the Driver App echo json_encode([ "status" => "success", "message" => "Ride Accepted", "data" => $driverInfo ]); } else { // Failure: This means rowCount was 0. // Reason: The ride status was NOT 'waiting' (another driver took it milliseconds ago). jsonError("Ride not available (Already taken)"); } } catch (Exception $e) { // Handle unexpected errors jsonError("Error: " . $e->getMessage()); } ?>