fix(gpc): keep auto mode when balance permission is unknown
This commit is contained in:
@@ -210,13 +210,42 @@
|
||||
return Number.isFinite(numeric) ? Math.max(0, Math.floor(numeric)) : null;
|
||||
}
|
||||
|
||||
function isGpcAutoModeEnabled(payload = {}) {
|
||||
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
||||
if (rootScope.GoPayUtils?.isGpcAutoModeEnabled) {
|
||||
return rootScope.GoPayUtils.isGpcAutoModeEnabled(payload);
|
||||
function normalizeGpcAutoModePermissionValue(value) {
|
||||
if (typeof value === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
if (value === 1) return true;
|
||||
if (value === 0) return false;
|
||||
}
|
||||
const normalized = String(value ?? '').trim().toLowerCase();
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
if (['true', '1', 'yes', 'y', 'on', 'enabled', 'enable'].includes(normalized)) {
|
||||
return true;
|
||||
}
|
||||
if (['false', '0', 'no', 'n', 'off', 'disabled', 'disable'].includes(normalized)) {
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getGpcAutoModePermission(payload = {}) {
|
||||
const data = unwrapGpcResponse(payload);
|
||||
return data?.auto_mode_enabled === true || data?.autoModeEnabled === true;
|
||||
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
||||
return null;
|
||||
}
|
||||
return normalizeGpcAutoModePermissionValue(
|
||||
data.auto_mode_enabled
|
||||
?? data.autoModeEnabled
|
||||
?? data.auto_enabled
|
||||
?? data.autoEnabled
|
||||
);
|
||||
}
|
||||
|
||||
function isGpcAutoModePermissionDenied(payload = {}) {
|
||||
return getGpcAutoModePermission(payload) === false;
|
||||
}
|
||||
|
||||
async function assertGpcApiKeyReadyForCreate(state = {}, phoneMode = GPC_HELPER_PHONE_MODE_MANUAL, apiKey = '') {
|
||||
@@ -244,7 +273,7 @@
|
||||
if (remainingUses !== null && remainingUses <= 0) {
|
||||
throw new Error('创建 GPC 订单失败:API Key 剩余次数不足。');
|
||||
}
|
||||
if (phoneMode === GPC_HELPER_PHONE_MODE_AUTO && !isGpcAutoModeEnabled(balanceData)) {
|
||||
if (phoneMode === GPC_HELPER_PHONE_MODE_AUTO && isGpcAutoModePermissionDenied(balanceData)) {
|
||||
throw new Error('创建 GPC 订单失败:当前 GPC API Key 未开通自动模式。');
|
||||
}
|
||||
}
|
||||
|
||||
+10
-3
@@ -7775,7 +7775,7 @@ async function ensureGpcApiKeyReadyForStart(options = {}) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (selectedMode === GPC_HELPER_PHONE_MODE_AUTO && !balanceState.gopayHelperAutoModeEnabled) {
|
||||
if (selectedMode === GPC_HELPER_PHONE_MODE_AUTO && isGpcAutoModePermissionDenied(balanceState)) {
|
||||
if (typeof selectGpcHelperPhoneMode !== 'undefined' && selectGpcHelperPhoneMode) {
|
||||
selectGpcHelperPhoneMode.value = GPC_HELPER_PHONE_MODE_MANUAL;
|
||||
}
|
||||
@@ -11805,15 +11805,22 @@ btnGpcHelperBalance?.addEventListener('click', async () => {
|
||||
gopayHelperAutoModeEnabled: getGpcAutoModeEnabledFromResponse(response),
|
||||
gopayHelperApiKeyStatus: response?.apiKeyStatus || response?.data?.status || response?.payload?.data?.status || response?.payload?.status || '',
|
||||
};
|
||||
const nextAutoModePermission = getGpcAutoModePermissionFromPayload(nextState.gopayHelperBalancePayload);
|
||||
const nextAutoModeDenied = nextAutoModePermission === false;
|
||||
const nextAutoModeConfirmed = nextAutoModePermission === true || nextState.gopayHelperAutoModeEnabled;
|
||||
const selectedModeBeforeBalanceState = getSelectedGpcHelperPhoneMode();
|
||||
syncLatestState(nextState);
|
||||
if (!nextState.gopayHelperAutoModeEnabled && selectedModeBeforeBalanceState === GPC_HELPER_PHONE_MODE_AUTO) {
|
||||
if (nextAutoModeDenied && selectedModeBeforeBalanceState === GPC_HELPER_PHONE_MODE_AUTO) {
|
||||
selectGpcHelperPhoneMode.value = GPC_HELPER_PHONE_MODE_MANUAL;
|
||||
syncLatestState({ gopayHelperPhoneMode: GPC_HELPER_PHONE_MODE_MANUAL });
|
||||
await saveSettings({ silent: true, force: true }).catch(() => {});
|
||||
showToast('当前 API Key 未开通自动模式,已切回手动模式。', 'warn');
|
||||
} else if (nextAutoModeDenied) {
|
||||
showToast('GPC 余额已更新,当前 API Key 只能使用手动模式。', 'success');
|
||||
} else if (nextAutoModeConfirmed) {
|
||||
showToast('GPC 余额已更新,自动模式可用。', 'success');
|
||||
} else {
|
||||
showToast(nextState.gopayHelperAutoModeEnabled ? 'GPC 余额已更新,自动模式可用。' : 'GPC 余额已更新,当前 API Key 只能使用手动模式。', 'success');
|
||||
showToast('GPC 余额已更新,当前接口未返回自动模式权限,已保留所选模式。', 'success');
|
||||
}
|
||||
updatePlusModeUI();
|
||||
} catch (error) {
|
||||
|
||||
@@ -510,6 +510,63 @@ test('GPC auto checkout only sends access token and API Key', async () => {
|
||||
assert.equal(events.find((event) => event.type === 'complete')?.step, 6);
|
||||
});
|
||||
|
||||
test('GPC auto checkout keeps running when balance payload omits auto mode permission', async () => {
|
||||
const events = [];
|
||||
const fetchCalls = [];
|
||||
const executor = api.createPlusCheckoutCreateExecutor({
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
create: async () => {
|
||||
throw new Error('should not open token tab when direct access token exists');
|
||||
},
|
||||
remove: async () => {},
|
||||
},
|
||||
},
|
||||
completeStepFromBackground: async (step, payload) => events.push({ type: 'complete', step, payload }),
|
||||
ensureContentScriptReadyOnTabUntilStopped: async () => {},
|
||||
fetch: async (url, options = {}) => {
|
||||
fetchCalls.push({ url, options });
|
||||
return {
|
||||
ok: true,
|
||||
status: 200,
|
||||
json: async () => url.endsWith('/api/gp/balance')
|
||||
? createGpcBalanceResponse({ auto_mode_enabled: undefined, remaining_uses: 998 })
|
||||
: createGpcTaskResponse({
|
||||
task_id: 'task_auto_unknown_permission',
|
||||
status: 'queued',
|
||||
status_text: '排队中',
|
||||
phone_mode: 'auto',
|
||||
api_waiting_for: '',
|
||||
}),
|
||||
};
|
||||
},
|
||||
registerTab: async () => {},
|
||||
sendTabMessageUntilStopped: async () => ({}),
|
||||
setState: async (payload) => events.push({ type: 'set-state', payload }),
|
||||
sleepWithStop: async () => {},
|
||||
waitForTabCompleteUntilStopped: async () => {},
|
||||
});
|
||||
|
||||
await executor.executePlusCheckoutCreate({
|
||||
plusPaymentMethod: 'gpc-helper',
|
||||
gopayHelperPhoneMode: 'auto',
|
||||
gopayHelperApiUrl: 'https://gpc.qlhazycoder.top/',
|
||||
chatgptAccessToken: 'state-access-token',
|
||||
gopayHelperApiKey: 'gpc_auto_123',
|
||||
});
|
||||
|
||||
assert.equal(fetchCalls.length, 2);
|
||||
assert.equal(fetchCalls[0].url, 'https://gpc.qlhazycoder.top/api/gp/balance');
|
||||
assert.equal(fetchCalls[1].url, 'https://gpc.qlhazycoder.top/api/gp/tasks');
|
||||
const helperPayload = JSON.parse(fetchCalls[1].options.body);
|
||||
assert.equal(helperPayload.phone_mode, 'auto');
|
||||
const statePayload = events.find((event) => event.type === 'set-state')?.payload || {};
|
||||
assert.equal(statePayload.gopayHelperTaskId, 'task_auto_unknown_permission');
|
||||
assert.equal(statePayload.gopayHelperPhoneMode, 'auto');
|
||||
assert.equal(events.find((event) => event.type === 'complete')?.step, 6);
|
||||
});
|
||||
|
||||
test('GPC auto checkout blocks API Keys without auto mode permission', async () => {
|
||||
const fetchCalls = [];
|
||||
const executor = api.createPlusCheckoutCreateExecutor({
|
||||
|
||||
@@ -502,6 +502,70 @@ return { updatePlusModeUI, selectGpcHelperPhoneMode, plusPaymentMethodCaption, r
|
||||
assert.match(api.plusPaymentMethodCaption.textContent, /自动/);
|
||||
});
|
||||
|
||||
test('sidepanel start check keeps GPC auto mode when balance payload omits permission field', async () => {
|
||||
const bundle = [
|
||||
extractFunction('normalizeGpcAutoModePermissionValue'),
|
||||
extractFunction('getGpcAutoModePermissionFromPayload'),
|
||||
extractFunction('isGpcAutoModePermissionDenied'),
|
||||
extractFunction('normalizeGpcRemainingUsesValue'),
|
||||
extractFunction('ensureGpcApiKeyReadyForStart'),
|
||||
].join('\n');
|
||||
|
||||
const api = new Function(`
|
||||
let latestState = { gopayHelperPhoneMode: 'auto' };
|
||||
const GPC_HELPER_PHONE_MODE_AUTO = 'auto';
|
||||
const GPC_HELPER_PHONE_MODE_MANUAL = 'manual';
|
||||
const selectGpcHelperPhoneMode = { value: 'auto' };
|
||||
const dialogs = [];
|
||||
let saveCalls = 0;
|
||||
let updateCalls = 0;
|
||||
${bundle}
|
||||
function isGpcHelperCheckoutSelected() { return true; }
|
||||
function getSelectedGpcHelperPhoneMode() { return selectGpcHelperPhoneMode.value; }
|
||||
async function refreshGpcBalanceForStart() {
|
||||
return {
|
||||
gopayHelperRemainingUses: 998,
|
||||
gopayHelperApiKeyStatus: 'active',
|
||||
gopayHelperAutoModeEnabled: false,
|
||||
gopayHelperBalancePayload: {
|
||||
status: 'active',
|
||||
remaining_uses: 998,
|
||||
},
|
||||
};
|
||||
}
|
||||
async function showGpcStartBlockedDialog(message) {
|
||||
dialogs.push(message);
|
||||
}
|
||||
function syncLatestState(nextState) {
|
||||
latestState = { ...latestState, ...nextState };
|
||||
}
|
||||
function updatePlusModeUI() {
|
||||
updateCalls += 1;
|
||||
}
|
||||
async function saveSettings() {
|
||||
saveCalls += 1;
|
||||
}
|
||||
function showToast() {}
|
||||
return {
|
||||
ensureGpcApiKeyReadyForStart,
|
||||
selectGpcHelperPhoneMode,
|
||||
getDialogs: () => dialogs.slice(),
|
||||
getSaveCalls: () => saveCalls,
|
||||
getUpdateCalls: () => updateCalls,
|
||||
getPersistedPhoneMode: () => latestState.gopayHelperPhoneMode,
|
||||
};
|
||||
`)();
|
||||
|
||||
const allowed = await api.ensureGpcApiKeyReadyForStart();
|
||||
|
||||
assert.equal(allowed, true);
|
||||
assert.equal(api.selectGpcHelperPhoneMode.value, 'auto');
|
||||
assert.equal(api.getPersistedPhoneMode(), 'auto');
|
||||
assert.equal(api.getSaveCalls(), 0);
|
||||
assert.equal(api.getUpdateCalls(), 0);
|
||||
assert.deepEqual(api.getDialogs(), []);
|
||||
});
|
||||
|
||||
test('sidepanel resolves pending GoPay manual confirmation from DATA_UPDATED state', async () => {
|
||||
const bundle = [
|
||||
extractFunction('openPlusManualConfirmationDialog'),
|
||||
|
||||
Reference in New Issue
Block a user