# Windsurf 注册自动化配置 site: name: Windsurf url: https://windsurf.com/refer?referral_code=55424ec434 # 工作流定义 workflow: # ==================== 步骤 0: 处理邀请链接 ==================== - action: navigate name: "打开邀请链接" url: "https://windsurf.com/refer?referral_code=55424ec434" options: waitUntil: 'networkidle2' timeout: 30000 - action: click name: "点击接受邀请" selector: - text: 'Sign up to accept referral' selector: 'button' - css: 'button.bg-sk-aqua' - css: 'button:has-text("Sign up to accept referral")' timeout: 30000 waitForNavigation: true # 验证跳转到注册页面(带referral_code) - action: verify name: "验证跳转到注册页面" conditions: success: - urlContains: "/account/register" - urlContains: "referral_code=55424ec434" timeout: 10000 # ==================== 步骤 1: 打开注册页面并填写信息(带重试) ==================== - action: retryBlock name: "打开注册页面并填写基本信息" maxRetries: 3 retryDelay: 3000 steps: # 1.1 验证注册页面元素已加载 - action: wait name: "等待注册表单加载" type: element selector: '#firstName' timeout: 15000 # 1.2 填写基本信息 - action: fillForm name: "填写基本信息" fields: firstName: "{{account.firstName}}" lastName: "{{account.lastName}}" email: "{{account.email}}" # 1.3 勾选同意条款 - action: click name: "勾选同意条款" selector: - css: 'input[type="checkbox"]' optional: true # 1.4 点击 Continue - action: click name: "点击 Continue (基本信息)" selector: - text: 'Continue' selector: 'button, a' # 明确指定查找范围:按钮或链接 timeout: 30000 # 验证点击后密码页面出现 verifyAfter: appears: - '#password' - 'input[type="password"]' # ==================== 步骤 2: 设置密码 ==================== - action: fillForm name: "设置密码" fields: password: "{{account.password}}" passwordConfirmation: "{{account.password}}" - action: click name: "提交密码" selector: - text: 'Continue' selector: 'button, a' timeout: 30000 # ==================== 步骤 2.5: Cloudflare Turnstile 验证 ==================== - action: custom name: "Cloudflare Turnstile 验证" handler: "handleTurnstile" params: timeout: 30000 maxRetries: 3 # 重试策略: # - 'refresh': 刷新页面(适用于保持状态的网站) # - 'restart': 刷新后重新填写(适用于刷新=重置的网站,如 Windsurf) # - 'wait': 只等待不刷新 retryStrategy: 'restart' # Windsurf 刷新会回到第一步 optional: true # ==================== 步骤 3: 邮箱验证 ==================== # 需要自定义处理:获取邮件验证码 + 填写6个输入框 - action: custom name: "邮箱验证" handler: "handleEmailVerification" params: timeout: 120000 # ==================== 步骤 4: 跳过问卷调查 ==================== - action: click name: "跳过问卷" selector: - text: 'Skip this step' selector: 'button, a' - text: 'skip' selector: 'button, a' exact: false # ==================== 步骤 5: 选择计划 ==================== - action: click name: "选择计划" selector: - text: 'Select plan' selector: 'button, a' timeout: 30000 # 等待跳转到 Stripe 支付页面(通用 URL 等待) - action: wait name: "等待跳转到 Stripe" type: url urlContains: "stripe.com" timeout: 20000 # 生成银行卡数据(首次执行) - action: custom name: "生成银行卡" handler: "generateCard" # ==================== 步骤 6: 填写支付信息(带重试) ==================== - action: retryBlock name: "提交支付并验证" maxRetries: 9999 # 无限重试,直到成功 retryDelay: 15000 # 增加到15秒,避免触发Stripe风控 onRetryBefore: # 生成银行卡(第一次或重试都生成新卡) - action: custom handler: "generateCard" steps: # 6.1 选择银行卡支付方式(每次重试都重新选择) - action: click name: "选择银行卡支付" selector: - css: 'input[type="radio"][value="card"]' verifyAfter: checked: true # 验证 radio button 是否选中 # 填写支付信息(银行卡+国家+地址) - action: fillForm name: "填写支付信息" fields: cardNumber: "{{card.number}}" cardExpiry: "{{card.month}}{{card.year}}" cardCvc: "{{card.cvv}}" billingName: "{{account.firstName}} {{account.lastName}}" billingCountry: find: - css: '#billingCountry' value: "MO" type: "select" addressLine1: find: - css: '#billingAddressLine1' # 使用 id 选择器,兼容所有语言 - css: 'input[name="billingAddressLine1"]' - css: 'input[placeholder*="地址"]' - css: 'input[placeholder*="Address"]' - css: 'input[placeholder*="Alamat"]' # 印尼语 value: "kowloon" # 滚动到页面底部(确保订阅按钮可见) - action: scroll name: "滚动到订阅按钮" type: bottom # 提交支付(内部会等待按钮变为可点击状态) - action: click name: "点击提交支付" selector: - css: 'button[data-testid="hosted-payment-submit-button"]' # Stripe 官方 testid - css: 'button.SubmitButton' # Stripe 按钮类名 - css: 'button[type="submit"]' # 通用 submit 按钮 - css: 'button:has(.SubmitButton-IconContainer)' # 包含 icon 容器的按钮 timeout: 30000 # 给足够时间等待按钮从 disabled 变为可点击 waitForEnabled: true # 循环等待按钮激活(不是等待出现,而是等待可点击) # 验证点击是否生效(检查按钮状态变化) - action: custom name: "验证订阅按钮点击生效" handler: "verifySubmitClick" # 处理 hCaptcha(等待60秒让用户手动完成) - action: custom name: "hCaptcha 验证" handler: "handleHCaptcha" # 验证支付结果(轮询检测成功或失败) - action: verify name: "验证支付结果" conditions: success: - urlNotContains: "stripe.com" - urlNotContains: "checkout.stripe.com" failure: # 英文 - textContains: "card was declined" - textContains: "Your card was declined" - textContains: "declined" - textContains: "We are unable to authenticate your payment method" # 中文 - textContains: "卡片被拒绝" - textContains: "我们无法验证您的支付方式" - textContains: "我们未能验证您的支付方式" - textContains: "请选择另一支付方式并重试" # 马来语 - textContains: "Kad anda telah ditolak" # 您的卡已被拒绝 - textContains: "Kami tidak dapat mensahihkan kaedah pembayaran" - textContains: "Sila pilih kaedah pembayaran yang berbeza" # 印尼语 - textContains: "Kami tidak dapat memverifikasi metode pembayaran" - textContains: "Silakan pilih metode pembayaran" - textContains: "kartu ditolak" # 泰语 - textContains: "บัตรของคุณถูกปฏิเสธ" # 您的卡被拒绝 - textContains: "ไม่สามารถยืนยัน" # 无法验证 - textContains: "เราตรวจสอบสิทธิ์วิธีการชำระเงินของคุณไม่ได้" # 我们无法验证您的支付方式 - textContains: "โปรดเลือกวิธีการชำระเงินอื่น" # 请选择另一个支付方式 # 通用错误元素 - elementExists: ".error-message" timeout: 15000 pollInterval: 500 onFailure: "throw" # ==================== 步骤 7: 获取订阅信息 ==================== # 7.1 跳转到订阅使用页面 - action: navigate name: "跳转订阅页面" url: "https://windsurf.com/subscription/usage" # 7.2 提取配额信息 - action: extract name: "提取配额信息" selector: "p.caption1.font-medium.text-sk-black\\/80 span.caption3 span" multiple: true contextKey: "quotaRaw" required: false # 7.3 提取账单周期信息 - action: extract name: "提取账单周期" selector: "p.caption1" extractType: "text" filter: contains: "Next billing cycle" regex: "(\\d+)\\s+days?.*on\\s+([A-Za-z]+\\s+\\d+,\\s+\\d{4})" saveTo: days: "$1" date: "$2" contextKey: "billingInfo" required: false # 7.4 处理提取的数据(自定义) - action: custom name: "处理订阅数据" handler: "processSubscriptionData" optional: true # ==================== 步骤 8: 保存到数据库 ==================== - action: custom name: "保存到数据库" handler: "saveToDatabase" optional: true # ==================== 步骤 9: 退出登录 ==================== # 9.1 滚动到页面底部 - action: scroll name: "滚动到页面底部" direction: "bottom" # 9.2 等待滚动完成 - action: wait name: "等待滚动完成" duration: 1000 # 9.3 点击退出登录 - action: click name: "点击退出登录" selector: - css: 'div.body3.cursor-pointer:has-text("Log out")' - css: 'div.body3:has-text("Log out")' - xpath: '//div[contains(@class, "body3") and contains(text(), "Log out")]' - text: "Log out" timeout: 15000 waitForNavigation: true # 9.4 验证页面已变化(离开订阅页面) - action: verify name: "验证页面已变化" conditions: success: - or: - urlContains: "/account/login" - urlContains: "/login" - urlContains: "/" failure: - urlContains: "/subscription/usage" timeout: 10000 optional: true # 9.5 验证跳转到登录页 - action: verify name: "验证跳转到登录页" conditions: success: - or: - urlContains: "/account/login" - urlContains: "/login" timeout: 5000 optional: true # 错误处理配置 errorHandling: screenshot: true retry: enabled: true maxAttempts: 3 delay: 2000