Refactor the Payment Model and Handle Order Confirmation Page
This commit is contained in:
parent
94dac8110e
commit
5564400ef8
15 changed files with 311 additions and 266 deletions
23
uncloud_pay/migrations/0023_auto_20210730_1342.py
Normal file
23
uncloud_pay/migrations/0023_auto_20210730_1342.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.4 on 2021-07-30 13:42
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('uncloud_pay', '0022_remove_order_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='notes',
|
||||
field=models.TextField(blank=True, default='', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='payment',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('send', 'Send Money'), ('receive', 'Receive Money')], default='send', max_length=256),
|
||||
),
|
||||
]
|
||||
23
uncloud_pay/migrations/0024_auto_20210730_1441.py
Normal file
23
uncloud_pay/migrations/0024_auto_20210730_1441.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.2.4 on 2021-07-30 14:41
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('uncloud_pay', '0023_auto_20210730_1342'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='source',
|
||||
field=models.CharField(blank=True, choices=[('wire', 'Wire Transfer'), ('stripe', 'Stripe'), ('voucher', 'Voucher'), ('referral', 'Referral')], max_length=256, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='payment',
|
||||
name='type',
|
||||
field=models.CharField(choices=[('withdraw', 'Withdraw Money'), ('deposit', 'Deposit Money')], default='send', max_length=256),
|
||||
),
|
||||
]
|
||||
|
|
@ -78,6 +78,13 @@ class StripeCreditCard(models.Model):
|
|||
|
||||
class Payment(models.Model):
|
||||
owner = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
|
||||
type = models.CharField(max_length=256,
|
||||
choices = (
|
||||
('withdraw', 'Withdraw Money'),
|
||||
('deposit', 'Deposit Money')
|
||||
), null=False, blank=False, default="send")
|
||||
|
||||
notes = models.TextField(default="", null=True, blank=True)
|
||||
|
||||
amount = models.DecimalField(
|
||||
max_digits=AMOUNT_MAX_DIGITS,
|
||||
|
|
@ -90,7 +97,7 @@ class Payment(models.Model):
|
|||
('stripe', 'Stripe'),
|
||||
('voucher', 'Voucher'),
|
||||
('referral', 'Referral'),
|
||||
))
|
||||
), null=True, blank=True,)
|
||||
|
||||
timestamp = models.DateTimeField(default=timezone.now)
|
||||
|
||||
|
|
@ -101,19 +108,24 @@ class Payment(models.Model):
|
|||
def __str__(self):
|
||||
return f"{self.amount}{self.currency} from {self.owner} via {self.source} on {self.timestamp}"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Try to charge the user via the active card before saving otherwise throw payment Error
|
||||
if self.source == 'stripe':
|
||||
@classmethod
|
||||
def deposit(cls, owner, amount, source, currency='CHF', notes=''):
|
||||
if source == 'stripe':
|
||||
try:
|
||||
result = uncloud_pay.stripe.charge_customer(self.owner, self.amount, self.currency,)
|
||||
if not result.status or result.status != 'succeeded':
|
||||
payment_intent = uncloud_pay.stripe.charge_customer(owner, amount, currency)
|
||||
if not payment_intent.status or payment_intent.status != 'succeeded':
|
||||
raise Exception("The payment has been failed, please try to activate another card")
|
||||
super().save(*args, **kwargs)
|
||||
return cls.objects.create(owner=owner, type="deposit", amount=amount, external_reference=payment_intent["id"],
|
||||
currency=currency, source=source, notes=notes)
|
||||
except Exception as e:
|
||||
raise e
|
||||
|
||||
|
||||
|
||||
@classmethod
|
||||
def withdraw(cls, owner, amount, currency='CHF', notes=''):
|
||||
cls.objects.create(owner=owner, type="withdraw", amount=amount,
|
||||
currency=currency, notes=notes)
|
||||
|
||||
|
||||
# See https://docs.djangoproject.com/en/dev/ref/models/fields/#field-choices-enum-types
|
||||
class RecurringPeriodDefaultChoices(models.IntegerChoices):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -2,23 +2,17 @@ from django.utils import timezone
|
|||
from django.db import transaction
|
||||
from .models import *
|
||||
|
||||
def get_payments_for_user(user):
|
||||
payments = [ payment.amount for payment in Payment.objects.filter(owner=user) ]
|
||||
|
||||
def get_deposit_payments_for_user(user):
|
||||
payments = [ payment.amount for payment in Payment.objects.filter(owner=user, type='deposit')]
|
||||
return sum(payments)
|
||||
|
||||
def get_spendings_for_user(user):
|
||||
bills = Bill.objects.filter(owner=user)
|
||||
|
||||
amount = 0
|
||||
for bill in bills:
|
||||
amount += bill.sum
|
||||
|
||||
return amount
|
||||
spendings = [payment.amount for payment in Payment.objects.filter(owner=user, type='withdraw')]
|
||||
return sum(spendings)
|
||||
|
||||
@transaction.atomic
|
||||
def get_balance_for_user(user):
|
||||
return get_payments_for_user(user) - get_spendings_for_user(user)
|
||||
return get_deposit_payments_for_user(user) - get_spendings_for_user(user)
|
||||
|
||||
@transaction.atomic
|
||||
def has_enough_balance(user, due_amount):
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ def create_tax_id(stripe_customer_id, billing_address_id, type):
|
|||
}
|
||||
|
||||
def apply_vat_discount(subtotal, pricing_plan, vat_rate=False, vat_validation_status=False):
|
||||
vat_percent = vat_rate or pricing_plan.vat_percentage
|
||||
vat_percent = vat_rate
|
||||
if pricing_plan.vat_inclusive or (vat_validation_status and vat_validation_status in ["verified", "not_needed"]):
|
||||
vat_percent = decimal.Decimal(0)
|
||||
vat = decimal.Decimal(0)
|
||||
|
|
@ -119,7 +119,7 @@ def apply_vat_discount(subtotal, pricing_plan, vat_rate=False, vat_validation_st
|
|||
subtotal = round(float(subtotal), 2)
|
||||
vat_percent = round(float(vat_percent), 2)
|
||||
discount = {
|
||||
'name': pricing_plan.discount_name,
|
||||
'name': pricing_plan.discount_name or 'Discount',
|
||||
'amount': discount_amount,
|
||||
'amount_with_vat': round(float(discount_amount_with_vat), 2)
|
||||
}
|
||||
|
|
@ -149,7 +149,17 @@ def get_order_total_with_vat(cores, memory, storage,
|
|||
(decimal.Decimal(memory) * pricing.ram_unit_price) +
|
||||
(decimal.Decimal(storage) * (pricing.storage_unit_price))
|
||||
)
|
||||
return apply_vat_discount(subtotal, pricing, vat_rate, vat_validation_status)
|
||||
subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, discount = \
|
||||
apply_vat_discount(subtotal, pricing, vat_rate, vat_validation_status)
|
||||
return {
|
||||
"name": pricing.name,
|
||||
"subtotal": subtotal,
|
||||
"discount": discount,
|
||||
"vat": vat, "vat_percent": vat_percent,
|
||||
"vat_validation_status": vat_validation_status,
|
||||
"subtotal_after_discount": subtotal_after_discount,
|
||||
"total": price_after_discount_with_vat
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic.base import TemplateView, View
|
||||
from django.shortcuts import render
|
||||
from django.db import transaction
|
||||
from django.contrib.auth import get_user_model
|
||||
|
|
@ -23,6 +23,7 @@ import logging
|
|||
from .models import *
|
||||
from .serializers import *
|
||||
from .selectors import *
|
||||
from .utils import get_order_total_with_vat
|
||||
|
||||
from datetime import datetime
|
||||
from vat_validator import sanitize_vat
|
||||
|
|
@ -34,8 +35,16 @@ import stripe
|
|||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
###
|
||||
# 2020-12 checked code
|
||||
|
||||
class PricingView(View):
|
||||
def get(self, request, **args):
|
||||
pricing = get_order_total_with_vat(
|
||||
request.GET.get('cores'),
|
||||
request.GET.get('memory'),
|
||||
request.GET.get('storage'),
|
||||
pricing_name = args['name']
|
||||
)
|
||||
return JsonResponse(pricing)
|
||||
|
||||
class RegisterCard(TemplateView):
|
||||
template_name = "uncloud_pay/register_stripe.html"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue