Added Hosting Order model, Created Billing Address Model , Method to create a customer using Stripe API , Created Customer Stripe profile to store for further charges , Method in order to charge an amount to a customer

This commit is contained in:
Levi 2016-04-26 01:16:03 -05:00
parent 981be2dcf4
commit fdf8722c8f
17 changed files with 281 additions and 42 deletions

View file

@ -6,8 +6,8 @@ from .views import ContactView, IndexView, AboutView
urlpatterns = [ urlpatterns = [
# url(r'^$', IndexView.as_view(), name='home'), # url(r'^$', IndexView.as_view(), name='home'),
# url(_(r'home/?$'), IndexView.as_view(), name='home'), url(_(r'home/?$'), IndexView.as_view(), name='home'),
# url(_(r'about/?$'), AboutView.as_view(), name='about'), url(_(r'about/?$'), AboutView.as_view(), name='about'),
url(_(r'contact/?$'), ContactView.as_view(), name='contact'), url(_(r'contact/?$'), ContactView.as_view(), name='contact'),
url(_(r'supporters/?$'), views.supporters, name='supporters'), url(_(r'supporters/?$'), views.supporters, name='supporters'),
url(r'calendar_api/(?P<month>\d+)/(?P<year>\d+)?$',views.CalendarApi.as_view()), url(r'calendar_api/(?P<month>\d+)/(?P<year>\d+)?$',views.CalendarApi.as_view()),

View file

@ -446,8 +446,8 @@ AUTH_USER_MODEL = 'membership.CustomUser'
# PAYMENT # PAYMENT
STRIPE_API_PUBLIC_KEY = 'pk_test_uvWyHNJgVL2IB8kjfgJkGjg4' # used in frontend to call from user browser STRIPE_API_PUBLIC_KEY = 'pk_test_QqBZ50Am8KOxaAlOxbcm9Psl' # used in frontend to call from user browser
STRIPE_API_PRIVATE_KEY = 'sk_test_uIPMdgXoRGydrcD7fkwcn7dj' # used in backend payment STRIPE_API_PRIVATE_KEY = 'sk_test_dqAmbKAij12QCGfkYZ3poGt2' # used in backend payment
STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services" STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services"
# EMAIL MESSAGES # EMAIL MESSAGES

View file

@ -28,7 +28,7 @@ urlpatterns += i18n_patterns('',
url(r'^digitalglarus/', include('digitalglarus.urls', url(r'^digitalglarus/', include('digitalglarus.urls',
namespace="digitalglarus"),name='digitalglarus'), namespace="digitalglarus"),name='digitalglarus'),
url(r'^blog/',include('ungleich.urls',namespace='ungleich')), url(r'^blog/',include('ungleich.urls',namespace='ungleich')),
url(r'^ungleich_page/',include('ungleich_page.urls',namespace='ungleich_page'),name='ungleich_page'), url(r'^',include('ungleich_page.urls',namespace='ungleich_page'),name='ungleich_page'),
url(r'^', include('cms.urls')), url(r'^', include('cms.urls')),
) )

View file

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 04:44
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('utils', '0002_billingaddress'),
('membership', '0004_stripecustomer'),
('hosting', '0008_virtualmachineplan'),
]
operations = [
migrations.CreateModel(
name='HostingOrder',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.RemoveField(
model_name='virtualmachineplan',
name='client',
),
migrations.AddField(
model_name='hostingorder',
name='VMPlan',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachinePlan'),
),
migrations.AddField(
model_name='hostingorder',
name='billing_address',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='utils.BillingAddress'),
),
migrations.AddField(
model_name='hostingorder',
name='customer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='membership.StripeCustomer'),
),
]

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 05:30
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hosting', '0009_auto_20160426_0444'),
]
operations = [
migrations.AddField(
model_name='hostingorder',
name='approved',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='hostingorder',
name='stripe_charge_id',
field=models.CharField(max_length=100, null=True),
),
]

View file

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 05:55
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('hosting', '0010_auto_20160426_0530'),
]
operations = [
migrations.AlterField(
model_name='hostingorder',
name='VMPlan',
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachinePlan'),
),
]

View file

@ -3,7 +3,8 @@ import json
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core import serializers from django.core import serializers
from membership.models import CustomUser from membership.models import StripeCustomer
from utils.models import BillingAddress
class RailsBetaUser(models.Model): class RailsBetaUser(models.Model):
@ -78,13 +79,36 @@ class VirtualMachinePlan(models.Model):
memory = models.IntegerField() memory = models.IntegerField()
disk_size = models.IntegerField() disk_size = models.IntegerField()
vm_type = models.ForeignKey(VirtualMachineType) vm_type = models.ForeignKey(VirtualMachineType)
client = models.ManyToManyField(CustomUser)
price = models.FloatField() price = models.FloatField()
@classmethod @classmethod
def create(cls, data, user): def create(cls, data, user):
instance = cls.objects.create(**data) instance = cls.objects.create(**data)
instance.client.add(user) return instance
class HostingOrder(models.Model):
VMPlan = models.OneToOneField(VirtualMachinePlan)
customer = models.ForeignKey(StripeCustomer)
billing_address = models.ForeignKey(BillingAddress)
created_at = models.DateTimeField(auto_now_add=True)
approved = models.BooleanField(default=False)
stripe_charge_id = models.CharField(max_length=100, null=True)
@classmethod
def create(cls, VMPlan=None, customer=None, billing_address=None):
instance = cls.objects.create(VMPlan=VMPlan, customer=customer,
billing_address=billing_address)
return instance
def set_approved(self):
self.approved = True
self.save()
def set_stripe_charge(self, stripe_charge):
self.stripe_charge_id = stripe_charge.id
self.save()

View file

@ -35,7 +35,7 @@ $( document ).ready(function() {
/* Visual feedback */ /* Visual feedback */
$form.find('[type=submit]').html('Validating <i class="fa fa-spinner fa-pulse"></i>'); $form.find('[type=submit]').html('Validating <i class="fa fa-spinner fa-pulse"></i>');
var PublishableKey = 'pk_test_6pRNASCoBOKtIshFeQd4XMUh'; // Replace with your API publishable key var PublishableKey = window.stripeKey;
Stripe.setPublishableKey(PublishableKey); Stripe.setPublishableKey(PublishableKey);
Stripe.card.createToken($form, function stripeResponseHandler(status, response) { Stripe.card.createToken($form, function stripeResponseHandler(status, response) {
if (response.error) { if (response.error) {
@ -52,27 +52,11 @@ $( document ).ready(function() {
$form.find('.payment-errors').text(""); $form.find('.payment-errors').text("");
// response contains id and card, which contains additional card details // response contains id and card, which contains additional card details
var token = response.id; var token = response.id;
console.log(token);
// AJAX // AJAX
//set token on a hidden input //set token on a hidden input
$('#id_token').val(token); $('#id_token').val(token);
$('#billing-form').submit(); $('#billing-form').submit();
// $.post('/hosting/payment/', {
// token: token,
// })
// // Assign handlers immediately after making the request,
// .done(function(data, textStatus, jqXHR) {
// $form.find('[type=submit]').html('Payment successful <i class="fa fa-check"></i>').prop('disabled', true);
// })
// .fail(function(jqXHR, textStatus, errorThrown) {
// $form.find('[type=submit]').html('There was a problem').removeClass('success').addClass('error');
// /* Show Stripe errors on the form */
// $form.find('.payment-errors').text('Try refreshing the page and trying again.');
// $form.find('.payment-errors').closest('.row').show();
// });
} }
}); });
} }

View file

@ -96,8 +96,15 @@
</div> </div>
</div> </div>
<!-- stripe key data -->
{% if stripe_key %}
<script type="text/javascript">
(function () {window.stripeKey = "{{stripe_key}}";})();
</script>
{%endif%}
{%endblock%} {%endblock%}

View file

@ -9,13 +9,15 @@ from django.contrib.auth import authenticate, login
from django.conf import settings from django.conf import settings
from membership.forms import PaymentForm from membership.forms import PaymentForm
from membership.models import CustomUser from membership.models import CustomUser, StripeCustomer
from utils.stripe_utils import StripeUtils
from utils.forms import BillingAddressForm from utils.forms import BillingAddressForm
from .models import VirtualMachineType, VirtualMachinePlan from .models import VirtualMachineType, VirtualMachinePlan, HostingOrder
from .forms import HostingUserSignupForm, HostingUserLoginForm from .forms import HostingUserSignupForm, HostingUserLoginForm
from .mixins import ProcessVMSelectionMixin from .mixins import ProcessVMSelectionMixin
class DjangoHostingView(ProcessVMSelectionMixin, View): class DjangoHostingView(ProcessVMSelectionMixin, View):
template_name = "hosting/django.html" template_name = "hosting/django.html"
@ -157,21 +159,46 @@ class PaymentVMView(FormView):
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)
final_price = vm.calculate_price(specifications)
plan_data = { plan_data = {
'vm_type': vm, 'vm_type': vm,
'cores': specifications.get('cores'), 'cores': specifications.get('cores'),
'memory': specifications.get('memory'), 'memory': specifications.get('memory'),
'disk_size': specifications.get('disk_size'), 'disk_size': specifications.get('disk_size'),
'price': vm.calculate_price(specifications) 'price': final_price
} }
token = form.cleaned_data.get('token')
# Stripe payment goes here # Get or create stripe customer
customer = StripeCustomer.get_or_create(email=self.request.user.email,
token=token)
# Create Virtual Machine Plan
plan = VirtualMachinePlan.create(plan_data, request.user)
# Create Billing Address
billing_address = form.save()
# Create a Hosting Order
order = HostingOrder.create(VMPlan=plan, customer=customer,
billing_address=billing_address)
# Make stripe charge to a customer
stripe_utils = StripeUtils()
charge = stripe_utils.make_charge(amount=final_price,
customer=customer.stripe_id)
order.set_stripe_charge(charge)
if not charge.paid:
# raise an error
pass
# If the Stripe payment was successed, set order status approved
order.set_approved()
# order.charge =
# Billing Address should be store here # Billing Address should be store here
VirtualMachinePlan.create(plan_data, request.user)
return HttpResponseRedirect(reverse('hosting:payment')) return HttpResponseRedirect(reverse('hosting:payment'))
else: else:
return self.form_invalid(form) return self.form_invalid(form)

View file

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.4 on 2016-04-26 04:44
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('membership', '0003_auto_20160422_1002'),
]
operations = [
migrations.CreateModel(
name='StripeCustomer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('stripe_id', models.CharField(max_length=100, unique=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]

View file

@ -8,6 +8,7 @@ from django.core.mail import send_mail
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from utils.stripe_utils import StripeUtils
REGISTRATION_MESSAGE = {'subject': "Validation mail", REGISTRATION_MESSAGE = {'subject': "Validation mail",
'message': 'Please validate Your account under this link http://localhost:8000/en-us/login/validate/{}', 'message': 'Please validate Your account under this link http://localhost:8000/en-us/login/validate/{}',
@ -121,6 +122,32 @@ class CustomUser(AbstractBaseUser):
return self.is_admin return self.is_admin
class StripeCustomer(models.Model):
user = models.OneToOneField(CustomUser)
stripe_id = models.CharField(unique=True, max_length=100)
@classmethod
def get_or_create(cls, email=None, token=None):
"""
Check if there is a registered stripe customer with that email
or create a new one
"""
try:
stripe_customer = cls.objects.get(user__email=email)
return stripe_customer
except StripeCustomer.DoesNotExist:
user = CustomUser.objects.get(email=email)
stripe_utils = StripeUtils()
stripe_data = stripe_utils.create_customer(token, email)
stripe_customer = StripeCustomer.objects.\
create(user=user, stripe_id=stripe_data.get('id'))
return stripe_customer
class CreditCards(models.Model): class CreditCards(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE) user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE)

View file

@ -1,4 +1,5 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db import models
from django import forms from django import forms
# http://xml.coverpages.org/country3166.html # http://xml.coverpages.org/country3166.html
@ -245,10 +246,11 @@ COUNTRIES = (
) )
class CountryField(forms.ChoiceField): class CountryField(models.CharField):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
kwargs.setdefault('choices', COUNTRIES) kwargs.setdefault('choices', COUNTRIES)
kwargs.setdefault('initial', 'CH') kwargs.setdefault('default', 'CH')
kwargs.setdefault('max_length', 2)
super(CountryField, self).__init__(*args, **kwargs) super(CountryField, self).__init__(*args, **kwargs)

View file

@ -1,19 +1,17 @@
from django import forms from django import forms
from .models import ContactMessage from .models import ContactMessage, BillingAddress
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.core.mail import EmailMultiAlternatives from django.core.mail import EmailMultiAlternatives
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from utils.fields import CountryField # from utils.fields import CountryField
class BillingAddressForm(forms.Form): class BillingAddressForm(forms.ModelForm):
street_address = forms.CharField()
city = forms.CharField()
postal_code = forms.CharField()
country = CountryField()
token = forms.CharField(widget=forms.HiddenInput()) token = forms.CharField(widget=forms.HiddenInput())
class Meta: class Meta:
model = BillingAddress
fields = ['street_address', 'city', 'postal_code', 'country']
labels = { labels = {
'street_address': _('Street Address'), 'street_address': _('Street Address'),
'city': _('City'), 'city': _('City'),

File diff suppressed because one or more lines are too long

View file

@ -1,8 +1,18 @@
from django.db import models from django.db import models
from .fields import CountryField
# Create your models here. # Create your models here.
class BillingAddress(models.Model):
street_address = models.CharField(max_length=100)
city = models.CharField(max_length=50)
postal_code = models.CharField(max_length=50)
country = CountryField()
class ContactMessage(models.Model): class ContactMessage(models.Model):
name = models.CharField(max_length=200) name = models.CharField(max_length=200)
email = models.EmailField() email = models.EmailField()
@ -11,4 +21,4 @@ class ContactMessage(models.Model):
received_date = models.DateTimeField(auto_now_add=True) received_date = models.DateTimeField(auto_now_add=True)
def __str__(self): def __str__(self):
return "%s - %s - %s" % (self.name, self.email, self.received_date) return "%s - %s - %s" % (self.name, self.email, self.received_date)

View file

@ -12,6 +12,25 @@ 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
def create_customer(self, token, email):
stripe.api_key = settings.STRIPE_API_PRIVATE_KEY
customer = stripe.Customer.create(
source=token,
description='description for testing',
email=email
)
return customer
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,
customer=customer
)
return charge
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,
@ -20,7 +39,7 @@ class StripeUtils(object):
currency=self.CURRENCY, currency=self.CURRENCY,
id=id) id=id)
def make_payment(self, user, amount, token, time): def make_payment(self, user, amount, token):
try: try:
# Use Stripe's library to make requests... # Use Stripe's library to make requests...
charge = self.stripe.Charge.create( charge = self.stripe.Charge.create(