This commit is contained in:
dengqichen 2025-11-17 13:13:38 +08:00
parent bfd6c9ef3d
commit 703e41b890
2 changed files with 477 additions and 192 deletions

48
aaa.js Normal file
View File

@ -0,0 +1,48 @@
/**
* 验证生成的卡号是否符合规律
*/
const CardGenerator = require('./src/tools/card-generator/generator');
const Formatter = require('./src/tools/card-generator/formatter');
const { luhnCheck } = require('./src/shared/utils');
const generator = new CardGenerator();
const formatter = new Formatter();
console.log('=== 生成卡号验证 ===\n');
// 生成10张银联卡
console.log('生成10张银联卡进行验证:\n');
const cards = generator.generateBatch(10, 'unionpay');
cards.forEach((card, index) => {
const isValid = luhnCheck(card.number);
const status = isValid ? '✓' : '✗';
const formatted = formatter.format(card, 'pipe');
console.log(`${index + 1}. ${formatted} ${status}`);
});
console.log('\n=== 验证结果 ===');
const validCount = cards.filter(card => luhnCheck(card.number)).length;
console.log(`Luhn校验: ${validCount}/${cards.length} 通过`);
if (validCount === cards.length) {
console.log('✓ 所有卡号都通过Luhn校验符合规律');
console.log('\n=== 提供测试卡号 ===');
const testCard = cards[0];
console.log('\n请使用以下卡号进行测试');
console.log(`\n${formatter.format(testCard, 'pipe')}\n`);
console.log('详细信息:');
console.log(formatter.format(testCard, 'pretty'));
} else {
console.log('✗ 部分卡号未通过校验,需要修复');
}
// 对比格式
console.log('\n=== 格式对比 ===');
console.log('原始样本格式:');
console.log(' 6228367546781457|11|27|792');
console.log('\n生成的卡号格式:');
console.log(` ${formatter.format(cards[0], 'pipe')}`);
console.log('\n格式一致性: ✓');

View File

@ -214,19 +214,61 @@ class WindsurfRegister {
// 如果配置了 CapSolver加载扩展
if (this.capsolverKey) {
const fs = require('fs');
const os = require('os');
// 使用绝对路径:从当前文件向上找到项目根目录
const projectRoot = path.resolve(__dirname, '../../../../');
const extensionPath = path.join(projectRoot, 'extensions/capsolver');
const extensionPath = path.join(projectRoot, 'extensions', 'capsolver');
if (fs.existsSync(extensionPath)) {
// 检查关键文件是否存在
const manifestPath = path.join(extensionPath, 'manifest.json');
const configPath = path.join(extensionPath, 'assets', 'config.js');
if (!fs.existsSync(manifestPath)) {
logger.error(this.siteName, '✗ manifest.json 不存在!扩展不完整');
throw new Error('扩展文件不完整');
}
if (!fs.existsSync(configPath)) {
logger.error(this.siteName, '✗ assets/config.js 不存在!扩展不完整');
throw new Error('扩展配置文件不存在');
}
logger.success(this.siteName, '✓ 扩展文件完整性检查通过');
// 关键修复Windows上路径需要转换为正斜杠
let normalizedPath = path.resolve(extensionPath);
if (process.platform === 'win32') {
// Windows: 将反斜杠转换为正斜杠Chrome要求
normalizedPath = normalizedPath.replace(/\\/g, '/');
logger.info(this.siteName, `Windows平台路径转换为正斜杠格式`);
}
logger.info(this.siteName, `扩展路径: ${normalizedPath}`);
// Windows特定使用临时用户数据目录
if (process.platform === 'win32') {
const tempUserDataDir = path.join(os.tmpdir(), 'chrome-capsolver-' + Date.now());
launchOptions.userDataDir = tempUserDataDir;
logger.info(this.siteName, `临时用户数据目录: ${tempUserDataDir}`);
// Windows必需参数
launchOptions.args.push(
'--disable-features=RendererCodeIntegrity',
'--disable-blink-features=AutomationControlled'
);
}
launchOptions.args.push(
`--disable-extensions-except=${extensionPath}`,
`--load-extension=${extensionPath}`
`--disable-extensions-except=${normalizedPath}`,
`--load-extension=${normalizedPath}`
);
logger.info(this.siteName, '✓ CapSolver 扩展已加载');
logger.info(this.siteName, '✓ CapSolver 扩展配置完成');
} else {
logger.warn(this.siteName, `⚠️ CapSolver 扩展未找到: ${extensionPath}`);
logger.warn(this.siteName, '请下载扩展: https://chromewebstore.google.com/detail/captcha-solver-auto-captc/pgojnojmmhpofjgdmaebadhbocahppod');
logger.warn(this.siteName, '请检查扩展目录是否存在');
}
}
@ -250,6 +292,12 @@ class WindsurfRegister {
logger.info(this.siteName, '等待浏览器完全准备...');
await this.human.randomDelay(2000, 3000);
// 默认启用自动模式如果配置了CapSolver
if (this.capsolverKey) {
this.capsolverWorking = true;
logger.info(this.siteName, '✓ CapSolver自动模式已启用如失败会降级到手动模式');
}
}
/**
@ -263,70 +311,100 @@ class WindsurfRegister {
}
/**
* 步骤1: 填写基本信息First Name, Last Name, Email- 使用人类行为
* 步骤1: 填写基本信息
*/
async step1_fillBasicInfo() {
logger.info(this.siteName, `[步骤 1/${this.getTotalSteps()}] 填写基本信息`);
// 打开注册页面
logger.info(this.siteName, `打开注册页面: ${this.siteUrl}`);
await this.page.goto(this.siteUrl, {
waitUntil: 'networkidle2',
timeout: 30000
});
const overallStartTime = Date.now();
const overallMaxWait = 180000; // 总共最多重试3分钟
let stepCompleted = false;
let retryCount = 0;
// 模拟阅读页面1-3秒
await this.human.readPage(1, 3);
while (!stepCompleted && (Date.now() - overallStartTime < overallMaxWait)) {
try {
if (retryCount > 0) {
logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试...`);
}
// 填写First Name使用人类行为
logger.info(this.siteName, ' → 填写First Name...');
await this.page.waitForSelector('#firstName', { timeout: 10000 });
await this.human.humanType(this.page, '#firstName', this.accountData.firstName);
// 打开注册页面
logger.info(this.siteName, `打开注册页面: ${this.siteUrl}`);
await this.page.goto(this.siteUrl, {
waitUntil: 'networkidle2',
timeout: 30000
});
// 填写Last Name
logger.info(this.siteName, ' → 填写Last Name...');
await this.page.waitForSelector('#lastName', { timeout: 10000 });
await this.human.humanType(this.page, '#lastName', this.accountData.lastName);
// 模拟阅读页面1-3秒
await this.human.readPage(1, 3);
// 填写Email
logger.info(this.siteName, ' → 填写Email...');
await this.page.waitForSelector('#email', { timeout: 10000 });
await this.human.humanType(this.page, '#email', this.accountData.email);
// 填写First Name使用人类行为
logger.info(this.siteName, ' → 填写First Name...');
await this.page.waitForSelector('#firstName', { timeout: 10000 });
await this.human.humanType(this.page, '#firstName', this.accountData.firstName);
// 勾选同意条款(如果有)
try {
const checkbox = await this.page.$('input[type="checkbox"]');
if (checkbox) {
logger.info(this.siteName, ' → 勾选同意条款...');
await this.human.humanCheckbox(this.page, 'input[type="checkbox"]');
// 填写Last Name
logger.info(this.siteName, ' → 填写Last Name...');
await this.page.waitForSelector('#lastName', { timeout: 10000 });
await this.human.humanType(this.page, '#lastName', this.accountData.lastName);
// 填写Email
logger.info(this.siteName, ' → 填写Email...');
await this.page.waitForSelector('#email', { timeout: 10000 });
await this.human.humanType(this.page, '#email', this.accountData.email);
// 勾选同意条款(如果有)
try {
const checkbox = await this.page.$('input[type="checkbox"]');
if (checkbox) {
logger.info(this.siteName, ' → 勾选同意条款...');
await this.human.humanCheckbox(this.page, 'input[type="checkbox"]');
}
} catch (error) {
logger.warn(this.siteName, ' → 未找到同意条款checkbox跳过');
}
// 点击Continue按钮并等待跳转到密码页面
const success = await this.clickButtonAndWaitForPageChange({
buttonText: 'Continue',
checkContentChanged: async () => {
// 检查是否有密码输入框(表示已进入下一步)
return await this.page.evaluate(() => {
return !!document.querySelector('#password');
});
},
waitButtonTimeout: 30000,
waitContentTimeout: 15000,
actionName: '点击Continue进入密码设置页面'
});
if (!success) {
logger.warn(this.siteName, ' → ⚠️ 未能进入密码页面,将重新尝试');
retryCount++;
await this.human.randomDelay(2000, 3000);
continue;
}
// 成功完成
stepCompleted = true;
} catch (error) {
logger.warn(this.siteName, ` → 执行出错: ${error.message},将重新尝试`);
retryCount++;
await this.human.randomDelay(3000, 5000);
continue;
}
} catch (error) {
logger.warn(this.siteName, ' → 未找到同意条款checkbox跳过');
}
// 点击Continue按钮并等待跳转到密码页面
const success = await this.clickButtonAndWaitForPageChange({
buttonText: 'Continue',
checkContentChanged: async () => {
// 检查是否有密码输入框(表示已进入下一步)
return await this.page.evaluate(() => {
return !!document.querySelector('#password');
});
},
waitButtonTimeout: 30000,
waitContentTimeout: 15000,
actionName: '点击Continue进入密码设置页面'
});
if (!success) {
throw new Error('步骤1未能成功进入密码设置页面');
if (!stepCompleted) {
logger.error(this.siteName, ` → ✗ 步骤1失败${retryCount + 1}次尝试后仍未成功`);
throw new Error(`步骤1${retryCount + 1}次尝试后仍未能完成基本信息填写`);
}
// 额外等待确保页面稳定
await this.human.randomDelay(1000, 2000);
this.currentStep = 1;
logger.success(this.siteName, `步骤 1 完成`);
logger.success(this.siteName, `步骤 1 完成 (共尝试 ${retryCount + 1} 次)`);
}
/**
@ -400,6 +478,31 @@ class WindsurfRegister {
await this.human.randomDelay(2000, 3000);
// ===== Cloudflare Turnstile 验证处理 =====
// 首先检查Turnstile API是否加载成功
logger.info(this.siteName, ' → 检查 Cloudflare Turnstile API 加载状态...');
const turnstileApiStatus = await this.page.evaluate(() => {
return {
hasTurnstile: typeof window.turnstile !== 'undefined',
hasCallback: typeof window.cf__reactTurnstileOnLoad !== 'undefined',
scripts: Array.from(document.querySelectorAll('script')).map(s => s.src).filter(src => src.includes('turnstile') || src.includes('cloudflare'))
};
});
if (turnstileApiStatus.hasTurnstile) {
logger.success(this.siteName, ' → ✓ Turnstile API 已成功加载');
} else {
logger.error(this.siteName, ' → ⚠️ Turnstile API 未加载!');
logger.error(this.siteName, ' → 检测到的脚本: ' + JSON.stringify(turnstileApiStatus.scripts));
logger.warn(this.siteName, ' → 可能原因:');
logger.warn(this.siteName, ' 1. 网络问题Cloudflare CDN 在中国大陆可能不稳定)');
logger.warn(this.siteName, ' 2. 浏览器扩展阻止了脚本加载');
logger.warn(this.siteName, ' 3. 防火墙/代理问题');
logger.warn(this.siteName, ' → 建议检查网络连接或使用VPN/代理');
logger.info(this.siteName, ' → 等待30秒看是否能延迟加载...');
await this.human.randomDelay(30000, 30000);
}
// 如果配置了 CapSolver 扩展,等待扩展自动处理验证
if (this.capsolverKey) {
logger.info(this.siteName, ' → 检测到 Cloudflare Turnstile 验证');
@ -949,26 +1052,46 @@ class WindsurfRegister {
}
logger.success(this.siteName, ' → 验证码已填写完成');
// 点击"Create account"按钮并等待页面内容变化
const success = await this.clickButtonAndWaitForPageChange({
buttonText: 'Create account',
checkContentChanged: async () => {
// 检查验证码输入框是否已消失(表示已进入下一步)
return await this.page.evaluate(() => {
return !document.querySelector('input[type="text"]');
});
},
waitButtonTimeout: 30000,
waitContentTimeout: 15000,
actionName: '点击Create account进入下一步'
});
// 输入完6位验证码后页面会自动提交
logger.info(this.siteName, ' → 等待邮箱验证完成并跳转到问卷页面...');
if (!success) {
throw new Error('步骤3未能成功创建账号');
// 等待页面跳转到 /account/onboarding?page=source
const startTime = Date.now();
const maxWait = 20000; // 最多等待20秒
let registrationComplete = false;
while (Date.now() - startTime < maxWait) {
const currentUrl = this.page.url();
// 精确检查:必须是 /account/onboarding?page=source
if (currentUrl.includes('/account/onboarding') && currentUrl.includes('page=source')) {
registrationComplete = true;
logger.success(this.siteName, ` → ✓ 邮箱验证成功!已跳转到问卷页面`);
logger.info(this.siteName, ` → 当前页面: ${currentUrl}`);
break;
}
// 每2秒输出一次进度
const elapsed = Date.now() - startTime;
if (elapsed > 0 && elapsed % 2000 === 0) {
logger.info(this.siteName, ` → 等待中... 已用时 ${elapsed/1000}`);
}
await new Promise(resolve => setTimeout(resolve, 500));
}
if (!registrationComplete) {
const finalUrl = this.page.url();
logger.error(this.siteName, ` → ✗ 邮箱验证失败或页面未跳转`);
logger.error(this.siteName, ` → 当前页面: ${finalUrl}`);
throw new Error('步骤3邮箱验证未成功跳转到问卷页面');
}
// 额外等待页面稳定
await this.human.randomDelay(2000, 3000);
this.currentStep = 3;
logger.success(this.siteName, `步骤 3 完成`);
logger.success(this.siteName, `步骤 3 完成 - 账号创建成功`);
} else {
logger.error(this.siteName, ' → 未找到验证码输入框!');
@ -992,74 +1115,129 @@ class WindsurfRegister {
async step4_skipSurvey() {
logger.info(this.siteName, `[步骤 4/${this.getTotalSteps()}] 跳过问卷`);
try {
// 初始等待页面加载
await this.human.readPage(2, 3);
const overallStartTime = Date.now();
const overallMaxWait = 120000; // 总共最多重试2分钟
let stepCompleted = false;
let retryCount = 0;
logger.info(this.siteName, ' → 持续查找"Skip this step"按钮...');
while (!stepCompleted && (Date.now() - overallStartTime < overallMaxWait)) {
try {
if (retryCount > 0) {
logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试...`);
}
const startTime = Date.now();
const maxWait = 60000; // 最多等待60秒
let elapsed = 0;
let skipButton = null;
let buttonFound = false;
// 等待页面加载
await this.human.readPage(2, 3);
// 轮询查找按钮
while (elapsed < maxWait && !buttonFound) {
try {
// 查找所有按钮
const buttons = await this.page.$$('button');
logger.info(this.siteName, ' → 查找"Skip this step"按钮...');
for (const button of buttons) {
const text = await this.page.evaluate(el => el.textContent?.trim(), button);
if (text && text.toLowerCase().includes('skip')) {
skipButton = button;
buttonFound = true;
logger.success(this.siteName, ` → ✓ 找到按钮: "${text}" (耗时: ${((Date.now() - startTime) / 1000).toFixed(1)}秒)`);
const startTime = Date.now();
const maxWait = 30000; // 单次查找最多等待30秒
let skipButton = null;
let buttonFound = false;
// 轮询查找按钮
while (!buttonFound && (Date.now() - startTime < maxWait)) {
try {
// 查找所有按钮或链接
const buttons = await this.page.$$('button, a');
for (const button of buttons) {
const text = await this.page.evaluate(el => el.textContent?.trim(), button);
// 匹配 "Skip this step" 或 "skip"
if (text && (text.toLowerCase().includes('skip this step') || text.toLowerCase() === 'skip')) {
skipButton = button;
buttonFound = true;
logger.success(this.siteName, ` → ✓ 找到按钮: "${text}" (耗时: ${((Date.now() - startTime) / 1000).toFixed(1)}秒)`);
break;
}
}
if (buttonFound) {
break;
}
// 每5秒输出一次进度
const elapsed = Date.now() - startTime;
if (elapsed > 0 && elapsed % 5000 === 0) {
logger.info(this.siteName, ` → 等待按钮出现... 已用时 ${elapsed/1000}`);
}
// 等待2秒后继续查找
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
logger.warn(this.siteName, ` → 查找按钮时出错: ${error.message},继续尝试...`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
if (buttonFound) {
break;
}
// 每5秒输出一次进度
if (elapsed > 0 && elapsed % 5000 === 0) {
logger.info(this.siteName, ` → 等待按钮出现... 已用时 ${elapsed/1000}`);
}
// 等待2秒后继续查找
await new Promise(resolve => setTimeout(resolve, 2000));
elapsed = Date.now() - startTime;
} catch (error) {
logger.warn(this.siteName, ` → 查找按钮时出错: ${error.message},继续尝试...`);
await new Promise(resolve => setTimeout(resolve, 2000));
elapsed = Date.now() - startTime;
}
}
if (skipButton && buttonFound) {
logger.info(this.siteName, ' → 点击"Skip this step"按钮...');
await skipButton.click();
if (skipButton && buttonFound) {
logger.info(this.siteName, ' → 点击"Skip this step"按钮...');
// 等待页面跳转
// 直接点击找到的按钮元素
await skipButton.click();
logger.success(this.siteName, ' → ✓ 已点击按钮');
// 等待页面跳转到 /account/upgrade-prompt
logger.info(this.siteName, ' → 等待跳转到 /account/upgrade-prompt 页面...');
const jumpStartTime = Date.now();
const jumpMaxWait = 15000; // 最多等待15秒
let jumpSuccess = false;
while (Date.now() - jumpStartTime < jumpMaxWait) {
const newUrl = this.page.url();
// 必须跳转到 upgrade-prompt 页面
if (newUrl.includes('/account/upgrade-prompt')) {
jumpSuccess = true;
stepCompleted = true;
logger.success(this.siteName, ` → ✓ 成功跳转到计划选择页面`);
logger.info(this.siteName, ` → 当前页面: ${newUrl}`);
break;
}
await new Promise(resolve => setTimeout(resolve, 500));
}
if (!jumpSuccess) {
const finalUrl = this.page.url();
logger.warn(this.siteName, ` → ⚠️ 未跳转到升级页面,将重新尝试`);
logger.warn(this.siteName, ` → 当前页面: ${finalUrl}`);
retryCount++;
await this.human.randomDelay(2000, 3000);
continue; // 重新开始循环
}
} else {
// 未找到按钮,重试
logger.warn(this.siteName, ` → ⚠️ 未找到Skip按钮将重新尝试`);
retryCount++;
await this.human.randomDelay(2000, 3000);
continue;
}
} catch (error) {
logger.warn(this.siteName, ` → 执行出错: ${error.message},将重新尝试`);
retryCount++;
await this.human.randomDelay(2000, 3000);
this.currentStep = 4;
logger.success(this.siteName, `步骤 4 完成`);
} else if (elapsed >= maxWait) {
logger.warn(this.siteName, ` → ⚠️ 等待${maxWait/1000}秒后仍未找到Skip按钮`);
logger.warn(this.siteName, ' → 可能原因:页面已自动跳过或页面结构已变化');
logger.info(this.siteName, ' → 尝试继续执行下一步...');
this.currentStep = 4;
continue;
}
} catch (error) {
logger.error(this.siteName, `跳过问卷失败: ${error.message}`);
throw error;
}
if (!stepCompleted) {
const currentUrl = this.page.url();
logger.error(this.siteName, ` → ✗ 步骤4失败${retryCount + 1}次尝试后仍未成功`);
logger.error(this.siteName, ` → 当前页面: ${currentUrl}`);
throw new Error(`步骤4${retryCount + 1}次尝试后仍未成功跳转到 /account/upgrade-prompt 页面`);
}
// 额外等待页面稳定
await this.human.randomDelay(2000, 3000);
this.currentStep = 4;
logger.success(this.siteName, `步骤 4 完成 (共尝试 ${retryCount + 1} 次)`);
}
/**
@ -1068,73 +1246,132 @@ class WindsurfRegister {
async step5_selectPlan() {
logger.info(this.siteName, `[步骤 5/${this.getTotalSteps()}] 选择计划`);
try {
// 等待页面加载
await this.human.readPage(2, 3);
const overallStartTime = Date.now();
const overallMaxWait = 120000; // 总共最多重试2分钟
let stepCompleted = false;
let retryCount = 0;
// 查找并点击"Select plan"按钮
logger.info(this.siteName, ' → 查找"Select plan"按钮...');
// 方式1: 通过按钮文本查找
const buttons = await this.page.$$('button');
let selectButton = null;
for (const button of buttons) {
const text = await this.page.evaluate(el => el.textContent?.trim(), button);
if (text && text.toLowerCase().includes('select plan')) {
selectButton = button;
logger.info(this.siteName, ` → 找到按钮: "${text}"`);
break;
}
}
if (selectButton) {
logger.info(this.siteName, ' → 点击"Select plan"按钮...');
// 点击后会跳转到 Stripe checkout 页面,需要等待导航完成
logger.info(this.siteName, ' → 等待跳转到支付页面...');
await Promise.all([
this.page.waitForNavigation({
waitUntil: 'networkidle2',
timeout: 30000
}).catch(e => {
logger.warn(this.siteName, ` → 导航等待异常: ${e.message}`);
}),
selectButton.click()
]);
// 额外等待页面稳定
await this.human.randomDelay(2000, 3000);
const currentUrl = this.page.url();
logger.info(this.siteName, ` → 当前URL: ${currentUrl}`);
if (currentUrl.includes('checkout.stripe.com')) {
logger.success(this.siteName, ' → ✓ 已跳转到 Stripe 支付页面');
while (!stepCompleted && (Date.now() - overallStartTime < overallMaxWait)) {
try {
if (retryCount > 0) {
logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试...`);
}
this.currentStep = 5;
logger.success(this.siteName, `步骤 5 完成`);
} else {
logger.warn(this.siteName, ' → 未找到Select plan按钮尝试点击Skip');
// 如果没有Select plan尝试点击Skip
const skipButtons = await this.page.$$('button');
for (const btn of skipButtons) {
const text = await this.page.evaluate(el => el.textContent?.trim(), btn);
if (text && text.toLowerCase().includes('skip')) {
await btn.click();
logger.info(this.siteName, ' → 已点击Skip按钮');
break;
// 等待页面加载
await this.human.readPage(2, 3);
logger.info(this.siteName, ' → 查找计划选择按钮...');
const startTime = Date.now();
const maxWait = 30000; // 单次查找最多等待30秒
let selectButton = null;
let buttonFound = false;
// 轮询查找按钮
while (!buttonFound && (Date.now() - startTime < maxWait)) {
try {
const buttons = await this.page.$$('button, a');
for (const button of buttons) {
const text = await this.page.evaluate(el => el.textContent?.trim(), button);
// 匹配多种可能的按钮文本
if (text && (
text.toLowerCase().includes('select plan') ||
text.toLowerCase().includes('continue') ||
text.toLowerCase().includes('get started') ||
text.toLowerCase() === 'select'
)) {
selectButton = button;
buttonFound = true;
logger.success(this.siteName, ` → ✓ 找到按钮: "${text}" (耗时: ${((Date.now() - startTime) / 1000).toFixed(1)}秒)`);
break;
}
}
if (buttonFound) {
break;
}
// 每5秒输出一次进度
const elapsed = Date.now() - startTime;
if (elapsed > 0 && elapsed % 5000 === 0) {
logger.info(this.siteName, ` → 等待按钮出现... 已用时 ${elapsed/1000}`);
}
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
logger.warn(this.siteName, ` → 查找按钮时出错: ${error.message},继续尝试...`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
await this.human.randomDelay(2000, 3000);
this.currentStep = 5;
}
} catch (error) {
logger.error(this.siteName, `选择计划失败: ${error.message}`);
throw error;
if (selectButton && buttonFound) {
logger.info(this.siteName, ' → 点击计划选择按钮...');
// 直接点击找到的按钮元素
await selectButton.click();
logger.success(this.siteName, ' → ✓ 已点击按钮');
// 等待页面跳转到 Stripe checkout
logger.info(this.siteName, ' → 等待跳转到 Stripe 支付页面...');
const jumpStartTime = Date.now();
const jumpMaxWait = 20000; // 最多等待20秒
let jumpSuccess = false;
while (Date.now() - jumpStartTime < jumpMaxWait) {
const newUrl = this.page.url();
// 必须跳转到 Stripe checkout
if (newUrl.includes('checkout.stripe.com') || newUrl.includes('stripe.com')) {
jumpSuccess = true;
stepCompleted = true;
logger.success(this.siteName, ` → ✓ 成功跳转到 Stripe 支付页面`);
logger.info(this.siteName, ` → 当前页面: ${newUrl}`);
break;
}
await new Promise(resolve => setTimeout(resolve, 500));
}
if (!jumpSuccess) {
const finalUrl = this.page.url();
logger.warn(this.siteName, ` → ⚠️ 未跳转到支付页面,将重新尝试`);
logger.warn(this.siteName, ` → 当前页面: ${finalUrl}`);
retryCount++;
await this.human.randomDelay(2000, 3000);
continue;
}
} else {
// 未找到按钮,重试
logger.warn(this.siteName, ` → ⚠️ 未找到计划选择按钮,将重新尝试`);
retryCount++;
await this.human.randomDelay(2000, 3000);
continue;
}
} catch (error) {
logger.warn(this.siteName, ` → 执行出错: ${error.message},将重新尝试`);
retryCount++;
await this.human.randomDelay(2000, 3000);
continue;
}
}
if (!stepCompleted) {
const currentUrl = this.page.url();
logger.error(this.siteName, ` → ✗ 步骤5失败${retryCount + 1}次尝试后仍未成功`);
logger.error(this.siteName, ` → 当前页面: ${currentUrl}`);
throw new Error(`步骤5${retryCount + 1}次尝试后仍未成功跳转到 Stripe 支付页面`);
}
// 额外等待页面稳定
await this.human.randomDelay(2000, 3000);
this.currentStep = 5;
logger.success(this.siteName, `步骤 5 完成 (共尝试 ${retryCount + 1} 次)`);
}
/**