encryption = $encryption; } // ══════════════════════════════════════════════ // PASSENGER LOGIN & REGISTRATION // ══════════════════════════════════════════════ public function passengerLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); $phone = $request->input('phone'); $password = $request->input('password'); $fingerprint = $request->input('fingerprint'); $fcmToken = $request->input('fcm_token'); $rawPhone = $phone; $localPhone = '0' . substr($phone, 3); $encRawPhone = $this->encryption->encrypt($rawPhone); $encLocalPhone = $this->encryption->encrypt($localPhone); $passenger = Passenger::active() ->whereIn('phone', [$rawPhone, $localPhone, $encRawPhone, $encLocalPhone]) ->first(); if (!$passenger) { return $this->failure('Invalid credentials'); } $storedPassword = $passenger->password; if (!password_verify($password, $storedPassword) && !hash_equals($storedPassword, hash_hmac('sha256', $password, config('intaleq.jwt_secret')))) { return $this->failure('Invalid credentials'); } $passengerToken = PassengerToken::where('passengerID', $passenger->id)->first(); $encryptedFcm = $this->encryption->encrypt($fcmToken); if ($passengerToken) { $passengerToken->update([ 'token' => $encryptedFcm, 'fingerPrint' => $fingerprint, ]); } else { PassengerToken::create([ 'token' => $encryptedFcm, 'passengerID' => $passenger->id, 'fingerPrint' => $fingerprint, ]); } if (empty($passenger->api_key)) { $this->generateApiKeys($passenger); } $jwt = $this->createJwt($passenger->id, 'passenger', $fingerprint, 3600); // 1 Hour return $this->success([ 'token' => $jwt, 'expires_in' => 3600, 'user_id' => $passenger->id, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret, ]); } public function passengerRegister(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'email' => 'required|email', 'first_name' => 'required|string', 'last_name' => 'required|string', 'fingerprint' => 'nullable|string', 'fcm_token' => 'nullable|string', ]); $phone = $request->input('phone'); $encryptedPhone = $this->encryption->encrypt($phone); if (Passenger::where('phone', $encryptedPhone)->exists()) { return $this->failure('Phone number already registered'); } $passengerId = (string) mt_rand(1000000000, 9999999999) . mt_rand(100000000, 999999999); $passenger = Passenger::create([ 'id' => $passengerId, 'phone' => $encryptedPhone, 'email' => $this->encryption->encrypt($request->input('email')), 'password' => password_hash($request->input('password', '123456'), PASSWORD_BCRYPT), 'first_name' => $this->encryption->encrypt($request->input('first_name')), 'last_name' => $this->encryption->encrypt($request->input('last_name')), 'gender' => $this->encryption->encrypt($request->input('gender', 'male')), 'birthdate' => $this->encryption->encrypt($request->input('birthdate', '2000-01-01')), 'site' => $request->input('site', 'Syria'), ]); if ($request->has('fcm_token')) { PassengerToken::create([ 'token' => $this->encryption->encrypt($request->input('fcm_token')), 'passengerID' => $passengerId, 'fingerPrint' => $request->input('fingerprint', 'unknown'), ]); } $this->generateApiKeys($passenger); $jwt = $this->createJwt($passengerId, 'passenger', $request->input('fingerprint', 'unknown'), 3600); return $this->success([ 'token' => $jwt, 'expires_in' => 3600, 'user_id' => $passengerId, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret, ], 201); } // ══════════════════════════════════════════════ // DRIVER LOGIN // ══════════════════════════════════════════════ public function driverLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); $encryptedPhone = $this->encryption->encrypt($request->input('phone')); $driver = Driver::active()->where('phone', $encryptedPhone)->first(); if (!$driver || (!password_verify($request->input('password'), $driver->password) && !hash_equals($driver->password, hash_hmac('sha256', $request->input('password'), config('intaleq.jwt_secret'))))) { return $this->failure('Invalid credentials'); } $jwt = $this->createJwt($driver->id, 'driver', $request->input('fingerprint'), 14400); // 4 Hours return $this->success([ 'token' => $jwt, 'expires_in' => 14400, 'user_id' => $driver->id, 'api_key' => $driver->api_key, 'api_secret' => $driver->api_secret, ]); } // ══════════════════════════════════════════════ // HANDSHAKE (V1 Compatibility) // ══════════════════════════════════════════════ public function passengerJwtHandshake(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $passenger = Passenger::find($request->input('id')); if (!$passenger) return $this->failure('User not found'); if (empty($passenger->api_key)) { $this->generateApiKeys($passenger); } $jwt = $this->createJwt($passenger->id, 'passenger', $request->input('fingerPrint'), 3600); return response()->json([ 'status' => 'success', 'jwt' => $jwt, 'expires_in' => 3600, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret ]); } public function driverJwtHandshake(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $driver = Driver::find($request->input('id')); if (!$driver) return $this->failure('User not found'); if (empty($driver->api_key)) { $this->generateApiKeys($driver); } $jwt = $this->createJwt($driver->id, 'driver', $request->input('fingerPrint'), 14400); return response()->json([ 'status' => 'success', 'jwt' => $jwt, 'expires_in' => 14400, 'api_key' => $driver->api_key, 'api_secret' => $driver->api_secret ]); } // ══════════════════════════════════════════════ // WALLET LOGIN // ══════════════════════════════════════════════ public function passengerWalletLogin(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $jwt = $this->createWalletJwt($request->input('id'), $request->input('fingerPrint'), $request->input('aud', 'TripzWallet'), 60); $hmac = hash_hmac('sha256', $request->input('id'), config('intaleq.wallet_hmac_secret')); return $this->success(['jwt' => $jwt, 'hmac' => $hmac, 'expires_in' => 60]); } public function driverWalletLogin(Request $request): JsonResponse { $request->validate(['id' => 'required|string', 'fingerPrint' => 'required|string']); $jwt = $this->createWalletJwt($request->input('id'), $request->input('fingerPrint'), $request->input('aud', 'TripzWallet'), 60, config('intaleq.wallet_jwt_secret')); $hmac = hash_hmac('sha256', $request->input('id'), config('intaleq.wallet_hmac_secret')); return $this->success(['jwt' => $jwt, 'hmac' => $hmac, 'expires_in' => 60]); } // ══════════════════════════════════════════════ // ADMIN LOGIN // ══════════════════════════════════════════════ public function adminLogin(Request $request): JsonResponse { $request->validate(['device_number' => 'required|string', 'password' => 'required|string']); $admin = DB::connection('primary')->table('adminUser')->where('device_number', $request->input('device_number'))->first(); if (!$admin || !password_verify($request->input('password'), $admin->password ?? '')) { return $this->failure('Invalid credentials'); } $jwt = $this->createJwt((string)$admin->id, 'admin', $request->input('device_number'), 900); return $this->success(['token' => $jwt, 'expires_in' => 900, 'user_id' => $admin->id]); } // ══════════════════════════════════════════════ // GOOGLE LOGIN // ══════════════════════════════════════════════ public function passengerLoginGoogle(Request $request): JsonResponse { $request->validate(['email' => 'required|string', 'id' => 'required|string']); $email = $request->input('email'); $searchEmail = (str_contains($email, '+') || str_contains($email, '/')) ? $email : $this->encryption->encrypt($email); $row = DB::connection('primary') ->table('passengers as p') ->leftJoin('phone_verification_passenger', 'phone_verification_passenger.phone_number', '=', 'p.phone') ->leftJoin('invitesToPassengers', 'invitesToPassengers.inviterPassengerPhone', '=', 'p.phone') ->leftJoin('promos', 'promos.passengerID', '=', 'p.id') ->select([ 'p.id', 'p.phone', 'p.email', 'p.gender', 'p.status', 'p.birthdate', 'p.site', 'p.first_name', 'p.last_name', 'p.sosPhone', 'p.education', 'p.employmentType', 'p.maritalStatus', 'p.created_at', 'p.updated_at', 'phone_verification_passenger.verified', 'invitesToPassengers.isInstall', 'invitesToPassengers.inviteCode', 'invitesToPassengers.isGiftToken', 'promos.promo_code as promo', 'promos.amount as discount', 'promos.validity_end_date as validity', 'p.api_key', 'p.api_secret', ]) ->selectSub(function ($query) use ($request) { $query->from('packageInfo') ->select('version') ->where('platform', $request->input('platform', 'ios')) ->limit(1); }, 'package') ->where('p.email', $searchEmail) ->where('p.id', $request->input('id')) ->first(); if (!$row) return response()->json(['status' => 'Failure', 'data' => 'User does not exist.']); $data = (array) $row; $data['package'] = $data['package'] ?? '1.1.33'; // Default to avoid Null error in Flutter // Ensure API keys exist if (empty($data['api_key'])) { $passenger = Passenger::find($data['id']); if ($passenger) { $this->generateApiKeys($passenger); $data['api_key'] = $passenger->api_key; $data['api_secret'] = $passenger->api_secret; } } foreach ($data as $key => $value) { if (is_string($value) && !in_array($key, ['id', 'status', 'created_at', 'updated_at', 'verified', 'isInstall', 'isGiftToken', 'api_key', 'api_secret', 'package'])) { $dec = $this->encryption->decrypt($value); if ($dec) $data[$key] = $dec; } } // Fetch Notification Token & Fingerprint $tokenRow = DB::connection('primary')->table('passengerToken')->where('passengerID', $data['id'])->first(); if ($tokenRow) { $data['fcm_token'] = $this->encryption->decrypt($tokenRow->token); $data['fingerprint'] = $tokenRow->fingerPrint; } else { $data['fcm_token'] = null; $data['fingerprint'] = null; } // Generate JWT using the header fingerprint, or fallback to the stored one $clientFp = $request->header('X-Device-FP'); $jwtFp = !empty($clientFp) ? $clientFp : ($data['fingerprint'] ?? 'unknown'); $jwt = $this->createJwt($data['id'], 'passenger', $jwtFp, 3600); return response()->json([ 'status' => 'success', 'count' => 1, 'data' => [$data], 'jwt' => $jwt, 'expires_in' => 3600 ]); } // ══════════════════════════════════════════════ // HELPERS // ══════════════════════════════════════════════ private function createJwt(string $userId, string $userType, string $fingerprint, int $expiry): string { $payload = [ 'user_id' => $userId, 'user_type' => $userType, 'fingerprint' => $fingerprint, 'iat' => time(), 'exp' => time() + $expiry, 'aud' => 'mobile-app', 'iss' => 'Tripz', 'jti' => bin2hex(random_bytes(16)), ]; return JWT::encode($payload, config('intaleq.jwt_secret'), 'HS256'); } private function createWalletJwt(string $userId, string $fingerprint, string $audience, int $expiry, ?string $secret = null): string { $payload = [ 'user_id' => $userId, 'fingerPrint' => hash('sha256', $fingerprint . config('intaleq.fp_pepper', '')), 'exp' => time() + $expiry, 'iat' => time(), 'iss' => 'Tripz-Wallet', 'aud' => $audience, 'jti' => bin2hex(random_bytes(16)), ]; return JWT::encode($payload, $secret ?? config('intaleq.jwt_secret'), 'HS256'); } private function generateApiKeys($model) { $model->api_key = bin2hex(random_bytes(16)); $model->api_secret = bin2hex(random_bytes(32)); DB::connection('primary')->table($model->getTable())->where('id', $model->id)->update([ 'api_key' => $model->api_key, 'api_secret' => $model->api_secret ]); } }