From 46ab3641846fab55a42d40b2d014c21f547d1521 Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 27 Apr 2016 01:54:15 -0500 Subject: [PATCH] Handled stripe payment errors , Added invoice template, Added view to handle invoice data --- hosting/templates/hosting/invoice.html | 79 ++++++++++++++++++++++++++ hosting/templates/hosting/payment.html | 14 ++++- hosting/urls.py | 4 +- hosting/views.py | 66 +++++++++++++++++---- utils/stripe_utils.py | 55 +++++++++++++++++- 5 files changed, 202 insertions(+), 16 deletions(-) create mode 100644 hosting/templates/hosting/invoice.html 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 %} + + + +
+
+
+
+

Invoice

Order # {{order.id}}

+
+
+
+
+
+

Billed To:

+ John Smith
+ 1234 Main
+ Apt. 4B
+ Springfield, ST 54321 +
+
+
+
+ Order Date:
+ {{order.created_at}}

+
+
+
+
+
+
+ Payment Method:
+ {{brand}} ending **** {{last4}}
+ {{user.email}} +
+
+
+
+
+ +
+
+

Order summary

+
+
+

Type {{request.session.vm_specs.hosting_company_name}}

+
+

Cores {{request.session.vm_specs.cores}}

+
+

Memory {{request.session.vm_specs.memory}} GiB

+
+

Disk space {{request.session.vm_specs.disk_size}} GiB

+
+

Total

{{request.session.vm_specs.final_price}} CHF

+
+
+
+
+{%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 %}
@@ -64,6 +64,17 @@

+ {% if paymentError %} +
+
+

+ {% bootstrap_alert paymentError alert_type='danger' %} +

+
+
+ {% endif %} + + @@ -88,6 +99,7 @@ + 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,