Homework 12: Checking Credit Cards

Assigned
Friday October 28, 2016
Due
5 p.m., Friday November 4, 2016
Goals
You will practice top-down design, design by contract, and unit testing, as well as manipulating strings and lists.
Collaboration
Do this assignment with a new partner of your choice.
Submitting
Submit a Python program using the online turnin form. See below.
Scoring
30 points

Background: Credit Card Numbers

Processing credit cards is an important aspect of e-commerce. Some card types such as Visa and Mastercard are widely accepted, while others such as Diner's Club are not. When a shopper types their credit card number in a web browser, we would like to verify that the card is accepted and the number is valid before contacting the credit card company.

The typical credit card has a 15- or 16-digit number embossed on the front. There is a lot of information packed into this number, but we will focus on just the type of card and the checksum.

The type of a credit card can be identified by the first few digits. The following table lists prefixes used for several popular credit card types.

Card Type Prefix Example numbers (fake!)
American Express
34, 37
378 2822 4631 0005
371 4496 3539 8431
Discover 6011, 644, 65
6011 1111 1111 1117
6011 0009 9013 9424
MasterCard 51—55 5105 1051 0510 5100
5394 1291 0548 2350
5555 5555 5555 4444
Visa 4 4111 1111 1111 1111
4012 8888 8888 1881
4222 2222 2222 2

Note that American Express card numbers are 15 digits, while most other card numbers are 16 digits. The final Visa number, which is only 13 digits, is also valid.

Of course, people sometimes type their credit card numbers incorrectly. To catch simple errors (e.g,. transpositions, off-by-one, etc.), the rightmost digit of the card number is actually a checksum. In other words, the value of the rightmost digit is the result of a carefully designed calculation involving the other digits. It is an example of a Luhn Algorithm, which is itself part of a much larger class of error detecting codes. If the checksum is inconsistent with the other digits, the card number is invalid and should be re-entered. By pre-checking the number before contacting the card provider, this approach can provide feedback quickly and avoid invalid transactions.

The checksum is validated as follows:

  1. Working from right to left, double every other digit, starting after the rightmost digit. If any of the resulting numbers are two digits, add the digits together to get a single digit.
  2. Sum up all the numbers.
  3. If the total ends in zero (30, 40, 50, etc.), then the number is valid.

Consider the following example of a valid 15-digit number:

Original card number
3
7
1
4
4
9
6
3
5
3
9
8
4
3
1
Sum
Valid
Doubled digits

14

8

18

6

6

16

6

Doubled digits, reduced to a single digit

5

8

9

6

6

7

6

47
Unchanged digits
3

1

4

6

5

9

4

1
33
Check

80
True

And here is an example of an invalid 16-digit number:

Original card number
4
0
1
2
3
4
5
6 7 8
9
0
1
2
3
4
Sum
Valid
Doubled digits
8

2

6

10

14

18

2

6

Doubled digits, reduced to a single digit
8

2

6
1

5
9

2

6

39
Unchanged digits

0

2

4

6

8

0

2

4
26
Check

65
False

Assignment

Create two files: one named credit_card.py and one named test_credit_card.py.

In the credit_card module, you will implement the functions partially documented below:

def is_type_accepted(card_number):
'''Checks if the given card is of an accepted type.
Parameter: A string representing a credit card number.
Return value: True if the number is an American Express, Discover, MasterCard, or Visa number;
False otherwise.
'''


def is_checksum_valid(card_number):
'''Checks if a credit card number is valid.
Parameter: A string representing an integer.
Return value: True if the number passes the Luhn checksum algorithm;
False if it does not.
'''

def main():
'''Asks the user for a credit card number. Reports whether the number is accepted and valid.
'''

if __name__=='__main__':
main()

Follow the pattern suggested by How to Solve It:

Begin by carefully reading the examples above to understand the problem. Work additional examples. Try checking one of your own credit card numbers: You know who issued the card, and you know the number is valid. Change one of the digits to create an invalid number, and make sure you can show it is invalid. Destroy any scratch paper when you are done so you don't give your card number away!

Then develop a top-down design and write pseudocode. How can you break down the problems into smaller parts? There are many different ways. We will compare ideas in class on Wednesday; make sure you have developed a top-down design and written pseudocode by then.

Next, implement these functions. As you go along,

Finally, review your code for clarity, correctness, and efficiency.

Remember this process is iterative: At any point you may need to go back to revise your work at an earlier step.

To help you get started with your unit tests, here is a unit test for is_checksum_valid.

from credit_card import *    


def test_is_checksum_valid():
assert is_checksum_valid("378 2822 4631 0005") == True
assert is_checksum_valid("378 2822 4631 0000") == False # Changed check digit
assert is_checksum_valid("379 2822 4631 0005") == False # Changed undoubled digit
assert is_checksum_valid("378 5822 4631 0005") == False # Changed doubled digit
assert is_checksum_valid("738 2822 4631 0005") == False # Transposed two digits

assert is_checksum_valid("6011 0009 9013 9424") == True
assert is_checksum_valid("5394 1291 0548 2350") == True

assert is_checksum_valid("2000 0000 0000 0006") == True
assert is_checksum_valid("7000 0000 0000 0005") == False

assert is_checksum_valid("240") == True
assert is_checksum_valid("345") == False

assert is_checksum_valid("1010101010") == True
assert is_checksum_valid("2010101010") == False

assert is_checksum_valid("4222 2222 2222 2") == True
assert is_checksum_valid("4111 1111 1111 1") == False

assert is_checksum_valid("5555 5555 5555 4444") == True
assert is_checksum_valid("5555 5555 5555 0101") == False

assert is_checksum_valid("6011111111111117") == True
assert is_checksum_valid("6011111111111110") == False

print("is_checksum_valid: All tests pass!")

Grading and Submission

Please style your code per section 3.4 of the textbook.

Submit two files, credit_card.py and test_credit_card.py, through the online turnin form.

2 points -
is_type_accepted
4 points -
is_checksum_valid
1 point -
main
6 points -
Supporting functions based on top-down design
6 points -
Documentation for supporting functions
3 points -
Unit tests for is_type_accepted
6 points -
Unit tests for supporting functions
2 points -
Style and following instrutions


Janet Davis (davisj@whitman.edu).
This assignment is adapted from one assigned this spring. Thanks to PayPal for providing sample (fake) credit card numbers for testing purposes and to FreeFormatter for providing an online validation tool.

Created October 27, 2016
Last revised November 28, 2016, 09:53:50 AM PST
CC-BY-NC-SA This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.