PayPay QR码兜底链接处理 + 抽离locator竞赛方法
- 在支付跳转流程中检测PayPay QR码支付页,若存在"こちら"兜底链接则点击进入账号登录环节 - 将 _wait_for_paypay_login_result 中的并发等待逻辑提取为通用 _race_locators 辅助方法
This commit is contained in:
+28
-28
@@ -715,6 +715,18 @@ class SurugayaPurchaseTask(BasePurchaseTask):
|
||||
expiry_timer = (await page.locator(".expiry-timer-wrap .timer .time").inner_text()).strip()
|
||||
logger.debug(f"[任务 {self.task_id}] 倒计时:{expiry_timer}")
|
||||
|
||||
# 页面可能默认展示"PayPayアプリでQRコードをスキャンして支払い",需点击兜底链接才能进入账号登录/验证码环节
|
||||
logger.debug(f"[任务 {self.task_id}] 检测是否存在QR码支付页的兜底链接...")
|
||||
qr_fallback_link = page.get_by_role("link", name="こちら")
|
||||
try:
|
||||
await qr_fallback_link.wait_for(state="visible", timeout=3000)
|
||||
logger.debug(f"[任务 {self.task_id}] 检测到PayPay QR码支付页,点击'スキャンできない場合はこちら'兜底链接")
|
||||
await page.wait_for_timeout(random.randint(500, 1000))
|
||||
await qr_fallback_link.click()
|
||||
logger.debug(f"[任务 {self.task_id}] 已点击QR码兜底链接")
|
||||
except PlaywrightTimeoutError:
|
||||
logger.debug(f"[任务 {self.task_id}] 未检测到QR码支付页兜底链接,跳过")
|
||||
|
||||
# 判断是否直接进入了支付环节
|
||||
is_submit_btn = False
|
||||
logger.debug(f"[任务 {self.task_id}] 检测是否直接进入了支付环节")
|
||||
@@ -796,39 +808,27 @@ class SurugayaPurchaseTask(BasePurchaseTask):
|
||||
logger.error(f"[任务 {self.task_id}] 支付异常:{e}")
|
||||
raise e
|
||||
|
||||
async def _race_locators(self, locators: dict, *, timeout_sec: float = 10.0) -> str:
|
||||
"""并发等待多个 locator,谁先可见就返回对应的 key,全部超时则抛异常"""
|
||||
try:
|
||||
async with asyncio.timeout(timeout_sec):
|
||||
tasks = {key: asyncio.create_task(locator.wait_for(state="visible")) for key, locator in locators.items()}
|
||||
done, pending = await asyncio.wait(tasks.values(), return_when=asyncio.FIRST_COMPLETED)
|
||||
for p_task in pending:
|
||||
p_task.cancel()
|
||||
first_completed = done.pop()
|
||||
for key, task in tasks.items():
|
||||
if task is first_completed:
|
||||
return key
|
||||
except TimeoutError:
|
||||
raise Exception(f"等待 {list(locators.keys())} 之一超时(限时 {timeout_sec} 秒)")
|
||||
|
||||
async def _wait_for_paypay_login_result(self, page: Page, login_iframe, *, timeout_sec: float = 10.0):
|
||||
"""同时等待支付按钮或短信验证码登录页面出现(异步竞赛版)"""
|
||||
logger.debug(f"[任务 {self.task_id}] 登录提交后同时等待支付按钮或短信验证码登录页面出现,限时 {timeout_sec} 秒")
|
||||
submit_btn = page.locator(".submit-button-wrap button.submit-button")
|
||||
sms_prompt = login_iframe.get_by_text("SMSで届いた認証コードを入力してください").first
|
||||
|
||||
# 1. 使用 Python 标准的 asyncio.timeout 控制总时间
|
||||
try:
|
||||
async with asyncio.timeout(timeout_sec):
|
||||
|
||||
# 2. 创建两个并发等待任务
|
||||
task_submit = asyncio.create_task(submit_btn.wait_for(state="visible"))
|
||||
task_sms = asyncio.create_task(sms_prompt.wait_for(state="visible"))
|
||||
|
||||
# 3. 展开速度竞赛
|
||||
done, pending = await asyncio.wait(
|
||||
{task_submit, task_sms},
|
||||
return_when=asyncio.FIRST_COMPLETED
|
||||
)
|
||||
|
||||
# 4. 谁先到终点,就取消另一个并返回对应结果
|
||||
for p_task in pending:
|
||||
p_task.cancel()
|
||||
|
||||
first_completed = done.pop()
|
||||
if first_completed == task_submit:
|
||||
return "submit"
|
||||
elif first_completed == task_sms:
|
||||
return "sms"
|
||||
|
||||
except TimeoutError:
|
||||
# 当 asyncio.timeout 超时,会自动抛出内置的 TimeoutError
|
||||
raise Exception(f"登录后等待支付按钮或短信验证码超时(限时 {timeout_sec} 秒)")
|
||||
return await self._race_locators({"submit": submit_btn, "sms": sms_prompt}, timeout_sec=timeout_sec)
|
||||
|
||||
async def _wait_for_manual_sms_verification(self, page: Page, login_iframe, *, timeout_sec: float = 180):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user