From 703e41b8904baa42f6e29a47a676911098a89d91 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Mon, 17 Nov 2025 13:13:38 +0800 Subject: [PATCH] dasdasd --- aaa.js | 48 ++ src/tools/account-register/sites/windsurf.js | 621 +++++++++++++------ 2 files changed, 477 insertions(+), 192 deletions(-) create mode 100644 aaa.js diff --git a/aaa.js b/aaa.js new file mode 100644 index 0000000..c251ee2 --- /dev/null +++ b/aaa.js @@ -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格式一致性: ✓'); diff --git a/src/tools/account-register/sites/windsurf.js b/src/tools/account-register/sites/windsurf.js index 5eb2cf2..4a733db 100644 --- a/src/tools/account-register/sites/windsurf.js +++ b/src/tools/account-register/sites/windsurf.js @@ -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); - - // 填写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); - - // 填写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'); + while (!stepCompleted && (Date.now() - overallStartTime < overallMaxWait)) { + try { + if (retryCount > 0) { + logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试...`); + } + + // 打开注册页面 + logger.info(this.siteName, `打开注册页面: ${this.siteUrl}`); + await this.page.goto(this.siteUrl, { + waitUntil: 'networkidle2', + timeout: 30000 }); - }, - waitButtonTimeout: 30000, - waitContentTimeout: 15000, - actionName: '点击Continue进入密码设置页面' - }); + + // 模拟阅读页面(1-3秒) + await this.human.readPage(1, 3); + + // 填写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); + + // 填写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; + } + } - 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); - - logger.info(this.siteName, ' → 持续查找"Skip this step"按钮...'); - - const startTime = Date.now(); - const maxWait = 60000; // 最多等待60秒 - let elapsed = 0; - let skipButton = null; - let buttonFound = false; - - // 轮询查找按钮 - while (elapsed < maxWait && !buttonFound) { - try { - // 查找所有按钮 - const buttons = await this.page.$$('button'); - - 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 overallStartTime = Date.now(); + const overallMaxWait = 120000; // 总共最多重试2分钟 + let stepCompleted = false; + let retryCount = 0; + + while (!stepCompleted && (Date.now() - overallStartTime < overallMaxWait)) { + try { + if (retryCount > 0) { + logger.info(this.siteName, ` → 第 ${retryCount + 1} 次尝试...`); + } + + // 等待页面加载 + await this.human.readPage(2, 3); + + logger.info(this.siteName, ' → 查找"Skip this step"按钮...'); + + 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); - - // 查找并点击"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 支付页面'); + const overallStartTime = Date.now(); + const overallMaxWait = 120000; // 总共最多重试2分钟 + let stepCompleted = false; + let retryCount = 0; + + 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)); } } + + 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); - this.currentStep = 5; + continue; } - - } catch (error) { - logger.error(this.siteName, `选择计划失败: ${error.message}`); - throw error; } + + 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} 次)`); } /**