增加Gopay模式
This commit is contained in:
+103
-10
@@ -31,6 +31,7 @@ importScripts(
|
||||
'background/steps/clear-login-cookies.js',
|
||||
'background/steps/create-plus-checkout.js',
|
||||
'background/steps/fill-plus-checkout.js',
|
||||
'background/steps/gopay-manual-confirm.js',
|
||||
'background/steps/paypal-approve.js',
|
||||
'background/steps/plus-return-confirm.js',
|
||||
'background/steps/oauth-login.js',
|
||||
@@ -48,10 +49,19 @@ importScripts(
|
||||
);
|
||||
|
||||
const NORMAL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({ plusModeEnabled: false }) || [];
|
||||
const PLUS_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({ plusModeEnabled: true }) || NORMAL_STEP_DEFINITIONS;
|
||||
const PLUS_PAYPAL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
plusModeEnabled: true,
|
||||
plusPaymentMethod: 'paypal',
|
||||
}) || NORMAL_STEP_DEFINITIONS;
|
||||
const PLUS_GOPAY_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
plusModeEnabled: true,
|
||||
plusPaymentMethod: 'gopay',
|
||||
}) || PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
const PLUS_STEP_DEFINITIONS = PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
const ALL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getAllSteps?.() || [
|
||||
...NORMAL_STEP_DEFINITIONS,
|
||||
...PLUS_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_STEP_DEFINITIONS,
|
||||
];
|
||||
const STEP_IDS = Array.from(new Set(ALL_STEP_DEFINITIONS
|
||||
.map((definition) => Number(definition?.id))
|
||||
@@ -61,13 +71,18 @@ const NORMAL_STEP_IDS = NORMAL_STEP_DEFINITIONS
|
||||
.map((definition) => Number(definition?.id))
|
||||
.filter(Number.isFinite)
|
||||
.sort((left, right) => left - right);
|
||||
const PLUS_STEP_IDS = PLUS_STEP_DEFINITIONS
|
||||
const PLUS_PAYPAL_STEP_IDS = PLUS_PAYPAL_STEP_DEFINITIONS
|
||||
.map((definition) => Number(definition?.id))
|
||||
.filter(Number.isFinite)
|
||||
.sort((left, right) => left - right);
|
||||
const PLUS_GOPAY_STEP_IDS = PLUS_GOPAY_STEP_DEFINITIONS
|
||||
.map((definition) => Number(definition?.id))
|
||||
.filter(Number.isFinite)
|
||||
.sort((left, right) => left - right);
|
||||
const LAST_STEP_ID = Math.max(
|
||||
NORMAL_STEP_IDS[NORMAL_STEP_IDS.length - 1] || 10,
|
||||
PLUS_STEP_IDS[PLUS_STEP_IDS.length - 1] || 10
|
||||
PLUS_PAYPAL_STEP_IDS[PLUS_PAYPAL_STEP_IDS.length - 1] || 10,
|
||||
PLUS_GOPAY_STEP_IDS[PLUS_GOPAY_STEP_IDS.length - 1] || 10
|
||||
);
|
||||
const FINAL_OAUTH_CHAIN_START_STEP = 7;
|
||||
|
||||
@@ -337,6 +352,10 @@ function isPlusModeState(state = {}) {
|
||||
return Boolean(state?.plusModeEnabled);
|
||||
}
|
||||
|
||||
function normalizePlusPaymentMethod(value = '') {
|
||||
return String(value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal';
|
||||
}
|
||||
|
||||
function normalizeContributionModeSource(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === CONTRIBUTION_SOURCE_SUB2API
|
||||
@@ -372,11 +391,21 @@ function resolveContributionModeRoutingState(state = {}) {
|
||||
}
|
||||
|
||||
function getStepDefinitionsForState(state = {}) {
|
||||
return isPlusModeState(state) ? PLUS_STEP_DEFINITIONS : NORMAL_STEP_DEFINITIONS;
|
||||
if (!isPlusModeState(state)) {
|
||||
return NORMAL_STEP_DEFINITIONS;
|
||||
}
|
||||
return normalizePlusPaymentMethod(state?.plusPaymentMethod) === 'gopay'
|
||||
? PLUS_GOPAY_STEP_DEFINITIONS
|
||||
: PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
}
|
||||
|
||||
function getStepIdsForState(state = {}) {
|
||||
return isPlusModeState(state) ? PLUS_STEP_IDS : NORMAL_STEP_IDS;
|
||||
if (!isPlusModeState(state)) {
|
||||
return NORMAL_STEP_IDS;
|
||||
}
|
||||
return normalizePlusPaymentMethod(state?.plusPaymentMethod) === 'gopay'
|
||||
? PLUS_GOPAY_STEP_IDS
|
||||
: PLUS_PAYPAL_STEP_IDS;
|
||||
}
|
||||
|
||||
function getLastStepIdForState(state = {}) {
|
||||
@@ -573,6 +602,12 @@ const DEFAULT_STATE = {
|
||||
plusBillingAddress: null,
|
||||
plusPaypalApprovedAt: null,
|
||||
plusReturnUrl: '',
|
||||
plusManualConfirmationPending: false,
|
||||
plusManualConfirmationRequestId: '',
|
||||
plusManualConfirmationStep: 0,
|
||||
plusManualConfirmationMethod: '',
|
||||
plusManualConfirmationTitle: '',
|
||||
plusManualConfirmationMessage: '',
|
||||
flowStartTime: null, // 当前流程开始时间。
|
||||
tabRegistry: {}, // 程序维护的标签页注册表。
|
||||
sourceLastUrls: {}, // 各来源页面最近一次打开的地址记录。
|
||||
@@ -6244,6 +6279,12 @@ function getDownstreamStateResets(step, state = {}) {
|
||||
plusBillingAddress: null,
|
||||
plusPaypalApprovedAt: null,
|
||||
plusReturnUrl: '',
|
||||
plusManualConfirmationPending: false,
|
||||
plusManualConfirmationRequestId: '',
|
||||
plusManualConfirmationStep: 0,
|
||||
plusManualConfirmationMethod: '',
|
||||
plusManualConfirmationTitle: '',
|
||||
plusManualConfirmationMessage: '',
|
||||
};
|
||||
|
||||
if (step <= 1) {
|
||||
@@ -6311,6 +6352,12 @@ function getDownstreamStateResets(step, state = {}) {
|
||||
plusBillingAddress: null,
|
||||
plusPaypalApprovedAt: null,
|
||||
plusReturnUrl: '',
|
||||
plusManualConfirmationPending: false,
|
||||
plusManualConfirmationRequestId: '',
|
||||
plusManualConfirmationStep: 0,
|
||||
plusManualConfirmationMethod: '',
|
||||
plusManualConfirmationTitle: '',
|
||||
plusManualConfirmationMessage: '',
|
||||
} : {}),
|
||||
...(step === 8 ? {
|
||||
plusPaypalApprovedAt: null,
|
||||
@@ -7231,8 +7278,12 @@ const AUTO_RUN_BACKGROUND_COMPLETED_STEP_KEYS = new Set([
|
||||
const STEP_COMPLETION_SIGNAL_STEP_KEYS = new Set([
|
||||
'fill-password',
|
||||
'fill-profile',
|
||||
'gopay-subscription-confirm',
|
||||
'platform-verify',
|
||||
]);
|
||||
const STEP_COMPLETION_SIGNAL_TIMEOUTS_BY_STEP_KEY = new Map([
|
||||
['gopay-subscription-confirm', 1800000],
|
||||
]);
|
||||
const AUTO_RUN_PRE_EXECUTION_DELAYS_BY_STEP_KEY = new Map([
|
||||
['plus-checkout-create', 20000],
|
||||
]);
|
||||
@@ -7313,6 +7364,14 @@ function getAutoRunPreExecutionDelayMs(step, state = {}) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
function getStepCompletionSignalTimeoutMs(step, state = {}) {
|
||||
const stepKey = getStepExecutionKeyForState(step, state);
|
||||
if (stepKey) {
|
||||
return STEP_COMPLETION_SIGNAL_TIMEOUTS_BY_STEP_KEY.get(stepKey) || AUTO_RUN_SIGNAL_COMPLETION_TIMEOUT_MS;
|
||||
}
|
||||
return AUTO_RUN_SIGNAL_COMPLETION_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
function notifyStepComplete(step, payload) {
|
||||
const waiter = stepWaiters.get(step);
|
||||
console.log(LOG_PREFIX, `[notifyStepComplete] step ${step}, hasWaiter=${Boolean(waiter)}`);
|
||||
@@ -7393,8 +7452,12 @@ async function finalizeDeferredStepExecutionError(step, error) {
|
||||
await appendManualAccountRunRecordIfNeeded(`step${step}_failed`, latestState, getErrorMessage(error));
|
||||
}
|
||||
|
||||
async function executeStepViaCompletionSignal(step, timeoutMs = AUTO_RUN_SIGNAL_COMPLETION_TIMEOUT_MS) {
|
||||
const completionResultPromise = waitForStepComplete(step, timeoutMs).then(
|
||||
async function executeStepViaCompletionSignal(step, timeoutMs = 0) {
|
||||
const executionState = await getState();
|
||||
const resolvedTimeoutMs = Number(timeoutMs) > 0
|
||||
? timeoutMs
|
||||
: getStepCompletionSignalTimeoutMs(step, executionState);
|
||||
const completionResultPromise = waitForStepComplete(step, resolvedTimeoutMs).then(
|
||||
payload => ({ ok: true, payload }),
|
||||
error => ({ ok: false, error }),
|
||||
);
|
||||
@@ -7592,6 +7655,19 @@ async function requestStop(options = {}) {
|
||||
}
|
||||
stepWaiters.clear();
|
||||
|
||||
if (state.plusManualConfirmationPending) {
|
||||
const clearManualConfirmationState = {
|
||||
plusManualConfirmationPending: false,
|
||||
plusManualConfirmationRequestId: '',
|
||||
plusManualConfirmationStep: 0,
|
||||
plusManualConfirmationMethod: '',
|
||||
plusManualConfirmationTitle: '',
|
||||
plusManualConfirmationMessage: '',
|
||||
};
|
||||
await setState(clearManualConfirmationState);
|
||||
broadcastDataUpdate(clearManualConfirmationState);
|
||||
}
|
||||
|
||||
if (resumeWaiter) {
|
||||
resumeWaiter.reject(new Error(STOP_ERROR_MESSAGE));
|
||||
resumeWaiter = null;
|
||||
@@ -8995,6 +9071,15 @@ const plusCheckoutBillingExecutor = self.MultiPageBackgroundPlusCheckoutBilling?
|
||||
waitForTabCompleteUntilStopped,
|
||||
waitForTabUrlMatchUntilStopped,
|
||||
});
|
||||
const goPayManualConfirmExecutor = self.MultiPageBackgroundGoPayManualConfirm?.createGoPayManualConfirmExecutor({
|
||||
addLog,
|
||||
broadcastDataUpdate,
|
||||
chrome,
|
||||
getTabId,
|
||||
isTabAlive,
|
||||
registerTab,
|
||||
setState,
|
||||
});
|
||||
const payPalApproveExecutor = self.MultiPageBackgroundPayPalApprove?.createPayPalApproveExecutor({
|
||||
addLog,
|
||||
chrome,
|
||||
@@ -9045,6 +9130,7 @@ const stepExecutorsByKey = {
|
||||
'clear-login-cookies': () => step6Executor.executeStep6(),
|
||||
'plus-checkout-create': (state) => plusCheckoutCreateExecutor.executePlusCheckoutCreate(state),
|
||||
'plus-checkout-billing': (state) => plusCheckoutBillingExecutor.executePlusCheckoutBilling(state),
|
||||
'gopay-subscription-confirm': (state) => goPayManualConfirmExecutor.executeGoPayManualConfirm(state),
|
||||
'paypal-approve': (state) => payPalApproveExecutor.executePayPalApprove(state),
|
||||
'plus-checkout-return': (state) => plusReturnConfirmExecutor.executePlusReturnConfirm(state),
|
||||
'oauth-login': (state) => step7Executor.executeStep7(state),
|
||||
@@ -9070,6 +9156,7 @@ const messageRouter = self.MultiPageBackgroundMessageRouter?.createMessageRouter
|
||||
clearStopRequest,
|
||||
closeLocalhostCallbackTabs,
|
||||
closeTabsByUrlPrefix,
|
||||
completeStepFromBackground,
|
||||
deleteHotmailAccount,
|
||||
deleteHotmailAccounts,
|
||||
deleteIcloudAlias,
|
||||
@@ -9180,10 +9267,16 @@ function buildStepRegistry(definitions = []) {
|
||||
}
|
||||
|
||||
const normalStepRegistry = buildStepRegistry(NORMAL_STEP_DEFINITIONS);
|
||||
const plusStepRegistry = buildStepRegistry(PLUS_STEP_DEFINITIONS);
|
||||
const plusPayPalStepRegistry = buildStepRegistry(PLUS_PAYPAL_STEP_DEFINITIONS);
|
||||
const plusGoPayStepRegistry = buildStepRegistry(PLUS_GOPAY_STEP_DEFINITIONS);
|
||||
|
||||
function getStepRegistryForState(state = {}) {
|
||||
return isPlusModeState(state) ? plusStepRegistry : normalStepRegistry;
|
||||
if (!isPlusModeState(state)) {
|
||||
return normalStepRegistry;
|
||||
}
|
||||
return normalizePlusPaymentMethod(state?.plusPaymentMethod) === 'gopay'
|
||||
? plusGoPayStepRegistry
|
||||
: plusPayPalStepRegistry;
|
||||
}
|
||||
|
||||
async function requestOAuthUrlFromPanel(state, options = {}) {
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
clearStopRequest,
|
||||
closeLocalhostCallbackTabs,
|
||||
closeTabsByUrlPrefix,
|
||||
completeStepFromBackground,
|
||||
deleteHotmailAccount,
|
||||
deleteHotmailAccounts,
|
||||
deleteIcloudAlias,
|
||||
@@ -418,6 +419,54 @@
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
case 'RESOLVE_PLUS_MANUAL_CONFIRMATION': {
|
||||
const currentState = await getState();
|
||||
const step = Number(message.payload?.step) || Number(currentState?.plusManualConfirmationStep) || 0;
|
||||
const confirmed = Boolean(message.payload?.confirmed);
|
||||
const requestId = String(message.payload?.requestId || '').trim();
|
||||
const currentRequestId = String(currentState?.plusManualConfirmationRequestId || '').trim();
|
||||
if (!currentState?.plusManualConfirmationPending) {
|
||||
return { ok: true, ignored: true };
|
||||
}
|
||||
if (requestId && currentRequestId && requestId !== currentRequestId) {
|
||||
return { ok: true, ignored: true };
|
||||
}
|
||||
|
||||
const clearManualConfirmationState = {
|
||||
plusManualConfirmationPending: false,
|
||||
plusManualConfirmationRequestId: '',
|
||||
plusManualConfirmationStep: 0,
|
||||
plusManualConfirmationMethod: '',
|
||||
plusManualConfirmationTitle: '',
|
||||
plusManualConfirmationMessage: '',
|
||||
};
|
||||
await setState(clearManualConfirmationState);
|
||||
if (typeof broadcastDataUpdate === 'function') {
|
||||
broadcastDataUpdate(clearManualConfirmationState);
|
||||
}
|
||||
|
||||
if (confirmed) {
|
||||
const methodLabel = String(currentState?.plusManualConfirmationMethod || '').trim().toLowerCase() === 'gopay'
|
||||
? 'GoPay'
|
||||
: '手动';
|
||||
await addLog(`步骤 ${step}:已确认${methodLabel}订阅完成,准备继续下一步。`, 'ok');
|
||||
await completeStepFromBackground(step, {
|
||||
plusManualConfirmationMethod: currentState?.plusManualConfirmationMethod || '',
|
||||
plusManualConfirmedAt: Date.now(),
|
||||
});
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
const cancelMessage = String(currentState?.plusManualConfirmationMethod || '').trim().toLowerCase() === 'gopay'
|
||||
? '已取消 GoPay 订阅确认'
|
||||
: '已取消当前手动确认';
|
||||
await setStepStatus(step, 'failed');
|
||||
await addLog(`步骤 ${step}:${cancelMessage}。`, 'warn');
|
||||
await appendManualAccountRunRecordIfNeeded(`step${step}_failed`, null, cancelMessage);
|
||||
notifyStepError(step, cancelMessage);
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
case 'GET_STATE': {
|
||||
return await getState();
|
||||
}
|
||||
@@ -649,12 +698,19 @@
|
||||
const sessionUpdates = buildLuckmailSessionSettingsPayload(message.payload || {});
|
||||
const modeChanged = Object.prototype.hasOwnProperty.call(updates, 'plusModeEnabled')
|
||||
&& Boolean(currentState?.plusModeEnabled) !== Boolean(updates.plusModeEnabled);
|
||||
const plusPaymentChanged = Object.prototype.hasOwnProperty.call(updates, 'plusPaymentMethod')
|
||||
&& String(currentState?.plusPaymentMethod || 'paypal').trim().toLowerCase()
|
||||
!== String(updates.plusPaymentMethod || 'paypal').trim().toLowerCase();
|
||||
const nextPlusModeEnabled = Object.prototype.hasOwnProperty.call(updates, 'plusModeEnabled')
|
||||
? Boolean(updates.plusModeEnabled)
|
||||
: Boolean(currentState?.plusModeEnabled);
|
||||
const stepModeChanged = modeChanged || (nextPlusModeEnabled && plusPaymentChanged);
|
||||
await setPersistentSettings(updates);
|
||||
const stateUpdates = {
|
||||
...updates,
|
||||
...sessionUpdates,
|
||||
};
|
||||
if (modeChanged && typeof getStepIdsForState === 'function') {
|
||||
if (stepModeChanged && typeof getStepIdsForState === 'function') {
|
||||
const nextStateForSteps = { ...currentState, ...stateUpdates };
|
||||
stateUpdates.stepStatuses = Object.fromEntries(
|
||||
getStepIdsForState(nextStateForSteps).map((stepId) => [stepId, 'pending'])
|
||||
@@ -705,6 +761,13 @@
|
||||
: 'Plus 模式已关闭,已恢复普通注册授权步骤。',
|
||||
'info'
|
||||
);
|
||||
} else if (plusPaymentChanged && nextPlusModeEnabled) {
|
||||
const selectedPlusPaymentMethod = String(
|
||||
stateUpdates.plusPaymentMethod ?? currentState?.plusPaymentMethod ?? 'paypal'
|
||||
).trim().toLowerCase() === 'gopay'
|
||||
? 'GoPay'
|
||||
: 'PayPal';
|
||||
await addLog(`Plus 鏀粯鏂瑰紡宸插垏鎹负 ${selectedPlusPaymentMethod}锛屽凡鏇存柊瀵瑰簲鐨?Plus 姝ラ銆?`, 'info');
|
||||
}
|
||||
return { ok: true, state: await getState(), proxyRouting };
|
||||
}
|
||||
|
||||
@@ -18,11 +18,21 @@
|
||||
waitForTabCompleteUntilStopped,
|
||||
} = deps;
|
||||
|
||||
function normalizePlusPaymentMethod(value = '') {
|
||||
return String(value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal';
|
||||
}
|
||||
|
||||
function getCheckoutModeLabel(state = {}) {
|
||||
return normalizePlusPaymentMethod(state?.plusPaymentMethod) === 'gopay'
|
||||
? 'GoPay 订阅页'
|
||||
: 'Plus Checkout';
|
||||
}
|
||||
|
||||
async function openFreshChatGptTabForCheckoutCreate() {
|
||||
const tab = await chrome.tabs.create({ url: PLUS_CHECKOUT_ENTRY_URL, active: true });
|
||||
const tabId = Number(tab?.id);
|
||||
if (!Number.isInteger(tabId)) {
|
||||
throw new Error('步骤 6:打开 ChatGPT 页面失败,未获取到有效标签页 ID。');
|
||||
throw new Error('步骤 6:打开 ChatGPT 页面失败,无法创建订阅页。');
|
||||
}
|
||||
if (typeof registerTab === 'function') {
|
||||
await registerTab(PLUS_CHECKOUT_SOURCE, tabId);
|
||||
@@ -30,8 +40,9 @@
|
||||
return tabId;
|
||||
}
|
||||
|
||||
async function executePlusCheckoutCreate() {
|
||||
await addLog('步骤 6:正在新打开 ChatGPT 会话页,准备创建 Plus Checkout...', 'info');
|
||||
async function executePlusCheckoutCreate(state = {}) {
|
||||
const checkoutModeLabel = getCheckoutModeLabel(state);
|
||||
await addLog(`步骤 6:正在打开新的 ChatGPT 会话,准备创建${checkoutModeLabel}...`, 'info');
|
||||
const tabId = await openFreshChatGptTabForCheckoutCreate();
|
||||
|
||||
await waitForTabCompleteUntilStopped(tabId);
|
||||
@@ -39,30 +50,32 @@
|
||||
await ensureContentScriptReadyOnTabUntilStopped(PLUS_CHECKOUT_SOURCE, tabId, {
|
||||
inject: PLUS_CHECKOUT_INJECT_FILES,
|
||||
injectSource: PLUS_CHECKOUT_SOURCE,
|
||||
logMessage: '步骤 6:ChatGPT 页面仍在加载,等待 Plus Checkout 脚本就绪...',
|
||||
logMessage: '步骤 6:正在等待 ChatGPT 页面完成加载,再继续创建订阅页...',
|
||||
});
|
||||
|
||||
const result = await sendTabMessageUntilStopped(tabId, PLUS_CHECKOUT_SOURCE, {
|
||||
type: 'CREATE_PLUS_CHECKOUT',
|
||||
source: 'background',
|
||||
payload: {},
|
||||
payload: {
|
||||
paymentMethod: normalizePlusPaymentMethod(state?.plusPaymentMethod),
|
||||
},
|
||||
});
|
||||
|
||||
if (result?.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
if (!result?.checkoutUrl) {
|
||||
throw new Error('步骤 6:Plus Checkout 创建后未返回支付链接。');
|
||||
throw new Error(`步骤 6:${checkoutModeLabel}未返回可用的订阅链接。`);
|
||||
}
|
||||
|
||||
await addLog('步骤 6:Plus Checkout 已创建,正在打开支付页面...', 'ok');
|
||||
await addLog(`步骤 6:${checkoutModeLabel}已创建,正在打开订阅页面...`, 'ok');
|
||||
await chrome.tabs.update(tabId, { url: result.checkoutUrl, active: true });
|
||||
await waitForTabCompleteUntilStopped(tabId);
|
||||
await sleepWithStop(1000);
|
||||
await ensureContentScriptReadyOnTabUntilStopped(PLUS_CHECKOUT_SOURCE, tabId, {
|
||||
inject: PLUS_CHECKOUT_INJECT_FILES,
|
||||
injectSource: PLUS_CHECKOUT_SOURCE,
|
||||
logMessage: '步骤 6:Checkout 页面仍在加载,等待页面脚本就绪...',
|
||||
logMessage: '步骤 6:正在等待订阅页面完成加载...',
|
||||
});
|
||||
|
||||
await setState({
|
||||
@@ -72,7 +85,7 @@
|
||||
plusCheckoutCurrency: result.currency || 'EUR',
|
||||
});
|
||||
|
||||
await addLog('步骤 6:Plus Checkout 页面已就绪,准备继续下一步。', 'info');
|
||||
await addLog(`步骤 6:${checkoutModeLabel}已就绪。`, 'info');
|
||||
|
||||
await completeStepFromBackground(6, {
|
||||
plusCheckoutCountry: result.country || 'DE',
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
(function attachBackgroundGoPayManualConfirm(root, factory) {
|
||||
root.MultiPageBackgroundGoPayManualConfirm = factory();
|
||||
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundGoPayManualConfirmModule() {
|
||||
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
|
||||
const DEFAULT_CONFIRM_TITLE = 'GoPay 订阅确认';
|
||||
const DEFAULT_CONFIRM_MESSAGE = 'GoPay 订阅页已打开。请先手动完成订阅,完成后确认继续 OAuth 登录。';
|
||||
|
||||
function createGoPayManualConfirmExecutor(deps = {}) {
|
||||
const {
|
||||
addLog,
|
||||
broadcastDataUpdate,
|
||||
chrome,
|
||||
getTabId,
|
||||
isTabAlive,
|
||||
registerTab,
|
||||
setState,
|
||||
} = deps;
|
||||
|
||||
function buildRequestId() {
|
||||
return `gopay-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
||||
}
|
||||
|
||||
async function resolveCheckoutTabId(state = {}) {
|
||||
const registeredTabId = typeof getTabId === 'function'
|
||||
? await getTabId(PLUS_CHECKOUT_SOURCE)
|
||||
: null;
|
||||
if (registeredTabId && typeof isTabAlive === 'function' && await isTabAlive(PLUS_CHECKOUT_SOURCE)) {
|
||||
return Number(registeredTabId) || 0;
|
||||
}
|
||||
|
||||
const storedTabId = Number(state?.plusCheckoutTabId) || 0;
|
||||
if (storedTabId && chrome?.tabs?.get) {
|
||||
const tab = await chrome.tabs.get(storedTabId).catch(() => null);
|
||||
if (tab?.id) {
|
||||
if (typeof registerTab === 'function') {
|
||||
await registerTab(PLUS_CHECKOUT_SOURCE, tab.id);
|
||||
}
|
||||
return tab.id;
|
||||
}
|
||||
}
|
||||
|
||||
const checkoutUrl = String(state?.plusCheckoutUrl || '').trim();
|
||||
if (!checkoutUrl) {
|
||||
throw new Error('步骤 7:未检测到 GoPay 订阅页,请先执行步骤 6。');
|
||||
}
|
||||
|
||||
if (!chrome?.tabs?.create) {
|
||||
throw new Error('步骤 7:无法自动重新打开 GoPay 订阅页。');
|
||||
}
|
||||
|
||||
const tab = await chrome.tabs.create({ url: checkoutUrl, active: true });
|
||||
const tabId = Number(tab?.id) || 0;
|
||||
if (!tabId) {
|
||||
throw new Error('步骤 7:重新打开 GoPay 订阅页失败。');
|
||||
}
|
||||
if (typeof registerTab === 'function') {
|
||||
await registerTab(PLUS_CHECKOUT_SOURCE, tabId);
|
||||
}
|
||||
return tabId;
|
||||
}
|
||||
|
||||
async function executeGoPayManualConfirm(state = {}) {
|
||||
const tabId = await resolveCheckoutTabId(state);
|
||||
if (chrome?.tabs?.update && tabId) {
|
||||
await chrome.tabs.update(tabId, { active: true }).catch(() => {});
|
||||
}
|
||||
|
||||
const payload = {
|
||||
plusCheckoutTabId: tabId,
|
||||
plusManualConfirmationPending: true,
|
||||
plusManualConfirmationRequestId: buildRequestId(),
|
||||
plusManualConfirmationStep: 7,
|
||||
plusManualConfirmationMethod: 'gopay',
|
||||
plusManualConfirmationTitle: DEFAULT_CONFIRM_TITLE,
|
||||
plusManualConfirmationMessage: DEFAULT_CONFIRM_MESSAGE,
|
||||
};
|
||||
|
||||
await setState(payload);
|
||||
if (typeof broadcastDataUpdate === 'function') {
|
||||
broadcastDataUpdate(payload);
|
||||
}
|
||||
|
||||
await addLog('步骤 7:正在等待手动完成 GoPay 订阅,确认后继续 OAuth 登录。', 'info');
|
||||
}
|
||||
|
||||
return {
|
||||
executeGoPayManualConfirm,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
createGoPayManualConfirmExecutor,
|
||||
};
|
||||
});
|
||||
+48
-11
@@ -5,19 +5,33 @@ console.log('[MultiPage:plus-checkout] Content script loaded on', location.href)
|
||||
window.__MULTIPAGE_PLUS_CHECKOUT_READY__ = true;
|
||||
|
||||
const PLUS_CHECKOUT_LISTENER_SENTINEL = 'data-multipage-plus-checkout-listener';
|
||||
const PLUS_CHECKOUT_PAYLOAD = {
|
||||
const PLUS_CHECKOUT_BASE_PAYLOAD = {
|
||||
entry_point: 'all_plans_pricing_modal',
|
||||
plan_name: 'chatgptplusplan',
|
||||
billing_details: {
|
||||
country: 'DE',
|
||||
currency: 'EUR',
|
||||
},
|
||||
checkout_ui_mode: 'custom',
|
||||
promo_campaign: {
|
||||
promo_campaign_id: 'plus-1-month-free',
|
||||
is_coupon_from_query_param: false,
|
||||
},
|
||||
};
|
||||
const PLUS_CHECKOUT_CONFIGS = {
|
||||
paypal: {
|
||||
billing_details: {
|
||||
country: 'DE',
|
||||
currency: 'EUR',
|
||||
},
|
||||
checkoutUrlPrefix: 'https://chatgpt.com/checkout/openai_ie/',
|
||||
paymentLabel: 'PayPal',
|
||||
},
|
||||
gopay: {
|
||||
billing_details: {
|
||||
country: 'ID',
|
||||
currency: 'IDR',
|
||||
},
|
||||
checkoutUrlPrefix: 'https://chatgpt.com/checkout/openai_llc/',
|
||||
paymentLabel: 'GoPay',
|
||||
},
|
||||
};
|
||||
const PAYPAL_DIAGNOSTIC_LOG_INTERVAL_MS = 5000;
|
||||
|
||||
if (document.documentElement.getAttribute(PLUS_CHECKOUT_LISTENER_SENTINEL) !== '1') {
|
||||
@@ -55,7 +69,7 @@ if (document.documentElement.getAttribute(PLUS_CHECKOUT_LISTENER_SENTINEL) !== '
|
||||
async function handlePlusCheckoutCommand(message) {
|
||||
switch (message.type) {
|
||||
case 'CREATE_PLUS_CHECKOUT':
|
||||
return createPlusCheckoutSession();
|
||||
return createPlusCheckoutSession(message.payload || {});
|
||||
case 'FILL_PLUS_BILLING_AND_SUBMIT':
|
||||
return fillPlusBillingAndSubmit(message.payload || {});
|
||||
case 'PLUS_CHECKOUT_SELECT_PAYPAL':
|
||||
@@ -112,6 +126,27 @@ function normalizeText(text = '') {
|
||||
return String(text || '').replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
|
||||
function normalizePlusPaymentMethod(value = '') {
|
||||
return String(value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal';
|
||||
}
|
||||
|
||||
function buildPlusCheckoutConfig(payload = {}) {
|
||||
const paymentMethod = normalizePlusPaymentMethod(payload.paymentMethod);
|
||||
const config = PLUS_CHECKOUT_CONFIGS[paymentMethod] || PLUS_CHECKOUT_CONFIGS.paypal;
|
||||
return {
|
||||
paymentMethod,
|
||||
paymentLabel: config.paymentLabel,
|
||||
checkoutUrlPrefix: config.checkoutUrlPrefix,
|
||||
checkoutPayload: {
|
||||
...PLUS_CHECKOUT_BASE_PAYLOAD,
|
||||
billing_details: {
|
||||
country: config.billing_details.country,
|
||||
currency: config.billing_details.currency,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function getActionText(el) {
|
||||
return normalizeText([
|
||||
el?.textContent,
|
||||
@@ -400,8 +435,9 @@ function writePayPalDiagnostics(reason, level = 'info') {
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
async function createPlusCheckoutSession() {
|
||||
async function createPlusCheckoutSession(payload = {}) {
|
||||
await waitForDocumentComplete();
|
||||
const checkoutConfig = buildPlusCheckoutConfig(payload);
|
||||
log('Plus:正在读取 ChatGPT 登录会话...');
|
||||
|
||||
const sessionResponse = await fetch('/api/auth/session', {
|
||||
@@ -421,7 +457,7 @@ async function createPlusCheckoutSession() {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(PLUS_CHECKOUT_PAYLOAD),
|
||||
body: JSON.stringify(checkoutConfig.checkoutPayload),
|
||||
});
|
||||
|
||||
const data = await response.json().catch(() => ({}));
|
||||
@@ -431,9 +467,10 @@ async function createPlusCheckoutSession() {
|
||||
}
|
||||
|
||||
return {
|
||||
checkoutUrl: `https://chatgpt.com/checkout/openai_ie/${data.checkout_session_id}`,
|
||||
country: PLUS_CHECKOUT_PAYLOAD.billing_details.country,
|
||||
currency: PLUS_CHECKOUT_PAYLOAD.billing_details.currency,
|
||||
checkoutUrl: `${checkoutConfig.checkoutUrlPrefix}${data.checkout_session_id}`,
|
||||
country: checkoutConfig.checkoutPayload.billing_details.country,
|
||||
currency: checkoutConfig.checkoutPayload.billing_details.currency,
|
||||
paymentMethod: checkoutConfig.paymentMethod,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
{ id: 10, order: 100, key: 'platform-verify', title: '平台回调验证' },
|
||||
];
|
||||
|
||||
const PLUS_STEP_DEFINITIONS = [
|
||||
const PLUS_PAYPAL_STEP_DEFINITIONS = [
|
||||
{ id: 1, order: 10, key: 'open-chatgpt', title: '打开 ChatGPT 官网' },
|
||||
{ id: 2, order: 20, key: 'submit-signup-email', title: '注册并输入邮箱' },
|
||||
{ id: 3, order: 30, key: 'fill-password', title: '填写密码并继续' },
|
||||
@@ -30,12 +30,35 @@
|
||||
{ id: 13, order: 130, key: 'platform-verify', title: '平台回调验证' },
|
||||
];
|
||||
|
||||
const PLUS_GOPAY_STEP_DEFINITIONS = [
|
||||
{ id: 1, order: 10, key: 'open-chatgpt', title: '打开 ChatGPT 官网' },
|
||||
{ id: 2, order: 20, key: 'submit-signup-email', title: '注册并输入邮箱' },
|
||||
{ id: 3, order: 30, key: 'fill-password', title: '填写密码并继续' },
|
||||
{ id: 4, order: 40, key: 'fetch-signup-code', title: '获取注册验证码' },
|
||||
{ id: 5, order: 50, key: 'fill-profile', title: '填写姓名和生日' },
|
||||
{ id: 6, order: 60, key: 'plus-checkout-create', title: '打开 GoPay 订阅页' },
|
||||
{ id: 7, order: 70, key: 'gopay-subscription-confirm', title: '等待 GoPay 订阅确认' },
|
||||
{ id: 10, order: 100, key: 'oauth-login', title: '刷新 OAuth 并登录' },
|
||||
{ id: 11, order: 110, key: 'fetch-login-code', title: '获取登录验证码' },
|
||||
{ id: 12, order: 120, key: 'confirm-oauth', title: '自动确认 OAuth' },
|
||||
{ id: 13, order: 130, key: 'platform-verify', title: '平台回调验证' },
|
||||
];
|
||||
|
||||
function isPlusModeEnabled(options = {}) {
|
||||
return Boolean(options?.plusModeEnabled || options?.plusMode);
|
||||
}
|
||||
|
||||
function normalizePlusPaymentMethod(value = '') {
|
||||
return String(value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal';
|
||||
}
|
||||
|
||||
function getModeStepDefinitions(options = {}) {
|
||||
return isPlusModeEnabled(options) ? PLUS_STEP_DEFINITIONS : NORMAL_STEP_DEFINITIONS;
|
||||
if (!isPlusModeEnabled(options)) {
|
||||
return NORMAL_STEP_DEFINITIONS;
|
||||
}
|
||||
return normalizePlusPaymentMethod(options?.plusPaymentMethod || options?.paymentMethod) === 'gopay'
|
||||
? PLUS_GOPAY_STEP_DEFINITIONS
|
||||
: PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
}
|
||||
|
||||
function cloneSteps(steps = []) {
|
||||
@@ -48,7 +71,11 @@
|
||||
|
||||
function getAllSteps() {
|
||||
const keyed = new Map();
|
||||
for (const step of [...NORMAL_STEP_DEFINITIONS, ...PLUS_STEP_DEFINITIONS]) {
|
||||
for (const step of [
|
||||
...NORMAL_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_STEP_DEFINITIONS,
|
||||
]) {
|
||||
keyed.set(`${step.id}:${step.key}`, step);
|
||||
}
|
||||
return cloneSteps(Array.from(keyed.values()).sort((left, right) => {
|
||||
@@ -80,12 +107,15 @@
|
||||
return {
|
||||
STEP_DEFINITIONS: NORMAL_STEP_DEFINITIONS,
|
||||
NORMAL_STEP_DEFINITIONS,
|
||||
PLUS_STEP_DEFINITIONS,
|
||||
PLUS_STEP_DEFINITIONS: PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
PLUS_GOPAY_STEP_DEFINITIONS,
|
||||
getAllSteps,
|
||||
getLastStepId,
|
||||
getStepById,
|
||||
getStepIds,
|
||||
getSteps,
|
||||
isPlusModeEnabled,
|
||||
normalizePlusPaymentMethod,
|
||||
};
|
||||
});
|
||||
|
||||
+144
-25
@@ -356,10 +356,11 @@ const btnAutoStartContinue = document.getElementById('btn-auto-start-continue');
|
||||
const autoHintText = document.querySelector('.auto-hint');
|
||||
const stepsList = document.querySelector('.steps-list');
|
||||
let currentPlusModeEnabled = false;
|
||||
let currentPlusPaymentMethod = 'paypal';
|
||||
let heroSmsCountrySelectionOrder = [];
|
||||
let heroSmsCountryMenuSearchKeyword = '';
|
||||
const heroSmsCountrySearchTextById = new Map();
|
||||
let stepDefinitions = getStepDefinitionsForMode(false);
|
||||
let stepDefinitions = getStepDefinitionsForMode(false, currentPlusPaymentMethod);
|
||||
let STEP_IDS = stepDefinitions.map((step) => Number(step.id)).filter(Number.isFinite);
|
||||
let STEP_DEFAULT_STATUSES = Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending']));
|
||||
let SKIPPABLE_STEPS = new Set(STEP_IDS);
|
||||
@@ -453,8 +454,22 @@ const AUTO_RUN_PLUS_RISK_PROMPT_DISMISSED_STORAGE_KEY = 'multipage-auto-run-plus
|
||||
const PLUS_CONTRIBUTION_PROMPT_LEDGER_STORAGE_KEY = 'multipage-plus-contribution-prompt-ledger';
|
||||
const PHONE_VERIFICATION_SECTION_EXPANDED_STORAGE_KEY = 'multipage-phone-verification-section-expanded';
|
||||
|
||||
function getStepDefinitionsForMode(plusModeEnabled = false) {
|
||||
return (window.MultiPageStepDefinitions?.getSteps?.({ plusModeEnabled }) || [])
|
||||
function normalizePlusPaymentMethod(value = '') {
|
||||
return String(value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal';
|
||||
}
|
||||
|
||||
function getSelectedPlusPaymentMethod() {
|
||||
if (typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod) {
|
||||
return normalizePlusPaymentMethod(selectPlusPaymentMethod.value);
|
||||
}
|
||||
return normalizePlusPaymentMethod(latestState?.plusPaymentMethod || currentPlusPaymentMethod);
|
||||
}
|
||||
|
||||
function getStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethod = 'paypal') {
|
||||
return (window.MultiPageStepDefinitions?.getSteps?.({
|
||||
plusModeEnabled,
|
||||
plusPaymentMethod: normalizePlusPaymentMethod(plusPaymentMethod),
|
||||
}) || [])
|
||||
.sort((left, right) => {
|
||||
const leftOrder = Number.isFinite(left.order) ? left.order : left.id;
|
||||
const rightOrder = Number.isFinite(right.order) ? right.order : right.id;
|
||||
@@ -463,9 +478,10 @@ function getStepDefinitionsForMode(plusModeEnabled = false) {
|
||||
});
|
||||
}
|
||||
|
||||
function rebuildStepDefinitionState(plusModeEnabled = false) {
|
||||
function rebuildStepDefinitionState(plusModeEnabled = false, plusPaymentMethod = 'paypal') {
|
||||
currentPlusModeEnabled = Boolean(plusModeEnabled);
|
||||
stepDefinitions = getStepDefinitionsForMode(currentPlusModeEnabled);
|
||||
currentPlusPaymentMethod = normalizePlusPaymentMethod(plusPaymentMethod);
|
||||
stepDefinitions = getStepDefinitionsForMode(currentPlusModeEnabled, currentPlusPaymentMethod);
|
||||
STEP_IDS = stepDefinitions.map((step) => Number(step.id)).filter(Number.isFinite);
|
||||
STEP_DEFAULT_STATUSES = Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending']));
|
||||
SKIPPABLE_STEPS = new Set(STEP_IDS);
|
||||
@@ -758,6 +774,8 @@ let cloudflareTempEmailDomainEditMode = false;
|
||||
let modalChoiceResolver = null;
|
||||
let currentModalActions = [];
|
||||
let modalResultBuilder = null;
|
||||
let activePlusManualConfirmationRequestId = '';
|
||||
let plusManualConfirmationDialogInFlight = false;
|
||||
let scheduledCountdownTimer = null;
|
||||
let configMenuOpen = false;
|
||||
let configActionInFlight = false;
|
||||
@@ -1194,6 +1212,98 @@ async function openConfirmModalWithOption({
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Override the initial GoPay confirmation helpers so the dialog can
|
||||
// recover after an accidental close instead of silently leaving step 7 hanging.
|
||||
async function openPlusManualConfirmationDialog(options = {}) {
|
||||
const method = String(options.method || '').trim().toLowerCase();
|
||||
const title = String(options.title || '').trim() || (method === 'gopay' ? 'GoPay 订阅确认' : '手动确认');
|
||||
const message = String(options.message || '').trim()
|
||||
|| (method === 'gopay'
|
||||
? '请在当前订阅页中手动完成 GoPay 订阅,完成后点击“我已完成订阅”继续。'
|
||||
: '请先在页面中完成当前手动操作,完成后点击确认继续。');
|
||||
return openActionModal({
|
||||
title,
|
||||
message,
|
||||
actions: [
|
||||
{ id: 'cancel', label: '取消等待', variant: 'btn-ghost' },
|
||||
{ id: 'confirm', label: '我已完成订阅', variant: 'btn-primary' },
|
||||
],
|
||||
alert: method === 'gopay'
|
||||
? { text: '确认后流程会直接继续到 Plus 模式第 10 步 OAuth 登录。', tone: 'info' }
|
||||
: null,
|
||||
});
|
||||
}
|
||||
|
||||
async function syncPlusManualConfirmationDialog() {
|
||||
const requestId = String(latestState?.plusManualConfirmationRequestId || '').trim();
|
||||
const pending = Boolean(latestState?.plusManualConfirmationPending);
|
||||
if (!pending || !requestId || plusManualConfirmationDialogInFlight || activePlusManualConfirmationRequestId === requestId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const step = Number(latestState?.plusManualConfirmationStep) || 0;
|
||||
const method = String(latestState?.plusManualConfirmationMethod || '').trim().toLowerCase();
|
||||
const title = latestState?.plusManualConfirmationTitle;
|
||||
const message = latestState?.plusManualConfirmationMessage;
|
||||
activePlusManualConfirmationRequestId = requestId;
|
||||
plusManualConfirmationDialogInFlight = true;
|
||||
let shouldReopenDialog = false;
|
||||
|
||||
try {
|
||||
const choice = await openPlusManualConfirmationDialog({
|
||||
method,
|
||||
title,
|
||||
message,
|
||||
});
|
||||
const currentRequestId = String(latestState?.plusManualConfirmationRequestId || '').trim();
|
||||
const stillPending = Boolean(latestState?.plusManualConfirmationPending);
|
||||
if (!stillPending || currentRequestId !== requestId) {
|
||||
return;
|
||||
}
|
||||
if (choice == null) {
|
||||
shouldReopenDialog = true;
|
||||
showToast('当前订阅确认仍在等待中,将重新弹出确认窗口。', 'info', 1800);
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = choice === 'confirm';
|
||||
const response = await chrome.runtime.sendMessage({
|
||||
type: 'RESOLVE_PLUS_MANUAL_CONFIRMATION',
|
||||
source: 'sidepanel',
|
||||
payload: {
|
||||
step,
|
||||
requestId,
|
||||
confirmed,
|
||||
},
|
||||
});
|
||||
if (response?.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
if (confirmed) {
|
||||
showToast(method === 'gopay' ? 'GoPay 订阅已确认,正在继续 OAuth 登录...' : '已确认,流程继续执行中...', 'info', 2200);
|
||||
} else {
|
||||
showToast(method === 'gopay' ? '已取消 GoPay 订阅等待。' : '已取消当前手动确认。', 'warn', 2200);
|
||||
}
|
||||
} catch (error) {
|
||||
showToast(error?.message || String(error || '未知错误'), 'error');
|
||||
} finally {
|
||||
if (activePlusManualConfirmationRequestId === requestId) {
|
||||
activePlusManualConfirmationRequestId = '';
|
||||
}
|
||||
plusManualConfirmationDialogInFlight = false;
|
||||
if (
|
||||
shouldReopenDialog
|
||||
&& latestState?.plusManualConfirmationPending
|
||||
&& String(latestState?.plusManualConfirmationRequestId || '').trim() === requestId
|
||||
) {
|
||||
setTimeout(() => {
|
||||
void syncPlusManualConfirmationDialog();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isPromptDismissed(storageKey) {
|
||||
return localStorage.getItem(storageKey) === '1';
|
||||
}
|
||||
@@ -2447,9 +2557,13 @@ function collectSettingsPayload() {
|
||||
const currentPayPalAccount = typeof getCurrentPayPalAccount === 'function'
|
||||
? getCurrentPayPalAccount(latestState)
|
||||
: payPalAccounts.find((account) => account?.id === String(latestState?.currentPayPalAccountId || '').trim()) || null;
|
||||
const plusPaymentMethod = typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod
|
||||
? (String(selectPlusPaymentMethod.value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal')
|
||||
: (String(latestState?.plusPaymentMethod || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal');
|
||||
const plusPaymentMethod = typeof getSelectedPlusPaymentMethod === 'function'
|
||||
? getSelectedPlusPaymentMethod()
|
||||
: (String(
|
||||
(typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod
|
||||
? selectPlusPaymentMethod.value
|
||||
: latestState?.plusPaymentMethod) || ''
|
||||
).trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal');
|
||||
return {
|
||||
...(contributionModeEnabled ? {} : {
|
||||
panelMode: selectPanelMode.value,
|
||||
@@ -3593,9 +3707,7 @@ function updatePlusModeUI() {
|
||||
const enabled = typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled
|
||||
? Boolean(inputPlusModeEnabled.checked)
|
||||
: false;
|
||||
const paymentMethod = typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod
|
||||
? (String(selectPlusPaymentMethod.value || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal')
|
||||
: (String(latestState?.plusPaymentMethod || '').trim().toLowerCase() === 'gopay' ? 'gopay' : 'paypal');
|
||||
const paymentMethod = getSelectedPlusPaymentMethod();
|
||||
if (typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod) {
|
||||
selectPlusPaymentMethod.value = paymentMethod;
|
||||
selectPlusPaymentMethod.style.display = enabled ? '' : 'none';
|
||||
@@ -3842,14 +3954,17 @@ function renderStepsList() {
|
||||
updateButtonStates();
|
||||
}
|
||||
|
||||
function syncStepDefinitionsForMode(plusModeEnabled = false, options = {}) {
|
||||
function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethod = 'paypal', options = {}) {
|
||||
const nextPlusModeEnabled = Boolean(plusModeEnabled);
|
||||
const shouldRender = Boolean(options.render) || nextPlusModeEnabled !== currentPlusModeEnabled;
|
||||
const nextPlusPaymentMethod = normalizePlusPaymentMethod(plusPaymentMethod);
|
||||
const shouldRender = Boolean(options.render)
|
||||
|| nextPlusModeEnabled !== currentPlusModeEnabled
|
||||
|| nextPlusPaymentMethod !== currentPlusPaymentMethod;
|
||||
if (!shouldRender) {
|
||||
return;
|
||||
}
|
||||
|
||||
rebuildStepDefinitionState(nextPlusModeEnabled);
|
||||
rebuildStepDefinitionState(nextPlusModeEnabled, nextPlusPaymentMethod);
|
||||
renderStepsList();
|
||||
}
|
||||
|
||||
@@ -3859,7 +3974,7 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, options = {}) {
|
||||
|
||||
function applySettingsState(state) {
|
||||
if (typeof syncStepDefinitionsForMode === 'function') {
|
||||
syncStepDefinitionsForMode(Boolean(state?.plusModeEnabled));
|
||||
syncStepDefinitionsForMode(Boolean(state?.plusModeEnabled), state?.plusPaymentMethod);
|
||||
}
|
||||
const fallbackIpProxyService = '711proxy';
|
||||
const fallbackIpProxyMode = 'account';
|
||||
@@ -3909,9 +4024,7 @@ function applySettingsState(state) {
|
||||
inputPlusModeEnabled.checked = Boolean(state?.plusModeEnabled);
|
||||
}
|
||||
if (typeof selectPlusPaymentMethod !== 'undefined' && selectPlusPaymentMethod) {
|
||||
selectPlusPaymentMethod.value = String(state?.plusPaymentMethod || '').trim().toLowerCase() === 'gopay'
|
||||
? 'gopay'
|
||||
: 'paypal';
|
||||
selectPlusPaymentMethod.value = normalizePlusPaymentMethod(state?.plusPaymentMethod);
|
||||
}
|
||||
inputVpsUrl.value = state?.vpsUrl || '';
|
||||
inputVpsPassword.value = state?.vpsPassword || '';
|
||||
@@ -4182,6 +4295,9 @@ function applySettingsState(state) {
|
||||
queueLuckmailPurchaseRefresh();
|
||||
}
|
||||
updateButtonStates();
|
||||
if (typeof syncPlusManualConfirmationDialog === 'function') {
|
||||
void syncPlusManualConfirmationDialog();
|
||||
}
|
||||
}
|
||||
|
||||
async function restoreState() {
|
||||
@@ -6801,16 +6917,15 @@ inputPassword.addEventListener('blur', () => {
|
||||
|
||||
inputPlusModeEnabled?.addEventListener('change', () => {
|
||||
updatePlusModeUI();
|
||||
syncStepDefinitionsForMode(Boolean(inputPlusModeEnabled.checked), { render: true });
|
||||
syncStepDefinitionsForMode(Boolean(inputPlusModeEnabled.checked), getSelectedPlusPaymentMethod(), { render: true });
|
||||
markSettingsDirty(true);
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
});
|
||||
|
||||
selectPlusPaymentMethod?.addEventListener('change', () => {
|
||||
selectPlusPaymentMethod.value = String(selectPlusPaymentMethod.value || '').trim().toLowerCase() === 'gopay'
|
||||
? 'gopay'
|
||||
: 'paypal';
|
||||
selectPlusPaymentMethod.value = normalizePlusPaymentMethod(selectPlusPaymentMethod.value);
|
||||
updatePlusModeUI();
|
||||
syncStepDefinitionsForMode(Boolean(inputPlusModeEnabled?.checked), selectPlusPaymentMethod.value, { render: true });
|
||||
markSettingsDirty(true);
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
});
|
||||
@@ -8149,11 +8264,14 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
inputPlusModeEnabled.checked = Boolean(message.payload.plusModeEnabled);
|
||||
}
|
||||
if (message.payload.plusPaymentMethod !== undefined && selectPlusPaymentMethod) {
|
||||
selectPlusPaymentMethod.value = String(message.payload.plusPaymentMethod || '').trim().toLowerCase() === 'gopay'
|
||||
? 'gopay'
|
||||
: 'paypal';
|
||||
selectPlusPaymentMethod.value = normalizePlusPaymentMethod(message.payload.plusPaymentMethod);
|
||||
}
|
||||
if (message.payload.plusModeEnabled !== undefined || message.payload.plusPaymentMethod !== undefined) {
|
||||
syncStepDefinitionsForMode(
|
||||
Boolean(latestState?.plusModeEnabled),
|
||||
latestState?.plusPaymentMethod,
|
||||
{ render: true }
|
||||
);
|
||||
updatePlusModeUI();
|
||||
}
|
||||
if (message.payload.currentHotmailAccountId !== undefined || message.payload.hotmailAccounts !== undefined) {
|
||||
@@ -8343,6 +8461,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
}
|
||||
updateAccountRunHistorySettingsUI();
|
||||
renderContributionMode();
|
||||
void syncPlusManualConfirmationDialog();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +123,9 @@ return {
|
||||
|
||||
assert.equal(api.normalizePersistentSettingValue('accountRunHistoryTextEnabled', 1), true);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneVerificationEnabled', 1), true);
|
||||
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'gopay'), 'gopay');
|
||||
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'paypal'), 'paypal');
|
||||
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'unknown'), 'paypal');
|
||||
assert.equal(api.normalizePersistentSettingValue('verificationResendCount', '7'), 7);
|
||||
assert.equal(api.normalizePersistentSettingValue('verificationResendCount', '-1'), 0);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneVerificationReplacementLimit', '9'), 9);
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
|
||||
const source = fs.readFileSync('background/steps/gopay-manual-confirm.js', 'utf8');
|
||||
const globalScope = {};
|
||||
const api = new Function('self', `${source}; return self.MultiPageBackgroundGoPayManualConfirm;`)(globalScope);
|
||||
|
||||
test('GoPay manual confirm executor publishes pending manual confirmation state for step 7', async () => {
|
||||
const stateUpdates = [];
|
||||
const broadcasts = [];
|
||||
const events = [];
|
||||
const executor = api.createGoPayManualConfirmExecutor({
|
||||
addLog: async (message, level = 'info') => {
|
||||
events.push({ type: 'log', message, level });
|
||||
},
|
||||
broadcastDataUpdate: (payload) => {
|
||||
broadcasts.push(payload);
|
||||
},
|
||||
chrome: {
|
||||
tabs: {
|
||||
update: async (tabId, payload) => {
|
||||
events.push({ type: 'tab-update', tabId, payload });
|
||||
},
|
||||
},
|
||||
},
|
||||
getTabId: async () => 42,
|
||||
isTabAlive: async () => true,
|
||||
registerTab: async () => {},
|
||||
setState: async (payload) => {
|
||||
stateUpdates.push(payload);
|
||||
},
|
||||
});
|
||||
|
||||
await executor.executeGoPayManualConfirm({ plusCheckoutTabId: 42, plusCheckoutUrl: 'https://chatgpt.com/checkout/openai_llc/session' });
|
||||
|
||||
assert.equal(stateUpdates.length, 1);
|
||||
assert.equal(stateUpdates[0].plusManualConfirmationPending, true);
|
||||
assert.equal(stateUpdates[0].plusManualConfirmationMethod, 'gopay');
|
||||
assert.equal(stateUpdates[0].plusManualConfirmationStep, 7);
|
||||
assert.match(String(stateUpdates[0].plusManualConfirmationRequestId || ''), /^gopay-/);
|
||||
assert.equal(broadcasts.length, 1);
|
||||
assert.equal(broadcasts[0].plusManualConfirmationPending, true);
|
||||
assert.deepStrictEqual(
|
||||
events.find((event) => event.type === 'tab-update'),
|
||||
{ type: 'tab-update', tabId: 42, payload: { active: true } }
|
||||
);
|
||||
});
|
||||
@@ -32,11 +32,14 @@ test('Plus checkout create does not wait 20 seconds after opening checkout page'
|
||||
registerTab: async (source, tabId) => {
|
||||
events.push({ type: 'register', source, tabId });
|
||||
},
|
||||
sendTabMessageUntilStopped: async () => ({
|
||||
checkoutUrl: 'https://checkout.stripe.com/c/pay/session',
|
||||
country: 'US',
|
||||
currency: 'USD',
|
||||
}),
|
||||
sendTabMessageUntilStopped: async (tabId, source, message) => {
|
||||
events.push({ type: 'tab-message', tabId, source, message });
|
||||
return {
|
||||
checkoutUrl: 'https://checkout.stripe.com/c/pay/session',
|
||||
country: 'US',
|
||||
currency: 'USD',
|
||||
};
|
||||
},
|
||||
setState: async (payload) => {
|
||||
events.push({ type: 'set-state', payload });
|
||||
},
|
||||
@@ -61,11 +64,45 @@ test('Plus checkout create does not wait 20 seconds after opening checkout page'
|
||||
|
||||
const sleepEvents = events.filter((event) => event.type === 'sleep');
|
||||
assert.deepStrictEqual(sleepEvents.map((event) => event.ms), [1000, 1000]);
|
||||
assert.deepStrictEqual(
|
||||
events.find((event) => event.type === 'tab-message')?.message?.payload,
|
||||
{ paymentMethod: 'paypal' }
|
||||
);
|
||||
|
||||
const completeIndex = events.findIndex((event) => event.type === 'complete');
|
||||
const readyLogIndex = events.findIndex((event) => event.type === 'log' && /Plus Checkout 页面已就绪/.test(event.message));
|
||||
const readyLogIndex = events.findIndex((event) => event.type === 'log' && /已就绪/.test(event.message));
|
||||
assert.ok(readyLogIndex > -1);
|
||||
assert.ok(completeIndex > readyLogIndex);
|
||||
assert.equal(events.some((event) => event.type === 'sleep' && event.ms === 20000), false);
|
||||
assert.equal(events.some((event) => event.type === 'log' && /固定等待 20 秒后继续下一步/.test(event.message)), false);
|
||||
});
|
||||
|
||||
test('GoPay plus checkout create forwards gopay payment method to the checkout content script', async () => {
|
||||
const events = [];
|
||||
const executor = api.createPlusCheckoutCreateExecutor({
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
create: async () => ({ id: 99 }),
|
||||
update: async () => {},
|
||||
},
|
||||
},
|
||||
completeStepFromBackground: async () => {},
|
||||
ensureContentScriptReadyOnTabUntilStopped: async () => {},
|
||||
registerTab: async () => {},
|
||||
sendTabMessageUntilStopped: async (_tabId, _source, message) => {
|
||||
events.push(message);
|
||||
return {
|
||||
checkoutUrl: 'https://chatgpt.com/checkout/openai_llc/test-session',
|
||||
country: 'ID',
|
||||
currency: 'IDR',
|
||||
};
|
||||
},
|
||||
setState: async () => {},
|
||||
sleepWithStop: async () => {},
|
||||
waitForTabCompleteUntilStopped: async () => {},
|
||||
});
|
||||
|
||||
await executor.executePlusCheckoutCreate({ plusPaymentMethod: 'gopay' });
|
||||
|
||||
assert.deepStrictEqual(events[0]?.payload, { paymentMethod: 'gopay' });
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
const api = new Function('self', `${source}; return self.MultiPageStepDefinitions;`)(globalScope);
|
||||
const steps = api.getSteps();
|
||||
const plusSteps = api.getSteps({ plusModeEnabled: true });
|
||||
const goPaySteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gopay' });
|
||||
|
||||
assert.equal(Array.isArray(steps), true);
|
||||
assert.equal(steps.length, 10);
|
||||
@@ -31,6 +32,9 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
'platform-verify',
|
||||
]
|
||||
);
|
||||
assert.equal(steps[0].title, '打开 ChatGPT 官网');
|
||||
assert.equal(steps[5].title, '清理登录 Cookies');
|
||||
|
||||
assert.deepStrictEqual(
|
||||
plusSteps.map((step) => step.key),
|
||||
[
|
||||
@@ -53,6 +57,29 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
assert.equal(plusSteps.some((step) => step.key === 'fetch-login-code'), true);
|
||||
assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
|
||||
assert.equal(api.getLastStepId({ plusModeEnabled: true }), 13);
|
||||
assert.equal(plusSteps[5].title, '创建 Plus Checkout');
|
||||
assert.equal(plusSteps[7].title, 'PayPal 登录与授权');
|
||||
|
||||
assert.deepStrictEqual(
|
||||
goPaySteps.map((step) => step.key),
|
||||
[
|
||||
'open-chatgpt',
|
||||
'submit-signup-email',
|
||||
'fill-password',
|
||||
'fetch-signup-code',
|
||||
'fill-profile',
|
||||
'plus-checkout-create',
|
||||
'gopay-subscription-confirm',
|
||||
'oauth-login',
|
||||
'fetch-login-code',
|
||||
'confirm-oauth',
|
||||
'platform-verify',
|
||||
]
|
||||
);
|
||||
assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), [1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13]);
|
||||
assert.equal(api.getLastStepId({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), 13);
|
||||
assert.equal(goPaySteps[5].title, '打开 GoPay 订阅页');
|
||||
assert.equal(goPaySteps[6].title, '等待 GoPay 订阅确认');
|
||||
});
|
||||
|
||||
test('sidepanel html loads shared step definitions before sidepanel bootstrap', () => {
|
||||
|
||||
Reference in New Issue
Block a user