Enhance VerifyPaymentView with user authorization check, streamline payment verification process, and improve response messages. Update settings.py for consistent formatting and clarity in PASSWORDLESS_AUTH configuration.
All checks were successful
Build and Push Docker Images / Build and Push Docker Images (push) Successful in 2m53s

This commit is contained in:
i701 2025-04-10 21:50:21 +05:00
parent 9595476569
commit f8253d572d
Signed by: i701
GPG Key ID: 54A0DA1E26D8E587
2 changed files with 67 additions and 81 deletions

View File

@ -104,57 +104,57 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
lookup_field = "pk" lookup_field = "pk"
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
# TODO: Fix check for success payment
payment = self.get_object()
data = request.data data = request.data
user = request.user user = request.user
if payment.user != user and not user.is_superuser:
return Response(
{"message": "You are not authorized to verify this payment."},
status=status.HTTP_403_FORBIDDEN,
)
method = data.get("method") method = data.get("method")
payment_id = kwargs.get("pk")
if not method: if not method:
return Response( return Response(
{"message": "method is required. 'WALLET' or 'TRANSFER'"}, {"message": "method is required. 'WALLET' or 'TRANSFER'"},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
) )
if not payment_id:
return Response( devices = payment.devices.all()
{"message": "payment_id is required."}, payment_status = False
status=status.HTTP_400_BAD_REQUEST, if method == "WALLET":
payment_status = self.process_wallet_payment(
user,
payment,
) )
if method == "TRANSFER":
data = {
"benefName": f"{user.first_name} {user.last_name}",
"accountNo": user.acc_no,
"absAmount": payment.amount,
"time": (timezone.now() + timedelta(minutes=5)).strftime(
"%Y-%m-%d %H:%M"
),
}
payment_status = self.verify_transfer_payment(data, payment)
try: if payment_status:
payment = Payment.objects.get(id=payment_id)
devices = payment.devices.all()
if data["type"] == "WALLET":
print("processing WALLET payment")
self.process_wallet_payment(user, payment, float(data["abs_amount"]))
elif data["type"] == "TRANSFER":
self.verify_external_payment(data, payment)
# Update devices # Update devices
expiry_date = timezone.now() + timedelta(days=30 * payment.number_of_months) expiry_date = timezone.now() + timedelta(days=30 * payment.number_of_months)
devices.update( devices.update(
is_active=True, expiry_date=expiry_date, has_a_pending_payment=False is_active=True, expiry_date=expiry_date, has_a_pending_payment=False
) )
return Response({"message": "Payment verified successfully."})
except Payment.DoesNotExist:
return Response( return Response(
{"message": "Payment not found."}, status=status.HTTP_404_NOT_FOUND {"message": f"Payment verified successfully using [{method}]."}
)
except InsufficientFundsError:
return Response(
{"message": "Insufficient funds in wallet."},
status=status.HTTP_400_BAD_REQUEST,
)
except Exception as e:
return Response(
{"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR
) )
def process_wallet_payment(self, user, payment, amount): return Response({"message": "Payment verified successfully using [{method}]."})
print("processing wallet payment")
print(user, amount) def process_wallet_payment(self, user, payment):
if user.wallet_balance < amount: print("processing wallet payment...")
print(user, payment.amount)
if user.wallet_balance < payment.amount:
return Response( return Response(
{"message": "Insufficient funds in wallet."}, {"message": "Insufficient funds in wallet."},
status=status.HTTP_400_BAD_REQUEST, status=status.HTTP_400_BAD_REQUEST,
@ -165,10 +165,12 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
payment.method = "WALLET" payment.method = "WALLET"
payment.save() payment.save()
user.wallet_balance -= amount user.wallet_balance -= payment.amount
user.save() user.save()
return True
def verify_external_payment(self, data, payment): def verify_transfer_payment(self, data, payment):
print("verifying transfer payment...")
response = requests.post( response = requests.post(
f"{config('PAYMENT_VERIFY_BASE_URL')}/verify-payment", f"{config('PAYMENT_VERIFY_BASE_URL')}/verify-payment",
json=data, json=data,
@ -177,7 +179,11 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView):
response.raise_for_status() response.raise_for_status()
print(response.json()) print(response.json())
if not response.json().get("success"): if not response.json().get("success"):
raise Exception("Payment verification failed.") return Response(
{"message": "MIB Payment verification failed."},
status=status.HTTP_400_BAD_REQUEST,
)
return True
class DeletePaymentView(StaffEditorPermissionMixin, generics.DestroyAPIView): class DeletePaymentView(StaffEditorPermissionMixin, generics.DestroyAPIView):

View File

@ -1,79 +1,59 @@
from django.conf import settings from django.conf import settings
from rest_framework.settings import APISettings from rest_framework.settings import APISettings
USER_SETTINGS = getattr(settings, 'PASSWORDLESS_AUTH', None) USER_SETTINGS = getattr(settings, "PASSWORDLESS_AUTH", None)
DEFAULTS = { DEFAULTS = {
# Allowed auth types, can be EMAIL, MOBILE, or both. # Allowed auth types, can be EMAIL, MOBILE, or both.
'PASSWORDLESS_AUTH_TYPES': ['EMAIL'], "PASSWORDLESS_AUTH_TYPES": ["EMAIL"],
# Amount of time that tokens last, in seconds # Amount of time that tokens last, in seconds
'PASSWORDLESS_TOKEN_EXPIRE_TIME': 15 * 60, "PASSWORDLESS_TOKEN_EXPIRE_TIME": 15 * 60,
# The user's email field name # The user's email field name
'PASSWORDLESS_USER_EMAIL_FIELD_NAME': 'email', "PASSWORDLESS_USER_EMAIL_FIELD_NAME": "email",
# The user's mobile field name # The user's mobile field name
'PASSWORDLESS_USER_MOBILE_FIELD_NAME': 'mobile', "PASSWORDLESS_USER_MOBILE_FIELD_NAME": "mobile",
# Marks itself as verified the first time a user completes auth via token. # Marks itself as verified the first time a user completes auth via token.
# Automatically unmarks itself if email is changed. # Automatically unmarks itself if email is changed.
'PASSWORDLESS_USER_MARK_EMAIL_VERIFIED': False, "PASSWORDLESS_USER_MARK_EMAIL_VERIFIED": False,
'PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME': 'email_verified', "PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME": "email_verified",
# Marks itself as verified the first time a user completes auth via token. # Marks itself as verified the first time a user completes auth via token.
# Automatically unmarks itself if mobile number is changed. # Automatically unmarks itself if mobile number is changed.
'PASSWORDLESS_USER_MARK_MOBILE_VERIFIED': False, "PASSWORDLESS_USER_MARK_MOBILE_VERIFIED": False,
'PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME': 'mobile_verified', "PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME": "mobile_verified",
# The email the callback token is sent from # The email the callback token is sent from
'PASSWORDLESS_EMAIL_NOREPLY_ADDRESS': None, "PASSWORDLESS_EMAIL_NOREPLY_ADDRESS": None,
# The email subject # The email subject
'PASSWORDLESS_EMAIL_SUBJECT': "Your Login Token", "PASSWORDLESS_EMAIL_SUBJECT": "Your Login Token",
# A plaintext email message overridden by the html message. Takes one string. # A plaintext email message overridden by the html message. Takes one string.
'PASSWORDLESS_EMAIL_PLAINTEXT_MESSAGE': "Enter this token to sign in: %s", "PASSWORDLESS_EMAIL_PLAINTEXT_MESSAGE": "Enter this token to sign in: %s",
# The email template name. # The email template name.
'PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE_NAME': "passwordless_default_token_email.html", "PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE_NAME": "passwordless_default_token_email.html",
# Your twilio number that sends the callback tokens. # Your twilio number that sends the callback tokens.
'PASSWORDLESS_MOBILE_NOREPLY_NUMBER': None, "PASSWORDLESS_MOBILE_NOREPLY_NUMBER": None,
# The message sent to mobile users logging in. Takes one string. # The message sent to mobile users logging in. Takes one string.
'PASSWORDLESS_MOBILE_MESSAGE': "Use this code to log in: %s", "PASSWORDLESS_MOBILE_MESSAGE": "Your SARLINK OTP is: %s \nPlease do not share this code with anyone.",
# Registers previously unseen aliases as new users. # Registers previously unseen aliases as new users.
'PASSWORDLESS_REGISTER_NEW_USERS': False, "PASSWORDLESS_REGISTER_NEW_USERS": False,
# Suppresses actual SMS for testing # Suppresses actual SMS for testing
'PASSWORDLESS_TEST_SUPPRESSION': False, "PASSWORDLESS_TEST_SUPPRESSION": False,
# Context Processors for Email Template # Context Processors for Email Template
'PASSWORDLESS_CONTEXT_PROCESSORS': [], "PASSWORDLESS_CONTEXT_PROCESSORS": [],
# The verification email subject # The verification email subject
'PASSWORDLESS_EMAIL_VERIFICATION_SUBJECT': "Your Verification Token", "PASSWORDLESS_EMAIL_VERIFICATION_SUBJECT": "Your Verification Token",
# A plaintext verification email message overridden by the html message. Takes one string. # A plaintext verification email message overridden by the html message. Takes one string.
'PASSWORDLESS_EMAIL_VERIFICATION_PLAINTEXT_MESSAGE': "Enter this verification code: %s", "PASSWORDLESS_EMAIL_VERIFICATION_PLAINTEXT_MESSAGE": "Enter this verification code: %s",
# The verification email template name. # The verification email template name.
'PASSWORDLESS_EMAIL_VERIFICATION_TOKEN_HTML_TEMPLATE_NAME': "passwordless_default_verification_token_email.html", "PASSWORDLESS_EMAIL_VERIFICATION_TOKEN_HTML_TEMPLATE_NAME": "passwordless_default_verification_token_email.html",
# The message sent to mobile users logging in. Takes one string. # The message sent to mobile users logging in. Takes one string.
'PASSWORDLESS_MOBILE_VERIFICATION_MESSAGE': "Enter this verification code: %s", "PASSWORDLESS_MOBILE_VERIFICATION_MESSAGE": "Enter this verification code: %s",
# Automatically send verification email or sms when a user changes their alias. # Automatically send verification email or sms when a user changes their alias.
'PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN': False, "PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN": False,
} }
# List of settings that may be in string import notation. # List of settings that may be in string import notation.
IMPORT_STRINGS = ( IMPORT_STRINGS = (
'PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE', "PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE",
'PASSWORDLESS_CONTEXT_PROCESSORS', "PASSWORDLESS_CONTEXT_PROCESSORS",
) )
api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) #type: ignore api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) # type: ignore