bugfix for oidc
This commit is contained in:
parent
b1fa50f007
commit
c32d9b75c2
16 changed files with 42 additions and 630 deletions
|
|
@ -1,4 +1,33 @@
|
|||
from oauth2_provider.settings import OAuth2ProviderSettings, USER_SETTINGS, DEFAULTS, IMPORT_STRINGS, MANDATORY
|
||||
from oauth2_provider.settings import OAuth2ProviderSettings, USER_SETTINGS, DEFAULTS, IMPORT_STRINGS, MANDATORY, \
|
||||
_PhonyHttpRequest
|
||||
|
||||
from django.http import HttpRequest
|
||||
from django.urls import reverse
|
||||
from oauthlib.common import Request
|
||||
|
||||
|
||||
class GooyalOAuth2Settings(OAuth2ProviderSettings):
|
||||
def oidc_issuer(self, request):
|
||||
"""
|
||||
Helper function to get the OIDC issuer URL, either from the settings
|
||||
or constructing it from the passed request.
|
||||
|
||||
If only an oauthlib request is available, a dummy django request is
|
||||
built from that and used to generate the URL.
|
||||
"""
|
||||
if self.OIDC_ISS_ENDPOINT:
|
||||
return self.OIDC_ISS_ENDPOINT
|
||||
if isinstance(request, HttpRequest):
|
||||
django_request = request
|
||||
elif isinstance(request, Request):
|
||||
django_request = _PhonyHttpRequest()
|
||||
django_request.META = request.headers
|
||||
if request.headers.get("X_DJANGO_OAUTH_TOOLKIT_SECURE", False):
|
||||
django_request._scheme = "https"
|
||||
else:
|
||||
raise TypeError("request must be a django or oauthlib request: got %r" % request)
|
||||
abs_url = django_request.build_absolute_uri(reverse("gooyal_oauth2:oidc-connect-discovery-info"))
|
||||
return abs_url[: -len("/.well-known/openid-configuration")]
|
||||
|
||||
GOOYAL_DEFAULTS = {
|
||||
# Resource Server with Token Introspection additions
|
||||
|
|
@ -17,4 +46,4 @@ DEFAULTS.update(GOOYAL_DEFAULTS)
|
|||
IMPORT_STRINGS = IMPORT_STRINGS + GOOYAL_IMPORT_STRINGS
|
||||
MANDATORY = MANDATORY + GOOYAL_MANDATORY
|
||||
|
||||
oauth2_settings = OAuth2ProviderSettings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS, MANDATORY)
|
||||
oauth2_settings = GooyalOAuth2Settings(USER_SETTINGS, DEFAULTS, IMPORT_STRINGS, MANDATORY)
|
||||
|
|
|
|||
|
|
@ -74,3 +74,6 @@ class OAuth2Validator(BaseOAuth2Validator): # pylint: disable=w0223
|
|||
claims.update(add)
|
||||
|
||||
return claims
|
||||
|
||||
def get_oidc_issuer_endpoint(self, request):
|
||||
return oauth2_settings.oidc_issuer(request)
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
from django.contrib import admin
|
||||
from .models import Transaction
|
||||
|
||||
|
||||
class TransactionAdmin(admin.ModelAdmin):
|
||||
fields = [
|
||||
'uuid',
|
||||
'amount',
|
||||
'payer',
|
||||
'payee',
|
||||
'create',
|
||||
'last_update',
|
||||
'state',
|
||||
'delay',
|
||||
'detail_id',
|
||||
'application'
|
||||
]
|
||||
readonly_fields = ['uuid', 'last_update', 'create', 'detail_id', 'application']
|
||||
|
||||
|
||||
admin.site.register(Transaction, TransactionAdmin)
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WalletConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'apps.wallet'
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
from django.db.models import IntegerChoices
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class StateChoices(IntegerChoices):
|
||||
CREATED = 1 , _('created')
|
||||
DELAYED = 2 , _('delayed') # delayed as user wish
|
||||
PENDING = 3 , _('pending') # wait for external service response
|
||||
INCOMPLETE = 4 , _('incomplete') # started and wait for internal progress to complete
|
||||
SUCCESS = 5 , _('success')
|
||||
FAILED = 6 , _('failed')
|
||||
EXPECTED_FAILURE = 7 , _('expected_failure') # no exact data available but guessed to be failed
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
from django import forms
|
||||
from .models import Transaction
|
||||
|
||||
|
||||
class TransactionActionForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Transaction
|
||||
fields = []
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
# Generated by Django 4.1 on 2022-09-04 12:55
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Transaction',
|
||||
fields=[
|
||||
('uuid', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||
('amount', models.IntegerField(verbose_name='amount')),
|
||||
('delay', models.IntegerField(blank=True, default=0)),
|
||||
('state', models.IntegerField(choices=[(1, 'created'), (2, 'delayed'), (3, 'pending'), (4, 'incomplete'), (5, 'success'), (6, 'failed'), (7, 'expected_failure')], default=1)),
|
||||
('create', models.DateTimeField(auto_now=True, null=True, verbose_name='create')),
|
||||
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='last update')),
|
||||
('application_client_id', models.CharField(db_index=True, max_length=100, null=True)),
|
||||
('detail_id', models.IntegerField(blank=True, null=True)),
|
||||
('payee', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='receipts', to=settings.AUTH_USER_MODEL)),
|
||||
('payer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='payments', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 4.1 on 2022-09-08 12:16
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
|
||||
('wallet', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='transaction',
|
||||
name='application_client_id',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='transaction',
|
||||
name='application',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, related_name='transactions', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
import datetime
|
||||
import uuid
|
||||
|
||||
from django.db import models
|
||||
from django.db import transaction
|
||||
from django.db.models import F
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework.exceptions import APIException, ValidationError
|
||||
from django.conf import settings
|
||||
from .constans import StateChoices
|
||||
from apps.users.models import User
|
||||
|
||||
|
||||
class TransactionManager(models.Manager):
|
||||
def create_transaction(
|
||||
self,
|
||||
amount,
|
||||
payee: User,
|
||||
payer: User = None,
|
||||
delay=0,
|
||||
# detail_id=None,
|
||||
application=None,
|
||||
# state=None,
|
||||
uuid=None
|
||||
):
|
||||
if not (isinstance(payer, User) or payer is None):
|
||||
raise ValidationError('payer type not valid')
|
||||
if not isinstance(payee, User):
|
||||
raise ValidationError('payee type not valid')
|
||||
if not amount > 0:
|
||||
raise ValidationError('amount should be greater than zero')
|
||||
if not delay >= 0:
|
||||
raise ValidationError('delay should be greater than equal zero')
|
||||
|
||||
transaction = self.model(
|
||||
uuid=uuid,
|
||||
payer=payer,
|
||||
payee=payee,
|
||||
amount=amount,
|
||||
delay=delay,
|
||||
# detail_id=detail_id,
|
||||
application=application
|
||||
)
|
||||
# if state:
|
||||
# transaction.state = state
|
||||
|
||||
transaction.save()
|
||||
return transaction
|
||||
|
||||
|
||||
class Transaction(models.Model):
|
||||
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True,
|
||||
db_index=True) # reserve_code
|
||||
amount = models.IntegerField(_('amount'), blank=False)
|
||||
delay = models.IntegerField(default=0, blank=True) # number of days for delayed payments
|
||||
payer = models.ForeignKey(User, on_delete=models.PROTECT, related_name='payments', blank=True, null=True)
|
||||
payee = models.ForeignKey(User, on_delete=models.PROTECT, related_name='receipts')
|
||||
state = models.IntegerField(choices=StateChoices.choices, default=StateChoices.CREATED)
|
||||
create = models.DateTimeField(_('create'), blank=True, null=True, auto_now=True)
|
||||
last_update = models.DateTimeField(_('last update'), blank=True, null=True, auto_now=True)
|
||||
application = models.ForeignKey(settings.OAUTH2_PROVIDER_APPLICATION_MODEL, on_delete=models.PROTECT,
|
||||
related_name='transactions', null=True)
|
||||
detail_id = models.IntegerField(null=True, blank=True) # TODO: or better detail url
|
||||
|
||||
objects = TransactionManager()
|
||||
|
||||
# class Meta:
|
||||
# unique_together = ('application', 'detail_id',)
|
||||
|
||||
def pay(self, payer=None):
|
||||
if not self.payer and not payer:
|
||||
raise APIException('payer identification error')
|
||||
|
||||
if self.payer and payer and not self.payer == payer:
|
||||
raise APIException('payer identification error')
|
||||
|
||||
# TODO: check bundry conditions
|
||||
if not self.state == StateChoices.CREATED:
|
||||
raise APIException('this transaction is not ready to pay')
|
||||
|
||||
if not self.payer:
|
||||
self.payer = payer
|
||||
|
||||
with transaction.atomic():
|
||||
self.last_update = datetime.datetime.now()
|
||||
|
||||
if self.payer.balance < self.amount:
|
||||
raise APIException('not enough balance')
|
||||
|
||||
self.payer.balance = F('balance') - self.amount
|
||||
self.payer.save(update_fields=['balance'])
|
||||
|
||||
self.payer.refresh_from_db()
|
||||
if self.payer.balance < 0:
|
||||
raise APIException('not enough balance')
|
||||
|
||||
if self.delay:
|
||||
self.state = StateChoices.DELAYED
|
||||
self.save()
|
||||
|
||||
else:
|
||||
self.payee.balance = F('balance') + self.amount
|
||||
self.payee.save(update_fields=['balance'])
|
||||
|
||||
self.state = StateChoices.SUCCESS
|
||||
self.save()
|
||||
|
||||
self.save()
|
||||
return self
|
||||
|
||||
def receipt(self):
|
||||
# TODO: check bundry conditions
|
||||
if not self.state == StateChoices.DELAYED:
|
||||
raise APIException('this transaction is not delayed')
|
||||
|
||||
with transaction.atomic():
|
||||
self.last_update = datetime.datetime.now()
|
||||
self.payee.balance = F('balance') + self.amount
|
||||
self.state = StateChoices.SUCCESS
|
||||
self.payee.save()
|
||||
self.save()
|
||||
|
||||
def submit(self):
|
||||
if not self.payer:
|
||||
raise APIException('payer identification error')
|
||||
|
||||
if not self.payee:
|
||||
raise APIException('payee identification error')
|
||||
|
||||
# TODO: check bundry conditions
|
||||
if not self.state == StateChoices.CREATED:
|
||||
raise APIException('this transaction is not ready to pay')
|
||||
|
||||
with transaction.atomic():
|
||||
self.last_update = datetime.datetime.now()
|
||||
|
||||
if self.payer.balance < self.amount:
|
||||
raise APIException('not enough balance')
|
||||
|
||||
self.payer.balance = F('balance') - self.amount
|
||||
self.payer.save(update_fields=['balance'])
|
||||
|
||||
self.payer.refresh_from_db()
|
||||
self.state = StateChoices.PENDING
|
||||
if self.payer.balance < 0:
|
||||
raise APIException('not enough balance')
|
||||
|
||||
self.save()
|
||||
return self
|
||||
|
||||
def verify(self):
|
||||
# TODO: check bundry conditions
|
||||
if not self.state == StateChoices.PENDING:
|
||||
raise APIException('this transaction is not pending')
|
||||
|
||||
with transaction.atomic():
|
||||
self.last_update = datetime.datetime.now()
|
||||
self.payee.balance = F('balance') + self.amount
|
||||
self.state = StateChoices.SUCCESS
|
||||
self.payee.save()
|
||||
self.save()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.payer} -> {self.payee} ({self.uuid}): {self.amount}"
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
from django.db import IntegrityError
|
||||
from django.db.transaction import atomic
|
||||
from rest_framework import serializers
|
||||
from rest_framework.exceptions import APIException
|
||||
|
||||
from apps.users.serializers import PublicUserSerializer
|
||||
from .models import Transaction
|
||||
from ..users.models import User
|
||||
|
||||
|
||||
class TransactionSerializer(serializers.ModelSerializer):
|
||||
payer = PublicUserSerializer(required=False)
|
||||
payee = PublicUserSerializer(required=False, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
fields = ('uuid',
|
||||
'payer',
|
||||
'payee',
|
||||
"create",
|
||||
"state",
|
||||
'amount',
|
||||
'delay',
|
||||
'last_update',
|
||||
# 'detail_id',
|
||||
)
|
||||
|
||||
read_only_fields = ['uuid', 'last_update', 'state', 'create'] # , 'detail_id']
|
||||
|
||||
def create(self, validated_data):
|
||||
# payer = validated_data.get('payer')
|
||||
# request = self.context.get('request')
|
||||
# user = request.user if request else payer
|
||||
|
||||
# if user == payer:
|
||||
# with atomic():
|
||||
# transaction = Transaction.objects.create_transaction(**validated_data)
|
||||
# transaction.pay()
|
||||
#
|
||||
# else:
|
||||
transaction = Transaction.objects.create_transaction(**validated_data)
|
||||
return transaction
|
||||
|
||||
|
||||
class DepositSerializer(serializers.ModelSerializer):
|
||||
uuid = serializers.UUIDField(required=True)
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
fields = (
|
||||
'uuid',
|
||||
'payer',
|
||||
'payee',
|
||||
"create",
|
||||
"state",
|
||||
'amount',
|
||||
'delay',
|
||||
'last_update',
|
||||
# 'detail_id',
|
||||
)
|
||||
|
||||
read_only_fields = ['last_update', 'state', 'create', 'payer'] # , 'detail_id']
|
||||
|
||||
def create(self, validated_data):
|
||||
with atomic():
|
||||
try:
|
||||
transaction = Transaction.objects.create_transaction(**validated_data)
|
||||
transaction.submit()
|
||||
except IntegrityError as e:
|
||||
raise APIException('duplicate uuid')
|
||||
|
||||
return transaction
|
||||
|
||||
|
||||
class WithdrawSerializer(serializers.ModelSerializer):
|
||||
uuid = serializers.UUIDField(required=True)
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
fields = (
|
||||
'uuid',
|
||||
'payer',
|
||||
'payee',
|
||||
"create",
|
||||
"state",
|
||||
'amount',
|
||||
'delay',
|
||||
'last_update',
|
||||
# 'detail_id',
|
||||
)
|
||||
|
||||
read_only_fields = ['last_update', 'state', 'create', 'payee'] # , 'detail_id']
|
||||
|
||||
def create(self, validated_data):
|
||||
with atomic():
|
||||
try:
|
||||
transaction = Transaction.objects.create_transaction(**validated_data)
|
||||
transaction.submit()
|
||||
except IntegrityError as e:
|
||||
raise APIException('duplicate uuid')
|
||||
|
||||
return transaction
|
||||
|
||||
|
||||
class WalletSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model=User
|
||||
fields = ['balance']
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
from django.urls import path
|
||||
from .views import TransactionList, TransactionDetail, TransactionPay, TransactionReceipt, \
|
||||
WithdrawVerify, WithdrawSubmit, DepositSubmit, DepositVerify, TransactionListView, TransactionDetailView, \
|
||||
DefaultWalletBalanceView
|
||||
|
||||
app_name = "wallet"
|
||||
|
||||
urlpatterns = [
|
||||
path('transactions/', TransactionListView.as_view(), name='transaction_list'),
|
||||
path('transactions/<pk>', TransactionDetailView.as_view(), name='transaction_detail'),
|
||||
|
||||
path('api/wallet/default/balance', DefaultWalletBalanceView.as_view(), name='default_wallet_balance_api'),
|
||||
|
||||
path('api/transactions/', TransactionList.as_view(), name='transaction_list_api'),
|
||||
path('api/transactions/<uuid>', TransactionDetail.as_view(), name='transaction_detail_api'),
|
||||
path('api/transactions/<uuid>/pay', TransactionPay.as_view(), name='transaction_pay_api'),
|
||||
path('api/transactions/<uuid>/receipt', TransactionReceipt.as_view(), name='transaction_receipt_api'),
|
||||
#
|
||||
path('api/deposit/', DepositSubmit.as_view(), name='deposit_submit_api'),
|
||||
path('api/deposit/<uuid>/verify', DepositVerify.as_view(), name='deposit_verify_api'),
|
||||
#
|
||||
path('api/withdraw/', WithdrawSubmit.as_view(), name='withdraw_submit_api'),
|
||||
path('api/withdraw/<uuid>/verify', WithdrawVerify.as_view(), name='withdraw_verify_api'),
|
||||
]
|
||||
|
|
@ -1,218 +0,0 @@
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import DetailView, ListView, UpdateView
|
||||
|
||||
from oauth2_provider.contrib.rest_framework import TokenHasScope, IsAuthenticatedOrTokenHasScope
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework.response import Response
|
||||
|
||||
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
|
||||
from apps.wallet.models import Transaction
|
||||
from apps.wallet.serializers import TransactionSerializer, DepositSerializer, WithdrawSerializer, WalletSerializer
|
||||
from .constans import StateChoices
|
||||
from .forms import TransactionActionForm
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
def get_application(request):
|
||||
try:
|
||||
application = request.auth.application
|
||||
except:
|
||||
application = None
|
||||
return application
|
||||
|
||||
|
||||
class DefaultWalletBalanceView(generics.RetrieveAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
|
||||
serializer_class = WalletSerializer
|
||||
required_scopes = ['wallet.wallet:get_balance']
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
|
||||
|
||||
# TODO: this is user invoice create view
|
||||
# class TransactionList(generics.ListCreateAPIView):
|
||||
# permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||
# serializer_class = TransactionSerializer
|
||||
# required_alternate_scopes = {
|
||||
# "GET": [['wallet.transaction:list']],
|
||||
# "POST": [['wallet.invoice:create']],
|
||||
# }
|
||||
#
|
||||
# def get_queryset(self):
|
||||
# user = self.request.user
|
||||
# return Transaction.objects.filter(Q(payee=user) | Q(payer=user)).all()
|
||||
#
|
||||
# def perform_create(self, serializer):
|
||||
# user = self.request.user
|
||||
# serializer.save(payee=user, application=get_application(self.request))
|
||||
|
||||
|
||||
# this is application create invoice view
|
||||
class TransactionList(generics.ListCreateAPIView):
|
||||
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
|
||||
serializer_class = TransactionSerializer
|
||||
required_alternate_scopes = {
|
||||
"GET": [['wallet.transaction:list']],
|
||||
"POST": [['wallet.invoice:create']],
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return Transaction.objects.filter(Q(payee=user) | Q(payer=user)).all()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
payer = self.request.user
|
||||
payee = self.request.auth.application.user
|
||||
serializer.save(payer=payer, payee=payee, application=get_application(self.request))
|
||||
|
||||
|
||||
class TransactionDetail(generics.RetrieveAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
|
||||
serializer_class = TransactionSerializer
|
||||
lookup_field = 'uuid'
|
||||
required_scopes = ['wallet.transaction:retrieve']
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return Transaction.objects.filter(Q(payee=user) | Q(payer=user) | Q(payer__isnull=True)).all()
|
||||
|
||||
|
||||
class TransactionPay(generics.RetrieveAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
|
||||
serializer_class = TransactionSerializer
|
||||
lookup_field = 'uuid'
|
||||
required_scopes = ['wallet.invoice:pay']
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return Transaction.objects.filter(Q(payer=user) | Q(payer__isnull=True)).all()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
instance.pay(payer=self.request.user)
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class TransactionReceipt(generics.RetrieveAPIView):
|
||||
permission_classes = [permissions.IsAuthenticated, IsAuthenticatedOrTokenHasScope]
|
||||
serializer_class = TransactionSerializer
|
||||
lookup_field = 'uuid'
|
||||
required_scopes = ['wallet.invoice:receipt']
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return Transaction.objects.filter(payer=user).all()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
instance.receipt()
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class DepositSubmit(generics.CreateAPIView):
|
||||
permission_classes = [TokenHasScope]
|
||||
serializer_class = DepositSerializer
|
||||
required_scopes = ['wallet.deposit:submit']
|
||||
|
||||
def get_queryset(self):
|
||||
application = get_application(self.request)
|
||||
return Transaction.objects.filter(application=application).all()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
payer = self.request.auth.application.user
|
||||
serializer.save(payer=payer, application=get_application(self.request))
|
||||
|
||||
|
||||
class DepositVerify(generics.RetrieveAPIView):
|
||||
permission_classes = [TokenHasScope]
|
||||
serializer_class = DepositSerializer
|
||||
lookup_field = 'uuid'
|
||||
required_scopes = ['wallet.deposit:verify']
|
||||
|
||||
def get_queryset(self):
|
||||
application = get_application(self.request)
|
||||
return Transaction.objects.filter(application=application).all()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
if instance.state == StateChoices.PENDING:
|
||||
instance.verify()
|
||||
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
class WithdrawSubmit(generics.CreateAPIView):
|
||||
permission_classes = [TokenHasScope]
|
||||
serializer_class = WithdrawSerializer
|
||||
required_scopes = ['wallet.withdraw:submit']
|
||||
|
||||
def get_queryset(self):
|
||||
application = get_application(self.request)
|
||||
return Transaction.objects.filter(application=application).all()
|
||||
|
||||
def perform_create(self, serializer):
|
||||
payee = self.request.auth.application.user
|
||||
serializer.save(payee=payee, application=get_application(self.request))
|
||||
|
||||
|
||||
class WithdrawVerify(generics.RetrieveAPIView):
|
||||
permission_classes = [TokenHasScope]
|
||||
serializer_class = WithdrawSerializer
|
||||
lookup_field = 'uuid'
|
||||
required_scopes = ['wallet.withdraw:verify']
|
||||
|
||||
def get_queryset(self):
|
||||
application = get_application(self.request)
|
||||
return Transaction.objects.filter(application=application).all()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
if instance.state == StateChoices.PENDING:
|
||||
instance.verify()
|
||||
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class TransactionListView(ListView):
|
||||
model = Transaction
|
||||
template_name = 'wallet/transaction_list.html'
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return Transaction.objects.filter(Q(payee=user) | Q(payer=user)).all()
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class TransactionDetailView(UpdateView):
|
||||
model = Transaction
|
||||
form_class = TransactionActionForm
|
||||
template_name = 'wallet/transaction_detail.html'
|
||||
|
||||
def get_queryset(self):
|
||||
user = self.request.user
|
||||
return Transaction.objects.filter(Q(payee=user) | Q(payer=user) | Q(payer__isnull=True)).all()
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('wallet:transaction_detail', kwargs={'pk': self.get_object().pk})
|
||||
|
||||
def form_valid(self, form):
|
||||
transaction: Transaction = form.instance
|
||||
if 'pay' in self.request.POST:
|
||||
result = transaction.pay(payer=self.request.user)
|
||||
messages.error(self.request, _('success.'))
|
||||
if 'receipt' in self.request.POST:
|
||||
result = transaction.receipt()
|
||||
messages.error(self.request, _('success.'))
|
||||
return super().form_valid(form)
|
||||
|
||||
|
|
@ -13,7 +13,8 @@
|
|||
|
||||
<link href="{% static 'bootstrap-5.3.0-dist/css/bootstrap.rtl.min.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'bootstrap-icons-1.11.3/font/bootstrap-icons.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'fonts/vazirmatn-v33.003/misc/UI-Farsi-Digits/Vazirmatn-UI-FD-font-face.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'fonts/vazirmatn-v33.003/misc/UI-Farsi-Digits/Vazirmatn-UI-FD-font-face.css' %}"
|
||||
rel="stylesheet">
|
||||
<link href="{% static 'fonts/Pelak/fonts.css' %}" rel="stylesheet">
|
||||
<link href="{% static 'main.css' %}" rel="stylesheet">
|
||||
<script src="{% static 'jquery-3.6.0.min.js' %}"></script>
|
||||
|
|
@ -42,8 +43,9 @@
|
|||
<div class="row vh-100 overflow-auto">
|
||||
{# {% include "include/sidebar.html" %}#}
|
||||
<div class="col d-flex flex-column h-sm-100">
|
||||
<div class="m-0 p-0 w-100 " style="background-color: green; height: 3px;"></div>
|
||||
<main class="row overflow-auto pb-3">
|
||||
<div class="col pt-3">
|
||||
<div class="col pt-2">
|
||||
{% include "include/top_bar.html" %}
|
||||
{% include "include/messages.html" %}
|
||||
{# {% block breadcrumbs %}#}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue