Merge pull request #50 from levivm/feature/vm_pricing

Handled stripe payment errors , Added payment invoice
This commit is contained in:
Levi Velázquez 2016-04-29 01:53:55 -05:00
commit 484c80d15a
5 changed files with 202 additions and 16 deletions

View file

@ -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%}

View file

@ -1,6 +1,6 @@
{% extends "hosting/base_short.html" %} {% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 %} {% load staticfiles bootstrap3 %}
{%block content %} {% block content %}
<!-- Credit card form --> <!-- Credit card form -->
<div> <div>
<div class="container payment-container"> <div class="container payment-container">
@ -64,6 +64,17 @@
<p class="payment-errors"></p> <p class="payment-errors"></p>
</div> </div>
</div> </div>
{% if paymentError %}
<div class="row">
<div class="col-xs-12">
<p>
{% bootstrap_alert paymentError alert_type='danger' %}
</p>
</div>
</div>
{% endif %}
</form> </form>
</div> </div>
</div> </div>
@ -88,6 +99,7 @@
</div> </div>
</div> </div>
</form> </form>
</div> </div>

View file

@ -1,7 +1,8 @@
from django.conf.urls import url from django.conf.urls import url
from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \ from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \
NodeJSHostingView, LoginView, SignupView, IndexView NodeJSHostingView, LoginView, SignupView, IndexView, \
InvoiceVMView
urlpatterns = [ urlpatterns = [
url(r'index/?$', IndexView.as_view(), name='index'), url(r'index/?$', IndexView.as_view(), name='index'),
@ -11,4 +12,5 @@ urlpatterns = [
url(r'login/?$', LoginView.as_view(), name='login'), url(r'login/?$', LoginView.as_view(), name='login'),
url(r'signup/?$', SignupView.as_view(), name='signup'), url(r'signup/?$', SignupView.as_view(), name='signup'),
url(r'payment/?$', PaymentVMView.as_view(), name='payment'), url(r'payment/?$', PaymentVMView.as_view(), name='payment'),
url(r'invoice/?$', InvoiceVMView.as_view(), name='invoice'),
] ]

View file

@ -12,6 +12,7 @@ from membership.forms import PaymentForm
from membership.models import CustomUser, StripeCustomer from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm from utils.forms import BillingAddressForm
from utils.models import BillingAddress
from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
from .forms import HostingUserSignupForm, HostingUserLoginForm from .forms import HostingUserSignupForm, HostingUserLoginForm
from .mixins import ProcessVMSelectionMixin from .mixins import ProcessVMSelectionMixin
@ -155,7 +156,7 @@ class PaymentVMView(FormView):
form = self.get_form() form = self.get_form()
if form.is_valid(): if form.is_valid():
context = self.get_context_data()
specifications = request.session.get('vm_specs') specifications = request.session.get('vm_specs')
vm_type = specifications.get('hosting_company') vm_type = specifications.get('hosting_company')
vm = VirtualMachineType.objects.get(hosting_company=vm_type) vm = VirtualMachineType.objects.get(hosting_company=vm_type)
@ -185,20 +186,63 @@ class PaymentVMView(FormView):
# Make stripe charge to a customer # Make stripe charge to a customer
stripe_utils = StripeUtils() stripe_utils = StripeUtils()
charge = stripe_utils.make_charge(amount=final_price, charge_response = stripe_utils.make_charge(amount=final_price,
customer=customer.stripe_id) customer=customer.stripe_id)
order.set_stripe_charge(charge) charge = charge_response.get('response_object')
if not charge.paid: # Check if the payment was approved
# raise an error if not charge:
pass 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 # If the Stripe payment was successed, set order status approved
order.set_approved() order.set_approved()
# order.charge = request.session.update({
'charge':charge,
# Billing Address should be store here 'order':order.id,
'billing_address':billing_address.id
return HttpResponseRedirect(reverse('hosting:payment')) })
return HttpResponseRedirect(reverse('hosting:invoice'))
else: else:
return self.form_invalid(form) 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)

View file

@ -2,6 +2,54 @@ import stripe
from django.conf import settings 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): class StripeUtils(object):
CURRENCY = 'chf' CURRENCY = 'chf'
@ -12,18 +60,18 @@ class StripeUtils(object):
self.stripe = stripe self.stripe = stripe
self.stripe.api_key = settings.STRIPE_API_PRIVATE_KEY self.stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
@handleStripeError
def create_customer(self, token, email): def create_customer(self, token, email):
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY customer = self.stripe.Customer.create(
customer = stripe.Customer.create(
source=token, source=token,
description='description for testing', description='description for testing',
email=email email=email
) )
return customer return customer
@handleStripeError
def make_charge(self, amount=None, customer=None): def make_charge(self, amount=None, customer=None):
amount = int(amount * 100) # stripe amount unit, in cents amount = int(amount * 100) # stripe amount unit, in cents
charge = self.stripe.Charge.create( charge = self.stripe.Charge.create(
amount=amount, # in cents amount=amount, # in cents
currency=self.CURRENCY, currency=self.CURRENCY,
@ -31,6 +79,7 @@ class StripeUtils(object):
) )
return charge return charge
@handleStripeError
def create_plan(self, amount, name, id): def create_plan(self, amount, name, id):
self.stripe.Plan.create( self.stripe.Plan.create(
amount=amount, amount=amount,