import logging import json 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 RequestHostedVMForm, BillingAddressForm from django.urls import reverse from django.conf import settings from django.http import ( HttpResponseRedirect, JsonResponse ) 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 import uncloud_pay.stripe as uncloud_stripe from .models import VMInstance from .serializers import * logger = logging.getLogger(__name__) class PricingView(View): def get(self, request, **args): subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, discount = get_order_total_with_vat( request.GET.get('cores'), request.GET.get('memory'), request.GET.get('storage'), pricing_name = args['name'] ) return JsonResponse({'subtotal': subtotal}) class IndexView(FormView): template_name = "matrixhosting/index.html" form_class = RequestHostedVMForm 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 subtotal, subtotal_with_discount, total, vat, vat_percent, discount = 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'] = {'name': form.cleaned_data['pricing_name'], 'subtotal': subtotal, 'vat': vat, 'vat_percent': vat_percent, 'discount': discount} return HttpResponseRedirect(reverse('matrix:payment')) class OrderPaymentView(FormView): template_name = 'matrixhosting/payment.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) 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} ) customer_id = uncloud_stripe.get_customer_id_for(self.request.user) cards = uncloud_stripe.get_customer_cards(customer_id) context.update({ 'matrix_vm_pricing': PricingPlan.get_by_name(self.request.session.get('pricing', {'name': 'unknown'})['name']), 'billing_address_form': billing_address_form, 'cards': cards, 'stripe_key': settings.STRIPE_PUBLIC_KEY }) 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']: if request.session.get(k): request.session.pop(k) if 'order' not in request.session: return HttpResponseRedirect(reverse('matrix:index')) return self.render_to_response(self.get_context_data()) def form_valid(self, address_form): id_payment_method = self.request.POST.get('id_payment_method', None) self.request.session["id_payment_method"] = id_payment_method this_user = { 'email': self.request.user.email, 'username': self.request.user.username } customer_id = uncloud_stripe.get_customer_id_for(self.request.user) uncloud_stripe.attach_payment_method(id_payment_method, customer_id) 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'] = address_form.cleaned_data self.request.session['billing_address_data']['owner'] = self.request.user.id self.request.session['user'] = this_user self.request.session['customer'] = customer_id vat_number = address_form.cleaned_data.get('vat_number').strip() if vat_number: 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='vat_error' ) return HttpResponseRedirect( reverse('matrix:payment') + '#vat_error' ) self.request.session["vat_validation_status"] = validate_result["status"] return HttpResponseRedirect(reverse('matrix:order_details')) class OrderDetailsView(DetailView): template_name = "matrixhosting/order_detail.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 = {} if ('order' not in request.session or 'user' not in request.session): return HttpResponseRedirect(reverse('matrix:index')) if 'id_payment_method' in self.request.session: card = uncloud_stripe.get_card_from_payment(self.request.user, self.request.session['id_payment_method']) if not card: return HttpResponseRedirect(reverse('matrix:payment')) context['card'] = card elif 'id_payment_method' not in self.request.session or 'vat_validation_status' not in self.request.session: return HttpResponseRedirect(reverse('matrix:payment')) specs = request.session.get('order') pricing = request.session.get('pricing') billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id')) vat_rate = VATRate.get_vat_rate(billing_address) vat_validation_status = "verified" if billing_address.vat_number_validated_on and billing_address.vat_number_verified else False subtotal, subtotal_after_discount, price_after_discount_with_vat, vat, vat_percent, discount = 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 ) pricing = { "subtotal": subtotal, "discount": discount, "vat": vat, "vat_percent": vat_percent, "vat_country": billing_address.country.lower(), "subtotal_after_discount": subtotal_after_discount, "price_after_discount_with_vat": price_after_discount_with_vat } pricing["price_with_vat"] = round(subtotal * (1 + pricing["vat_percent"] * 0.01), 2) discount["amount_with_vat"] = round(pricing["price_with_vat"] - pricing["price_after_discount_with_vat"], 2) pricing["total_price"] = pricing["price_after_discount_with_vat"] self.request.session['total_price'] = pricing["price_after_discount_with_vat"] payment_intent_response = uncloud_stripe.get_payment_intent(request.user, pricing["price_after_discount_with_vat"]) context.update({ 'payment_intent_secret': payment_intent_response.client_secret, 'order': specs, 'pricing': pricing, 'stripe_key': settings.STRIPE_PUBLIC_KEY, }) return render(request, self.template_name, context) def post(self, request, *args, **kwargs): customer = StripeCustomer.objects.get(owner=self.request.user) billing_address = BillingAddress.objects.get(id=request.session.get('billing_address_id')) if 'id_payment_method' in request.session: card = uncloud_stripe.get_card_from_payment(self.request.user, self.request.session['id_payment_method']) if not card: return show_error("There was a payment related error.", self.request) else: return show_error("There was a payment related error.", self.request) order = finalize_order(request, customer, billing_address, self.request.session['total_price'], PricingPlan.get_by_name(self.request.session['pricing']['name']), request.session.get('order')) if order: bill = Bill.create_next_bill_for_user_address(billing_address) payment= Payment.objects.create(owner=request.user, amount=self.request.session['total_price'], source='stripe') if payment: #Close the bill as the payment has been added bill.close() response = { 'status': True, 'redirect': (reverse('matrix:dashboard')), 'msg_title': str(_('Thank you for the order.')), 'msg_body': str( _('Your VM will be up and running in a few moments.' ' We will send you a confirmation email as soon as' ' it is ready.')) } return JsonResponse(response) def finalize_order(request, customer, billing_address, one_time_price, pricing_plan, specs): product = Product.objects.first() recurring_period_product = ProductToRecurringPeriod.objects.filter(product=product, is_default=True).first() order = Order.objects.create( owner=request.user, customer=customer, billing_address=billing_address, one_time_price=one_time_price, pricing_plan=pricing_plan, recurring_period= recurring_period_product.recurring_period, product = product, config=json.dumps(specs) ) return order class Dashboard(ListView): template_name = "matrixhosting/dashboard.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) def post(self, request, *args, **kwargs): order = Order.objects.get(id=request.POST.get('order_id', 0)) order.cancel() return JsonResponse({'message': 'Successfully Cancelled'}) def get_error_response_dict(request): response = { 'status': False, 'redirect': "{url}#{section}".format( url=(reverse('matrix:payment')), section='payment_error' ), 'msg_title': str(_('Error.')), 'msg_body': str( _('There was a payment related error.' ' On close of this popup, you will be redirected back to' ' the payment page.')) } return response def show_error(msg, request): messages.add_message(request, messages.ERROR, msg, extra_tags='failed_payment') return JsonResponse(get_error_response_dict(request)) class MachineViewSet(viewsets.ReadOnlyModelViewSet): serializer_class = VMInstanceSerializer permission_classes = [permissions.IsAuthenticated] def get_queryset(self): return VMInstance.objects.filter(owner=self.request.user)