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()
|
expiry_timer = (await page.locator(".expiry-timer-wrap .timer .time").inner_text()).strip()
|
||||||
logger.debug(f"[任务 {self.task_id}] 倒计时:{expiry_timer}")
|
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
|
is_submit_btn = False
|
||||||
logger.debug(f"[任务 {self.task_id}] 检测是否直接进入了支付环节")
|
logger.debug(f"[任务 {self.task_id}] 检测是否直接进入了支付环节")
|
||||||
@@ -796,39 +808,27 @@ class SurugayaPurchaseTask(BasePurchaseTask):
|
|||||||
logger.error(f"[任务 {self.task_id}] 支付异常:{e}")
|
logger.error(f"[任务 {self.task_id}] 支付异常:{e}")
|
||||||
raise 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):
|
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} 秒")
|
logger.debug(f"[任务 {self.task_id}] 登录提交后同时等待支付按钮或短信验证码登录页面出现,限时 {timeout_sec} 秒")
|
||||||
submit_btn = page.locator(".submit-button-wrap button.submit-button")
|
submit_btn = page.locator(".submit-button-wrap button.submit-button")
|
||||||
sms_prompt = login_iframe.get_by_text("SMSで届いた認証コードを入力してください").first
|
sms_prompt = login_iframe.get_by_text("SMSで届いた認証コードを入力してください").first
|
||||||
|
return await self._race_locators({"submit": submit_btn, "sms": sms_prompt}, timeout_sec=timeout_sec)
|
||||||
# 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} 秒)")
|
|
||||||
|
|
||||||
async def _wait_for_manual_sms_verification(self, page: Page, login_iframe, *, timeout_sec: float = 180):
|
async def _wait_for_manual_sms_verification(self, page: Page, login_iframe, *, timeout_sec: float = 180):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user