validate([ 'email' => 'required|email', ]); $email = $request->email; $code = $this->otpService->generate($email); try { Mail::to($email)->send(new TwoFactorOtp( otp: $code, ipAddress: $request->ip(), userAgent: $request->userAgent(), )); } catch (\Throwable $e) { Log::error('OTP send failed', ['email' => $email, 'error' => $e->getMessage()]); return ApiResponse::serverError('Failed to send OTP. Please try again later.'); } return ApiResponse::success(null, 'OTP has been sent to your email'); } #[OA\Post( path: '/v1/otp/verify', operationId: 'otpVerify', tags: ['OTP'], summary: 'Verify an OTP code', requestBody: new OA\RequestBody( required: true, content: new OA\JsonContent( required: ['email', 'code'], properties: [ new OA\Property(property: 'email', type: 'string', format: 'email'), new OA\Property(property: 'code', type: 'string', minLength: 6, maxLength: 6, example: '123456'), ] ) ), responses: [ new OA\Response(response: 200, description: 'OTP verified'), new OA\Response(response: 422, description: 'Invalid or expired OTP'), ] )] public function verify(Request $request) { $request->validate([ 'email' => 'required|email', 'code' => 'required|string|size:6', ]); if (! $this->otpService->verify($request->input('email'), $request->input('code'))) { return ApiResponse::error('Invalid or expired OTP code', 422); } return ApiResponse::success(null, 'OTP verified successfully'); } }