Files
FlowPilot/background/steps/paypal-approve.js
T
2026-05-08 20:06:14 -04:00

307 lines
10 KiB
JavaScript

(function attachBackgroundPayPalApprove(root, factory) {
root.MultiPageBackgroundPayPalApprove = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundPayPalApproveModule() {
const PAYPAL_SOURCE = 'paypal-flow';
const PLUS_CHECKOUT_SOURCE = 'plus-checkout';
const PAYPAL_INJECT_FILES = ['content/utils.js', 'content/operation-delay.js', 'content/paypal-flow.js'];
const PAYPAL_LOGIN_TRANSITION_TIMEOUT_MS = 30000;
const PAYPAL_LOGIN_TRANSITION_POLL_MS = 500;
function createPayPalApproveExecutor(deps = {}) {
const {
addLog,
chrome,
completeStepFromBackground,
ensureContentScriptReadyOnTabUntilStopped,
getTabId,
isTabAlive,
sendTabMessageUntilStopped,
setState,
sleepWithStop,
waitForTabCompleteUntilStopped,
waitForTabUrlMatchUntilStopped,
} = deps;
async function resolvePayPalTabId(state = {}) {
const paypalTabId = await getTabId(PAYPAL_SOURCE);
if (paypalTabId && await isTabAlive(PAYPAL_SOURCE)) {
return paypalTabId;
}
const discoveredPayPalTabId = await findOpenPayPalTabId();
if (discoveredPayPalTabId) {
await addLog('步骤 8:已从当前浏览器标签中发现 PayPal 页面,正在接管继续执行。', 'info');
return discoveredPayPalTabId;
}
const checkoutTabId = await getTabId(PLUS_CHECKOUT_SOURCE);
if (checkoutTabId) {
return checkoutTabId;
}
const storedTabId = Number(state.plusCheckoutTabId) || 0;
if (storedTabId) {
return storedTabId;
}
throw new Error('步骤 8:未找到 PayPal 标签页,请先完成步骤 7。');
}
async function findOpenPayPalTabId() {
if (!chrome?.tabs?.query) {
return 0;
}
const tabs = await chrome.tabs.query({}).catch(() => []);
const candidates = (Array.isArray(tabs) ? tabs : [])
.filter((tab) => Number.isInteger(tab?.id) && isPayPalUrl(tab.url || ''));
if (!candidates.length) {
return 0;
}
const match = candidates.find((tab) => tab.active && tab.currentWindow)
|| candidates.find((tab) => tab.active)
|| candidates[0];
if (match?.id && chrome?.tabs?.update) {
await chrome.tabs.update(match.id, { active: true }).catch(() => {});
}
return match?.id || 0;
}
async function ensurePayPalReady(tabId, logMessage = '') {
await waitForTabUrlMatchUntilStopped(tabId, (url) => /paypal\./i.test(url));
await waitForTabCompleteUntilStopped(tabId);
await sleepWithStop(1000);
await ensureContentScriptReadyOnTabUntilStopped(PAYPAL_SOURCE, tabId, {
inject: PAYPAL_INJECT_FILES,
injectSource: PAYPAL_SOURCE,
logMessage: logMessage || '步骤 8:PayPal 页面仍在加载,等待脚本就绪...',
});
}
async function getPayPalState(tabId) {
const result = await sendTabMessageUntilStopped(tabId, PAYPAL_SOURCE, {
type: 'PAYPAL_GET_STATE',
source: 'background',
payload: {},
});
if (result?.error) {
throw new Error(result.error);
}
return result || {};
}
async function dismissPrompts(tabId) {
const result = await sendTabMessageUntilStopped(tabId, PAYPAL_SOURCE, {
type: 'PAYPAL_DISMISS_PROMPTS',
source: 'background',
payload: {},
});
if (result?.error) {
throw new Error(result.error);
}
return result || {};
}
function resolvePayPalCredentials(state = {}) {
const currentId = String(state?.currentPayPalAccountId || '').trim();
const accounts = Array.isArray(state?.paypalAccounts) ? state.paypalAccounts : [];
const selectedAccount = currentId
? accounts.find((account) => String(account?.id || '').trim() === currentId) || null
: null;
return {
email: String(selectedAccount?.email || state?.paypalEmail || '').trim(),
password: String(selectedAccount?.password || state?.paypalPassword || ''),
};
}
async function submitLogin(tabId, state = {}) {
const credentials = resolvePayPalCredentials(state);
if (!credentials.password) {
throw new Error('步骤 8:未配置可用的 PayPal 账号,请先在侧边栏添加并选择账号。');
}
await addLog('步骤 8:正在填写 PayPal 登录信息并提交...', 'info');
const result = await sendTabMessageUntilStopped(tabId, PAYPAL_SOURCE, {
type: 'PAYPAL_SUBMIT_LOGIN',
source: 'background',
payload: {
email: credentials.email,
password: credentials.password,
},
});
if (result?.error) {
throw new Error(result.error);
}
return result || {};
}
function isPayPalUrl(url = '') {
return /paypal\./i.test(String(url || ''));
}
function isPayPalPasswordState(pageState = {}) {
return Boolean(pageState.hasPasswordInput)
|| pageState.loginPhase === 'password'
|| pageState.loginPhase === 'login_combined';
}
async function waitForPayPalPostLoginDecision(tabId, actionResult = {}) {
const phase = String(actionResult?.phase || '').trim();
const startedAt = Date.now();
while (Date.now() - startedAt < PAYPAL_LOGIN_TRANSITION_TIMEOUT_MS) {
const tab = await chrome.tabs.get(tabId).catch(() => null);
if (!tab) {
throw new Error('步骤 8:PayPal 标签页已关闭,无法继续识别登录后的页面。');
}
const currentUrl = tab.url || '';
if (!currentUrl) {
await sleepWithStop(PAYPAL_LOGIN_TRANSITION_POLL_MS);
continue;
}
if (currentUrl && !isPayPalUrl(currentUrl)) {
return {
outcome: 'left_paypal',
url: currentUrl,
};
}
if (tab.status !== 'complete') {
await sleepWithStop(PAYPAL_LOGIN_TRANSITION_POLL_MS);
continue;
}
await ensurePayPalReady(
tabId,
phase === 'email_submitted'
? '步骤 8:PayPal 账号已提交,正在识别下一页...'
: '步骤 8:PayPal 密码已提交,正在识别跳转结果...'
);
const pageState = await getPayPalState(tabId);
if (pageState.hasPasskeyPrompt) {
return {
outcome: 'prompt',
pageState,
};
}
if (pageState.approveReady) {
return {
outcome: 'approve_ready',
pageState,
};
}
if (phase === 'email_submitted' && isPayPalPasswordState(pageState)) {
return {
outcome: 'password_ready',
pageState,
};
}
if (phase === 'password_submitted' && !pageState.needsLogin) {
return {
outcome: 'post_login_state',
pageState,
};
}
await sleepWithStop(PAYPAL_LOGIN_TRANSITION_POLL_MS);
}
return {
outcome: 'timeout',
phase,
};
}
async function clickApprove(tabId) {
const result = await sendTabMessageUntilStopped(tabId, PAYPAL_SOURCE, {
type: 'PAYPAL_CLICK_APPROVE',
source: 'background',
payload: {},
});
if (result?.error) {
throw new Error(result.error);
}
return Boolean(result?.clicked);
}
async function executePayPalApprove(state = {}) {
const tabId = await resolvePayPalTabId(state);
await ensurePayPalReady(tabId);
await setState({ plusCheckoutTabId: tabId });
let loggedWaiting = false;
while (true) {
const currentUrl = (await chrome.tabs.get(tabId).catch(() => null))?.url || '';
if (currentUrl && !isPayPalUrl(currentUrl)) {
await addLog('步骤 8:PayPal 已跳转离开授权页,准备进入回跳确认。', 'ok');
break;
}
await ensurePayPalReady(tabId, '步骤 8:PayPal 页面正在切换,等待脚本重新就绪...');
const pageState = await getPayPalState(tabId);
if (pageState.needsLogin) {
const submitResult = await submitLogin(tabId, state);
const decision = await waitForPayPalPostLoginDecision(tabId, submitResult);
if (decision.outcome === 'left_paypal') {
await addLog('步骤 8:PayPal 登录后已跳转离开登录/授权页,继续进入回跳确认。', 'ok');
break;
}
if (decision.outcome === 'password_ready') {
await addLog('步骤 8:PayPal 账号页提交后已识别到密码页,继续填写密码。', 'info');
} else if (decision.outcome === 'approve_ready') {
await addLog('步骤 8:PayPal 登录后已识别到授权确认页,继续点击授权。', 'info');
} else if (decision.outcome === 'prompt') {
await addLog('步骤 8:PayPal 登录后已识别到提示弹窗,继续处理弹窗。', 'info');
} else if (decision.outcome === 'timeout') {
await addLog('步骤 8:PayPal 登录动作后暂未识别到新页面,重新检查当前页面状态。', 'warn');
}
loggedWaiting = false;
continue;
}
if (pageState.hasPasskeyPrompt) {
await addLog('步骤 8:检测到 PayPal 通行密钥提示,正在关闭...', 'info');
await dismissPrompts(tabId);
await sleepWithStop(1000);
continue;
}
const dismissed = await dismissPrompts(tabId).catch(() => ({ clicked: 0 }));
if (dismissed.clicked) {
await sleepWithStop(1000);
continue;
}
if (pageState.approveReady) {
await addLog('步骤 8:正在点击 PayPal“同意并继续”...', 'info');
const clicked = await clickApprove(tabId);
if (clicked) {
await setState({ plusPaypalApprovedAt: Date.now() });
break;
}
}
if (!loggedWaiting) {
loggedWaiting = true;
await addLog('步骤 8:等待 PayPal 授权按钮或下一步页面出现...', 'info');
}
await sleepWithStop(500);
}
await completeStepFromBackground(8, {
plusPaypalApprovedAt: Date.now(),
});
}
return {
executePayPalApprove,
};
}
return {
createPayPalApproveExecutor,
};
});