@maytes/checkout-button 1.0.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.
@@ -0,0 +1,806 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ Maytes: () => Maytes,
24
+ MaytesError: () => MaytesError,
25
+ MaytesErrorCode: () => MaytesErrorCode,
26
+ SDK_VERSION: () => SDK_VERSION
27
+ });
28
+ module.exports = __toCommonJS(src_exports);
29
+
30
+ // src/foundation/brand.generated.ts
31
+ var foundation = {
32
+ brand: {
33
+ primary: "#4A0324",
34
+ secondary: "#FE572A"
35
+ },
36
+ palette: {
37
+ burgundy: {
38
+ 700: "#3B021D"
39
+ },
40
+ base: {
41
+ white: "#FFFFFF"
42
+ }
43
+ },
44
+ text: {
45
+ primaryInverse: "#FFFFFF"
46
+ }
47
+ };
48
+
49
+ // src/branding.ts
50
+ var MAYTES_RICHE = foundation.brand.primary;
51
+ var MAYTES_WALLET = foundation.brand.secondary;
52
+ var SVG_NS = "http://www.w3.org/2000/svg";
53
+ var MAYTES_LOGO_PATHS = [
54
+ "M392,6.11c-.3-1.61-1.72-2.79-3.38-2.79h-99.97c-1.66,0-3.09,1.19-3.38,2.79l-51.14,275.49c-.36,2.07,1.24,4.03,3.38,4.03h71.07c1.72,0,3.15-1.24,3.38-2.97l5.57-42.24c.23-1.72,1.66-2.97,3.38-2.97h32.45c1.72,0,3.15,1.24,3.38,2.97l5.57,42.24c.23,1.72,1.66,2.97,3.38,2.97h74.46c2.14,0,3.8-1.96,3.38-4.03L392,6.11ZM344.42,174.89h-14.59c-2.07,0-3.62-1.84-3.38-3.86l9.55-74.33c.06-.48.48-.83.95-.83s.89.35.95.83l9.91,74.33c.3,2.02-1.31,3.86-3.38,3.86h0Z",
55
+ "M218.86,3.32h-77.72c-1.48,0-2.85,1.01-3.27,2.44l-25.63,83.65c-.12.35-.41.6-.83.6s-.71-.23-.83-.6L84.9,5.76c-.41-1.42-1.78-2.44-3.27-2.44H3.44c-1.9,0-3.44,1.54-3.44,3.44v275.43c0,1.9,1.54,3.45,3.44,3.45h71.26c1.9,0,3.44-1.54,3.44-3.45v-77.3l-.83-51.44c0-.53.41-.95.95-.95.41,0,.77.3.89.71l14.42,59.62c.35,1.54,1.72,2.61,3.32,2.61h28.42c1.61,0,2.97-1.06,3.32-2.61l14.47-59.79c.12-.3.35-.53.71-.53.41,0,.71.3.71.71l-.42,51.67-.06.06v77.3c0,1.9,1.54,3.44,3.44,3.44h71.37c1.9,0,3.44-1.54,3.44-3.44V6.77c0-1.9-1.54-3.44-3.44-3.44h0Z",
56
+ "M1179.6,147c-9.73-11.8-20.71-21.12-47.76-38.44-27.06-16.91-31.69-23.26-31.69-31.68,0-6.76,3.8-11.39,10.56-11.39,8.19,0,14.18,10.44,15.43,26.28.18,2.14,2.13,3.74,4.27,3.26l63.66-14.12c1.6-.36,2.73-1.9,2.61-3.56-3.97-49.3-35-77.36-86.44-77.36-27.47,0-50.25,8.07-67.63,24.09-17.39,16.01-25.82,36.72-25.82,62.53,0,16.91,4.22,32.09,13.12,45.62,8.9,13.53,25.75,28.3,51.56,44.38,14.77,9.32,24.09,16.49,27.88,21.12,4.21,4.63,6.35,9.31,6.35,14.36,0,6.35-4.63,10.56-11.81,10.56-10.5,0-18.15-11.87-19.28-29.66-.12-2.25-2.31-3.91-4.51-3.26l-63.6,18.68c-1.55.42-2.55,1.84-2.43,3.44,1.78,22.84,11.03,41.41,26.88,55.65,16.49,14.36,36.77,21.53,64.24,21.53s50.32-7.59,67.64-23.26c17.74-15.66,26.64-36.37,26.64-61.28,0-22.01-6.35-40.58-19.87-57.49v-.02Z",
57
+ "M1002.33,3.32h-146.18c-1.9,0-3.44,1.54-3.44,3.44v275.49c0,1.9,1.54,3.44,3.44,3.44h146.18c1.9,0,3.44-1.54,3.44-3.44v-62.05c0-1.9-1.54-3.44-3.44-3.44h-63.3c-1.9,0-3.45-1.54-3.45-3.45v-34.58c0-1.9,1.54-3.44,3.45-3.44v.18h53.63c1.9,0,3.45-1.54,3.45-3.44v-57.84c0-1.9-1.55-3.44-3.45-3.44h-53.63c-1.9,0-3.45-1.54-3.45-3.44v-31.61c0-1.9,1.54-3.44,3.45-3.44h63.3c1.9,0,3.44-1.54,3.44-3.44V6.77c0-1.9-1.54-3.44-3.44-3.44Z",
58
+ "M840.22,6.77c0-1.9-1.54-3.44-3.44-3.44h-175.31c-1.9,0-3.45,1.54-3.45,3.44v62c0,1.9,1.54,3.44,3.45,3.44h41.77c1.9,0,3.44,1.54,3.44,3.44v206.62c0,1.9,1.54,3.45,3.44,3.45h78.14c1.9,0,3.45-1.54,3.45-3.45V75.7c0-1.9,1.54-3.44,3.45-3.44h41.77c1.9,0,3.44-1.54,3.44-3.44h-.12V6.77h-.02Z",
59
+ "M607.71,3.38l-.06-.06c-23.23,0-32.59,48.08-40.22,80.25-1.38,5.82-4.74,18.31-7.75,27.31-4.44,13.26-10.68,30.43-22.21,31.93-18.85,2.47-7.14-39.43-7.14-39.43l21.67-92.98c.84-3.6-1.92-7.02-5.58-7.02h-85.49c-3.12,0-5.64,2.46-5.71,5.64l-1.68,75.09c-.48,21.25,5.64,42.31,18.37,59.37,12.72,17.05,38.81,28,63.12,25.9,52.48-7.08,65.46-70.49,68.4-71.1,3.06-.64,2.56,9.3.84,22.15-2.2,16.42-8.46,32.1-18.52,45.27-5.3,6.95-11.13,12.9-16.55,16.37-32.96,22.51-95.69,9.48-122.4-2.7-3.66-1.68-7.75,1.02-7.75,5.04l16.39,83.98c0,.84.18,1.68.54,2.4,9.48,17.83,53.31,19.33,74.43,17.77,32.18-2.04,100.37-29.35,115.32-122.33,12.17-103.08-1.14-162.91-38.06-162.84l.02-.02Z"
60
+ ];
61
+ function buildMaytesLogo(height, color) {
62
+ const svg = document.createElementNS(SVG_NS, "svg");
63
+ svg.setAttribute("xmlns", SVG_NS);
64
+ svg.setAttribute("viewBox", "0 0 1199.47 289.04");
65
+ svg.style.height = height;
66
+ svg.style.width = "auto";
67
+ svg.style.color = color;
68
+ for (const d of MAYTES_LOGO_PATHS) {
69
+ const path = document.createElementNS(SVG_NS, "path");
70
+ path.setAttribute("fill", "currentColor");
71
+ path.setAttribute("d", d);
72
+ svg.appendChild(path);
73
+ }
74
+ return svg;
75
+ }
76
+
77
+ // src/errors.ts
78
+ var MaytesErrorCode = {
79
+ Config: "CONFIG"
80
+ };
81
+ var MaytesError = class extends Error {
82
+ constructor(code, message) {
83
+ super(message);
84
+ this.code = code;
85
+ this.name = "MaytesError";
86
+ }
87
+ };
88
+
89
+ // src/overlay.ts
90
+ var TITLE_ID = "maytes-overlay-title";
91
+ var STYLE_MARKER = "data-maytes-checkout-button-overlay-styles";
92
+ var NATIVE_BACKDROP_FALLBACK = "rgba(0, 0, 0, 0.6)";
93
+ function tryReadTopDocument() {
94
+ if (window.top === null || window.top === window) return null;
95
+ try {
96
+ const topDoc = window.top.document;
97
+ return topDoc.body !== null ? topDoc : null;
98
+ } catch (err) {
99
+ if (!(err instanceof DOMException && err.name === "SecurityError")) {
100
+ console.warn("[maytes/checkout-button] unexpected error resolving overlay host", err);
101
+ }
102
+ return null;
103
+ }
104
+ }
105
+ function resolveOverlayHost() {
106
+ return tryReadTopDocument() ?? document;
107
+ }
108
+ function ensureOverlayStylesInTopDocument(host) {
109
+ if (host === document) return;
110
+ if (host.querySelector(`style[${STYLE_MARKER}]`) !== null) return;
111
+ const localStyle = document.head.querySelector("style[data-maytes-checkout-button]");
112
+ if (localStyle === null) return;
113
+ const copy = host.createElement("style");
114
+ copy.setAttribute(STYLE_MARKER, "");
115
+ const nonce = localStyle.getAttribute("nonce");
116
+ if (nonce !== null) copy.setAttribute("nonce", nonce);
117
+ copy.textContent = localStyle.textContent ?? "";
118
+ host.head.appendChild(copy);
119
+ }
120
+ function buildOverlay(host, state) {
121
+ const dialog = host.createElement("dialog");
122
+ dialog.className = "maytes-checkout-overlay";
123
+ dialog.setAttribute("aria-labelledby", TITLE_ID);
124
+ dialog.setAttribute("data-maytes-overlay", "");
125
+ const content = host.createElement("div");
126
+ content.className = "maytes-checkout-overlay__content";
127
+ const logoDiv = host.createElement("div");
128
+ logoDiv.setAttribute("aria-hidden", "true");
129
+ logoDiv.appendChild(buildMaytesLogo("2.5rem", MAYTES_WALLET));
130
+ content.appendChild(logoDiv);
131
+ const spinner = host.createElement("div");
132
+ spinner.className = "maytes-checkout-overlay__spinner";
133
+ spinner.setAttribute("role", "status");
134
+ spinner.setAttribute("aria-live", "polite");
135
+ spinner.setAttribute("aria-label", "Checkout in progress");
136
+ content.appendChild(spinner);
137
+ const text = host.createElement("p");
138
+ text.id = TITLE_ID;
139
+ text.className = "maytes-checkout-overlay__text";
140
+ text.textContent = "Completing checkout with Maytes\u2026";
141
+ content.appendChild(text);
142
+ const link = host.createElement("button");
143
+ link.className = "maytes-checkout-overlay__link";
144
+ link.type = "button";
145
+ link.textContent = "Return to Maytes";
146
+ link.addEventListener("click", () => {
147
+ if (state.popupWindow !== null && !state.popupWindow.closed) {
148
+ try {
149
+ state.popupWindow.focus();
150
+ } catch (err) {
151
+ if (!(err instanceof DOMException && err.name === "SecurityError")) throw err;
152
+ }
153
+ }
154
+ });
155
+ content.appendChild(link);
156
+ dialog.appendChild(content);
157
+ dialog.addEventListener("cancel", (event) => {
158
+ event.preventDefault();
159
+ if (state.popupWindow !== null && !state.popupWindow.closed) state.popupWindow.close();
160
+ hideOverlay(state);
161
+ });
162
+ return dialog;
163
+ }
164
+ function applyNativeBackdropFallback(dialog) {
165
+ dialog.style.background = NATIVE_BACKDROP_FALLBACK;
166
+ }
167
+ function showOverlay(state) {
168
+ if (state.overlayEl !== null) return;
169
+ const host = resolveOverlayHost();
170
+ ensureOverlayStylesInTopDocument(host);
171
+ const dialog = buildOverlay(host, state);
172
+ state.overlayEl = dialog;
173
+ host.body.appendChild(dialog);
174
+ if (typeof dialog.showModal === "function") {
175
+ try {
176
+ dialog.showModal();
177
+ } catch (err) {
178
+ console.warn("[maytes/checkout-button] dialog.showModal failed; falling back to inline backdrop", err);
179
+ dialog.setAttribute("open", "");
180
+ applyNativeBackdropFallback(dialog);
181
+ }
182
+ } else {
183
+ dialog.setAttribute("open", "");
184
+ applyNativeBackdropFallback(dialog);
185
+ }
186
+ }
187
+ function hideOverlay(state) {
188
+ if (state.overlayEl === null) return;
189
+ const dialog = state.overlayEl;
190
+ const host = dialog.ownerDocument;
191
+ if (typeof dialog.close === "function" && dialog.open) dialog.close();
192
+ dialog.remove();
193
+ if (host !== document) {
194
+ host.head.querySelector(`style[${STYLE_MARKER}]`)?.remove();
195
+ }
196
+ state.overlayEl = null;
197
+ }
198
+
199
+ // src/env.ts
200
+ var ENVIRONMENT_BASE_URLS = {
201
+ staging: "https://staging-checkout.maytes.co",
202
+ sandbox: "https://sandbox-checkout.maytes.co",
203
+ production: "https://checkout.maytes.co"
204
+ };
205
+ function isValidEnvironment(value) {
206
+ return value === "sandbox" || value === "production" || value === "staging";
207
+ }
208
+ function resolveBaseUrl(environment, override) {
209
+ if (override !== void 0) return override;
210
+ const url = ENVIRONMENT_BASE_URLS[environment];
211
+ if (url === void 0) {
212
+ throw new MaytesError(MaytesErrorCode.Config, `Unknown environment: ${String(environment)}`);
213
+ }
214
+ return url;
215
+ }
216
+
217
+ // src/redirect.ts
218
+ function isHttpUrl(value) {
219
+ let parsed;
220
+ try {
221
+ parsed = new URL(value);
222
+ } catch {
223
+ return false;
224
+ }
225
+ return parsed.protocol === "https:" || parsed.protocol === "http:";
226
+ }
227
+ function normalizeBaseUrl(rawBaseUrl) {
228
+ const candidate = rawBaseUrl.trim().replace(/\/+$/, "");
229
+ let parsed;
230
+ try {
231
+ parsed = new URL(candidate);
232
+ } catch {
233
+ throw new MaytesError(MaytesErrorCode.Config, `baseUrl is not a valid URL: ${candidate}`);
234
+ }
235
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
236
+ throw new MaytesError(
237
+ MaytesErrorCode.Config,
238
+ `baseUrl must use http or https; got ${parsed.protocol}`
239
+ );
240
+ }
241
+ return candidate;
242
+ }
243
+ function requireCheckoutId(id) {
244
+ if (typeof id !== "string" || id.trim().length === 0) {
245
+ throw new MaytesError(MaytesErrorCode.Config, "checkoutId is required (non-empty string)");
246
+ }
247
+ return id.trim();
248
+ }
249
+ function buildCheckoutUrl(environment, baseUrlOverride, checkoutId) {
250
+ const id = requireCheckoutId(checkoutId);
251
+ const base = normalizeBaseUrl(resolveBaseUrl(environment, baseUrlOverride));
252
+ return `${base}/?id=${encodeURIComponent(id)}`;
253
+ }
254
+ function performRedirect(url, replace) {
255
+ if (typeof window === "undefined") {
256
+ throw new MaytesError(
257
+ MaytesErrorCode.Config,
258
+ "redirectToCheckout requires a browser context (window is undefined)"
259
+ );
260
+ }
261
+ if (replace) {
262
+ window.location.replace(url);
263
+ } else {
264
+ window.location.href = url;
265
+ }
266
+ }
267
+ function makeRedirect(environment, baseUrlOverride) {
268
+ return {
269
+ checkoutUrl(opts) {
270
+ return buildCheckoutUrl(environment, baseUrlOverride, opts.checkoutId);
271
+ },
272
+ redirectToCheckout(opts) {
273
+ const url = buildCheckoutUrl(environment, baseUrlOverride, opts.checkoutId);
274
+ performRedirect(url, opts.replace === true);
275
+ }
276
+ };
277
+ }
278
+
279
+ // src/version.ts
280
+ var SDK_VERSION = "1.0.0";
281
+
282
+ // src/styles.ts
283
+ var STYLE_ATTR = "data-maytes-checkout-button";
284
+ var STYLE_SELECTOR = `style[${STYLE_ATTR}="${SDK_VERSION}"]`;
285
+ var REFCOUNT_KEY = `__maytes_checkout_button_styles_refs_${SDK_VERSION}__`;
286
+ var FOCUS_RING = "#6b8aff";
287
+ var BUTTON_CSS = `
288
+ .maytes-checkout-button {
289
+ display: inline-flex;
290
+ align-items: center;
291
+ justify-content: center;
292
+ gap: 0.57em;
293
+ padding: 0.71em 1.29em;
294
+ border: none;
295
+ border-radius: 999px;
296
+ background: ${foundation.brand.primary};
297
+ color: ${foundation.text.primaryInverse};
298
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
299
+ font-size: var(--maytes-button-font-size, 14px);
300
+ font-weight: 600;
301
+ line-height: 1;
302
+ cursor: pointer;
303
+ user-select: none;
304
+ transition: background-color 120ms ease, opacity 120ms ease;
305
+ }
306
+ .maytes-checkout-button:hover { background: ${foundation.palette.burgundy[700]}; }
307
+ .maytes-checkout-button:focus-visible {
308
+ outline: 2px solid ${FOCUS_RING};
309
+ outline-offset: 2px;
310
+ }
311
+ .maytes-checkout-button[aria-disabled='true'] {
312
+ cursor: progress;
313
+ opacity: 0.7;
314
+ }
315
+ .maytes-checkout-button--block {
316
+ display: flex;
317
+ width: 100%;
318
+ }
319
+ .maytes-checkout-button__logo { height: 1em; width: auto; flex: none; }
320
+ .maytes-checkout-button__spinner {
321
+ height: 1em;
322
+ width: 1em;
323
+ border-radius: 50%;
324
+ border: 0.14em solid currentColor;
325
+ border-right-color: transparent;
326
+ animation: maytes-checkout-button-spin 0.7s linear infinite;
327
+ }
328
+ @keyframes maytes-checkout-button-spin {
329
+ to { transform: rotate(360deg); }
330
+ }
331
+ .maytes-checkout-overlay {
332
+ position: fixed;
333
+ inset: 0;
334
+ width: 100vw;
335
+ height: 100vh;
336
+ max-width: 100vw;
337
+ max-height: 100vh;
338
+ margin: 0;
339
+ padding: 0;
340
+ border: none;
341
+ background: transparent;
342
+ z-index: 2147483646;
343
+ font: 500 15px/1.4 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
344
+ }
345
+ .maytes-checkout-overlay[open] {
346
+ display: flex;
347
+ align-items: center;
348
+ justify-content: center;
349
+ }
350
+ .maytes-checkout-overlay::backdrop {
351
+ background: rgba(0, 0, 0, 0.6);
352
+ backdrop-filter: blur(4px);
353
+ -webkit-backdrop-filter: blur(4px);
354
+ }
355
+ .maytes-checkout-overlay__content {
356
+ display: flex;
357
+ flex-direction: column;
358
+ align-items: center;
359
+ gap: 1.5rem;
360
+ }
361
+ .maytes-checkout-overlay__spinner {
362
+ width: 2.5rem;
363
+ height: 2.5rem;
364
+ border: 4px solid rgba(255, 255, 255, 0.3);
365
+ border-top-color: ${foundation.brand.secondary};
366
+ border-radius: 50%;
367
+ animation: maytes-checkout-button-spin 0.8s linear infinite;
368
+ }
369
+ .maytes-checkout-overlay__text {
370
+ color: ${foundation.text.primaryInverse};
371
+ font-size: 1.125rem;
372
+ font-weight: 500;
373
+ margin: 0;
374
+ }
375
+ .maytes-checkout-overlay__link {
376
+ color: rgba(255, 255, 255, 0.7);
377
+ font-size: 0.875rem;
378
+ text-decoration: underline;
379
+ text-underline-offset: 4px;
380
+ background: none;
381
+ border: none;
382
+ cursor: pointer;
383
+ font-family: inherit;
384
+ transition: color 0.15s;
385
+ }
386
+ .maytes-checkout-overlay__link:hover { color: ${foundation.text.primaryInverse}; }
387
+ @media (prefers-reduced-motion: reduce) {
388
+ .maytes-checkout-overlay__spinner,
389
+ .maytes-checkout-button__spinner { animation: none; }
390
+ }
391
+ `;
392
+ var POPUP_LOADING_CSS = `
393
+ :root { color-scheme: light; }
394
+ html, body { margin: 0; height: 100%; }
395
+ body.maytes-popup-loading {
396
+ display: flex;
397
+ align-items: center;
398
+ justify-content: center;
399
+ min-height: 100vh;
400
+ background: ${foundation.brand.primary};
401
+ color: ${foundation.text.primaryInverse};
402
+ font: 500 16px/1.45 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
403
+ }
404
+ .maytes-popup-loading__content {
405
+ display: flex;
406
+ flex-direction: column;
407
+ align-items: center;
408
+ gap: 1.5rem;
409
+ padding: 2rem;
410
+ text-align: center;
411
+ }
412
+ .maytes-popup-loading__logo { height: 2.75rem; width: auto; }
413
+ .maytes-popup-loading__spinner {
414
+ width: 2.5rem;
415
+ height: 2.5rem;
416
+ border: 4px solid rgba(255, 255, 255, 0.25);
417
+ border-top-color: ${foundation.brand.secondary};
418
+ border-radius: 50%;
419
+ animation: maytes-checkout-button-spin 0.8s linear infinite;
420
+ }
421
+ @keyframes maytes-checkout-button-spin {
422
+ to { transform: rotate(360deg); }
423
+ }
424
+ .maytes-popup-loading__text { margin: 0; font-size: 1.05rem; font-weight: 500; }
425
+ .maytes-popup-loading__slow {
426
+ margin: 0;
427
+ font-size: 0.9rem;
428
+ font-weight: 400;
429
+ color: rgba(255, 255, 255, 0.75);
430
+ max-width: 22rem;
431
+ }
432
+ .maytes-popup-loading__slow[hidden] { display: none; }
433
+ @media (prefers-reduced-motion: reduce) {
434
+ .maytes-popup-loading__spinner { animation: none; }
435
+ }
436
+ `;
437
+ function getRefCount() {
438
+ const w = window;
439
+ return w[REFCOUNT_KEY] ?? 0;
440
+ }
441
+ function setRefCount(value) {
442
+ const w = window;
443
+ w[REFCOUNT_KEY] = value;
444
+ }
445
+ function ensureStylesInjected(cspNonce) {
446
+ const next = getRefCount() + 1;
447
+ setRefCount(next);
448
+ if (document.head.querySelector(STYLE_SELECTOR) !== null) return;
449
+ const el = document.createElement("style");
450
+ el.setAttribute(STYLE_ATTR, SDK_VERSION);
451
+ if (cspNonce !== void 0 && cspNonce !== "") el.setAttribute("nonce", cspNonce);
452
+ el.textContent = BUTTON_CSS;
453
+ document.head.appendChild(el);
454
+ }
455
+ function releaseStyles() {
456
+ const next = Math.max(0, getRefCount() - 1);
457
+ setRefCount(next);
458
+ if (next > 0) return;
459
+ const el = document.head.querySelector(STYLE_SELECTOR);
460
+ if (el !== null) el.remove();
461
+ }
462
+
463
+ // src/button.ts
464
+ var POPUP_WIDTH = 500;
465
+ var POPUP_HEIGHT = 800;
466
+ var POPUP_POLL_INTERVAL_MS = 500;
467
+ var MOBILE_MAX_WIDTH = 600;
468
+ var SLOW_NETWORK_DELAY_MS = 8e3;
469
+ function buildLogo() {
470
+ const svg = buildMaytesLogo("1em", "currentColor");
471
+ svg.setAttribute("class", "maytes-checkout-button__logo");
472
+ svg.setAttribute("aria-hidden", "true");
473
+ return svg;
474
+ }
475
+ function buildSpinner() {
476
+ const spinner = document.createElement("span");
477
+ spinner.className = "maytes-checkout-button__spinner";
478
+ spinner.setAttribute("aria-hidden", "true");
479
+ return spinner;
480
+ }
481
+ function isValidCheckoutResult(value) {
482
+ if (typeof value !== "object" || value === null) return false;
483
+ const obj = value;
484
+ if (typeof obj.checkoutId !== "string" || obj.checkoutId.trim().length === 0) return false;
485
+ if (obj.checkoutUrl !== void 0) {
486
+ if (typeof obj.checkoutUrl !== "string" || obj.checkoutUrl.trim().length === 0) return false;
487
+ if (!isHttpUrl(obj.checkoutUrl)) return false;
488
+ }
489
+ return true;
490
+ }
491
+ function isSecurityError(err) {
492
+ return err instanceof DOMException && err.name === "SecurityError";
493
+ }
494
+ function refocusPopup(popup) {
495
+ try {
496
+ popup.focus();
497
+ } catch (err) {
498
+ if (!isSecurityError(err)) throw err;
499
+ }
500
+ }
501
+ function closePopupWindow(popup) {
502
+ if (popup === null) return;
503
+ try {
504
+ if (!popup.closed) popup.close();
505
+ } catch (err) {
506
+ if (!isSecurityError(err)) throw err;
507
+ }
508
+ }
509
+ function popupFeatures() {
510
+ const screenWidth = window.screen?.width ?? POPUP_WIDTH;
511
+ const screenHeight = window.screen?.height ?? POPUP_HEIGHT;
512
+ const left = Math.max(0, Math.round((screenWidth - POPUP_WIDTH) / 2));
513
+ const top = Math.max(0, Math.round((screenHeight - POPUP_HEIGHT) / 2));
514
+ return `popup=yes,width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},resizable=yes,scrollbars=yes`;
515
+ }
516
+ function stopPopupPoll(state) {
517
+ if (state.popupPollHandle !== null) {
518
+ clearInterval(state.popupPollHandle);
519
+ state.popupPollHandle = null;
520
+ }
521
+ }
522
+ function startPopupPoll(state, onClose) {
523
+ stopPopupPoll(state);
524
+ state.popupPollHandle = setInterval(() => {
525
+ const popup = state.popupWindow;
526
+ if (popup === null || popup.closed) {
527
+ stopPopupPoll(state);
528
+ state.popupWindow = null;
529
+ onClose();
530
+ }
531
+ }, POPUP_POLL_INTERVAL_MS);
532
+ }
533
+ function isMobileViewport() {
534
+ return window.innerWidth <= MOBILE_MAX_WIDTH;
535
+ }
536
+ function openBlankPopup(state) {
537
+ return window.open("about:blank", state.popupName, popupFeatures());
538
+ }
539
+ function navigatePopup(popup, url) {
540
+ popup.location.replace(url);
541
+ }
542
+ function removePopupChildren(doc) {
543
+ while (doc.body.firstChild !== null) doc.body.removeChild(doc.body.firstChild);
544
+ }
545
+ function paintPopupLoadingScreen(popup, cspNonce) {
546
+ const doc = popup.document;
547
+ if (doc === null || doc.head === null || doc.body === null) return;
548
+ doc.title = "Maytes checkout";
549
+ doc.documentElement.lang = "en";
550
+ const style = doc.createElement("style");
551
+ if (cspNonce !== void 0 && cspNonce !== "") style.setAttribute("nonce", cspNonce);
552
+ style.textContent = POPUP_LOADING_CSS;
553
+ doc.head.appendChild(style);
554
+ doc.body.classList.add("maytes-popup-loading");
555
+ removePopupChildren(doc);
556
+ const content = doc.createElement("main");
557
+ content.className = "maytes-popup-loading__content";
558
+ content.setAttribute("role", "status");
559
+ content.setAttribute("aria-live", "polite");
560
+ const logo = buildMaytesLogo("2.75rem", foundation.text.primaryInverse);
561
+ logo.setAttribute("class", "maytes-popup-loading__logo");
562
+ logo.setAttribute("aria-hidden", "true");
563
+ content.appendChild(logo);
564
+ const spinner = doc.createElement("div");
565
+ spinner.className = "maytes-popup-loading__spinner";
566
+ spinner.setAttribute("aria-hidden", "true");
567
+ content.appendChild(spinner);
568
+ const text = doc.createElement("p");
569
+ text.className = "maytes-popup-loading__text";
570
+ text.textContent = "Completing checkout with Maytes\u2026";
571
+ content.appendChild(text);
572
+ const slow = doc.createElement("p");
573
+ slow.className = "maytes-popup-loading__slow";
574
+ slow.hidden = true;
575
+ slow.textContent = "Still connecting. This can take a moment on slow networks.";
576
+ content.appendChild(slow);
577
+ doc.body.appendChild(content);
578
+ setTimeout(() => {
579
+ slow.hidden = false;
580
+ }, SLOW_NETWORK_DELAY_MS);
581
+ }
582
+ function renderButton(state, container, options = {}) {
583
+ if (state.destroyed) {
584
+ throw new MaytesError(
585
+ MaytesErrorCode.Config,
586
+ "renderButton() called on a destroyed Maytes instance"
587
+ );
588
+ }
589
+ if (!(container instanceof HTMLElement)) {
590
+ throw new MaytesError(MaytesErrorCode.Config, "renderButton(container) requires an HTMLElement");
591
+ }
592
+ ensureStylesInjected(state.config.cspNonce);
593
+ const label = options.label ?? "Split with";
594
+ const block = options.block === true;
595
+ const mode = options.mode ?? "redirect";
596
+ if (mode !== "redirect" && mode !== "popup") {
597
+ throw new MaytesError(
598
+ MaytesErrorCode.Config,
599
+ "renderButton({ mode }) must be 'redirect' or 'popup'"
600
+ );
601
+ }
602
+ const button = document.createElement("button");
603
+ button.type = "button";
604
+ button.className = block ? "maytes-checkout-button maytes-checkout-button--block" : "maytes-checkout-button";
605
+ button.setAttribute("aria-label", `${label} Maytes`);
606
+ const labelNode = document.createTextNode(`${label} `);
607
+ const logo = buildLogo();
608
+ button.appendChild(labelNode);
609
+ button.appendChild(logo);
610
+ const setBusy = (busy) => {
611
+ state.busy = busy;
612
+ if (busy) {
613
+ button.setAttribute("aria-disabled", "true");
614
+ button.setAttribute("aria-busy", "true");
615
+ logo.replaceWith(buildSpinner());
616
+ } else {
617
+ button.removeAttribute("aria-disabled");
618
+ button.removeAttribute("aria-busy");
619
+ const currentIndicator = button.querySelector(".maytes-checkout-button__spinner, .maytes-checkout-button__logo");
620
+ if (currentIndicator !== null) currentIndicator.replaceWith(buildLogo());
621
+ }
622
+ };
623
+ const teardownActiveCheckout = () => {
624
+ hideOverlay(state);
625
+ setBusy(false);
626
+ };
627
+ const onPopupClosed = () => {
628
+ teardownActiveCheckout();
629
+ document.dispatchEvent(new CustomEvent("maytes:checkout-closed"));
630
+ };
631
+ const dispatchFailed = (reason, cause) => {
632
+ document.dispatchEvent(new CustomEvent("maytes:checkout-failed", {
633
+ detail: cause === void 0 ? { reason } : { reason, cause }
634
+ }));
635
+ };
636
+ const dispatchRedirected = (url) => {
637
+ document.dispatchEvent(new CustomEvent("maytes:checkout-redirected", { detail: { url } }));
638
+ };
639
+ const redirectSameWindow = (url) => {
640
+ teardownActiveCheckout();
641
+ dispatchRedirected(url);
642
+ window.location.href = url;
643
+ };
644
+ const closeOrphanPopup = (popup) => {
645
+ closePopupWindow(popup);
646
+ stopPopupPoll(state);
647
+ if (state.popupWindow === popup) state.popupWindow = null;
648
+ teardownActiveCheckout();
649
+ };
650
+ const handleClick = async () => {
651
+ if (state.busy || state.destroyed) return;
652
+ setBusy(true);
653
+ const shouldUsePopup = mode === "popup" && !isMobileViewport();
654
+ const popup = shouldUsePopup ? openBlankPopup(state) : null;
655
+ if (popup !== null) {
656
+ state.popupWindow = popup;
657
+ refocusPopup(popup);
658
+ paintPopupLoadingScreen(popup, state.config.cspNonce);
659
+ showOverlay(state);
660
+ document.dispatchEvent(new CustomEvent("maytes:checkout-opened"));
661
+ startPopupPoll(state, onPopupClosed);
662
+ }
663
+ try {
664
+ const result = await state.config.createCheckout();
665
+ if (state.destroyed || popup !== null && popup.closed) {
666
+ closePopupWindow(popup);
667
+ return;
668
+ }
669
+ if (!isValidCheckoutResult(result)) {
670
+ closeOrphanPopup(popup);
671
+ const shapeError = new MaytesError(
672
+ MaytesErrorCode.Config,
673
+ "createCheckout must resolve to { checkoutId: string, checkoutUrl?: http(s) URL }"
674
+ );
675
+ console.error(shapeError);
676
+ dispatchFailed("invalid-shape", shapeError);
677
+ return;
678
+ }
679
+ const url = result.checkoutUrl ?? buildCheckoutUrl(
680
+ state.config.environment,
681
+ state.config.baseUrl,
682
+ result.checkoutId
683
+ );
684
+ if (popup !== null) {
685
+ navigatePopup(popup, url);
686
+ } else {
687
+ if (shouldUsePopup) {
688
+ console.warn("[maytes/checkout-button] popup was blocked; falling back to same-window redirect");
689
+ }
690
+ redirectSameWindow(url);
691
+ }
692
+ } catch (err) {
693
+ if (popup !== null && popup.closed) return;
694
+ closeOrphanPopup(popup);
695
+ console.error("[maytes/checkout-button] createCheckout failed:", err);
696
+ dispatchFailed("create-checkout-rejected", err);
697
+ }
698
+ };
699
+ button.addEventListener("click", handleClick);
700
+ container.appendChild(button);
701
+ let cleaned = false;
702
+ const teardown = () => {
703
+ if (cleaned) return;
704
+ cleaned = true;
705
+ button.removeEventListener("click", handleClick);
706
+ button.remove();
707
+ state.teardowns.delete(teardown);
708
+ releaseStyles();
709
+ };
710
+ state.teardowns.add(teardown);
711
+ return teardown;
712
+ }
713
+
714
+ // src/state.ts
715
+ function popupNameSuffix() {
716
+ return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
717
+ }
718
+ function generatePopupName() {
719
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
720
+ try {
721
+ return `maytes-checkout-${crypto.randomUUID()}`;
722
+ } catch {
723
+ return `maytes-checkout-${popupNameSuffix()}`;
724
+ }
725
+ }
726
+ return `maytes-checkout-${popupNameSuffix()}`;
727
+ }
728
+ function createInstanceState(options, internal) {
729
+ const config = {
730
+ createCheckout: options.createCheckout,
731
+ environment: options.environment
732
+ };
733
+ if (internal?.baseUrl !== void 0) config.baseUrl = internal.baseUrl;
734
+ if (internal?.cspNonce !== void 0) config.cspNonce = internal.cspNonce;
735
+ return {
736
+ config,
737
+ busy: false,
738
+ overlayEl: null,
739
+ popupWindow: null,
740
+ popupName: generatePopupName(),
741
+ popupPollHandle: null,
742
+ destroyed: false,
743
+ teardowns: /* @__PURE__ */ new Set()
744
+ };
745
+ }
746
+
747
+ // src/maytes.ts
748
+ function validateOptions(options) {
749
+ if (typeof options !== "object" || options === null) {
750
+ throw new MaytesError(MaytesErrorCode.Config, "Maytes() requires an options object");
751
+ }
752
+ if (typeof options.createCheckout !== "function") {
753
+ throw new MaytesError(
754
+ MaytesErrorCode.Config,
755
+ "Maytes({ createCheckout }) must be an async function returning { checkoutId }"
756
+ );
757
+ }
758
+ if (!isValidEnvironment(options.environment)) {
759
+ throw new MaytesError(
760
+ MaytesErrorCode.Config,
761
+ "Maytes({ environment }) must be 'sandbox' or 'production'"
762
+ );
763
+ }
764
+ }
765
+ function validateInternalOptions(internal) {
766
+ if (internal === void 0) return;
767
+ if (internal.baseUrl !== void 0) normalizeBaseUrl(internal.baseUrl);
768
+ }
769
+ var Maytes = (options, internal) => {
770
+ validateOptions(options);
771
+ validateInternalOptions(internal);
772
+ const state = createInstanceState(options, internal);
773
+ const { checkoutUrl, redirectToCheckout } = makeRedirect(
774
+ state.config.environment,
775
+ state.config.baseUrl
776
+ );
777
+ const sdk = {
778
+ renderButton(container, opts) {
779
+ return renderButton(state, container, opts);
780
+ },
781
+ redirectToCheckout(opts) {
782
+ redirectToCheckout(opts);
783
+ },
784
+ checkoutUrl(opts) {
785
+ return checkoutUrl(opts);
786
+ },
787
+ destroy() {
788
+ if (state.destroyed) return;
789
+ state.destroyed = true;
790
+ stopPopupPoll(state);
791
+ hideOverlay(state);
792
+ closePopupWindow(state.popupWindow);
793
+ state.popupWindow = null;
794
+ state.busy = false;
795
+ for (const teardown of [...state.teardowns]) teardown();
796
+ state.teardowns.clear();
797
+ }
798
+ };
799
+ return sdk;
800
+ };
801
+
802
+ // src/index.ts
803
+ if (typeof window !== "undefined") {
804
+ window.Maytes = Maytes;
805
+ }
806
+ //# sourceMappingURL=checkout-button.cjs.map