From f8253d572d91672bb0d4aa3af0c0ee3c1678b4ff Mon Sep 17 00:00:00 2001 From: i701 Date: Thu, 10 Apr 2025 21:50:21 +0500 Subject: [PATCH] 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. --- billing/views.py | 76 ++++++++++++++++-------------- djangopasswordlessknox/settings.py | 72 ++++++++++------------------ 2 files changed, 67 insertions(+), 81 deletions(-) diff --git a/billing/views.py b/billing/views.py index 5fc9842..08b2619 100644 --- a/billing/views.py +++ b/billing/views.py @@ -104,57 +104,57 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView): lookup_field = "pk" def update(self, request, *args, **kwargs): + # TODO: Fix check for success payment + payment = self.get_object() data = request.data 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") - payment_id = kwargs.get("pk") if not method: return Response( {"message": "method is required. 'WALLET' or 'TRANSFER'"}, status=status.HTTP_400_BAD_REQUEST, ) - if not payment_id: - return Response( - {"message": "payment_id is required."}, - status=status.HTTP_400_BAD_REQUEST, + + devices = payment.devices.all() + payment_status = False + 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: - 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) - + if payment_status: # Update devices expiry_date = timezone.now() + timedelta(days=30 * payment.number_of_months) devices.update( is_active=True, expiry_date=expiry_date, has_a_pending_payment=False ) - return Response({"message": "Payment verified successfully."}) - - except Payment.DoesNotExist: return Response( - {"message": "Payment not found."}, status=status.HTTP_404_NOT_FOUND - ) - 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 + {"message": f"Payment verified successfully using [{method}]."} ) - def process_wallet_payment(self, user, payment, amount): - print("processing wallet payment") - print(user, amount) - if user.wallet_balance < amount: + return Response({"message": "Payment verified successfully using [{method}]."}) + + def process_wallet_payment(self, user, payment): + print("processing wallet payment...") + print(user, payment.amount) + if user.wallet_balance < payment.amount: return Response( {"message": "Insufficient funds in wallet."}, status=status.HTTP_400_BAD_REQUEST, @@ -165,10 +165,12 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView): payment.method = "WALLET" payment.save() - user.wallet_balance -= amount + user.wallet_balance -= payment.amount 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( f"{config('PAYMENT_VERIFY_BASE_URL')}/verify-payment", json=data, @@ -177,7 +179,11 @@ class VerifyPaymentView(StaffEditorPermissionMixin, generics.UpdateAPIView): response.raise_for_status() print(response.json()) 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): diff --git a/djangopasswordlessknox/settings.py b/djangopasswordlessknox/settings.py index b8d1837..edebcfd 100644 --- a/djangopasswordlessknox/settings.py +++ b/djangopasswordlessknox/settings.py @@ -1,79 +1,59 @@ from django.conf import settings from rest_framework.settings import APISettings -USER_SETTINGS = getattr(settings, 'PASSWORDLESS_AUTH', None) +USER_SETTINGS = getattr(settings, "PASSWORDLESS_AUTH", None) DEFAULTS = { # 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 - 'PASSWORDLESS_TOKEN_EXPIRE_TIME': 15 * 60, - + "PASSWORDLESS_TOKEN_EXPIRE_TIME": 15 * 60, # The user's email field name - 'PASSWORDLESS_USER_EMAIL_FIELD_NAME': 'email', - + "PASSWORDLESS_USER_EMAIL_FIELD_NAME": "email", # 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. # Automatically unmarks itself if email is changed. - 'PASSWORDLESS_USER_MARK_EMAIL_VERIFIED': False, - 'PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME': 'email_verified', - + "PASSWORDLESS_USER_MARK_EMAIL_VERIFIED": False, + "PASSWORDLESS_USER_EMAIL_VERIFIED_FIELD_NAME": "email_verified", # Marks itself as verified the first time a user completes auth via token. # Automatically unmarks itself if mobile number is changed. - 'PASSWORDLESS_USER_MARK_MOBILE_VERIFIED': False, - 'PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME': 'mobile_verified', - + "PASSWORDLESS_USER_MARK_MOBILE_VERIFIED": False, + "PASSWORDLESS_USER_MOBILE_VERIFIED_FIELD_NAME": "mobile_verified", # The email the callback token is sent from - 'PASSWORDLESS_EMAIL_NOREPLY_ADDRESS': None, - + "PASSWORDLESS_EMAIL_NOREPLY_ADDRESS": None, # 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. - '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. - '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. - 'PASSWORDLESS_MOBILE_NOREPLY_NUMBER': None, - + "PASSWORDLESS_MOBILE_NOREPLY_NUMBER": None, # 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. - 'PASSWORDLESS_REGISTER_NEW_USERS': False, - + "PASSWORDLESS_REGISTER_NEW_USERS": False, # Suppresses actual SMS for testing - 'PASSWORDLESS_TEST_SUPPRESSION': False, - + "PASSWORDLESS_TEST_SUPPRESSION": False, # Context Processors for Email Template - 'PASSWORDLESS_CONTEXT_PROCESSORS': [], - + "PASSWORDLESS_CONTEXT_PROCESSORS": [], # 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. - '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. - '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. - '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. - 'PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN': False, - + "PASSWORDLESS_AUTO_SEND_VERIFICATION_TOKEN": False, } # List of settings that may be in string import notation. IMPORT_STRINGS = ( - 'PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE', - 'PASSWORDLESS_CONTEXT_PROCESSORS', + "PASSWORDLESS_EMAIL_TOKEN_HTML_TEMPLATE", + "PASSWORDLESS_CONTEXT_PROCESSORS", ) -api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) #type: ignore +api_settings = APISettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS) # type: ignore