diff --git a/hosting/templates/hosting/invoice.html b/hosting/templates/hosting/invoice.html new file mode 100644 index 00000000..302028fe --- /dev/null +++ b/hosting/templates/hosting/invoice.html @@ -0,0 +1,79 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 %} +{% block content %} + +<style type="text/css"> +.invoice-title h2, .invoice-title h3 { + display: inline-block; +} + +.table > tbody > tr > .no-line { + border-top: none; +} + +.table > thead > tr > .no-line { + border-bottom: none; +} + +.table > tbody > tr > .thick-line { + border-top: 2px solid; +} + + +</style> + +<div class="container payment-container"> + <div class="row"> + <div class="col-xs-8 col-xs-offset-2"> + <div class="invoice-title"> + <h2>Invoice</h2><h3 class="pull-right">Order # {{order.id}}</h3> + </div> + <hr> + <div class="row"> + <div class="col-xs-6"> + <address> + <h3><b>Billed To:</b></h3> + John Smith<br> + 1234 Main<br> + Apt. 4B<br> + Springfield, ST 54321 + </address> + </div> + <div class="col-xs-6 text-right"> + <address> + <strong>Order Date:</strong><br> + {{order.created_at}}<br><br> + </address> + </div> + </div> + <div class="row"> + <div class="col-xs-6"> + <address> + <strong>Payment Method:</strong><br> + {{brand}} ending **** {{last4}}<br> + {{user.email}} + </address> + </div> + </div> + </div> + </div> + + <div class="row"> + <div class="col-md-8 col-md-offset-2"> + <h3><b>Order summary</b></h3> + <hr> + <div class="content"> + <p><b>Type</b> <span class="pull-right">{{request.session.vm_specs.hosting_company_name}}</span></p> + <hr> + <p><b>Cores</b> <span class="pull-right">{{request.session.vm_specs.cores}}</span></p> + <hr> + <p><b>Memory</b> <span class="pull-right">{{request.session.vm_specs.memory}} GiB</span></p> + <hr> + <p><b>Disk space</b> <span class="pull-right">{{request.session.vm_specs.disk_size}} GiB</span></p> + <hr> + <h4>Total<p class="pull-right"><b>{{request.session.vm_specs.final_price}} CHF</b></p></h4> + </div> + </div> + </div> +</div> +{%endblock%} diff --git a/hosting/templates/hosting/payment.html b/hosting/templates/hosting/payment.html index 1ebb1770..ec942131 100644 --- a/hosting/templates/hosting/payment.html +++ b/hosting/templates/hosting/payment.html @@ -1,6 +1,6 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 %} -{%block content %} +{% block content %} <!-- Credit card form --> <div> <div class="container payment-container"> @@ -64,6 +64,17 @@ <p class="payment-errors"></p> </div> </div> + {% if paymentError %} + <div class="row"> + <div class="col-xs-12"> + <p> + {% bootstrap_alert paymentError alert_type='danger' %} + </p> + </div> + </div> + {% endif %} + + </form> </div> </div> @@ -88,6 +99,7 @@ </div> </div> </form> + </div> diff --git a/hosting/urls.py b/hosting/urls.py index 1dbd1a54..f50b7a2f 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -1,7 +1,8 @@ from django.conf.urls import url from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \ - NodeJSHostingView, LoginView, SignupView, IndexView + NodeJSHostingView, LoginView, SignupView, IndexView, \ + InvoiceVMView urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), @@ -11,4 +12,5 @@ urlpatterns = [ url(r'login/?$', LoginView.as_view(), name='login'), url(r'signup/?$', SignupView.as_view(), name='signup'), url(r'payment/?$', PaymentVMView.as_view(), name='payment'), + url(r'invoice/?$', InvoiceVMView.as_view(), name='invoice'), ] diff --git a/hosting/views.py b/hosting/views.py index 16556bc1..75ce03eb 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -12,6 +12,7 @@ from membership.forms import PaymentForm from membership.models import CustomUser, StripeCustomer from utils.stripe_utils import StripeUtils from utils.forms import BillingAddressForm +from utils.models import BillingAddress from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder from .forms import HostingUserSignupForm, HostingUserLoginForm from .mixins import ProcessVMSelectionMixin @@ -155,7 +156,7 @@ class PaymentVMView(FormView): form = self.get_form() if form.is_valid(): - + context = self.get_context_data() specifications = request.session.get('vm_specs') vm_type = specifications.get('hosting_company') vm = VirtualMachineType.objects.get(hosting_company=vm_type) @@ -185,20 +186,63 @@ class PaymentVMView(FormView): # Make stripe charge to a customer stripe_utils = StripeUtils() - charge = stripe_utils.make_charge(amount=final_price, + charge_response = stripe_utils.make_charge(amount=final_price, customer=customer.stripe_id) - order.set_stripe_charge(charge) + charge = charge_response.get('response_object') - if not charge.paid: - # raise an error - pass + # Check if the payment was approved + if not charge: + context.update({ + 'paymentError': charge_response.get('error'), + 'form':form + }) + return render(request, self.template_name, context) + + charge = charge_response.get('response_object') + + # Associate an order with a stripe payment + order.set_stripe_charge(charge) # If the Stripe payment was successed, set order status approved order.set_approved() - # order.charge = - - # Billing Address should be store here - - return HttpResponseRedirect(reverse('hosting:payment')) + request.session.update({ + 'charge':charge, + 'order':order.id, + 'billing_address':billing_address.id + }) + return HttpResponseRedirect(reverse('hosting:invoice')) else: return self.form_invalid(form) + + +class InvoiceVMView(View): + template_name = "hosting/invoice.html" + + def get_context_data(self, **kwargs): + charge = self.request.session.get('charge') + order_id = self.request.session.get('order') + billing_address_id = self.request.session.get('billing_address') + last4 = charge.get('source').get('last4') + brand = charge.get('source').get('brand') + + order = get_object_or_404(HostingOrder, pk=order_id) + billing_address = get_object_or_404(BillingAddress, pk=billing_address_id) + + if not charge: + return + + context = { + 'last4': last4, + 'brand': brand, + 'order': order, + 'billing_address': billing_address, + } + return context + + + def get(self, request, *args, **kwargs): + + context = self.get_context_data() + + return render(request, self.template_name, context) + diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index cfe1e63a..eb49fcde 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -2,6 +2,54 @@ import stripe from django.conf import settings + +def handleStripeError(f): + def handleProblems(*args, **kwargs): + response = { + 'paid': False, + 'response_object': None, + 'error': None + } + common_message = "Currently its not possible to make payments." + + try: + response_object = f(*args, **kwargs) + response = { + 'response_object': response_object, + 'error': None + } + return response + except stripe.error.CardError as e: + # Since it's a decline, stripe.error.CardError will be caught + body = e.json_body + err = body['error'] + response.update({'error':err['message']}) + return response + except stripe.error.RateLimitError as e: + response.update({'error':"Too many requests made to the API too quickly"}) + return response + except stripe.error.InvalidRequestError as e: + response.update({'error':"Invalid parameters"}) + return response + except stripe.error.AuthenticationError as e: + # Authentication with Stripe's API failed + # (maybe you changed API keys recently) + response.update({'error':common_message}) + return response + except stripe.error.APIConnectionError as e: + response.update({'error':common_message}) + return response + except stripe.error.StripeError as e: + # maybe send email + response.update({'error':common_message}) + return response + except Exception as e: + # maybe send email + response.update({'error':common_message}) + return response + return handleProblems + + class StripeUtils(object): CURRENCY = 'chf' @@ -12,18 +60,18 @@ class StripeUtils(object): self.stripe = stripe self.stripe.api_key = settings.STRIPE_API_PRIVATE_KEY + @handleStripeError def create_customer(self, token, email): - stripe.api_key = settings.STRIPE_API_PRIVATE_KEY - customer = stripe.Customer.create( + customer = self.stripe.Customer.create( source=token, description='description for testing', email=email ) return customer + @handleStripeError def make_charge(self, amount=None, customer=None): amount = int(amount * 100) # stripe amount unit, in cents - charge = self.stripe.Charge.create( amount=amount, # in cents currency=self.CURRENCY, @@ -31,6 +79,7 @@ class StripeUtils(object): ) return charge + @handleStripeError def create_plan(self, amount, name, id): self.stripe.Plan.create( amount=amount,