155 lines
5.6 KiB
Python
155 lines
5.6 KiB
Python
import random
|
||
import datetime
|
||
import re
|
||
|
||
class CardGenerator:
|
||
def __init__(self):
|
||
self.current_year = datetime.datetime.now().year
|
||
|
||
def luhn_checksum(self, card_number_str):
|
||
"""计算 Luhn 校验和"""
|
||
digits = [int(d) for d in card_number_str]
|
||
checksum = 0
|
||
is_second = False
|
||
for digit in reversed(digits):
|
||
if is_second:
|
||
digit *= 2
|
||
if digit > 9:
|
||
digit -= 9
|
||
checksum += digit
|
||
is_second = not is_second
|
||
return checksum
|
||
|
||
def calculate_check_digit(self, card_number_without_check):
|
||
"""计算最后一位校验位"""
|
||
temp_number = card_number_without_check + "0"
|
||
checksum = self.luhn_checksum(temp_number)
|
||
if checksum % 10 == 0:
|
||
return "0"
|
||
else:
|
||
return str(10 - (checksum % 10))
|
||
|
||
def generate_card_number(self, pattern):
|
||
"""
|
||
核心生成逻辑:支持任意位置的 'x'
|
||
"""
|
||
# 1. 确定卡号长度标准 (Amex 15位,其他 16位)
|
||
# 如果 pattern 已经很长,就用 pattern 的长度,否则根据卡头判断
|
||
target_len = 16
|
||
if pattern.startswith(('34', '37')):
|
||
target_len = 15
|
||
|
||
# 2. 补全 pattern 到目标长度 (减去最后一位校验位)
|
||
# 如果输入的 pattern 比如是 "547958" (长度6),需要补 x 到 15位 (留一位给校验)
|
||
# 如果输入已经包含了 x 且长度足够,则保留原样
|
||
|
||
working_pattern = pattern.lower()
|
||
|
||
# 移除可能存在的非数字/x字符
|
||
working_pattern = re.sub(r'[^0-9x]', '', working_pattern)
|
||
|
||
# 自动填充 x
|
||
if len(working_pattern) < target_len:
|
||
working_pattern += 'x' * (target_len - len(working_pattern))
|
||
|
||
# 截断到 倒数第二位 (最后一位是校验位)
|
||
# 注意:如果用户输入了完整的 16位 pattern 且最后一位不是 x,我们会覆盖它,
|
||
# 因为 Luhn 算法要求最后一位必须是计算出来的。
|
||
# 除非用户就是想校验?但在生成逻辑里,我们通常假设最后一位是计算的。
|
||
base_pattern = working_pattern[:target_len-1]
|
||
|
||
generated_prefix = ""
|
||
for char in base_pattern:
|
||
if char == 'x':
|
||
generated_prefix += str(random.randint(0, 9))
|
||
else:
|
||
generated_prefix += char
|
||
|
||
# 计算校验位
|
||
check_digit = self.calculate_check_digit(generated_prefix)
|
||
return generated_prefix + check_digit
|
||
|
||
def parse_date_cvv(self, card_num, m_req, y_req, c_req):
|
||
"""
|
||
处理日期和CVV的占位符逻辑
|
||
"""
|
||
# --- Month ---
|
||
if m_req in ['(m)', 'rnd', '', None]:
|
||
month = str(random.randint(1, 12)).zfill(2)
|
||
else:
|
||
month = m_req.strip() # 使用用户指定的静态值,如 '05'
|
||
|
||
# --- Year ---
|
||
if y_req in ['(y)', 'rnd', '', None]:
|
||
year = str(self.current_year + random.randint(2, 5))
|
||
else:
|
||
year = y_req.strip() # 使用用户指定的静态值,如 '2023' 或 '23'
|
||
|
||
# --- CVV ---
|
||
if c_req in ['(cvv)', 'rnd', '', None]:
|
||
if card_num.startswith(('34', '37')):
|
||
cvv = str(random.randint(1102, 9999))
|
||
else:
|
||
cvv = str(random.randint(112, 999))
|
||
else:
|
||
cvv = c_req.strip()
|
||
|
||
return month, year, cvv
|
||
|
||
def process_line(self, raw_input, count=1):
|
||
"""
|
||
智能解析输入字符串并生成
|
||
支持格式:
|
||
- BIN only: 547958
|
||
- Pattern: 460723xxxxxxxxxx
|
||
- Complex: 5319719xxx5xxx87
|
||
- Full: 434256|11|2022|212
|
||
- Templates: 434256|(m)|(y)|(cvv)
|
||
"""
|
||
# 默认值
|
||
parts = raw_input.split('|')
|
||
|
||
# 提取各个部分,没有的就设为 None,交给 parse_date_cvv 处理默认逻辑
|
||
pattern_part = parts[0] if len(parts) > 0 else ""
|
||
month_part = parts[1] if len(parts) > 1 else None
|
||
year_part = parts[2] if len(parts) > 2 else None
|
||
cvv_part = parts[3] if len(parts) > 3 else None
|
||
|
||
results = []
|
||
for _ in range(count):
|
||
# 1. 生成卡号
|
||
card_num = self.generate_card_number(pattern_part)
|
||
|
||
# 2. 处理附属信息
|
||
m, y, c = self.parse_date_cvv(card_num, month_part, year_part, cvv_part)
|
||
|
||
results.append(f"{card_num}|{m}|{y}|{c}")
|
||
|
||
return results
|
||
|
||
# --- 测试与演示 ---
|
||
|
||
if __name__ == "__main__":
|
||
gen = CardGenerator()
|
||
|
||
# 你列出的所有测试用例
|
||
test_cases = [
|
||
"547958", # 仅 BIN
|
||
"460723xxxxxxxxxx", # 带图案
|
||
"5319719xxx5xxx87", # 专业用户 (混合 pattern)
|
||
"434256|11|2022|212", # 你自己的固定模式
|
||
"559917|11|2022|(cvv)", # 随机 CVV
|
||
"434256|(m)|(y)|(cvv)", # 全随机占位符
|
||
"434256240669|(m)|(y)|(cvv)", # 短卡号 (没有最后4位) -> 会自动补全
|
||
"53673712123xxxxx|05|2023|", # 指定长年份
|
||
"53673712123xxxxx|05|23|" # 指定短年份
|
||
]
|
||
|
||
print(f"{'INPUT FORMAT':<35} | {'GENERATED OUTPUT'}")
|
||
print("-" * 80)
|
||
|
||
for case in test_cases:
|
||
# 每个案例生成 1 条作为演示
|
||
output = gen.process_line(case, count=1)[0]
|
||
print(f"{case:<35} -> {output}")
|