ipg/apps/pg/models.py
Sayyid Hamid Mahdavi f0edf10f53 fake gateway
2025-05-31 13:36:25 +03:30

228 lines
8.6 KiB
Python

import urllib.parse
from mimetypes import guess_all_extensions
from django.conf import settings
from django.contrib import messages
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')
# TODO: use generic relation
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 == GatewayChoise.PNA:
return PNA.objects.get(payment_request=self)
if self.gateway == GatewayChoise.FAKE:
return Fake.objects.get(payment_request=self)
def set_gateway(self, data):
if 'fake' in data:
self.gateway = GatewayChoise.FAKE
fake = Fake.objects.create(payment_request=self)
fake.payment_request = self
elif 'pna' in data:
self.gateway = GatewayChoise.PNA
pna = PNA.objects.create(payment_request=self)
pna.payment_request = self
else:
raise Exception('Unknown gateway')
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_pg_page_url(self):
if self.pna_token:
return self.client.pg_base_url.format(token=self.pna_token)
def confirm_request(self, request=None):
print(dict(request.POST))
post_dict = dict(request.POST)
if post_dict.get('status') in [0, '0', [0], ['0']]:
try:
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=='}
# post = <QueryDict: {'Token': ['NDEwMTI0Nzc0MDMzMA=='], 'OrderId': ['7'], 'TerminalNo': ['31127029'], 'RRN': ['511629156251'], 'status': ['0'], 'TspToken': ['00000000-0000-0000-0000-000000000000'], 'HashCardNumber': ['6008617E6BAB76997499446356F5C8B60288445650DD551FDF32007BCF2B14AF'], 'Amount': ['21,000'], 'SwAmount': [''], 'STraceNo': ['123232']}>
# post = <QueryDict: {'Token': ['NDI2MDY3ODI0MTg1OA=='], 'OrderId': ['47'], 'status': ['-138']}>
self.details = response
if response['status'] == 0:
self.pna_status = StateChoices.SUCCESS.value
self.save()
self.payment_request.charge_wallet_balance()
messages.success(request, 'شارژ انجام شد')
except Exception as e:
print(e)
messages.error(request, str(e))
else:
print(request.POST)
messages.error(request, "نتیجه تراکنش از سمت درگاه دریافت نشد.")
def reverse_request(self):
raise NotImplementedError()
class Fake(BasePGModel):
payment_request = models.OneToOneField(PaymentRequest, on_delete=models.PROTECT, related_name='+')
def make_request(self):
pass
def get_pg_page_url(self):
return reverse('pg:payment-confirm-view', kwargs={'uuid': self.payment_request.uuid})
def confirm_request(self, request=None):
self.payment_request.charge_wallet_balance()
messages.success(request, 'شارژ انجام شد')
self.save()
def reverse_request(self):
raise NotImplementedError()