From ee946b4a4b2bfab0a1f22f672d4e710ce4266630 Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Mon, 11 May 2026 03:28:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BAPlus=20Checkout?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E9=87=8D=E5=90=AF=E9=80=BB=E8=BE=91=E5=8F=8A=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 44 ++++++++++-- ...un-step4-mail2925-thread-terminate.test.js | 3 + tests/auto-run-step4-restart.test.js | 3 + tests/auto-run-step6-restart.test.js | 67 +++++++++++++++++++ 4 files changed, 111 insertions(+), 6 deletions(-) diff --git a/background.js b/background.js index 25253e8..a9d9db2 100644 --- a/background.js +++ b/background.js @@ -7779,6 +7779,21 @@ function isGpcCheckoutRestartRequiredFailure(error) { return /GPC\s*API\s*请求超时|GPC\s*任务状态超过\s*\d+\s*秒无进展|GPC[\s\S]*请重新创建任务|步骤\s*[67][\s\S]*GPC[\s\S]*(?:access\s*token|accessToken|任务轮询超时|请求超时|超时|timeout|timed\s*out|卡死|无响应|失败)|account\s+already\s+linked|GOPAY已经绑了订阅|(?:账号|账户|GoPay|GOPAY)[\s\S]*(?:已绑定|已经绑定|已绑|绑了订阅|绑定了订阅)|创建\s*GPC\s*订单失败[\s\S]*(?:任务已结束|任务结束|failed|expired|discarded|请求超时|timeout|timed\s*out)/i.test(message); } +function isPlusCheckoutRestartStep(step, stepExecutionKey = '', state = {}) { + const normalizedKey = String(stepExecutionKey || '').trim(); + if (normalizedKey === 'plus-checkout-create' + || normalizedKey === 'plus-checkout-billing' + || normalizedKey === 'gopay-subscription-confirm') { + return true; + } + const numericStep = Number(step); + return Boolean(state?.plusModeEnabled) && (numericStep === 6 || numericStep === 7); +} + +function isPlusCheckoutRestartRequiredFailure(error) { + return !isPlusCheckoutNonFreeTrialFailure(error); +} + function isGoPayCheckoutRestartRequiredFailure(error) { const message = getErrorMessage(error); return /GOPAY_RESTART_FROM_STEP6::|GOPAY_RETRY_REQUIRED::/i.test(message); @@ -10590,6 +10605,7 @@ async function runAutoSequenceFromStep(startStep, context = {}) { let postStep7RestartCount = 0; let goPayCheckoutRestartCount = 0; let gpcCheckoutRestartCount = 0; + let plusCheckoutRestartCount = 0; let step4RestartCount = 0; const stepIdleRestartCounts = new Map(); let currentStartStep = startStep; @@ -10777,16 +10793,32 @@ async function runAutoSequenceFromStep(startStep, context = {}) { : ''; const isGpcCheckoutStep = normalizePlusPaymentMethodForRun(latestState?.plusPaymentMethod) === plusPaymentMethodGpcHelper || String(latestState?.plusCheckoutSource || '').trim() === plusPaymentMethodGpcHelper; - if (isGpcCheckoutStep - && (stepExecutionKey === 'plus-checkout-create' || stepExecutionKey === 'plus-checkout-billing') - && isGpcCheckoutRestartRequiredFailure(err)) { - gpcCheckoutRestartCount += 1; + if (isPlusCheckoutRestartStep(step, stepExecutionKey, latestState) + && isPlusCheckoutRestartRequiredFailure(err)) { + const isGoPayCheckoutStep = stepExecutionKey === 'gopay-subscription-confirm' + || normalizePlusPaymentMethodForRun(latestState?.plusPaymentMethod) === 'gopay'; + if (isGpcCheckoutStep) { + gpcCheckoutRestartCount += 1; + } else if (isGoPayCheckoutStep) { + goPayCheckoutRestartCount += 1; + } else { + plusCheckoutRestartCount += 1; + } + const checkoutRestartCount = isGpcCheckoutStep + ? gpcCheckoutRestartCount + : (isGoPayCheckoutStep ? goPayCheckoutRestartCount : plusCheckoutRestartCount); + const checkoutLabel = isGpcCheckoutStep + ? 'GPC 任务' + : (isGoPayCheckoutStep ? 'GoPay 订阅' : 'Plus Checkout'); + const recreateLabel = isGpcCheckoutStep + ? '重新创建 GPC 任务' + : (isGoPayCheckoutStep ? '重新创建 GoPay 订阅' : '重新创建 Plus Checkout'); await addLog( - `步骤 ${step}:检测到 GPC 任务失败/卡住,准备回到步骤 6 重新创建 GPC 任务(第 ${gpcCheckoutRestartCount} 次)。原因:${getErrorMessage(err)}`, + `步骤 ${step}:检测到 ${checkoutLabel} 失败/卡住,准备回到步骤 6 ${recreateLabel}(第 ${checkoutRestartCount} 次)。原因:${getErrorMessage(err)}`, 'warn' ); await invalidateDownstreamAfterStepRestart(5, { - logLabel: `步骤 ${step} GPC 任务失败后准备回到步骤 6 重试(第 ${gpcCheckoutRestartCount} 次)`, + logLabel: `步骤 ${step} ${checkoutLabel}失败后准备回到步骤 6 重试(第 ${checkoutRestartCount} 次)`, }); step = 6; continue; diff --git a/tests/auto-run-step4-mail2925-thread-terminate.test.js b/tests/auto-run-step4-mail2925-thread-terminate.test.js index 0e357a7..87527a2 100644 --- a/tests/auto-run-step4-mail2925-thread-terminate.test.js +++ b/tests/auto-run-step4-mail2925-thread-terminate.test.js @@ -61,6 +61,9 @@ const bundle = [ extractFunction('isAddPhoneAuthState'), extractFunction('isMail2925ThreadTerminatedError'), extractFunction('isSignupUserAlreadyExistsFailure'), + extractFunction('isPlusCheckoutNonFreeTrialFailure'), + extractFunction('isPlusCheckoutRestartStep'), + extractFunction('isPlusCheckoutRestartRequiredFailure'), extractFunction('getLatestLogTimestamp'), extractFunction('buildAutoRunStepIdleRestartError'), extractFunction('isAutoRunStepIdleRestartError'), diff --git a/tests/auto-run-step4-restart.test.js b/tests/auto-run-step4-restart.test.js index a4a52f2..5c676b1 100644 --- a/tests/auto-run-step4-restart.test.js +++ b/tests/auto-run-step4-restart.test.js @@ -64,6 +64,9 @@ const bundle = [ extractFunction('getSignupPhonePasswordMismatchRestartPayload'), extractFunction('restartSignupPhonePasswordMismatchAttemptFromStep'), extractFunction('isSignupUserAlreadyExistsFailure'), + extractFunction('isPlusCheckoutNonFreeTrialFailure'), + extractFunction('isPlusCheckoutRestartStep'), + extractFunction('isPlusCheckoutRestartRequiredFailure'), extractFunction('getLatestLogTimestamp'), extractFunction('buildAutoRunStepIdleRestartError'), extractFunction('isAutoRunStepIdleRestartError'), diff --git a/tests/auto-run-step6-restart.test.js b/tests/auto-run-step6-restart.test.js index 9ddfac0..2a87a35 100644 --- a/tests/auto-run-step6-restart.test.js +++ b/tests/auto-run-step6-restart.test.js @@ -56,7 +56,10 @@ const bundle = [ extractFunction('isAddPhoneAuthFailure'), extractFunction('isAddPhoneAuthUrl'), extractFunction('isAddPhoneAuthState'), + extractFunction('isPlusCheckoutNonFreeTrialFailure'), extractFunction('isGpcCheckoutRestartRequiredFailure'), + extractFunction('isPlusCheckoutRestartStep'), + extractFunction('isPlusCheckoutRestartRequiredFailure'), extractFunction('getLatestLogTimestamp'), extractFunction('buildAutoRunStepIdleRestartError'), extractFunction('isAutoRunStepIdleRestartError'), @@ -530,6 +533,70 @@ test('auto-run restarts Plus/GPC transient platform verify exchange failures fro assert.ok(events.logs.some(({ message }) => /步骤 13:.*回到步骤 12 重新开始授权流程/.test(message))); }); +test('auto-run restarts Plus checkout from step 6 when checkout creation fails', async () => { + const plusPaypalSteps = { + 6: { key: 'plus-checkout-create' }, + 7: { key: 'plus-checkout-billing' }, + 8: { key: 'paypal-approve' }, + 9: { key: 'plus-checkout-return' }, + 10: { key: 'oauth-login' }, + 11: { key: 'fetch-login-code' }, + 12: { key: 'confirm-oauth' }, + 13: { key: 'platform-verify' }, + }; + const harness = createHarness({ + startStep: 6, + failureStep: 6, + failureBudget: 1, + failureMessage: '步骤 6:创建 Plus Checkout 失败:checkout request failed', + stepDefinitions: plusPaypalSteps, + finalOAuthChainStartStep: 10, + customState: { + stepStatuses: { 3: 'completed' }, + plusModeEnabled: true, + plusPaymentMethod: 'paypal', + }, + }); + + const events = await harness.run(); + + assert.deepStrictEqual(events.steps, [6, 6, 7, 8, 9, 10, 11, 12, 13]); + assert.deepStrictEqual(events.invalidations.map((entry) => entry.step), [5]); + assert.ok(events.logs.some(({ message }) => /回到步骤 6 重新创建 Plus Checkout/.test(message))); +}); + +test('auto-run restarts Plus checkout from step 6 when billing fails for non-free-trial reasons', async () => { + const plusPaypalSteps = { + 6: { key: 'plus-checkout-create' }, + 7: { key: 'plus-checkout-billing' }, + 8: { key: 'paypal-approve' }, + 9: { key: 'plus-checkout-return' }, + 10: { key: 'oauth-login' }, + 11: { key: 'fetch-login-code' }, + 12: { key: 'confirm-oauth' }, + 13: { key: 'platform-verify' }, + }; + const harness = createHarness({ + startStep: 6, + failureStep: 7, + failureBudget: 1, + failureMessage: '步骤 7:账单地址 iframe 无法注入,请重新创建 checkout。', + stepDefinitions: plusPaypalSteps, + finalOAuthChainStartStep: 10, + customState: { + stepStatuses: { 3: 'completed' }, + plusModeEnabled: true, + plusPaymentMethod: 'paypal', + }, + }); + + const events = await harness.run(); + + assert.deepStrictEqual(events.steps, [6, 7, 6, 7, 8, 9, 10, 11, 12, 13]); + assert.deepStrictEqual(events.invalidations.map((entry) => entry.step), [5]); + assert.ok(events.logs.some(({ message }) => /回到步骤 6 重新创建 Plus Checkout/.test(message))); +}); + test('auto-run restarts GPC checkout from step 6 when step 7 task polling stalls', async () => { const plusGpcSteps = { 6: { key: 'plus-checkout-create' },