This commit is contained in:
dengqichen 2025-11-17 20:26:17 +08:00
parent b0b81e9279
commit 468d4d6d73

View File

@ -1048,7 +1048,7 @@ class WindsurfRegister {
} }
/** /**
* 提交支付递归重试 * 提交支付递归重试轮询检测
*/ */
async submitPayment(retryCount = 0, maxRetries = 5) { async submitPayment(retryCount = 0, maxRetries = 5) {
if (retryCount >= maxRetries) { if (retryCount >= maxRetries) {
@ -1068,16 +1068,37 @@ class WindsurfRegister {
await submitButton.click(); await submitButton.click();
logger.success(this.siteName, ' → ✓ 已点击订阅按钮'); logger.success(this.siteName, ' → ✓ 已点击订阅按钮');
await this.human.randomDelay(3000, 5000);
// 处理验证码 // 等待一下让 Stripe 开始处理
await this.handleHCaptcha();
await this.human.randomDelay(2000, 3000); await this.human.randomDelay(2000, 3000);
// 检查卡是否被拒绝 // 检查是否有验证码(快速检查)
const hasHCaptcha = await this.page.evaluate(() => {
return !!(
document.querySelector('iframe[src*="hcaptcha.com"]') ||
document.querySelector('.h-captcha')
);
});
if (hasHCaptcha) {
logger.info(this.siteName, ' → 检测到验证码,处理中...');
await this.handleHCaptcha();
await this.human.randomDelay(1000, 2000);
}
// 开始轮询检测:同时等待成功或失败
logger.info(this.siteName, ' → 轮询检测支付结果...');
const startTime = Date.now();
const maxWait = 30000; // 最多等待30秒
let checkCount = 0;
while (Date.now() - startTime < maxWait) {
checkCount++;
// 1. 优先检查是否有错误(卡被拒绝)
const cardRejected = await this.checkCardRejected(); const cardRejected = await this.checkCardRejected();
if (cardRejected) { if (cardRejected) {
logger.warn(this.siteName, ` → ⚠️ 银行卡被拒绝!(第 ${retryCount + 1}/${maxRetries} 次)`); logger.warn(this.siteName, ` → ⚠️ 银行卡被拒绝!(第 ${retryCount + 1}/${maxRetries} 次,检测了 ${checkCount} 次)`);
// 生成新卡 // 生成新卡
logger.info(this.siteName, ' → 生成新的银行卡信息...'); logger.info(this.siteName, ' → 生成新的银行卡信息...');
@ -1102,11 +1123,52 @@ class WindsurfRegister {
return await this.submitPayment(retryCount + 1, maxRetries); return await this.submitPayment(retryCount + 1, maxRetries);
} }
// 等待支付成功 // 2. 检查是否支付成功(跳转离开 Stripe
await this.waitForPaymentSuccess(); const currentUrl = this.page.url();
if (!currentUrl.includes('stripe.com') && !currentUrl.includes('checkout.stripe.com')) {
const totalTime = ((Date.now() - startTime) / 1000).toFixed(1);
this.registrationTime = new Date();
logger.success(this.siteName, ` → ✓ 支付成功!(耗时: ${totalTime}秒,检测了 ${checkCount} 次)`);
logger.info(this.siteName, ` → 当前页面: ${currentUrl}`);
return true; return true;
} }
// 3. 每5秒输出一次进度
const elapsed = Date.now() - startTime;
if (elapsed > 0 && elapsed % 5000 === 0) {
logger.info(this.siteName, ` → 支付处理中... (${(elapsed/1000).toFixed(0)}秒,已检测 ${checkCount} 次)`);
}
// 4. 等待500ms后继续检测
await new Promise(resolve => setTimeout(resolve, 500));
}
// 超时:视为可能失败,生成新卡重试
logger.warn(this.siteName, ` → ⚠️ 支付超时(${maxWait/1000}秒),共检测 ${checkCount}`);
logger.info(this.siteName, ` → 将生成新卡重试 (第 ${retryCount + 1}/${maxRetries} 次)`);
// 生成新卡
const cardGen = new CardGenerator();
const newCard = cardGen.generate('unionpay');
logger.info(this.siteName, ` → 新卡号: ${newCard.number}`);
this.cardInfo = {
number: newCard.number,
month: newCard.month,
year: newCard.year,
cvv: newCard.cvv,
country: 'MO'
};
// 重新填写卡信息
await this.fillCardForm(newCard, true);
logger.success(this.siteName, ' → ✓ 已更新银行卡信息');
await this.human.randomDelay(2000, 3000);
// 递归重试
return await this.submitPayment(retryCount + 1, maxRetries);
}
/** /**
* 步骤6: 填写支付信息 * 步骤6: 填写支付信息
*/ */
@ -1155,55 +1217,75 @@ class WindsurfRegister {
logger.info(this.siteName, `[步骤 7/${this.getTotalSteps()}] 获取订阅信息`); logger.info(this.siteName, `[步骤 7/${this.getTotalSteps()}] 获取订阅信息`);
try { try {
// 0. 检查并关闭可能存在的弹窗("要打开 Windsurf 吗?" // 0. 先关闭多余的窗口/标签页
logger.info(this.siteName, ' → 检查是否有弹窗需要关闭...'); logger.info(this.siteName, ' → 检查并关闭多余窗口...');
try { try {
await new Promise(resolve => setTimeout(resolve, 2000)); // 等待弹窗出现 const pages = await this.browser.pages();
logger.info(this.siteName, ` → 当前共有 ${pages.length} 个页面`);
// 方法1: 尝试按ESC键关闭浏览器原生对话框 if (pages.length > 1) {
logger.info(this.siteName, ' → 尝试按ESC键关闭原生对话框...'); // 保留一个 windsurf.com 的页面,关闭其他
await this.page.keyboard.press('Escape'); let validPage = null;
await this.human.randomDelay(500, 1000); const pagesToClose = [];
// 方法2: 尝试查找并点击网页内的按钮如果是HTML弹窗 for (const page of pages) {
const closeDialog = await this.page.evaluate(() => { try {
// 查找包含"取消"或"打开Windsurf"的按钮 const url = page.url();
const buttons = Array.from(document.querySelectorAll('button')); if (url.includes('windsurf.com') && !validPage) {
const cancelBtn = buttons.find(btn => validPage = page;
btn.textContent.includes('取消') || logger.info(this.siteName, ` → ✓ 保留: ${url}`);
btn.textContent.includes('打开Windsurf') ||
btn.textContent.includes('Cancel')
);
if (cancelBtn) {
// 优先点击"取消"按钮
const actualCancelBtn = buttons.find(btn =>
btn.textContent.includes('取消') ||
btn.textContent.includes('Cancel')
);
if (actualCancelBtn) {
actualCancelBtn.click();
return '取消';
} else { } else {
cancelBtn.click(); pagesToClose.push(page);
return '打开Windsurf'; logger.info(this.siteName, ` → ✗ 关闭: ${url}`);
}
}
return null;
});
if (closeDialog) {
logger.success(this.siteName, ` → ✓ 已关闭HTML弹窗点击了"${closeDialog}"`);
await this.human.randomDelay(1000, 2000);
} else {
logger.success(this.siteName, ' → ✓ 已尝试关闭原生对话框ESC键');
} }
} catch (e) { } catch (e) {
logger.info(this.siteName, ` → 关闭弹窗时出错: ${e.message}`); pagesToClose.push(page);
}
} }
// 1. 跳转到订阅使用页面 // 如果没找到 windsurf 页面,保留第一个
if (!validPage && pages.length > 0) {
validPage = pages[0];
pagesToClose.shift();
}
// 关闭多余页面
for (const page of pagesToClose) {
try {
await page.close();
} catch (e) {
// 忽略
}
}
this.page = validPage;
logger.success(this.siteName, ` → ✓ 已关闭 ${pagesToClose.length} 个多余页面`);
}
} catch (e) {
logger.warn(this.siteName, ` → 关闭窗口失败: ${e.message}`);
}
await this.human.randomDelay(1000, 2000);
// 1. 持续按ESC关闭弹窗循环5次确保关闭
logger.info(this.siteName, ' → 关闭可能存在的对话框...');
for (let i = 0; i < 5; i++) {
try {
await this.page.keyboard.press('Escape');
await new Promise(resolve => setTimeout(resolve, 300));
} catch (e) {
// 忽略
}
}
logger.success(this.siteName, ' → ✓ 已按5次ESC键');
await this.human.randomDelay(500, 1000);
// 2. 跳转到订阅使用页面
logger.info(this.siteName, ' → 跳转到订阅使用页面...'); logger.info(this.siteName, ' → 跳转到订阅使用页面...');
const currentUrl = this.page.url();
logger.info(this.siteName, ` → 当前页面: ${currentUrl}`);
await this.page.goto('https://windsurf.com/subscription/usage', { await this.page.goto('https://windsurf.com/subscription/usage', {
waitUntil: 'networkidle2', waitUntil: 'networkidle2',
timeout: 30000 timeout: 30000