Django Form Credit Card Field with Pattern, Length and Luhn Validation

I’ve been doing a bit of ecommerce work lately and one of my needs was a credit card field for a Django form. Ideally it needed to support all major cards (PayPal offers a good reference list) and be easily extendable in the future.

All I could find via Google was this older Django project and various blog posts, none of which really fit my needs.

After a bit more digging I decided to port over portions of Stripe’s jQuery.payment which does almost exactly what I needed.

from django import forms
from django.forms.widgets import TextInput
from django.utils.translation import ugettext_lazy as _
class TelephoneInput(TextInput):
# switch input type to type tel so that the numeric keyboard shows on mobile devices
input_type = 'tel'
class CreditCardField(forms.CharField):
# validates almost all of the example cards from PayPal
# https://www.paypalobjects.com/en_US/vhelp/paypalmanager_help/credit_card_numbers.htm
cards = [
{
'type': 'maestro',
'patterns': [5018, 502, 503, 506, 56, 58, 639, 6220, 67],
'length': [12, 13, 14, 15, 16, 17, 18, 19],
'cvvLength': [3],
'luhn': True
}, {
'type': 'forbrugsforeningen',
'patterns': [600],
'length': [16],
'cvvLength': [3],
'luhn': True
}, {
'type': 'dankort',
'patterns': [5019],
'length': [16],
'cvvLength': [3],
'luhn': True
}, {
'type': 'visa',
'patterns': [4],
'length': [13, 16],
'cvvLength': [3],
'luhn': True
}, {
'type': 'mastercard',
'patterns': [51, 52, 53, 54, 55, 22, 23, 24, 25, 26, 27],
'length': [16],
'cvvLength': [3],
'luhn': True
}, {
'type': 'amex',
'patterns': [34, 37],
'length': [15],
'cvvLength': [3, 4],
'luhn': True
}, {
'type': 'dinersclub',
'patterns': [30, 36, 38, 39],
'length': [14],
'cvvLength': [3],
'luhn': True
}, {
'type': 'discover',
'patterns': [60, 64, 65, 622],
'length': [16],
'cvvLength': [3],
'luhn': True
}, {
'type': 'unionpay',
'patterns': [62, 88],
'length': [16, 17, 18, 19],
'cvvLength': [3],
'luhn': False
}, {
'type': 'jcb',
'patterns': [35],
'length': [16],
'cvvLength': [3],
'luhn': True
}
]
def __init__(self, placeholder=None, *args, **kwargs):
super(CreditCardField, self).__init__(
# override default widget
widget=TelephoneInput(attrs={
'placeholder': placeholder
})
, *args, **kwargs)
default_error_messages = {
'invalid': _(u'The credit card number is invalid'),
}
def clean(self, value):
# ensure no spaces or dashes
value = value.replace(' ', '').replace('-', '')
# get the card type and its specs
card = self.card_from_number(value)
# if no card found, invalid
if not card:
raise forms.ValidationError(self.error_messages['invalid'])
# check the length
if not len(value) in card['length']:
raise forms.ValidationError(self.error_messages['invalid'])
# test luhn if necessary
if card['luhn']:
if not self.validate_mod10(value):
raise forms.ValidationError(self.error_messages['invalid'])
return value
def card_from_number(self, num):
# find this card, based on the card number, in the defined set of cards
for card in self.cards:
for pattern in card['patterns']:
if (str(pattern) == str(num)[:len(str(pattern))]):
return card
def validate_mod10(self, num):
# validate card number using the Luhn (mod 10) algorithm
checksum, factor = 0, 1
for c in reversed(num):
for c in str(factor * int(c)):
checksum += int(c)
factor = 3 factor
return checksum % 10 == 0

view raw
fields.py
hosted with ❤ by GitHub

You can then use this field in your form:

class CreditCardForm(ModelForm):
card_number = CreditCardField(placeholder=u'0000 0000 0000 0000', min_length=12, max_length=19)

view raw
form.py
hosted with ❤ by GitHub

One thought on “Django Form Credit Card Field with Pattern, Length and Luhn Validation”

Leave a Reply to Luis Glez Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s