├── LICENSE ├── README.txt ├── __init__.py └── fields.py /LICENSE: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | 15 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | django-creditcard-fields provides custom form fields for collecting and 2 | validating credit card information. 3 | 4 | Example usage: 5 | 6 | ---------------------------------------------------------------------------- 7 | 8 | from django import forms 9 | from fields import CreditCardField, ExpiryDateField, VerificationValueField 10 | 11 | class PaymentForm(forms.Form): 12 | 13 | name_on_card = forms.CharField(max_length=50, required=True) 14 | card_number = CreditCardField(required=True) 15 | expiry_date = ExpiryDateField(required=True) 16 | card_code = VerificationValueField(required=True) 17 | 18 | ---------------------------------------------------------------------------- 19 | 20 | THIS SOFTWARE IS PROVIDED "AS IS". NO WARRANTY OF ANY KIND IS EXPRESSED OR 21 | IMPLIED. YOU USE IT AT YOUR OWN RISK. IN NO EVENT SHALL THE AUTHORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bryanchow/django-creditcard-fields/bfc6d1a341d09d05133a32ed6f44ea4513e6435d/__init__.py -------------------------------------------------------------------------------- /fields.py: -------------------------------------------------------------------------------- 1 | import re 2 | from datetime import date 3 | from calendar import monthrange, IllegalMonthError 4 | from django import forms 5 | from django.conf import settings 6 | from django.utils.translation import ugettext_lazy as _ 7 | 8 | 9 | CREDIT_CARD_RE = r'^(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|6(?:011|5[0-9][0-9])[0-9]{12}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|(?:2131|1800|35\\d{3})\d{11})$' 10 | MONTH_FORMAT = getattr(settings, 'MONTH_FORMAT', '%b') 11 | VERIFICATION_VALUE_RE = r'^([0-9]{3,4})$' 12 | 13 | 14 | class CreditCardField(forms.CharField): 15 | """ 16 | Form field that validates credit card numbers. 17 | """ 18 | 19 | default_error_messages = { 20 | 'required': _(u'Please enter a credit card number.'), 21 | 'invalid': _(u'The credit card number you entered is invalid.'), 22 | } 23 | 24 | def clean(self, value): 25 | value = value.replace(' ', '').replace('-', '') 26 | if self.required and not value: 27 | raise forms.util.ValidationError(self.error_messages['required']) 28 | if value and not re.match(CREDIT_CARD_RE, value): 29 | raise forms.util.ValidationError(self.error_messages['invalid']) 30 | return value 31 | 32 | 33 | class ExpiryDateWidget(forms.MultiWidget): 34 | """ 35 | Widget containing two select boxes for selecting the month and year. 36 | """ 37 | 38 | def decompress(self, value): 39 | return [value.month, value.year] if value else [None, None] 40 | 41 | def format_output(self, rendered_widgets): 42 | return u'
%s
' % ' '.join(rendered_widgets) 43 | 44 | 45 | class ExpiryDateField(forms.MultiValueField): 46 | """ 47 | Form field that validates credit card expiry dates. 48 | """ 49 | 50 | default_error_messages = { 51 | 'invalid_month': _(u'Please enter a valid month.'), 52 | 'invalid_year': _(u'Please enter a valid year.'), 53 | 'date_passed': _(u'This expiry date has passed.'), 54 | } 55 | 56 | def __init__(self, *args, **kwargs): 57 | today = date.today() 58 | error_messages = self.default_error_messages.copy() 59 | if 'error_messages' in kwargs: 60 | error_messages.update(kwargs['error_messages']) 61 | if 'initial' not in kwargs: 62 | # Set default expiry date based on current month and year 63 | kwargs['initial'] = today 64 | months = [(x, '%02d (%s)' % (x, date(2000, x, 1).strftime(MONTH_FORMAT))) for x in xrange(1, 13)] 65 | years = [(x, x) for x in xrange(today.year, today.year + 15)] 66 | fields = ( 67 | forms.ChoiceField(choices=months, error_messages={'invalid': error_messages['invalid_month']}), 68 | forms.ChoiceField(choices=years, error_messages={'invalid': error_messages['invalid_year']}), 69 | ) 70 | super(ExpiryDateField, self).__init__(fields, *args, **kwargs) 71 | self.widget = ExpiryDateWidget(widgets=[fields[0].widget, fields[1].widget]) 72 | 73 | def clean(self, value): 74 | expiry_date = super(ExpiryDateField, self).clean(value) 75 | if date.today() > expiry_date: 76 | raise forms.ValidationError(self.error_messages['date_passed']) 77 | return expiry_date 78 | 79 | def compress(self, data_list): 80 | if data_list: 81 | try: 82 | month = int(data_list[0]) 83 | except (ValueError, TypeError): 84 | raise forms.ValidationError(self.error_messages['invalid_month']) 85 | try: 86 | year = int(data_list[1]) 87 | except (ValueError, TypeError): 88 | raise forms.ValidationError(self.error_messages['invalid_year']) 89 | try: 90 | day = monthrange(year, month)[1] # last day of the month 91 | except IllegalMonthError: 92 | raise forms.ValidationError(self.error_messages['invalid_month']) 93 | except ValueError: 94 | raise forms.ValidationError(self.error_messages['invalid_year']) 95 | return date(year, month, day) 96 | return None 97 | 98 | 99 | class VerificationValueField(forms.CharField): 100 | """ 101 | Form field that validates credit card verification values (e.g. CVV2). 102 | See http://en.wikipedia.org/wiki/Card_Security_Code 103 | """ 104 | 105 | widget = forms.TextInput(attrs={'maxlength': 4}) 106 | default_error_messages = { 107 | 'required': _(u'Please enter the three- or four-digit verification code for your credit card.'), 108 | 'invalid': _(u'The verification value you entered is invalid.'), 109 | } 110 | 111 | def clean(self, value): 112 | value = value.replace(' ', '') 113 | if not value and self.required: 114 | raise forms.util.ValidationError(self.error_messages['required']) 115 | if value and not re.match(VERIFICATION_VALUE_RE, value): 116 | raise forms.util.ValidationError(self.error_messages['invalid']) 117 | return value 118 | --------------------------------------------------------------------------------