def checksum(card_without_check):
card_without_check = card_without_check[-1::-1]
def numbers(string):
return [int(x) for x in string]
print(card_without_check)
odd_numbers = numbers(card_without_check[0::2])
even_numbers = numbers(card_without_check[1::2])
odd_numbers = [x * 2 for x in odd_numbers]
odd_numbers = [x - 9 if x > 9 else x for x in odd_numbers]
print(even_numbers)
print(odd_numbers)
return sum(odd_numbers) + sum(even_numbers)
def check(checksum, check):
return checksum % 10 == int(check)
card_number = input("Enter card number:\n")
print(checksum(card_number[:-1]))
print("Card is", check(checksum(card_number[:-1]), card_number[-1]))
此算法似乎适用于“4556737586899855”等示例,但不适用于“30569309025904”等示例。我已经按照这个过程找不到它处理数字的缺陷了,我可能只是错过了这里的一些难题。
答案 0 :(得分:1)
我使用这个解决方案来解决基于Luhn公式的代码问题:
def checksum(n):
nums = reversed(list(map(int, n)))
doubled = (ele * 2 if ind % 2 else ele for ind, ele in enumerate(nums))
return not sum(sum(map(int, str(ele))) for ele in doubled) % 10
问题描述中列出了这些步骤:
从最右边的数字,即校验位,向左移动,每隔一位数的值加倍;如果该倍增操作的乘积大于9(例如,7×2 = 14),则将乘积的数字相加(例如,12:1 + 2 = 3,14:1 + 4 = 5)。 取所有数字的总和。 如果总模10等于0(如果总数以零结束)那么,根据Luhn公式,该数是有效的;否则,它无效。
答案 1 :(得分:0)
这是我从(挪威语)维基百科的描述中写的MOD10(Luhn算法)的代码:
def weights(n, base):
"""Repeating series of numbers from base.
For kontroll_10 it returns the series: 2,1,2,1,2,1...
which is equivalent to multiplying every other digit
by two (code is originally from a broader checsum module).
"""
for i in range(n):
yield base[i % len(base)]
def luhn_algorithm(s):
"""Also known as the MOD10 algorithm.
"""
digits = map(int, list(s))[::-1] # reversed
_weights = weights(len(digits), [2, 1])
products = ''.join(str(s * v) for (s, v) in zip(digits, _weights))
sum_of_digits = sum(int(c) for c in products)
check_digit = sum_of_digits % 10
if check_digit == 0:
checksum = check_digit
else:
checksum = 10 - check_digit
return str(checksum)
看起来你可能在entallsiffer
为零时跳过特殊处理,而你也没有从10中减去,这意味着当控制/校验和数字为{{{}时,你只会得到正确的答案。 1}}。
答案 2 :(得分:0)
你几乎把它弄好了,除了最后一个数字的初始切断。您不能以一个切片表示法反转和切片一个,而是两个:
card_without_check = card[::-1][1:]
然后在对checksum()例程的调用中,逻辑变得过火。试试这个:
def checksum(card):
def numbers(string):
return [int(x) for x in string]
card_without_check = card[::-1][1:]
print(card_without_check)
odd_numbers = numbers(card_without_check[0::2])
even_numbers = numbers(card_without_check[1::2])
odd_numbers = [x * 2 for x in odd_numbers]
odd_numbers = [x - 9 if x > 9 else x for x in odd_numbers]
print odd_numbers
print even_numbers
return (sum(odd_numbers) + sum(even_numbers)) % 10
def check(card):
return checksum(card) == int(card[-1])
def main():
card = str(input("Enter card number:\n"))
print "Valid? ", check(card)
输入4556737586899855得出:
Valid? 589986857376554
[1, 9, 7, 7, 5, 5, 1, 8]
[8, 9, 6, 5, 3, 6, 5]
True
答案 3 :(得分:0)
这是我写的信用卡工具库,涵盖了luhn校验和以及其他信用卡支票。
import re
import logging
import datetime
VISA_CC = 'Visa' # Visa
MASTERCARD_CC = 'MasterCard' # MasterCard
AMEX_CC = 'American Express' # American Express
JCB_CC = 'JCB'
DISCOVER_CC = 'DISCOVER'
DINERS_CC = 'DINERS'
MAESTRO_CC = 'MAESTRO'
LASER_CC = 'LASER'
OTHER_CC = '' # UNKNOWN
# Set which cards you accept
ACCEPTED_CARDS = [VISA_CC, MASTERCARD_CC, AMEX_CC]
def is_american_express(cc_number):
"""Checks if the card is an american express. If us billing address country code, & is_amex, use vpos
https://en.wikipedia.org/wiki/Bank_card_number#cite_note-GenCardFeatures-3
:param cc_number: unicode card number
"""
return bool(re.match(r'^3[47][0-9]{13}$', cc_number))
def is_visa(cc_number):
"""Checks if the card is a visa, begins with 4 and 12 or 15 additional digits.
:param cc_number: unicode card number
"""
# Standard Visa is 13 or 16, debit can be 19
if bool(re.match(r'^4', cc_number)) and len(cc_number) in [13, 16, 19]:
return True
return False
def is_mastercard(cc_number):
"""Checks if the card is a mastercard. Begins with 51-55 or 2221-2720 and 16 in length.
:param cc_number: unicode card number
"""
if len(cc_number) == 16 and cc_number.isdigit(): # Check digit, before cast to int
return bool(re.match(r'^5[1-5]', cc_number)) or int(cc_number[:4]) in range(2221, 2721)
return False
def is_discover(cc_number):
"""Checks if the card is discover, re would be too hard to maintain. Not a supported card.
:param cc_number: unicode card number
"""
if len(cc_number) == 16:
try:
# return bool(cc_number[:4] == '6011' or cc_number[:2] == '65' or cc_number[:6] in range(622126, 622926))
return bool(cc_number[:4] == '6011' or cc_number[:2] == '65' or 622126 <= int(cc_number[:6]) <= 622925)
except ValueError:
return False
return False
def is_jcb(cc_number):
"""Checks if the card is a jcb. Not a supported card.
:param cc_number: unicode card number
"""
# return bool(re.match(r'^(?:2131|1800|35\d{3})\d{11}$', cc_number)) # wikipedia
return bool(re.match(r'^35(2[89]|[3-8][0-9])[0-9]{12}$', cc_number)) # PawelDecowski
def is_diners_club(cc_number):
"""Checks if the card is a diners club. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^3(?:0[0-6]|[68][0-9])[0-9]{11}$', cc_number)) # 0-5 = carte blance, 6 = international
def is_laser(cc_number):
"""Checks if the card is laser. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^(6304|670[69]|6771)', cc_number))
def is_maestro(cc_number):
"""Checks if the card is maestro. Not a supported card.
:param cc_number: unicode card number
"""
possible_lengths = [12, 13, 14, 15, 16, 17, 18, 19]
return bool(re.match(r'^(50|5[6-9]|6[0-9])', cc_number)) and len(cc_number) in possible_lengths
# Child cards
def is_visa_electron(cc_number):
"""Child of visa. Checks if the card is a visa electron. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^(4026|417500|4508|4844|491(3|7))', cc_number)) and len(cc_number) == 16
def is_total_rewards_visa(cc_number):
"""Child of visa. Checks if the card is a Total Rewards Visa. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^41277777[0-9]{8}$', cc_number))
def is_diners_club_carte_blanche(cc_number):
"""Child card of diners. Checks if the card is a diners club carte blance. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^30[0-5][0-9]{11}$', cc_number)) # github PawelDecowski, jquery-creditcardvalidator
def is_diners_club_carte_international(cc_number):
"""Child card of diners. Checks if the card is a diners club international. Not a supported card.
:param cc_number: unicode card number
"""
return bool(re.match(r'^36[0-9]{12}$', cc_number)) # jquery-creditcardvalidator
def get_card_type_by_number(cc_number):
"""Return card type constant by credit card number
:param cc_number: unicode card number
"""
if is_visa(cc_number):
return VISA_CC
if is_mastercard(cc_number):
return MASTERCARD_CC
if is_american_express(cc_number):
return AMEX_CC
if is_discover(cc_number):
return DISCOVER_CC
if is_jcb(cc_number):
return JCB_CC
if is_diners_club(cc_number):
return DINERS_CC
if is_laser(cc_number): # Check before maestro, as its inner range
return LASER_CC
if is_maestro(cc_number):
return MAESTRO_CC
return OTHER_CC
def get_card_type_by_name(cc_type):
"""Return card type constant by string
:param cc_type: dirty string card type or name
"""
cc_type_str = cc_type.replace(' ', '').replace('-', '').lower()
# Visa
if 'visa' in cc_type_str:
return VISA_CC
# MasterCard
if 'mc' in cc_type_str or 'mastercard' in cc_type_str:
return MASTERCARD_CC
# American Express
if cc_type_str in ('americanexpress', 'amex'):
return AMEX_CC
# Discover
if 'discover' in cc_type_str:
return DISCOVER_CC
# JCB
if 'jcb' in cc_type_str:
return JCB_CC
# Diners
if 'diners' in cc_type_str:
return DINERS_CC
# Maestro
if 'maestro' in cc_type_str:
return MAESTRO_CC
# Laser
if 'laser' in cc_type_str:
return LASER_CC
# Other Unsupported Cards Dankort, Union, Cartebleue, Airplus etc..
return OTHER_CC
def credit_card_luhn_checksum(card_number):
"""Credit card luhn checksum
:param card_number: unicode card number
"""
def digits_of(cc):
return [int(_digit) for _digit in str(cc)]
digits = digits_of(card_number)
odd_digits = digits[-1::-2]
even_digits = digits[-2::-2]
checksum = sum(odd_digits)
for digit in even_digits:
checksum += sum(digits_of(digit * 2))
return checksum % 10
def is_valid_cvv(card_type, cvv):
"""Validates the cvv based on card type
:param cvv: card cvv security code
:param card_type: string card type
"""
if card_type == AMEX_CC:
return len(str(cvv)) == 4
else:
return len(str(cvv)) == 3
def is_cc_luhn_valid(card_number):
"""Returns true if the luhn code is 0
:param card_number: unicode string for card_number, cannot be other type.
"""
is_valid_cc = card_number.isdecimal() and credit_card_luhn_checksum(card_number) == 0
if not is_valid_cc:
logging.error("Invalid Credit Card Number, fails luhn: {}".format(card_number))
return is_valid_cc
def is_valid_cc_expiry(expiry_month, expiry_year):
"""Returns true if the card expiry is not good.
Edge case: It's end of month, the expiry is on the current month and user is in a different timezone.
:param expiry_year: unicode two digit year
:param expiry_month: unicode two digit month
"""
try:
today = datetime.date.today()
cur_month, cur_year = today.month, int(str(today.year)[2:])
expiry_month, expiry_year = int(expiry_month), int(expiry_year)
is_invalid_year = expiry_year < cur_year
is_invalid_month = False
if not is_invalid_year:
is_invalid_month = ((expiry_month < cur_month and cur_year == expiry_year) or
expiry_month not in range(1, 13))
if is_invalid_year or is_invalid_month:
logging.info("Invalid credit card expiry {}/{}.".format(expiry_month, expiry_year))
return False
except ValueError:
logging.error("Could not calculate valid expiry for month year {}/{}.".format(expiry_month, expiry_year))
return False
return True
def is_supported_credit_card(card_type):
"""Checks if card type is in accepted cards
:param card_type: string card type
"""
if card_type in ACCEPTED_CARDS:
return True
logging.error("Card type not supported, {}.".format(card_type))
return False # (OTHER_CC, DISCOVER_CC)
def cc_card_to_mask(cc_number, show_first=6, show_last=4):
"""Returns masked credit card number
:param show_last: beginning of card, chars not to mask
:param show_first: end of card, chars not to mask
:param cc_number: unicode card number
"""
cc_number = str(cc_number)
if cc_number:
return "{}{}{}".format(cc_number[:show_first],
"X" * (len(cc_number) - show_first - show_last),
cc_number[show_last * -1:])
else:
return ""
def string_to_full_mask(cc_field):
"""Returns credit card field or any string converted to a full mask.
I.e. cvv, expiry month, expiry year, password.
:param cc_field: a generic credit card field, other than cc card no
"""
try:
cc_field = cc_field.strip()
return "X" * len(cc_field)
except (TypeError, AttributeError):
return ""
如果有任何遗漏有用,请随时发表评论。欢呼声。