Pass in the credit card number and the card type and get back a result with an explanation.
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 | cc_patterns = { # '<cc_type>' : { dict of valid lengths : [list of associated prefixes] }
'amex' : { (15) : ('34', '37') },
'carteblanche' : { (14) : ('300', '301', '302', '303', '304', '305', '36', '38') },
'dinersclub' : { (14) : ('300', '301', '302', '303', '304', '305', '36', '38') },
'discover' : { (16) : ('6011') },
'enroute' : { (15) : ('2014', '2149') },
'jcb' : { (15) : ('2131', '1800'), (16) : ('3') },
'mastercard' : { (16) : ('51', '52', '53', '54', '55') },
'visa' : { (13, 16) : ('4') }
}
def cc_check( cc_num, cc_type ):
'''
Returns a touple of (isValidated, strReason)
* All doctest cc#s are random and do not reflect real world numbers *
>>> result = cc_check('5235235879456682', 'MASTERCARD')
>>> result[0]
True
>>> result = cc_check('5235235879456683', 'MASTERCARD')
>>> result[0]
False
>>> result[1].find('Invalid check sum')
0
>>> result = cc_check('523523583', 'MASTERCARD')
>>> result[0]
False
>>> result[1].find('Invalid length')
0
>>> result = cc_check('3235235879456683', 'MASTERCARD')
>>> result[0]
False
>>> result[1].find('Invalid prefix')
0
'''
cc_type = cc_type.strip().lower().replace(' ', '')
cc_num = str(cc_num)
if cc_type in cc_patterns.keys():
# Perform length check
valid_lengths = cc_patterns[cc_type].keys()
if len(cc_num) not in valid_lengths:
return (False, 'Invalid length (%s) for %s' % \
(str(valid_lengths).strip('[]'), cc_type))
# Perform prefix check
valid_prefixes = cc_patterns[cc_type][len(cc_num)]
prefix_match = False
for prefix in valid_prefixes:
prefix_length = len(prefix)
if cc_num[:prefix_length] == prefix:
prefix_match = True
if not prefix_match:
return (False, 'Invalid prefix (not in %s)' % \
','.join(valid_prefixes))
# Perform mod 10 check
if cc_type == 'enroute':
pass # Only cc_type that doesn't do a mod 10 check
else:
check_digits = []
is_even = False
for i, digit in enumerate(cc_num):
is_even = (i % 2 == 1)
if is_even:
check_digits.append(int(digit))
else:
mult_digits = str(int(digit) * 2)
check_digits.append(int(mult_digits[0]))
if len(mult_digits) == 2:
check_digits.append(int(mult_digits[1]))
check_sum = sum(check_digits)
if check_sum % 10 != 0:
return (False, 'Invalid check sum (%d)' % check_sum)
return (True, '')
else:
return (False, 'Invalid cc type (%s)' % cc_type)
if __name__ == "__main__":
import doctest
doctest.testmod()
|
Tags: validation
This code is not correct! See is_even is not modified ever.
I have corrected this code: https://gist.github.com/zahradil/6208954
Updated. Thanks Jiri for the correction!