Add support for stripe to process credit cards

This commit is contained in:
Trever Fischer
2012-09-03 18:09:08 -04:00
parent 4a35740bef
commit 5203649d4f
6 changed files with 220 additions and 42 deletions

View File

@@ -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')

View File

@@ -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)

View File

@@ -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)

View File

@@ -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):

View File

@@ -9,3 +9,4 @@ django-piston==0.2.2
coverage
pydot==1.0.28
beanstalkc
stripe

View File

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