Add support for stripe to process credit cards
This commit is contained in:
@@ -1,4 +1,19 @@
|
||||
from django import forms
|
||||
|
||||
class DonationForm(forms.Form):
|
||||
quantity = forms.DecimalField(min_value=0.01, required=True, label="Donation Quantity", help_text="In USD")
|
||||
METHOD_DWOLLA = 0
|
||||
METHOD_STRIPE = 1
|
||||
METHODS = (
|
||||
(METHOD_DWOLLA, 'Dwolla'),
|
||||
(METHOD_STRIPE, 'Credit Card via Stripe')
|
||||
)
|
||||
method = forms.ChoiceField(choices=METHODS)
|
||||
quantity = forms.DecimalField(min_value=0.5, required=True, label="Donation Quantity", help_text="In USD")
|
||||
|
||||
class StripeForm(forms.Form):
|
||||
MONTHS = map(lambda x:(x, x), range(1, 13))
|
||||
YEARS = map(lambda x:(x, x), range(2012, 2026))
|
||||
card = forms.CharField(label="Card Number")
|
||||
month = forms.ChoiceField(choices=MONTHS, label='Expiration Month')
|
||||
year = forms.ChoiceField(choices=YEARS, label='Expiration Year')
|
||||
cvc = forms.IntegerField(label='CVC', help_text='Three digits found on back of card')
|
||||
|
@@ -16,7 +16,7 @@ class Donation(models.Model):
|
||||
created = models.DateTimeField(editable=False, auto_now_add=True)
|
||||
updated = models.DateTimeField(editable=False, auto_now=True)
|
||||
quantity = models.FloatField()
|
||||
transactionId = models.IntegerField(blank=True, null=True)
|
||||
transactionId = models.TextField(blank=True, null=True)
|
||||
status = models.IntegerField(default=STATUS_PENDING, choices = STATUS)
|
||||
user = models.ForeignKey(User)
|
||||
|
||||
|
134
donate/tests.py
134
donate/tests.py
@@ -1,16 +1,126 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
from django.utils import unittest
|
||||
from django.test.client import Client
|
||||
from django.contrib.auth.models import User
|
||||
import json
|
||||
import forms
|
||||
import models
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
class DwollaTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = User.objects.create_user('ValidUsername',
|
||||
'test@example.com', 'password')
|
||||
self.client.login(username='ValidUsername', password='password')
|
||||
|
||||
from django.test import TestCase
|
||||
def tearDown(self):
|
||||
self.user.delete()
|
||||
for d in models.Donation.objects.all():
|
||||
d.delete()
|
||||
|
||||
def testStartDwolla(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_DWOLLA,
|
||||
'base-quantity': 100
|
||||
})
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
donations = models.Donation.objects.all()
|
||||
self.assertEqual(len(donations), 1)
|
||||
self.assertEqual(donations[0].status, models.Donation.STATUS_PENDING)
|
||||
self.assertEqual(donations[0].quantity, 100)
|
||||
#FIXME: Test transactionId
|
||||
|
||||
def testFinishDwolla(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_DWOLLA,
|
||||
'base-quantity': 200
|
||||
})
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
donation = models.Donation.objects.all()[0]
|
||||
resp = self.client.post('/donate/dwolla', json.dumps({
|
||||
'OrderId': donation.id,
|
||||
'TransactionId': donation.transactionId
|
||||
}), 'text/json')
|
||||
donation = models.Donation.objects.all()[0]
|
||||
self.assertEqual(donation.status, models.Donation.STATUS_PAID)
|
||||
self.assertEqual(donation.quantity, 200)
|
||||
self.assertEqual(resp.status_code, 204)
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
class StripeTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.user = User.objects.create_user('ValidUsername',
|
||||
'test@example.com', 'password')
|
||||
self.client.login(username='ValidUsername', password='password')
|
||||
|
||||
def tearDown(self):
|
||||
self.user.delete()
|
||||
for d in models.Donation.objects.all():
|
||||
d.delete()
|
||||
|
||||
def testValidCard(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_STRIPE,
|
||||
'base-quantity': 300,
|
||||
'stripe-card': '4242424242424242',
|
||||
'stripe-month': '12',
|
||||
'stripe-year': '2020',
|
||||
'stripe-cvc': '123',
|
||||
})
|
||||
print resp.content
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
donations = models.Donation.objects.all()
|
||||
self.assertEqual(len(donations), 1)
|
||||
self.assertEqual(donations[0].status, models.Donation.STATUS_PAID)
|
||||
self.assertEqual(donations[0].quantity, 300)
|
||||
|
||||
def testDeclinedCard(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_STRIPE,
|
||||
'base-quantity': 300,
|
||||
'stripe-card': '4000000000000002',
|
||||
'stripe-month': '12',
|
||||
'stripe-year': '2038',
|
||||
'stripe-cvc': '000',
|
||||
})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
donations = models.Donation.objects.all()
|
||||
self.assertEqual(len(donations), 0)
|
||||
|
||||
def testBad(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_STRIPE,
|
||||
'base-quantity': 400,
|
||||
'stripe-card': '4242424242424242',
|
||||
'stripe-month': '12',
|
||||
'stripe-year': '2038',
|
||||
'stripe-cvc': '99',
|
||||
})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
donations = models.Donation.objects.all()
|
||||
self.assertEqual(len(donations), 0)
|
||||
|
||||
def testBadCVC(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_STRIPE,
|
||||
'base-quantity': 400,
|
||||
'stripe-card': '4242424242424242',
|
||||
'stripe-month': '12',
|
||||
'stripe-year': '2038',
|
||||
'stripe-cvc': '99',
|
||||
})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
donations = models.Donation.objects.all()
|
||||
self.assertEqual(len(donations), 0)
|
||||
|
||||
def testBadNumber(self):
|
||||
resp = self.client.post('/donate/', {
|
||||
'base-method': forms.DonationForm.METHOD_STRIPE,
|
||||
'base-quantity': 400,
|
||||
'stripe-card': '4242424242424241',
|
||||
'stripe-month': '12',
|
||||
'stripe-year': '2038',
|
||||
'stripe-cvc': '000',
|
||||
})
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
donations = models.Donation.objects.all()
|
||||
self.assertEqual(len(donations), 0)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from django.shortcuts import render_to_response
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib import messages
|
||||
from django.template import RequestContext
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
@@ -11,37 +12,68 @@ import json
|
||||
import forms
|
||||
import models
|
||||
import signals
|
||||
import stripe
|
||||
|
||||
def index(request):
|
||||
if request.method == 'POST':
|
||||
form = forms.DonationForm(request.POST)
|
||||
form = forms.DonationForm(request.POST, prefix='base')
|
||||
stripeForm = forms.StripeForm(request.POST, prefix='stripe')
|
||||
else:
|
||||
form = forms.DonationForm()
|
||||
form = forms.DonationForm(prefix='base')
|
||||
stripeForm = forms.StripeForm(prefix='stripe')
|
||||
if form.is_valid():
|
||||
data = {}
|
||||
order = {}
|
||||
item = {}
|
||||
item['Description'] = "Caminus Donation"
|
||||
item['Name'] = "Caminus Donation"
|
||||
item['Price'] = str(form.cleaned_data['quantity'])
|
||||
item['Quantity'] = 1
|
||||
order['OrderItems'] = [item,]
|
||||
order['Test'] = True
|
||||
order['Tax'] = 0.00
|
||||
order['Total'] = str(form.cleaned_data['quantity'])
|
||||
order['DestinationId'] = settings.DWOLLA_API_ID
|
||||
data['PurchaseOrder'] = order
|
||||
data['Key'] = settings.DWOLLA_API_KEY
|
||||
data['Secret'] = settings.DWOLLA_API_SECRET
|
||||
data['Callback'] = 'http://camin.us%s'%reverse('donate.views.dwollaCallback')
|
||||
donation = models.Donation.objects.create(quantity=form.cleaned_data['quantity'], user=request.user)
|
||||
order['OrderId'] = donation.id
|
||||
data['Redirect'] = 'http://camin.us%s'%reverse('donate.views.thanks', kwargs={'donation':donation.id})
|
||||
req = urllib2.Request("https://www.dwolla.com/payment/request", data=json.dumps(data), headers={'Content-Type': 'application/json'})
|
||||
response = json.load(urllib2.urlopen(req))
|
||||
return HttpResponseRedirect("https://www.dwolla.com/payment/checkout/%s"%(response['CheckoutId']))
|
||||
else:
|
||||
return render_to_response('donate/index.html', {'form': form}, context_instance = RequestContext(request))
|
||||
if int(form.cleaned_data['method']) == forms.DonationForm.METHOD_DWOLLA:
|
||||
data = {}
|
||||
order = {}
|
||||
item = {}
|
||||
item['Description'] = "Caminus Donation"
|
||||
item['Name'] = "Caminus Donation"
|
||||
item['Price'] = str(form.cleaned_data['quantity'])
|
||||
item['Quantity'] = 1
|
||||
order['OrderItems'] = [item,]
|
||||
order['Test'] = True
|
||||
order['Tax'] = 0.00
|
||||
order['Total'] = str(form.cleaned_data['quantity'])
|
||||
order['DestinationId'] = settings.DWOLLA_API_ID
|
||||
data['PurchaseOrder'] = order
|
||||
data['Key'] = settings.DWOLLA_API_KEY
|
||||
data['Secret'] = settings.DWOLLA_API_SECRET
|
||||
data['Callback'] = 'http://camin.us%s'%reverse('donate.views.dwollaCallback')
|
||||
donation = models.Donation.objects.create(quantity=form.cleaned_data['quantity'], user=request.user)
|
||||
order['OrderId'] = donation.id
|
||||
data['Redirect'] = 'http://camin.us%s'%reverse('donate.views.thanks', kwargs={'donation':donation.id})
|
||||
req = urllib2.Request("https://www.dwolla.com/payment/request", data=json.dumps(data), headers={'Content-Type': 'application/json'})
|
||||
response = json.load(urllib2.urlopen(req))
|
||||
return HttpResponseRedirect("https://www.dwolla.com/payment/checkout/%s"%(response['CheckoutId']))
|
||||
elif stripeForm.is_valid():
|
||||
stripe.api_key = settings.STRIPE_KEY
|
||||
cardData = {}
|
||||
cardData['number'] = stripeForm.cleaned_data['card']
|
||||
cardData['exp_month'] = stripeForm.cleaned_data['month']
|
||||
cardData['exp_year'] = stripeForm.cleaned_data['year']
|
||||
cardData['cvc'] = stripeForm.cleaned_data['cvc']
|
||||
try:
|
||||
charge = stripe.Charge.create(
|
||||
amount = str(form.cleaned_data['quantity']*100),
|
||||
currency = 'usd',
|
||||
card = cardData,
|
||||
description = 'Caminus Donation from %s'%(request.user.email)
|
||||
)
|
||||
except stripe.CardError, e:
|
||||
messages.error(request, "There was an error while processing your card: %s"%(e.message))
|
||||
return render_to_response('donate/index.html', {'form': form,
|
||||
'stripeForm': stripeForm}, context_instance = RequestContext(request))
|
||||
donation = models.Donation.objects.create(quantity=form.cleaned_data['quantity'], user=request.user)
|
||||
donation.status = models.Donation.STATUS_PAID
|
||||
donation.transactionId = charge.id
|
||||
donation.save()
|
||||
acct = donation.user.minecraftprofile.currencyaccount
|
||||
acct.balance = F('balance')+(donation.quantity*2000)
|
||||
acct.save()
|
||||
notification.send_now([donation.user], "donation_paid", {"donation":donation, "credit":donation.quantity*2000})
|
||||
return HttpResponseRedirect('http://camin.us%s'%reverse('donate.views.thanks', kwargs={'donation':donation.id}))
|
||||
return render_to_response('donate/index.html', {'form': form,
|
||||
'stripeForm': stripeForm}, context_instance = RequestContext(request))
|
||||
|
||||
@csrf_exempt
|
||||
def dwollaCallback(request):
|
||||
|
@@ -9,3 +9,4 @@ django-piston==0.2.2
|
||||
coverage
|
||||
pydot==1.0.28
|
||||
beanstalkc
|
||||
stripe
|
||||
|
@@ -1,13 +1,33 @@
|
||||
{% extends 'base_simple.html'%}
|
||||
{%load static %}
|
||||
{% get_static_prefix as STATIC_PREFIX %}
|
||||
{% block extrahead %}
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
if ($('#id_base-method').val() != 1) {
|
||||
$('#stripe-data').hide();
|
||||
}
|
||||
$('#id_base-method').change(function() {
|
||||
if ($(this).val() == 1) {
|
||||
$('#stripe-data').show('blind');
|
||||
} else {
|
||||
$('#stripe-data').hide('blind');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>Donating to caminus helps keep us alive!</p>
|
||||
<p>All donations recieve 2000gr per $1 USD. You must be logged in before donating to recieve this.</p>
|
||||
<p>Donations are handled through <a href="http://dwolla.com/">Dwolla</a></p>
|
||||
<p>Donations are handled through <a href="http://dwolla.com/">Dwolla</a> and <a href="http://stripe.com">Stripe</a></p>
|
||||
<form method="POST">
|
||||
{{form.as_p}}
|
||||
{{form.as_p}}
|
||||
|
||||
<div id="stripe-data">
|
||||
{{stripeForm.as_p}}
|
||||
</div>
|
||||
{%csrf_token%}
|
||||
<input type="submit"/>
|
||||
</form>
|
||||
|
Reference in New Issue
Block a user