Prabha-AIMLOPS commited on
Commit
797987e
·
1 Parent(s): 3e42e33

added regenerate otp endpoint, updated verify to validate either sms/email and made company id, email str code, mobile str code, status as optional attributes

Browse files
app/controllers/merchant_controller.py CHANGED
@@ -94,4 +94,30 @@ async def get_otps(key: str = Query(..., description="The email or mobile number
94
  raise e
95
  except Exception as e:
96
  logger.error("Unexpected error while retrieving OTP for key %s: %s", key, e)
97
- raise HTTPException(status_code=500, detail="Failed to retrieve OTP") from e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  raise e
95
  except Exception as e:
96
  logger.error("Unexpected error while retrieving OTP for key %s: %s", key, e)
97
+ raise HTTPException(status_code=500, detail="Failed to retrieve OTP") from e
98
+
99
+ from app.services.merchant_services import generate_and_send_otp
100
+
101
+ @router.post("/otp/regenerate")
102
+ async def regenerate_otp(user: MerchantRegister = Body(...)):
103
+ """
104
+ API endpoint to regenerate an OTP for a given email, mobile number, or both.
105
+
106
+ Args:
107
+ user (MerchantRegister): The Merchant object containing email and mobile details.
108
+
109
+ Returns:
110
+ dict: Confirmation message indicating the OTP has been regenerated.
111
+ """
112
+ try:
113
+ logger.info("Regenerating OTP for Merchant with email: %s and mobile: %s", user.email, user.mobile)
114
+
115
+ # Regenerate OTP for email & mobile
116
+ if user.email and user.mobile:
117
+ await generate_and_send_otp(user.email, user.mobile)
118
+
119
+ return {"message": "OTP successfully regenerated for email and/or mobile.", "email": user.email, "mobile": user.mobile}
120
+
121
+ except Exception as e:
122
+ logger.error("Failed to regenerate OTP for Merchant with email: %s and mobile: %s. Error: %s", user.email, user.mobile, e)
123
+ raise HTTPException(status_code=500, detail="Failed to regenerate OTP") from e
app/models/merchant.py CHANGED
@@ -1,4 +1,5 @@
1
  from pydantic import BaseModel, EmailStr, Field
 
2
  from datetime import datetime
3
 
4
  class MerchantRegister(BaseModel):
@@ -6,13 +7,13 @@ class MerchantRegister(BaseModel):
6
  Pydantic model for Merchant registration request payload.
7
  """
8
  merchant_name: str = Field(..., description="The name of the Merchant")
9
- merchant_id: str = Field(..., description="The unique identifier for the Merchant")
10
  country: str = Field(..., description="The country where the Merchant is located")
11
  city: str = Field(..., description="The city where the Merchant is located")
12
  area: str = Field(..., description="The area where the Merchant is located")
13
  email: EmailStr = Field(..., description="The email address of the customer")
14
  mobile: str = Field(..., description="The 10-digit mobile number of the customer")
15
- status: str = Field("pending-verification", description="The status of the customer account")
16
- email_ver_code: str = Field(..., description="The email verification code")
17
- mobile_ver_code: str = Field(..., description="The mobile verification code")
18
- created_at: datetime = Field(default_factory=datetime.now, description="The timestamp when the customer was created")
 
1
  from pydantic import BaseModel, EmailStr, Field
2
+ from typing import Optional
3
  from datetime import datetime
4
 
5
  class MerchantRegister(BaseModel):
 
7
  Pydantic model for Merchant registration request payload.
8
  """
9
  merchant_name: str = Field(..., description="The name of the Merchant")
10
+ merchant_id: Optional[str] = Field(..., description="The unique identifier for the Merchant")
11
  country: str = Field(..., description="The country where the Merchant is located")
12
  city: str = Field(..., description="The city where the Merchant is located")
13
  area: str = Field(..., description="The area where the Merchant is located")
14
  email: EmailStr = Field(..., description="The email address of the customer")
15
  mobile: str = Field(..., description="The 10-digit mobile number of the customer")
16
+ status: Optional[str] = Field("pending-verification", description="The status of the customer account")
17
+ email_ver_code: Optional[str] = Field(..., description="The email verification code")
18
+ mobile_ver_code: Optional[str] = Field(..., description="The mobile verification code")
19
+ created_at: Optional[datetime] = Field(default_factory=datetime.now, description="The timestamp when the customer was created")
app/services/merchant_services.py CHANGED
@@ -44,52 +44,36 @@ async def validate_merchant(merchant: MerchantRegister) -> dict:
44
  return {"status": "error", "errors": errors}
45
  else:
46
  #merchant_id = generate_tenant_id(merchant.merchantname, merchant.country, merchant.city, merchant.area) # Send verification emails and SMS if no errors
47
- await send_email_verification(merchant.email)
48
  await send_sms_verification(merchant.mobile)
49
  #merchant.merchantId=merchant_id
50
 
51
  # If all validations pass
52
  return merchant.dict()
53
 
54
- async def send_email_verification(email: str):
55
  """
56
- Send an email verification link or code.
57
 
58
  Args:
59
  email (str): The email address to send the verification to.
 
60
  """
61
  # Implement your email sending logic here
62
  # Generate a 6-digit OTP
63
  otp = ''.join(random.choices("0123456789", k=6))
64
  # Store OTP in cache with a 15-minute expiry
65
  await set_otp(email, {"otp": otp, "expiry_duration": 15 * 60}) # 15 minutes in seconds
 
66
  # Send the OTP to the email address
67
  # (You would typically use an email service here)
68
- # For demonstration, we'll just log the OTP
69
- logger.info("Sending email verification to: %s with OTP: %s", email, otp)
70
- logger.info("Sending email verification to: %s", email)
71
 
72
- async def send_sms_verification(mobile: str):
73
- """
74
- Send an SMS verification code.
75
 
76
- Args:
77
- mobile (str): The mobile number to send the verification to.
78
- """
79
- # Implement your SMS sending logic here
80
- # Generate a 6-digit OTP
81
- otp = ''.join(random.choices("0123456789", k=6))
82
- # Store OTP in cache with a 15-minute expiry
83
- await set_otp(mobile, {"otp": otp, "expiry_duration": 15 * 60}) # 15 minutes in seconds
84
- # Send the OTP to the mobile number
85
- # (You would typically use an SMS service here)
86
- # For demonstration, we'll just log the OTP
87
- logger.info("Sending SMS verification to: %s with OTP: %s", mobile, otp)
88
- logger.info("Sending SMS verification to: %s", mobile)
89
-
90
  async def verify_otp_and_save_merchant(merchant: MerchantRegister) -> dict:
91
  """
92
- Verify SMS OTP and email OTP, and save the complete merchant object in the database.
93
 
94
  Args:
95
  merchant (MerchantRegister): The complete merchant object including email, mobile, and OTPs.
@@ -104,22 +88,24 @@ async def verify_otp_and_save_merchant(merchant: MerchantRegister) -> dict:
104
  mobile_otp_data = await get_otp_from_cache(merchant.mobile)
105
 
106
  # Verify email OTP
107
- if not email_otp_data or email_otp_data["otp"] != merchant.email_ver_code:
108
- errors.append("Invalid or expired email OTP")
109
 
110
  # Verify mobile OTP
111
- if not mobile_otp_data or mobile_otp_data["otp"] != merchant.mobile_ver_code:
112
- errors.append("Invalid or expired mobile OTP")
113
 
114
- # Return errors if any
 
 
 
 
115
  if errors:
116
  return {"status": "error", "errors": errors}
117
 
118
  # Save the complete merchant object to the database
119
  merchant_data = merchant.dict()
120
- merchant_data["merchant_id"] = generate_tenant_id(merchant.merchant_name, merchant.country, merchant.city, merchant.area)
121
  merchant_data["status"] = "completed" # Update status to completed
122
-
123
  await save_merchant(merchant_data)
124
 
125
  return {"status": "success", "message": "Merchant successfully registered and verified"}
@@ -141,4 +127,29 @@ async def get_otp_from_cache(key: str) -> dict:
141
  except Exception as e:
142
  # Log the error and re-raise it
143
  logger.error(f"Failed to retrieve OTP for key {key}: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  raise
 
44
  return {"status": "error", "errors": errors}
45
  else:
46
  #merchant_id = generate_tenant_id(merchant.merchantname, merchant.country, merchant.city, merchant.area) # Send verification emails and SMS if no errors
47
+ await send_email_verification(merchant.email,merchant.mobile)
48
  await send_sms_verification(merchant.mobile)
49
  #merchant.merchantId=merchant_id
50
 
51
  # If all validations pass
52
  return merchant.dict()
53
 
54
+ async def send_sms_email_verification(email: str, mobile: str):
55
  """
56
+ Send an email & sms verification link or code.
57
 
58
  Args:
59
  email (str): The email address to send the verification to.
60
+ mobile (str): The mobile number to send the verification to.
61
  """
62
  # Implement your email sending logic here
63
  # Generate a 6-digit OTP
64
  otp = ''.join(random.choices("0123456789", k=6))
65
  # Store OTP in cache with a 15-minute expiry
66
  await set_otp(email, {"otp": otp, "expiry_duration": 15 * 60}) # 15 minutes in seconds
67
+ await set_otp(mobile, {"otp": otp, "expiry_duration": 15 * 60}) # 15 minutes in seconds
68
  # Send the OTP to the email address
69
  # (You would typically use an email service here)
70
+ # For demonstration, we'll just log the OTP
71
+ logger.info("Sending email & sms verification to: %s & %s with otp: %s", email, mobile, otp)
 
72
 
 
 
 
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  async def verify_otp_and_save_merchant(merchant: MerchantRegister) -> dict:
75
  """
76
+ Verify either SMS OTP or email OTP, and save the complete merchant object in the database.
77
 
78
  Args:
79
  merchant (MerchantRegister): The complete merchant object including email, mobile, and OTPs.
 
88
  mobile_otp_data = await get_otp_from_cache(merchant.mobile)
89
 
90
  # Verify email OTP
91
+ email_verified = email_otp_data and email_otp_data["otp"] == merchant.email_ver_code
 
92
 
93
  # Verify mobile OTP
94
+ mobile_verified = mobile_otp_data and mobile_otp_data["otp"] == merchant.mobile_ver_code
 
95
 
96
+ # Check if at least one verification is successful
97
+ if not email_verified and not mobile_verified:
98
+ errors.append("Invalid or expired OTP for both email and mobile")
99
+
100
+ # Return errors if neither OTP is valid
101
  if errors:
102
  return {"status": "error", "errors": errors}
103
 
104
  # Save the complete merchant object to the database
105
  merchant_data = merchant.dict()
106
+ merchant_data["merchant_id"] = generate_tenant_id(merchant.merchant_name, merchant.country, merchant.city, merchant.area)
107
  merchant_data["status"] = "completed" # Update status to completed
108
+
109
  await save_merchant(merchant_data)
110
 
111
  return {"status": "success", "message": "Merchant successfully registered and verified"}
 
127
  except Exception as e:
128
  # Log the error and re-raise it
129
  logger.error(f"Failed to retrieve OTP for key {key}: {e}")
130
+ raise
131
+
132
+ async def generate_and_send_otp(email: str, mobile: str) -> None:
133
+ """
134
+ Generate, store, and send an OTP for the given key (email or mobile).
135
+
136
+ Args:
137
+ email and mobile (str): The email or mobile number to send the OTP to.
138
+
139
+ Returns:
140
+ None
141
+ """
142
+ try:
143
+ # Generate a 6-digit OTP
144
+ otp = ''.join(random.choices("0123456789", k=6))
145
+
146
+ # Store OTP in cache with a 15-minute expiry
147
+ await set_otp(email, {"otp": otp, "expiry_duration": 15 * 60}) # 15 minutes in seconds
148
+ await set_otp(mobile, {"otp": otp, "expiry_duration": 15 * 60})
149
+
150
+ # Log the OTP generation
151
+ logger.info("Generated OTP for %s & %s: %s", email, mobile, otp)
152
+
153
+ except Exception as e:
154
+ logger.error("Failed to generate and send OTP for %s, %s: %s", email, mobile, e)
155
  raise