import logging import json import decimal 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 matrixhosting.forms import InitialRequestForm, RequestDomainsNamesForm from uncloud_pay.forms import BillingAddressForm from django.urls import reverse from django.conf import settings from django.http import ( HttpResponseRedirect, JsonResponse, HttpResponse ) from django.utils import timezone from django_q.models import Schedule from django_q.tasks import schedule 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 * from ldap.ldapobject import LDAPObject logger = logging.getLogger(__name__) class PricingView(TemplateView): template_name = "matrixhosting/pricing.html" 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} ) 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({ '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) 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('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'], context['matrix_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('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) - 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) - 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('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('uncloud_pay: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 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() if hasattr(order, 'matrix_instance_id'): last_bill_record = BillRecord.objects.filter(order=order).order_by('id').last() schedule('matrixhosting.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 = "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') @require_POST @csrf_exempt def handle_k8s_webhook(request): payload = request.body print(">>>>>>>>>>>>>>>>>>>>>>>>>>") print(payload) return HttpResponse(status=200)