182 lines
4.1 KiB
TypeScript
182 lines
4.1 KiB
TypeScript
|
|
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<string>;
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
type FetchLike = (input: string, init: RequestInit) => Promise<Response>;
|
||
|
|
|
||
|
|
type RequestActivityCheckOptions = {
|
||
|
|
appUrl: unknown;
|
||
|
|
order?: CheckoutOrder;
|
||
|
|
locale: string;
|
||
|
|
getSessionToken: () => Promise<string>;
|
||
|
|
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<ActivityResponse> {
|
||
|
|
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<string, unknown>;
|
||
|
|
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,
|
||
|
|
};
|
||
|
|
}
|