fix: align Plus no-payment flow and step5 completion
This commit is contained in:
@@ -34,6 +34,7 @@
|
||||
setStep8PendingReject,
|
||||
setStep8TabUpdatedListener,
|
||||
shouldDeferStep9CallbackTimeout,
|
||||
getStepIdByKeyForState = null,
|
||||
} = deps;
|
||||
|
||||
const LOCALHOST_CALLBACK_LOCAL_TIMEOUT_MS = 240000;
|
||||
@@ -45,7 +46,17 @@
|
||||
}
|
||||
|
||||
function getAuthLoginStepForVisibleStep(visibleStep) {
|
||||
return visibleStep >= 12 ? 10 : 7;
|
||||
return visibleStep >= 12 ? Math.max(1, visibleStep - 3) : 7;
|
||||
}
|
||||
|
||||
function getAuthLoginStepForState(state = {}, visibleStep = 9) {
|
||||
const authStep = typeof getStepIdByKeyForState === 'function'
|
||||
? Number(getStepIdByKeyForState('oauth-login', state))
|
||||
: 0;
|
||||
if (Number.isInteger(authStep) && authStep > 0) {
|
||||
return authStep;
|
||||
}
|
||||
return getAuthLoginStepForVisibleStep(visibleStep);
|
||||
}
|
||||
|
||||
function addStepLog(step, message, level = 'info') {
|
||||
@@ -57,7 +68,7 @@
|
||||
let activeState = state;
|
||||
|
||||
if (!activeState.oauthUrl) {
|
||||
const authLoginStep = getAuthLoginStepForVisibleStep(visibleStep);
|
||||
const authLoginStep = getAuthLoginStepForState(activeState, visibleStep);
|
||||
throw new Error(`缺少登录用 OAuth 链接,请先完成步骤 ${authLoginStep}。`);
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS,
|
||||
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS,
|
||||
throwIfStopped,
|
||||
getStepIdByKeyForState = null,
|
||||
} = deps;
|
||||
let activeFetchLoginCodeStep = null;
|
||||
let activeFetchLoginCodeStepKey = 'fetch-login-code';
|
||||
@@ -93,7 +94,17 @@
|
||||
}
|
||||
|
||||
function getAuthLoginStepForVisibleStep(visibleStep) {
|
||||
return visibleStep >= 11 ? 10 : 7;
|
||||
return visibleStep >= 11 ? Math.max(1, visibleStep - 1) : 7;
|
||||
}
|
||||
|
||||
function getAuthLoginStepForState(state = {}, visibleStep = 8) {
|
||||
const authStep = typeof getStepIdByKeyForState === 'function'
|
||||
? Number(getStepIdByKeyForState('oauth-login', state))
|
||||
: 0;
|
||||
if (Number.isInteger(authStep) && authStep > 0) {
|
||||
return authStep;
|
||||
}
|
||||
return getAuthLoginStepForVisibleStep(visibleStep);
|
||||
}
|
||||
|
||||
async function getStep8ReadyTimeoutMs(actionLabel, expectedOauthUrl = '', visibleStep = 8) {
|
||||
@@ -331,7 +342,7 @@
|
||||
}
|
||||
|
||||
async function recoverStep8PollingFailure(currentState, visibleStep) {
|
||||
const authLoginStep = getAuthLoginStepForVisibleStep(visibleStep);
|
||||
const authLoginStep = getAuthLoginStepForState(currentState, visibleStep);
|
||||
try {
|
||||
const pageState = await ensureStep8VerificationPageReady({
|
||||
visibleStep,
|
||||
@@ -771,7 +782,7 @@
|
||||
await chrome.tabs.update(authTabId, { active: true });
|
||||
} else {
|
||||
if (!state.oauthUrl) {
|
||||
throw new Error(`缺少登录用 OAuth 链接,请先完成步骤 ${getAuthLoginStepForVisibleStep(visibleStep)}。`);
|
||||
throw new Error(`缺少登录用 OAuth 链接,请先完成步骤 ${getAuthLoginStepForState(state, visibleStep)}。`);
|
||||
}
|
||||
await reuseOrCreateTab('openai-auth', state.oauthUrl);
|
||||
}
|
||||
@@ -779,7 +790,7 @@
|
||||
throwIfStopped();
|
||||
let pageState = await ensureStep8VerificationPageReady({
|
||||
visibleStep,
|
||||
authLoginStep: getAuthLoginStepForVisibleStep(visibleStep),
|
||||
authLoginStep: getAuthLoginStepForState(state, visibleStep),
|
||||
allowPhoneVerificationPage: true,
|
||||
allowAddEmailPage: true,
|
||||
timeoutMs: await getStep8ReadyTimeoutMs('确认登录验证码页已就绪', state?.oauthUrl || '', visibleStep),
|
||||
@@ -848,7 +859,7 @@
|
||||
return;
|
||||
} catch (err) {
|
||||
const visibleStep = getVisibleStep(currentState, 8);
|
||||
const authLoginStep = getAuthLoginStepForVisibleStep(visibleStep);
|
||||
const authLoginStep = getAuthLoginStepForState(currentState, visibleStep);
|
||||
let currentError = err;
|
||||
let retryWithoutStep7 = false;
|
||||
|
||||
|
||||
@@ -2961,6 +2961,27 @@ function isLikelyLoggedInChatgptHomeUrl(rawUrl = location.href) {
|
||||
}
|
||||
}
|
||||
|
||||
function isStep5CompletionChatgptUrl(rawUrl = location.href) {
|
||||
const url = String(rawUrl || '').trim();
|
||||
if (!url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = new URL(url);
|
||||
const protocol = String(parsed.protocol || '').toLowerCase();
|
||||
const host = String(parsed.hostname || '').toLowerCase();
|
||||
if (protocol !== 'https:' || !['chatgpt.com', 'www.chatgpt.com'].includes(host)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const path = String(parsed.pathname || '');
|
||||
return !/^\/(?:auth\/|create-account\/|email-verification|log-in|add-phone)(?:[/?#]|$)/i.test(path);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getStep4PostVerificationState(options = {}) {
|
||||
const { ignoreVerificationVisibility = false } = options;
|
||||
// Newer auth flows can briefly render profile fields before the email-verification
|
||||
@@ -6693,43 +6714,13 @@ function getStep5PostSubmitSuccessState() {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isLikelyLoggedInChatgptHomeUrl()) {
|
||||
if (isStep5CompletionChatgptUrl()) {
|
||||
return {
|
||||
state: 'logged_in_home',
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof isOAuthConsentPage === 'function' && isOAuthConsentPage()) {
|
||||
return {
|
||||
state: 'oauth_consent',
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof isAddPhonePageReady === 'function' && isAddPhonePageReady()) {
|
||||
return {
|
||||
state: 'add_phone',
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
if (!isStep5ProfileStillVisible()) {
|
||||
try {
|
||||
const parsed = new URL(String(location.href || '').trim());
|
||||
const host = String(parsed.hostname || '').toLowerCase();
|
||||
if (['auth.openai.com', 'auth0.openai.com', 'accounts.openai.com'].includes(host)) {
|
||||
return null;
|
||||
}
|
||||
} catch {
|
||||
// Fall through to the generic "left_profile" success state.
|
||||
}
|
||||
return {
|
||||
state: 'left_profile',
|
||||
url: location.href,
|
||||
};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -6764,6 +6755,29 @@ function getStep5SubmitState() {
|
||||
};
|
||||
}
|
||||
|
||||
function logStep5SubmitDebug(message, options = {}) {
|
||||
const resolvedState = options?.state && typeof options.state === 'object'
|
||||
? options.state
|
||||
: getStep5SubmitState();
|
||||
const summary = [
|
||||
`url=${resolvedState?.url || location.href}`,
|
||||
`retryPage=${Boolean(resolvedState?.retryPage)}`,
|
||||
`retryEnabled=${Boolean(resolvedState?.retryEnabled)}`,
|
||||
`successState=${resolvedState?.successState || 'none'}`,
|
||||
`profileVisible=${Boolean(resolvedState?.profileVisible)}`,
|
||||
`unknownAuthPage=${Boolean(resolvedState?.unknownAuthPage)}`,
|
||||
`maxCheckAttemptsBlocked=${Boolean(resolvedState?.maxCheckAttemptsBlocked)}`,
|
||||
`userAlreadyExistsBlocked=${Boolean(resolvedState?.userAlreadyExistsBlocked)}`,
|
||||
resolvedState?.errorText ? `errorText=${resolvedState.errorText}` : null,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(' | ');
|
||||
log(`步骤 5 [调试] ${message} | ${summary}`, options?.level || 'info', {
|
||||
step: 5,
|
||||
stepKey: 'fill-profile',
|
||||
});
|
||||
}
|
||||
|
||||
async function recoverStep5SubmitRetryPage(payload = {}) {
|
||||
return recoverCurrentAuthRetryPage({
|
||||
...payload,
|
||||
@@ -6780,14 +6794,21 @@ function installStep5NavigationCompletionReporter(completeOnce) {
|
||||
if (typeof window === 'undefined' || typeof window.addEventListener !== 'function') {
|
||||
return () => {};
|
||||
}
|
||||
const debugLog = typeof logStep5SubmitDebug === 'function'
|
||||
? logStep5SubmitDebug
|
||||
: (message, options = {}) => {
|
||||
if (typeof log === 'function') {
|
||||
log(`步骤 5 [调试] ${message}`, options?.level || 'info', {
|
||||
step: 5,
|
||||
stepKey: 'fill-profile',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onNavigationStarted = () => {
|
||||
completeOnce({
|
||||
navigationStarted: true,
|
||||
outcome: {
|
||||
state: 'navigation_started',
|
||||
url: location.href,
|
||||
},
|
||||
const onNavigationStarted = (event) => {
|
||||
const eventType = String(event?.type || 'navigation').trim() || 'navigation';
|
||||
debugLog(`检测到页面开始导航(event=${eventType})。`, {
|
||||
level: 'warn',
|
||||
});
|
||||
};
|
||||
|
||||
@@ -6801,6 +6822,16 @@ function installStep5NavigationCompletionReporter(completeOnce) {
|
||||
}
|
||||
|
||||
async function waitForStep5SubmitOutcome(options = {}) {
|
||||
const debugLog = typeof logStep5SubmitDebug === 'function'
|
||||
? logStep5SubmitDebug
|
||||
: (message, logOptions = {}) => {
|
||||
if (typeof log === 'function') {
|
||||
log(`步骤 5 [调试] ${message}`, logOptions?.level || 'info', {
|
||||
step: 5,
|
||||
stepKey: 'fill-profile',
|
||||
});
|
||||
}
|
||||
};
|
||||
const {
|
||||
timeoutMs = 120000,
|
||||
maxAuthRetryRecoveries = 2,
|
||||
@@ -6828,6 +6859,9 @@ async function waitForStep5SubmitOutcome(options = {}) {
|
||||
throw new Error(`步骤 5:资料提交后连续进入认证重试页 ${maxAuthRetryRecoveries} 次,页面仍未恢复。URL: ${location.href}`);
|
||||
}
|
||||
authRetryRecoveryCount += 1;
|
||||
debugLog(`检测到资料提交后的认证重试页,准备执行恢复(${authRetryRecoveryCount}/${maxAuthRetryRecoveries})。`, {
|
||||
level: 'warn',
|
||||
});
|
||||
log(`步骤 5:资料提交后进入认证重试页,正在自动恢复(${authRetryRecoveryCount}/${maxAuthRetryRecoveries})...`, 'warn');
|
||||
await recoverCurrentAuthRetryPage({
|
||||
flow: 'signup',
|
||||
@@ -6837,12 +6871,18 @@ async function waitForStep5SubmitOutcome(options = {}) {
|
||||
step: 5,
|
||||
timeoutMs: 12000,
|
||||
});
|
||||
debugLog('认证重试页恢复动作已完成,准备继续等待最终结果。', {
|
||||
level: 'info',
|
||||
});
|
||||
lastSubmitClickAt = Date.now();
|
||||
continue;
|
||||
}
|
||||
|
||||
const successState = getStep5PostSubmitSuccessState();
|
||||
if (successState) {
|
||||
debugLog(`检测到资料提交成功状态:${successState.state || 'unknown'}`, {
|
||||
level: 'ok',
|
||||
});
|
||||
return successState;
|
||||
}
|
||||
|
||||
@@ -7146,8 +7186,23 @@ async function step5_fillNameBirthday(payload) {
|
||||
}
|
||||
|
||||
let reportedCompletionPayload = null;
|
||||
const debugLog = typeof logStep5SubmitDebug === 'function'
|
||||
? logStep5SubmitDebug
|
||||
: (message, logOptions = {}) => {
|
||||
if (typeof log === 'function') {
|
||||
log(`步骤 5 [调试] ${message}`, logOptions?.level || 'info', {
|
||||
step: 5,
|
||||
stepKey: 'fill-profile',
|
||||
});
|
||||
}
|
||||
};
|
||||
function completeStep5Once(extra = {}) {
|
||||
const completionReason = extra?.outcome?.state
|
||||
|| (extra?.navigationStarted ? `navigation_started:${extra?.navigationEventType || 'unknown'}` : 'direct_completion');
|
||||
if (reportedCompletionPayload) {
|
||||
debugLog(`忽略重复完成信号(reason=${completionReason})。`, {
|
||||
level: 'warn',
|
||||
});
|
||||
return reportedCompletionPayload;
|
||||
}
|
||||
|
||||
@@ -7156,6 +7211,9 @@ async function step5_fillNameBirthday(payload) {
|
||||
navigationStarted: Boolean(extra.navigationStarted),
|
||||
outcome: extra.outcome || null,
|
||||
});
|
||||
debugLog(`准备发送完成信号(reason=${completionReason},isAgeMode=${isAgeMode})。`, {
|
||||
level: extra?.navigationStarted ? 'warn' : 'info',
|
||||
});
|
||||
reportedCompletionPayload = completionPayload;
|
||||
reportComplete(5, completionPayload);
|
||||
return completionPayload;
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
const SIGNUP_METHOD_PHONE = 'phone';
|
||||
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
|
||||
const PLUS_PAYMENT_METHOD_PAYPAL_HOSTED = 'paypal-hosted';
|
||||
const PLUS_PAYMENT_METHOD_NONE = 'none';
|
||||
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
|
||||
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
|
||||
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
|
||||
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
|
||||
const PLUS_ACCOUNT_ACCESS_STRATEGY_CPA_CODEX_SESSION = 'cpa_codex_session';
|
||||
const PLUS_PAYMENT_STEP_KEY = 'paypal-approve';
|
||||
const PLUS_REGISTRATION_WAIT_STEP_KEY = 'wait-registration-success';
|
||||
|
||||
function freezeDeep(entry) {
|
||||
if (!entry || typeof entry !== 'object' || Object.isFrozen(entry)) {
|
||||
@@ -3014,12 +3016,87 @@
|
||||
]
|
||||
});
|
||||
|
||||
const PLUS_PAYMENT_CHAIN_STEP_KEYS = Object.freeze([
|
||||
'plus-checkout-create',
|
||||
'plus-checkout-billing',
|
||||
'paypal-approve',
|
||||
'plus-checkout-return',
|
||||
'paypal-hosted-email',
|
||||
'paypal-hosted-card',
|
||||
'paypal-hosted-create-account',
|
||||
'paypal-hosted-review',
|
||||
'gopay-subscription-confirm',
|
||||
]);
|
||||
|
||||
function omitPlusPaymentChainSteps(steps = []) {
|
||||
return steps.filter((step) => !PLUS_PAYMENT_CHAIN_STEP_KEYS.includes(String(step?.key || '').trim()));
|
||||
}
|
||||
|
||||
function reindexModeStepDefinitions(steps = []) {
|
||||
return (Array.isArray(steps) ? steps : []).map((step, index) => ({
|
||||
...step,
|
||||
id: index + 1,
|
||||
order: (index + 1) * 10,
|
||||
}));
|
||||
}
|
||||
|
||||
function getPlusRegistrationWaitStep() {
|
||||
const sourceStep = STEP_VARIANTS.normal.find((step) => step.key === PLUS_REGISTRATION_WAIT_STEP_KEY);
|
||||
return {
|
||||
...(sourceStep || {
|
||||
key: PLUS_REGISTRATION_WAIT_STEP_KEY,
|
||||
title: '等待注册成功',
|
||||
sourceId: 'chatgpt',
|
||||
driverId: null,
|
||||
command: PLUS_REGISTRATION_WAIT_STEP_KEY,
|
||||
flowId: 'openai',
|
||||
}),
|
||||
id: 6,
|
||||
order: 60,
|
||||
};
|
||||
}
|
||||
|
||||
function shiftPlusStepAfterRegistrationWait(step = {}) {
|
||||
const nextStep = { ...step };
|
||||
const id = Number(step.id);
|
||||
const order = Number(step.order);
|
||||
if (Number.isFinite(id)) {
|
||||
nextStep.id = id + 1;
|
||||
}
|
||||
if (Number.isFinite(order)) {
|
||||
nextStep.order = order + 10;
|
||||
}
|
||||
return nextStep;
|
||||
}
|
||||
|
||||
function insertPlusRegistrationWaitStep(steps = []) {
|
||||
if (!Array.isArray(steps) || steps.some((step) => step.key === PLUS_REGISTRATION_WAIT_STEP_KEY)) {
|
||||
return steps;
|
||||
}
|
||||
const fillProfileIndex = steps.findIndex((step) => step.key === 'fill-profile');
|
||||
if (fillProfileIndex < 0) {
|
||||
return steps;
|
||||
}
|
||||
return steps.flatMap((step, index) => {
|
||||
if (index < fillProfileIndex) {
|
||||
return [step];
|
||||
}
|
||||
if (index === fillProfileIndex) {
|
||||
return [step, getPlusRegistrationWaitStep()];
|
||||
}
|
||||
return [shiftPlusStepAfterRegistrationWait(step)];
|
||||
});
|
||||
}
|
||||
|
||||
function isPlusModeEnabled(options = {}) {
|
||||
return Boolean(options?.plusModeEnabled || options?.plusMode);
|
||||
}
|
||||
|
||||
function normalizePlusPaymentMethod(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
if (normalized === PLUS_PAYMENT_METHOD_NONE || normalized === 'no-payment' || normalized === 'skip-payment') {
|
||||
return PLUS_PAYMENT_METHOD_NONE;
|
||||
}
|
||||
if (normalized === PLUS_PAYMENT_METHOD_PAYPAL_HOSTED || normalized === 'paypal_direct' || normalized === 'paypal-direct') {
|
||||
return PLUS_PAYMENT_METHOD_PAYPAL_HOSTED;
|
||||
}
|
||||
@@ -3118,13 +3195,27 @@
|
||||
}
|
||||
|
||||
function getModeStepDefinitions(options = {}) {
|
||||
return getVariantStepDefinitions(resolveVariantKey(options));
|
||||
const isPlusMode = isPlusModeEnabled(options);
|
||||
let steps = getVariantStepDefinitions(resolveVariantKey(options));
|
||||
if (isPlusMode) {
|
||||
steps = insertPlusRegistrationWaitStep(steps);
|
||||
}
|
||||
if (
|
||||
isPlusMode
|
||||
&& normalizePlusPaymentMethod(options?.plusPaymentMethod || options?.paymentMethod) === PLUS_PAYMENT_METHOD_NONE
|
||||
) {
|
||||
steps = omitPlusPaymentChainSteps(steps);
|
||||
}
|
||||
return reindexModeStepDefinitions(steps);
|
||||
}
|
||||
|
||||
function getAllSteps() {
|
||||
const keyed = new Map();
|
||||
Object.values(STEP_VARIANTS).forEach((steps) => {
|
||||
(Array.isArray(steps) ? steps : []).forEach((step) => {
|
||||
Object.entries(STEP_VARIANTS).forEach(([variantKey, steps]) => {
|
||||
const variantSteps = String(variantKey || '').startsWith('plus')
|
||||
? insertPlusRegistrationWaitStep(steps)
|
||||
: steps;
|
||||
reindexModeStepDefinitions(variantSteps).forEach((step) => {
|
||||
keyed.set(`${step.id}:${step.key}`, step);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user