Alternative version of newform code for handling credit cards. Unlike the other two credit-card snippets (http://www.djangosnippets.org/snippets/764/ and http://www.djangosnippets.org/snippets/830/), this uses two drop-down boxes instead of text fields for the expiration date, which is a bit friendlier. It doesn't do as much checking as snippet #764 since we rely on the payment gateway for doing that. We only accept Mastercard, Visa, and American Express, so the validation code checks for that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | from datetime import date, datetime
from calendar import monthrange
class CreditCardField(forms.IntegerField):
@staticmethod
def get_cc_type(number):
"""
Gets credit card type given number. Based on values from Wikipedia page
"Credit card number".
http://en.wikipedia.org/w/index.php?title=Credit_card_number
"""
number = str(number)
#group checking by ascending length of number
if len(number) == 13:
if number[0] == "4":
return "Visa"
elif len(number) == 14:
if number[:2] == "36":
return "MasterCard"
elif len(number) == 15:
if number[:2] in ("34", "37"):
return "American Express"
elif len(number) == 16:
if number[:4] == "6011":
return "Discover"
if number[:2] in ("51", "52", "53", "54", "55"):
return "MasterCard"
if number[0] == "4":
return "Visa"
return "Unknown"
def clean(self, value):
"""Check if given CC number is valid and one of the
card types we accept"""
if value and (len(value) < 13 or len(value) > 16):
raise forms.ValidationError("Please enter in a valid "+\
"credit card number.")
elif self.get_cc_type(value) not in ("Visa", "MasterCard",
"American Express"):
raise forms.ValidationError("Please enter in a Visa, "+\
"Master Card, or American Express credit card number.")
return super(CreditCardField, self).clean(value)
class CCExpWidget(forms.MultiWidget):
""" Widget containing two select boxes for selecting the month and year"""
def decompress(self, value):
return [value.month, value.year] if value else [None, None]
def format_output(self, rendered_widgets):
html = u' / '.join(rendered_widgets)
return u'<span style="white-space: nowrap">%s</span>' % html
class CCExpField(forms.MultiValueField):
EXP_MONTH = [(x, x) for x in xrange(1, 13)]
EXP_YEAR = [(x, x) for x in xrange(date.today().year,
date.today().year + 15)]
default_error_messages = {
'invalid_month': u'Enter a valid month.',
'invalid_year': u'Enter a valid year.',
}
def __init__(self, *args, **kwargs):
errors = self.default_error_messages.copy()
if 'error_messages' in kwargs:
errors.update(kwargs['error_messages'])
fields = (
forms.ChoiceField(choices=self.EXP_MONTH,
error_messages={'invalid': errors['invalid_month']}),
forms.ChoiceField(choices=self.EXP_YEAR,
error_messages={'invalid': errors['invalid_year']}),
)
super(CCExpField, self).__init__(fields, *args, **kwargs)
self.widget = CCExpWidget(widgets =
[fields[0].widget, fields[1].widget])
def clean(self, value):
exp = super(CCExpField, self).clean(value)
if date.today() > exp:
raise forms.ValidationError(
"The expiration date you entered is in the past.")
return exp
def compress(self, data_list):
if data_list:
if data_list[1] in forms.fields.EMPTY_VALUES:
error = self.error_messages['invalid_year']
raise forms.ValidationError(error)
if data_list[0] in forms.fields.EMPTY_VALUES:
error = self.error_messages['invalid_month']
raise forms.ValidationError(error)
year = int(data_list[1])
month = int(data_list[0])
# find last day of the month
day = monthrange(year, month)[1]
return date(year, month, day)
return None
class PaymentForm(forms.Form):
number = CreditCardField(required = True, label = "Card Number")
holder = forms.CharField(required = True, label = "Card Holder Name",
max_length = 60)
expiration = CCExpField(required = True, label = "Expiration")
ccv_number = forms.IntegerField(required = True, label = "CCV Number",
max_value = 9999, widget = forms.TextInput(attrs={'size': '4'}))
def __init__(self, *args, **kwargs):
self.payment_data = kwargs.pop('payment_data', None)
super(PaymentForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned = super(PaymentForm, self).clean()
if not self.errors:
result = self.process_payment()
if result and result[0] == 'Card declined':
raise forms.ValidationError('Your credit card was declined.')
elif result and result[0] == 'Processing error':
raise forms.ValidationError(
'We encountered the following error while processing '+\
'your credit card: '+result[1])
return cleaned
def process_payment(self):
if self.payment_data:
# don't process payment if payment_data wasn't set
datadict = self.cleaned_data
datadict.update(self.payment_data)
from virtualmerchant import VirtualMerchant
vmerchant = VirtualMerchant(datadict)
return vmerchant.process_virtualmerchant_payment()
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 10 months, 3 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months ago
- Serializer factory with Django Rest Framework by julio 1 year, 5 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 6 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.