# Credit card validator in Python

Posted on

Problem

I have began taking the CS50 course on EDX the past couple of days, and one of the tasks to accomplish was to write a credit card validator in C. I wrote it in C, and then I thought that I could go about the same thing in Python, and wrote the same program in Python. For those of you unfamiliar with the problem, here is the description.

Because I took the program and converted from C to Python, it is probably not going to be written in the most Pythonic way. I tried to add some Pythonic ways of doing things, but there are probably many more things I could have done. If you find anything that could be done in a better, faster, or more Pythonic way, please let me know. Thanks.

``````def main():
# cc_number = int(input("Enter a valid credit card number: "))

cc_number = 12345678912356789
if not checksum(cc_number):
print("INVALID")
else:
print(check_provider(cc_number))

def check_provider(number):
if len(str(number)) < 13 or len(str(number)) > 16:
return 'INVALID'

if ((list(str(number)))[:2]) == ['3', '4'] or ((list(str(number)))[:2]) == ['3', '7']:
return 'AMEX'
elif ((list(str(number)))[:2]) == ['5', '1'] or ((list(str(number)))[:2]) == ['5', '2']
or ((list(str(number)))[:2]) == ['5', '3'] or ((list(str(number)))[:2]) == ['5', '4']
or ((list(str(number)))[:2]) == ['5', '5']:
return 'MASTERCARD'
elif ((list(str(number)))) == '4':
return 'VISA'
else:
return 'INVALID'

def checksum(number):
list_number = list(str(number))
odd_indices = []
even_indices = []

last_index = len(list_number) - 1

rev_list = list(reversed(list_number))

for i in rev_list[1::2]:
odd_indices.append(i)

for i in rev_list[::2]:
even_indices.append(i)

sum_odd = sum(split_to_digits(''.join(int_to_str(mul(odd_indices, 2)))))
sum_even = sum(split_to_digits(''.join(int_to_str(even_indices))))
s = sum_odd + sum_even

print(s)

if s % 10 == 0:
return True
return False

def mul(list, x):
return [(int(n) * 2) for n in list]

def split_to_digits(n):
return [int(i) for i in str(n)]

def int_to_str(n):
return [str(x) for x in n]

def str_to_int(n):
return [int(x) for x in n]

main()
``````

Solution

General notes:

• use `if __name__ == '__main__':` to avoid the `main()` function to be executed when the module is imported
• I’d pass around the credit card number as a string instead of converting it to string in every single validation step
• add docstrings to each of the defined functions

Regarding `check_provider()` function:

• you can check the length to be in range in one go:

``````if not(13 <= len(str(number)) <= 16):
``````
• I would improve the way you distinguish between cards by introducing a mapping between brands and regular expressions (like it was done in `pycard` module). Then, match each of the expressions one by one until you find a match or a brand was not found:

``````import re

BRANDS = {
'Visa': re.compile(r'^4d{12}(d{3})?\$'),
'Mastercard': re.compile(r'''
^(5[1-5]d{4}|677189)d{10}\$|  # Traditional 5-series + RU support
^(222[1-9]|2[3-6]d{2}|27[0-1]d|2720)d{12}\$  # 2016 2-series
''', re.VERBOSE),
'American Express': re.compile(r'^3d{13}\$'),
'Discover': re.compile(r'^(6011|65d{2})d{12}\$'),
}

def check_provider(number):
"""Checks a credit card number and returns a matching brand name, or INVALID if no brand matched."""
for brand, regexp in BRANDS.items():
if regexp.match(number):
return brand
return 'INVALID'
``````

Regarding implementing the Luhn algorithm, check the `pycard`‘s implementation – it is quite clean and understandable.