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
135
136
137
138
139
|
from __future__ import unicode_literals
from calendar import monthrange
from datetime import date
import re
from django import forms
from django.core import validators
from django.utils.translation import ugettext_lazy as _
from .core import get_credit_card_issuer
from .utils import get_month_choices, get_year_choices
from .widgets import CreditCardExpiryWidget, CreditCardNumberWidget
class CreditCardNumberField(forms.CharField):
widget = CreditCardNumberWidget(
attrs={'autocomplete': 'cc-number', 'required': 'required'})
default_error_messages = {
'invalid': _('Please enter a valid card number'),
'invalid_type': _('We accept only %(valid_types)s')}
def __init__(self, valid_types=None, *args, **kwargs):
self.valid_types = valid_types
kwargs['max_length'] = kwargs.pop('max_length', 32)
super(CreditCardNumberField, self).__init__(*args, **kwargs)
def to_python(self, value):
if value is not None:
value = re.sub(r'[\s-]+', '', value)
return super(CreditCardNumberField, self).to_python(value)
def validate(self, value):
card_type, issuer_name = get_credit_card_issuer(value)
if value in validators.EMPTY_VALUES and self.required:
raise forms.ValidationError(self.error_messages['required'])
if value and not self.cart_number_checksum_validation(self, value):
raise forms.ValidationError(self.error_messages['invalid'])
if (value and not self.valid_types is None
and not card_type in self.valid_types):
valid_types = map(issuer_name, self.valid_types)
error_message = self.error_messages['invalid_type'] % {
'valid_types': ', '.join(valid_types)
}
raise forms.ValidationError(error_message)
@staticmethod
def cart_number_checksum_validation(cls, number):
digits = []
even = False
if not number.isdigit():
return False
for digit in reversed(number):
digit = ord(digit) - ord('0')
if even:
digit *= 2
if digit >= 10:
digit = digit % 10 + digit // 10
digits.append(digit)
even = not even
return sum(digits) % 10 == 0 if digits else False
class CreditCardExpiryField(forms.MultiValueField):
default_error_messages = {
'invalid_month': 'Enter a valid month.',
'invalid_year': '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=get_month_choices(),
error_messages={'invalid': errors['invalid_month']},
widget=forms.Select(
attrs={'autocomplete': 'cc-exp-month',
'required': 'required'})),
forms.ChoiceField(
choices=get_year_choices(),
error_messages={'invalid': errors['invalid_year']},
widget=forms.Select(
attrs={'autocomplete': 'cc-exp-year',
'required': 'required'})),
)
super(CreditCardExpiryField, self).__init__(fields, *args, **kwargs)
self.widget = CreditCardExpiryWidget(widgets=[fields[0].widget,
fields[1].widget])
def clean(self, value):
exp = super(CreditCardExpiryField, self).clean(value)
if exp and 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 CreditCardVerificationField(forms.CharField):
widget = forms.TextInput(
attrs={'autocomplete': 'cc-csc'})
default_error_messages = {
'invalid': _('Enter a valid security number.')}
def __init__(self, *args, **kwargs):
kwargs['max_length'] = kwargs.pop('max_length', 4)
super(CreditCardVerificationField, self).__init__(*args, **kwargs)
def validate(self, value):
if value in validators.EMPTY_VALUES and self.required:
raise forms.ValidationError(self.error_messages['required'])
if value and not re.match('^[0-9]{3,4}$', value):
raise forms.ValidationError(self.error_messages['invalid'])
class CreditCardNameField(forms.CharField):
widget = forms.TextInput(
attrs={'autocomplete': 'cc-name', 'required': 'required'})
|