encryption = $encryption; $this->fcm = $fcm; $this->socket = $socket; } /** * POST /v2/rides * Replaces: ride/rides/add_ride.php */ public function store(Request $request): JsonResponse { $request->validate([ 'start_location' => 'required|string', 'end_location' => 'required|string', 'start_lat' => 'required|numeric', 'start_lng' => 'required|numeric', 'end_lat' => 'required|numeric', 'end_lng' => 'required|numeric', 'price' => 'required|numeric|min:0', 'car_type' => 'required|string', 'payment_method' => 'required|in:cash,visa', 'distance' => 'required|numeric', ]); $passengerId = $request->input('_jwt_user_id'); // Prevent duplicate active rides $activeRide = Ride::forPassenger($passengerId)->active()->first(); if ($activeRide) { return response()->json([ 'status' => 'failure', 'message' => 'You already have an active ride', ], 409); } // Begin transaction across both databases DB::connection('ride')->beginTransaction(); try { $ride = Ride::create([ 'start_location' => $request->input('start_location'), 'end_location' => $request->input('end_location'), 'date' => now()->toDateString(), 'time' => now()->toTimeString(), 'endtime' => '00:00:00', 'price' => $request->input('price'), 'passenger_id' => $passengerId, 'driver_id' => 'none', 'status' => 'waiting', 'paymentMethod' => $request->input('payment_method', 'Cash'), 'carType' => $request->input('car_type', 'Speed'), 'price_for_passenger' => $request->input('price'), 'distance' => $request->input('distance'), ]); // Also insert into waiting rides (for driver search) DB::connection('primary')->table('waitingRides')->insert([ 'id' => (string) $ride->id, 'start_location' => $request->input('start_location'), 'start_lat' => $request->input('start_lat'), 'start_lng' => $request->input('start_lng'), 'end_location' => $request->input('end_location'), 'end_lat' => $request->input('end_lat'), 'end_lng' => $request->input('end_lng'), 'date' => now()->toDateString(), 'time' => now()->toTimeString(), 'price' => $request->input('price'), 'passenger_id' => $passengerId, 'status' => 'waiting', 'carType' => $request->input('car_type', 'Speed'), 'passengerRate' => 5.0, 'price_for_passenger' => $request->input('price'), 'distance' => $request->input('distance'), 'duration' => $request->input('duration', '0'), 'payment_method' => $request->input('payment_method', 'cash'), 'passenger_wallet' => $request->input('wallet_balance', '0'), ]); DB::connection('ride')->commit(); // Notify nearby drivers via socket $this->socket->sendToLocationServer('new_ride', [ 'ride_id' => $ride->id, 'lat' => $request->input('start_lat'), 'lng' => $request->input('start_lng'), 'car_type' => $request->input('car_type'), ]); return response()->json([ 'status' => 'success', 'data' => ['ride_id' => $ride->id], ], 201); } catch (\Exception $e) { DB::connection('ride')->rollBack(); return response()->json([ 'status' => 'failure', 'message' => 'Failed to create ride', ], 500); } } /** * POST /v2/rides/{id}/accept * Replaces: ride/rides/acceptRide.php */ public function accept(Request $request, int $rideId): JsonResponse { $driverId = $request->input('_jwt_user_id'); DB::connection('ride')->beginTransaction(); try { // Lock the ride row to prevent race conditions $ride = Ride::lockForUpdate()->find($rideId); if (!$ride || !in_array($ride->status, ['waiting', 'wait', 'Apply'])) { DB::connection('ride')->rollBack(); return response()->json([ 'status' => 'failure', 'message' => 'Ride not available', ], 409); } // Update ride status atomically $ride->update([ 'driver_id' => $driverId, 'status' => 'Applied', 'DriverIsGoingToPassenger' => now(), ]); // Update driver order DriverOrder::where('order_id', (string) $rideId) ->where('driver_id', $driverId) ->update(['status' => 'accepted']); // Remove from waiting rides DB::connection('primary') ->table('waitingRides') ->where('id', (string) $rideId) ->update(['status' => 'Applied']); // Sync to primary DB ride table DB::connection('primary') ->table('ride') ->where('id', $rideId) ->update([ 'driver_id' => $driverId, 'status' => 'Applied', ]); DB::connection('ride')->commit(); // Notify passenger via FCM $passengerToken = PassengerToken::where('passengerID', $ride->passenger_id)->first(); if ($passengerToken) { $decryptedToken = $this->encryption->decrypt($passengerToken->token); $this->fcm->sendToDevice( $decryptedToken, 'تم قبول طلبك', 'السائق في الطريق إليك', ['ride_id' => (string) $rideId, 'status' => 'Applied'], 'ride_accepted' ); } // Notify passenger via socket $this->socket->notifyPassenger($ride->passenger_id, [ 'ride_id' => $rideId, 'status' => 'Applied', 'driver_id' => $driverId, ]); return response()->json(['status' => 'success']); } catch (\Exception $e) { DB::connection('ride')->rollBack(); return response()->json([ 'status' => 'failure', 'message' => 'Failed to accept ride', ], 500); } } /** * POST /v2/rides/{id}/start * Replaces: ride/rides/start_ride.php */ public function start(Request $request, int $rideId): JsonResponse { $driverId = $request->input('_jwt_user_id'); $ride = Ride::where('id', $rideId) ->where('driver_id', $driverId) ->whereIn('status', ['Applied', 'Arrived']) ->first(); if (!$ride) { return response()->json(['status' => 'failure', 'message' => 'Ride not found or not ready'], 404); } $ride->update([ 'status' => 'Begin', 'rideTimeStart' => now(), ]); // Sync to primary DB::connection('primary')->table('ride') ->where('id', $rideId) ->update(['status' => 'Begin', 'rideTimeStart' => now()]); // Notify passenger $passengerToken = PassengerToken::where('passengerID', $ride->passenger_id)->first(); if ($passengerToken) { $this->fcm->sendToDevice( $this->encryption->decrypt($passengerToken->token), 'الرحلة بدأت', 'رحلة سعيدة!', ['ride_id' => (string) $rideId, 'status' => 'Begin'], 'ride_started' ); } $this->socket->notifyPassenger($ride->passenger_id, [ 'ride_id' => $rideId, 'status' => 'Begin', ]); return response()->json(['status' => 'success']); } /** * POST /v2/rides/{id}/arrive * Replaces: ride/rides/arrive_ride.php */ public function arrive(Request $request, int $rideId): JsonResponse { $driverId = $request->input('_jwt_user_id'); $ride = Ride::where('id', $rideId) ->where('driver_id', $driverId) ->where('status', 'Applied') ->first(); if (!$ride) { return response()->json(['status' => 'failure', 'message' => 'Ride not found'], 404); } $ride->update(['status' => 'Arrived']); DB::connection('primary')->table('ride') ->where('id', $rideId)->update(['status' => 'Arrived']); $passengerToken = PassengerToken::where('passengerID', $ride->passenger_id)->first(); if ($passengerToken) { $this->fcm->sendToDevice( $this->encryption->decrypt($passengerToken->token), 'السائق وصل', 'السائق في انتظارك', ['ride_id' => (string) $rideId, 'status' => 'Arrived'], 'driver_arrived' ); } return response()->json(['status' => 'success']); } /** * POST /v2/rides/{id}/finish * Replaces: ride/rides/finish_ride_updates.php */ public function finish(Request $request, int $rideId): JsonResponse { $driverId = $request->input('_jwt_user_id'); $request->validate([ 'price_for_driver' => 'required|numeric', 'price_for_passenger' => 'required|numeric', 'distance' => 'required|numeric', ]); $ride = Ride::where('id', $rideId) ->where('driver_id', $driverId) ->where('status', 'Begin') ->first(); if (!$ride) { return response()->json(['status' => 'failure', 'message' => 'Ride not found'], 404); } DB::connection('ride')->beginTransaction(); try { $ride->update([ 'status' => 'finish', 'rideTimeFinish' => now(), 'endtime' => now()->toTimeString(), 'price_for_driver' => $request->input('price_for_driver'), 'price_for_passenger' => $request->input('price_for_passenger'), 'distance' => $request->input('distance'), ]); // Sync to primary DB::connection('primary')->table('ride') ->where('id', $rideId) ->update([ 'status' => 'finish', 'rideTimeFinish' => now(), 'price_for_driver' => $request->input('price_for_driver'), 'price_for_passenger' => $request->input('price_for_passenger'), 'distance' => $request->input('distance'), ]); // Create payment record DB::connection('primary')->table('payments')->insert([ 'id' => uniqid('pay_'), 'amount' => $request->input('price_for_passenger'), 'payment_method' => $ride->paymentMethod, 'passengerID' => $ride->passenger_id, 'rideId' => (string) $rideId, 'driverID' => $driverId, ]); DB::connection('ride')->commit(); // Notify passenger $passengerToken = PassengerToken::where('passengerID', $ride->passenger_id)->first(); if ($passengerToken) { $this->fcm->sendToDevice( $this->encryption->decrypt($passengerToken->token), 'الرحلة انتهت', 'شكراً لاستخدامك انطلق', [ 'ride_id' => (string) $rideId, 'status' => 'finish', 'price' => (string) $request->input('price_for_passenger'), ], 'ride_finished' ); } return response()->json(['status' => 'success']); } catch (\Exception $e) { DB::connection('ride')->rollBack(); return response()->json(['status' => 'failure', 'message' => 'Failed to finish ride'], 500); } } /** * POST /v2/rides/{id}/cancel/passenger * Replaces: ride/rides/cancel_ride_by_passenger.php */ public function cancelByPassenger(Request $request, int $rideId): JsonResponse { $passengerId = $request->input('_jwt_user_id'); $ride = Ride::where('id', $rideId) ->where('passenger_id', $passengerId) ->active() ->first(); if (!$ride) { return response()->json(['status' => 'failure', 'message' => 'Ride not found'], 404); } $ride->update(['status' => 'CancelByPassenger']); DB::connection('primary')->table('ride') ->where('id', $rideId)->update(['status' => 'CancelByPassenger']); DB::connection('primary')->table('waitingRides') ->where('id', (string) $rideId)->update(['status' => 'CancelByPassenger']); // Log cancellation DB::connection('ride')->table('canecl')->insert([ 'driverID' => $ride->driver_id ?? 'none', 'passengerID' => $passengerId, 'rideID' => (string) $rideId, 'note' => $request->input('reason', 'nothing'), ]); // Notify driver if assigned if ($ride->driver_id !== 'none') { $driverToken = DriverToken::where('captain_id', $ride->driver_id)->first(); if ($driverToken) { $this->fcm->sendToDevice( $this->encryption->decrypt($driverToken->token), 'تم إلغاء الرحلة', 'الراكب ألغى الطلب', ['ride_id' => (string) $rideId, 'status' => 'CancelByPassenger'], 'ride_cancelled' ); } $this->socket->sendToLocationServer('cancel_ride', [ 'driver_id' => $ride->driver_id, 'ride_id' => $rideId, ]); } return response()->json(['status' => 'success']); } /** * POST /v2/rides/{id}/cancel/driver * Replaces: ride/rides/cancel_ride_by_driver.php */ public function cancelByDriver(Request $request, int $rideId): JsonResponse { $driverId = $request->input('_jwt_user_id'); $ride = Ride::where('id', $rideId) ->where('driver_id', $driverId) ->active() ->first(); if (!$ride) { return response()->json(['status' => 'failure', 'message' => 'Ride not found'], 404); } $ride->update(['status' => 'CancelByDriver', 'driver_id' => 'none']); DB::connection('primary')->table('ride') ->where('id', $rideId)->update(['status' => 'CancelByDriver']); // Re-add to waiting rides for re-search DB::connection('primary')->table('waitingRides') ->where('id', (string) $rideId)->update(['status' => 'waiting']); // Log cancellation DB::connection('ride')->table('canecl')->insert([ 'driverID' => $driverId, 'passengerID' => $ride->passenger_id, 'rideID' => (string) $rideId, 'note' => $request->input('reason', 'nothing'), ]); // Notify passenger $passengerToken = PassengerToken::where('passengerID', $ride->passenger_id)->first(); if ($passengerToken) { $this->fcm->sendToDevice( $this->encryption->decrypt($passengerToken->token), 'تم إلغاء الرحلة', 'السائق ألغى الطلب، جاري البحث...', ['ride_id' => (string) $rideId, 'status' => 'CancelByDriver'], 'ride_cancelled' ); } return response()->json(['status' => 'success']); } /** * GET /v2/rides/{id} * Replaces: ride/rides/getRideOrderID.php */ public function show(int $rideId): JsonResponse { $ride = Ride::find($rideId); if (!$ride) { return response()->json(['status' => 'failure', 'message' => 'Ride not found'], 404); } return response()->json(['status' => 'success', 'data' => $ride]); } /** * GET /v2/rides/active * Replaces: ride/rides/getRideStatusFromStartApp.php */ public function active(Request $request): JsonResponse { $userId = $request->input('_jwt_user_id'); $userType = $request->input('_jwt_user_type'); $query = Ride::active(); if ($userType === 'driver') { $query->forDriver($userId); } else { $query->forPassenger($userId); } $ride = $query->orderBy('id', 'desc')->first(); return response()->json([ 'status' => 'success', 'data' => $ride, ]); } /** * GET /v2/rides * Replaces: ride/rides/get.php */ public function index(Request $request): JsonResponse { $userId = $request->input('_jwt_user_id'); $userType = $request->input('_jwt_user_type'); $page = $request->input('page', 1); $limit = min($request->input('limit', 20), 50); $query = Ride::query(); if ($userType === 'driver') { $query->forDriver($userId); } else { $query->forPassenger($userId); } $rides = $query->orderBy('id', 'desc') ->skip(($page - 1) * $limit) ->take($limit) ->get(); return response()->json(['status' => 'success', 'data' => $rides]); } }