separate payment gateways model
This commit is contained in:
parent
12a490393b
commit
b37a11fdc2
12 changed files with 268 additions and 103 deletions
|
|
@ -17,6 +17,22 @@ class BaseModel(models.Model):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class BasePGModel(models.Model):
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True, db_index=True)
|
||||||
|
|
||||||
|
def make_request(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def confirm_request(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def reverse_request(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from .models import ChargeRequest
|
from .models import PaymentRequest
|
||||||
|
|
||||||
|
|
||||||
class ChargeRequestAdmin(admin.ModelAdmin):
|
class PaymentRequestAdmin(admin.ModelAdmin):
|
||||||
list_display = ['uuid', 'id']
|
list_display = ['uuid', 'amount', 'user_id']
|
||||||
|
|
||||||
admin.site.register(ChargeRequest, ChargeRequestAdmin)
|
admin.site.register(PaymentRequest, PaymentRequestAdmin)
|
||||||
|
|
@ -4,12 +4,15 @@ from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
class StateChoices(IntegerChoices):
|
class StateChoices(IntegerChoices):
|
||||||
CREATED = 1 , _('created')
|
CREATED = 1 , _('created')
|
||||||
DELAYED = 2 , _('delayed') # delayed as user wish
|
IPG_PENDING = 2 , _('ipg pending')
|
||||||
PENDING = 3 , _('pending') # wait for external service response (or action)
|
IPG_SUCCESS = 3 , _('ipg success')
|
||||||
INCOMPLETE = 4 , _('incomplete') # started and wait for internal progress to complete
|
|
||||||
SUCCESS = 5 , _('success')
|
WALLET_SUBMIT_PENDING = 11 , _('wallet submit pending')
|
||||||
FAILED = 6 , _('failed')
|
WALLET_SUBMIT_SUCCESS = 12 , _('wallet submit success')
|
||||||
EXPECTED_FAILURE = 7 , _('expected_failure') # no exact data available but guessed to be failed
|
|
||||||
|
FAILED = 18 , _('failed')
|
||||||
|
EXPECTED_FAILURE = 19 , _('expected failure') # no exact data available but guessed to be failed
|
||||||
|
SUCCESS = 20 , _('success')
|
||||||
|
|
||||||
|
|
||||||
class TypeChoices(IntegerChoices):
|
class TypeChoices(IntegerChoices):
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from .models import ChargeRequest
|
from .models import PaymentRequest
|
||||||
|
|
||||||
|
|
||||||
class ChargeRequestForm(forms.ModelForm):
|
class PaymentRequestForm(forms.ModelForm):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ChargeRequest
|
model = PaymentRequest
|
||||||
fields = []
|
fields = []
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Generated by Django 5.1.7 on 2025-05-26 15:28
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pg', '0002_alter_chargerequest_callback_url'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PaymentRequest',
|
||||||
|
fields=[
|
||||||
|
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('gateway', models.CharField(choices=[('fake', 'fake'), ('pna', 'pna')], max_length=255)),
|
||||||
|
('state', models.IntegerField(choices=[(1, 'created'), (2, 'ipg pending'), (3, 'ipg success'), (11, 'wallet submit pending'), (12, 'wallet submit success'), (18, 'failed'), (19, 'expected failure'), (20, 'success')], default=1)),
|
||||||
|
('amount', models.PositiveBigIntegerField(blank=True, null=True)),
|
||||||
|
('callback_url', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
|
||||||
|
('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='PNA',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('details', models.JSONField(blank=True, default=dict, null=True)),
|
||||||
|
('pna_token', models.CharField(blank=True, db_index=True, max_length=255, null=True)),
|
||||||
|
('pna_order_id', models.IntegerField(blank=True, db_index=True, null=True)),
|
||||||
|
('pna_originator', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('pna_status', models.IntegerField(blank=True, choices=[(1, 'created'), (2, 'ipg pending'), (3, 'ipg success'), (11, 'wallet submit pending'), (12, 'wallet submit success'), (18, 'failed'), (19, 'expected failure'), (20, 'success')], db_index=True, null=True)),
|
||||||
|
('pna_status_code', models.IntegerField(blank=True, null=True)),
|
||||||
|
('pna_status_message', models.CharField(blank=True, max_length=255, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
|
||||||
|
('payment_request', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='pg.paymentrequest')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.DeleteModel(
|
||||||
|
name='ChargeRequest',
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 5.1.7 on 2025-05-26 16:52
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('pg', '0003_paymentrequest_pna_delete_chargerequest'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='paymentrequest',
|
||||||
|
name='phone_number',
|
||||||
|
field=models.CharField(blank=True, max_length=255, null=True),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='pna',
|
||||||
|
name='payment_request',
|
||||||
|
field=models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='pna', to='pg.paymentrequest'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,45 +1,68 @@
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
from mimetypes import guess_all_extensions
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import TextChoices
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from oauth2_provider.settings import oauth2_settings
|
from oauth2_provider.settings import oauth2_settings
|
||||||
|
|
||||||
from apps.core.models import BaseModel
|
from apps.core.models import BaseModel, BasePGModel
|
||||||
from apps.pg.constans import StateChoices
|
from apps.pg.constans import StateChoices
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from utils.wallet_client import deposit_to_user_wallet_submit, withdraw_from_user_wallet_verify, \
|
from utils.wallet_client import deposit_to_user_wallet_submit, withdraw_from_user_wallet_verify, \
|
||||||
deposit_to_user_wallet_verify
|
deposit_to_user_wallet_verify
|
||||||
|
|
||||||
from utils.pna import PNAClient
|
from utils.pna import PNAClient
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
class ChargeRequest(models.Model):
|
class GatewayChoise(TextChoices):
|
||||||
uuid = models.UUIDField(editable=False, default=uuid.uuid4, unique=True, db_index=True)
|
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,
|
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL, on_delete=models.PROTECT,
|
||||||
related_name='+', null=True, blank=True)
|
related_name='+', null=True, blank=True)
|
||||||
|
|
||||||
|
gateway = models.CharField(max_length=255, choices=GatewayChoise.choices)
|
||||||
|
|
||||||
state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED)
|
state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED)
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||||
amount = models.PositiveBigIntegerField(null=True, blank=True)
|
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)
|
callback_url = models.CharField(max_length=255, null=True, blank=True)
|
||||||
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)
|
|
||||||
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||||
updated_at = models.DateTimeField(auto_now=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
|
# TODO: complete and use this
|
||||||
def change_state(self, from_state, to_state, error_message=None, **data):
|
def change_state(self, from_state, to_state, error_message=None, **data):
|
||||||
if self.state == from_state:
|
if self.state == from_state:
|
||||||
updated = ChargeRequest.objects.filter(pk=self.pk, state=from_state).update(
|
updated = PaymentRequest.objects.filter(pk=self.pk, state=from_state).update(
|
||||||
state=to_state,
|
state=to_state,
|
||||||
**data
|
**data
|
||||||
)
|
)
|
||||||
|
|
@ -50,44 +73,13 @@ class ChargeRequest(models.Model):
|
||||||
else:
|
else:
|
||||||
raise Exception(error_message)
|
raise Exception(error_message)
|
||||||
|
|
||||||
def request_sale(self):
|
|
||||||
pna_client = PNAClient()
|
|
||||||
|
|
||||||
description = 'from addwin ipg'
|
|
||||||
pna_callback_url = urllib.parse.urljoin(
|
|
||||||
settings.IPG_BASE_PUBLIC_URL,
|
|
||||||
reverse("pg:charge-confirm-view", kwargs={"uuid": self.uuid}))
|
|
||||||
response = pna_client.request_normal_sale(self.amount, description, pna_callback_url, order_id=self.id)
|
|
||||||
|
|
||||||
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_pna_redirect_url(self):
|
|
||||||
if self.pna_token:
|
|
||||||
return PNAClient.pg_base_url.format(token=self.pna_token)
|
|
||||||
|
|
||||||
|
|
||||||
def confirm_sale(self):
|
|
||||||
pna_client = PNAClient()
|
|
||||||
description = 'from addwin ipg'
|
|
||||||
response = pna_client.request_normal_sale_confirm(self.pna_token)
|
|
||||||
# response = {'status': 0, 'cardNumberMasked': '504172******9210', 'rrn': 511629210215, 'appToken': 4101434578524, 'token': 'NDEwMTQzNDU3ODUyNA=='}
|
|
||||||
|
|
||||||
if response['status'] == 0:
|
|
||||||
self.pna_status = StateChoices.SUCCESS.value
|
|
||||||
|
|
||||||
def charge_wallet_balance(self):
|
def charge_wallet_balance(self):
|
||||||
if self.state in [StateChoices.CREATED]:
|
if self.state in [StateChoices.IPG_SUCCESS]:
|
||||||
to_pending = ChargeRequest.objects.filter(
|
to_pending = PaymentRequest.objects.filter(
|
||||||
pk=self.pk,
|
pk=self.pk,
|
||||||
state=StateChoices.CREATED.value,
|
state=StateChoices.IPG_SUCCESS.value,
|
||||||
).update(
|
).update(
|
||||||
state=StateChoices.PENDING.value)
|
state=StateChoices.WALLET_SUBMIT_PENDING.value)
|
||||||
if not to_pending:
|
if not to_pending:
|
||||||
raise Exception('Cannot charge balance because state')
|
raise Exception('Cannot charge balance because state')
|
||||||
|
|
||||||
|
|
@ -107,14 +99,29 @@ class ChargeRequest(models.Model):
|
||||||
if not submit_response.uuid:
|
if not submit_response.uuid:
|
||||||
raise Exception('Failed to submit payment in wallet')
|
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)
|
verify_response = deposit_to_user_wallet_verify(self.uuid)
|
||||||
|
|
||||||
if not verify_response.uuid or verify_response.state != 5:
|
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')
|
raise Exception('Failed to verify payment in wallet')
|
||||||
|
|
||||||
to_pay_success = ChargeRequest.objects.filter(
|
to_pay_success = PaymentRequest.objects.filter(
|
||||||
pk=self.pk,
|
pk=self.pk,
|
||||||
state=StateChoices.PENDING.value
|
state=StateChoices.WALLET_SUBMIT_SUCCESS.value
|
||||||
).update(
|
).update(
|
||||||
state=StateChoices.SUCCESS.value,
|
state=StateChoices.SUCCESS.value,
|
||||||
)
|
)
|
||||||
|
|
@ -123,3 +130,56 @@ class ChargeRequest(models.Model):
|
||||||
raise Exception('Failed to update state payment')
|
raise Exception('Failed to update state payment')
|
||||||
|
|
||||||
return bool(to_pay_success)
|
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import ChargeRequest
|
from .models import PaymentRequest
|
||||||
|
|
||||||
|
|
||||||
class ChargeRequestSerializer(serializers.ModelSerializer):
|
class PaymentRequestSerializer(serializers.ModelSerializer):
|
||||||
# uuid = serializers.UUIDField(required=True)
|
# uuid = serializers.UUIDField(required=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ChargeRequest
|
model = PaymentRequest
|
||||||
fields = (
|
fields = (
|
||||||
'uuid',
|
'uuid',
|
||||||
'user',
|
'user',
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from .views import ChargeRequestView, ChargeRequestCreateApiView, ChargeConfirmView
|
from .views import PaymentRequestView, PaymentRequestCreateApiView, PaymentConfirmView
|
||||||
|
|
||||||
app_name = "pg"
|
app_name = "pg"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('page/charge/<uuid>/', ChargeRequestView.as_view(), name='charge-request-view'),
|
path('page/charge/<uuid>/', PaymentRequestView.as_view(), name='payment-request-view'),
|
||||||
path('page/pna_callback/<uuid>/', ChargeConfirmView.as_view(), name='charge-confirm-view'),
|
path('page/pna_callback/<uuid>/', PaymentConfirmView.as_view(), name='payment-confirm-view'),
|
||||||
path('api/charge_create/', ChargeRequestCreateApiView.as_view(), name='charge-request-create-api-view'),
|
path('api/charge_create/', PaymentRequestCreateApiView.as_view(), name='charge-request-create-api-view'),
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -10,31 +10,31 @@ from oauth2_provider.contrib.rest_framework import TokenHasScope, IsAuthenticate
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
from .forms import ChargeRequestForm
|
from .forms import PaymentRequestForm
|
||||||
from .models import ChargeRequest
|
from .models import PaymentRequest, GatewayChoise
|
||||||
from .serializers import ChargeRequestSerializer
|
from .serializers import PaymentRequestSerializer
|
||||||
from .constans import StateChoices
|
from .constans import StateChoices
|
||||||
from ..gooyal_oauth2.utils import get_application
|
from ..gooyal_oauth2.utils import get_application
|
||||||
|
|
||||||
|
|
||||||
class ChargeRequestCreateApiView(generics.CreateAPIView):
|
class PaymentRequestCreateApiView(generics.CreateAPIView):
|
||||||
permission_classes = [IsAuthenticatedOrTokenHasScope]
|
permission_classes = [IsAuthenticatedOrTokenHasScope]
|
||||||
serializer_class = ChargeRequestSerializer
|
serializer_class = PaymentRequestSerializer
|
||||||
required_scopes = ['wallet.wallet:get_balance'] # TODO: is it good scope?
|
required_scopes = ['wallet.wallet:get_balance'] # TODO: is it good scope?
|
||||||
model = ChargeRequest
|
model = PaymentRequest
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
user = self.request.user
|
user = self.request.user
|
||||||
application = get_application(self.request)
|
application = get_application(self.request)
|
||||||
charge_request: ChargeRequest = serializer.save(user=user, application=application)
|
charge_request: PaymentRequest = serializer.save(user=user, application=application)
|
||||||
|
|
||||||
# TODO: web view
|
# TODO: web view
|
||||||
# charge_request.charge_wallet_balance()
|
# charge_request.charge_wallet_balance()
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class ChargeRequestView(UpdateView):
|
class PaymentRequestView(UpdateView):
|
||||||
template_name = 'pg/charge-request.html'
|
template_name = 'pg/payment-request.html'
|
||||||
form_class = ChargeRequestForm
|
form_class = PaymentRequestForm
|
||||||
# pk_url_kwarg = 'uuid'
|
# pk_url_kwarg = 'uuid'
|
||||||
slug_field = 'uuid'
|
slug_field = 'uuid'
|
||||||
slug_url_kwarg = 'uuid'
|
slug_url_kwarg = 'uuid'
|
||||||
|
|
@ -42,36 +42,42 @@ class ChargeRequestView(UpdateView):
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
# return ChargeRequest.objects.all()
|
# return ChargeRequest.objects.all()
|
||||||
# return Charge.objects.filter(state__in=[StateChoices.CREATED.value, StateChoices.SUCCESS.value])
|
# return Charge.objects.filter(state__in=[StateChoices.CREATED.value, StateChoices.SUCCESS.value])
|
||||||
return ChargeRequest.objects.filter(state__in=[StateChoices.CREATED.value])
|
return PaymentRequest.objects.filter(state__in=[StateChoices.CREATED.value])
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
self.object = form.save()
|
self.object = form.save()
|
||||||
charge_request: ChargeRequest = self.get_object()
|
charge_request: PaymentRequest = self.get_object()
|
||||||
|
|
||||||
|
if 'fake' in form.data:
|
||||||
|
print('fake')
|
||||||
|
|
||||||
|
if 'pna' in form.data:
|
||||||
|
charge_request.set_gateway('pna')
|
||||||
|
|
||||||
|
tool = charge_request.get_gateway_tools()
|
||||||
|
|
||||||
|
|
||||||
# try:
|
# try:
|
||||||
charge_request.request_sale()
|
tool.make_request()
|
||||||
pna_redirect_url = charge_request.get_pna_redirect_url()
|
redirect_url = tool.get_redirect_url()
|
||||||
if pna_redirect_url:
|
if redirect_url:
|
||||||
return HttpResponseRedirect(pna_redirect_url)
|
return HttpResponseRedirect(redirect_url)
|
||||||
else:
|
else:
|
||||||
messages.error(self.request, 'can not redirect to pna')
|
messages.error(self.request, 'can not redirect to payment gateway')
|
||||||
# TODO: redirect to error page
|
# TODO: redirect to error page
|
||||||
return HttpResponseRedirect(reverse('pg:charge-confirm-view', kwargs={'uuid': charge_request.uuid}))
|
return HttpResponseRedirect(reverse('pg:payment-confirm-view', kwargs={'uuid': charge_request.uuid}))
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return self.object.callback_url
|
|
||||||
# return reverse('pg:charge-request-view', kwargs={'pk': self.object.pk})
|
|
||||||
|
|
||||||
@method_decorator(csrf_exempt, name='dispatch')
|
@method_decorator(csrf_exempt, name='dispatch')
|
||||||
class ChargeConfirmView(UpdateView):
|
class PaymentConfirmView(UpdateView):
|
||||||
template_name = 'pg/charge-confirm.html'
|
template_name = 'pg/payment-confirm.html'
|
||||||
form_class = ChargeRequestForm
|
form_class = PaymentRequestForm
|
||||||
# pk_url_kwarg = 'uuid'
|
# pk_url_kwarg = 'uuid'
|
||||||
slug_field = 'uuid'
|
slug_field = 'uuid'
|
||||||
slug_url_kwarg = 'uuid'
|
slug_url_kwarg = 'uuid'
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return ChargeRequest.objects.all()
|
return PaymentRequest.objects.all()
|
||||||
# return Charge.objects.filter(state__in=[StateChoices.CREATED.value, StateChoices.SUCCESS.value])
|
# return Charge.objects.filter(state__in=[StateChoices.CREATED.value, StateChoices.SUCCESS.value])
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
|
|
@ -99,16 +105,16 @@ class ChargeConfirmView(UpdateView):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
||||||
# 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': ['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']}>
|
# post = <QueryDict: {'Token': ['NDI2MDY3ODI0MTg1OA=='], 'OrderId': ['47'], 'status': ['-138']}>
|
||||||
print(dict(request.POST))
|
print(dict(request.POST))
|
||||||
post_dict = dict(request.POST)
|
post_dict = dict(request.POST)
|
||||||
if post_dict.get('status') in [0, '0', [0], ['0']]:
|
if post_dict.get('status') in [0, '0', [0], ['0']]:
|
||||||
try:
|
try:
|
||||||
charge_request: ChargeRequest = self.get_object()
|
payment_request: PaymentRequest = self.get_object()
|
||||||
charge_request.charge_wallet_balance()
|
tool = payment_request.get_gateway_tools()
|
||||||
charge_request.confirm_sale()
|
tool.confirm_request()
|
||||||
|
payment_request.charge_wallet_balance()
|
||||||
messages.success(self.request, 'شارژ انجام شد')
|
messages.success(self.request, 'شارژ انجام شد')
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -20,11 +20,16 @@
|
||||||
{# </div>#}
|
{# </div>#}
|
||||||
</div>
|
</div>
|
||||||
<h6 class="h6">پرداخت با:</h6>
|
<h6 class="h6">پرداخت با:</h6>
|
||||||
<div class="row">
|
<div class="row justify-content-center">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<button type="submit" class="btn mx-auto d-block">
|
{# <div class="">#}
|
||||||
<img class="img-thumbnail" width="128" height="128" src="{% static "image/pna.png" %}"/>
|
<button type="submit" class="btn mx-auto center-block" name="pna">
|
||||||
</button>
|
<img class="img-thumbnail" width="128" height="128" src="{% static "image/pna.png" %}"/>
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn mx-auto center-block" name="fake">
|
||||||
|
<img class="img-thumbnail" width="128" height="128" src="{% static "image/fake.png" %}"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Loading…
Add table
Reference in a new issue