start creating service transaction
This commit is contained in:
parent
9b4aaec9af
commit
24fd4010c0
5 changed files with 103 additions and 26 deletions
|
|
@ -19,7 +19,7 @@ class Migration(migrations.Migration):
|
||||||
('code', models.IntegerField(db_index=True, unique=True, verbose_name='code')),
|
('code', models.IntegerField(db_index=True, unique=True, verbose_name='code')),
|
||||||
('amount', models.IntegerField(verbose_name='amount')),
|
('amount', models.IntegerField(verbose_name='amount')),
|
||||||
('delay', models.IntegerField(blank=True, default=0)),
|
('delay', models.IntegerField(blank=True, default=0)),
|
||||||
('state', models.IntegerField(choices=[(1, 'created'), (2, 'delayed'), (3, 'pending'), (4, 'failed'), (5, 'done')], default=1)),
|
('state', models.IntegerField(choices=[(1, 'created'), (2, 'delayed'), (3, 'pending'), (4, 'failed'), (5, 'success')], default=1)),
|
||||||
('create', models.DateTimeField(auto_now=True, null=True, verbose_name='create')),
|
('create', models.DateTimeField(auto_now=True, null=True, verbose_name='create')),
|
||||||
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='last update')),
|
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='last update')),
|
||||||
('application_id', models.IntegerField(blank=True, null=True, verbose_name='application ID')),
|
('application_id', models.IntegerField(blank=True, null=True, verbose_name='application ID')),
|
||||||
|
|
|
||||||
|
|
@ -8,18 +8,19 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from model_utils.choices import Choices
|
from model_utils.choices import Choices
|
||||||
from rest_framework.exceptions import APIException, ValidationError
|
from rest_framework.exceptions import APIException, ValidationError
|
||||||
|
|
||||||
|
from apps.users.constans import STATE_CHOICES
|
||||||
from apps.users.models import User
|
from apps.users.models import User
|
||||||
|
|
||||||
|
|
||||||
class TransactionManager(models.Manager):
|
class TransactionManager(models.Manager):
|
||||||
def create_transaction(self,
|
def create_user_transaction(self,
|
||||||
amount,
|
amount,
|
||||||
payee: User,
|
payee: User,
|
||||||
payer: User = None,
|
payer: User = None,
|
||||||
delay=0,
|
delay=0,
|
||||||
detail_id=None,
|
detail_id=None,
|
||||||
application_client_id=None,
|
application_client_id=None,
|
||||||
state=None):
|
state=None):
|
||||||
if not (isinstance(payer, User) or payer is None):
|
if not (isinstance(payer, User) or payer is None):
|
||||||
raise ValidationError('payer type not valid')
|
raise ValidationError('payer type not valid')
|
||||||
if not isinstance(payee, User):
|
if not isinstance(payee, User):
|
||||||
|
|
@ -42,16 +43,42 @@ class TransactionManager(models.Manager):
|
||||||
transaction.save()
|
transaction.save()
|
||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
|
def create_service_transaction(
|
||||||
|
self,
|
||||||
|
amount,
|
||||||
|
payee: User = None,
|
||||||
|
payer: User = None,
|
||||||
|
# delay=0,
|
||||||
|
detail_id=None,
|
||||||
|
application_client_id=None,
|
||||||
|
# state=None
|
||||||
|
):
|
||||||
|
if not (isinstance(payer, User) or payer is None):
|
||||||
|
raise ValidationError('payer type not valid')
|
||||||
|
if not (isinstance(payee, User) or payee is None):
|
||||||
|
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(
|
||||||
|
payer=payer,
|
||||||
|
payee=payee,
|
||||||
|
amount=amount,
|
||||||
|
# delay=delay,
|
||||||
|
detail_id=detail_id,
|
||||||
|
application_client_id=application_client_id,
|
||||||
|
state=STATE_CHOICES.pending
|
||||||
|
)
|
||||||
|
|
||||||
|
transaction.save()
|
||||||
|
return transaction
|
||||||
|
|
||||||
|
|
||||||
class Transaction(models.Model):
|
class Transaction(models.Model):
|
||||||
STATE_CHOICES = Choices(
|
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True,
|
||||||
(1, 'created', 'created'),
|
db_index=True) # reserve_code
|
||||||
(2, 'delayed', 'delayed'),
|
|
||||||
(3, 'pending', 'pending'),
|
|
||||||
(4, 'failed', 'failed'),
|
|
||||||
(5, 'done', 'done'),
|
|
||||||
)
|
|
||||||
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True) # reserve_code
|
|
||||||
amount = models.IntegerField(_('amount'), blank=False)
|
amount = models.IntegerField(_('amount'), blank=False)
|
||||||
delay = models.IntegerField(default=0, blank=True) # number of days for delayed payments
|
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)
|
payer = models.ForeignKey(User, on_delete=models.PROTECT, related_name='payments', blank=True, null=True)
|
||||||
|
|
@ -81,7 +108,7 @@ class Transaction(models.Model):
|
||||||
# TODO: check bundry conditions
|
# TODO: check bundry conditions
|
||||||
self.last_update = datetime.datetime.now()
|
self.last_update = datetime.datetime.now()
|
||||||
|
|
||||||
if not self.state == self.STATE_CHOICES.created:
|
if not self.state == STATE_CHOICES.created:
|
||||||
raise APIException('this transaction is not ready to pay')
|
raise APIException('this transaction is not ready to pay')
|
||||||
|
|
||||||
if self.payer.balance < self.amount:
|
if self.payer.balance < self.amount:
|
||||||
|
|
@ -95,14 +122,14 @@ class Transaction(models.Model):
|
||||||
raise APIException('not enough balance')
|
raise APIException('not enough balance')
|
||||||
|
|
||||||
if self.delay:
|
if self.delay:
|
||||||
self.state = self.STATE_CHOICES.delayed
|
self.state = STATE_CHOICES.delayed
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.payee.balance = F('balance') + self.amount
|
self.payee.balance = F('balance') + self.amount
|
||||||
self.payee.save(update_fields=['balance'])
|
self.payee.save(update_fields=['balance'])
|
||||||
|
|
||||||
self.state = self.STATE_CHOICES.done
|
self.state = STATE_CHOICES.success
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
@ -112,11 +139,49 @@ class Transaction(models.Model):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
self.last_update = datetime.datetime.now()
|
self.last_update = datetime.datetime.now()
|
||||||
# TODO: check bundry conditions
|
# TODO: check bundry conditions
|
||||||
if not self.state == self.STATE_CHOICES.delayed:
|
if not self.state == STATE_CHOICES.delayed:
|
||||||
raise APIException('this transaction is not delayed')
|
raise APIException('this transaction is not delayed')
|
||||||
|
|
||||||
self.payee.balance = F('balance') + self.amount
|
self.payee.balance = F('balance') + self.amount
|
||||||
self.state = self.STATE_CHOICES.done
|
self.state = STATE_CHOICES.success
|
||||||
|
self.payee.save()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def submit(self):
|
||||||
|
with transaction.atomic():
|
||||||
|
# TODO: check bundry conditions
|
||||||
|
self.last_update = datetime.datetime.now()
|
||||||
|
|
||||||
|
if not self.state == STATE_CHOICES.created:
|
||||||
|
raise APIException('this transaction is not ready to pay')
|
||||||
|
|
||||||
|
if self.payer:
|
||||||
|
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')
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.payee.balance = F('balance') + self.amount
|
||||||
|
self.payee.save(update_fields=['balance'])
|
||||||
|
|
||||||
|
self.state = STATE_CHOICES.success
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
self.save()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def verify(self):
|
||||||
|
with transaction.atomic():
|
||||||
|
self.last_update = datetime.datetime.now()
|
||||||
|
# TODO: check bundry conditions
|
||||||
|
if not self.state == STATE_CHOICES.pending:
|
||||||
|
raise APIException('this transaction is not pending')
|
||||||
|
|
||||||
|
self.payee.balance = F('balance') + self.amount
|
||||||
|
self.state = STATE_CHOICES.success
|
||||||
self.payee.save()
|
self.payee.save()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,10 +34,10 @@ class TransactionSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
if user == payer:
|
if user == payer:
|
||||||
with atomic():
|
with atomic():
|
||||||
transaction = Transaction.objects.create_transaction(**validated_data)
|
transaction = Transaction.objects.create_user_transaction(**validated_data)
|
||||||
transaction.pay()
|
transaction.pay()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
transaction = Transaction.objects.create_transaction(**validated_data)
|
transaction = Transaction.objects.create_user_transaction(**validated_data)
|
||||||
|
|
||||||
return transaction
|
return transaction
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,14 @@
|
||||||
|
from model_utils.choices import Choices
|
||||||
|
|
||||||
MAX_OTP_TRY = 3
|
MAX_OTP_TRY = 3
|
||||||
DEVELOPMENT_PHONE_NUMBERS = ['+989999999999', '+989999999998']
|
DEVELOPMENT_PHONE_NUMBERS = ['+989999999999', '+989999999998']
|
||||||
|
|
||||||
|
STATE_CHOICES = Choices(
|
||||||
|
(1, 'created', 'created'),
|
||||||
|
(2, 'delayed', 'delayed'), # delayed as user wish
|
||||||
|
(3, 'pending', 'pending'), # wait for external service response
|
||||||
|
(4, 'incomplete', 'incomplete'), # started and wait for internal progress to complete
|
||||||
|
(5, 'success', 'success'),
|
||||||
|
(6, 'failed', 'failed'),
|
||||||
|
(7, 'expected_failure', 'incomplete'), # no exact data available but guessed to be failed
|
||||||
|
)
|
||||||
|
|
@ -128,7 +128,7 @@ class ApiClient():
|
||||||
path = f'transactions/{uuid}'
|
path = f'transactions/{uuid}'
|
||||||
return self._request(path=path)
|
return self._request(path=path)
|
||||||
|
|
||||||
def create_transaction(self, amount, delay, payer=None):
|
def create_user_transaction(self, amount, delay, payer=None):
|
||||||
data = {
|
data = {
|
||||||
'delay': delay,
|
'delay': delay,
|
||||||
'amount': amount
|
'amount': amount
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue