// The PayPal JS SDK is injected at runtime by the script tag below, so there is // no package to import types from. This declares the sliver of it this app // actually uses, rather than repeating `(window as any).paypal` at each call // site — three casts that each silently opted out of type checking. // // Deliberately narrow: it describes what is called here, not the whole SDK. A // wider guess would be fiction, and a wrong shape typed confidently is worse // than an honest `unknown`. export interface PaypalButtonsConfig { createOrder: () => Promise; onApprove: (data: { orderID: string }) => Promise | void; onError: (err: unknown) => void; } export interface PaypalSdk { Buttons: (config: PaypalButtonsConfig) => { render: (selector: string) => void }; } declare global { interface Window { // Absent until the SDK script has loaded, which is what loadPaypalSdk and // every caller has to check before using it. paypal?: PaypalSdk; } } let loadPromise: Promise | null = null; export function loadPaypalSdk(clientId: string, currency: string): Promise { if (window.paypal) return Promise.resolve(); if (loadPromise) return loadPromise; loadPromise = new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = `https://www.paypal.com/sdk/js?client-id=${encodeURIComponent(clientId)}¤cy=${currency}`; script.onload = () => resolve(); script.onerror = () => reject(new Error('failed to load paypal sdk')); document.head.appendChild(script); }); return loadPromise; }