esaalat 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -1
- package/dist/esaalat.global.js +3 -3
- package/dist/index.cjs +319 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +182 -2
- package/dist/index.d.ts +182 -2
- package/dist/index.js +314 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/modal.ts","../src/window.ts","../src/client.ts"],"sourcesContent":["export { EsaalatCheckout } from \"./client\";\nexport { EsaalatCheckoutError } from \"./errors\";\nexport type { CheckoutErrorCode } from \"./errors\";\nexport type {\n CheckoutItem,\n CheckoutIntent,\n CheckoutLocale,\n CheckoutOpenOptions,\n CheckoutPresentation,\n CheckoutProvider,\n CheckoutResult,\n CheckoutResultReason,\n CheckoutResultStatus,\n CheckoutSession,\n CheckoutWallet,\n EsaalatCheckoutOptions,\n ExpectedReferenceType,\n} from \"./types\";\n\nexport { EsaalatCheckout as default } from \"./client\";\n","export type CheckoutErrorCode =\n | \"configuration\"\n | \"network\"\n | \"api\"\n | \"session\"\n | \"unavailable\"\n | \"aborted\";\n\nexport class EsaalatCheckoutError extends Error {\n readonly code: CheckoutErrorCode;\n readonly status?: number;\n readonly details?: unknown;\n\n constructor(\n message: string,\n code: CheckoutErrorCode,\n options: { status?: number; details?: unknown; cause?: unknown } = {}\n ) {\n super(message);\n this.name = \"EsaalatCheckoutError\";\n this.code = code;\n this.status = options.status;\n this.details = options.details;\n if (options.cause !== undefined) {\n // `cause` is ES2022; assigning it keeps the package ES2020-compatible.\n (this as Error & { cause?: unknown }).cause = options.cause;\n }\n }\n}\n","import type { CheckoutView } from \"./view\";\n\nexport interface CheckoutModalLabels {\n close: string;\n loading: string;\n errorTitle: string;\n}\n\ninterface CheckoutModalOptions {\n zIndex: number;\n title: string;\n labels: CheckoutModalLabels;\n onDismiss: () => void;\n}\n\nconst MODAL_CSS = `\n :host { all: initial; }\n *, *::before, *::after { box-sizing: border-box; }\n .overlay {\n position: fixed;\n inset: 0;\n z-index: 0;\n display: grid;\n place-items: center;\n padding: 12px;\n background: rgba(12, 11, 8, 0.58);\n -webkit-backdrop-filter: blur(7px);\n backdrop-filter: blur(7px);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n color: #0b0b0b;\n pointer-events: auto;\n animation: fade 180ms ease-out;\n }\n .dialog {\n position: relative;\n width: min(460px, calc(100vw - 24px));\n height: min(720px, calc(100dvh - 24px));\n overflow: hidden;\n border-radius: 22px;\n background: #fffefa;\n border: 1px solid rgba(255, 255, 255, 0.5);\n box-shadow: 0 36px 100px rgba(0, 0, 0, 0.4), 0 6px 24px rgba(0, 0, 0, 0.2);\n animation: rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1);\n }\n .dialog.is-frame { background: #f3f0e8; }\n .chrome {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 16px;\n border-bottom: 1px solid #e7e2d7;\n background: #fffefa;\n }\n .dialog.is-frame .chrome { display: none; }\n .mark {\n width: 28px;\n height: 28px;\n border-radius: 8px;\n display: grid;\n place-items: center;\n background: #0b0b0b;\n color: #fff;\n font-size: 14px;\n font-weight: 800;\n flex-shrink: 0;\n }\n .chrome-title {\n font-size: 13px;\n font-weight: 700;\n line-height: 1.3;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .close {\n width: 32px;\n height: 32px;\n display: grid;\n place-items: center;\n border: 0;\n border-radius: 9px;\n background: transparent;\n color: #716f67;\n cursor: pointer;\n flex-shrink: 0;\n }\n .close:hover { background: #f3f0e8; color: #0b0b0b; }\n .close:focus-visible { outline: 2px solid #0b0b0b; outline-offset: 2px; }\n .close svg { width: 18px; height: 18px; }\n .content {\n position: absolute;\n inset: 57px 0 0;\n overflow: hidden;\n }\n .dialog.is-frame .content { inset: 0; }\n .frame {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n background: #f3f0e8;\n }\n .panel {\n height: 100%;\n display: grid;\n place-content: center;\n justify-items: center;\n gap: 12px;\n padding: 32px;\n text-align: center;\n }\n .spinner {\n width: 34px;\n height: 34px;\n border-radius: 50%;\n border: 2px solid #e7e2d7;\n border-top-color: #0b0b0b;\n animation: spin 900ms linear infinite;\n }\n .loading { font-size: 13px; color: #716f67; }\n .error-icon {\n width: 54px;\n height: 54px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n background: #fef1ef;\n border: 1px solid #f5cfca;\n color: #b42318;\n }\n .error-icon svg { width: 25px; height: 25px; }\n .error-title { margin: 0; font-size: 17px; line-height: 1.4; }\n .error-message { margin: 0; font-size: 13px; line-height: 1.7; color: #716f67; max-width: 34ch; }\n .error-close {\n margin-top: 6px;\n min-height: 42px;\n padding: 9px 22px;\n border: 1px solid #e7e2d7;\n border-radius: 11px;\n background: transparent;\n color: #0b0b0b;\n font: inherit;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n }\n .error-close:hover { background: #f3f0e8; }\n .error-close:focus-visible { outline: 2px solid #0b0b0b; outline-offset: 2px; }\n @keyframes spin { to { transform: rotate(360deg); } }\n @keyframes fade { from { opacity: 0; } }\n @keyframes rise { from { opacity: 0; transform: translateY(10px) scale(0.985); } }\n @media (max-width: 520px) {\n .overlay { padding: 0; }\n .dialog {\n width: 100vw;\n height: 100dvh;\n max-height: none;\n border-radius: 0;\n border: 0;\n }\n .chrome { padding-top: calc(14px + env(safe-area-inset-top)); }\n }\n @media (prefers-color-scheme: dark) {\n .overlay { background: rgba(0, 0, 0, 0.7); }\n .dialog, .chrome { background: #1d1b17; border-color: #343028; color: #f6f2e8; }\n .dialog.is-frame, .frame { background: #141310; }\n .chrome-title { color: #f6f2e8; }\n .close { color: #aaa59a; }\n .close:hover { background: #2b2721; color: #f6f2e8; }\n .spinner { border-color: #343028; border-top-color: #f6f2e8; }\n .loading, .error-message { color: #aaa59a; }\n .error-icon { background: #2b1b19; border-color: #4b2c28; color: #f18d83; }\n .error-close { border-color: #343028; color: #f6f2e8; }\n .error-close:hover { background: #2b2721; }\n }\n @media (prefers-reduced-motion: reduce) {\n .overlay, .dialog { animation: none; }\n .spinner { animation-duration: 0.01ms; }\n }\n`;\n\nfunction element<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className) node.className = className;\n return node;\n}\n\nfunction closeSvg(): SVGSVGElement {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"1.8\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n const path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n path.setAttribute(\"d\", \"M18 6 6 18M6 6l12 12\");\n svg.append(path);\n return svg;\n}\n\nfunction alertSvg(): SVGSVGElement {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"1.8\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n const path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n path.setAttribute(\"d\", \"M12 9v4M12 17h.01M10.3 3.9 2.5 17.4A2 2 0 0 0 4.2 20h15.6a2 2 0 0 0 1.7-2.6L13.7 3.9a2 2 0 0 0-3.4 0Z\");\n svg.append(path);\n return svg;\n}\n\n/**\n * The modal lives in a shadow root so a merchant's global CSS cannot leak into\n * the checkout (or the other way around). The iframe is the only content once\n * the session is ready; the portal owns its own close button.\n */\nexport class CheckoutModal implements CheckoutView {\n private readonly host: HTMLDivElement;\n private readonly shadow: ShadowRoot;\n private readonly overlay: HTMLDivElement;\n private readonly dialog: HTMLDivElement;\n private readonly closeButton: HTMLButtonElement;\n private readonly content: HTMLDivElement;\n private frame: HTMLIFrameElement | null = null;\n private previousOverflow = \"\";\n private previousFocus: Element | null = null;\n private destroyed = false;\n\n onLoad: (() => void) | null = null;\n private readonly onDismiss: () => void;\n\n constructor(private readonly options: CheckoutModalOptions) {\n this.onDismiss = options.onDismiss;\n\n this.host = document.createElement(\"div\");\n this.host.setAttribute(\"data-esaalat-checkout\", \"\");\n this.host.style.cssText = `all: initial; position: fixed; inset: 0; z-index: ${options.zIndex}; pointer-events: none;`;\n\n this.shadow = this.host.attachShadow({ mode: \"open\" });\n const style = element(\"style\");\n style.textContent = MODAL_CSS;\n this.shadow.append(style);\n\n this.overlay = element(\"div\", \"overlay\");\n this.overlay.addEventListener(\"mousedown\", (event) => {\n if (event.target === this.overlay) this.onDismiss();\n });\n\n this.dialog = element(\"div\", \"dialog\");\n this.dialog.setAttribute(\"role\", \"dialog\");\n this.dialog.setAttribute(\"aria-modal\", \"true\");\n this.dialog.setAttribute(\"aria-label\", options.title);\n\n const chrome = element(\"div\", \"chrome\");\n const mark = element(\"span\", \"mark\");\n mark.textContent = \"E\";\n mark.setAttribute(\"aria-hidden\", \"true\");\n const title = element(\"span\", \"chrome-title\");\n title.textContent = options.title;\n\n this.closeButton = element(\"button\", \"close\");\n this.closeButton.type = \"button\";\n this.closeButton.setAttribute(\"aria-label\", options.labels.close);\n this.closeButton.append(closeSvg());\n this.closeButton.addEventListener(\"click\", () => this.onDismiss());\n\n chrome.append(mark, title, this.closeButton);\n\n this.content = element(\"div\", \"content\");\n this.showLoading();\n\n this.dialog.append(chrome, this.content);\n this.overlay.append(this.dialog);\n this.shadow.append(this.overlay);\n }\n\n open(): void {\n if (!document.body) {\n throw new Error(\"Esaalat checkout needs a document body.\");\n }\n this.previousOverflow = document.body.style.overflow;\n this.previousFocus = document.activeElement;\n document.body.style.overflow = \"hidden\";\n document.body.append(this.host);\n document.addEventListener(\"keydown\", this.handleKeyDown, true);\n this.closeButton.focus({ preventScroll: true });\n }\n\n load(url: string): void {\n if (this.destroyed) return;\n this.dialog.classList.add(\"is-frame\");\n this.content.replaceChildren();\n\n const frame = document.createElement(\"iframe\");\n frame.className = \"frame\";\n frame.src = url;\n frame.title = this.options.title;\n frame.setAttribute(\"allow\", \"clipboard-write\");\n frame.setAttribute(\"referrerpolicy\", \"strict-origin-when-cross-origin\");\n frame.setAttribute(\"loading\", \"eager\");\n frame.addEventListener(\"load\", () => {\n this.onLoad?.();\n try {\n frame.focus({ preventScroll: true });\n } catch {\n // focus is best-effort in embedded browsers\n }\n });\n\n this.frame = frame;\n this.content.append(frame);\n }\n\n get contentWindow(): Window | null {\n return this.frame?.contentWindow ?? null;\n }\n\n showError(message: string): void {\n if (this.destroyed) return;\n this.content.replaceChildren();\n\n const panel = element(\"div\", \"panel\");\n const icon = element(\"span\", \"error-icon\");\n icon.append(alertSvg());\n const title = element(\"h2\", \"error-title\");\n title.textContent = this.options.labels.errorTitle;\n const text = element(\"p\", \"error-message\");\n text.textContent = message;\n const close = element(\"button\", \"error-close\");\n close.type = \"button\";\n close.textContent = this.options.labels.close;\n close.addEventListener(\"click\", () => this.onDismiss());\n\n panel.append(icon, title, text, close);\n this.content.append(panel);\n this.dialog.classList.remove(\"is-frame\");\n close.focus({ preventScroll: true });\n }\n\n private showLoading(): void {\n this.content.replaceChildren();\n const panel = element(\"div\", \"panel\");\n const spinner = element(\"span\", \"spinner\");\n spinner.setAttribute(\"aria-hidden\", \"true\");\n const label = element(\"p\", \"loading\");\n label.textContent = this.options.labels.loading;\n panel.append(spinner, label);\n this.content.append(panel);\n }\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n this.onDismiss();\n }\n };\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n document.removeEventListener(\"keydown\", this.handleKeyDown, true);\n document.body.style.overflow = this.previousOverflow;\n this.host.remove();\n if (this.previousFocus instanceof HTMLElement && this.previousFocus.isConnected) {\n try {\n this.previousFocus.focus({ preventScroll: true });\n } catch {\n // ignore\n }\n }\n this.onLoad = null;\n this.frame = null;\n }\n}\n","import type { CheckoutView } from \"./view\";\n\nexport interface CheckoutWindowOptions {\n title: string;\n labels: {\n loading: string;\n errorTitle: string;\n close: string;\n };\n dir: \"rtl\" | \"ltr\";\n lang: string;\n onDismiss: () => void;\n}\n\nconst WINDOW_NAME = \"esaalat-checkout\";\nconst WINDOW_WIDTH = 480;\nconst WINDOW_HEIGHT = 800;\nconst CLOSE_POLL_MS = 400;\n\nconst DOC_CSS = `\n * { box-sizing: border-box; }\n html, body { height: 100%; }\n body {\n margin: 0;\n display: grid;\n place-items: center;\n padding: 24px;\n background: #f3f0e8;\n color: #0b0b0b;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n }\n .panel {\n display: grid;\n justify-items: center;\n gap: 14px;\n max-width: 34ch;\n text-align: center;\n }\n .error-icon {\n width: 54px;\n height: 54px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n background: #fef1ef;\n border: 1px solid #f5cfca;\n color: #b42318;\n font-size: 24px;\n font-weight: 800;\n }\n .error-title { margin: 0; font-size: 17px; line-height: 1.4; }\n .error-message { margin: 0; font-size: 13px; line-height: 1.7; color: #716f67; }\n .error-close {\n margin-top: 4px;\n min-height: 42px;\n padding: 9px 22px;\n border: 1px solid #ded7c8;\n border-radius: 11px;\n background: transparent;\n color: #0b0b0b;\n font: inherit;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n }\n .error-close:hover { background: #e9e4d8; }\n @media (prefers-color-scheme: dark) {\n body { background: #141310; color: #f6f2e8; }\n .error-message { color: #aaa59a; }\n .error-icon { background: #2b1b19; border-color: #4b2c28; color: #f18d83; }\n .error-close { border-color: #343028; color: #f6f2e8; }\n .error-close:hover { background: #2b2721; }\n }\n`;\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\n/** A centered popup, sized like the checkout card it replaces. */\nfunction windowFeatures(): string {\n const screenWidth = window.screen?.availWidth ?? WINDOW_WIDTH;\n const screenHeight = window.screen?.availHeight ?? WINDOW_HEIGHT;\n const width = Math.min(WINDOW_WIDTH, Math.max(320, screenWidth - 40));\n const height = Math.min(WINDOW_HEIGHT, Math.max(480, screenHeight - 40));\n const baseX = typeof window.screenX === \"number\" ? window.screenX : 0;\n const baseY = typeof window.screenY === \"number\" ? window.screenY : 0;\n const outerWidth = window.outerWidth || width;\n const outerHeight = window.outerHeight || height;\n const left = Math.max(0, Math.round(baseX + (outerWidth - width) / 2));\n const top = Math.max(0, Math.round(baseY + (outerHeight - height) / 2));\n\n return [\n \"popup=yes\",\n `width=${width}`,\n `height=${height}`,\n `left=${left}`,\n `top=${top}`,\n \"resizable=yes\",\n \"scrollbars=yes\",\n ].join(\",\");\n}\n\nfunction documentHtml(\n title: string,\n dir: \"rtl\" | \"ltr\",\n lang: string,\n body: string\n): string {\n return [\n \"<!doctype html>\",\n `<html dir=\"${dir}\" lang=\"${lang}\">`,\n \"<head>\",\n '<meta charset=\"utf-8\" />',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\n '<meta name=\"robots\" content=\"noindex\" />',\n `<title>${escapeHtml(title)}</title>`,\n `<style>${DOC_CSS}</style>`,\n \"</head>\",\n `<body>${body}</body>`,\n \"</html>\",\n ].join(\"\");\n}\n\n/**\n * Opens the hosted checkout in its own browser window.\n *\n * The window has to be requested synchronously from the buyer's click, before\n * the session request, or every popup blocker swallows it — so the constructor\n * opens a blank window with a \"preparing checkout\" page and `load()` navigates\n * it once the session exists.\n *\n * The merchant page keeps talking to the portal with `postMessage`: the window\n * handle stays valid across the navigation, and the portal answers the opener\n * (see `createEmbeddedBridge`).\n */\nexport class CheckoutWindow implements CheckoutView {\n private readonly win: Window;\n private readonly onDismiss: () => void;\n private readonly dir: \"rtl\" | \"ltr\";\n private readonly lang: string;\n private readonly title: string;\n private readonly labels: CheckoutWindowOptions[\"labels\"];\n private pollId: number | null = null;\n private navigated = false;\n private destroyed = false;\n\n onLoad: (() => void) | null = null;\n\n constructor(options: CheckoutWindowOptions) {\n const opened = window.open(\"\", WINDOW_NAME, windowFeatures());\n if (!opened) {\n throw new Error(\"The browser blocked the checkout window.\");\n }\n\n this.win = opened;\n this.onDismiss = options.onDismiss;\n this.title = options.title;\n this.labels = options.labels;\n this.dir = options.dir;\n this.lang = options.lang;\n\n this.pollId = window.setInterval(() => this.checkClosed(), CLOSE_POLL_MS);\n\n try {\n this.win.focus();\n } catch {\n // focus is best-effort in embedded browsers\n }\n }\n\n get contentWindow(): Window | null {\n return this.win;\n }\n\n open(): void {\n try {\n this.win.focus();\n } catch {\n // focus is best-effort\n }\n }\n\n load(url: string): void {\n if (this.destroyed) return;\n this.navigated = true;\n try {\n // `replace` keeps the empty window out of the buyer's back history.\n this.win.location.replace(url);\n } catch {\n if (this.win.closed) {\n this.dismiss();\n return;\n }\n // Some embedded browsers refuse one navigation call but accept another.\n try {\n this.win.location.href = url;\n } catch {\n // The portal never loaded; the close poll will notice if it is gone.\n }\n }\n this.onLoad?.();\n }\n\n showError(message: string): void {\n // After the navigation the portal owns that document; nothing safe to paint.\n if (this.destroyed || this.navigated) return;\n\n this.renderError(\n documentHtml(\n this.title,\n this.dir,\n this.lang,\n `<div class=\"panel\">` +\n `<span class=\"error-icon\" aria-hidden=\"true\">!</span>` +\n `<h2 class=\"error-title\">${escapeHtml(this.labels.errorTitle)}</h2>` +\n `<p class=\"error-message\">${escapeHtml(message)}</p>` +\n `<button class=\"error-close\" type=\"button\">${escapeHtml(this.labels.close)}</button>` +\n `</div>`\n )\n );\n\n try {\n const close = this.win.document.querySelector(\".error-close\");\n close?.addEventListener(\"click\", () => this.onDismiss());\n } catch {\n // The window went away before the close button could be wired.\n }\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.stopPolling();\n this.onLoad = null;\n try {\n this.win.close();\n } catch {\n // Already closed by the buyer.\n }\n }\n\n /**\n * Best-effort: the placeholder document is cosmetic. A refused write must\n * never cancel the checkout — the navigation below still has to happen.\n */\n private renderError(html: string): void {\n try {\n const doc = this.win.document;\n doc.open();\n doc.write(html);\n doc.close();\n } catch {\n // Browser refused to let us paint into the blank window.\n }\n }\n\n private checkClosed(): void {\n if (this.destroyed) {\n this.stopPolling();\n return;\n }\n if (this.win.closed) this.dismiss();\n }\n\n private dismiss(): void {\n if (this.destroyed) return;\n this.stopPolling();\n this.onDismiss();\n }\n\n private stopPolling(): void {\n if (this.pollId !== null) {\n window.clearInterval(this.pollId);\n this.pollId = null;\n }\n }\n}\n","import { EsaalatCheckoutError } from \"./errors\";\nimport { CheckoutModal } from \"./modal\";\nimport type {\n CheckoutItem,\n CheckoutLocale,\n CheckoutOpenOptions,\n CheckoutPresentation,\n CheckoutResult,\n CheckoutResultReason,\n CheckoutResultStatus,\n CheckoutSession,\n EsaalatCheckoutOptions,\n} from \"./types\";\nimport type { CheckoutView } from \"./view\";\nimport { CheckoutWindow } from \"./window\";\n\nconst DEFAULT_API_BASE = \"https://api.esaalat.com/api/v1\";\nconst DEFAULT_PORTAL_BASE = \"https://pay.esaalat.com\";\nconst SDK_VERSION = \"0.1.0\";\nconst REQUEST_TIMEOUT_MS = 20_000;\nconst INIT_RETRY_MS = 300;\n// A browser window needs longer than an iframe: the init message races the\n// navigation, so keep knocking until the portal answers `ready`.\nconst INIT_ATTEMPTS = 100;\nconst VERIFIED_CLOSE_MS = 1_800;\nconst ERROR_CLOSE_MS = 6_000;\n\nconst RESULT_STATUSES: CheckoutResultStatus[] = [\n \"VERIFIED\",\n \"WRONG_AMOUNT\",\n \"EXPIRED\",\n \"CANCELLED\",\n \"FAILED\",\n];\n\nconst RESULT_REASONS: CheckoutResultReason[] = [\n \"dismissed\",\n \"destroyed\",\n \"intent_cancelled\",\n \"session_expired\",\n \"session_not_found\",\n \"network_error\",\n];\n\ninterface ActiveCheckout {\n token: string | null;\n locale: CheckoutLocale;\n controller: AbortController;\n view: CheckoutView;\n settled: boolean;\n resolve: (result: CheckoutResult) => void;\n reject: (error: Error) => void;\n onResult?: (result: CheckoutResult) => void;\n}\n\ninterface PortalMessage {\n source?: unknown;\n type?: unknown;\n sessionToken?: unknown;\n payload?: unknown;\n}\n\ninterface PortalResultPayload {\n status?: unknown;\n reason?: unknown;\n intentCode?: unknown;\n amount?: unknown;\n paidAmount?: unknown;\n currency?: unknown;\n externalOrderId?: unknown;\n merchantName?: unknown;\n}\n\nfunction normalizeBase(value: string, fallback: string): string {\n const raw = value.trim() || fallback;\n let url: URL; try {\n const base =\n typeof window !== \"undefined\" ? window.location?.href : undefined;\n url = new URL(raw, base);\n } catch {\n throw new EsaalatCheckoutError(\n `Invalid URL: ${raw}`,\n \"configuration\"\n );\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new EsaalatCheckoutError(\n `Unsupported URL protocol: ${url.protocol}`,\n \"configuration\"\n );\n }\n return url.toString().replace(/\\/+$/, \"\");\n}\n\nfunction requestId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n // UUID v4 fallback for older embedded browsers.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (char) => {\n const random = (Math.random() * 16) | 0;\n const value = char === \"x\" ? random : (random & 0x3) | 0x8;\n return value.toString(16);\n });\n}\n\nfunction stringOr(value: unknown, fallback = \"\"): string {\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction nullOrString(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\nfunction errorDetail(body: unknown): string | null {\n if (!body || typeof body !== \"object\") return null;\n const record = body as Record<string, unknown>;\n if (typeof record.detail === \"string\") return record.detail;\n if (Array.isArray(record.errors)) {\n for (const error of record.errors) {\n if (error && typeof error === \"object\") {\n const detail = (error as Record<string, unknown>).detail;\n if (typeof detail === \"string\") return detail;\n }\n }\n }\n for (const value of Object.values(record)) {\n if (typeof value === \"string\") return value;\n if (Array.isArray(value) && typeof value[0] === \"string\") return value[0];\n }\n return null;\n}\n\nfunction isCheckoutSession(value: unknown): value is CheckoutSession {\n if (!value || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record.session_token === \"string\" &&\n typeof record.portal_url === \"string\" &&\n typeof record.amount === \"string\" &&\n typeof record.currency === \"string\"\n );\n}\n\nfunction cancelledResult(\n token: string | null,\n reason: \"dismissed\" | \"destroyed\"\n): CheckoutResult {\n return {\n status: \"CANCELLED\",\n reason,\n sessionToken: token ?? \"\",\n intentCode: null,\n amount: \"0.00\",\n paidAmount: null,\n currency: \"YER\",\n externalOrderId: \"\",\n merchantName: \"\",\n };\n}\n\n/**\n * Framework-agnostic Esaalat checkout client.\n *\n * It creates a checkout session with the merchant's publishable key, opens the\n * Esaalat-hosted portal in its own browser window (or an in-page modal when\n * asked, or when the browser blocks the window), and resolves `open()` with a\n * terminal result. The publishable key is public by design; keep the client\n * secret and any order confirmation on the server.\n */\nexport class EsaalatCheckout {\n private readonly publishableKey: string;\n private readonly apiBaseUrl: string;\n private readonly portalBaseUrl: string;\n private readonly zIndex: number;\n private readonly presentation: CheckoutPresentation;\n private readonly locale: CheckoutLocale;\n private readonly fetchImpl: typeof fetch | null;\n private readonly defaultOnResult?: (result: CheckoutResult) => void;\n private readonly defaultOnError?: (error: Error) => void;\n\n private active: ActiveCheckout | null = null;\n private view: CheckoutView | null = null;\n private portalOrigin: string | null = null;\n private sessionToken: string | null = null;\n private initTimer: number | null = null;\n private verifiedTimer: number | null = null;\n private errorTimer: number | null = null;\n\n constructor(options: EsaalatCheckoutOptions) {\n if (!options || typeof options.publishableKey !== \"string\") {\n throw new EsaalatCheckoutError(\n \"A publishable key is required.\",\n \"configuration\"\n );\n }\n const key = options.publishableKey.trim();\n if (!key) {\n throw new EsaalatCheckoutError(\n \"A publishable key is required.\",\n \"configuration\"\n );\n }\n\n this.publishableKey = key;\n this.locale =\n options.locale === \"en\" || options.locale === \"ar\" ? options.locale : \"ar\";\n this.apiBaseUrl = normalizeBase(options.apiBaseUrl ?? \"\", DEFAULT_API_BASE);\n this.portalBaseUrl = normalizeBase(\n options.portalBaseUrl ?? \"\",\n DEFAULT_PORTAL_BASE\n );\n this.zIndex =\n typeof options.zIndex === \"number\" && Number.isFinite(options.zIndex)\n ? options.zIndex\n : 2_147_483_000;\n this.presentation =\n options.presentation === \"modal\" ? \"modal\" : \"window\";\n\n const fetchFromOptions = options.fetch;\n const globalFetch =\n typeof fetch === \"function\" ? fetch.bind(globalThis) : null;\n this.fetchImpl = fetchFromOptions ?? globalFetch;\n this.defaultOnResult = options.onResult;\n this.defaultOnError = options.onError;\n }\n\n /** True while a checkout window or modal is on screen. */\n get isOpen(): boolean {\n return this.view !== null;\n }\n\n /**\n * Open the hosted checkout and resolve with its terminal result.\n *\n * A second call while a checkout is open rejects; close the first one with\n * `close()` or wait for its result.\n */\n async open(options: CheckoutOpenOptions = {}): Promise<CheckoutResult> {\n if (this.active || this.view) {\n throw new EsaalatCheckoutError(\n \"A checkout is already open. Close it before starting another.\",\n \"configuration\"\n );\n }\n if (!this.fetchImpl) {\n throw new EsaalatCheckoutError(\n \"This browser does not support fetch. Use the SDK on a modern browser.\",\n \"configuration\"\n );\n }\n\n const locale =\n options.locale === \"en\" || options.locale === \"ar\"\n ? options.locale\n : this.locale;\n const items = options.items ?? [];\n const existingToken = options.sessionToken?.trim() || null;\n const hasAmount =\n options.amount !== undefined &&\n options.amount !== null &&\n String(options.amount).trim() !== \"\";\n if (!existingToken && !hasAmount && items.length === 0) {\n throw new EsaalatCheckoutError(\n \"Provide an amount, items, or a server-created session token.\",\n \"configuration\"\n );\n }\n if (!existingToken && items.length > 0) {\n for (const item of items) {\n if (!item.name || item.price === undefined || item.price === null) {\n throw new EsaalatCheckoutError(\n \"Every item needs a name and a price.\",\n \"configuration\"\n );\n }\n }\n }\n\n const labels =\n locale === \"ar\"\n ? {\n close: \"إغلاق\",\n loading: \"جارٍ تجهيز الدفع الآمن…\",\n errorTitle: \"تعذّر بدء الدفع\",\n }\n : {\n close: \"Close\",\n loading: \"Preparing secure checkout…\",\n errorTitle: \"Couldn't start the checkout\",\n };\n\n const presentation: CheckoutPresentation =\n options.presentation === \"window\" || options.presentation === \"modal\"\n ? options.presentation\n : this.presentation;\n\n // The view is created before the first `await`: a browser window has to be\n // requested inside the buyer's click, or popup blockers discard it.\n let view: CheckoutView;\n try {\n view = this.createView(presentation, labels, locale);\n view.open();\n } catch (error) {\n throw new EsaalatCheckoutError(\n \"The checkout could not be opened.\",\n \"unavailable\",\n { cause: error }\n );\n }\n\n const controller = new AbortController();\n let resolvePromise!: (result: CheckoutResult) => void;\n let rejectPromise!: (error: Error) => void;\n const promise = new Promise<CheckoutResult>((resolve, reject) => {\n resolvePromise = resolve;\n rejectPromise = reject;\n });\n\n const onResult = options.onResult ?? this.defaultOnResult;\n const onError = options.onError ?? this.defaultOnError;\n\n const active: ActiveCheckout = {\n token: null,\n locale,\n controller,\n view,\n settled: false,\n resolve: resolvePromise,\n reject: rejectPromise,\n onResult,\n };\n this.active = active;\n this.view = view;\n this.sessionToken = null;\n this.portalOrigin = null;\n\n view.onLoad = () => this.startInitRetry();\n\n let portalUrl: string;\n try {\n if (existingToken) {\n active.token = existingToken;\n this.sessionToken = existingToken;\n portalUrl = this.buildPortalUrl(\n existingToken,\n options.portalUrl ?? \"\",\n locale\n );\n } else {\n const session = await this.createSession(\n options,\n locale,\n controller.signal\n );\n // The buyer closed the checkout while the session was being created.\n if (active.settled) return promise;\n active.token = session.session_token;\n this.sessionToken = session.session_token;\n portalUrl = this.buildPortalUrl(\n session.session_token,\n session.portal_url,\n locale\n );\n }\n this.portalOrigin = new URL(portalUrl).origin;\n } catch (error) {\n if (active.settled) return promise;\n const wrapped =\n error instanceof EsaalatCheckoutError\n ? error\n : new EsaalatCheckoutError(\n \"Esaalat returned an invalid checkout URL.\",\n \"session\",\n { cause: error }\n );\n active.settled = true;\n this.active = null;\n this.removeMessageListener();\n try {\n onError?.(wrapped);\n } catch {\n // A merchant callback must not prevent the promise from settling.\n }\n active.reject(wrapped);\n view.showError(wrapped.message);\n this.errorTimer = window.setTimeout(() => {\n if (this.view === view) this.teardown();\n }, ERROR_CLOSE_MS);\n return promise;\n }\n\n window.addEventListener(\"message\", this.handleMessage);\n view.load(portalUrl);\n return promise;\n }\n\n /** Close the checkout. An in-flight `open()` resolves with CANCELLED. */\n close(): void {\n if (this.active) {\n this.dismiss();\n return;\n }\n this.teardown();\n }\n\n /** Remove the checkout view and any listeners without resolving a result. */\n destroy(): void {\n if (this.active && !this.active.settled) {\n this.finish(cancelledResult(this.sessionToken, \"destroyed\"));\n return;\n }\n this.teardown();\n }\n\n private createView(\n presentation: CheckoutPresentation,\n labels: { close: string; loading: string; errorTitle: string },\n locale: CheckoutLocale\n ): CheckoutView {\n const title =\n locale === \"ar\" ? \"إيصالات — دفع آمن\" : \"Esaalat — Secure checkout\";\n const onDismiss = () => this.dismiss();\n\n if (presentation === \"window\") {\n try {\n return new CheckoutWindow({\n title,\n labels,\n dir: locale === \"ar\" ? \"rtl\" : \"ltr\",\n lang: locale,\n onDismiss,\n });\n } catch {\n // The popup was blocked (or there is no window): the modal is the\n // always-available fallback, so the buyer still gets to pay.\n }\n }\n\n return new CheckoutModal({\n zIndex: this.zIndex,\n title,\n labels,\n onDismiss,\n });\n }\n\n private buildPortalUrl(\n sessionToken: string,\n rawPortalUrl: string,\n locale: CheckoutLocale\n ): string {\n const raw =\n rawPortalUrl.trim() ||\n `${this.portalBaseUrl}/checkout/${encodeURIComponent(sessionToken)}`;\n const url = new URL(raw);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"Unsupported portal protocol\");\n }\n url.searchParams.set(\"lang\", locale);\n return url.toString();\n }\n\n private async createSession(\n options: CheckoutOpenOptions,\n locale: CheckoutLocale,\n outerSignal: AbortSignal\n ): Promise<CheckoutSession> {\n if (!this.fetchImpl) {\n throw new EsaalatCheckoutError(\"fetch is unavailable\", \"configuration\");\n }\n\n const body: Record<string, unknown> = {};\n if (options.amount !== undefined && options.amount !== null) {\n body.amount = String(options.amount);\n }\n if (options.items?.length) {\n body.items = options.items.map((item: CheckoutItem) => ({\n name: item.name,\n ...(item.description ? { description: item.description } : {}),\n price: String(item.price),\n quantity: item.quantity ?? 1,\n }));\n }\n if (options.currency) body.currency = options.currency;\n if (options.externalOrderId) {\n body.external_order_id = options.externalOrderId;\n }\n if (options.notes) body.notes = options.notes;\n if (options.wallet) body.wallet = options.wallet;\n\n const controller = new AbortController();\n let timedOut = false;\n const timeout = window.setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, REQUEST_TIMEOUT_MS);\n const abortOuter = () => controller.abort();\n outerSignal.addEventListener(\"abort\", abortOuter);\n\n try {\n const response = await this.fetchImpl(\n `${this.apiBaseUrl}/checkout/sdk/sessions/`,\n {\n method: \"POST\",\n credentials: \"omit\",\n cache: \"no-store\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"ESAALAT-PUBLISHABLE-KEY\": this.publishableKey,\n \"ESAALAT-IDEMPOTENCY-KEY\":\n options.idempotencyKey?.trim() || requestId(),\n \"ESAALAT-SDK-VERSION\": SDK_VERSION,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n }\n );\n\n const payload: unknown = await response.json().catch(() => null);\n if (!response.ok) {\n throw new EsaalatCheckoutError(\n errorDetail(payload) ||\n (locale === \"ar\"\n ? \"تعذّر إنشاء جلسة الدفع.\"\n : \"Couldn't create the checkout session.\"),\n \"api\",\n { status: response.status, details: payload }\n );\n }\n if (!isCheckoutSession(payload)) {\n throw new EsaalatCheckoutError(\n \"Esaalat returned an invalid checkout session.\",\n \"session\",\n { details: payload }\n );\n }\n\n return payload;\n } catch (error) {\n if (error instanceof EsaalatCheckoutError) throw error;\n if (timedOut) {\n throw new EsaalatCheckoutError(\n locale === \"ar\"\n ? \"انتهت مهلة الاتصال بإيصالات. حاول مرة أخرى.\"\n : \"The connection to Esaalat timed out. Please try again.\",\n \"network\",\n { cause: error }\n );\n }\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n throw new EsaalatCheckoutError(\n locale === \"ar\"\n ? \"تعذّر الاتصال بإيصالات. تحقق من اتصالك وحاول مرة أخرى.\"\n : \"Couldn't reach Esaalat. Check your connection and try again.\",\n \"network\",\n { cause: error }\n );\n } finally {\n window.clearTimeout(timeout);\n outerSignal.removeEventListener(\"abort\", abortOuter);\n }\n }\n\n private startInitRetry(): void {\n this.clearInitTimer();\n let attempts = 0;\n\n const tick = () => {\n if (!this.view || !this.portalOrigin || !this.sessionToken) return;\n this.postInit();\n attempts += 1;\n if (attempts < INIT_ATTEMPTS) {\n this.initTimer = window.setTimeout(tick, INIT_RETRY_MS);\n } else {\n this.initTimer = null;\n }\n };\n\n tick();\n }\n\n private postInit(): void {\n const target = this.view?.contentWindow;\n if (!target || !this.portalOrigin || !this.sessionToken) return;\n const message = {\n source: \"esaalat-checkout-sdk\",\n version: 1,\n type: \"init\",\n sessionToken: this.sessionToken,\n parentOrigin: window.location.origin,\n locale: this.active?.locale ?? this.locale,\n };\n try {\n target.postMessage(message, this.portalOrigin);\n } catch {\n // The iframe may have been torn down between the check and the post.\n }\n }\n\n private clearInitTimer(): void {\n if (this.initTimer !== null) {\n window.clearTimeout(this.initTimer);\n this.initTimer = null;\n }\n }\n\n private handleMessage = (event: MessageEvent): void => {\n if (!this.view || !this.portalOrigin) return;\n if (event.source !== this.view.contentWindow) return;\n if (event.origin !== this.portalOrigin) return;\n\n const data = event.data as PortalMessage | null;\n if (!data || typeof data !== \"object\") return;\n if (data.source !== \"esaalat-checkout\") return;\n if (data.sessionToken !== this.sessionToken) return;\n\n if (data.type === \"ready\") {\n this.clearInitTimer();\n return;\n }\n\n if (data.type === \"close\") {\n this.dismiss();\n return;\n }\n\n if (data.type === \"result\") {\n const result = this.parseResult(data.payload);\n if (result) this.finish(result);\n }\n };\n\n private parseResult(payload: unknown): CheckoutResult | null {\n if (!payload || typeof payload !== \"object\") return null;\n const value = payload as PortalResultPayload;\n if (\n typeof value.status !== \"string\" ||\n !RESULT_STATUSES.includes(value.status as CheckoutResultStatus)\n ) {\n return null;\n }\n\n const reason =\n typeof value.reason === \"string\" &&\n RESULT_REASONS.includes(value.reason as CheckoutResultReason)\n ? (value.reason as CheckoutResultReason)\n : undefined;\n\n return {\n status: value.status as CheckoutResultStatus,\n ...(reason ? { reason } : {}),\n sessionToken: this.sessionToken ?? \"\",\n intentCode: nullOrString(value.intentCode),\n amount: stringOr(value.amount, \"0.00\"),\n paidAmount: nullOrString(value.paidAmount),\n currency: stringOr(value.currency, \"YER\"),\n externalOrderId: stringOr(value.externalOrderId),\n merchantName: stringOr(value.merchantName),\n };\n }\n\n private finish(result: CheckoutResult): void {\n const active = this.active;\n if (!active || active.settled) return;\n\n active.settled = true;\n this.active = null;\n this.clearInitTimer();\n active.controller.abort();\n try {\n active.onResult?.(result);\n } catch {\n // A merchant callback must not prevent the promise from settling.\n }\n active.resolve(result);\n\n if (result.reason === \"dismissed\" || result.reason === \"destroyed\") {\n this.teardown();\n return;\n }\n\n if (result.status === \"VERIFIED\") {\n this.verifiedTimer = window.setTimeout(() => this.teardown(), VERIFIED_CLOSE_MS);\n }\n // Other terminal states stay visible until the buyer closes them, so the\n // reason (wrong amount, expiry, cancellation) is not flashed away.\n }\n\n private dismiss(): void {\n if (this.active && !this.active.settled) {\n this.finish(cancelledResult(this.sessionToken, \"dismissed\"));\n return;\n }\n this.teardown();\n }\n\n private removeMessageListener(): void {\n window.removeEventListener(\"message\", this.handleMessage);\n }\n\n private clearTimers(): void {\n this.clearInitTimer();\n if (this.verifiedTimer !== null) {\n window.clearTimeout(this.verifiedTimer);\n this.verifiedTimer = null;\n }\n if (this.errorTimer !== null) {\n window.clearTimeout(this.errorTimer);\n this.errorTimer = null;\n }\n }\n\n private teardown(): void {\n this.clearTimers();\n this.removeMessageListener();\n if (this.view) {\n this.view.destroy();\n this.view = null;\n }\n this.portalOrigin = null;\n this.sessionToken = null;\n this.active = null;\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/modal.ts","../src/window.ts","../src/client.ts","../src/element.ts"],"sourcesContent":["export { EsaalatCheckout } from \"./client\";\nexport {\n ESAALAT_CHECKOUT_BUTTON_TAG,\n ESAALAT_CHECKOUT_EVENTS,\n EsaalatCheckoutButtonElement,\n registerEsaalatCheckoutButton,\n} from \"./element\";\nexport { EsaalatCheckoutError } from \"./errors\";\nexport type { CheckoutErrorCode } from \"./errors\";\nexport type {\n CheckoutBeforeCreateOrderHandler,\n CheckoutCreatePayload,\n CheckoutItem,\n CheckoutIntent,\n CheckoutLocale,\n CheckoutOpenOptions,\n CheckoutPresentation,\n CheckoutProvider,\n CheckoutResult,\n CheckoutResultReason,\n CheckoutResultStatus,\n CheckoutSession,\n CheckoutWallet,\n EsaalatCheckoutOptions,\n ExpectedReferenceType,\n} from \"./types\";\n\nexport { EsaalatCheckout as default } from \"./client\";\n","export type CheckoutErrorCode =\n | \"configuration\"\n | \"network\"\n | \"api\"\n | \"session\"\n | \"unavailable\"\n | \"aborted\";\n\nexport class EsaalatCheckoutError extends Error {\n readonly code: CheckoutErrorCode;\n readonly status?: number;\n readonly details?: unknown;\n\n constructor(\n message: string,\n code: CheckoutErrorCode,\n options: { status?: number; details?: unknown; cause?: unknown } = {}\n ) {\n super(message);\n this.name = \"EsaalatCheckoutError\";\n this.code = code;\n this.status = options.status;\n this.details = options.details;\n if (options.cause !== undefined) {\n // `cause` is ES2022; assigning it keeps the package ES2020-compatible.\n (this as Error & { cause?: unknown }).cause = options.cause;\n }\n }\n}\n","import type { CheckoutView } from \"./view\";\n\nexport interface CheckoutModalLabels {\n close: string;\n loading: string;\n errorTitle: string;\n}\n\ninterface CheckoutModalOptions {\n zIndex: number;\n title: string;\n labels: CheckoutModalLabels;\n onDismiss: () => void;\n}\n\nconst MODAL_CSS = `\n :host { all: initial; }\n *, *::before, *::after { box-sizing: border-box; }\n .overlay {\n position: fixed;\n inset: 0;\n z-index: 0;\n display: grid;\n place-items: center;\n padding: 12px;\n background: rgba(12, 11, 8, 0.58);\n -webkit-backdrop-filter: blur(7px);\n backdrop-filter: blur(7px);\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n color: #0b0b0b;\n pointer-events: auto;\n animation: fade 180ms ease-out;\n }\n .dialog {\n position: relative;\n width: min(460px, calc(100vw - 24px));\n height: min(720px, calc(100dvh - 24px));\n overflow: hidden;\n border-radius: 22px;\n background: #fffefa;\n border: 1px solid rgba(255, 255, 255, 0.5);\n box-shadow: 0 36px 100px rgba(0, 0, 0, 0.4), 0 6px 24px rgba(0, 0, 0, 0.2);\n animation: rise 220ms cubic-bezier(0.2, 0.8, 0.2, 1);\n }\n .dialog.is-frame { background: #f3f0e8; }\n .chrome {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 14px 16px;\n border-bottom: 1px solid #e7e2d7;\n background: #fffefa;\n }\n .dialog.is-frame .chrome { display: none; }\n .mark {\n width: 28px;\n height: 28px;\n border-radius: 8px;\n display: grid;\n place-items: center;\n background: #0b0b0b;\n color: #fff;\n font-size: 14px;\n font-weight: 800;\n flex-shrink: 0;\n }\n .chrome-title {\n font-size: 13px;\n font-weight: 700;\n line-height: 1.3;\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n .close {\n width: 32px;\n height: 32px;\n display: grid;\n place-items: center;\n border: 0;\n border-radius: 9px;\n background: transparent;\n color: #716f67;\n cursor: pointer;\n flex-shrink: 0;\n }\n .close:hover { background: #f3f0e8; color: #0b0b0b; }\n .close:focus-visible { outline: 2px solid #0b0b0b; outline-offset: 2px; }\n .close svg { width: 18px; height: 18px; }\n .content {\n position: absolute;\n inset: 57px 0 0;\n overflow: hidden;\n }\n .dialog.is-frame .content { inset: 0; }\n .frame {\n display: block;\n width: 100%;\n height: 100%;\n border: 0;\n background: #f3f0e8;\n }\n .panel {\n height: 100%;\n display: grid;\n place-content: center;\n justify-items: center;\n gap: 12px;\n padding: 32px;\n text-align: center;\n }\n .spinner {\n width: 34px;\n height: 34px;\n border-radius: 50%;\n border: 2px solid #e7e2d7;\n border-top-color: #0b0b0b;\n animation: spin 900ms linear infinite;\n }\n .loading { font-size: 13px; color: #716f67; }\n .error-icon {\n width: 54px;\n height: 54px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n background: #fef1ef;\n border: 1px solid #f5cfca;\n color: #b42318;\n }\n .error-icon svg { width: 25px; height: 25px; }\n .error-title { margin: 0; font-size: 17px; line-height: 1.4; }\n .error-message { margin: 0; font-size: 13px; line-height: 1.7; color: #716f67; max-width: 34ch; }\n .error-close {\n margin-top: 6px;\n min-height: 42px;\n padding: 9px 22px;\n border: 1px solid #e7e2d7;\n border-radius: 11px;\n background: transparent;\n color: #0b0b0b;\n font: inherit;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n }\n .error-close:hover { background: #f3f0e8; }\n .error-close:focus-visible { outline: 2px solid #0b0b0b; outline-offset: 2px; }\n @keyframes spin { to { transform: rotate(360deg); } }\n @keyframes fade { from { opacity: 0; } }\n @keyframes rise { from { opacity: 0; transform: translateY(10px) scale(0.985); } }\n @media (max-width: 520px) {\n .overlay { padding: 0; }\n .dialog {\n width: 100vw;\n height: 100dvh;\n max-height: none;\n border-radius: 0;\n border: 0;\n }\n .chrome { padding-top: calc(14px + env(safe-area-inset-top)); }\n }\n @media (prefers-color-scheme: dark) {\n .overlay { background: rgba(0, 0, 0, 0.7); }\n .dialog, .chrome { background: #1d1b17; border-color: #343028; color: #f6f2e8; }\n .dialog.is-frame, .frame { background: #141310; }\n .chrome-title { color: #f6f2e8; }\n .close { color: #aaa59a; }\n .close:hover { background: #2b2721; color: #f6f2e8; }\n .spinner { border-color: #343028; border-top-color: #f6f2e8; }\n .loading, .error-message { color: #aaa59a; }\n .error-icon { background: #2b1b19; border-color: #4b2c28; color: #f18d83; }\n .error-close { border-color: #343028; color: #f6f2e8; }\n .error-close:hover { background: #2b2721; }\n }\n @media (prefers-reduced-motion: reduce) {\n .overlay, .dialog { animation: none; }\n .spinner { animation-duration: 0.01ms; }\n }\n`;\n\nfunction element<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n className?: string\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n if (className) node.className = className;\n return node;\n}\n\nfunction closeSvg(): SVGSVGElement {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"1.8\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n const path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n path.setAttribute(\"d\", \"M18 6 6 18M6 6l12 12\");\n svg.append(path);\n return svg;\n}\n\nfunction alertSvg(): SVGSVGElement {\n const svg = document.createElementNS(\"http://www.w3.org/2000/svg\", \"svg\");\n svg.setAttribute(\"viewBox\", \"0 0 24 24\");\n svg.setAttribute(\"fill\", \"none\");\n svg.setAttribute(\"stroke\", \"currentColor\");\n svg.setAttribute(\"stroke-width\", \"1.8\");\n svg.setAttribute(\"stroke-linecap\", \"round\");\n svg.setAttribute(\"stroke-linejoin\", \"round\");\n svg.setAttribute(\"aria-hidden\", \"true\");\n const path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\");\n path.setAttribute(\"d\", \"M12 9v4M12 17h.01M10.3 3.9 2.5 17.4A2 2 0 0 0 4.2 20h15.6a2 2 0 0 0 1.7-2.6L13.7 3.9a2 2 0 0 0-3.4 0Z\");\n svg.append(path);\n return svg;\n}\n\n/**\n * The modal lives in a shadow root so a merchant's global CSS cannot leak into\n * the checkout (or the other way around). The iframe is the only content once\n * the session is ready; the portal owns its own close button.\n */\nexport class CheckoutModal implements CheckoutView {\n private readonly host: HTMLDivElement;\n private readonly shadow: ShadowRoot;\n private readonly overlay: HTMLDivElement;\n private readonly dialog: HTMLDivElement;\n private readonly closeButton: HTMLButtonElement;\n private readonly content: HTMLDivElement;\n private frame: HTMLIFrameElement | null = null;\n private previousOverflow = \"\";\n private previousFocus: Element | null = null;\n private destroyed = false;\n\n onLoad: (() => void) | null = null;\n private readonly onDismiss: () => void;\n\n constructor(private readonly options: CheckoutModalOptions) {\n this.onDismiss = options.onDismiss;\n\n this.host = document.createElement(\"div\");\n this.host.setAttribute(\"data-esaalat-checkout\", \"\");\n this.host.style.cssText = `all: initial; position: fixed; inset: 0; z-index: ${options.zIndex}; pointer-events: none;`;\n\n this.shadow = this.host.attachShadow({ mode: \"open\" });\n const style = element(\"style\");\n style.textContent = MODAL_CSS;\n this.shadow.append(style);\n\n this.overlay = element(\"div\", \"overlay\");\n this.overlay.addEventListener(\"mousedown\", (event) => {\n if (event.target === this.overlay) this.onDismiss();\n });\n\n this.dialog = element(\"div\", \"dialog\");\n this.dialog.setAttribute(\"role\", \"dialog\");\n this.dialog.setAttribute(\"aria-modal\", \"true\");\n this.dialog.setAttribute(\"aria-label\", options.title);\n\n const chrome = element(\"div\", \"chrome\");\n const mark = element(\"span\", \"mark\");\n mark.textContent = \"E\";\n mark.setAttribute(\"aria-hidden\", \"true\");\n const title = element(\"span\", \"chrome-title\");\n title.textContent = options.title;\n\n this.closeButton = element(\"button\", \"close\");\n this.closeButton.type = \"button\";\n this.closeButton.setAttribute(\"aria-label\", options.labels.close);\n this.closeButton.append(closeSvg());\n this.closeButton.addEventListener(\"click\", () => this.onDismiss());\n\n chrome.append(mark, title, this.closeButton);\n\n this.content = element(\"div\", \"content\");\n this.showLoading();\n\n this.dialog.append(chrome, this.content);\n this.overlay.append(this.dialog);\n this.shadow.append(this.overlay);\n }\n\n open(): void {\n if (!document.body) {\n throw new Error(\"Esaalat checkout needs a document body.\");\n }\n this.previousOverflow = document.body.style.overflow;\n this.previousFocus = document.activeElement;\n document.body.style.overflow = \"hidden\";\n document.body.append(this.host);\n document.addEventListener(\"keydown\", this.handleKeyDown, true);\n this.closeButton.focus({ preventScroll: true });\n }\n\n load(url: string): void {\n if (this.destroyed) return;\n this.dialog.classList.add(\"is-frame\");\n this.content.replaceChildren();\n\n const frame = document.createElement(\"iframe\");\n frame.className = \"frame\";\n frame.src = url;\n frame.title = this.options.title;\n frame.setAttribute(\"allow\", \"clipboard-write\");\n frame.setAttribute(\"referrerpolicy\", \"strict-origin-when-cross-origin\");\n frame.setAttribute(\"loading\", \"eager\");\n frame.addEventListener(\"load\", () => {\n this.onLoad?.();\n try {\n frame.focus({ preventScroll: true });\n } catch {\n // focus is best-effort in embedded browsers\n }\n });\n\n this.frame = frame;\n this.content.append(frame);\n }\n\n get contentWindow(): Window | null {\n return this.frame?.contentWindow ?? null;\n }\n\n showError(message: string): void {\n if (this.destroyed) return;\n this.content.replaceChildren();\n\n const panel = element(\"div\", \"panel\");\n const icon = element(\"span\", \"error-icon\");\n icon.append(alertSvg());\n const title = element(\"h2\", \"error-title\");\n title.textContent = this.options.labels.errorTitle;\n const text = element(\"p\", \"error-message\");\n text.textContent = message;\n const close = element(\"button\", \"error-close\");\n close.type = \"button\";\n close.textContent = this.options.labels.close;\n close.addEventListener(\"click\", () => this.onDismiss());\n\n panel.append(icon, title, text, close);\n this.content.append(panel);\n this.dialog.classList.remove(\"is-frame\");\n close.focus({ preventScroll: true });\n }\n\n private showLoading(): void {\n this.content.replaceChildren();\n const panel = element(\"div\", \"panel\");\n const spinner = element(\"span\", \"spinner\");\n spinner.setAttribute(\"aria-hidden\", \"true\");\n const label = element(\"p\", \"loading\");\n label.textContent = this.options.labels.loading;\n panel.append(spinner, label);\n this.content.append(panel);\n }\n\n private handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n this.onDismiss();\n }\n };\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n document.removeEventListener(\"keydown\", this.handleKeyDown, true);\n document.body.style.overflow = this.previousOverflow;\n this.host.remove();\n if (this.previousFocus instanceof HTMLElement && this.previousFocus.isConnected) {\n try {\n this.previousFocus.focus({ preventScroll: true });\n } catch {\n // ignore\n }\n }\n this.onLoad = null;\n this.frame = null;\n }\n}\n","import type { CheckoutView } from \"./view\";\n\nexport interface CheckoutWindowOptions {\n title: string;\n labels: {\n loading: string;\n errorTitle: string;\n close: string;\n };\n dir: \"rtl\" | \"ltr\";\n lang: string;\n onDismiss: () => void;\n}\n\nconst WINDOW_NAME = \"esaalat-checkout\";\nconst WINDOW_WIDTH = 480;\nconst WINDOW_HEIGHT = 800;\nconst CLOSE_POLL_MS = 400;\n\nconst DOC_CSS = `\n * { box-sizing: border-box; }\n html, body { height: 100%; }\n body {\n margin: 0;\n display: grid;\n place-items: center;\n padding: 24px;\n background: #f3f0e8;\n color: #0b0b0b;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n }\n .panel {\n display: grid;\n justify-items: center;\n gap: 14px;\n max-width: 34ch;\n text-align: center;\n }\n .error-icon {\n width: 54px;\n height: 54px;\n border-radius: 50%;\n display: grid;\n place-items: center;\n background: #fef1ef;\n border: 1px solid #f5cfca;\n color: #b42318;\n font-size: 24px;\n font-weight: 800;\n }\n .error-title { margin: 0; font-size: 17px; line-height: 1.4; }\n .error-message { margin: 0; font-size: 13px; line-height: 1.7; color: #716f67; }\n .error-close {\n margin-top: 4px;\n min-height: 42px;\n padding: 9px 22px;\n border: 1px solid #ded7c8;\n border-radius: 11px;\n background: transparent;\n color: #0b0b0b;\n font: inherit;\n font-size: 13px;\n font-weight: 700;\n cursor: pointer;\n }\n .error-close:hover { background: #e9e4d8; }\n @media (prefers-color-scheme: dark) {\n body { background: #141310; color: #f6f2e8; }\n .error-message { color: #aaa59a; }\n .error-icon { background: #2b1b19; border-color: #4b2c28; color: #f18d83; }\n .error-close { border-color: #343028; color: #f6f2e8; }\n .error-close:hover { background: #2b2721; }\n }\n`;\n\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\");\n}\n\n/** A centered popup, sized like the checkout card it replaces. */\nfunction windowFeatures(): string {\n const screenWidth = window.screen?.availWidth ?? WINDOW_WIDTH;\n const screenHeight = window.screen?.availHeight ?? WINDOW_HEIGHT;\n const width = Math.min(WINDOW_WIDTH, Math.max(320, screenWidth - 40));\n const height = Math.min(WINDOW_HEIGHT, Math.max(480, screenHeight - 40));\n const baseX = typeof window.screenX === \"number\" ? window.screenX : 0;\n const baseY = typeof window.screenY === \"number\" ? window.screenY : 0;\n const outerWidth = window.outerWidth || width;\n const outerHeight = window.outerHeight || height;\n const left = Math.max(0, Math.round(baseX + (outerWidth - width) / 2));\n const top = Math.max(0, Math.round(baseY + (outerHeight - height) / 2));\n\n return [\n \"popup=yes\",\n `width=${width}`,\n `height=${height}`,\n `left=${left}`,\n `top=${top}`,\n \"resizable=yes\",\n \"scrollbars=yes\",\n ].join(\",\");\n}\n\nfunction documentHtml(\n title: string,\n dir: \"rtl\" | \"ltr\",\n lang: string,\n body: string\n): string {\n return [\n \"<!doctype html>\",\n `<html dir=\"${dir}\" lang=\"${lang}\">`,\n \"<head>\",\n '<meta charset=\"utf-8\" />',\n '<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />',\n '<meta name=\"robots\" content=\"noindex\" />',\n `<title>${escapeHtml(title)}</title>`,\n `<style>${DOC_CSS}</style>`,\n \"</head>\",\n `<body>${body}</body>`,\n \"</html>\",\n ].join(\"\");\n}\n\n/**\n * Opens the hosted checkout in its own browser window.\n *\n * The window has to be requested synchronously from the buyer's click, before\n * the session request, or every popup blocker swallows it — so the constructor\n * opens a blank window with a \"preparing checkout\" page and `load()` navigates\n * it once the session exists.\n *\n * The merchant page keeps talking to the portal with `postMessage`: the window\n * handle stays valid across the navigation, and the portal answers the opener\n * (see `createEmbeddedBridge`).\n */\nexport class CheckoutWindow implements CheckoutView {\n private readonly win: Window;\n private readonly onDismiss: () => void;\n private readonly dir: \"rtl\" | \"ltr\";\n private readonly lang: string;\n private readonly title: string;\n private readonly labels: CheckoutWindowOptions[\"labels\"];\n private pollId: number | null = null;\n private navigated = false;\n private destroyed = false;\n\n onLoad: (() => void) | null = null;\n\n constructor(options: CheckoutWindowOptions) {\n const opened = window.open(\"\", WINDOW_NAME, windowFeatures());\n if (!opened) {\n throw new Error(\"The browser blocked the checkout window.\");\n }\n\n this.win = opened;\n this.onDismiss = options.onDismiss;\n this.title = options.title;\n this.labels = options.labels;\n this.dir = options.dir;\n this.lang = options.lang;\n\n this.pollId = window.setInterval(() => this.checkClosed(), CLOSE_POLL_MS);\n\n try {\n this.win.focus();\n } catch {\n // focus is best-effort in embedded browsers\n }\n }\n\n get contentWindow(): Window | null {\n return this.win;\n }\n\n open(): void {\n try {\n this.win.focus();\n } catch {\n // focus is best-effort\n }\n }\n\n load(url: string): void {\n if (this.destroyed) return;\n this.navigated = true;\n try {\n // `replace` keeps the empty window out of the buyer's back history.\n this.win.location.replace(url);\n } catch {\n if (this.win.closed) {\n this.dismiss();\n return;\n }\n // Some embedded browsers refuse one navigation call but accept another.\n try {\n this.win.location.href = url;\n } catch {\n // The portal never loaded; the close poll will notice if it is gone.\n }\n }\n this.onLoad?.();\n }\n\n showError(message: string): void {\n // After the navigation the portal owns that document; nothing safe to paint.\n if (this.destroyed || this.navigated) return;\n\n this.renderError(\n documentHtml(\n this.title,\n this.dir,\n this.lang,\n `<div class=\"panel\">` +\n `<span class=\"error-icon\" aria-hidden=\"true\">!</span>` +\n `<h2 class=\"error-title\">${escapeHtml(this.labels.errorTitle)}</h2>` +\n `<p class=\"error-message\">${escapeHtml(message)}</p>` +\n `<button class=\"error-close\" type=\"button\">${escapeHtml(this.labels.close)}</button>` +\n `</div>`\n )\n );\n\n try {\n const close = this.win.document.querySelector(\".error-close\");\n close?.addEventListener(\"click\", () => this.onDismiss());\n } catch {\n // The window went away before the close button could be wired.\n }\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.stopPolling();\n this.onLoad = null;\n try {\n this.win.close();\n } catch {\n // Already closed by the buyer.\n }\n }\n\n /**\n * Best-effort: the placeholder document is cosmetic. A refused write must\n * never cancel the checkout — the navigation below still has to happen.\n */\n private renderError(html: string): void {\n try {\n const doc = this.win.document;\n doc.open();\n doc.write(html);\n doc.close();\n } catch {\n // Browser refused to let us paint into the blank window.\n }\n }\n\n private checkClosed(): void {\n if (this.destroyed) {\n this.stopPolling();\n return;\n }\n if (this.win.closed) this.dismiss();\n }\n\n private dismiss(): void {\n if (this.destroyed) return;\n this.stopPolling();\n this.onDismiss();\n }\n\n private stopPolling(): void {\n if (this.pollId !== null) {\n window.clearInterval(this.pollId);\n this.pollId = null;\n }\n }\n}\n","import { EsaalatCheckoutError } from \"./errors\";\nimport { CheckoutModal } from \"./modal\";\nimport type {\n CheckoutBeforeCreateOrderHandler,\n CheckoutCreatePayload,\n CheckoutItem,\n CheckoutLocale,\n CheckoutOpenOptions,\n CheckoutPresentation,\n CheckoutResult,\n CheckoutResultReason,\n CheckoutResultStatus,\n CheckoutSession,\n EsaalatCheckoutOptions,\n} from \"./types\";\nimport type { CheckoutView } from \"./view\";\nimport { CheckoutWindow } from \"./window\";\n\nconst DEFAULT_API_BASE = \"https://api.esaalat.com/api/v1\";\nconst DEFAULT_PORTAL_BASE = \"https://pay.esaalat.com\";\nconst SDK_VERSION = \"0.1.0\";\nconst REQUEST_TIMEOUT_MS = 20_000;\nconst INIT_RETRY_MS = 300;\n// A browser window needs longer than an iframe: the init message races the\n// navigation, so keep knocking until the portal answers `ready`.\nconst INIT_ATTEMPTS = 100;\nconst VERIFIED_CLOSE_MS = 1_800;\nconst ERROR_CLOSE_MS = 6_000;\n\nconst RESULT_STATUSES: CheckoutResultStatus[] = [\n \"VERIFIED\",\n \"WRONG_AMOUNT\",\n \"EXPIRED\",\n \"CANCELLED\",\n \"FAILED\",\n];\n\nconst RESULT_REASONS: CheckoutResultReason[] = [\n \"dismissed\",\n \"destroyed\",\n \"intent_cancelled\",\n \"session_expired\",\n \"session_not_found\",\n \"network_error\",\n];\n\ninterface ActiveCheckout {\n token: string | null;\n locale: CheckoutLocale;\n controller: AbortController;\n view: CheckoutView;\n settled: boolean;\n resolve: (result: CheckoutResult) => void;\n reject: (error: Error) => void;\n onResult?: (result: CheckoutResult) => void;\n}\n\ninterface PortalMessage {\n source?: unknown;\n type?: unknown;\n sessionToken?: unknown;\n payload?: unknown;\n}\n\ninterface PortalResultPayload {\n status?: unknown;\n reason?: unknown;\n intentCode?: unknown;\n amount?: unknown;\n paidAmount?: unknown;\n currency?: unknown;\n externalOrderId?: unknown;\n merchantName?: unknown;\n}\n\nfunction normalizeBase(value: string, fallback: string): string {\n const raw = value.trim() || fallback;\n let url: URL;\n try {\n const base =\n typeof window !== \"undefined\" ? window.location?.href : undefined;\n url = new URL(raw, base);\n } catch {\n throw new EsaalatCheckoutError(\n `Invalid URL: ${raw}`,\n \"configuration\"\n );\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new EsaalatCheckoutError(\n `Unsupported URL protocol: ${url.protocol}`,\n \"configuration\"\n );\n }\n return url.toString().replace(/\\/+$/, \"\");\n}\n\nfunction requestId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n // UUID v4 fallback for older embedded browsers.\n return \"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx\".replace(/[xy]/g, (char) => {\n const random = (Math.random() * 16) | 0;\n const value = char === \"x\" ? random : (random & 0x3) | 0x8;\n return value.toString(16);\n });\n}\n\nfunction stringOr(value: unknown, fallback = \"\"): string {\n return typeof value === \"string\" ? value : fallback;\n}\n\nfunction nullOrString(value: unknown): string | null {\n return typeof value === \"string\" ? value : null;\n}\n\nfunction errorDetail(body: unknown): string | null {\n if (!body || typeof body !== \"object\") return null;\n const record = body as Record<string, unknown>;\n if (typeof record.detail === \"string\") return record.detail;\n if (Array.isArray(record.errors)) {\n for (const error of record.errors) {\n if (error && typeof error === \"object\") {\n const detail = (error as Record<string, unknown>).detail;\n if (typeof detail === \"string\") return detail;\n }\n }\n }\n for (const value of Object.values(record)) {\n if (typeof value === \"string\") return value;\n if (Array.isArray(value) && typeof value[0] === \"string\") return value[0];\n }\n return null;\n}\n\nfunction isCheckoutSession(value: unknown): value is CheckoutSession {\n if (!value || typeof value !== \"object\") return false;\n const record = value as Record<string, unknown>;\n return (\n typeof record.session_token === \"string\" &&\n typeof record.portal_url === \"string\" &&\n typeof record.amount === \"string\" &&\n typeof record.currency === \"string\"\n );\n}\n\nfunction cancelledResult(\n token: string | null,\n reason: \"dismissed\" | \"destroyed\"\n): CheckoutResult {\n return {\n status: \"CANCELLED\",\n reason,\n sessionToken: token ?? \"\",\n intentCode: null,\n amount: \"0.00\",\n paidAmount: null,\n currency: \"YER\",\n externalOrderId: \"\",\n merchantName: \"\",\n };\n}\n\n/**\n * Framework-agnostic Esaalat checkout client.\n *\n * It creates a checkout session with the merchant's publishable key, opens the\n * Esaalat-hosted portal in its own browser window (or an in-page modal when\n * asked, or when the browser blocks the window), and resolves `open()` with a\n * terminal result. The publishable key is public by design; keep the client\n * secret and any order confirmation on the server.\n */\nexport class EsaalatCheckout {\n private readonly publishableKey: string;\n private readonly apiBaseUrl: string;\n private readonly portalBaseUrl: string;\n private readonly zIndex: number;\n private readonly presentation: CheckoutPresentation;\n private readonly locale: CheckoutLocale;\n private readonly fetchImpl: typeof fetch | null;\n private readonly defaultOnResult?: (result: CheckoutResult) => void;\n private readonly defaultOnError?: (error: Error) => void;\n\n private active: ActiveCheckout | null = null;\n private view: CheckoutView | null = null;\n private portalOrigin: string | null = null;\n private sessionToken: string | null = null;\n private initTimer: number | null = null;\n private verifiedTimer: number | null = null;\n private errorTimer: number | null = null;\n\n constructor(options: EsaalatCheckoutOptions) {\n // The key is only needed to create a session in the browser. A merchant\n // that opens a server-created session passes its token instead, so an\n // empty key is allowed here and refused by `open()` only on the create\n // path.\n const key =\n typeof options?.publishableKey === \"string\"\n ? options.publishableKey.trim()\n : \"\";\n\n this.publishableKey = key;\n this.locale =\n options.locale === \"en\" || options.locale === \"ar\" ? options.locale : \"ar\";\n this.apiBaseUrl = normalizeBase(options.apiBaseUrl ?? \"\", DEFAULT_API_BASE);\n this.portalBaseUrl = normalizeBase(\n options.portalBaseUrl ?? \"\",\n DEFAULT_PORTAL_BASE\n );\n this.zIndex =\n typeof options.zIndex === \"number\" && Number.isFinite(options.zIndex)\n ? options.zIndex\n : 2_147_483_000;\n this.presentation =\n options.presentation === \"modal\" ? \"modal\" : \"window\";\n\n const fetchFromOptions = options.fetch;\n const globalFetch =\n typeof fetch === \"function\" ? fetch.bind(globalThis) : null;\n this.fetchImpl = fetchFromOptions ?? globalFetch;\n this.defaultOnResult = options.onResult;\n this.defaultOnError = options.onError;\n }\n\n /** True while a checkout window or modal is on screen. */\n get isOpen(): boolean {\n return this.view !== null;\n }\n\n /**\n * Open the hosted checkout and resolve with its terminal result.\n *\n * A second call while a checkout is open rejects; close the first one with\n * `close()` or wait for its result.\n */\n async open(options: CheckoutOpenOptions = {}): Promise<CheckoutResult> {\n if (this.active || this.view) {\n throw new EsaalatCheckoutError(\n \"A checkout is already open. Close it before starting another.\",\n \"configuration\"\n );\n }\n if (!this.fetchImpl) {\n throw new EsaalatCheckoutError(\n \"This browser does not support fetch. Use the SDK on a modern browser.\",\n \"configuration\"\n );\n }\n\n const locale =\n options.locale === \"en\" || options.locale === \"ar\"\n ? options.locale\n : this.locale;\n const items = options.items ?? [];\n const existingToken = options.sessionToken?.trim() || null;\n const hasAmount =\n options.amount !== undefined &&\n options.amount !== null &&\n String(options.amount).trim() !== \"\";\n if (!existingToken && !this.publishableKey) {\n throw new EsaalatCheckoutError(\n \"A publishable key is required to create a session. Pass one, or open a server-created `sessionToken`.\",\n \"configuration\"\n );\n }\n if (!existingToken && !hasAmount && items.length === 0) {\n throw new EsaalatCheckoutError(\n \"Provide an amount, items, or a server-created session token.\",\n \"configuration\"\n );\n }\n if (!existingToken && items.length > 0) {\n for (const item of items) {\n if (!item.name || item.price === undefined || item.price === null) {\n throw new EsaalatCheckoutError(\n \"Every item needs a name and a price.\",\n \"configuration\"\n );\n }\n }\n }\n\n const labels =\n locale === \"ar\"\n ? {\n close: \"إغلاق\",\n loading: \"جارٍ تجهيز الدفع الآمن…\",\n errorTitle: \"تعذّر بدء الدفع\",\n }\n : {\n close: \"Close\",\n loading: \"Preparing secure checkout…\",\n errorTitle: \"Couldn't start the checkout\",\n };\n\n const presentation: CheckoutPresentation =\n options.presentation === \"window\" || options.presentation === \"modal\"\n ? options.presentation\n : this.presentation;\n\n // The view is created before the first `await`: a browser window has to be\n // requested inside the buyer's click, or popup blockers discard it.\n let view: CheckoutView;\n try {\n view = this.createView(presentation, labels, locale);\n view.open();\n } catch (error) {\n throw new EsaalatCheckoutError(\n \"The checkout could not be opened.\",\n \"unavailable\",\n { cause: error }\n );\n }\n\n const controller = new AbortController();\n let resolvePromise!: (result: CheckoutResult) => void;\n let rejectPromise!: (error: Error) => void;\n const promise = new Promise<CheckoutResult>((resolve, reject) => {\n resolvePromise = resolve;\n rejectPromise = reject;\n });\n\n const onResult = options.onResult ?? this.defaultOnResult;\n const onError = options.onError ?? this.defaultOnError;\n\n const active: ActiveCheckout = {\n token: null,\n locale,\n controller,\n view,\n settled: false,\n resolve: resolvePromise,\n reject: rejectPromise,\n onResult,\n };\n this.active = active;\n this.view = view;\n this.sessionToken = null;\n this.portalOrigin = null;\n\n view.onLoad = () => this.startInitRetry();\n\n let portalUrl: string;\n try {\n if (existingToken) {\n active.token = existingToken;\n this.sessionToken = existingToken;\n portalUrl = this.buildPortalUrl(\n existingToken,\n options.portalUrl ?? \"\",\n locale\n );\n } else {\n const session = await this.createSession(\n options,\n locale,\n controller.signal\n );\n // The buyer closed the checkout while the session was being created.\n if (active.settled) return promise;\n active.token = session.session_token;\n this.sessionToken = session.session_token;\n portalUrl = this.buildPortalUrl(\n session.session_token,\n session.portal_url,\n locale\n );\n }\n this.portalOrigin = new URL(portalUrl).origin;\n } catch (error) {\n if (active.settled) return promise;\n const wrapped =\n error instanceof EsaalatCheckoutError\n ? error\n : new EsaalatCheckoutError(\n \"Esaalat returned an invalid checkout URL.\",\n \"session\",\n { cause: error }\n );\n active.settled = true;\n this.active = null;\n this.removeMessageListener();\n try {\n onError?.(wrapped);\n } catch {\n // A merchant callback must not prevent the promise from settling.\n }\n active.reject(wrapped);\n view.showError(wrapped.message);\n this.errorTimer = window.setTimeout(() => {\n if (this.view === view) this.teardown();\n }, ERROR_CLOSE_MS);\n return promise;\n }\n\n window.addEventListener(\"message\", this.handleMessage);\n view.load(portalUrl);\n return promise;\n }\n\n /** Close the checkout. An in-flight `open()` resolves with CANCELLED. */\n close(): void {\n if (this.active) {\n this.dismiss();\n return;\n }\n this.teardown();\n }\n\n /** Remove the checkout view and any listeners without resolving a result. */\n destroy(): void {\n if (this.active && !this.active.settled) {\n this.finish(cancelledResult(this.sessionToken, \"destroyed\"));\n return;\n }\n this.teardown();\n }\n\n private createView(\n presentation: CheckoutPresentation,\n labels: { close: string; loading: string; errorTitle: string },\n locale: CheckoutLocale\n ): CheckoutView {\n const title =\n locale === \"ar\" ? \"إيصالات — دفع آمن\" : \"Esaalat — Secure checkout\";\n const onDismiss = () => this.dismiss();\n\n if (presentation === \"window\") {\n try {\n return new CheckoutWindow({\n title,\n labels,\n dir: locale === \"ar\" ? \"rtl\" : \"ltr\",\n lang: locale,\n onDismiss,\n });\n } catch {\n // The popup was blocked (or there is no window): the modal is the\n // always-available fallback, so the buyer still gets to pay.\n }\n }\n\n return new CheckoutModal({\n zIndex: this.zIndex,\n title,\n labels,\n onDismiss,\n });\n }\n\n private buildPortalUrl(\n sessionToken: string,\n rawPortalUrl: string,\n locale: CheckoutLocale\n ): string {\n const raw =\n rawPortalUrl.trim() ||\n `${this.portalBaseUrl}/checkout/${encodeURIComponent(sessionToken)}`;\n const url = new URL(raw);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(\"Unsupported portal protocol\");\n }\n url.searchParams.set(\"lang\", locale);\n return url.toString();\n }\n\n private async createSession(\n options: CheckoutOpenOptions,\n locale: CheckoutLocale,\n outerSignal: AbortSignal\n ): Promise<CheckoutSession> {\n if (!this.fetchImpl) {\n throw new EsaalatCheckoutError(\"fetch is unavailable\", \"configuration\");\n }\n\n let payload: CheckoutCreatePayload = {};\n if (options.amount !== undefined && options.amount !== null) {\n payload.amount = String(options.amount);\n }\n if (options.items?.length) {\n payload.items = options.items.map((item: CheckoutItem) => ({\n name: item.name,\n ...(item.description ? { description: item.description } : {}),\n price: String(item.price),\n quantity: item.quantity ?? 1,\n }));\n }\n if (options.currency) payload.currency = options.currency;\n if (options.externalOrderId) {\n payload.external_order_id = options.externalOrderId;\n }\n if (options.notes) payload.notes = options.notes;\n if (options.wallet) payload.wallet = options.wallet;\n\n // The merchant's last word on what is sent. Whatever this throws becomes\n // the checkout's own error, so a veto reaches `open()` as `configuration`\n // with the merchant's message instead of being swallowed.\n const beforeCreate: CheckoutBeforeCreateOrderHandler | undefined =\n options.onBeforeCreateOrder;\n if (beforeCreate) {\n let updated: CheckoutCreatePayload | void;\n try {\n updated = await beforeCreate(payload);\n } catch (error) {\n throw new EsaalatCheckoutError(\n error instanceof Error && error.message\n ? error.message\n : locale === \"ar\"\n ? \"أُلغي الدفع.\"\n : \"The checkout was cancelled.\",\n \"configuration\",\n { cause: error }\n );\n }\n if (updated && typeof updated === \"object\") payload = updated;\n }\n\n const controller = new AbortController();\n let timedOut = false;\n const timeout = window.setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, REQUEST_TIMEOUT_MS);\n const abortOuter = () => controller.abort();\n outerSignal.addEventListener(\"abort\", abortOuter);\n\n try {\n const response = await this.fetchImpl(\n `${this.apiBaseUrl}/checkout/sdk/sessions/`,\n {\n method: \"POST\",\n credentials: \"omit\",\n cache: \"no-store\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n \"ESAALAT-PUBLISHABLE-KEY\": this.publishableKey,\n \"ESAALAT-IDEMPOTENCY-KEY\":\n options.idempotencyKey?.trim() || requestId(),\n \"ESAALAT-SDK-VERSION\": SDK_VERSION,\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n }\n );\n\n const session: unknown = await response.json().catch(() => null);\n if (!response.ok) {\n throw new EsaalatCheckoutError(\n errorDetail(session) ||\n (locale === \"ar\"\n ? \"تعذّر إنشاء جلسة الدفع.\"\n : \"Couldn't create the checkout session.\"),\n \"api\",\n { status: response.status, details: session }\n );\n }\n if (!isCheckoutSession(session)) {\n throw new EsaalatCheckoutError(\n \"Esaalat returned an invalid checkout session.\",\n \"session\",\n { details: session }\n );\n }\n\n // The session exists; a broken listener must not kill the payment.\n try {\n options.onOrderCreated?.(session);\n } catch {\n // Ignored on purpose.\n }\n\n return session;\n } catch (error) {\n if (error instanceof EsaalatCheckoutError) throw error;\n if (timedOut) {\n throw new EsaalatCheckoutError(\n locale === \"ar\"\n ? \"انتهت مهلة الاتصال بإيصالات. حاول مرة أخرى.\"\n : \"The connection to Esaalat timed out. Please try again.\",\n \"network\",\n { cause: error }\n );\n }\n if (error instanceof DOMException && error.name === \"AbortError\") {\n throw error;\n }\n throw new EsaalatCheckoutError(\n locale === \"ar\"\n ? \"تعذّر الاتصال بإيصالات. تحقق من اتصالك وحاول مرة أخرى.\"\n : \"Couldn't reach Esaalat. Check your connection and try again.\",\n \"network\",\n { cause: error }\n );\n } finally {\n window.clearTimeout(timeout);\n outerSignal.removeEventListener(\"abort\", abortOuter);\n }\n }\n\n private startInitRetry(): void {\n this.clearInitTimer();\n let attempts = 0;\n\n const tick = () => {\n if (!this.view || !this.portalOrigin || !this.sessionToken) return;\n this.postInit();\n attempts += 1;\n if (attempts < INIT_ATTEMPTS) {\n this.initTimer = window.setTimeout(tick, INIT_RETRY_MS);\n } else {\n this.initTimer = null;\n }\n };\n\n tick();\n }\n\n private postInit(): void {\n const target = this.view?.contentWindow;\n if (!target || !this.portalOrigin || !this.sessionToken) return;\n const message = {\n source: \"esaalat-checkout-sdk\",\n version: 1,\n type: \"init\",\n sessionToken: this.sessionToken,\n parentOrigin: window.location.origin,\n locale: this.active?.locale ?? this.locale,\n };\n try {\n target.postMessage(message, this.portalOrigin);\n } catch {\n // The iframe may have been torn down between the check and the post.\n }\n }\n\n private clearInitTimer(): void {\n if (this.initTimer !== null) {\n window.clearTimeout(this.initTimer);\n this.initTimer = null;\n }\n }\n\n private handleMessage = (event: MessageEvent): void => {\n if (!this.view || !this.portalOrigin) return;\n if (event.source !== this.view.contentWindow) return;\n if (event.origin !== this.portalOrigin) return;\n\n const data = event.data as PortalMessage | null;\n if (!data || typeof data !== \"object\") return;\n if (data.source !== \"esaalat-checkout\") return;\n if (data.sessionToken !== this.sessionToken) return;\n\n if (data.type === \"ready\") {\n this.clearInitTimer();\n return;\n }\n\n if (data.type === \"close\") {\n this.dismiss();\n return;\n }\n\n if (data.type === \"result\") {\n const result = this.parseResult(data.payload);\n if (result) this.finish(result);\n }\n };\n\n private parseResult(payload: unknown): CheckoutResult | null {\n if (!payload || typeof payload !== \"object\") return null;\n const value = payload as PortalResultPayload;\n if (\n typeof value.status !== \"string\" ||\n !RESULT_STATUSES.includes(value.status as CheckoutResultStatus)\n ) {\n return null;\n }\n\n const reason =\n typeof value.reason === \"string\" &&\n RESULT_REASONS.includes(value.reason as CheckoutResultReason)\n ? (value.reason as CheckoutResultReason)\n : undefined;\n\n return {\n status: value.status as CheckoutResultStatus,\n ...(reason ? { reason } : {}),\n sessionToken: this.sessionToken ?? \"\",\n intentCode: nullOrString(value.intentCode),\n amount: stringOr(value.amount, \"0.00\"),\n paidAmount: nullOrString(value.paidAmount),\n currency: stringOr(value.currency, \"YER\"),\n externalOrderId: stringOr(value.externalOrderId),\n merchantName: stringOr(value.merchantName),\n };\n }\n\n private finish(result: CheckoutResult): void {\n const active = this.active;\n if (!active || active.settled) return;\n\n active.settled = true;\n this.active = null;\n this.clearInitTimer();\n active.controller.abort();\n try {\n active.onResult?.(result);\n } catch {\n // A merchant callback must not prevent the promise from settling.\n }\n active.resolve(result);\n\n if (result.reason === \"dismissed\" || result.reason === \"destroyed\") {\n this.teardown();\n return;\n }\n\n if (result.status === \"VERIFIED\") {\n this.verifiedTimer = window.setTimeout(() => this.teardown(), VERIFIED_CLOSE_MS);\n }\n // Other terminal states stay visible until the buyer closes them, so the\n // reason (wrong amount, expiry, cancellation) is not flashed away.\n }\n\n private dismiss(): void {\n if (this.active && !this.active.settled) {\n this.finish(cancelledResult(this.sessionToken, \"dismissed\"));\n return;\n }\n this.teardown();\n }\n\n private removeMessageListener(): void {\n window.removeEventListener(\"message\", this.handleMessage);\n }\n\n private clearTimers(): void {\n this.clearInitTimer();\n if (this.verifiedTimer !== null) {\n window.clearTimeout(this.verifiedTimer);\n this.verifiedTimer = null;\n }\n if (this.errorTimer !== null) {\n window.clearTimeout(this.errorTimer);\n this.errorTimer = null;\n }\n }\n\n private teardown(): void {\n this.clearTimers();\n this.removeMessageListener();\n if (this.view) {\n this.view.destroy();\n this.view = null;\n }\n this.portalOrigin = null;\n this.sessionToken = null;\n this.active = null;\n }\n}\n","import { EsaalatCheckout } from \"./client\";\nimport { EsaalatCheckoutError } from \"./errors\";\nimport type {\n CheckoutBeforeCreateOrderHandler,\n CheckoutItem,\n CheckoutLocale,\n CheckoutOpenOptions,\n CheckoutPresentation,\n CheckoutResult,\n CheckoutResultStatus,\n CheckoutSession,\n} from \"./types\";\n\n/**\n * A \"Pay with Esaalat\" button that any framework can render.\n *\n * It is a custom element, not a React/Vue/Angular component, because the\n * checkout is one behaviour that should not be reimplemented per framework:\n * the same tag and the same attributes work in plain HTML, in a Vue template\n * (`@esaalat-verified`), in an Angular template (`(esaalat-verified)` with\n * `CUSTOM_ELEMENTS_SCHEMA`) and — through `esaalat-react`, which only sets\n * properties and listeners — in JSX.\n *\n * The element is a thin shell over `EsaalatCheckout`: a click calls `open()`\n * with the attributes as options, and the terminal result is routed to ONE\n * named callback per outcome (see `OUTCOMES`). Nothing here re-implements the\n * portal, the session API or the result contract.\n *\n * ```html\n * <esaalat-checkout-button\n * publishable-key=\"pk_live_...\"\n * amount=\"5000.00\"\n * external-order-id=\"order-123\"\n * ></esaalat-checkout-button>\n * <script>\n * const button = document.querySelector(\"esaalat-checkout-button\");\n * button.onVerified = (result) => console.log(result.intentCode);\n * button.onCancelled = () => console.log(\"not this time\");\n * </script>\n * ```\n */\n\n/** The tag this element is registered under. */\nexport const ESAALAT_CHECKOUT_BUTTON_TAG = \"esaalat-checkout-button\";\n\n/** Label shown when the merchant passes neither `label` nor child text. */\nconst DEFAULT_LABELS: Record<CheckoutLocale, string> = {\n ar: \"ادفع الآن\",\n en: \"Pay now\",\n};\n\ntype HandlerName =\n | \"onVerified\"\n | \"onCancelled\"\n | \"onWrongAmount\"\n | \"onExpired\"\n | \"onFailed\";\n\n/**\n * One callback per terminal status: the buyer's outcome picks exactly one row,\n * so a merchant never has to switch on `result.status` to find the interesting\n * case. `FAILED` is a result (the portal could not load a valid session), not a\n * thrown error — `onError` stays reserved for `open()` rejecting.\n */\nconst OUTCOMES: Record<\n CheckoutResultStatus,\n { handler: HandlerName; event: string }\n> = {\n VERIFIED: { handler: \"onVerified\", event: \"esaalat-verified\" },\n CANCELLED: { handler: \"onCancelled\", event: \"esaalat-cancelled\" },\n WRONG_AMOUNT: { handler: \"onWrongAmount\", event: \"esaalat-wrong-amount\" },\n EXPIRED: { handler: \"onExpired\", event: \"esaalat-expired\" },\n FAILED: { handler: \"onFailed\", event: \"esaalat-failed\" },\n};\n\n/** Names of the DOM events, exported for `addEventListener` users. */\nexport const ESAALAT_CHECKOUT_EVENTS = {\n verified: \"esaalat-verified\",\n cancelled: \"esaalat-cancelled\",\n wrongAmount: \"esaalat-wrong-amount\",\n expired: \"esaalat-expired\",\n failed: \"esaalat-failed\",\n error: \"esaalat-error\",\n} as const;\n\n/** How often the button re-checks whether its checkout has been closed. */\nconst WATCH_INTERVAL_MS = 300;\n\nfunction asError(error: unknown): Error {\n if (error instanceof Error) return error;\n return new EsaalatCheckoutError(String(error) || \"The checkout failed.\", \"unavailable\");\n}\n\nexport class EsaalatCheckoutButtonElement extends HTMLElement {\n static get observedAttributes(): string[] {\n return [\n \"publishable-key\",\n \"amount\",\n \"currency\",\n \"external-order-id\",\n \"notes\",\n \"wallet\",\n \"locale\",\n \"presentation\",\n \"session-token\",\n \"portal-url\",\n \"label\",\n \"disabled\",\n \"items\",\n ];\n }\n\n /** Called with the result when the wallet alert verified the payment. */\n onVerified?: (result: CheckoutResult) => void;\n /** Called when the buyer dismissed the checkout, or the intent was cancelled. */\n onCancelled?: (result: CheckoutResult) => void;\n /** Called when a payment matched but for a different amount. */\n onWrongAmount?: (result: CheckoutResult) => void;\n /** Called when the buyer ran out of time. */\n onExpired?: (result: CheckoutResult) => void;\n /** Called when the portal could not load a valid session. */\n onFailed?: (result: CheckoutResult) => void;\n /** Called when `open()` rejects (configuration, network, API or unavailable). */\n onError?: (error: Error) => void;\n\n /**\n * Called just before this button creates the session in the browser, with\n * the body about to be sent. Return an object to add or override fields\n * (`external_order_id`, `notes`, anything your API accepts); throwing\n * cancels the checkout and reports the message via `onError`.\n */\n onBeforeCreateOrder?: CheckoutBeforeCreateOrderHandler;\n\n /**\n * Called with the created session (token, intent, expiry...) when this\n * button created it in the browser. Not called when a `session-token`\n * attribute is set, because that session was made on your server.\n */\n onOrderCreated?: (session: CheckoutSession) => void;\n\n /**\n * Order lines as data. The `items` attribute carries the same as JSON, for\n * HTML templates that have no way to set a property.\n */\n items: CheckoutItem[] | null = null;\n\n private client: EsaalatCheckout | null = null;\n private clientKey: string | null = null;\n private button: HTMLButtonElement | null = null;\n private busy = false;\n private watchTimer: ReturnType<typeof setInterval> | null = null;\n /** Text written between the tags, captured before the button is injected. */\n private slotLabel: string | null = null;\n\n connectedCallback(): void {\n this.render();\n }\n\n disconnectedCallback(): void {\n // The checkout itself lives on `document.body`, so a removed button must\n // not cancel a payment in progress. Only the state watcher goes away.\n this.stopWatch();\n }\n\n attributeChangedCallback(\n name: string,\n previous: string | null,\n value: string | null\n ): void {\n if (previous === value) return;\n\n if (name === \"publishable-key\") {\n // A different key means a different client; the next click builds it.\n this.client = null;\n this.clientKey = null;\n return;\n }\n if (name === \"label\" || name === \"locale\") {\n this.applyLabel();\n return;\n }\n if (name === \"disabled\") {\n this.syncBusy();\n }\n }\n\n /** Open the checkout, exactly as a click does (for programmatic use). */\n async open(): Promise<void> {\n await this.start();\n }\n\n private handleClick = (): void => {\n // Not awaited: the click task must keep running synchronously so the\n // browser-window presentation is still inside the buyer's gesture.\n void this.start();\n };\n\n private async start(): Promise<void> {\n if (this.busy || this.isCheckoutOpen()) return;\n\n const key = (this.getAttribute(\"publishable-key\") ?? \"\").trim();\n // A `session-token` already names the checkout; the key only creates one.\n if (!key && !this.attribute(\"session-token\")) {\n this.reportError(\n new EsaalatCheckoutError(\"A publishable key is required.\", \"configuration\")\n );\n return;\n }\n\n this.setBusy(true);\n this.watch();\n\n try {\n const items = this.readItems();\n const client = this.ensureClient(key);\n const result = await client.open(this.openOptions(items));\n this.handleResult(result);\n } catch (error) {\n this.reportError(asError(error));\n } finally {\n this.setBusy(false);\n }\n }\n\n private ensureClient(key: string): EsaalatCheckout {\n if (this.client && this.clientKey === key) return this.client;\n // Replacing the client only happens when the key changes. It has no open\n // checkout to lose: `start()` refuses to run while one is on screen.\n this.clientKey = key;\n this.client = new EsaalatCheckout({\n publishableKey: key,\n locale: this.locale(),\n });\n return this.client;\n }\n\n private openOptions(items: CheckoutItem[] | null): CheckoutOpenOptions {\n return {\n amount: this.attribute(\"amount\"),\n items: items ?? undefined,\n currency: this.attribute(\"currency\"),\n externalOrderId: this.attribute(\"external-order-id\"),\n notes: this.attribute(\"notes\"),\n wallet: this.attribute(\"wallet\"),\n sessionToken: this.attribute(\"session-token\"),\n portalUrl: this.attribute(\"portal-url\"),\n locale: this.locale(),\n presentation: this.presentation(),\n onBeforeCreateOrder: this.onBeforeCreateOrder,\n onOrderCreated: this.onOrderCreated,\n };\n }\n\n private handleResult(result: CheckoutResult): void {\n const outcome = OUTCOMES[result.status];\n if (!outcome) return;\n\n // The status is the single source of truth: only the matching callback\n // runs, so a merchant can wire all five without guarding each one.\n this.callSafely(this[outcome.handler] as ((r: CheckoutResult) => void) | undefined, result);\n this.dispatchOutcome(outcome.event, result);\n }\n\n private reportError(error: Error): void {\n this.callSafely(this.onError, error);\n this.dispatchOutcome(ESAALAT_CHECKOUT_EVENTS.error, error);\n }\n\n /**\n * DOM events carry the same detail as the callbacks, so templates that bind\n * events instead of properties (`@esaalat-verified`, `(esaalat-verified)`)\n * need no extra JS.\n */\n private dispatchOutcome(name: string, detail: CheckoutResult | Error): void {\n this.dispatchEvent(\n new CustomEvent(name, { detail, bubbles: true, composed: true })\n );\n }\n\n private callSafely<Arg>(handler: ((argument: Arg) => void) | undefined, argument: Arg): void {\n if (!handler) return;\n try {\n handler(argument);\n } catch {\n // A merchant callback must never break the element's own bookkeeping.\n }\n }\n\n private readItems(): CheckoutItem[] | null {\n if (this.items && this.items.length > 0) return this.items;\n\n const raw = this.attribute(\"items\");\n if (!raw) return null;\n try {\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) throw new Error(\"not an array\");\n return parsed as CheckoutItem[];\n } catch {\n throw new EsaalatCheckoutError(\n \"The items attribute must be a JSON array of order lines.\",\n \"configuration\"\n );\n }\n }\n\n private attribute(name: string): string | undefined {\n const value = (this.getAttribute(name) ?? \"\").trim();\n return value || undefined;\n }\n\n private locale(): CheckoutLocale {\n return this.getAttribute(\"locale\") === \"en\" ? \"en\" : \"ar\";\n }\n\n private presentation(): CheckoutPresentation {\n // A component implies in-page, so the modal is the default here even\n // though the imperative client opens a browser window by default.\n return this.getAttribute(\"presentation\") === \"window\" ? \"window\" : \"modal\";\n }\n\n private isCheckoutOpen(): boolean {\n return this.client?.isOpen ?? false;\n }\n\n // -- rendering -----------------------------------------------------------\n\n private render(): void {\n if (!this.button) {\n // Read any \"Pay now\" written between the tags before the button exists,\n // so `<esaalat-checkout-button>Pay</esaalat-checkout-button>` works.\n if (this.slotLabel === null) {\n this.slotLabel = (this.textContent ?? \"\").trim();\n }\n\n const button = this.ownerDocument.createElement(\"button\");\n button.type = \"button\";\n button.className = \"esaalat-checkout-button\";\n button.addEventListener(\"click\", this.handleClick);\n this.replaceChildren(button);\n this.button = button;\n this.applyStyles(button);\n }\n this.applyLabel();\n this.syncBusy();\n }\n\n private applyLabel(): void {\n if (!this.button) return;\n const label =\n this.attribute(\"label\") ??\n (this.slotLabel || undefined) ??\n DEFAULT_LABELS[this.locale()];\n this.button.textContent = label;\n }\n\n /**\n * Defaults live on the inner button and read CSS custom properties, so a\n * merchant themes the control from their own stylesheet without the element\n * shipping a global stylesheet or a shadow root that would trap their CSS.\n */\n private applyStyles(button: HTMLButtonElement): void {\n button.style.appearance = \"none\";\n button.style.border =\n \"1px solid var(--esaalat-button-border, transparent)\";\n button.style.borderRadius = \"var(--esaalat-button-radius, 12px)\";\n button.style.background = \"var(--esaalat-button-background, #1f2a24)\";\n button.style.color = \"var(--esaalat-button-color, #ffffff)\";\n button.style.font = \"inherit\";\n button.style.fontSize = \"var(--esaalat-button-font-size, 15px)\";\n button.style.fontWeight = \"600\";\n button.style.lineHeight = \"1.2\";\n button.style.padding = \"var(--esaalat-button-padding, 14px 22px)\";\n button.style.cursor = \"pointer\";\n button.style.transition = \"opacity 120ms ease-out\";\n }\n\n private setBusy(busy: boolean): void {\n this.busy = busy;\n this.syncBusy();\n }\n\n private syncBusy(): void {\n if (!this.button) return;\n const busy = this.busy || this.isCheckoutOpen();\n // `disabled` always locks the button; otherwise it is locked only while\n // this element is the one driving a checkout.\n const locked = busy || this.hasAttribute(\"disabled\");\n this.button.disabled = locked;\n this.button.setAttribute(\"aria-busy\", busy ? \"true\" : \"false\");\n this.button.style.opacity = this.hasAttribute(\"disabled\") && !busy ? \"0.6\" : \"1\";\n this.button.style.cursor = locked ? \"default\" : \"pointer\";\n }\n\n // -- state watcher -------------------------------------------------------\n\n /**\n * A result resolves `open()`, but the portal stays on screen for a moment\n * (`VERIFIED` auto-closes; every other outcome waits for the buyer to close\n * it). The button has to stay locked until then — a click in that window\n * would only reach \"a checkout is already open\".\n */\n private watch(): void {\n if (this.watchTimer !== null || typeof setInterval !== \"function\") return;\n this.watchTimer = setInterval(() => {\n // `busy` covers the window before the portal has loaded (a slow session\n // request): the checkout is not open yet, but the watch is still needed\n // for when it closes afterwards.\n if (!this.busy && !this.isCheckoutOpen()) this.stopWatch();\n this.syncBusy();\n }, WATCH_INTERVAL_MS);\n }\n\n private stopWatch(): void {\n if (this.watchTimer === null) return;\n clearInterval(this.watchTimer);\n this.watchTimer = null;\n }\n}\n\n/**\n * Define `<esaalat-checkout-button>`. Safe to call repeatedly and on the\n * server (no `customElements`), so a bundler can import this module during\n * SSR without a guard of its own.\n *\n * Returns whether this call defined the element.\n */\nexport function registerEsaalatCheckoutButton(): boolean {\n if (typeof customElements === \"undefined\") return false;\n if (customElements.get(ESAALAT_CHECKOUT_BUTTON_TAG)) return false;\n customElements.define(ESAALAT_CHECKOUT_BUTTON_TAG, EsaalatCheckoutButtonElement);\n return true;\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n [ESAALAT_CHECKOUT_BUTTON_TAG]: EsaalatCheckoutButtonElement;\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -85,13 +85,46 @@ interface CheckoutItem {
|
|
|
85
85
|
price: string | number;
|
|
86
86
|
quantity?: number;
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* The body the SDK is about to POST to create the session from the browser.
|
|
90
|
+
*
|
|
91
|
+
* `onBeforeCreateOrder` receives it, may return a changed copy, and may add
|
|
92
|
+
* fields the SDK has no opinion about; whatever comes back goes to the API
|
|
93
|
+
* exactly as returned.
|
|
94
|
+
*/
|
|
95
|
+
interface CheckoutCreatePayload {
|
|
96
|
+
amount?: string;
|
|
97
|
+
items?: Array<{
|
|
98
|
+
name: string;
|
|
99
|
+
description?: string;
|
|
100
|
+
price: string;
|
|
101
|
+
quantity: number;
|
|
102
|
+
}>;
|
|
103
|
+
currency?: string;
|
|
104
|
+
external_order_id?: string;
|
|
105
|
+
notes?: string;
|
|
106
|
+
wallet?: string;
|
|
107
|
+
[key: string]: unknown;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Runs right before the session request is sent.
|
|
111
|
+
*
|
|
112
|
+
* Return an object (or a promise of one) to send it instead; return nothing to
|
|
113
|
+
* send the payload as the SDK built it. Throwing cancels the checkout and
|
|
114
|
+
* surfaces the thrown message to the buyer.
|
|
115
|
+
*/
|
|
116
|
+
type CheckoutBeforeCreateOrderHandler = (payload: CheckoutCreatePayload) => CheckoutCreatePayload | void | Promise<CheckoutCreatePayload | void>;
|
|
88
117
|
interface EsaalatCheckoutOptions {
|
|
89
118
|
/**
|
|
90
119
|
* The merchant's publishable key (`pk_...`) from Dashboard → API Keys.
|
|
91
120
|
* This key is safe to embed in a storefront: it can only start checkout
|
|
92
121
|
* sessions, never read wallets, intents, or account data.
|
|
122
|
+
*
|
|
123
|
+
* Required when the browser creates the session. Omit it when every
|
|
124
|
+
* `open()` call passes a `sessionToken` created by your own server — the
|
|
125
|
+
* key is never used on that path.
|
|
93
126
|
*/
|
|
94
|
-
publishableKey
|
|
127
|
+
publishableKey?: string;
|
|
95
128
|
/** Portal language. Defaults to Arabic. */
|
|
96
129
|
locale?: CheckoutLocale;
|
|
97
130
|
/**
|
|
@@ -147,6 +180,19 @@ interface CheckoutOpenOptions {
|
|
|
147
180
|
locale?: CheckoutLocale;
|
|
148
181
|
/** Overrides the client-level presentation for this checkout. */
|
|
149
182
|
presentation?: CheckoutPresentation;
|
|
183
|
+
/**
|
|
184
|
+
* Runs right before the browser creates the session (POST
|
|
185
|
+
* `/checkout/sdk/sessions/`), with the body about to be sent. Return an
|
|
186
|
+
* object to send it changed, or throw to cancel the checkout. Never called
|
|
187
|
+
* when `sessionToken` is supplied: that session already exists.
|
|
188
|
+
*/
|
|
189
|
+
onBeforeCreateOrder?: CheckoutBeforeCreateOrderHandler;
|
|
190
|
+
/**
|
|
191
|
+
* Runs once the browser-created session came back, with the full session
|
|
192
|
+
* (token, intent, expiry...) while the portal is still loading. Not called
|
|
193
|
+
* for a `sessionToken` supplied from your own server.
|
|
194
|
+
*/
|
|
195
|
+
onOrderCreated?: (session: CheckoutSession) => void;
|
|
150
196
|
onResult?: (result: CheckoutResult) => void;
|
|
151
197
|
onError?: (error: Error) => void;
|
|
152
198
|
}
|
|
@@ -206,6 +252,140 @@ declare class EsaalatCheckout {
|
|
|
206
252
|
private teardown;
|
|
207
253
|
}
|
|
208
254
|
|
|
255
|
+
/**
|
|
256
|
+
* A "Pay with Esaalat" button that any framework can render.
|
|
257
|
+
*
|
|
258
|
+
* It is a custom element, not a React/Vue/Angular component, because the
|
|
259
|
+
* checkout is one behaviour that should not be reimplemented per framework:
|
|
260
|
+
* the same tag and the same attributes work in plain HTML, in a Vue template
|
|
261
|
+
* (`@esaalat-verified`), in an Angular template (`(esaalat-verified)` with
|
|
262
|
+
* `CUSTOM_ELEMENTS_SCHEMA`) and — through `esaalat-react`, which only sets
|
|
263
|
+
* properties and listeners — in JSX.
|
|
264
|
+
*
|
|
265
|
+
* The element is a thin shell over `EsaalatCheckout`: a click calls `open()`
|
|
266
|
+
* with the attributes as options, and the terminal result is routed to ONE
|
|
267
|
+
* named callback per outcome (see `OUTCOMES`). Nothing here re-implements the
|
|
268
|
+
* portal, the session API or the result contract.
|
|
269
|
+
*
|
|
270
|
+
* ```html
|
|
271
|
+
* <esaalat-checkout-button
|
|
272
|
+
* publishable-key="pk_live_..."
|
|
273
|
+
* amount="5000.00"
|
|
274
|
+
* external-order-id="order-123"
|
|
275
|
+
* ></esaalat-checkout-button>
|
|
276
|
+
* <script>
|
|
277
|
+
* const button = document.querySelector("esaalat-checkout-button");
|
|
278
|
+
* button.onVerified = (result) => console.log(result.intentCode);
|
|
279
|
+
* button.onCancelled = () => console.log("not this time");
|
|
280
|
+
* </script>
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
/** The tag this element is registered under. */
|
|
284
|
+
declare const ESAALAT_CHECKOUT_BUTTON_TAG = "esaalat-checkout-button";
|
|
285
|
+
/** Names of the DOM events, exported for `addEventListener` users. */
|
|
286
|
+
declare const ESAALAT_CHECKOUT_EVENTS: {
|
|
287
|
+
readonly verified: "esaalat-verified";
|
|
288
|
+
readonly cancelled: "esaalat-cancelled";
|
|
289
|
+
readonly wrongAmount: "esaalat-wrong-amount";
|
|
290
|
+
readonly expired: "esaalat-expired";
|
|
291
|
+
readonly failed: "esaalat-failed";
|
|
292
|
+
readonly error: "esaalat-error";
|
|
293
|
+
};
|
|
294
|
+
declare class EsaalatCheckoutButtonElement extends HTMLElement {
|
|
295
|
+
static get observedAttributes(): string[];
|
|
296
|
+
/** Called with the result when the wallet alert verified the payment. */
|
|
297
|
+
onVerified?: (result: CheckoutResult) => void;
|
|
298
|
+
/** Called when the buyer dismissed the checkout, or the intent was cancelled. */
|
|
299
|
+
onCancelled?: (result: CheckoutResult) => void;
|
|
300
|
+
/** Called when a payment matched but for a different amount. */
|
|
301
|
+
onWrongAmount?: (result: CheckoutResult) => void;
|
|
302
|
+
/** Called when the buyer ran out of time. */
|
|
303
|
+
onExpired?: (result: CheckoutResult) => void;
|
|
304
|
+
/** Called when the portal could not load a valid session. */
|
|
305
|
+
onFailed?: (result: CheckoutResult) => void;
|
|
306
|
+
/** Called when `open()` rejects (configuration, network, API or unavailable). */
|
|
307
|
+
onError?: (error: Error) => void;
|
|
308
|
+
/**
|
|
309
|
+
* Called just before this button creates the session in the browser, with
|
|
310
|
+
* the body about to be sent. Return an object to add or override fields
|
|
311
|
+
* (`external_order_id`, `notes`, anything your API accepts); throwing
|
|
312
|
+
* cancels the checkout and reports the message via `onError`.
|
|
313
|
+
*/
|
|
314
|
+
onBeforeCreateOrder?: CheckoutBeforeCreateOrderHandler;
|
|
315
|
+
/**
|
|
316
|
+
* Called with the created session (token, intent, expiry...) when this
|
|
317
|
+
* button created it in the browser. Not called when a `session-token`
|
|
318
|
+
* attribute is set, because that session was made on your server.
|
|
319
|
+
*/
|
|
320
|
+
onOrderCreated?: (session: CheckoutSession) => void;
|
|
321
|
+
/**
|
|
322
|
+
* Order lines as data. The `items` attribute carries the same as JSON, for
|
|
323
|
+
* HTML templates that have no way to set a property.
|
|
324
|
+
*/
|
|
325
|
+
items: CheckoutItem[] | null;
|
|
326
|
+
private client;
|
|
327
|
+
private clientKey;
|
|
328
|
+
private button;
|
|
329
|
+
private busy;
|
|
330
|
+
private watchTimer;
|
|
331
|
+
/** Text written between the tags, captured before the button is injected. */
|
|
332
|
+
private slotLabel;
|
|
333
|
+
connectedCallback(): void;
|
|
334
|
+
disconnectedCallback(): void;
|
|
335
|
+
attributeChangedCallback(name: string, previous: string | null, value: string | null): void;
|
|
336
|
+
/** Open the checkout, exactly as a click does (for programmatic use). */
|
|
337
|
+
open(): Promise<void>;
|
|
338
|
+
private handleClick;
|
|
339
|
+
private start;
|
|
340
|
+
private ensureClient;
|
|
341
|
+
private openOptions;
|
|
342
|
+
private handleResult;
|
|
343
|
+
private reportError;
|
|
344
|
+
/**
|
|
345
|
+
* DOM events carry the same detail as the callbacks, so templates that bind
|
|
346
|
+
* events instead of properties (`@esaalat-verified`, `(esaalat-verified)`)
|
|
347
|
+
* need no extra JS.
|
|
348
|
+
*/
|
|
349
|
+
private dispatchOutcome;
|
|
350
|
+
private callSafely;
|
|
351
|
+
private readItems;
|
|
352
|
+
private attribute;
|
|
353
|
+
private locale;
|
|
354
|
+
private presentation;
|
|
355
|
+
private isCheckoutOpen;
|
|
356
|
+
private render;
|
|
357
|
+
private applyLabel;
|
|
358
|
+
/**
|
|
359
|
+
* Defaults live on the inner button and read CSS custom properties, so a
|
|
360
|
+
* merchant themes the control from their own stylesheet without the element
|
|
361
|
+
* shipping a global stylesheet or a shadow root that would trap their CSS.
|
|
362
|
+
*/
|
|
363
|
+
private applyStyles;
|
|
364
|
+
private setBusy;
|
|
365
|
+
private syncBusy;
|
|
366
|
+
/**
|
|
367
|
+
* A result resolves `open()`, but the portal stays on screen for a moment
|
|
368
|
+
* (`VERIFIED` auto-closes; every other outcome waits for the buyer to close
|
|
369
|
+
* it). The button has to stay locked until then — a click in that window
|
|
370
|
+
* would only reach "a checkout is already open".
|
|
371
|
+
*/
|
|
372
|
+
private watch;
|
|
373
|
+
private stopWatch;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Define `<esaalat-checkout-button>`. Safe to call repeatedly and on the
|
|
377
|
+
* server (no `customElements`), so a bundler can import this module during
|
|
378
|
+
* SSR without a guard of its own.
|
|
379
|
+
*
|
|
380
|
+
* Returns whether this call defined the element.
|
|
381
|
+
*/
|
|
382
|
+
declare function registerEsaalatCheckoutButton(): boolean;
|
|
383
|
+
declare global {
|
|
384
|
+
interface HTMLElementTagNameMap {
|
|
385
|
+
[ESAALAT_CHECKOUT_BUTTON_TAG]: EsaalatCheckoutButtonElement;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
209
389
|
type CheckoutErrorCode = "configuration" | "network" | "api" | "session" | "unavailable" | "aborted";
|
|
210
390
|
declare class EsaalatCheckoutError extends Error {
|
|
211
391
|
readonly code: CheckoutErrorCode;
|
|
@@ -218,4 +398,4 @@ declare class EsaalatCheckoutError extends Error {
|
|
|
218
398
|
});
|
|
219
399
|
}
|
|
220
400
|
|
|
221
|
-
export { type CheckoutErrorCode, type CheckoutIntent, type CheckoutItem, type CheckoutLocale, type CheckoutOpenOptions, type CheckoutPresentation, type CheckoutProvider, type CheckoutResult, type CheckoutResultReason, type CheckoutResultStatus, type CheckoutSession, type CheckoutWallet, EsaalatCheckout, EsaalatCheckoutError, type EsaalatCheckoutOptions, type ExpectedReferenceType, EsaalatCheckout as default };
|
|
401
|
+
export { type CheckoutBeforeCreateOrderHandler, type CheckoutCreatePayload, type CheckoutErrorCode, type CheckoutIntent, type CheckoutItem, type CheckoutLocale, type CheckoutOpenOptions, type CheckoutPresentation, type CheckoutProvider, type CheckoutResult, type CheckoutResultReason, type CheckoutResultStatus, type CheckoutSession, type CheckoutWallet, ESAALAT_CHECKOUT_BUTTON_TAG, ESAALAT_CHECKOUT_EVENTS, EsaalatCheckout, EsaalatCheckoutButtonElement, EsaalatCheckoutError, type EsaalatCheckoutOptions, type ExpectedReferenceType, EsaalatCheckout as default, registerEsaalatCheckoutButton };
|