import urllib.parse from mimetypes import guess_all_extensions from django.conf import settings from django.db import models from django.db.models import TextChoices from django.urls import reverse from oauth2_provider.settings import oauth2_settings from apps.core.models import BaseModel, BasePGModel from apps.pg.constans import StateChoices import uuid from utils.wallet_client import deposit_to_user_wallet_submit, withdraw_from_user_wallet_verify, \ deposit_to_user_wallet_verify from utils.pna import PNAClient from django.utils.translation import gettext_lazy as _ class GatewayChoise(TextChoices): FAKE = "fake", _("fake") PNA = 'pna' , _('pna') class PaymentRequest(models.Model): id = None uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True) application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT, related_name='+', null=True, blank=True) gateway = models.CharField(max_length=255, choices=GatewayChoise.choices) state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED) user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) amount = models.PositiveBigIntegerField(null=True, blank=True) phone_number = models.CharField(max_length=255, null=True, blank=True) callback_url = models.CharField(max_length=255, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True, db_index=True) updated_at = models.DateTimeField(auto_now=True, db_index=True) def get_gateway_tools(self): if self.gateway == 'pna': return self.pna def set_gateway(self, gateway): self.gateway = gateway if gateway == 'pna': pna = PNA.objects.create() pna.payment_request = self if gateway == 'fake': pass self.save() # TODO: complete and use this def change_state(self, from_state, to_state, error_message=None, **data): if self.state == from_state: updated = PaymentRequest.objects.filter(pk=self.pk, state=from_state).update( state=to_state, **data ) if updated: # double check in db level self.state = to_state else: raise Exception(error_message) else: raise Exception(error_message) def charge_wallet_balance(self): if self.state in [StateChoices.IPG_SUCCESS]: to_pending = PaymentRequest.objects.filter( pk=self.pk, state=StateChoices.IPG_SUCCESS.value, ).update( state=StateChoices.WALLET_SUBMIT_PENDING.value) if not to_pending: raise Exception('Cannot charge balance because state') data = { "uuid": str(self.uuid), "payee_id": str(self.user.uuid), "payee_type": 1, "amount": self.amount, "details": { 'description': "charge via IPG", 'reference_id': str(self.uuid), 'application_details_url': '' }, } submit_response = deposit_to_user_wallet_submit(data) if not submit_response.uuid: raise Exception('Failed to submit payment in wallet') to_success = PaymentRequest.objects.filter( pk=self.pk, state=StateChoices.WALLET_SUBMIT_PENDING.value, ).update( state=StateChoices.WALLET_SUBMIT_SUCCESS.value) if not to_success: raise Exception('Cannot charge balance because state') verify_response = deposit_to_user_wallet_verify(self.uuid) if not verify_response.uuid or verify_response.state != 5: to_pay_failed = PaymentRequest.objects.filter( pk=self.pk, state=StateChoices.WALLET_SUBMIT_SUCCESS.value ).update( state=StateChoices.FAILED.value, ) raise Exception('Failed to verify payment in wallet') to_pay_success = PaymentRequest.objects.filter( pk=self.pk, state=StateChoices.WALLET_SUBMIT_SUCCESS.value ).update( state=StateChoices.SUCCESS.value, ) if not to_pay_success: raise Exception('Failed to update state payment') return bool(to_pay_success) class PNA(BasePGModel): payment_request = models.OneToOneField(PaymentRequest, on_delete=models.PROTECT, related_name='pna') details = models.JSONField(null=True, blank=True, default=dict) pna_token = models.CharField(null=True, blank=True, max_length=255, db_index=True) pna_order_id = models.IntegerField(null=True, blank=True, db_index=True) pna_originator = models.CharField(null=True, blank=True, max_length=255) pna_status = models.IntegerField(choices=StateChoices.choices, null=True, blank=True, db_index=True) pna_status_code = models.IntegerField(null=True, blank=True) pna_status_message = models.CharField(null=True, blank=True, max_length=255) client = PNAClient() def make_request(self): amount = self.payment_request.amount description = 'from addwin ipg' callback_url = urllib.parse.urljoin( settings.IPG_BASE_PUBLIC_URL, reverse("pg:payment-confirm-view", kwargs={"uuid": self.payment_request.uuid})) order_id = self.id phone_number = self.payment_request.phone_number response = self.client.request_normal_sale(amount, description, callback_url, order_id, phone_number) self.pna_status_code = response['status'] self.pna_status_message = response.get('title') if response['status'] == 0: self.pna_token = response['token'] self.save() def get_redirect_url(self): if self.pna_token: return self.client.pg_base_url.format(token=self.pna_token) def confirm_request(self): pna_token = self.pna_token response = self.client.request_normal_sale_confirm(pna_token) # response = {'status': 0, 'cardNumberMasked': '504172******9210', 'rrn': 511629210215, 'appToken': 4101434578524, 'token': 'NDEwMTQzNDU3ODUyNA=='} self.details = response if response['status'] == 0: self.pna_status = StateChoices.SUCCESS.value self.save() def reverse_request(self): raise NotImplementedError()