NewsBlur/vendor/paypal/pro/creditcard.py
2014-11-07 16:22:28 -08:00

71 lines
2.3 KiB
Python
Executable file

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Adapted from:
- http://www.djangosnippets.org/snippets/764/
- http://www.satchmoproject.com/trac/browser/satchmo/trunk/satchmo/apps/satchmo_utils/views.py
- http://tinyurl.com/shoppify-credit-cards
"""
import re
# Well known card regular expressions.
CARDS = {
'Visa': re.compile(r"^4\d{12}(\d{3})?$"),
'Mastercard': re.compile(r"^(5[1-5]\d{4}|677189)\d{10}$"),
'Dinersclub': re.compile(r"^3(0[0-5]|[68]\d)\d{11}$"),
'Amex': re.compile("^3[47]\d{13}$"),
'Discover': re.compile("^(6011|65\d{2})\d{12}$"),
}
# Well known test numbers
TEST_NUMBERS = [
"378282246310005", "371449635398431", "378734493671000", "30569309025904",
"38520000023237", "6011111111111117", "6011000990139424", "555555555554444",
"5105105105105100", "4111111111111111", "4012888888881881", "4222222222222"
]
def verify_credit_card(number):
"""Returns the card type for given card number or None if invalid."""
return CreditCard(number).verify()
class CreditCard(object):
def __init__(self, number):
self.number = number
def is_number(self):
"""True if there is at least one digit in number."""
self.number = re.sub(r'[^\d]', '', self.number)
return self.number.isdigit()
def is_mod10(self):
"""Returns True if number is valid according to mod10."""
double = 0
total = 0
for i in range(len(self.number) - 1, -1, -1):
for c in str((double + 1) * int(self.number[i])):
total = total + int(c)
double = (double + 1) % 2
return (total % 10) == 0
def is_test(self):
"""Returns True if number is a test card number."""
# Note: test numbers cannot be used in the PP Pro sandbox.
# Instead, use the credit card number associated with a
# sandbox account (Test Accounts -> View Details).
return self.number in TEST_NUMBERS
def get_type(self):
"""Return the type if it matches one of the cards."""
for card, pattern in CARDS.items():
if pattern.match(self.number):
return card
return None
def verify(self):
"""Returns the card type if valid else None."""
if self.is_number() and not self.is_test() and self.is_mod10():
return self.get_type()
return None