388 lines
17 KiB
Python
388 lines
17 KiB
Python
import logging
|
|
import json
|
|
import decimal
|
|
from stripe.error import CardError
|
|
from django.shortcuts import redirect, render
|
|
from django.contrib import messages
|
|
from django.utils.translation import get_language, ugettext_lazy as _
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.views.decorators.cache import cache_control
|
|
from django.utils.decorators import method_decorator
|
|
from django.views import View
|
|
from django.views.generic import FormView, DetailView
|
|
from django.views.generic.list import ListView
|
|
from matrixhosting.forms import InitialRequestForm, BillingAddressForm, RequestDomainsNamesForm
|
|
from django.urls import reverse
|
|
from django.conf import settings
|
|
from django.http import (
|
|
HttpResponseRedirect, JsonResponse, HttpResponse
|
|
)
|
|
from wkhtmltopdf.views import PDFTemplateResponse
|
|
from rest_framework import viewsets, permissions
|
|
|
|
from uncloud_pay.models import PricingPlan
|
|
from uncloud_pay.utils import get_order_total_with_vat
|
|
from uncloud_pay.models import *
|
|
from uncloud_pay.utils import validate_vat_number
|
|
from uncloud_pay.selectors import get_billing_address_for_user, has_enough_balance, get_balance_for_user
|
|
import uncloud_pay.stripe as uncloud_stripe
|
|
from .models import VMInstance
|
|
from .serializers import *
|
|
from .utils import *
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class IndexView(FormView):
|
|
template_name = "matrixhosting/index.html"
|
|
form_class = InitialRequestForm
|
|
success_url = "/matrixhosting#requestform"
|
|
success_message = "Thank you, we will contact you as soon as possible"
|
|
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
context['matrix_vm_pricing'] = PricingPlan.get_default_pricing()
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
self.request.session['order'] = form.cleaned_data
|
|
pricing = get_order_total_with_vat(
|
|
form.cleaned_data['cores'],
|
|
form.cleaned_data['memory'],
|
|
form.cleaned_data['storage'],
|
|
form.cleaned_data['pricing_name'],
|
|
False
|
|
)
|
|
self.request.session['pricing'] = pricing
|
|
return HttpResponseRedirect(reverse('matrix:payment'))
|
|
|
|
|
|
class OrderPaymentView(FormView):
|
|
template_name = 'matrixhosting/order_details.html'
|
|
success_url = 'matrix:order_confirmation'
|
|
form_class = BillingAddressForm
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(OrderPaymentView, self).get_context_data(**kwargs)
|
|
default_pricing = PricingPlan.get_default_pricing()
|
|
if 'billing_address_data' in self.request.session:
|
|
billing_address_form = BillingAddressForm(
|
|
initial=self.request.session['billing_address_data']
|
|
)
|
|
else:
|
|
old_active = get_billing_address_for_user(self.request.user)
|
|
billing_address_form = BillingAddressForm(
|
|
instance=old_active
|
|
) if old_active else BillingAddressForm(
|
|
initial={'active': True, 'owner': self.request.user.id}
|
|
)
|
|
details_form = InitialRequestForm(
|
|
initial=self.request.session.get('order', {'pricing_name': default_pricing.name})
|
|
)
|
|
balance = get_balance_for_user(self.request.user)
|
|
customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
|
|
#TODO optimize this part for better performance
|
|
uncloud_stripe.sync_cards_for_user(self.request.user)
|
|
cards = uncloud_stripe.get_customer_cards(customer_id)
|
|
pricing = self.request.session.get('pricing', {'name': default_pricing.name, 'total': 0})
|
|
context.update({
|
|
'matrix_vm_pricing': PricingPlan.get_by_name(pricing['name']),
|
|
'billing_address_form': billing_address_form,
|
|
'details_form': details_form,
|
|
'balance': balance,
|
|
'cards': cards,
|
|
'stripe_key': settings.STRIPE_PUBLIC_KEY,
|
|
'show_cards': True if balance < pricing['total'] else False,
|
|
})
|
|
return context
|
|
|
|
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
|
def get(self, request, *args, **kwargs):
|
|
for k in ['vat_validation_status', 'token', 'id_payment_method', 'total']:
|
|
if request.session.get(k):
|
|
request.session.pop(k)
|
|
return self.render_to_response(self.get_context_data())
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
details_form = InitialRequestForm(request.POST)
|
|
billing_address_form = BillingAddressForm(request.POST)
|
|
if not details_form.is_valid() or not billing_address_form.is_valid():
|
|
context = self.get_context_data()
|
|
context.update({'details_form': details_form,
|
|
'billing_address_form': billing_address_form})
|
|
return self.render_to_response(context)
|
|
address = get_billing_address_for_user(self.request.user)
|
|
if address:
|
|
form = BillingAddressForm(self.request.POST, instance=address)
|
|
else:
|
|
form = BillingAddressForm(self.request.POST)
|
|
if form.is_valid:
|
|
billing_address_ins = form.save()
|
|
self.request.session["billing_address_id"] = billing_address_ins.id
|
|
self.request.session['billing_address_data'] = billing_address_form.cleaned_data
|
|
self.request.session['billing_address_data']['owner'] = self.request.user.id
|
|
id_payment_method = self.request.POST.get('id_payment_method', False)
|
|
selected_card = False
|
|
if id_payment_method and id_payment_method != 'undefined':
|
|
uncloud_stripe.attach_payment_method(id_payment_method, self.request.user)
|
|
selected_card = StripeCreditCard.objects.filter(card_id=id_payment_method).first()
|
|
selected_card.activate()
|
|
vat_number = billing_address_form.cleaned_data.get('vat_number').strip()
|
|
if vat_number:
|
|
customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
|
|
validate_result = validate_vat_number(
|
|
stripe_customer_id=customer_id,
|
|
billing_address_id=billing_address_ins.id
|
|
)
|
|
if 'error' in validate_result and validate_result['error']:
|
|
messages.add_message(
|
|
self.request, messages.ERROR, validate_result["error"],
|
|
extra_tags='error'
|
|
)
|
|
return HttpResponseRedirect(
|
|
reverse('matrix:payment')
|
|
)
|
|
self.request.session["vat_validation_status"] = validate_result["status"]
|
|
specs = details_form.cleaned_data
|
|
vat_rate = VATRate.get_vat_rate(billing_address_ins)
|
|
vat_validation_status = "verified" if billing_address_ins.vat_number_validated_on and billing_address_ins.vat_number_verified else False
|
|
pricing = get_order_total_with_vat(
|
|
specs['cores'], specs['memory'], specs['storage'], request.session['pricing']['name'],
|
|
vat_rate=vat_rate * 100, vat_validation_status = vat_validation_status
|
|
)
|
|
self.request.session['pricing'] = pricing
|
|
self.request.session['order'] = specs
|
|
self.request.session['vat_validation_status'] = vat_validation_status
|
|
amount = get_balance_for_user(self.request.user) - decimal.Decimal(pricing["total"])
|
|
if (amount < 0 and not selected_card):
|
|
messages.add_message(
|
|
self.request, messages.ERROR, "You haven't enough balance please select credit card to continue",
|
|
extra_tags='error'
|
|
)
|
|
return HttpResponseRedirect(
|
|
reverse('matrix:payment')
|
|
)
|
|
return HttpResponseRedirect(reverse('matrix:order_confirmation'))
|
|
|
|
class OrderConfirmationView(DetailView):
|
|
template_name = "matrixhosting/order_confirmation.html"
|
|
context_object_name = "order"
|
|
model = Order
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = {
|
|
'order': self.request.session.get('order'),
|
|
'pricing': self.request.session.get('pricing'),
|
|
'balance': get_balance_for_user(self.request.user)
|
|
}
|
|
return context
|
|
|
|
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
|
def get(self, request, *args, **kwargs):
|
|
context = self.get_context_data()
|
|
context['domains_form'] = RequestDomainsNamesForm(initial={})
|
|
if ('order' not in request.session):
|
|
return HttpResponseRedirect(reverse('matrix:index'))
|
|
elif 'pricing' not in self.request.session or 'vat_validation_status' not in self.request.session:
|
|
return HttpResponseRedirect(reverse('matrix:payment'))
|
|
|
|
total = self.request.session['pricing']['total']
|
|
amount = get_balance_for_user(self.request.user) - decimal.Decimal(total)
|
|
if (amount < 0):
|
|
context['stripe_deposit_amount'] = max(amount, settings.MIN_PER_TRANSACTION)
|
|
return render(request, self.template_name, context)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
domains_form = RequestDomainsNamesForm(self.request.POST)
|
|
if domains_form.is_valid():
|
|
customer = StripeCustomer.objects.get(owner=self.request.user)
|
|
billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id'))
|
|
total = self.request.session['pricing']['total']
|
|
self.request.session['order']['matrix_domain'] = 'ungleich.ch'
|
|
self.request.session['order']['homeserver_domain'] = domains_form.cleaned_data.get('homeserver_name') + ".matrix.ungleich.cloud"
|
|
self.request.session['order']['webclient_domain'] = domains_form.cleaned_data.get('webclient_name') + ".matrix.0co2.cloud"
|
|
self.request.session['order']['is_open_registration'] = domains_form.cleaned_data.get('is_open_registration')
|
|
try:
|
|
amount = get_balance_for_user(self.request.user) - decimal.Decimal(total)
|
|
if (amount < 0):
|
|
Payment.deposit(request.user, max(abs(amount), settings.MIN_PER_TRANSACTION), source='stripe')
|
|
amount = get_balance_for_user(self.request.user) - decimal.Decimal(total)
|
|
if (amount < 0):
|
|
messages.add_message(
|
|
self.request, messages.ERROR, "Please make sure that you have enough balance in your wallet and try again later.",
|
|
extra_tags='error'
|
|
)
|
|
return HttpResponseRedirect(
|
|
reverse('matrix:order_confirmation')
|
|
)
|
|
order, bill = finalize_order(request, customer,
|
|
billing_address,
|
|
total,
|
|
PricingPlan.get_by_name(self.request.session['pricing']['name']),
|
|
request.session.get('order'))
|
|
if order and bill:
|
|
self.request.session['bill_id'] = bill.id
|
|
return HttpResponseRedirect(reverse('matrix:order_success'))
|
|
except CardError as e:
|
|
messages.add_message(
|
|
self.request, messages.ERROR, e.user_message,
|
|
extra_tags='error'
|
|
)
|
|
return HttpResponseRedirect(
|
|
reverse('matrix:order_confirmation')
|
|
)
|
|
context = self.get_context_data()
|
|
context['domains_form'] = domains_form
|
|
return self.render_to_response(context)
|
|
|
|
class OrderSuccessView(DetailView):
|
|
template_name = "matrixhosting/order_success.html"
|
|
context_object_name = "order"
|
|
model = Order
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
@cache_control(no_cache=True, must_revalidate=True, no_store=True)
|
|
def get(self, request, *args, **kwargs):
|
|
context = {
|
|
'order': self.request.session.get('order'),
|
|
'bill_id': self.request.session['bill_id'],
|
|
'balance': get_balance_for_user(self.request.user)
|
|
}
|
|
if ('order' not in request.session):
|
|
return HttpResponseRedirect(reverse('matrix:index'))
|
|
return render(request, self.template_name, context)
|
|
|
|
class InvoiceDownloadView(View):
|
|
template = 'matrixhosting/invoice.html'
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = {'base_url': f'{self.request.scheme}://{self.request.get_host()}'}
|
|
return context
|
|
|
|
def get(self, request, bill_id):
|
|
cmd_options = settings.REPORT_FORMAT
|
|
context = self.get_context_data()
|
|
bill = Bill.objects.get(owner=self.request.user, id=bill_id)
|
|
if bill:
|
|
context['bill'] = bill
|
|
context['vat_rate'] = str(round(bill.vat_rate * 100, 2))
|
|
context['tax_amount'] = round(bill.vat_rate * bill.subtotal, 2)
|
|
return PDFTemplateResponse(request=request,
|
|
template=self.template,
|
|
filename = f"bill-{bill_id}.pdf",
|
|
cmd_options= cmd_options,
|
|
footer_template= 'matrixhosting/includes/invoice_footer.html',
|
|
context= context)
|
|
|
|
|
|
class OrdersView(ListView):
|
|
template_name = "matrixhosting/orders.html"
|
|
model = Order
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return Order.objects.filter(owner=self.request.user).order_by('-creation_date')
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
order = Order.objects.get(id=request.POST.get('order_id', 0))
|
|
order.cancel()
|
|
return JsonResponse({'message': 'Successfully Cancelled'})
|
|
|
|
class InstancesView(ListView):
|
|
template_name = "matrixhosting/instances.html"
|
|
model = VMInstance
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return VMInstance.objects.filter(owner=self.request.user).order_by('-creation_date')
|
|
|
|
class PaymentsView(ListView):
|
|
template_name = "matrixhosting/payments.html"
|
|
model = Payment
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(PaymentsView, self).get_context_data(**kwargs)
|
|
context.update({
|
|
'balance': get_balance_for_user(self.request.user),
|
|
'type': self.request.GET.get('type')
|
|
})
|
|
return context
|
|
|
|
def get_queryset(self):
|
|
if self.request.GET.get('type'):
|
|
return Payment.objects.filter(owner=self.request.user, type=self.request.GET.get('type')).order_by('-timestamp')
|
|
return Payment.objects.filter(owner=self.request.user).order_by('-timestamp')
|
|
|
|
class CardsView(ListView):
|
|
template_name = "matrixhosting/cards.html"
|
|
model = StripeCreditCard
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(CardsView, self).get_context_data(**kwargs)
|
|
customer_id = uncloud_stripe.get_customer_id_for(self.request.user)
|
|
setup_intent = uncloud_stripe.create_setup_intent(customer_id)
|
|
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'balance': get_balance_for_user(self.request.user),
|
|
'client_secret': setup_intent.client_secret,
|
|
'username': self.request.user.username,
|
|
'stripe_pk':uncloud_stripe.public_api_key,
|
|
'min_amount': settings.MIN_PER_TRANSACTION
|
|
})
|
|
return context
|
|
|
|
def get_queryset(self):
|
|
uncloud_stripe.sync_cards_for_user(self.request.user)
|
|
return StripeCreditCard.objects.filter(owner=self.request.user).order_by('-active')
|
|
|
|
class BillsView(ListView):
|
|
template_name = "matrixhosting/bills.html"
|
|
model = Bill
|
|
|
|
@method_decorator(login_required)
|
|
def dispatch(self, *args, **kwargs):
|
|
return super().dispatch(*args, **kwargs)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super(BillsView, self).get_context_data(**kwargs)
|
|
|
|
context = super().get_context_data(**kwargs)
|
|
context.update({
|
|
'balance': get_balance_for_user(self.request.user),
|
|
})
|
|
return context
|
|
|
|
def get_queryset(self):
|
|
return Bill.objects.filter(owner=self.request.user).order_by('-creation_date')
|
|
|
|
|