export type ActivityResponse = | { success: true; redirectUrl: string; buttonText: string; } | { success: false; }; export type CheckoutOrder = { id?: string; name?: string; }; export type CheckoutExtensionApi = { settings?: { current?: { app_url?: unknown; }; value?: { app_url?: unknown; }; }; orderConfirmation?: { current?: { order?: CheckoutOrder; }; value?: { order?: CheckoutOrder; }; }; localization?: { current?: { language?: { isoCode?: string; }; }; value?: { language?: { isoCode?: string; }; }; }; i18n?: { locale?: string; }; sessionToken: { get: () => Promise; }; }; type FetchLike = (input: string, init: RequestInit) => Promise; type RequestActivityCheckOptions = { appUrl: unknown; order?: CheckoutOrder; locale: string; getSessionToken: () => Promise; fetchImpl?: FetchLike; timeoutMs?: number; }; export const DEFAULT_ACTIVITY_REQUEST_TIMEOUT_MS = 10_000; export function normalizeAppUrl(rawValue: unknown): string | null { if (typeof rawValue !== "string" || rawValue.trim() === "") { return null; } try { const url = new URL(rawValue.trim()); if (url.protocol !== "https:" || url.username || url.password) { return null; } const pathname = url.pathname === "/" ? "" : url.pathname.replace(/\/$/, ""); return `${url.origin}${pathname}`; } catch { return null; } } export async function requestActivityCheck({ appUrl, order, locale, getSessionToken, fetchImpl = fetch, timeoutMs = DEFAULT_ACTIVITY_REQUEST_TIMEOUT_MS, }: RequestActivityCheckOptions): Promise { const baseUrl = normalizeAppUrl(appUrl); if (!baseUrl || !order?.id) { return hiddenActivityResponse(); } const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); try { const token = await getSessionToken(); if (typeof token !== "string" || token.trim() === "") { return hiddenActivityResponse(); } const endpoint = new URL("api/action/activity/check", `${baseUrl}/`).toString(); const response = await fetchImpl(endpoint, { method: "POST", headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json", }, body: JSON.stringify({ orderId: order.id, orderNumber: order.name, locale, }), cache: "no-store", signal: controller.signal, }); if (!response.ok) { return hiddenActivityResponse(); } const payload = await response.json().catch(() => null); return toActivityResponse(payload); } catch { return hiddenActivityResponse(); } finally { clearTimeout(timeout); } } export function getAppUrlSetting(api: CheckoutExtensionApi): unknown { return api.settings?.current?.app_url ?? api.settings?.value?.app_url; } export function getCheckoutOrder(api: CheckoutExtensionApi): CheckoutOrder | undefined { return api.orderConfirmation?.current?.order ?? api.orderConfirmation?.value?.order; } export function getCheckoutLocale(api: CheckoutExtensionApi): string { return ( api.localization?.current?.language?.isoCode || api.localization?.value?.language?.isoCode || api.i18n?.locale || "en" ); } function toActivityResponse(payload: unknown): ActivityResponse { if (!payload || typeof payload !== "object") { return hiddenActivityResponse(); } const response = payload as Record; if (response.success !== true) { return hiddenActivityResponse(); } if (typeof response.redirectUrl !== "string" || response.redirectUrl.trim() === "") { return hiddenActivityResponse(); } if (typeof response.buttonText !== "string" || response.buttonText.trim() === "") { return hiddenActivityResponse(); } return { success: true, redirectUrl: response.redirectUrl, buttonText: response.buttonText, }; } function hiddenActivityResponse(): ActivityResponse { return { success: false, }; }