diff --git a/api/views.py b/api/views.py index 66ef5ce..dda1e06 100644 --- a/api/views.py +++ b/api/views.py @@ -115,14 +115,13 @@ class SendEmailOtpView(APIView): def post(self, request): email = request.data.get('email') - otp_code = request.data.get('otp_code') template_name = request.data.get('template_name', 'verification') if not email: return Response({'error': 'email is required'}, status=status.HTTP_400_BAD_REQUEST) - if not otp_code: - otp_code = str(random.randint(100000, 999999)) + # Always generate a fresh, unique 6-digit OTP code for every dispatch + otp_code = str(random.randint(100000, 999999)) user = request.user if (request.user and request.user.is_authenticated) else None if not user: @@ -380,18 +379,34 @@ class VerifyOtpView(APIView): 'user': UserSerializer(user_obj, context={'request': request}).data }) - if not user_obj: - return Response({'verified': False, 'error': 'No active user or OTP found'}, status=status.HTTP_400_BAD_REQUEST) - - record = OTPRecord.objects.filter( - user=user_obj, - type=otp_type, - used=False - ).order_by('-created_at').first() + # Smart Record Lookup: + # 1. Match by user_obj AND otp_hash + record = None + if user_obj: + record = OTPRecord.objects.filter( + user=user_obj, + type=otp_type, + otp_hash=h, + used=False, + expires_at__gte=timezone.now() + ).order_by('-created_at').first() + # 2. Match by type AND otp_hash across all unexpired records if not record: - # Fallback to check record across all active unexpired records matching hash - record = OTPRecord.objects.filter(type=otp_type, otp_hash=h, used=False, expires_at__gte=timezone.now()).order_by('-created_at').first() + record = OTPRecord.objects.filter( + type=otp_type, + otp_hash=h, + used=False, + expires_at__gte=timezone.now() + ).order_by('-created_at').first() + + # 3. Fallback to latest unexpired record for user_obj + if not record and user_obj: + record = OTPRecord.objects.filter( + user=user_obj, + type=otp_type, + used=False + ).order_by('-created_at').first() if not record: return Response({'verified': False, 'error': 'No active OTP found'}, status=status.HTTP_400_BAD_REQUEST)