import type { MembershipProduct } from "./membership"; export const PAYMENT_PACKAGES_CACHE_TTL_MS = 60_000; export type PaymentPackagesPayload = Readonly<{ enabled: boolean; packages: MembershipProduct[]; }>; type CacheEntry = Readonly<{ value: PaymentPackagesPayload; fetchedAt: number; }>; let cache: CacheEntry | null = null; let inflight: Promise | null = null; async function loadPaymentPackages(): Promise { const response = await fetch("/api/payment/packages", { cache: "no-store" }); const payload = await response.json().catch(() => null); if (!response.ok || !payload || typeof payload !== "object") { throw new Error("套餐支付暂时不可用,请稍后重试"); } const record = payload as { enabled?: unknown; packages?: unknown }; return { enabled: record.enabled === true, packages: Array.isArray(record.packages) ? record.packages as MembershipProduct[] : [], }; } function refreshPaymentPackages(): Promise { if (inflight) return inflight; const request = loadPaymentPackages().then((value) => { cache = { value, fetchedAt: Date.now() }; return value; }).finally(() => { if (inflight === request) inflight = null; }); inflight = request; return request; } /** * Return a fresh package response, or serve the last response while refreshing * it in the background after the 60-second freshness window expires. */ export function fetchPaymentPackages( onBackgroundRefresh?: (value: PaymentPackagesPayload) => void, ): Promise { if (!cache) return refreshPaymentPackages(); if (Date.now() - cache.fetchedAt < PAYMENT_PACKAGES_CACHE_TTL_MS) { return Promise.resolve(cache.value); } const request = refreshPaymentPackages(); void request.then((value) => onBackgroundRefresh?.(value)).catch(() => { // Stale package data remains usable when a background refresh fails. }); return Promise.resolve(cache.value); } export function prefetchPaymentPackages(): void { void fetchPaymentPackages().catch(() => undefined); } export function resetPaymentPackagesCacheForTests(): void { cache = null; inflight = null; }