Merge pull request #361 from pcoder/new_flow_enhancement

New flow enhancement
This commit is contained in:
Pcoder 2017-06-30 19:13:37 +02:00 committed by GitHub
commit 0ad3e0d7c7
5 changed files with 203 additions and 151 deletions

View file

@ -1,6 +1,7 @@
{% extends "hosting/base_short.html" %} {% extends "hosting/base_short.html" %}
{% load staticfiles bootstrap3 %} {% load staticfiles bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load custom_tags %}
{% block content %} {% block content %}
<div class="order-detail-container"> <div class="order-detail-container">
@ -19,37 +20,38 @@
{% if not error %} {% if not error %}
<div class="row"> <div class="row">
<div class="col-xs-12 col-md-8 col-md-offset-2"> <div class="col-xs-12 col-md-8 col-md-offset-2">
<div class="invoice-title"> <div class="invoice-title">
<h2>{% trans "Confirm Order"%}</h2><h3 class="pull-right">{% trans "Order #"%} {{order.id}}</h3> <h2>{% trans "Confirm Order"%}</h2>
</div> </div>
<hr> <hr>
<div class="row"> <div class="row">
<div class="col-xs-6"> <div class="col-xs-6">
<address> <address>
<h3><b>{% trans "Billed To:"%}</b></h3> <h3><b>{% trans "Billed To:"%}</b></h3>
{{user.name}}<br> {% with request.session.billing_address_data as billing_address %}
{{order.billing_address.street_address}},{{order.billing_address.postal_code}}<br> {{request.session.user.name}}<br> {{billing_address|get_value_from_dict:'street_address'}}, {{billing_address|get_value_from_dict:'postal_code'}}<br>
{{order.billing_address.city}}, {{order.billing_address.country}}. {{billing_address|get_value_from_dict:'city'}}, {{billing_address|get_value_from_dict:'country'}}.
</address> {% endwith %}
</div> </address>
</div>
<div class="col-xs-6 text-right"> <div class="col-xs-6 text-right">
<address> <address>
<strong>{% trans "Date"%}:</strong><br> <strong>{% trans "Date"%}:</strong><br>
{{order.created_at}}<br><br> <span id="order-created_at">{% now "Y-m-d H:i" %}</span><br><br>
</address> </address>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col-xs-6"> <div class="col-xs-6">
<address> <address>
<strong>{% trans "Payment Method:"%}</strong><br> <strong>{% trans "Payment Method:"%}</strong><br>
{{order.cc_brand}} ending **** {{order.last4}}<br> {{cc_brand}} ending **** {{cc_last4}}<br>
{{user.email}} {{request.session.user.email}}
</address> </address>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
@ -57,23 +59,37 @@
<h3><b>{% trans "Order summary"%}</b></h3> <h3><b>{% trans "Order summary"%}</b></h3>
<hr> <hr>
<div class="content"> <div class="content">
<p><b>{% trans "Cores"%}</b> <span class="pull-right">{{vm.cores}}</span></p> {% with request.session.specs as vm %}
<hr> <p><b>{% trans "Cores"%}</b> <span class="pull-right">{{vm.cpu}}</span></p>
<p><b>{% trans "Memory"%}</b> <span class="pull-right">{{vm.memory}} GB</span></p> <hr>
<hr> <p><b>{% trans "Memory"%}</b> <span class="pull-right">{{vm.memory}} GB</span></p>
<p><b>{% trans "Disk space"%}</b> <span class="pull-right">{{vm.disk_size}} GB</span></p> <hr>
<hr> <p><b>{% trans "Disk space"%}</b> <span class="pull-right">{{vm.disk_size}} GB</span></p>
<h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} CHF</b></p></h4> <hr>
<h4>{% trans "Total"%}<p class="pull-right"><b>{{vm.price}} CHF</b></p></h4>
{% endwith %}
</div> </div>
<br/> <br/>
{% url 'datacenterlight:payment' as payment_url %} <form method="post">
{% if payment_url in request.META.HTTP_REFERER %} {% csrf_token %}
<div class=" content pull-right"> <div class=" content pull-right">
<a href="{{next_url}}" ><button class="btn btn-info">{% trans "Finish Configuration"%}</button></a> <a href="{{next_url}}" ><button class="btn btn-info">{% trans "Place order"%}</button></a>
</div> </div>
{% endif %} </form>
</div> </div>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<script type="text/javascript">
window.onload = function () {
var locale_date = moment.utc(document.getElementById("order-created_at").textContent,'YYYY-MM-DD HH:mm').toDate();
locale_date = moment(locale_date).format("YYYY-MM-DD h:mm:ss a");
document.getElementById('order-created_at').innerHTML = locale_date;
};
</script>
{%endblock%} {%endblock%}

View file

@ -22,3 +22,13 @@ def change_lang(context, lang=None, *args, **kwargs):
return "%s" % url return "%s" % url
@register.filter('get_value_from_dict')
def get_value_from_dict(dict_data, key):
"""
usage example {{ your_dict|get_value_from_dict:your_key }}
"""
if key:
return dict_data.get(key)
else :
return ""

View file

@ -9,7 +9,7 @@ urlpatterns = [
url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'), url(r'^/landing/?$', LandingProgramView.as_view(), name='landing'),
url(r'^/pricing/?$', PricingView.as_view(), name='pricing'), url(r'^/pricing/?$', PricingView.as_view(), name='pricing'),
url(r'^/payment/?$', PaymentOrderView.as_view(), name='payment'), url(r'^/payment/?$', PaymentOrderView.as_view(), name='payment'),
url(r'^/order-confirmation/(?P<pk>\d+)/?$', OrderConfirmationView.as_view(), name='order_confirmation'), url(r'^/order-confirmation/?$', OrderConfirmationView.as_view(), name='order_confirmation'),
url(r'^/order-success/?$', SuccessView.as_view(), name='order_success'), url(r'^/order-success/?$', SuccessView.as_view(), name='order_success'),
url(r'^/beta_access?$', BetaAccessView.as_view(), name='beta_access'), url(r'^/beta_access?$', BetaAccessView.as_view(), name='beta_access'),
] ]

View file

@ -13,6 +13,7 @@ from django.core.exceptions import ValidationError
from django.views.decorators.cache import cache_control from django.views.decorators.cache import cache_control
from django.conf import settings from django.conf import settings
from utils.forms import BillingAddressForm, UserBillingAddressForm from utils.forms import BillingAddressForm, UserBillingAddressForm
from utils.models import BillingAddress
from membership.models import StripeCustomer from membership.models import StripeCustomer
from hosting.models import HostingOrder, HostingBill from hosting.models import HostingOrder, HostingBill
from utils.stripe_utils import StripeUtils from utils.stripe_utils import StripeUtils
@ -31,9 +32,14 @@ class SuccessView(TemplateView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if 'specs' not in request.session or 'user' not in request.session: if 'specs' not in request.session or 'user' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:index')) return HttpResponseRedirect(reverse('datacenterlight:index'))
else : elif 'token' not in request.session:
del request.session['specs'] return HttpResponseRedirect(reverse('datacenterlight:payment'))
del request.session['user'] elif 'order_confirmation' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:order_confirmation'))
else:
for session_var in ['specs', 'user', 'template', 'billing_address', 'billing_address_data', 'token', 'customer']:
if session_var in request.session:
del request.session[session_var]
return render(request, self.template_name) return render(request, self.template_name)
class PricingView(TemplateView): class PricingView(TemplateView):
@ -322,13 +328,9 @@ class PaymentOrderView(FormView):
if form.is_valid(): if form.is_valid():
# Get billing address data # Get billing address data
billing_address_data = form.cleaned_data billing_address_data = form.cleaned_data
context = self.get_context_data()
template = request.session.get('template')
specs = request.session.get('specs')
user = request.session.get('user')
vm_template_id = template.get('id', 1)
final_price = specs.get('price')
token = form.cleaned_data.get('token') token = form.cleaned_data.get('token')
user = request.session.get('user')
try: try:
custom_user = CustomUser.objects.get(email=user.get('email')) custom_user = CustomUser.objects.get(email=user.get('email'))
except CustomUser.DoesNotExist: except CustomUser.DoesNotExist:
@ -340,7 +342,6 @@ class PaymentOrderView(FormView):
app='dcl', app='dcl',
base_url=None, send_email=False) base_url=None, send_email=False)
# Get or create stripe customer # Get or create stripe customer
customer = StripeCustomer.get_or_create(email=user.get('email'), customer = StripeCustomer.get_or_create(email=user.get('email'),
token=token) token=token)
@ -350,115 +351,130 @@ class PaymentOrderView(FormView):
# Create Billing Address # Create Billing Address
billing_address = form.save() billing_address = form.save()
request.session['billing_address_data'] = billing_address_data
# Make stripe charge to a customer request.session['billing_address'] = billing_address.id
stripe_utils = StripeUtils() request.session['token'] = token
charge_response = stripe_utils.make_charge(amount=final_price, request.session['customer'] = customer.id
customer=customer.stripe_id) return HttpResponseRedirect(reverse('datacenterlight:order_confirmation'))
charge = charge_response.get('response_object')
# 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')
# Create OpenNebulaManager
manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME,
password=settings.OPENNEBULA_PASSWORD)
# Create a vm using logged user
vm_id = manager.create_vm(
template_id=vm_template_id,
specs=specs,
vm_name="{email}-{template_name}-{date}".format(
email=user.get('email'),
template_name=template.get('name'),
date=int(datetime.now().strftime("%s")))
)
# Create a Hosting Order
order = HostingOrder.create(
price=final_price,
vm_id=vm_id,
customer=customer,
billing_address=billing_address
)
# Create a Hosting Bill
bill = HostingBill.create(
customer=customer, billing_address=billing_address)
# Create Billing Address for User if he does not have one
if not customer.user.billing_addresses.count():
billing_address_data.update({
'user': customer.user.id
})
billing_address_user_form = UserBillingAddressForm(
billing_address_data)
billing_address_user_form.is_valid()
billing_address_user_form.save()
# 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()
vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
context = {
'name': user.get('name'),
'email': user.get('email'),
'cores': specs.get('cpu'),
'memory': specs.get('memory'),
'storage': specs.get('disk_size'),
'price': specs.get('price'),
'template': template.get('name'),
'vm.name': vm['name'],
'vm.id': vm['vm_id'],
'order.id': order.id
}
email_data = {
'subject': "Data Center Light Order from %s" % context['email'],
'from_email': '(Data Center Light) Data Center Light Support <support@datacenterlight.ch>',
'to': ['info@ungleich.ch'],
'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]),
'reply_to': [context['email']],
}
email = EmailMessage(**email_data)
email.send()
return HttpResponseRedirect(reverse('datacenterlight:order_confirmation', kwargs={'pk': order.id}))
else: else:
return self.form_invalid(form) return self.form_invalid(form)
class OrderConfirmationView(DetailView): class OrderConfirmationView(DetailView):
template_name = "datacenterlight/order_detail.html" template_name = "datacenterlight/order_detail.html"
payment_template_name = 'hosting/payment.html'
context_object_name = "order" context_object_name = "order"
model = HostingOrder model = HostingOrder
def get_context_data(self, **kwargs):
# Get context @cache_control(no_cache=True, must_revalidate=True, no_store=True)
context = super(DetailView, self).get_context_data(**kwargs) def get(self, request, *args, **kwargs):
obj = self.get_object() if 'specs' not in request.session or 'user' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:index'))
if 'token' not in request.session:
return HttpResponseRedirect(reverse('datacenterlight:payment'))
stripe_customer_id = request.session.get('customer')
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
stripe_utils = StripeUtils()
card_details = stripe_utils.get_card_details(customer.stripe_id, request.session.get('token'))
context = {
'cc_last4' : card_details.get('response_object').get('last4'),
'cc_brand' : card_details.get('response_object').get('brand')
}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
template = request.session.get('template')
specs = request.session.get('specs')
user = request.session.get('user')
stripe_customer_id = request.session.get('customer')
customer = StripeCustomer.objects.filter(id=stripe_customer_id).first()
billing_address_data = request.session.get('billing_address_data')
billing_address_id = request.session.get('billing_address')
billing_address = BillingAddress.objects.filter(id=billing_address_id).first()
token = request.session.get('token')
vm_template_id = template.get('id', 1)
final_price = specs.get('price')
# Make stripe charge to a customer
stripe_utils = StripeUtils()
charge_response = stripe_utils.make_charge(amount=final_price,
customer=customer.stripe_id)
charge = charge_response.get('response_object')
# Check if the payment was approved
if not charge:
context.update({
'paymentError': charge_response.get('error')
# TODO add logic in payment form to autofill data
#'form': form
})
return render(request, self.payment_template_name, context)
charge = charge_response.get('response_object')
# Create OpenNebulaManager
manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME, manager = OpenNebulaManager(email=settings.OPENNEBULA_USERNAME,
password=settings.OPENNEBULA_PASSWORD) password=settings.OPENNEBULA_PASSWORD)
try:
vm = manager.get_vm(obj.vm_id) # Create a vm using oneadmin, also specify the name
context['vm'] = VirtualMachineSerializer(vm).data vm_id = manager.create_vm(
context['next_url'] = reverse('datacenterlight:order_success') template_id=vm_template_id,
except WrongIdError: specs=specs,
messages.error(self.request, vm_name="{email}-{template_name}-{date}".format(
'The VM you are looking for is unavailable at the moment. \ email=user.get('email'),
Please contact Data Center Light support.' template_name=template.get('name'),
) date=int(datetime.now().strftime("%s")))
self.kwargs['error'] = 'WrongIdError' )
context['error'] = 'WrongIdError'
except ConnectionRefusedError: # Create a Hosting Order
messages.error(self.request, order = HostingOrder.create(
'In order to create a VM, you need to create/upload your SSH KEY first.' price=final_price,
) vm_id=vm_id,
return context customer=customer,
billing_address=billing_address
)
# Create a Hosting Bill
bill = HostingBill.create(
customer=customer, billing_address=billing_address)
# Create Billing Address for User if he does not have one
if not customer.user.billing_addresses.count():
billing_address_data.update({
'user': customer.user.id
})
billing_address_user_form = UserBillingAddressForm(
billing_address_data)
billing_address_user_form.is_valid()
billing_address_user_form.save()
# 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()
vm = VirtualMachineSerializer(manager.get_vm(vm_id)).data
context = {
'name': user.get('name'),
'email': user.get('email'),
'cores': specs.get('cpu'),
'memory': specs.get('memory'),
'storage': specs.get('disk_size'),
'price': specs.get('price'),
'template': template.get('name'),
'vm.name': vm['name'],
'vm.id': vm['vm_id'],
'order.id': order.id
}
email_data = {
'subject': settings.DCL_TEXT + " Order from %s" % context['email'],
'from_email': settings.DCL_SUPPORT_FROM_ADDRESS,
'to': ['info@ungleich.ch'],
'body': "\n".join(["%s=%s" % (k, v) for (k, v) in context.items()]),
'reply_to': [context['email']],
}
email = EmailMessage(**email_data)
email.send()
request.session['order_confirmation'] = True
return HttpResponseRedirect(reverse('datacenterlight:order_success'))

View file

@ -77,6 +77,16 @@ class StripeUtils(object):
} }
return new_card_data return new_card_data
@handleStripeError
def get_card_details(self, customer_id, token):
customer = stripe.Customer.retrieve(customer_id)
credit_card_raw_data = customer.sources.data.pop()
card_details = {
'last4': credit_card_raw_data.last4,
'brand': credit_card_raw_data.brand
}
return card_details
def check_customer(self, id, user, token): def check_customer(self, id, user, token):
customers = self.stripe.Customer.all() customers = self.stripe.Customer.all()
if not customers.get('data'): if not customers.get('data'):