mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-21 05:45:13 +00:00
71 lines
2.3 KiB
Python
Executable file
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
|