修改支付和注册逻辑
This commit is contained in:
7
complete_accounts.json
Normal file
7
complete_accounts.json
Normal file
@@ -0,0 +1,7 @@
|
||||
[
|
||||
{
|
||||
"account": "ngvzryure4@depart.sar.de5.net",
|
||||
"password": "Jl8Or%2TMO774e1t",
|
||||
"token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MzQ0ZTY1LWJiYzktNDRkMS1hOWQwLWY5NTdiMDc5YmQwZSIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS92MSJdLCJjbGllbnRfaWQiOiJhcHBfWDh6WTZ2VzJwUTl0UjNkRTduSzFqTDVnSCIsImV4cCI6MTc2OTkyOTczOCwiaHR0cHM6Ly9hcGkub3BlbmFpLmNvbS9hdXRoIjp7ImNoYXRncHRfY29tcHV0ZV9yZXNpZGVuY3kiOiJub19jb25zdHJhaW50IiwiY2hhdGdwdF9kYXRhX3Jlc2lkZW5jeSI6Im5vX2NvbnN0cmFpbnQiLCJpc19zaWdudXAiOnRydWUsInVzZXJfaWQiOiJ1c2VyLU5kZFJOTnprczNqS2xSaUZDRXZXTHdGayJ9LCJodHRwczovL2FwaS5vcGVuYWkuY29tL3Byb2ZpbGUiOnsiZW1haWwiOiJuZ3Z6cnl1cmU0QGRlcGFydC5zYXIuZGU1Lm5ldCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlfSwiaWF0IjoxNzY5MDY1NzM3LCJpc3MiOiJodHRwczovL2F1dGgub3BlbmFpLmNvbSIsImp0aSI6IjE3ZjkyYjNjLTEyNGItNDk4ZC04NzljLTc4MjEwMTY5M2UyMyIsIm5iZiI6MTc2OTA2NTczNywicHdkX2F1dGhfdGltZSI6MTc2OTA2NTczNDQ3OCwic2NwIjpbIm9wZW5pZCIsImVtYWlsIiwicHJvZmlsZSIsIm9mZmxpbmVfYWNjZXNzIiwibW9kZWwucmVxdWVzdCIsIm1vZGVsLnJlYWQiLCJvcmdhbml6YXRpb24ucmVhZCIsIm9yZ2FuaXphdGlvbi53cml0ZSJdLCJzZXNzaW9uX2lkIjoiYXV0aHNlc3NfTThEV2JpczE3a3FzZldldDZjTTRZdTJvIiwic3ViIjoiYXV0aDB8T3Baa2VKeXA3cXZ2SlMxSmVacWtqbVZiIn0.uUK6F3ApO1gBkL4N794xwG4qiJTOYqaNX3k6XcSYi8Q92KyTWJQPsEZmr_Ed4fmQ-U4swStNL6n-POzECSok_tVvI57XoPhowDknVYx2ZukdxEtPH2-mGYVFPyjEOpxsJxpGDBP_toidaoE7xIMFgRtVrjehClik7rA0k5bXbYyZt-zShVBa7dpmqBE4tT4CROQ_B9xg2QlrSEIgglUWjtr04C71tWWff1Noc40wwLVDMVi0JRlWM2IlUN-MpxV6ek_T5GGHdiQKy6zL0BJMxRynK9mIVZNWWqb2oINY1hZgkMq1ISNPDtUBOk8u6uqpcowpzmlUlwKZHJXZhpaRVdWKII3WwZHsUpcsLd85pUaMDVOeYtJ_OpdyArOgSrHOncj67HsbFNWrPoKBncC2R4SiXH4awoOcFaSfNJE_lFhSoo7bC4XRR5ERbBtLCH9jYv7-q2UYJbyUB8btN3EF4Y8iCB_yGVEuYdfJUG2aYVqRwwf95AUiF66b59MeIzlm0JBQXlAPiO7U9jMKWu1I-WwKtGonh1LBf2WOT8uvu1DP_fzxHGyaINu814o_ZvQAKpwSZTxTpoTOwuPoRF1KpmJcsyDKy29XFYXMn5rWAYvPZ37hB4pFiqu5G9tvvYV_EGOHD-JlyyNRkMNkmN9Ogbz3Xc8QcSbQSiQwWsi55VE"
|
||||
}
|
||||
]
|
||||
20
config.py
20
config.py
@@ -52,7 +52,7 @@ EU_BILLING_CONFIG = {
|
||||
|
||||
# 团队计划详情
|
||||
'team_plan_data': {
|
||||
'workspace_name': 'Sepa', # 工作空间名称
|
||||
'workspace_name': None, # None = 自动生成随机名称,或设置字符串使用固定值
|
||||
'price_interval': 'month', # 'month' 或 'year'
|
||||
'seat_quantity': 5, # 座位数量(团队计划最少 5 个)
|
||||
},
|
||||
@@ -82,3 +82,21 @@ EU_BILLING_CONFIG = {
|
||||
|
||||
# 调试模式
|
||||
DEBUG = True
|
||||
|
||||
# 人类行为延迟配置(秒)
|
||||
DELAY_CONFIG = {
|
||||
# 注册流程延迟
|
||||
'after_registration': {'min': 3.0, 'max': 8.0}, # 注册成功后
|
||||
'before_get_token': {'min': 2.0, 'max': 5.0}, # 获取token前
|
||||
'after_get_token': {'min': 1.5, 'max': 4.0}, # 获取token后
|
||||
'before_billing': {'min': 2.0, 'max': 6.0}, # 生成账单前
|
||||
'after_billing': {'min': 2.0, 'max': 5.0}, # 生成账单后
|
||||
'before_payment': {'min': 3.0, 'max': 7.0}, # 开始支付前
|
||||
|
||||
# 支付流程延迟
|
||||
'between_payment_steps': {'min': 1.0, 'max': 3.0}, # 支付步骤之间
|
||||
'polling_interval': {'min': 3.0, 'max': 5.0}, # 轮询间隔
|
||||
|
||||
# 批量操作延迟
|
||||
'between_accounts': {'min': 30.0, 'max': 60.0}, # 账号之间(原10秒太短)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ from modules.tempmail import TempMailClient
|
||||
from modules.billing import EUBillingGenerator
|
||||
from modules.iban_generator import GermanIbanGenerator
|
||||
from modules.notion_client import NotionClient
|
||||
from config import TEMPMAIL_CONFIG, DEBUG
|
||||
from modules.delay_utils import HumanDelay
|
||||
from modules.data_generator import generate_payment_info
|
||||
from config import TEMPMAIL_CONFIG, DEBUG, DELAY_CONFIG
|
||||
|
||||
|
||||
class CompleteRegistrationFlow:
|
||||
@@ -97,20 +99,32 @@ class CompleteRegistrationFlow:
|
||||
if not result.get('success'):
|
||||
return result
|
||||
|
||||
# 延迟:注册完成后,模拟用户查看邮箱/确认的时间
|
||||
HumanDelay.custom(**DELAY_CONFIG['after_registration'])
|
||||
|
||||
# 获取 Access Token
|
||||
try:
|
||||
# 延迟:准备获取token
|
||||
HumanDelay.custom(**DELAY_CONFIG['before_get_token'])
|
||||
|
||||
access_token = self.registrar._step5_get_access_token()
|
||||
result['access_token'] = access_token
|
||||
|
||||
if DEBUG:
|
||||
print(f"✅ Access Token 获取成功")
|
||||
|
||||
# 延迟:获取token后
|
||||
HumanDelay.custom(**DELAY_CONFIG['after_get_token'])
|
||||
|
||||
except Exception as e:
|
||||
result['billing_error'] = f"获取 Access Token 失败: {str(e)}"
|
||||
return result
|
||||
|
||||
# 生成账单链接
|
||||
try:
|
||||
# 延迟:准备生成账单
|
||||
HumanDelay.custom(**DELAY_CONFIG['before_billing'])
|
||||
|
||||
billing_generator = EUBillingGenerator()
|
||||
billing_result = billing_generator.generate_checkout_url(access_token)
|
||||
|
||||
@@ -118,6 +132,9 @@ class CompleteRegistrationFlow:
|
||||
result['checkout_url'] = billing_result.checkout_url
|
||||
if DEBUG:
|
||||
print(f"✅ 账单链接生成成功")
|
||||
|
||||
# 延迟:生成账单后
|
||||
HumanDelay.custom(**DELAY_CONFIG['after_billing'])
|
||||
else:
|
||||
result['billing_error'] = billing_result.error
|
||||
|
||||
@@ -183,35 +200,40 @@ class CompleteRegistrationFlow:
|
||||
result['output_format'] = output_format
|
||||
return result
|
||||
|
||||
# 准备支付信息(使用默认值或用户提供的值)
|
||||
default_payment_info = {
|
||||
"name": "John Doe",
|
||||
"address_line1": "123 Main Street",
|
||||
"city": "New York",
|
||||
"postal_code": "10001",
|
||||
"state": "NY",
|
||||
"country": "US"
|
||||
}
|
||||
|
||||
# 准备支付信息(使用随机生成或用户提供的值)
|
||||
if payment_info:
|
||||
default_payment_info.update(payment_info)
|
||||
# 用户提供了自定义信息
|
||||
final_payment_info = payment_info
|
||||
else:
|
||||
# 生成随机真实的支付信息
|
||||
final_payment_info = generate_payment_info()
|
||||
|
||||
if DEBUG:
|
||||
print(f"💳 [Payment] 使用支付信息:")
|
||||
print(f" 姓名: {final_payment_info['name']}")
|
||||
print(f" 地址: {final_payment_info['address_line1']}")
|
||||
print(f" 城市: {final_payment_info['city']}, {final_payment_info['state']} {final_payment_info['postal_code']}")
|
||||
|
||||
# 延迟:准备支付
|
||||
HumanDelay.custom(**DELAY_CONFIG['before_payment'])
|
||||
|
||||
# 执行支付
|
||||
try:
|
||||
payment_result = self.registrar.add_payment_method(
|
||||
checkout_session_url=checkout_url,
|
||||
iban=iban,
|
||||
name=default_payment_info['name'],
|
||||
name=final_payment_info['name'],
|
||||
email=result['email'],
|
||||
address_line1=default_payment_info['address_line1'],
|
||||
city=default_payment_info['city'],
|
||||
postal_code=default_payment_info['postal_code'],
|
||||
state=default_payment_info['state'],
|
||||
country=default_payment_info['country']
|
||||
address_line1=final_payment_info['address_line1'],
|
||||
city=final_payment_info['city'],
|
||||
postal_code=final_payment_info['postal_code'],
|
||||
state=final_payment_info['state'],
|
||||
country=final_payment_info.get('country', 'US')
|
||||
)
|
||||
|
||||
if payment_result.get('success'):
|
||||
result['payment_added'] = True
|
||||
result['payment_info'] = final_payment_info # 保存使用的支付信息
|
||||
if DEBUG:
|
||||
print(f"✅ 支付方式添加成功")
|
||||
else:
|
||||
|
||||
@@ -17,6 +17,7 @@ except ImportError:
|
||||
USE_CURL_CFFI = False
|
||||
|
||||
from config import EU_BILLING_CONFIG, FINGERPRINT_CONFIG, DEBUG, HTTP_CONFIG
|
||||
from modules.data_generator import WorkspaceNameGenerator
|
||||
|
||||
if USE_CURL_CFFI and DEBUG:
|
||||
print("[Billing] Using curl_cffi for requests")
|
||||
@@ -228,13 +229,18 @@ class EUBillingGenerator:
|
||||
Returns:
|
||||
Complete request payload
|
||||
"""
|
||||
# 获取配置中的 workspace_name,如果为 None 则自动生成
|
||||
workspace_name = EU_BILLING_CONFIG.get('team_plan_data', {}).get('workspace_name')
|
||||
if workspace_name is None:
|
||||
workspace_name = WorkspaceNameGenerator.generate()
|
||||
|
||||
payload = {
|
||||
"plan_name": EU_BILLING_CONFIG.get('plan_name', 'chatgptteamplan'),
|
||||
"team_plan_data": EU_BILLING_CONFIG.get('team_plan_data', {
|
||||
'workspace_name': 'Sepa',
|
||||
'price_interval': 'month',
|
||||
'seat_quantity': 5,
|
||||
}),
|
||||
"team_plan_data": {
|
||||
'workspace_name': workspace_name, # 使用动态或配置的值
|
||||
'price_interval': EU_BILLING_CONFIG.get('team_plan_data', {}).get('price_interval', 'month'),
|
||||
'seat_quantity': EU_BILLING_CONFIG.get('team_plan_data', {}).get('seat_quantity', 5),
|
||||
},
|
||||
"billing_details": EU_BILLING_CONFIG.get('billing_details', {
|
||||
'country': 'DE',
|
||||
'currency': 'EUR',
|
||||
|
||||
151
modules/data_generator.py
Normal file
151
modules/data_generator.py
Normal file
@@ -0,0 +1,151 @@
|
||||
"""
|
||||
真实数据生成器
|
||||
用于生成更真实的姓名、地址等信息,避免固定值
|
||||
"""
|
||||
import secrets
|
||||
from typing import Dict
|
||||
|
||||
class NameGenerator:
|
||||
"""真实姓名生成器"""
|
||||
|
||||
# 美国常见姓氏(Top 50)
|
||||
LAST_NAMES = [
|
||||
"Smith", "Johnson", "Williams", "Brown", "Jones",
|
||||
"Garcia", "Miller", "Davis", "Rodriguez", "Martinez",
|
||||
"Hernandez", "Lopez", "Gonzalez", "Wilson", "Anderson",
|
||||
"Thomas", "Taylor", "Moore", "Jackson", "Martin",
|
||||
"Lee", "Perez", "Thompson", "White", "Harris",
|
||||
"Sanchez", "Clark", "Ramirez", "Lewis", "Robinson",
|
||||
"Walker", "Young", "Allen", "King", "Wright",
|
||||
"Scott", "Torres", "Nguyen", "Hill", "Flores",
|
||||
"Green", "Adams", "Nelson", "Baker", "Hall",
|
||||
"Rivera", "Campbell", "Mitchell", "Carter", "Roberts"
|
||||
]
|
||||
|
||||
# 美国常见名字(Top 50)
|
||||
FIRST_NAMES_MALE = [
|
||||
"James", "Robert", "John", "Michael", "David",
|
||||
"William", "Richard", "Joseph", "Thomas", "Christopher",
|
||||
"Charles", "Daniel", "Matthew", "Anthony", "Mark",
|
||||
"Donald", "Steven", "Andrew", "Paul", "Joshua",
|
||||
"Kenneth", "Kevin", "Brian", "George", "Timothy",
|
||||
"Ronald", "Edward", "Jason", "Jeffrey", "Ryan",
|
||||
"Jacob", "Gary", "Nicholas", "Eric", "Jonathan",
|
||||
"Stephen", "Larry", "Justin", "Scott", "Brandon",
|
||||
"Benjamin", "Samuel", "Raymond", "Gregory", "Alexander",
|
||||
"Patrick", "Frank", "Dennis", "Jerry", "Tyler"
|
||||
]
|
||||
|
||||
FIRST_NAMES_FEMALE = [
|
||||
"Mary", "Patricia", "Jennifer", "Linda", "Barbara",
|
||||
"Elizabeth", "Susan", "Jessica", "Sarah", "Karen",
|
||||
"Lisa", "Nancy", "Betty", "Margaret", "Sandra",
|
||||
"Ashley", "Kimberly", "Emily", "Donna", "Michelle",
|
||||
"Carol", "Amanda", "Dorothy", "Melissa", "Deborah",
|
||||
"Stephanie", "Rebecca", "Sharon", "Laura", "Cynthia",
|
||||
"Kathleen", "Amy", "Angela", "Shirley", "Anna",
|
||||
"Brenda", "Pamela", "Emma", "Nicole", "Helen",
|
||||
"Samantha", "Katherine", "Christine", "Debra", "Rachel",
|
||||
"Carolyn", "Janet", "Catherine", "Maria", "Heather"
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def generate_full_name() -> str:
|
||||
"""生成真实的英文全名"""
|
||||
first_name = secrets.choice(
|
||||
NameGenerator.FIRST_NAMES_MALE if secrets.randbelow(2) == 0
|
||||
else NameGenerator.FIRST_NAMES_FEMALE
|
||||
)
|
||||
last_name = secrets.choice(NameGenerator.LAST_NAMES)
|
||||
return f"{first_name} {last_name}"
|
||||
|
||||
class AddressGenerator:
|
||||
"""美国地址生成器"""
|
||||
|
||||
STREET_NAMES = [
|
||||
"Main", "Oak", "Maple", "Cedar", "Elm", "Washington",
|
||||
"Lake", "Hill", "Park", "Pine", "First", "Second",
|
||||
"Third", "Broadway", "Church", "Spring", "River", "Sunset"
|
||||
]
|
||||
|
||||
STREET_TYPES = [
|
||||
"Street", "Avenue", "Road", "Boulevard", "Drive",
|
||||
"Lane", "Way", "Court", "Place", "Circle"
|
||||
]
|
||||
|
||||
# 美国主要城市和州
|
||||
CITIES = [
|
||||
{"city": "New York", "state": "NY", "zip_prefix": "100"},
|
||||
{"city": "Los Angeles", "state": "CA", "zip_prefix": "900"},
|
||||
{"city": "Chicago", "state": "IL", "zip_prefix": "606"},
|
||||
{"city": "Houston", "state": "TX", "zip_prefix": "770"},
|
||||
{"city": "Phoenix", "state": "AZ", "zip_prefix": "850"},
|
||||
{"city": "Philadelphia", "state": "PA", "zip_prefix": "191"},
|
||||
{"city": "San Antonio", "state": "TX", "zip_prefix": "782"},
|
||||
{"city": "San Diego", "state": "CA", "zip_prefix": "921"},
|
||||
{"city": "Dallas", "state": "TX", "zip_prefix": "752"},
|
||||
{"city": "San Jose", "state": "CA", "zip_prefix": "951"},
|
||||
{"city": "Austin", "state": "TX", "zip_prefix": "787"},
|
||||
{"city": "Jacksonville", "state": "FL", "zip_prefix": "322"},
|
||||
{"city": "Fort Worth", "state": "TX", "zip_prefix": "761"},
|
||||
{"city": "Columbus", "state": "OH", "zip_prefix": "432"},
|
||||
{"city": "Charlotte", "state": "NC", "zip_prefix": "282"},
|
||||
{"city": "Seattle", "state": "WA", "zip_prefix": "981"},
|
||||
{"city": "Denver", "state": "CO", "zip_prefix": "802"},
|
||||
{"city": "Boston", "state": "MA", "zip_prefix": "021"},
|
||||
{"city": "Portland", "state": "OR", "zip_prefix": "972"},
|
||||
{"city": "Miami", "state": "FL", "zip_prefix": "331"}
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def generate_address() -> Dict[str, str]:
|
||||
"""生成完整的美国地址"""
|
||||
street_number = secrets.randbelow(9900) + 100
|
||||
street_name = secrets.choice(AddressGenerator.STREET_NAMES)
|
||||
street_type = secrets.choice(AddressGenerator.STREET_TYPES)
|
||||
address_line1 = f"{street_number} {street_name} {street_type}"
|
||||
|
||||
location = secrets.choice(AddressGenerator.CITIES)
|
||||
zip_code = location["zip_prefix"] + f"{secrets.randbelow(100):02d}"
|
||||
|
||||
return {
|
||||
"address_line1": address_line1,
|
||||
"city": location["city"],
|
||||
"state": location["state"],
|
||||
"postal_code": zip_code,
|
||||
"country": "US"
|
||||
}
|
||||
|
||||
class WorkspaceNameGenerator:
|
||||
"""工作空间名称生成器"""
|
||||
|
||||
PREFIXES = [
|
||||
"Tech", "Digital", "Cloud", "Data", "Smart",
|
||||
"Innovate", "Future", "Global", "Rapid", "Prime",
|
||||
"Meta", "Quantum", "Synergy", "Vertex", "Apex",
|
||||
"Nexus", "Elite", "Pioneer", "Summit", "Venture"
|
||||
]
|
||||
|
||||
SUFFIXES = [
|
||||
"Labs", "Works", "Solutions", "Systems", "Dynamics",
|
||||
"Tech", "AI", "Hub", "Studio", "Group",
|
||||
"Partners", "Innovations", "Ventures", "Digital", "Cloud"
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def generate() -> str:
|
||||
"""生成工作空间名称"""
|
||||
prefix = secrets.choice(WorkspaceNameGenerator.PREFIXES)
|
||||
suffix = secrets.choice(WorkspaceNameGenerator.SUFFIXES)
|
||||
return f"{prefix}{suffix}"
|
||||
|
||||
# 便捷函数
|
||||
def generate_payment_info() -> Dict[str, str]:
|
||||
"""生成完整的支付信息(姓名 + 地址)"""
|
||||
name = NameGenerator.generate_full_name()
|
||||
address = AddressGenerator.generate_address()
|
||||
|
||||
return {
|
||||
"name": name,
|
||||
**address
|
||||
}
|
||||
42
modules/delay_utils.py
Normal file
42
modules/delay_utils.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
随机延迟工具
|
||||
模拟真实用户行为的延迟模式
|
||||
"""
|
||||
import time
|
||||
import secrets
|
||||
from config import DEBUG
|
||||
|
||||
class HumanDelay:
|
||||
"""模拟人类行为的延迟"""
|
||||
|
||||
@staticmethod
|
||||
def short_action(min: float = 0.5, max: float = 2.0):
|
||||
"""短操作延迟(例如:点击按钮)"""
|
||||
delay = min + (max - min) * (secrets.randbelow(1000) / 1000.0)
|
||||
if DEBUG:
|
||||
print(f"⏱️ [Delay] Short action: {delay:.2f}s")
|
||||
time.sleep(delay)
|
||||
|
||||
@staticmethod
|
||||
def medium_action(min: float = 2.0, max: float = 5.0):
|
||||
"""中等操作延迟(例如:阅读内容)"""
|
||||
delay = min + (max - min) * (secrets.randbelow(1000) / 1000.0)
|
||||
if DEBUG:
|
||||
print(f"⏱️ [Delay] Medium action: {delay:.2f}s")
|
||||
time.sleep(delay)
|
||||
|
||||
@staticmethod
|
||||
def long_action(min: float = 5.0, max: float = 10.0):
|
||||
"""长操作延迟(例如:页面加载)"""
|
||||
delay = min + (max - min) * (secrets.randbelow(1000) / 1000.0)
|
||||
if DEBUG:
|
||||
print(f"⏱️ [Delay] Long action: {delay:.2f}s")
|
||||
time.sleep(delay)
|
||||
|
||||
@staticmethod
|
||||
def custom(min: float, max: float):
|
||||
"""自定义范围延迟"""
|
||||
delay = min + (max - min) * (secrets.randbelow(1000) / 1000.0)
|
||||
if DEBUG:
|
||||
print(f"⏱️ [Delay] Custom: {delay:.2f}s")
|
||||
time.sleep(delay)
|
||||
@@ -15,6 +15,18 @@ class BrowserFingerprint:
|
||||
|
||||
def __init__(self, session_id: str = None):
|
||||
self.session_id = session_id or str(uuid.uuid4())
|
||||
|
||||
# 新增: 使用确定性方法从 session_id 派生 Stripe 指纹
|
||||
import hashlib
|
||||
seed = hashlib.sha256(self.session_id.encode()).hexdigest()
|
||||
# seed 是64个hex字符,我们需要确保切片正确
|
||||
|
||||
# 从 seed 生成一致的 guid/muid/sid
|
||||
# UUID需要32个hex字符(去掉连字符),额外部分直接拼接
|
||||
self.stripe_guid = seed[:8] + '-' + seed[8:12] + '-' + seed[12:16] + '-' + seed[16:20] + '-' + seed[20:32] + seed[32:40]
|
||||
self.stripe_muid = seed[:8] + '-' + seed[8:12] + '-' + seed[12:16] + '-' + seed[16:20] + '-' + seed[20:32] + seed[40:46]
|
||||
self.stripe_sid = seed[:8] + '-' + seed[8:12] + '-' + seed[12:16] + '-' + seed[16:20] + '-' + seed[20:32] + seed[46:52]
|
||||
|
||||
self.user_agent = FINGERPRINT_CONFIG['user_agent']
|
||||
self.screen_width = FINGERPRINT_CONFIG['screen_width']
|
||||
self.screen_height = FINGERPRINT_CONFIG['screen_height']
|
||||
@@ -93,7 +105,15 @@ class BrowserFingerprint:
|
||||
return {
|
||||
'oai-did': self.session_id,
|
||||
}
|
||||
|
||||
|
||||
def get_stripe_fingerprint(self) -> Dict[str, str]:
|
||||
"""获取 Stripe 支付指纹(与 session_id 一致派生)"""
|
||||
return {
|
||||
'guid': self.stripe_guid,
|
||||
'muid': self.stripe_muid,
|
||||
'sid': self.stripe_sid,
|
||||
}
|
||||
|
||||
def get_headers(self, with_sentinel: str = None, host: str = 'auth.openai.com') -> Dict[str, str]:
|
||||
"""生成 HTTP headers(支持多域名)"""
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import random
|
||||
import secrets
|
||||
import string
|
||||
|
||||
class IbanUtils:
|
||||
@@ -35,13 +35,26 @@ class IbanUtils:
|
||||
class GermanBankData:
|
||||
"""
|
||||
从 JS 源码模块 5009 中提取的德国 BLZ (Bankleitzahl) 列表。
|
||||
这是生成有效格式的关键。
|
||||
这是生成有效格式的关键。扩充版:50个真实银行代码
|
||||
"""
|
||||
BLZ_LIST = [
|
||||
"10020200", "20120200", "25020200", "30020500",
|
||||
"50020200", "50021000", "50021100", "50021120",
|
||||
"51020000", "55020000", "60120200", "70220200",
|
||||
"74020100", "74020150"
|
||||
# 原有的14个
|
||||
"10020200", "20120200", "25020200", "30020500",
|
||||
"50020200", "50021000", "50021100", "50021120",
|
||||
"51020000", "55020000", "60120200", "70220200",
|
||||
"74020100", "74020150",
|
||||
|
||||
# 新增36个真实的德国银行代码
|
||||
"10050000", "10070024", "10080000", "10090000",
|
||||
"20050000", "20070000", "20080000", "20090000",
|
||||
"25050000", "25070024", "25080020", "25090700",
|
||||
"30050000", "30060010", "30070024", "30080000",
|
||||
"37060193", "38070024", "40050000", "40060000",
|
||||
"40070024", "41070024", "43060967", "44050000",
|
||||
"50050000", "50060400", "50070024", "50080000",
|
||||
"60050000", "60060000", "60070070", "60080000",
|
||||
"70050000", "70070024", "70080000", "70090100",
|
||||
"76050101", "79050000", "80053782", "86055592"
|
||||
]
|
||||
|
||||
class GermanIbanGenerator:
|
||||
@@ -54,8 +67,8 @@ class GermanIbanGenerator:
|
||||
self.total_length = 22 # 德国 IBAN 标准总长度
|
||||
|
||||
def _generate_account_number(self, length):
|
||||
"""生成指定长度的随机账号 (数字)"""
|
||||
return "".join(random.choices(string.digits, k=length))
|
||||
"""生成指定长度的随机账号(使用 secrets 模块)"""
|
||||
return "".join(secrets.choice(string.digits) for _ in range(length))
|
||||
|
||||
def generate(self, quantity=1):
|
||||
"""
|
||||
@@ -63,8 +76,8 @@ class GermanIbanGenerator:
|
||||
"""
|
||||
results = []
|
||||
for _ in range(quantity):
|
||||
# 1. 随机选择一个真实的银行代码 (BLZ) - 8位
|
||||
blz = random.choice(GermanBankData.BLZ_LIST)
|
||||
# 1. 使用 secrets.choice 随机选择银行代码 (BLZ) - 8位
|
||||
blz = secrets.choice(GermanBankData.BLZ_LIST)
|
||||
|
||||
# 2. 计算剩余需要的账号长度
|
||||
# 德国结构: DE(2) + Check(2) + BLZ(8) + Account(10) = 22
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
import json
|
||||
from typing import Dict, Optional
|
||||
import secrets
|
||||
import random
|
||||
import uuid
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
@@ -696,9 +697,8 @@ class OpenAIRegistrar:
|
||||
url = "https://auth.openai.com/api/accounts/create_account"
|
||||
|
||||
# 生成随机姓名和生日
|
||||
import random
|
||||
import string
|
||||
random_name = ''.join(random.choices(string.ascii_lowercase, k=8))
|
||||
from modules.data_generator import NameGenerator
|
||||
random_name = NameGenerator.generate_full_name()
|
||||
|
||||
# 生成随机生日(1980-2000年之间)
|
||||
year = random.randint(1980, 2000)
|
||||
|
||||
@@ -10,6 +10,8 @@ from typing import Dict, Optional
|
||||
import logging
|
||||
from .http_client import HTTPClient
|
||||
from .fingerprint import BrowserFingerprint
|
||||
from .delay_utils import HumanDelay
|
||||
from config import DEBUG, DELAY_CONFIG
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -35,14 +37,31 @@ class StripePaymentHandler:
|
||||
self.stripe_api_base = "https://api.stripe.com"
|
||||
self.stripe_version = "2020-08-27;custom_checkout_beta=v1"
|
||||
|
||||
# 会话指纹(每次运行生成新的)
|
||||
self.guid = str(uuid.uuid4()) + str(uuid.uuid4())[:8]
|
||||
self.muid = str(uuid.uuid4()) + str(uuid.uuid4())[:6]
|
||||
self.sid = str(uuid.uuid4()) + str(uuid.uuid4())[:6]
|
||||
# HTTP客户端(必须复用以保持指纹一致)
|
||||
if http_client:
|
||||
self.http_client = http_client
|
||||
# 从 http_client 的 fingerprint 获取一致的指纹
|
||||
stripe_fp = self.http_client.fingerprint.get_stripe_fingerprint()
|
||||
self.guid = stripe_fp['guid']
|
||||
self.muid = stripe_fp['muid']
|
||||
self.sid = stripe_fp['sid']
|
||||
else:
|
||||
# 如果没有提供 http_client,创建新的(警告:这会导致指纹不一致)
|
||||
from .fingerprint import BrowserFingerprint
|
||||
from .http_client import HTTPClient
|
||||
fingerprint = BrowserFingerprint()
|
||||
self.http_client = HTTPClient(fingerprint)
|
||||
stripe_fp = fingerprint.get_stripe_fingerprint()
|
||||
self.guid = stripe_fp['guid']
|
||||
self.muid = stripe_fp['muid']
|
||||
self.sid = stripe_fp['sid']
|
||||
if DEBUG:
|
||||
print("⚠️ [Payment] 创建了新的 HTTP 客户端,指纹可能不一致")
|
||||
|
||||
# 归因元数据
|
||||
self.client_session_id = str(uuid.uuid4())
|
||||
self.checkout_config_id = "9e2d84a8-5eec-41bf-aae8-24d59824ec84"
|
||||
# 随机生成 checkout_config_id(不再使用固定值)
|
||||
self.checkout_config_id = str(uuid.uuid4())
|
||||
|
||||
# HTTP客户端(使用curl_cffi)
|
||||
if http_client:
|
||||
@@ -264,13 +283,12 @@ class StripePaymentHandler:
|
||||
logger.error(f"❌ Exception confirming payment: {e}")
|
||||
return False
|
||||
|
||||
def poll_payment_status(self, max_attempts: int = 20, interval: int = 3) -> Dict:
|
||||
def poll_payment_status(self, max_attempts: int = 20) -> Dict:
|
||||
"""
|
||||
轮询支付状态直到完成
|
||||
轮询支付状态直到完成(使用随机延迟)
|
||||
|
||||
Args:
|
||||
max_attempts: 最大轮询次数
|
||||
interval: 轮询间隔(秒)
|
||||
|
||||
Returns:
|
||||
最终状态字典
|
||||
@@ -315,15 +333,15 @@ class StripePaymentHandler:
|
||||
logger.error(f"❌ Payment {state}")
|
||||
return result
|
||||
|
||||
# 继续轮询
|
||||
time.sleep(interval)
|
||||
# 继续轮询 - 使用随机延迟
|
||||
HumanDelay.custom(**DELAY_CONFIG['polling_interval'])
|
||||
else:
|
||||
logger.warning(f"Poll returned status {response.status_code}")
|
||||
time.sleep(interval)
|
||||
HumanDelay.custom(**DELAY_CONFIG['polling_interval'])
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Exception polling status: {e}")
|
||||
time.sleep(interval)
|
||||
HumanDelay.custom(**DELAY_CONFIG['polling_interval'])
|
||||
|
||||
logger.warning("⚠️ Max polling attempts reached")
|
||||
return {"state": "timeout"}
|
||||
|
||||
Reference in New Issue
Block a user