all(), [ 'firstname' => 'nullable|string|max:100', 'lastname' => 'nullable|string|max:100', 'phone' => 'required|string|max:9', 'fcm_token' => 'nullable|string', 'device_type' => 'required|in:android,ios,web', 'password' => 'required|string|min:4', ]); if ($validator->fails()) { return ResponseService::validationError($validator->errors()->first()); } $phone = preg_replace('/\D/', '', $request->phone); $client = Clientsreg::where('phone', $phone)->first(); if ($client) { if ($client->active) { return ResponseService::errorResponse(__('You are already registered. Log in.')); } if ($client->code_expires_at && now()->lt($client->code_expires_at)) { return ResponseService::errorResponse(__('The previous code is still valid. Please wait.')); } } $otp = rand(1000, 9999); $client = Clientsreg::updateOrCreate( ['phone' => $phone], [ 'firstname' => $request->firstname, 'lastname' => $request->lastname, 'fcm_token' => $request->fcm_token, 'device_type' => $request->device_type, 'password' => bcrypt($request->password), 'otp' => $otp, // ✅ MUHIM 'code_sent_at' => now(), 'code_expires_at' => now()->addMinutes(1), 'active' => false, ] ); dispatch(new SendSmsJob($phone, $otp)); return ResponseService::successResponse(__('OTP sent successfully.')); } catch (\Throwable $th) { ResponseService::logErrorResponse($th, 'OTP Controller -> getOtp'); return ResponseService::errorResponse(); } } /** * @OA\Post( * path="/api/verify-otp", * summary="Verify OTP code", * tags={"Auth"}, * * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"phone","otp"}, * @OA\Property(property="phone", type="string", example="998901234567"), * @OA\Property(property="otp", type="integer", example=1234) * ) * ), * * @OA\Response( * response=200, * description="Phone verified successfully" * ) * ) */ public function verifyOtp(Request $request) { try { $validator = Validator::make($request->all(), [ 'phone' => 'required|string', 'otp' => 'required|numeric|digits:4', ]); if ($validator->fails()) { return ResponseService::validationError($validator->errors()->first()); } $phone = preg_replace('/\D/', '', $request->phone); $client = Clientsreg::where('phone', $phone)->first(); if (!$client) { return ResponseService::errorResponse(__('User not found.')); } if (!$client->code_expires_at || now()->gt($client->code_expires_at)) { return ResponseService::validationError(__('OTP has expired.')); } if ($client->otp != $request->otp) { return ResponseService::validationError(__('Invalid OTP.')); } $client->active = true; $client->otp = null; $client->code_expires_at = null; $client->save(); return ResponseService::successResponse(__('Phone verified successfully'), [ 'phone' => $phone, 'verified' => true, ]); } catch (\Throwable $th) { ResponseService::logErrorResponse($th, 'OTP Controller -> verifyOtp'); return ResponseService::errorResponse(); } } /** * @OA\Post( * path="/api/login", * summary="User login", * tags={"Auth"}, * * @OA\RequestBody( * required=true, * @OA\JsonContent( * required={"phone","password","device_type"}, * @OA\Property(property="phone", type="string", example="998901234567"), * @OA\Property(property="password", type="string", example="1234"), * @OA\Property(property="device_type", type="string", example="android"), * @OA\Property(property="fcm_token", type="string", example="token_here") * ) * ), * * @OA\Response( * response=200, * description="Login successful", * @OA\JsonContent( * @OA\Property(property="token", type="string", example="1|abcdefg123456") * ) * ) * ) */ public function login(Request $request) { try { $validator = Validator::make($request->all(), [ 'phone' => 'required|string|max:15', 'password' => 'required|string|min:4', 'device_type' => 'required|in:android,ios,web', 'fcm_token' => 'nullable|string', ]); if ($validator->fails()) { return ResponseService::validationError($validator->errors()->first()); } $phone = preg_replace('/\D/', '', $request->phone); $client = Clientsreg::where('phone', $phone)->first(); if (!$client) { return ResponseService::errorResponse(__('User not found.')); } if (!$client->active) { return ResponseService::errorResponse(__('Account not verified.')); } if (!Hash::check($request->password, $client->password)) { return ResponseService::errorResponse(__('Invalid password.')); } $client->update([ 'device_type' => $request->device_type, 'fcm_token' => $request->fcm_token, ]); $token = $client->createToken('client-token')->plainTextToken; return ResponseService::successResponse(__('Login successful'), [ 'token' => $token, // 'user' => $client, ]); } catch (\Throwable $th) { ResponseService::logErrorResponse($th, 'Auth -> login'); return ResponseService::errorResponse(); } } /** * @OA\Get( * path="/api/profile", * summary="Get user profile", * tags={"Auth"}, * security={{"bearerAuth":{}}}, * * @OA\Response( * response=200, * description="Profile data", * @OA\JsonContent( * @OA\Property(property="id", type="integer", example=1), * @OA\Property(property="firstname", type="string", example="Ali"), * @OA\Property(property="lastname", type="string", example="Valiyev"), * @OA\Property(property="phone", type="string", example="998901234567"), * @OA\Property(property="email", type="string", example="test@gmail.com"), * @OA\Property(property="device_type", type="string", example="android") * ) * ) * ) */ public function profile(Request $request) { $client = $request->user(); return ResponseService::successResponse(__('Profile data'), [ 'id' => $client->id, 'firstname' => $client->firstname, 'lastname' => $client->lastname, 'phone' => $client->phone, 'email' => $client->email, 'device_type' => $client->device_type, ]); } /** * @OA\Get( * path="/api/logout", * summary="Logout user", * tags={"Auth"}, * security={{"bearerAuth":{}}}, * * @OA\Response( * response=200, * description="Logged out successfully" * ) * ) */ public function logout(Request $request) { try { $request->user()->currentAccessToken()->delete(); return ResponseService::successResponse(__('Logged out successfully')); } catch (\Throwable $th) { ResponseService::logErrorResponse($th, 'Auth -> logout'); return ResponseService::errorResponse(); } } }