Update GoPay helper API URL to new domain and enhance cookie cleanup functionality

This commit is contained in:
QLHazyCoder
2026-05-07 16:31:58 +08:00
parent 43982825e1
commit 428e23d66a
17 changed files with 364 additions and 84 deletions
+1 -1
View File
@@ -605,7 +605,7 @@ Cloudflare 模式下,插件不会再调用 Cloudflare API 创建路由。
这一步只负责等待注册完成后的页面状态稳定:
- 固定等待 20 秒
-清理 `chatgpt.com / openai.com` 相关 cookies
- 默认不清理 `chatgpt.com / openai.com` 相关 cookies;侧栏开启第六步 `清 Cookies` 后才会在等待结束后执行清理
- 等待完成后直接进入后续 OAuth 登录链路
+14 -6
View File
@@ -239,7 +239,7 @@ const SUB2API_STEP1_RESPONSE_TIMEOUT_MS = 90000;
const SUB2API_STEP9_RESPONSE_TIMEOUT_MS = 120000;
const DEFAULT_SUB2API_URL = 'https://sub2api.hisence.fun/admin/accounts';
const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
const DEFAULT_SUB2API_GROUP_NAME = 'codex';
const DEFAULT_SUB2API_PROXY_NAME = '';
const DEFAULT_SUB2API_ACCOUNT_PRIORITY = 1;
@@ -646,6 +646,7 @@ const PERSISTED_SETTING_DEFAULTS = {
autoRunDelayEnabled: false,
autoRunDelayMinutes: 30,
autoStepDelaySeconds: null,
step6CookieCleanupEnabled: false,
phoneVerificationEnabled: false,
freePhoneReuseEnabled: true,
freePhoneReuseAutoEnabled: true,
@@ -2304,11 +2305,15 @@ function normalizePersistentSettingValue(key, value) {
);
case 'gopayHelperApiUrl':
{
const legacyGpcHelperApiUrl = 'https://gpc.leftcode.xyz';
const defaultGpcHelperApiUrl = PERSISTED_SETTING_DEFAULTS.gopayHelperApiUrl
|| (typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined' ? DEFAULT_GPC_HELPER_API_URL : 'https://gpc.leftcode.xyz');
return self.GoPayUtils?.normalizeGpcHelperBaseUrl
|| (typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined' ? DEFAULT_GPC_HELPER_API_URL : 'https://gpc.qlhazycoder.top');
const normalizedGpcHelperApiUrl = self.GoPayUtils?.normalizeGpcHelperBaseUrl
? self.GoPayUtils.normalizeGpcHelperBaseUrl(value || defaultGpcHelperApiUrl)
: String(value || defaultGpcHelperApiUrl).trim().replace(/\/+$/g, '');
return normalizedGpcHelperApiUrl === legacyGpcHelperApiUrl
? defaultGpcHelperApiUrl
: normalizedGpcHelperApiUrl;
}
case 'gopayHelperApiKey':
case 'gopayHelperCardKey':
@@ -2342,6 +2347,7 @@ function normalizePersistentSettingValue(key, value) {
case 'oauthFlowTimeoutEnabled':
case 'gopayHelperLocalSmsHelperEnabled':
case 'autoRunDelayEnabled':
case 'step6CookieCleanupEnabled':
case 'phoneVerificationEnabled':
case 'freePhoneReuseEnabled':
case 'freePhoneReuseAutoEnabled':
@@ -10680,7 +10686,9 @@ const step5Executor = self.MultiPageBackgroundStep5?.createStep5Executor({
});
const step6Executor = self.MultiPageBackgroundStep6?.createStep6Executor({
addLog,
chrome,
completeStepFromBackground,
getErrorMessage,
registrationSuccessWaitMs: STEP6_REGISTRATION_SUCCESS_WAIT_MS,
sleepWithStop,
});
@@ -10843,7 +10851,7 @@ const stepExecutorsByKey = {
'fill-password': (state) => step3Executor.executeStep3(state),
'fetch-signup-code': (state) => step4Executor.executeStep4(state),
'fill-profile': (state) => step5Executor.executeStep5(state),
'wait-registration-success': () => step6Executor.executeStep6(),
'wait-registration-success': (state) => step6Executor.executeStep6(state),
'plus-checkout-create': (state) => plusCheckoutCreateExecutor.executePlusCheckoutCreate(state),
'plus-checkout-billing': (state) => plusCheckoutBillingExecutor.executePlusCheckoutBilling(state),
'gopay-subscription-confirm': (state) => goPayManualConfirmExecutor.executeGoPayManualConfirm(state),
@@ -11724,8 +11732,8 @@ async function rerunStep7ForStep8Recovery(options = {}) {
}
}
async function executeStep6() {
return step6Executor.executeStep6();
async function executeStep6(state = null) {
return step6Executor.executeStep6(state || await getState());
}
// ============================================================
+1 -1
View File
@@ -7,7 +7,7 @@
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
function createPlusCheckoutCreateExecutor(deps = {}) {
const {
+1 -1
View File
@@ -10,7 +10,7 @@
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
const GPC_TASK_POLL_INTERVAL_MS = 3000;
const PAYMENT_METHOD_CONFIGS = {
[PLUS_PAYMENT_METHOD_PAYPAL]: {
+132 -1
View File
@@ -2,21 +2,152 @@
root.MultiPageBackgroundStep6 = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep6Module() {
const DEFAULT_REGISTRATION_SUCCESS_WAIT_MS = 20000;
const STEP6_COOKIE_CLEAR_DOMAINS = [
'chatgpt.com',
'chat.openai.com',
'openai.com',
'auth.openai.com',
'auth0.openai.com',
'accounts.openai.com',
];
const STEP6_COOKIE_CLEAR_ORIGINS = [
'https://chatgpt.com',
'https://chat.openai.com',
'https://auth.openai.com',
'https://auth0.openai.com',
'https://accounts.openai.com',
'https://openai.com',
];
function normalizeStep6CookieDomain(domain) {
return String(domain || '').trim().replace(/^\.+/, '').toLowerCase();
}
function shouldClearStep6Cookie(cookie) {
const domain = normalizeStep6CookieDomain(cookie?.domain);
if (!domain) return false;
return STEP6_COOKIE_CLEAR_DOMAINS.some((target) => (
domain === target || domain.endsWith(`.${target}`)
));
}
function buildStep6CookieRemovalUrl(cookie) {
const host = normalizeStep6CookieDomain(cookie?.domain);
const rawPath = String(cookie?.path || '/');
const path = rawPath.startsWith('/') ? rawPath : `/${rawPath}`;
return `https://${host}${path}`;
}
async function collectStep6Cookies(chromeApi) {
if (!chromeApi.cookies?.getAll) {
return [];
}
const stores = chromeApi.cookies.getAllCookieStores
? await chromeApi.cookies.getAllCookieStores()
: [{ id: undefined }];
const cookies = [];
const seen = new Set();
for (const store of stores) {
const storeId = store?.id;
const batch = await chromeApi.cookies.getAll(storeId ? { storeId } : {});
for (const cookie of batch || []) {
if (!shouldClearStep6Cookie(cookie)) continue;
const key = [
cookie.storeId || storeId || '',
cookie.domain || '',
cookie.path || '',
cookie.name || '',
cookie.partitionKey ? JSON.stringify(cookie.partitionKey) : '',
].join('|');
if (seen.has(key)) continue;
seen.add(key);
cookies.push(cookie);
}
}
return cookies;
}
async function removeStep6Cookie(chromeApi, cookie, getErrorMessage) {
const details = {
url: buildStep6CookieRemovalUrl(cookie),
name: cookie.name,
};
if (cookie.storeId) {
details.storeId = cookie.storeId;
}
if (cookie.partitionKey) {
details.partitionKey = cookie.partitionKey;
}
try {
const result = await chromeApi.cookies.remove(details);
return Boolean(result);
} catch (error) {
console.warn('[MultiPage:step6] remove cookie failed', {
domain: cookie?.domain,
name: cookie?.name,
message: getErrorMessage(error),
});
return false;
}
}
function createStep6Executor(deps = {}) {
const {
addLog = async () => {},
chrome: chromeApi = globalThis.chrome,
completeStepFromBackground,
getErrorMessage = (error) => error?.message || String(error || '未知错误'),
registrationSuccessWaitMs = DEFAULT_REGISTRATION_SUCCESS_WAIT_MS,
sleepWithStop = async (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, Number(ms) || 0))),
} = deps;
async function executeStep6() {
async function clearCookiesIfEnabled(state = {}) {
if (!state?.step6CookieCleanupEnabled) {
return;
}
if (!chromeApi?.cookies?.getAll || !chromeApi.cookies?.remove) {
await addLog('步骤 6:当前浏览器不支持 cookies API,跳过第六步 Cookies 清理。', 'warn');
return;
}
try {
await addLog('步骤 6:已开启 Cookies 清理,正在清理 ChatGPT / OpenAI cookies...', 'info');
const cookies = await collectStep6Cookies(chromeApi);
let removedCount = 0;
for (const cookie of cookies) {
if (await removeStep6Cookie(chromeApi, cookie, getErrorMessage)) {
removedCount += 1;
}
}
if (chromeApi.browsingData?.removeCookies) {
try {
await chromeApi.browsingData.removeCookies({
since: 0,
origins: STEP6_COOKIE_CLEAR_ORIGINS,
});
} catch (error) {
await addLog(`步骤 6browsingData 补扫 cookies 失败:${getErrorMessage(error)}`, 'warn');
}
}
await addLog(`步骤 6:已清理 ${removedCount} 个 ChatGPT / OpenAI cookies。`, 'ok');
} catch (error) {
await addLog(`步骤 6:Cookies 清理失败,已跳过并继续后续流程:${getErrorMessage(error)}`, 'warn');
}
}
async function executeStep6(state = {}) {
const waitMs = Math.max(0, Math.floor(Number(registrationSuccessWaitMs) || 0));
if (waitMs > 0) {
await addLog(`步骤 6:等待 ${Math.round(waitMs / 1000)} 秒,确认注册成功并让页面稳定...`, 'info');
await sleepWithStop(waitMs);
}
await clearCookiesIfEnabled(state);
await addLog('步骤 6:注册成功等待完成,准备继续获取 OAuth 链接并登录。', 'ok');
await completeStepFromBackground(6);
}
+5 -1
View File
@@ -4,7 +4,8 @@
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
const LEGACY_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
function normalizePlusPaymentMethod(value = '') {
const normalized = String(value || '').trim().toLowerCase();
@@ -66,6 +67,9 @@
normalized = normalized.replace(/\/api\/gp\/balance(?:\?.*)?$/i, '');
normalized = normalized.replace(/\/api\/card\/balance(?:\?.*)?$/i, '');
normalized = normalized.replace(/\/api\/card\/redeem-api-key(?:\?.*)?$/i, '');
if (normalized === LEGACY_GPC_HELPER_API_URL) {
return DEFAULT_GPC_HELPER_API_URL;
}
return normalized || DEFAULT_GPC_HELPER_API_URL;
}
+41
View File
@@ -1971,6 +1971,16 @@ header {
.data-select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-soft); }
[data-theme="dark"] .data-select { color-scheme: dark; }
.data-input[readonly] {
color: var(--text-secondary);
background: var(--bg-surface);
cursor: default;
}
.gpc-helper-api-input {
font-family: 'JetBrains Mono', monospace;
}
.editable-list-picker {
position: relative;
flex: 1;
@@ -2114,6 +2124,37 @@ header {
justify-content: flex-end;
}
.auto-delay-setting-pair {
flex-wrap: wrap;
row-gap: 8px;
}
#row-auto-delay-settings .setting-group-primary {
flex: 0 0 auto;
min-width: 116px;
}
#row-auto-delay-settings .setting-group-secondary {
margin-left: 0;
min-width: 0;
}
.step6-cookie-cleanup-setting {
gap: 8px;
}
.auto-run-delay-setting {
margin-left: auto !important;
}
#row-auto-delay-settings .setting-caption {
min-width: auto;
}
.setting-caption-left {
text-align: left;
}
.plus-payment-method-select {
flex: 0 0 156px;
min-width: 156px;
+19 -6
View File
@@ -264,7 +264,7 @@
<option value="gopay">GoPay</option>
<option value="gpc-helper">GPC</option>
</select>
<button id="btn-gpc-card-key-purchase" class="btn btn-outline btn-sm data-inline-btn" type="button" style="display:none;">获取 API Key</button>
<button id="btn-gpc-card-key-purchase" class="btn btn-outline btn-sm data-inline-btn" type="button" style="display:none;">购买卡密</button>
<span class="setting-caption" id="plus-payment-method-caption">PayPal 订阅链路</span>
</div>
</div>
@@ -285,8 +285,11 @@
</div>
<div class="data-row" id="row-gpc-helper-api" style="display:none;">
<span class="data-label">GPC API</span>
<input type="text" id="input-gpc-helper-api" class="data-input"
placeholder="https://gpc.leftcode.xyz" autocomplete="off" />
<div class="data-inline">
<input type="text" id="input-gpc-helper-api" class="data-input gpc-helper-api-input"
value="https://gpc.qlhazycoder.top/" placeholder="https://gpc.qlhazycoder.top/" autocomplete="off" readonly />
<button id="btn-gpc-helper-convert-api-key" class="btn btn-outline btn-sm data-inline-btn" type="button">转换 API Key</button>
</div>
</div>
<div class="data-row" id="row-gpc-helper-card-key" style="display:none;">
<span class="data-label">GPC API Key</span>
@@ -545,9 +548,19 @@
</div>
</div>
<div class="data-row module-divider-start" id="row-auto-delay-settings">
<span class="data-label">延迟</span>
<div class="data-inline setting-pair">
<div class="setting-group setting-group-primary">
<span class="data-label">第六步</span>
<div class="data-inline setting-pair auto-delay-setting-pair">
<div class="setting-group setting-group-primary step6-cookie-cleanup-setting">
<span class="setting-caption setting-caption-left">清 Cookies</span>
<label class="toggle-switch" for="input-step6-cookie-cleanup-enabled">
<input type="checkbox" id="input-step6-cookie-cleanup-enabled" />
<span class="toggle-switch-track" aria-hidden="true">
<span class="toggle-switch-thumb"></span>
</span>
</label>
</div>
<div class="setting-group setting-group-secondary auto-run-delay-setting">
<span class="setting-caption">延迟</span>
<label class="toggle-switch" for="input-auto-delay-enabled">
<input type="checkbox" id="input-auto-delay-enabled" />
<span class="toggle-switch-track" aria-hidden="true">
+32 -9
View File
@@ -187,6 +187,7 @@ const payPalAccountMenu = document.getElementById('paypal-account-menu');
const btnAddPayPalAccount = document.getElementById('btn-add-paypal-account');
const rowGpcHelperApi = document.getElementById('row-gpc-helper-api');
const inputGpcHelperApi = document.getElementById('input-gpc-helper-api');
const btnGpcHelperConvertApiKey = document.getElementById('btn-gpc-helper-convert-api-key');
const rowGpcHelperCardKey = document.getElementById('row-gpc-helper-card-key');
const inputGpcHelperCardKey = document.getElementById('input-gpc-helper-card-key');
const btnToggleGpcHelperCardKey = document.getElementById('btn-toggle-gpc-helper-card-key');
@@ -364,6 +365,7 @@ const btnCfDomainMode = document.getElementById('btn-cf-domain-mode');
const inputRunCount = document.getElementById('input-run-count');
const inputAutoSkipFailures = document.getElementById('input-auto-skip-failures');
const inputAutoSkipFailuresThreadIntervalMinutes = document.getElementById('input-auto-skip-failures-thread-interval-minutes');
const inputStep6CookieCleanupEnabled = document.getElementById('input-step6-cookie-cleanup-enabled');
const inputAutoDelayEnabled = document.getElementById('input-auto-delay-enabled');
const inputAutoDelayMinutes = document.getElementById('input-auto-delay-minutes');
const inputAutoStepDelaySeconds = document.getElementById('input-auto-step-delay-seconds');
@@ -496,7 +498,8 @@ const stepsList = document.querySelector('.steps-list');
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
const GPC_HELPER_PORTAL_URL = 'https://gpc.qlhazycoder.top/';
const DEFAULT_PLUS_PAYMENT_METHOD = PLUS_PAYMENT_METHOD_PAYPAL;
const SIGNUP_METHOD_EMAIL = 'email';
const SIGNUP_METHOD_PHONE = 'phone';
@@ -2695,7 +2698,7 @@ function applyCloudflareTempEmailSettingsState(state = {}) {
function collectSettingsPayload() {
const defaultGpcHelperApiUrl = typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined'
? DEFAULT_GPC_HELPER_API_URL
: 'https://gpc.leftcode.xyz';
: 'https://gpc.qlhazycoder.top';
const { domains, activeDomain } = getCloudflareDomainsFromState();
const selectedCloudflareDomain = normalizeCloudflareDomainValue(
!cloudflareDomainEditMode ? selectCfDomain.value : activeDomain
@@ -3283,10 +3286,8 @@ function collectSettingsPayload() {
? String(inputGoPayPin.value || '')
: String(latestState?.gopayPin || '')),
gopayHelperApiUrl: window.GoPayUtils?.normalizeGpcHelperBaseUrl
? window.GoPayUtils.normalizeGpcHelperBaseUrl(typeof inputGpcHelperApi !== 'undefined' && inputGpcHelperApi ? inputGpcHelperApi.value : (latestState?.gopayHelperApiUrl || defaultGpcHelperApiUrl))
: (typeof inputGpcHelperApi !== 'undefined' && inputGpcHelperApi
? String(inputGpcHelperApi.value || defaultGpcHelperApiUrl).trim().replace(/\/+$/g, '')
: String(latestState?.gopayHelperApiUrl || defaultGpcHelperApiUrl).trim()),
? window.GoPayUtils.normalizeGpcHelperBaseUrl(defaultGpcHelperApiUrl)
: String(defaultGpcHelperApiUrl).trim().replace(/\/+$/g, ''),
gopayHelperApiKey: typeof inputGpcHelperCardKey !== 'undefined' && inputGpcHelperCardKey
? String(inputGpcHelperCardKey.value || '').trim()
: String(latestState?.gopayHelperApiKey || latestState?.gopayHelperCardKey || '').trim(),
@@ -3358,6 +3359,9 @@ function collectSettingsPayload() {
cloudflareTempEmailDomains: tempEmailDomains,
autoRunSkipFailures: inputAutoSkipFailures.checked,
autoRunFallbackThreadIntervalMinutes: normalizeAutoRunThreadIntervalMinutes(inputAutoSkipFailuresThreadIntervalMinutes.value),
step6CookieCleanupEnabled: typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled
? Boolean(inputStep6CookieCleanupEnabled.checked)
: false,
autoRunDelayEnabled: inputAutoDelayEnabled.checked,
autoRunDelayMinutes: normalizeAutoDelayMinutes(inputAutoDelayMinutes.value),
autoStepDelaySeconds: normalizeAutoStepDelaySeconds(inputAutoStepDelaySeconds.value),
@@ -7922,8 +7926,8 @@ function applySettingsState(state) {
if (typeof inputGpcHelperApi !== 'undefined' && inputGpcHelperApi) {
const defaultGpcHelperApiUrl = typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined'
? DEFAULT_GPC_HELPER_API_URL
: 'https://gpc.leftcode.xyz';
inputGpcHelperApi.value = state?.gopayHelperApiUrl || defaultGpcHelperApiUrl;
: 'https://gpc.qlhazycoder.top';
inputGpcHelperApi.value = `${defaultGpcHelperApiUrl.replace(/\/+$/g, '')}/`;
}
if (typeof inputGpcHelperCardKey !== 'undefined' && inputGpcHelperCardKey) {
inputGpcHelperCardKey.value = state?.gopayHelperApiKey || state?.gopayHelperCardKey || '';
@@ -8169,6 +8173,9 @@ function applySettingsState(state) {
setCloudflareDomainEditMode(false, { clearInput: true });
inputAutoSkipFailures.checked = Boolean(state?.autoRunSkipFailures);
inputAutoSkipFailuresThreadIntervalMinutes.value = String(normalizeAutoRunThreadIntervalMinutes(state?.autoRunFallbackThreadIntervalMinutes));
if (typeof inputStep6CookieCleanupEnabled !== 'undefined' && inputStep6CookieCleanupEnabled) {
inputStep6CookieCleanupEnabled.checked = Boolean(state?.step6CookieCleanupEnabled);
}
inputAutoDelayEnabled.checked = Boolean(state?.autoRunDelayEnabled);
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(state?.autoRunDelayMinutes));
inputAutoStepDelaySeconds.value = formatAutoStepDelayInputValue(state?.autoStepDelaySeconds);
@@ -11336,13 +11343,17 @@ btnGpcCardKeyPurchase?.addEventListener('click', () => {
openExternalUrl('https://pay.ldxp.cn/shop/gpc');
});
btnGpcHelperConvertApiKey?.addEventListener('click', () => {
openExternalUrl(GPC_HELPER_PORTAL_URL);
});
btnGpcHelperBalance?.addEventListener('click', async () => {
try {
const response = await chrome.runtime.sendMessage({
type: 'REFRESH_GPC_CARD_BALANCE',
source: 'sidepanel',
payload: {
gopayHelperApiUrl: inputGpcHelperApi?.value || DEFAULT_GPC_HELPER_API_URL,
gopayHelperApiUrl: DEFAULT_GPC_HELPER_API_URL,
gopayHelperApiKey: inputGpcHelperCardKey?.value || '',
gopayHelperCountryCode: selectGpcHelperCountryCode?.value || '+86',
reason: 'manual',
@@ -12277,6 +12288,11 @@ inputAutoDelayEnabled.addEventListener('change', () => {
saveSettings({ silent: true }).catch(() => { });
});
inputStep6CookieCleanupEnabled?.addEventListener('change', () => {
markSettingsDirty(true);
saveSettings({ silent: true }).catch(() => { });
});
inputAutoDelayMinutes.addEventListener('input', () => {
markSettingsDirty(true);
scheduleSettingsAutoSave();
@@ -13397,6 +13413,13 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
inputAutoDelayEnabled.checked = Boolean(message.payload.autoRunDelayEnabled);
updateAutoDelayInputState();
}
if (
message.payload.step6CookieCleanupEnabled !== undefined
&& typeof inputStep6CookieCleanupEnabled !== 'undefined'
&& inputStep6CookieCleanupEnabled
) {
inputStep6CookieCleanupEnabled.checked = Boolean(message.payload.step6CookieCleanupEnabled);
}
if (message.payload.autoRunDelayMinutes !== undefined) {
inputAutoDelayMinutes.value = String(normalizeAutoDelayMinutes(message.payload.autoRunDelayMinutes));
}
@@ -159,7 +159,7 @@ const self = {
};
const PERSISTED_SETTING_DEFAULTS = {
autoStepDelaySeconds: null,
gopayHelperApiUrl: 'https://gpc.leftcode.xyz',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top',
mailProvider: '163',
};
function normalizePanelMode(value) { return value === 'sub2api' ? 'sub2api' : (value === 'codex2api' ? 'codex2api' : 'cpa'); }
@@ -194,18 +194,18 @@ return {
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'paypal'), 'paypal');
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'unknown'), 'paypal');
assert.equal(
api.normalizePersistentSettingValue('gopayHelperApiUrl', ' https://gpc.leftcode.xyz/api/checkout/start '),
'https://gpc.leftcode.xyz'
api.normalizePersistentSettingValue('gopayHelperApiUrl', ' https://gpc.qlhazycoder.top/api/checkout/start '),
'https://gpc.qlhazycoder.top'
);
assert.equal(
api.normalizePersistentSettingValue('gopayHelperApiUrl', ' https://gpc.leftcode.xyz/api/gp/tasks/task_1/pin '),
'https://gpc.leftcode.xyz'
api.normalizePersistentSettingValue('gopayHelperApiUrl', ' https://gpc.qlhazycoder.top/api/gp/tasks/task_1/pin '),
'https://gpc.qlhazycoder.top'
);
assert.equal(
api.normalizePersistentSettingValue('gopayHelperApiUrl', ' https://gpc.leftcode.xyz/api/gp/balance '),
'https://gpc.leftcode.xyz'
api.normalizePersistentSettingValue('gopayHelperApiUrl', ' https://gpc.qlhazycoder.top/api/gp/balance '),
'https://gpc.qlhazycoder.top'
);
assert.equal(api.normalizePersistentSettingValue('gopayHelperApiUrl', ''), 'https://gpc.leftcode.xyz');
assert.equal(api.normalizePersistentSettingValue('gopayHelperApiUrl', ''), 'https://gpc.qlhazycoder.top');
assert.equal(api.normalizePersistentSettingValue('gopayHelperApiKey', ' gpc-123 '), 'gpc-123');
assert.equal(api.normalizePersistentSettingValue('gopayHelperCountryCode', ' 86 '), '+86');
assert.equal(api.normalizePersistentSettingValue('gopayHelperPhoneNumber', ' +86 138-0013-8000 '), '+8613800138000');
@@ -32,6 +32,63 @@ test('step 6 waits for registration success and completes from background', asyn
assert.ok(events.logs.some(({ message }) => /等待 20 /.test(message)));
});
test('step 6 only clears cookies when cleanup switch is enabled', async () => {
const source = fs.readFileSync('background/steps/wait-registration-success.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep6;`)(globalScope);
const events = {
removedCookies: [],
browsingDataCalls: [],
completedSteps: [],
};
const chromeApi = {
cookies: {
getAllCookieStores: async () => [{ id: 'store-a' }],
getAll: async () => [
{ domain: '.chatgpt.com', path: '/auth', name: 'session', storeId: 'store-a' },
{ domain: '.example.com', path: '/', name: 'keep', storeId: 'store-a' },
],
remove: async (details) => {
events.removedCookies.push(details);
return details;
},
},
browsingData: {
removeCookies: async (details) => {
events.browsingDataCalls.push(details);
},
},
};
const executor = api.createStep6Executor({
addLog: async () => {},
chrome: chromeApi,
completeStepFromBackground: async (step) => {
events.completedSteps.push(step);
},
sleepWithStop: async () => {},
});
await executor.executeStep6({ step6CookieCleanupEnabled: false });
assert.deepStrictEqual(events.removedCookies, []);
assert.deepStrictEqual(events.browsingDataCalls, []);
await executor.executeStep6({ step6CookieCleanupEnabled: true });
assert.deepStrictEqual(events.completedSteps, [6, 6]);
assert.deepStrictEqual(events.removedCookies, [
{
url: 'https://chatgpt.com/auth',
name: 'session',
storeId: 'store-a',
},
]);
assert.equal(events.browsingDataCalls.length, 1);
assert.ok(events.browsingDataCalls[0].origins.includes('https://chatgpt.com'));
});
test('step 7 retries up to configured limit and then fails', async () => {
const source = fs.readFileSync('background/steps/oauth-login.js', 'utf8');
const globalScope = {};
+11 -11
View File
@@ -26,35 +26,35 @@ test('GoPay utils keeps GPC helper payment method distinct', () => {
test('GoPay utils builds GPC queue task and balance URLs from helper endpoints', () => {
const api = loadGoPayUtils();
assert.equal(api.DEFAULT_GPC_HELPER_API_URL, 'https://gpc.leftcode.xyz');
assert.equal(api.normalizeGpcHelperBaseUrl(''), 'https://gpc.leftcode.xyz');
assert.equal(api.DEFAULT_GPC_HELPER_API_URL, 'https://gpc.qlhazycoder.top');
assert.equal(api.normalizeGpcHelperBaseUrl(''), 'https://gpc.qlhazycoder.top');
assert.equal(
api.buildGpcHelperApiUrl('', '/api/checkout/start'),
'https://gpc.leftcode.xyz/api/checkout/start'
'https://gpc.qlhazycoder.top/api/checkout/start'
);
assert.equal(
api.buildGpcApiKeyBalanceUrl('http://localhost:18473/'),
'http://localhost:18473/api/gp/balance'
);
assert.equal(
api.buildGpcCardBalanceUrl('https://gpc.leftcode.xyz/api/gp/balance'),
'https://gpc.leftcode.xyz/api/gp/balance'
api.buildGpcCardBalanceUrl('https://gpc.qlhazycoder.top/api/gp/balance'),
'https://gpc.qlhazycoder.top/api/gp/balance'
);
assert.deepEqual(
api.buildGpcApiKeyHeaders(' gpc-123 ', { Accept: 'application/json' }),
{ Accept: 'application/json', 'X-API-Key': 'gpc-123' }
);
assert.equal(
api.buildGpcTaskCreateUrl('https://gpc.leftcode.xyz/api/checkout/start'),
'https://gpc.leftcode.xyz/api/gp/tasks'
api.buildGpcTaskCreateUrl('https://gpc.qlhazycoder.top/api/checkout/start'),
'https://gpc.qlhazycoder.top/api/gp/tasks'
);
assert.equal(
api.buildGpcTaskQueryUrl('https://gpc.leftcode.xyz/api/gp/tasks/task_old?card_key=old', 'task/1'),
'https://gpc.leftcode.xyz/api/gp/tasks/task%2F1'
api.buildGpcTaskQueryUrl('https://gpc.qlhazycoder.top/api/gp/tasks/task_old?card_key=old', 'task/1'),
'https://gpc.qlhazycoder.top/api/gp/tasks/task%2F1'
);
assert.equal(
api.buildGpcTaskActionUrl('https://gpc.leftcode.xyz/api/gp/tasks/task_old/stop', 'task_1', 'pin'),
'https://gpc.leftcode.xyz/api/gp/tasks/task_1/pin'
api.buildGpcTaskActionUrl('https://gpc.qlhazycoder.top/api/gp/tasks/task_old/stop', 'task_1', 'pin'),
'https://gpc.qlhazycoder.top/api/gp/tasks/task_1/pin'
);
});
@@ -833,7 +833,7 @@ test('GPC billing polls queue task, submits WhatsApp OTP then PIN, and waits unt
getState: async () => currentState,
fetchImpl: async (url, options = {}) => {
fetchCalls.push({ url, options });
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_123') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_123') {
pollCount += 1;
if (pollCount === 1) {
return {
@@ -877,7 +877,7 @@ test('GPC billing polls queue task, submits WhatsApp OTP then PIN, and waits unt
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_123',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/api/gp/tasks/task_old/otp',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/api/gp/tasks/task_old/otp',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_billing_123',
});
@@ -893,7 +893,7 @@ test('GPC billing polls queue task, submits WhatsApp OTP then PIN, and waits unt
await run;
assert.equal(fetchCalls[0].url, 'https://gpc.leftcode.xyz/api/gp/tasks/task_123');
assert.equal(fetchCalls[0].url, 'https://gpc.qlhazycoder.top/api/gp/tasks/task_123');
assert.equal(fetchCalls[0].options.headers['X-API-Key'], 'gpc_billing_123');
const otpCall = fetchCalls.find((call) => call.url.endsWith('/api/gp/tasks/task_123/otp'));
const pinCall = fetchCalls.find((call) => call.url.endsWith('/api/gp/tasks/task_123/pin'));
@@ -923,7 +923,7 @@ test('GPC billing reads SMS OTP from local helper for sms_otp_wait', async () =>
json: async () => ({ ok: true, otp: '654321', message_id: 'sms-1' }),
};
}
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_sms') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_sms') {
pollCount += 1;
if (pollCount === 1) {
return {
@@ -967,7 +967,7 @@ test('GPC billing reads SMS OTP from local helper for sms_otp_wait', async () =>
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_sms',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_sms',
gopayHelperOtpChannel: 'sms',
@@ -1008,7 +1008,7 @@ test('GPC billing can read WhatsApp OTP from local helper when enabled', async (
json: async () => ({ ok: true, otp: '765432', message_id: 'wa-1' }),
};
}
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_wa') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_wa') {
pollCount += 1;
if (pollCount === 1) {
return {
@@ -1052,7 +1052,7 @@ test('GPC billing can read WhatsApp OTP from local helper when enabled', async (
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_wa',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_wa',
gopayHelperOtpChannel: 'whatsapp',
@@ -1088,8 +1088,8 @@ test('GPC billing helper mode does not open OTP dialog when helper has no code a
json: async () => ({ ok: true, status: 'waiting', otp: '', message: '未查询到验证码' }),
};
}
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_timeout') {
const queryCount = fetchCalls.filter((call) => call.url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_timeout').length;
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_timeout') {
const queryCount = fetchCalls.filter((call) => call.url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_timeout').length;
return {
ok: true,
status: 200,
@@ -1118,7 +1118,7 @@ test('GPC billing helper mode does not open OTP dialog when helper has no code a
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_timeout',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_timeout',
gopayHelperOtpChannel: 'whatsapp',
@@ -1153,7 +1153,7 @@ test('GPC billing helper mode requests newer OTP after invalid OTP error', async
json: async () => ({ ok: true, otp: helperCallCount === 1 ? '111111' : '222222', message_id: `sms-${helperCallCount}` }),
};
}
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_retry') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_retry') {
taskPollCount += 1;
if (taskPollCount === 1) {
return { ok: true, status: 200, json: async () => createGpcTaskResponse({ task_id: 'task_retry', status: 'active', remote_stage: 'sms_otp_wait', api_waiting_for: 'otp' }) };
@@ -1181,7 +1181,7 @@ test('GPC billing helper mode requests newer OTP after invalid OTP error', async
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_retry',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_retry',
gopayHelperOtpChannel: 'sms',
@@ -1222,7 +1222,7 @@ test('GPC billing manual OTP wrong input opens next dialog only after previous o
getState: async () => currentState,
fetchImpl: async (url, options = {}) => {
fetchCalls.push({ url, options });
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_manual_retry') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_manual_retry') {
pollCount += 1;
if (pollCount === 1) {
return { ok: true, status: 200, json: async () => createGpcTaskResponse({ task_id: 'task_manual_retry', status: 'active', remote_stage: 'whatsapp_otp_wait', api_waiting_for: 'otp' }) };
@@ -1263,7 +1263,7 @@ test('GPC billing manual OTP wrong input opens next dialog only after previous o
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_manual_retry',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_manual_retry',
});
@@ -1297,7 +1297,7 @@ test('GPC billing manual OTP cancel stops task and ends current round', async ()
getState: async () => currentState,
fetchImpl: async (url, options = {}) => {
fetchCalls.push({ url, options });
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_cancel') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_cancel') {
return { ok: true, status: 200, json: async () => createGpcTaskResponse({ task_id: 'task_cancel', status: 'active', remote_stage: 'whatsapp_otp_wait', api_waiting_for: 'otp' }) };
}
if (url.endsWith('/api/gp/tasks/task_cancel/stop')) {
@@ -1311,7 +1311,7 @@ test('GPC billing manual OTP cancel stops task and ends current round', async ()
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_cancel',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_cancel',
});
@@ -1340,7 +1340,7 @@ test('GPC billing PIN failure ends task without retrying PIN', async () => {
stateByFrame: {},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({ url, options });
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_pin_failed') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_pin_failed') {
pollCount += 1;
if (pollCount === 1) {
return { ok: true, status: 200, json: async () => createGpcTaskResponse({ task_id: 'task_pin_failed', status: 'otp_ready', status_text: '等待 PIN', remote_stage: 'otp_ready', api_waiting_for: 'pin' }) };
@@ -1359,7 +1359,7 @@ test('GPC billing PIN failure ends task without retrying PIN', async () => {
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_pin_failed',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_pin_failed',
}),
@@ -1378,7 +1378,7 @@ for (const terminalStatus of ['failed', 'expired', 'discarded']) {
stateByFrame: {},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({ url, options });
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_bad') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_bad') {
return {
ok: true,
status: 200,
@@ -1399,7 +1399,7 @@ for (const terminalStatus of ['failed', 'expired', 'discarded']) {
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_bad',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_bad',
}),
@@ -1417,7 +1417,7 @@ test('GPC billing stops task best-effort when flow is interrupted before termina
stateByFrame: {},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({ url, options });
if (url === 'https://gpc.leftcode.xyz/api/gp/tasks/task_stop') {
if (url === 'https://gpc.qlhazycoder.top/api/gp/tasks/task_stop') {
return {
ok: false,
status: 500,
@@ -1440,7 +1440,7 @@ test('GPC billing stops task best-effort when flow is interrupted before termina
plusPaymentMethod: 'gpc-helper',
plusCheckoutSource: 'gpc-helper',
gopayHelperTaskId: 'task_stop',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPin: '654321',
gopayHelperApiKey: 'gpc_stop',
}),
+6 -6
View File
@@ -155,7 +155,7 @@ test('GPC checkout injects Plus script before reading ChatGPT session token and
await executor.executePlusCheckoutCreate({
email: 'Current.Round+GPC@Example.COM',
plusPaymentMethod: 'gpc-helper',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPhoneNumber: '+8613800138000',
gopayPhone: '',
gopayHelperCountryCode: '+86',
@@ -173,7 +173,7 @@ test('GPC checkout injects Plus script before reading ChatGPT session token and
includeAccessToken: true,
});
assert.equal(fetchCalls.length, 1);
assert.equal(fetchCalls[0].url, 'https://gpc.leftcode.xyz/api/gp/tasks');
assert.equal(fetchCalls[0].url, 'https://gpc.qlhazycoder.top/api/gp/tasks');
const helperPayload = JSON.parse(fetchCalls[0].options.body);
assert.deepEqual(helperPayload, {
access_token: 'session-access-token',
@@ -233,7 +233,7 @@ test('GPC checkout forwards selected SMS OTP channel', async () => {
await executor.executePlusCheckoutCreate({
email: 'sms@example.com',
plusPaymentMethod: 'gpc-helper',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
gopayHelperPhoneNumber: '+8613800138000',
gopayHelperCountryCode: '+86',
gopayHelperPin: '123456',
@@ -285,7 +285,7 @@ test('GPC checkout surfaces unified queue API errors', async () => {
() => executor.executePlusCheckoutCreate({
email: 'paid@example.com',
plusPaymentMethod: 'gpc-helper',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
chatgptAccessToken: 'state-access-token',
gopayHelperPhoneNumber: '+8613800138000',
gopayHelperCountryCode: '+86',
@@ -326,7 +326,7 @@ test('GPC checkout does not fall back to browser GoPay phone fields', async () =
await assert.rejects(
() => executor.executePlusCheckoutCreate({
plusPaymentMethod: 'gpc-helper',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
chatgptAccessToken: 'state-access-token',
email: 'helper-phone-test@example.com',
gopayPhone: '+8613800138000',
@@ -366,7 +366,7 @@ test('GPC checkout rejects missing API Key before calling helper API', async ()
await assert.rejects(
() => executor.executePlusCheckoutCreate({
plusPaymentMethod: 'gpc-helper',
gopayHelperApiUrl: 'https://gpc.leftcode.xyz/',
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
chatgptAccessToken: 'state-access-token',
email: 'missing-card@example.com',
gopayHelperPhoneNumber: '+8613800138000',
+3 -1
View File
@@ -135,9 +135,11 @@ test('sidepanel html exposes Plus mode, PayPal, and GoPay settings', () => {
assert.match(html, /id="input-gopay-pin"/);
assert.match(html, /<option value="gpc-helper">GPC<\/option>/);
assert.match(html, /id="btn-gpc-card-key-purchase"/);
assert.match(html, />获取 API Key</);
assert.match(html, />购买卡密</);
assert.match(html, /GPC API/);
assert.match(html, /id="input-gpc-helper-api"/);
assert.match(html, /id="btn-gpc-helper-convert-api-key"/);
assert.match(html, />转换 API Key</);
assert.match(html, /GPC API Key/);
assert.match(html, /id="input-gpc-helper-card-key"/);
assert.match(html, /id="btn-gpc-helper-balance"/);
+7 -6
View File
@@ -46,7 +46,7 @@
- 查询 GitHub Releases 并展示更新卡片;当前更新服务会区分 `Ultra`、历史 `Pro` 与 legacy `v` 三个版本族,排序时固定以 `Ultra` 为最高正式系列,同时会在读取缓存后重新排序,避免历史 `Pro``v` 版本误显示为比 `Ultra` 更新
- 展示一个单独的“接码”开关、注册方式 `signupMethod` 与“接码平台”下拉;接码平台当前支持 HeroSMS / 5sim / NexSMS。普通模式下开启接码后可把注册方式切到手机号注册,并在 OAuth 登录链路命中手机号登录验证码页时继续复用同一手机号续跑短信验证
- 侧栏在接码卡内提供一个独立运行态“注册手机号”输入框,位于接码订单运行状态下方;第 2 步自动拿到号码后立即回填,用户手动接管手机号注册时也可以直接改写这一个运行态槽位。这个输入框表达账号身份,不等同于接码订单;后续自动拉短信仍依赖 `signupPhoneActivation / signupPhoneCompletedActivation`
- 展示 `Plus 模式` 开关与 Plus 支付方式配置;支付方式支持 PayPal / GoPay / GPCPayPal 展示账号池下拉与添加按钮,GoPay 展示手机号和 PINGPC 展示 API 地址、API Key、专用手机号、OTP 渠道、本地 OTP helper 开关与 URL、PIN;步骤列表切换为 Plus 模式步骤定义,普通模式的注册成功等待步骤不再显示或执行,登录验证码步骤会移动到 Plus 可见第 11 步
- 展示 `Plus 模式` 开关与 Plus 支付方式配置;支付方式支持 PayPal / GoPay / GPCPayPal 展示账号池下拉与添加按钮,GoPay 展示手机号和 PIN,GPC 展示只读 API 地址、API Key、专用手机号、OTP 渠道、本地 OTP helper 开关与 URL、PINGPC 的 `购买卡密` 按钮打开卡密购买页,`转换 API Key` 按钮打开 `https://gpc.qlhazycoder.top/`步骤列表切换为 Plus 模式步骤定义,普通模式的注册成功等待步骤不再显示或执行,登录验证码步骤会移动到 Plus 可见第 11 步
- 为 Hotmail / 2925 账号池复用同一套“添加账号 / 取消添加 / 批量导入 / 收起列表”表单交互;共享的显隐控制放在 `sidepanel/account-pool-ui.js`,各自 manager 只保留 provider 相关字段校验与业务操作
### 2.2 Background Service Worker
@@ -183,7 +183,7 @@
- Codex2API 配置
- IP 代理持久配置:`ipProxyEnabled`、服务商、模式、API 地址、服务商配置快照、账号列表、固定 Host / Port / Protocol / Username / Password、地区参数、session 与自动切换阈值
- Plus 模式开关 `plusModeEnabled`
- Plus 支付方式 `plusPaymentMethod`GoPay 配置 `gopayPhone / gopayPin`GPC helper 配置 `gopayHelperApiUrl / gopayHelperApiKey / gopayHelperPhoneNumber / gopayHelperOtpChannel / gopayHelperLocalSmsHelperEnabled / gopayHelperLocalSmsHelperUrl / gopayHelperPin`
- Plus 支付方式 `plusPaymentMethod`GoPay 配置 `gopayPhone / gopayPin`GPC helper 配置 `gopayHelperApiUrl / gopayHelperApiKey / gopayHelperPhoneNumber / gopayHelperOtpChannel / gopayHelperLocalSmsHelperEnabled / gopayHelperLocalSmsHelperUrl / gopayHelperPin`;其中 GPC API 地址固定归一为 `https://gpc.qlhazycoder.top`
- PayPal 账号池配置 `paypalAccounts / currentPayPalAccountId`,以及供后台步骤兼容读取的 `paypalEmail / paypalPassword`
- 邮箱 provider 配置
- Hotmail 账号池
@@ -420,9 +420,10 @@ IP 代理模块在同步、切换、Change、出口探测和自动运行成功
流程:
1. 固定等待 20 秒
2. 不删除 ChatGPT / OpenAI cookies
3. 完成后进入后续登录链路
1. 固定等待 20 秒,等待注册成功状态和页面跳转稳定
2. 默认不删除 ChatGPT / OpenAI cookies
3. 如果侧栏第六步 `清 Cookies` 开关已开启,则在等待结束后清理 ChatGPT / OpenAI 相关 cookies
4. 完成后进入后续登录链路
### Step 7
@@ -548,7 +549,7 @@ Plus 模式通过 `plusModeEnabled` 开启,目标是在普通注册资料完
Plus 模式可见步骤:
1. 第 1~5 步:沿用普通注册入口、邮箱、密码、注册验证码、资料填写链路。
2. 第 6 步 `创建 Plus Checkout`PayPal / GoPay 打开已登录 ChatGPT 页面,通过 `/api/auth/session` 读取 accessToken,再请求 `https://chatgpt.com/backend-api/payments/checkout` 创建 `chatgptplusplan` 的 checkout session。PayPal 使用 `DE / EUR``https://chatgpt.com/checkout/openai_ie/{checkout_session_id}`GoPay 使用 `ID / IDR``https://chatgpt.com/checkout/openai_llc/{checkout_session_id}`GPC helper 模式改为把 accessToken、手机号、国家区号、`otp_channel` 等提交到 `gopayHelperApiUrl``/api/gp/tasks`,并通过 `gopayHelperApiKey` 发送 `X-API-Key` 认证,创建后保存 `task_id`
2. 第 6 步 `创建 Plus Checkout`PayPal / GoPay 打开已登录 ChatGPT 页面,通过 `/api/auth/session` 读取 accessToken,再请求 `https://chatgpt.com/backend-api/payments/checkout` 创建 `chatgptplusplan` 的 checkout session。PayPal 使用 `DE / EUR``https://chatgpt.com/checkout/openai_ie/{checkout_session_id}`GoPay 使用 `ID / IDR``https://chatgpt.com/checkout/openai_llc/{checkout_session_id}`GPC helper 模式改为把 accessToken、手机号、国家区号、`otp_channel` 等提交到 `gopayHelperApiUrl``/api/gp/tasks`,并通过 `gopayHelperApiKey` 发送 `X-API-Key` 认证,创建后保存 `task_id`;当前默认 GPC API 地址为 `https://gpc.qlhazycoder.top`,旧的 `https://gpc.leftcode.xyz` 配置会兼容归一到新地址
3. 第 7 步 `填写账单并提交订阅`PayPal / GoPay 仍走 checkout 页面与 Stripe iframe 自动化;GPC helper 模式不再操作 checkout iframe,而是轮询 `/api/gp/tasks/{task_id}`,根据远端 `api_waiting_for` 依次向 `/otp``/pin` 提交验证码与 PIN。若侧栏启用本地 OTP helper,后台会轮询 `gopayHelperLocalSmsHelperUrl``/latest-otp?phone=...&consume=1` 接口,按当前 GPC 手机号读取并消费当前 OTP 通道的验证码;未开启本地 helper 时才弹出手动 OTP 输入框。仓库内置的 `scripts/gpc_sms_helper_macos.py` 是 macOS Messages/本地通知兼容读取实现,其他通道需要提供兼容的本地 `/latest-otp``/otp` 接口。
4. 仅 PayPal / GoPay 会继续显示第 8 步:该步按当前支付方式显示为 `PayPal 登录与授权``GoPay 手机验证与授权`,底层 step key 仍为 `paypal-approve`
5. 第 9 步 `订阅回跳确认` 仅在 PayPal / GoPay 模式下等待授权后回跳到 ChatGPT / OpenAI 页面,页面加载完成后固定等待 1 秒。GPC helper 模式在第 7 步任务完成后会直接进入 Plus 可见第 10 步 OAuth 登录。
+4 -4
View File
@@ -63,9 +63,9 @@
## `background/steps/`
- `background/steps/wait-registration-success.js`:步骤 6 实现,负责注册资料提交后等待 20 秒,让注册成功状态和页面跳转稳定。
- `background/steps/wait-registration-success.js`:步骤 6 实现,负责注册资料提交后等待 20 秒,让注册成功状态和页面跳转稳定;默认不清理 cookies,只有侧栏第六步 `清 Cookies` 开关开启时才会在等待结束后清理 ChatGPT / OpenAI 相关 cookies
- `background/steps/confirm-oauth.js`:步骤 9 实现,负责 OAuth 同意页按钮定位、点击、localhost 回调监听与回调完成;等待 callback 期间会动态检查 OAuth 总预算开关,关闭后仅保留本地回调等待超时。
- `background/steps/create-plus-checkout.js`:Plus 模式第 6 步实现,负责在已登录 ChatGPT 页面创建 PayPal / GoPay checkout session,或在 GPC 模式下读取 accessToken 并通过 `https://gpc.leftcode.xyz/api/gp/tasks` 创建队列任务,记录 checkout / GPC task 运行态。
- `background/steps/create-plus-checkout.js`:Plus 模式第 6 步实现,负责在已登录 ChatGPT 页面创建 PayPal / GoPay checkout session,或在 GPC 模式下读取 accessToken 并通过 `https://gpc.qlhazycoder.top/api/gp/tasks` 创建队列任务,记录 checkout / GPC task 运行态。
- `background/steps/fetch-login-code.js`:步骤 8 实现,负责按真实认证页状态分发登录后续流程:邮箱验证码页走邮箱轮询、验证码回填与回退控制;真实 `phone-verification` 页才复用手机号验证码共享流程;手机号注册登录后若进入 `add-email`,会先生成/解析邮箱并提交绑定,再进入邮箱验证码轮询;邮箱分支对非 2925 provider 会固定当前验证码页显示邮箱作为本次 Step 8 的目标邮箱,当 provider 为 2925 时,会在轮询前先确保当前 2925 账号已自动登录。
- `background/steps/fetch-signup-code.js`:步骤 4 实现,负责注册验证码阶段的页面准备与验证码流程入口;邮箱注册继续走邮箱验证码,手机号注册改走短信验证码;当 provider 为 2925 时,会在邮箱轮询前先确保当前 2925 账号已自动登录。
- `background/steps/fill-plus-checkout.js`:Plus 模式第 7 步实现,负责驱动 PayPal / GoPay checkout 页面选择付款方式、填写账单地址、提交订阅并等待跳转;GPC 模式则轮询队列任务,按远端 `api_waiting_for` 提交 OTP / PIN,并在任务失败、过期或取消时清理运行态。
@@ -151,7 +151,7 @@
receive 模式把账号池开关一起隐藏;当前在 `邮箱生成` 区域新增 `自定义邮箱池` 选项和多行邮箱池输入框,并在 `邮箱服务 = 自定义邮箱`
额外显示 `自定义号池` 文本框;当邮箱服务为 iCloud 时,额外提供目标邮箱类型与转发邮箱 provider 配置,用于选择直接从 iCloud 收件箱收
码或从 QQ / 网易 / Gmail 转发目标邮箱收码;来源下拉框当前支持 `CPA / SUB2API / Codex2API`,其中 Codex2API 额外提供后台地址和管理密
钥配置行;设置卡片新增 `IP代理` 开关与代理配置折叠区,支持 711Proxy 账号密码模式、代理状态卡与出口检测按钮;接码卡内把注册方式、接码配置、接码订单运行态与“注册手机号”身份运行态分层展示,“注册手机号”位于订单运行状态下方,不嵌入当前分配/验证码网格;设置卡片新增 `Plus 模式` 开关与 PayPal 账号下拉框,右侧使用公共表单弹窗添加账号;GPC Plus 配置额外提供 API 地址、API Key、专用手机号、OTP 渠道、本地 OTP helper 开关、helper URL 与 PINHotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器;设置卡片还新增“授权总超时”开关,用于控制 Step 7 后链 5 分钟总预算。
钥配置行;设置卡片新增 `IP代理` 开关与代理配置折叠区,支持 711Proxy 账号密码模式、代理状态卡与出口检测按钮;接码卡内把注册方式、接码配置、接码订单运行态与“注册手机号”身份运行态分层展示,“注册手机号”位于订单运行状态下方,不嵌入当前分配/验证码网格;设置卡片新增 `Plus 模式` 开关与 PayPal 账号下拉框,右侧使用公共表单弹窗添加账号;GPC Plus 配置额外提供只读 API 地址、API Key、专用手机号、OTP 渠道、本地 OTP helper 开关、helper URL 与 PIN,并提供 `购买卡密``转换 API Key` 入口Hotmail / 2925 两个账号池当前都使用统一的头部“添加账号/取消添加”按钮和共享表单容器;设置卡片还新增“授权总超时”开关,用于控制 Step 7 后链 5 分钟总预算。
- `sidepanel/paypal-manager.js`:侧边栏 PayPal 账号管理器,负责 Plus 模式下的账号下拉框渲染、添加账号弹窗、保存账号与切换当前账号。
- `sidepanel/sidepanel.js`:侧边栏主入口脚本,负责 UI 状态同步、动态步骤渲染、按钮交互、共享验证码自动重发次数配置与广播接收,并装
配 Hotmail / 2925 / iCloud / LuckMail / 贡献模式 / 账号记录面板 / 贡献内容更新服务;当前贡献模式的“开始贡献”会直接复用主自动流程启
@@ -205,7 +205,7 @@
- `tests/background-signup-step2-branching.test.js`:测试在 Gmail / 2925 模式下,已有兼容别名邮箱时应直接复用,不应再次重生成。
- `tests/background-step-modules.test.js`:测试步骤模块文件都已由后台入口加载。
- `tests/background-step5-submit-short-circuit.test.js`:测试步骤 5 会把生成好的资料直接转发给内容脚本,并依赖完成信号收尾。
- `tests/background-step6-retry-limit.test.js`:测试步骤 6 的注册成功等待,以及步骤 7 的有限重试上限与 `add-phone` 命中后的立即跳出行为。
- `tests/background-step6-retry-limit.test.js`:测试步骤 6 的注册成功等待、可选 cookies 清理开关,以及步骤 7 的有限重试上限与 `add-phone` 命中后的立即跳出行为。
- `tests/background-step7-recovery.test.js`:测试步骤 8 获取登录验证码后直接提交(不再回放步骤 7),覆盖邮箱验证码页、真实手机验证码页、`add-email` 绑定邮箱后收码、2925 固定回看窗口与关闭重发间隔。
- `tests/background-step-registry.test.js`:测试后台步骤注册表和共享步骤定义已接入。
- `tests/background-tab-runtime-module.test.js`:测试标签运行时模块已接入且导出工厂,并覆盖等待标签完成、等待标签稳定完成,以及等待过程中的 Stop 中断行为。