forked from uncloud/uncloud
290 lines
13 KiB
Python
290 lines
13 KiB
Python
|
import logging
|
||
|
from django.views.generic.base import TemplateView
|
||
|
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 django.views.decorators.csrf import csrf_exempt
|
||
|
from django.views.decorators.http import require_POST
|
||
|
from .forms import InitialRequestForm, RequestDomainsNamesForm
|
||
|
from uncloud_pay.forms import BillingAddressForm
|
||
|
from django.urls import reverse
|
||
|
from django.conf import settings
|
||
|
from django.utils import timezone
|
||
|
from django_q.models import Schedule
|
||
|
from django_q.tasks import schedule
|
||
|
from django.http import (
|
||
|
HttpResponseRedirect, JsonResponse, HttpResponse
|
||
|
)
|
||
|
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, get_balance_for_user
|
||
|
import uncloud_pay.stripe as uncloud_stripe
|
||
|
from .models import VMMachine
|
||
|
from .serializers import *
|
||
|
from .utils import *
|
||
|
from ldap.ldapobject import LDAPObject
|
||
|
|
||
|
logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class PricingView(TemplateView):
|
||
|
template_name = "nextcloud/pricing.html"
|
||
|
|
||
|
class IndexView(FormView):
|
||
|
template_name = "nextcloud/index.html"
|
||
|
form_class = InitialRequestForm
|
||
|
success_url = "/nextcloud#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['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('nextcloud:payment'))
|
||
|
|
||
|
|
||
|
class OrderPaymentView(FormView):
|
||
|
template_name = 'nextcloud/order_details.html'
|
||
|
success_url = 'nextcloud: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}
|
||
|
)
|
||
|
if self.request.GET.get('product', False):
|
||
|
matched_prod = Product.objects.filter(name=self.request.GET.get('product')).first()
|
||
|
if matched_prod:
|
||
|
self.request.session['order'] = matched_prod.config
|
||
|
|
||
|
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({
|
||
|
'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)
|
||
|
context = self.get_context_data()
|
||
|
if not details_form.is_valid() or not billing_address_form.is_valid():
|
||
|
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('nextcloud: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'], context['vm_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) - 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('nextcloud:payment')
|
||
|
)
|
||
|
return HttpResponseRedirect(reverse('nextcloud:order_confirmation'))
|
||
|
|
||
|
class OrderConfirmationView(DetailView):
|
||
|
template_name = "nextcloud/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('nextcloud:index'))
|
||
|
elif 'pricing' not in self.request.session or 'vat_validation_status' not in self.request.session:
|
||
|
return HttpResponseRedirect(reverse('nextcloud:payment'))
|
||
|
|
||
|
total = self.request.session['pricing']['total']
|
||
|
amount = get_balance_for_user(self.request.user) - 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']['domain'] = domains_form.cleaned_data.get('subdomain') + "." + domains_form.cleaned_data.get('main_domain')
|
||
|
try:
|
||
|
amount = get_balance_for_user(self.request.user) - 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) - 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('nextcloud: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('uncloud_pay:order_success'))
|
||
|
except CardError as e:
|
||
|
messages.add_message(
|
||
|
self.request, messages.ERROR, e.user_message,
|
||
|
extra_tags='error'
|
||
|
)
|
||
|
return HttpResponseRedirect(
|
||
|
reverse('nextcloud:order_confirmation')
|
||
|
)
|
||
|
context = self.get_context_data()
|
||
|
context['domains_form'] = domains_form
|
||
|
return self.render_to_response(context)
|
||
|
|
||
|
class OrdersView(ListView):
|
||
|
template_name = "nextcloud/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()
|
||
|
if hasattr(order, 'instance_id'):
|
||
|
last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last()
|
||
|
schedule('nextcloud.tasks.delete_instance',
|
||
|
order.instance_id,
|
||
|
schedule_type=Schedule.ONCE,
|
||
|
next_run=last_bill_record.ending_date or (timezone.now() + datetime.timedelta(hours=1)))
|
||
|
return JsonResponse({'message': 'Successfully Cancelled'})
|
||
|
|
||
|
class InstancesView(ListView):
|
||
|
template_name = "nextcloud/instances.html"
|
||
|
model = VMMachine
|
||
|
|
||
|
@method_decorator(login_required)
|
||
|
def dispatch(self, *args, **kwargs):
|
||
|
return super().dispatch(*args, **kwargs)
|
||
|
|
||
|
def get_queryset(self):
|
||
|
return VMMachine.objects.filter(owner=self.request.user).order_by('-creation_date')
|
||
|
|