@funnelfox/billing 0.5.7 → 0.5.8

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.
@@ -5,1923 +5,4 @@
5
5
  * @author Funnelfox
6
6
  * @license MIT
7
7
  */
8
- /* eslint-disable @typescript-eslint/no-explicit-any */
9
- /**
10
- * @fileoverview Lightweight event emitter for Funnefox SDK
11
- */
12
- class EventEmitter {
13
- constructor() {
14
- this._events = new Map();
15
- }
16
- on(eventName, handler) {
17
- if (typeof handler !== 'function') {
18
- throw new Error('Event handler must be a function');
19
- }
20
- if (!this._events.has(eventName)) {
21
- this._events.set(eventName, []);
22
- }
23
- this._events.get(eventName).push(handler);
24
- return this;
25
- }
26
- once(eventName, handler) {
27
- if (typeof handler !== 'function') {
28
- throw new Error('Event handler must be a function');
29
- }
30
- const onceWrapper = (...args) => {
31
- this.off(eventName, onceWrapper);
32
- handler.apply(this, args);
33
- };
34
- return this.on(eventName, onceWrapper);
35
- }
36
- off(eventName, handler = null) {
37
- if (!this._events.has(eventName)) {
38
- return this;
39
- }
40
- if (handler === null) {
41
- this._events.delete(eventName);
42
- return this;
43
- }
44
- const handlers = this._events.get(eventName);
45
- const index = handlers.indexOf(handler);
46
- if (index !== -1) {
47
- handlers.splice(index, 1);
48
- if (handlers.length === 0) {
49
- this._events.delete(eventName);
50
- }
51
- }
52
- return this;
53
- }
54
- emit(eventName, ...args) {
55
- if (!this._events.has(eventName)) {
56
- return false;
57
- }
58
- const handlers = this._events.get(eventName).slice();
59
- for (const handler of handlers) {
60
- try {
61
- handler.apply(this, args);
62
- }
63
- catch (error) {
64
- // eslint-disable-next-line no-console
65
- console.warn(`Error in event handler for "${String(eventName)}":`, error);
66
- }
67
- }
68
- return true;
69
- }
70
- listenerCount(eventName) {
71
- return this._events.has(eventName)
72
- ? this._events.get(eventName).length
73
- : 0;
74
- }
75
- eventNames() {
76
- return Array.from(this._events.keys());
77
- }
78
- removeAllListeners() {
79
- this._events.clear();
80
- return this;
81
- }
82
- listeners(eventName) {
83
- return this._events.has(eventName)
84
- ? this._events.get(eventName).slice()
85
- : [];
86
- }
87
- }
88
-
89
- /**
90
- * @fileoverview Custom error classes for Funnefox SDK
91
- */
92
- class FunnefoxSDKError extends Error {
93
- constructor(message, code = ERROR_CODES.SDK_ERROR, details = null) {
94
- super(message);
95
- this.name = 'FunnefoxSDKError';
96
- this.code = code;
97
- this.details = details;
98
- if (Error.captureStackTrace) {
99
- Error.captureStackTrace(this, FunnefoxSDKError);
100
- }
101
- }
102
- }
103
- class ValidationError extends FunnefoxSDKError {
104
- constructor(field, message, value = null) {
105
- super(`Invalid ${field}: ${message}`, ERROR_CODES.VALIDATION_ERROR);
106
- this.name = 'ValidationError';
107
- this.field = field;
108
- this.value = value;
109
- }
110
- }
111
- class APIError extends FunnefoxSDKError {
112
- constructor(message, statusCode = null, options = {}) {
113
- super(message, options.errorCode || ERROR_CODES.API_ERROR);
114
- this.name = 'APIError';
115
- this.statusCode = statusCode;
116
- this.errorCode = options.errorCode || null;
117
- this.errorType = options.errorType || null;
118
- this.requestId = options.requestId || null;
119
- this.response = options.response || null;
120
- }
121
- }
122
- class PrimerError extends FunnefoxSDKError {
123
- constructor(message, primerError = null) {
124
- super(message, ERROR_CODES.PRIMER_ERROR);
125
- this.name = 'PrimerError';
126
- this.primerError = primerError;
127
- }
128
- }
129
- class CheckoutError extends FunnefoxSDKError {
130
- constructor(message, phase = null) {
131
- super(message, ERROR_CODES.CHECKOUT_ERROR);
132
- this.name = 'CheckoutError';
133
- this.phase = phase;
134
- }
135
- }
136
- class ConfigurationError extends FunnefoxSDKError {
137
- constructor(message) {
138
- super(message, ERROR_CODES.CONFIGURATION_ERROR);
139
- this.name = 'ConfigurationError';
140
- }
141
- }
142
- class NetworkError extends FunnefoxSDKError {
143
- constructor(message, originalError = null) {
144
- super(message, ERROR_CODES.NETWORK_ERROR);
145
- this.name = 'NetworkError';
146
- this.originalError = originalError;
147
- }
148
- }
149
-
150
- /**
151
- * @fileoverview Dynamic loader for Primer SDK
152
- * Loads Primer script and CSS from CDN independently of bundler
153
- */
154
- const PRIMER_CDN_BASE = 'https://sdk.primer.io/web';
155
- const DEFAULT_VERSION = '2.57.3';
156
- // Integrity hashes for specific versions (for SRI security)
157
- const INTEGRITY_HASHES = {
158
- '2.57.3': {
159
- js: 'sha384-xq2SWkYvTlKOMpuXQUXq1QI3eZN7JiqQ3Sc72U9wY1IE30MW3HkwQWg/1n6BTMz4',
160
- },
161
- };
162
- let loadingPromise = null;
163
- let isLoaded = false;
164
- /**
165
- * Injects a script tag into the document head
166
- */
167
- function injectScript$1(src, integrity) {
168
- return new Promise((resolve, reject) => {
169
- // Check if script already exists
170
- const existingScript = document.querySelector(`script[src="${src}"]`);
171
- if (existingScript) {
172
- resolve(existingScript);
173
- return;
174
- }
175
- const script = document.createElement('script');
176
- script.src = src;
177
- script.async = true;
178
- script.crossOrigin = 'anonymous';
179
- if (integrity) {
180
- script.integrity = integrity;
181
- }
182
- script.onload = () => resolve(script);
183
- script.onerror = () => reject(new Error(`Failed to load Primer SDK script from ${src}`));
184
- document.head.appendChild(script);
185
- });
186
- }
187
- /**
188
- * Injects a CSS link tag into the document head
189
- */
190
- function injectCSS(href, integrity) {
191
- return new Promise((resolve, reject) => {
192
- // Check if stylesheet already exists
193
- const existingLink = document.querySelector(`link[href="${href}"]`);
194
- if (existingLink) {
195
- resolve(existingLink);
196
- return;
197
- }
198
- const link = document.createElement('link');
199
- link.rel = 'stylesheet';
200
- link.href = href;
201
- link.crossOrigin = 'anonymous';
202
- if (integrity) {
203
- link.integrity = integrity;
204
- }
205
- link.onload = () => resolve(link);
206
- link.onerror = () => reject(new Error(`Failed to load Primer SDK CSS from ${href}`));
207
- document.head.appendChild(link);
208
- });
209
- }
210
- /**
211
- * Waits for window.Primer to be available
212
- */
213
- function waitForPrimer(timeout = 10000) {
214
- return new Promise((resolve, reject) => {
215
- const startTime = Date.now();
216
- const check = () => {
217
- if (typeof window !== 'undefined' &&
218
- window.Primer &&
219
- typeof window.Primer.createHeadless === 'function') {
220
- resolve();
221
- return;
222
- }
223
- if (Date.now() - startTime > timeout) {
224
- reject(new Error('Timeout waiting for Primer SDK to initialize on window'));
225
- return;
226
- }
227
- setTimeout(check, 50);
228
- };
229
- check();
230
- });
231
- }
232
- /**
233
- * Loads the Primer SDK script and CSS from CDN
234
- * @param version - The version of Primer SDK to load (default: 2.57.3)
235
- * @returns Promise that resolves when SDK is loaded and ready
236
- */
237
- async function loadPrimerSDK(version) {
238
- // Already loaded
239
- if (isLoaded) {
240
- return;
241
- }
242
- // Already loading - return existing promise
243
- if (loadingPromise) {
244
- return loadingPromise;
245
- }
246
- // Check if Primer is already available (user may have loaded it manually)
247
- if (typeof window !== 'undefined' &&
248
- window.Primer &&
249
- typeof window.Primer.createHeadless === 'function') {
250
- isLoaded = true;
251
- return;
252
- }
253
- const ver = version || DEFAULT_VERSION;
254
- const jsUrl = `${PRIMER_CDN_BASE}/v${ver}/Primer.min.js`;
255
- const cssUrl = `${PRIMER_CDN_BASE}/v${ver}/Checkout.css`;
256
- const hashes = INTEGRITY_HASHES[ver];
257
- loadingPromise = (async () => {
258
- try {
259
- // Load CSS and JS in parallel
260
- await Promise.all([
261
- injectCSS(cssUrl, hashes?.css),
262
- injectScript$1(jsUrl, hashes?.js),
263
- ]);
264
- // Wait for Primer to be available on window
265
- await waitForPrimer();
266
- isLoaded = true;
267
- }
268
- catch (error) {
269
- loadingPromise = null;
270
- throw error;
271
- }
272
- })();
273
- return loadingPromise;
274
- }
275
-
276
- /* eslint-disable @typescript-eslint/no-explicit-any */
277
- /**
278
- * @fileoverview Helper utilities for Funnefox SDK
279
- */
280
- function merge(...objects) {
281
- const result = {};
282
- for (const obj of objects) {
283
- if (obj && typeof obj === 'object') {
284
- for (const key in obj) {
285
- if (Object.prototype.hasOwnProperty.call(obj, key)) {
286
- if (typeof obj[key] === 'object' &&
287
- !Array.isArray(obj[key]) &&
288
- obj[key] !== null) {
289
- result[key] = merge(result[key] || {}, obj[key]);
290
- }
291
- else {
292
- result[key] = obj[key];
293
- }
294
- }
295
- }
296
- }
297
- }
298
- return result;
299
- }
300
- function generateId(prefix = '') {
301
- const timestamp = Date.now().toString(36);
302
- const random = Math.random().toString(36).substr(2, 5);
303
- return `${prefix}${timestamp}_${random}`;
304
- }
305
- function sleep(ms) {
306
- return new Promise(resolve => setTimeout(resolve, ms));
307
- }
308
- async function retry(fn, maxAttempts = 3, baseDelay = 1000) {
309
- let lastError;
310
- for (let attempt = 1; attempt <= maxAttempts; attempt++) {
311
- try {
312
- return await fn();
313
- }
314
- catch (error) {
315
- lastError = error;
316
- if (attempt === maxAttempts)
317
- throw lastError;
318
- const delay = baseDelay * Math.pow(2, attempt - 1);
319
- await sleep(delay);
320
- }
321
- }
322
- throw lastError;
323
- }
324
- function withTimeout(promise, timeoutMs, message = 'Operation timed out') {
325
- const timeoutPromise = new Promise((_, reject) => {
326
- setTimeout(() => reject(new Error(message)), timeoutMs);
327
- });
328
- return Promise.race([promise, timeoutPromise]);
329
- }
330
-
331
- /**
332
- * @fileoverview Headless checkout cache manager
333
- */
334
- /**
335
- * Manages caching and sequential creation of Primer headless checkout instances.
336
- * Ensures that multiple checkouts with the same configuration reuse the same instance,
337
- * and that creations happen sequentially to avoid race conditions.
338
- */
339
- class HeadlessManager {
340
- constructor() {
341
- this.cache = new Map();
342
- this.queue = Promise.resolve();
343
- }
344
- /**
345
- * Generates a cache key from clientToken and serializable options
346
- */
347
- generateKey(clientToken, options) {
348
- const serializableOptions = {
349
- paymentHandling: options.paymentHandling,
350
- apiVersion: options.apiVersion,
351
- style: options.style,
352
- card: options.card,
353
- applePay: options.applePay,
354
- paypal: options.paypal,
355
- googlePay: options.googlePay,
356
- };
357
- return `${clientToken}:${JSON.stringify(serializableOptions)}`;
358
- }
359
- /**
360
- * Gets a cached headless instance or creates a new one.
361
- * Ensures sequential creation order to avoid race conditions.
362
- */
363
- getOrCreate(clientToken, options) {
364
- const key = this.generateKey(clientToken, options);
365
- // Return cached promise if exists
366
- const cached = this.cache.get(key);
367
- if (cached)
368
- return cached;
369
- // Create new headless in sequential order
370
- const previousQueue = this.queue;
371
- const promise = (async () => {
372
- await previousQueue; // Wait for previous creation
373
- const primerOptions = merge({
374
- paymentHandling: 'MANUAL',
375
- apiVersion: '2.4',
376
- }, options);
377
- try {
378
- const headlessResult = await window.Primer.createHeadless(clientToken, primerOptions);
379
- const headless = await headlessResult;
380
- await headless.start();
381
- return headless;
382
- }
383
- catch (error) {
384
- // Remove from cache on failure
385
- this.cache.delete(key);
386
- throw new PrimerError('Failed to create Primer headless checkout', error);
387
- }
388
- })();
389
- this.cache.set(key, promise);
390
- this.queue = promise.catch(() => { }); // Update queue, ignore errors
391
- return promise;
392
- }
393
- /**
394
- * Removes a headless instance from the cache
395
- */
396
- remove(headlessPromise) {
397
- for (const [key, value] of this.cache.entries()) {
398
- if (value === headlessPromise) {
399
- this.cache.delete(key);
400
- break;
401
- }
402
- }
403
- }
404
- /**
405
- * Clears all cached instances
406
- */
407
- clear() {
408
- this.cache.clear();
409
- }
410
- }
411
-
412
- var PaymentMethod;
413
- (function (PaymentMethod) {
414
- PaymentMethod["GOOGLE_PAY"] = "GOOGLE_PAY";
415
- PaymentMethod["APPLE_PAY"] = "APPLE_PAY";
416
- PaymentMethod["PAYPAL"] = "PAYPAL";
417
- PaymentMethod["PAYMENT_CARD"] = "PAYMENT_CARD";
418
- })(PaymentMethod || (PaymentMethod = {}));
419
-
420
- /**
421
- * @fileoverview Constants for Funnefox SDK
422
- */
423
- const SDK_VERSION = '0.5.7';
424
- const DEFAULTS = {
425
- BASE_URL: 'https://billing.funnelfox.com',
426
- REGION: 'default',
427
- SANDBOX: false,
428
- REQUEST_TIMEOUT: 30000,
429
- RETRY_ATTEMPTS: 3,
430
- RETRY_BASE_DELAY: 1000,
431
- };
432
- const CHECKOUT_STATES = {
433
- INITIALIZING: 'initializing',
434
- READY: 'ready',
435
- PROCESSING: 'processing',
436
- ACTION_REQUIRED: 'action_required',
437
- UPDATING: 'updating',
438
- COMPLETED: 'completed',
439
- ERROR: 'error',
440
- DESTROYED: 'destroyed',
441
- };
442
- const EVENTS = {
443
- SUCCESS: 'success',
444
- ERROR: 'error',
445
- STATUS_CHANGE: 'status-change',
446
- DESTROY: 'destroy',
447
- INPUT_ERROR: 'input-error',
448
- LOADER_CHANGE: 'loader-change',
449
- METHOD_RENDER: 'method-render',
450
- METHOD_RENDER_ERROR: 'method-render-error',
451
- START_PURCHASE: 'start-purchase',
452
- PURCHASE_FAILURE: 'purchase-failure',
453
- PURCHASE_COMPLETED: 'purchase-completed',
454
- PURCHASE_CANCELLED: 'purchase-cancelled',
455
- METHODS_AVAILABLE: 'methods-available',
456
- };
457
- const API_ENDPOINTS = {
458
- CREATE_CLIENT_SESSION: '/v1/checkout/create_client_session',
459
- UPDATE_CLIENT_SESSION: '/v1/checkout/update_client_session',
460
- CREATE_PAYMENT: '/v1/checkout/create_payment',
461
- RESUME_PAYMENT: '/v1/checkout/resume_payment',
462
- };
463
- const ERROR_CODES = {
464
- SDK_ERROR: 'SDK_ERROR',
465
- VALIDATION_ERROR: 'VALIDATION_ERROR',
466
- API_ERROR: 'API_ERROR',
467
- PRIMER_ERROR: 'PRIMER_ERROR',
468
- CHECKOUT_ERROR: 'CHECKOUT_ERROR',
469
- CONFIGURATION_ERROR: 'CONFIGURATION_ERROR',
470
- NETWORK_ERROR: 'NETWORK_ERROR',
471
- };
472
- const ALLOWED_BUTTON_PAYMENT_METHODS = [
473
- PaymentMethod.GOOGLE_PAY,
474
- PaymentMethod.APPLE_PAY,
475
- PaymentMethod.PAYPAL,
476
- ];
477
- const ALLOWED_CARD_PAYMENT_METHODS = [
478
- PaymentMethod.PAYMENT_CARD,
479
- ];
480
- const ALLOWED_PAYMENT_METHODS = [
481
- ...ALLOWED_BUTTON_PAYMENT_METHODS,
482
- ...ALLOWED_CARD_PAYMENT_METHODS,
483
- ];
484
- const inputStyle = {
485
- input: {
486
- error: {
487
- borderColor: 'rgb(227, 47, 65)',
488
- },
489
- base: {
490
- borderWidth: '1px',
491
- borderStyle: 'solid',
492
- borderColor: 'rgb(0 0 0 / 10%)',
493
- height: '36px',
494
- paddingHorizontal: 10,
495
- borderRadius: '6px',
496
- },
497
- },
498
- };
499
- ({
500
- paddingLeft: inputStyle.input.base.paddingHorizontal + 'px',
501
- paddingRight: inputStyle.input.base.paddingHorizontal + 'px'});
502
- const DEFAULT_PAYMENT_METHOD_ORDER = [
503
- PaymentMethod.APPLE_PAY,
504
- PaymentMethod.GOOGLE_PAY,
505
- PaymentMethod.PAYPAL,
506
- PaymentMethod.PAYMENT_CARD,
507
- ];
508
-
509
- /**
510
- * @fileoverview Primer SDK integration wrapper
511
- */
512
- class PrimerWrapper {
513
- constructor() {
514
- this.isInitialized = false;
515
- this.destroyCallbacks = [];
516
- this.currentHeadless = null;
517
- this.availableMethods = [];
518
- this.paymentMethodsInterfaces = [];
519
- }
520
- isPrimerAvailable() {
521
- return (typeof window !== 'undefined' &&
522
- window.Primer &&
523
- typeof window.Primer?.createHeadless === 'function');
524
- }
525
- /**
526
- * Loads Primer SDK if not already available
527
- * @param version - Optional version to load (uses default if not specified)
528
- */
529
- async ensurePrimerLoaded(version) {
530
- if (this.isPrimerAvailable()) {
531
- return;
532
- }
533
- try {
534
- await loadPrimerSDK(version);
535
- }
536
- catch (error) {
537
- throw new PrimerError('Failed to load Primer SDK', error);
538
- }
539
- }
540
- ensurePrimerAvailable() {
541
- if (!this.isPrimerAvailable()) {
542
- throw new PrimerError('Primer SDK not found. Please include the Primer SDK script before initializing FunnefoxSDK.');
543
- }
544
- }
545
- async createHeadlessCheckout(clientToken, options) {
546
- await this.ensurePrimerLoaded();
547
- this.currentHeadless = PrimerWrapper.headlessManager.getOrCreate(clientToken, options);
548
- return this.currentHeadless;
549
- }
550
- disableButtons(disabled) {
551
- if (!this.paymentMethodsInterfaces)
552
- return;
553
- for (const paymentMethodInterface of this.paymentMethodsInterfaces) {
554
- paymentMethodInterface.setDisabled(disabled);
555
- }
556
- }
557
- async renderButton(allowedPaymentMethod, { htmlNode, onMethodRenderError, onMethodRender, }) {
558
- let button;
559
- // Ensure Primer SDK is loaded
560
- await this.ensurePrimerLoaded();
561
- if (!this.currentHeadless) {
562
- throw new PrimerError('Headless checkout not found');
563
- }
564
- try {
565
- const headless = await this.currentHeadless;
566
- const pmManager = await headless.createPaymentMethodManager(allowedPaymentMethod);
567
- if (!pmManager) {
568
- throw new Error('Payment method manager is not available');
569
- }
570
- button = pmManager.createButton();
571
- await button.render(htmlNode, {});
572
- this.destroyCallbacks.push(() => button.clean());
573
- onMethodRender(allowedPaymentMethod);
574
- return {
575
- setDisabled: (disabled) => {
576
- button.setDisabled(disabled);
577
- },
578
- destroy: () => button.clean(),
579
- };
580
- }
581
- catch (error) {
582
- onMethodRenderError(allowedPaymentMethod);
583
- throw new PrimerError('Failed to initialize Primer checkout', error);
584
- }
585
- }
586
- async initMethod(method, htmlNode, options) {
587
- try {
588
- if (method === PaymentMethod.PAYMENT_CARD) {
589
- if (!options.cardElements ||
590
- !options.onSubmit ||
591
- !options.onInputChange) {
592
- throw new PrimerError('Card elements, onSubmit, and onInputChange are required for PAYMENT_CARD method');
593
- }
594
- const cardInterface = await this.renderCardCheckoutWithElements(options.cardElements, {
595
- onSubmit: options.onSubmit,
596
- onInputChange: options.onInputChange,
597
- onMethodRenderError: options.onMethodRenderError,
598
- onMethodRender: options.onMethodRender,
599
- });
600
- this.paymentMethodsInterfaces.push(cardInterface);
601
- return cardInterface;
602
- }
603
- else {
604
- const buttonInterface = await this.renderButton(method, {
605
- htmlNode,
606
- onMethodRenderError: options.onMethodRenderError,
607
- onMethodRender: options.onMethodRender,
608
- });
609
- this.paymentMethodsInterfaces.push(buttonInterface);
610
- return buttonInterface;
611
- }
612
- }
613
- catch (error) {
614
- throw new PrimerError('Failed to initialize Primer checkout', error);
615
- }
616
- }
617
- async renderCardCheckoutWithElements(elements, { onSubmit, onInputChange, onMethodRenderError, onMethodRender, }) {
618
- try {
619
- if (!this.currentHeadless) {
620
- throw new PrimerError('Headless checkout not found');
621
- }
622
- const headless = await this.currentHeadless;
623
- const pmManager = await headless.createPaymentMethodManager('PAYMENT_CARD');
624
- if (!pmManager) {
625
- throw new Error('Payment method manager is not available');
626
- }
627
- const { cardNumberInput, expiryInput, cvvInput } = pmManager.createHostedInputs();
628
- const validateForm = async () => {
629
- if (!pmManager)
630
- return false;
631
- const { valid, validationErrors } = await pmManager.validate();
632
- const cardHolderError = validationErrors.find(v => v.name === 'cardholderName');
633
- dispatchError('cardholderName', cardHolderError?.message || null);
634
- return valid;
635
- };
636
- const dispatchError = (inputName, error) => {
637
- onInputChange(inputName, error);
638
- };
639
- const onHostedInputChange = (name) => (event) => {
640
- const input = event;
641
- if (input.submitted) {
642
- dispatchError(name, input.error);
643
- }
644
- };
645
- const cardHolderOnChange = async (e) => {
646
- pmManager.setCardholderName(e.target.value);
647
- dispatchError('cardholderName', null);
648
- };
649
- elements.cardholderName?.addEventListener('input', cardHolderOnChange);
650
- cardNumberInput.addEventListener('change', onHostedInputChange('cardNumber'));
651
- expiryInput.addEventListener('change', onHostedInputChange('expiryDate'));
652
- cvvInput.addEventListener('change', onHostedInputChange('cvv'));
653
- const onSubmitHandler = async () => {
654
- if (!(await validateForm())) {
655
- return;
656
- }
657
- try {
658
- onSubmit(true);
659
- await pmManager.submit();
660
- }
661
- catch (error) {
662
- const primerError = new PrimerError('Failed to submit payment', error);
663
- throw primerError;
664
- }
665
- finally {
666
- onSubmit(false);
667
- }
668
- };
669
- elements.button?.addEventListener('click', onSubmitHandler);
670
- await Promise.all([
671
- cardNumberInput.render(elements.cardNumber, {
672
- placeholder: '1234 1234 1234 1234',
673
- ariaLabel: 'Card number',
674
- style: inputStyle,
675
- }),
676
- expiryInput.render(elements.expiryDate, {
677
- placeholder: 'MM/YY',
678
- ariaLabel: 'Expiry date',
679
- style: inputStyle,
680
- }),
681
- cvvInput.render(elements.cvv, {
682
- placeholder: '123',
683
- ariaLabel: 'CVV',
684
- style: inputStyle,
685
- }),
686
- ]);
687
- const onDestroy = () => {
688
- pmManager.removeHostedInputs();
689
- elements.cardholderName?.removeEventListener('change', cardHolderOnChange);
690
- elements.button?.removeEventListener('click', onSubmitHandler);
691
- };
692
- this.destroyCallbacks.push(onDestroy);
693
- onMethodRender(PaymentMethod.PAYMENT_CARD);
694
- return {
695
- setDisabled: (disabled) => {
696
- cardNumberInput.setDisabled(disabled);
697
- expiryInput.setDisabled(disabled);
698
- cvvInput.setDisabled(disabled);
699
- if (elements.button) {
700
- elements.button.disabled = disabled;
701
- }
702
- if (elements.cardholderName) {
703
- elements.cardholderName.disabled = disabled;
704
- }
705
- },
706
- submit: () => onSubmitHandler(),
707
- destroy: () => {
708
- this.destroyCallbacks = this.destroyCallbacks.filter(callback => callback !== onDestroy);
709
- onDestroy();
710
- },
711
- };
712
- }
713
- catch (error) {
714
- onMethodRenderError(PaymentMethod.PAYMENT_CARD);
715
- throw new PrimerError('Failed to initialize Primer checkout', error);
716
- }
717
- }
718
- async initializeHeadlessCheckout(clientToken, primerOptions) {
719
- await this.createHeadlessCheckout(clientToken, {
720
- ...primerOptions,
721
- onTokenizeSuccess: this.wrapTokenizeHandler(primerOptions.onTokenizeSuccess),
722
- onResumeSuccess: this.wrapResumeHandler(primerOptions.onResumeSuccess),
723
- onAvailablePaymentMethodsLoad: (items) => {
724
- let isApplePayAvailable = false;
725
- this.availableMethods = ALLOWED_PAYMENT_METHODS.filter(method => {
726
- return items.some((item) => {
727
- if (item.type === PaymentMethod.APPLE_PAY) {
728
- isApplePayAvailable = true;
729
- }
730
- return item.type === method;
731
- });
732
- });
733
- if (isApplePayAvailable) {
734
- this.availableMethods = this.availableMethods.filter(method => method !== PaymentMethod.GOOGLE_PAY);
735
- }
736
- if (this.availableMethods.length === 0) {
737
- throw new PrimerError('No allowed payment methods found');
738
- }
739
- },
740
- });
741
- }
742
- async renderCheckout(clientToken, checkoutOptions, checkoutRenderOptions) {
743
- const { cardElements, paymentButtonElements, container, onSubmit, onInputChange, onMethodRender, onMethodRenderError, onMethodsAvailable, } = checkoutRenderOptions;
744
- await this.initializeHeadlessCheckout(clientToken, checkoutOptions);
745
- onMethodsAvailable?.(this.availableMethods);
746
- await Promise.all(this.availableMethods.map(method => {
747
- if (method === PaymentMethod.PAYMENT_CARD) {
748
- // For card, use the main container
749
- return this.initMethod(method, container, {
750
- cardElements,
751
- onSubmit,
752
- onInputChange,
753
- onMethodRender,
754
- onMethodRenderError,
755
- });
756
- }
757
- else {
758
- const buttonElementsMap = {
759
- [PaymentMethod.PAYPAL]: paymentButtonElements.paypal,
760
- [PaymentMethod.GOOGLE_PAY]: paymentButtonElements.googlePay,
761
- [PaymentMethod.APPLE_PAY]: paymentButtonElements.applePay,
762
- };
763
- // For buttons, use the specific button container element
764
- const buttonElement = buttonElementsMap[method];
765
- return this.initMethod(method, buttonElement, {
766
- onMethodRender,
767
- onMethodRenderError,
768
- });
769
- }
770
- }));
771
- this.isInitialized = true;
772
- }
773
- wrapTokenizeHandler(handler) {
774
- return async (paymentMethodTokenData, primerHandler) => {
775
- try {
776
- await handler(paymentMethodTokenData, primerHandler);
777
- }
778
- catch (error) {
779
- // eslint-disable-next-line no-console
780
- console.error('Error in tokenize handler:', error);
781
- primerHandler.handleFailure('Payment processing failed. Please try again.');
782
- }
783
- };
784
- }
785
- wrapResumeHandler(handler) {
786
- return async (resumeTokenData, primerHandler) => {
787
- try {
788
- await handler(resumeTokenData, primerHandler);
789
- }
790
- catch (error) {
791
- // eslint-disable-next-line no-console
792
- console.error('Error in resume handler:', error);
793
- primerHandler.handleFailure('Payment processing failed. Please try again.');
794
- }
795
- };
796
- }
797
- async destroy() {
798
- if (this.currentHeadless) {
799
- PrimerWrapper.headlessManager.remove(this.currentHeadless);
800
- this.currentHeadless = null;
801
- }
802
- if (this.destroyCallbacks) {
803
- try {
804
- Promise.all(this.destroyCallbacks.map(destroy => destroy()));
805
- }
806
- catch (error) {
807
- // eslint-disable-next-line no-console
808
- console.warn('Error destroying Primer checkout:', error);
809
- }
810
- }
811
- this.destroyCallbacks = [];
812
- this.isInitialized = false;
813
- }
814
- createHandlers(handlers) {
815
- return {
816
- handleSuccess: () => {
817
- if (handlers.onSuccess)
818
- handlers.onSuccess();
819
- },
820
- handleFailure: (message) => {
821
- if (handlers.onError)
822
- handlers.onError(new Error(message));
823
- },
824
- continueWithNewClientToken: (newClientToken) => {
825
- if (handlers.onActionRequired)
826
- handlers.onActionRequired(newClientToken);
827
- },
828
- };
829
- }
830
- getCurrentCheckout() {
831
- return this.destroyCallbacks;
832
- }
833
- isActive() {
834
- return this.isInitialized && this.destroyCallbacks.length > 0;
835
- }
836
- validateContainer(selector) {
837
- const element = document.querySelector(selector);
838
- if (!element) {
839
- throw new PrimerError(`Checkout container not found: ${selector}`);
840
- }
841
- const computedStyle = window.getComputedStyle(element);
842
- if (computedStyle.display === 'none') {
843
- // eslint-disable-next-line no-console
844
- console.warn('Checkout container is hidden, this may cause display issues');
845
- }
846
- return element;
847
- }
848
- }
849
- PrimerWrapper.headlessManager = new HeadlessManager();
850
-
851
- /**
852
- * @fileoverview Input validation utilities for Funnefox SDK
853
- */
854
- function sanitizeString(input) {
855
- return input?.trim() || '';
856
- }
857
- function requireString(value, fieldName) {
858
- const sanitized = sanitizeString(value);
859
- if (sanitized.length === 0) {
860
- throw new ValidationError(fieldName, 'must be a non-empty string', value);
861
- }
862
- return true;
863
- }
864
-
865
- /**
866
- * @fileoverview API client for Funnefox backend integration
867
- */
868
- class APIClient {
869
- constructor(config) {
870
- this.baseUrl = config.baseUrl.replace(/\/$/, '');
871
- this.orgId = config.orgId;
872
- this.timeout = config.timeout || 30000;
873
- this.retryAttempts = config.retryAttempts || 3;
874
- }
875
- async request(endpoint, options = {}) {
876
- const url = `${this.baseUrl}/${this.orgId}${endpoint}`;
877
- const requestOptions = {
878
- method: 'GET',
879
- headers: {
880
- 'Content-Type': 'application/json',
881
- 'X-SDK-Version': SDK_VERSION,
882
- ...(options.headers || {}),
883
- },
884
- ...options,
885
- };
886
- try {
887
- return await retry(async () => {
888
- return await withTimeout(this._makeRequest(url, requestOptions), this.timeout, 'Request timed out');
889
- }, this.retryAttempts);
890
- }
891
- catch (error) {
892
- if (error instanceof Error && error.name === 'APIError') {
893
- throw error;
894
- }
895
- throw new NetworkError('Network request failed', error);
896
- }
897
- }
898
- async _makeRequest(url, options) {
899
- let response;
900
- try {
901
- response = await fetch(url, options);
902
- }
903
- catch (error) {
904
- if (error instanceof Error && error.name === 'NetworkError') {
905
- throw error;
906
- }
907
- throw new NetworkError('Network request failed', error);
908
- }
909
- let data;
910
- try {
911
- data = await response.json();
912
- }
913
- catch {
914
- throw new APIError('Invalid JSON response', response.status, {});
915
- }
916
- if (!response.ok) {
917
- const d = data;
918
- const message = d.error?.[0]?.msg || 'Failed to create payment';
919
- throw new APIError(message, response.status, {
920
- response: data,
921
- });
922
- }
923
- return data;
924
- }
925
- async createClientSession(params) {
926
- const payload = {
927
- region: params.region || 'default',
928
- integration_type: 'primer',
929
- pp_ident: params.priceId,
930
- external_id: params.externalId,
931
- email_address: params.email,
932
- client_metadata: params.clientMetadata || {},
933
- };
934
- if (params.countryCode !== undefined) {
935
- payload.country_code = params.countryCode;
936
- }
937
- return (await this.request(API_ENDPOINTS.CREATE_CLIENT_SESSION, {
938
- method: 'POST',
939
- body: JSON.stringify(payload),
940
- }));
941
- }
942
- async updateClientSession(params) {
943
- const payload = {
944
- order_id: params.orderId,
945
- client_token: params.clientToken,
946
- pp_ident: params.priceId,
947
- client_metadata: params.clientMetadata || {},
948
- };
949
- return await this.request(API_ENDPOINTS.UPDATE_CLIENT_SESSION, {
950
- method: 'POST',
951
- body: JSON.stringify(payload),
952
- });
953
- }
954
- async createPayment(params) {
955
- const payload = {
956
- order_id: params.orderId,
957
- payment_method_token: params.paymentMethodToken,
958
- client_metadata: params.clientMetadata || {},
959
- };
960
- return (await this.request(API_ENDPOINTS.CREATE_PAYMENT, {
961
- method: 'POST',
962
- body: JSON.stringify(payload),
963
- }));
964
- }
965
- async resumePayment(params) {
966
- const payload = {
967
- order_id: params.orderId,
968
- resume_token: params.resumeToken,
969
- };
970
- return (await this.request(API_ENDPOINTS.RESUME_PAYMENT, {
971
- method: 'POST',
972
- body: JSON.stringify(payload),
973
- }));
974
- }
975
- processSessionResponse(response) {
976
- if (response.status === 'error') {
977
- const firstError = response.error?.[0];
978
- const message = firstError?.msg || 'Session creation failed';
979
- throw new APIError(message, null, {
980
- errorCode: firstError?.code,
981
- errorType: firstError?.type,
982
- requestId: response.req_id,
983
- response,
984
- });
985
- }
986
- const data = response.data;
987
- return {
988
- type: 'session_created',
989
- orderId: data.order_id,
990
- clientToken: data.client_token,
991
- };
992
- }
993
- processPaymentResponse(response) {
994
- if (response.status === 'error') {
995
- const firstError = response.error?.[0];
996
- const message = firstError?.msg || 'Payment request failed';
997
- throw new APIError(message, null, {
998
- errorCode: firstError?.code,
999
- errorType: firstError?.type,
1000
- response,
1001
- });
1002
- }
1003
- const data = response.data;
1004
- if (data.action_required_token) {
1005
- return {
1006
- type: 'action_required',
1007
- orderId: data.order_id,
1008
- clientToken: data.action_required_token,
1009
- };
1010
- }
1011
- if (data.checkout_status) {
1012
- switch (data.checkout_status) {
1013
- case 'succeeded':
1014
- return {
1015
- type: 'success',
1016
- orderId: data.order_id,
1017
- status: 'succeeded',
1018
- };
1019
- case 'failed':
1020
- throw new APIError(data.failed_message_for_user || 'Payment failed', null, { response });
1021
- case 'cancelled':
1022
- throw new APIError('Payment was cancelled by user', null, {
1023
- response,
1024
- });
1025
- case 'processing':
1026
- return {
1027
- type: 'processing',
1028
- orderId: data.order_id,
1029
- status: 'processing',
1030
- };
1031
- default:
1032
- throw new APIError(`Unhandled checkout status: ${data.checkout_status}`, null, { response });
1033
- }
1034
- }
1035
- throw new APIError('Invalid payment response format', null, { response });
1036
- }
1037
- async oneClick(payload) {
1038
- return (await this.request(`/billing/${this.orgId}/v1/checkout/one_click`, {
1039
- method: 'POST',
1040
- body: JSON.stringify(payload),
1041
- }));
1042
- }
1043
- }
1044
-
1045
- var loaderHtml = "<div class=\"ff-sdk-loader-container\">\n <div class=\"ff-sdk-loader\"></div>\n</div>\n";
1046
-
1047
- if(typeof document!=="undefined")document.head.appendChild(document.createElement("style")).textContent=".ff-sdk-loader-container {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n background-color: rgba(255, 255, 255);\n z-index: 2;\n}\n\n.ff-sdk-loader {\n width: 24px;\n height: 24px;\n border: 4px solid #e32f41;\n border-top: 4px solid transparent;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n }";
1048
-
1049
- const loaderConfig = {
1050
- html: loaderHtml,
1051
- selectors: {
1052
- loaderContainer: '.ff-sdk-loader-container'},
1053
- };
1054
- const renderLoader = (container) => {
1055
- const loaderContainer = document.querySelector(container);
1056
- if (loaderContainer) {
1057
- loaderContainer.innerHTML = loaderConfig.html;
1058
- }
1059
- };
1060
- const hideLoader = () => {
1061
- const loaderContainer = document.querySelector(loaderConfig.selectors.loaderContainer);
1062
- if (loaderContainer) {
1063
- loaderContainer.remove();
1064
- }
1065
- };
1066
-
1067
- var V3_URL = 'https://js.stripe.com/v3';
1068
- var V3_URL_REGEX = /^https:\/\/js\.stripe\.com\/v3\/?(\?.*)?$/;
1069
- var EXISTING_SCRIPT_MESSAGE = 'loadStripe.setLoadParameters was called but an existing Stripe.js script already exists in the document; existing script parameters will be used';
1070
- var findScript = function findScript() {
1071
- var scripts = document.querySelectorAll("script[src^=\"".concat(V3_URL, "\"]"));
1072
-
1073
- for (var i = 0; i < scripts.length; i++) {
1074
- var script = scripts[i];
1075
-
1076
- if (!V3_URL_REGEX.test(script.src)) {
1077
- continue;
1078
- }
1079
-
1080
- return script;
1081
- }
1082
-
1083
- return null;
1084
- };
1085
-
1086
- var injectScript = function injectScript(params) {
1087
- var queryString = '';
1088
- var script = document.createElement('script');
1089
- script.src = "".concat(V3_URL).concat(queryString);
1090
- var headOrBody = document.head || document.body;
1091
-
1092
- if (!headOrBody) {
1093
- throw new Error('Expected document.body not to be null. Stripe.js requires a <body> element.');
1094
- }
1095
-
1096
- headOrBody.appendChild(script);
1097
- return script;
1098
- };
1099
-
1100
- var registerWrapper = function registerWrapper(stripe, startTime) {
1101
- if (!stripe || !stripe._registerWrapper) {
1102
- return;
1103
- }
1104
-
1105
- stripe._registerWrapper({
1106
- name: 'stripe-js',
1107
- version: "4.6.0",
1108
- startTime: startTime
1109
- });
1110
- };
1111
-
1112
- var stripePromise = null;
1113
- var onErrorListener = null;
1114
- var onLoadListener = null;
1115
-
1116
- var onError = function onError(reject) {
1117
- return function () {
1118
- reject(new Error('Failed to load Stripe.js'));
1119
- };
1120
- };
1121
-
1122
- var onLoad = function onLoad(resolve, reject) {
1123
- return function () {
1124
- if (window.Stripe) {
1125
- resolve(window.Stripe);
1126
- } else {
1127
- reject(new Error('Stripe.js not available'));
1128
- }
1129
- };
1130
- };
1131
-
1132
- var loadScript = function loadScript(params) {
1133
- // Ensure that we only attempt to load Stripe.js at most once
1134
- if (stripePromise !== null) {
1135
- return stripePromise;
1136
- }
1137
-
1138
- stripePromise = new Promise(function (resolve, reject) {
1139
- if (typeof window === 'undefined' || typeof document === 'undefined') {
1140
- // Resolve to null when imported server side. This makes the module
1141
- // safe to import in an isomorphic code base.
1142
- resolve(null);
1143
- return;
1144
- }
1145
-
1146
- if (window.Stripe) {
1147
- resolve(window.Stripe);
1148
- return;
1149
- }
1150
-
1151
- try {
1152
- var script = findScript();
1153
-
1154
- if (script && params) ; else if (!script) {
1155
- script = injectScript(params);
1156
- } else if (script && onLoadListener !== null && onErrorListener !== null) {
1157
- var _script$parentNode;
1158
-
1159
- // remove event listeners
1160
- script.removeEventListener('load', onLoadListener);
1161
- script.removeEventListener('error', onErrorListener); // if script exists, but we are reloading due to an error,
1162
- // reload script to trigger 'load' event
1163
-
1164
- (_script$parentNode = script.parentNode) === null || _script$parentNode === void 0 ? void 0 : _script$parentNode.removeChild(script);
1165
- script = injectScript(params);
1166
- }
1167
-
1168
- onLoadListener = onLoad(resolve, reject);
1169
- onErrorListener = onError(reject);
1170
- script.addEventListener('load', onLoadListener);
1171
- script.addEventListener('error', onErrorListener);
1172
- } catch (error) {
1173
- reject(error);
1174
- return;
1175
- }
1176
- }); // Resets stripePromise on error
1177
-
1178
- return stripePromise["catch"](function (error) {
1179
- stripePromise = null;
1180
- return Promise.reject(error);
1181
- });
1182
- };
1183
- var initStripe = function initStripe(maybeStripe, args, startTime) {
1184
- if (maybeStripe === null) {
1185
- return null;
1186
- }
1187
-
1188
- var stripe = maybeStripe.apply(undefined, args);
1189
- registerWrapper(stripe, startTime);
1190
- return stripe;
1191
- }; // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
1192
-
1193
- var stripePromise$1;
1194
- var loadCalled = false;
1195
-
1196
- var getStripePromise = function getStripePromise() {
1197
- if (stripePromise$1) {
1198
- return stripePromise$1;
1199
- }
1200
-
1201
- stripePromise$1 = loadScript(null)["catch"](function (error) {
1202
- // clear cache on error
1203
- stripePromise$1 = null;
1204
- return Promise.reject(error);
1205
- });
1206
- return stripePromise$1;
1207
- }; // Execute our own script injection after a tick to give users time to do their
1208
- // own script injection.
1209
-
1210
-
1211
- Promise.resolve().then(function () {
1212
- return getStripePromise();
1213
- })["catch"](function (error) {
1214
- if (!loadCalled) {
1215
- console.warn(error);
1216
- }
1217
- });
1218
- var loadStripe = function loadStripe() {
1219
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
1220
- args[_key] = arguments[_key];
1221
- }
1222
-
1223
- loadCalled = true;
1224
- var startTime = Date.now(); // if previous attempts are unsuccessful, will re-load script
1225
-
1226
- return getStripePromise().then(function (maybeStripe) {
1227
- return initStripe(maybeStripe, args, startTime);
1228
- });
1229
- };
1230
-
1231
- var errorHtml = "<div class=\"ff-sdk-error-container\">\n <p class=\"ff-sdk-error-message\">The payment form didn’t load correctly.</p>\n <p>Please refresh the page to try again.</p>\n <p>If the problem persists, please contact support.</p>\n <p class=\"ff-sdk-error-request-id\"></p>\n</div>\n";
1232
-
1233
- if(typeof document!=="undefined")document.head.appendChild(document.createElement("style")).textContent=".ff-sdk-error-container {\n background-color: #d1000033;\n color: #d10000;\n font-size: 14px;\n padding: 16px 12px;\n border-radius: 8px;\n}";
1234
-
1235
- const errorConfig = {
1236
- html: errorHtml,
1237
- selectors: {
1238
- errorRequestId: '.ff-sdk-error-request-id',
1239
- },
1240
- };
1241
- const renderError = (container, reqId) => {
1242
- const errorContainer = document.querySelector(container);
1243
- if (errorContainer) {
1244
- errorContainer.innerHTML = errorConfig.html;
1245
- if (reqId) {
1246
- const errorRequestId = errorContainer.querySelector(errorConfig.selectors.errorRequestId);
1247
- if (errorRequestId) {
1248
- errorRequestId.textContent = `Request ID: ${reqId}`;
1249
- }
1250
- }
1251
- }
1252
- };
1253
-
1254
- /**
1255
- * @fileoverview Checkout instance manager for Funnefox SDK
1256
- */
1257
- class CheckoutInstance extends EventEmitter {
1258
- constructor(config) {
1259
- super();
1260
- this.counter = 0;
1261
- this.radarSessionId = null;
1262
- this.handleInputChange = (inputName, error) => {
1263
- this.emit(EVENTS.INPUT_ERROR, { name: inputName, error });
1264
- };
1265
- this.handleMethodRender = (method) => {
1266
- this.emit(EVENTS.METHOD_RENDER, method);
1267
- };
1268
- this.handleMethodRenderError = (method) => {
1269
- this.emit(EVENTS.METHOD_RENDER_ERROR, method);
1270
- };
1271
- this.handleSubmit = (isSubmitting) => {
1272
- this.onLoaderChangeWithRace(isSubmitting);
1273
- this._setState(isSubmitting ? 'processing' : 'ready');
1274
- };
1275
- this.handleTokenizeSuccess = async (paymentMethodTokenData, primerHandler) => {
1276
- try {
1277
- this.onLoaderChangeWithRace(true);
1278
- this._setState('processing');
1279
- const radarSessionId = await this.radarSessionId;
1280
- const paymentResponse = await this.apiClient.createPayment({
1281
- orderId: this.orderId,
1282
- paymentMethodToken: paymentMethodTokenData.token,
1283
- clientMetadata: {
1284
- radarSessionId,
1285
- },
1286
- });
1287
- const result = this.apiClient.processPaymentResponse(paymentResponse);
1288
- await this._processPaymentResult(result, primerHandler);
1289
- }
1290
- catch (error) {
1291
- this._setState('error');
1292
- this.emit(EVENTS.PURCHASE_FAILURE, new Error(error.message || 'Payment processing failed'));
1293
- primerHandler.handleFailure(error.message || 'Payment processing failed');
1294
- }
1295
- finally {
1296
- this.onLoaderChangeWithRace(false);
1297
- this._setState('ready');
1298
- }
1299
- };
1300
- this.handleResumeSuccess = async (resumeTokenData, primerHandler) => {
1301
- try {
1302
- this.onLoaderChangeWithRace(true);
1303
- this._setState('processing');
1304
- const resumeResponse = await this.apiClient.resumePayment({
1305
- orderId: this.orderId,
1306
- resumeToken: resumeTokenData.resumeToken,
1307
- });
1308
- const result = this.apiClient.processPaymentResponse(resumeResponse);
1309
- await this._processPaymentResult(result, primerHandler);
1310
- }
1311
- catch (error) {
1312
- this._setState('error');
1313
- this.emit(EVENTS.PURCHASE_FAILURE, new Error(error.message || 'Payment processing failed'));
1314
- primerHandler.handleFailure(error.message || 'Payment processing failed');
1315
- }
1316
- finally {
1317
- this.emit(EVENTS.PURCHASE_COMPLETED);
1318
- this.onLoaderChangeWithRace(false);
1319
- this._setState('ready');
1320
- }
1321
- };
1322
- this.handleMethodsAvailable = (methods) => {
1323
- this.emit(EVENTS.METHODS_AVAILABLE, methods);
1324
- };
1325
- this.onLoaderChangeWithRace = (state) => {
1326
- const isLoading = !!(state ? ++this.counter : --this.counter);
1327
- this.primerWrapper.disableButtons(isLoading);
1328
- this.emit(EVENTS.LOADER_CHANGE, isLoading);
1329
- };
1330
- this.id = generateId('checkout_');
1331
- this.orgId = config.orgId;
1332
- this.baseUrl = config.baseUrl;
1333
- this.region = config.region;
1334
- this.checkoutConfig = { ...config.checkoutConfig };
1335
- this.callbacks = {
1336
- onSuccess: this.checkoutConfig.onSuccess,
1337
- onError: this.checkoutConfig.onError,
1338
- onStatusChange: this.checkoutConfig.onStatusChange,
1339
- onDestroy: this.checkoutConfig.onDestroy,
1340
- };
1341
- delete this.checkoutConfig?.onSuccess;
1342
- delete this.checkoutConfig?.onError;
1343
- delete this.checkoutConfig?.onStatusChange;
1344
- delete this.checkoutConfig?.onDestroy;
1345
- this.state = 'initializing';
1346
- this.orderId = null;
1347
- this.clientToken = null;
1348
- this.primerWrapper = new PrimerWrapper();
1349
- this.isDestroyed = false;
1350
- this._setupCallbackBridges();
1351
- }
1352
- _setupCallbackBridges() {
1353
- if (this.callbacks.onSuccess) {
1354
- this.on(EVENTS.SUCCESS, this.callbacks.onSuccess);
1355
- }
1356
- if (this.callbacks.onError) {
1357
- this.on(EVENTS.ERROR, this.callbacks.onError);
1358
- }
1359
- if (this.callbacks.onStatusChange) {
1360
- this.on(EVENTS.STATUS_CHANGE, this.callbacks.onStatusChange);
1361
- }
1362
- if (this.callbacks.onDestroy) {
1363
- this.on(EVENTS.DESTROY, this.callbacks.onDestroy);
1364
- }
1365
- }
1366
- removeAllListeners() {
1367
- return super.removeAllListeners();
1368
- }
1369
- async initialize() {
1370
- try {
1371
- this.showInitializingLoader();
1372
- this._setState('initializing');
1373
- await this.createSession();
1374
- await this._initializePrimerCheckout();
1375
- this._setState('ready');
1376
- this.checkoutConfig?.onInitialized?.();
1377
- return this;
1378
- }
1379
- catch (error) {
1380
- this._setState('error');
1381
- renderError(this.checkoutConfig.container, error?.response?.req_id);
1382
- this.emit(EVENTS.ERROR, error);
1383
- throw error;
1384
- }
1385
- finally {
1386
- this.hideInitializingLoader();
1387
- }
1388
- }
1389
- async createSession() {
1390
- this.apiClient = new APIClient({
1391
- baseUrl: this.baseUrl || DEFAULTS.BASE_URL,
1392
- orgId: this.orgId,
1393
- timeout: DEFAULTS.REQUEST_TIMEOUT,
1394
- retryAttempts: DEFAULTS.RETRY_ATTEMPTS,
1395
- });
1396
- const sessionParams = {
1397
- priceId: this.checkoutConfig.priceId,
1398
- externalId: this.checkoutConfig.customer.externalId,
1399
- email: this.checkoutConfig.customer.email,
1400
- region: this.region || DEFAULTS.REGION,
1401
- clientMetadata: this.checkoutConfig.clientMetadata,
1402
- countryCode: this.checkoutConfig.customer.countryCode,
1403
- };
1404
- const cacheKey = [
1405
- this.orgId,
1406
- this.checkoutConfig.priceId,
1407
- this.checkoutConfig.customer.externalId,
1408
- this.checkoutConfig.customer.email,
1409
- ].join('-');
1410
- let sessionResponse;
1411
- // Return cached response if payload hasn't changed
1412
- const cachedResponse = CheckoutInstance.sessionCache.get(cacheKey);
1413
- if (cachedResponse) {
1414
- sessionResponse = await cachedResponse;
1415
- }
1416
- else {
1417
- const sessionRequest = this.apiClient
1418
- .createClientSession(sessionParams)
1419
- .then(response => {
1420
- if (response.data?.stripe_public_key) {
1421
- loadStripe(response.data?.stripe_public_key).then(stripe => {
1422
- this.radarSessionId = stripe
1423
- .createRadarSession()
1424
- .then(session => session?.radarSession?.id)
1425
- .catch(() => '');
1426
- });
1427
- }
1428
- return response;
1429
- });
1430
- // Cache the successful response
1431
- CheckoutInstance.sessionCache.set(cacheKey, sessionRequest);
1432
- sessionResponse = await sessionRequest;
1433
- }
1434
- const sessionData = this.apiClient.processSessionResponse(sessionResponse);
1435
- this.orderId = sessionData.orderId;
1436
- this.clientToken = sessionData.clientToken;
1437
- }
1438
- convertCardSelectorsToElements(selectors, container) {
1439
- const cardNumber = container.querySelector(selectors.cardNumber);
1440
- const expiryDate = container.querySelector(selectors.expiryDate);
1441
- const cvv = container.querySelector(selectors.cvv);
1442
- const cardholderName = container.querySelector(selectors.cardholderName);
1443
- const button = container.querySelector(selectors.button);
1444
- if (!cardNumber || !expiryDate || !cvv || !button) {
1445
- throw new CheckoutError('Required card input elements not found in container');
1446
- }
1447
- return {
1448
- cardNumber,
1449
- expiryDate,
1450
- cvv,
1451
- cardholderName,
1452
- button,
1453
- };
1454
- }
1455
- convertPaymentButtonSelectorsToElements(selectors) {
1456
- const paypal = document.querySelector(selectors.paypal);
1457
- const googlePay = document.querySelector(selectors.googlePay);
1458
- const applePay = document.querySelector(selectors.applePay);
1459
- if (!paypal || !googlePay || !applePay) {
1460
- throw new CheckoutError('Required payment button elements not found in container');
1461
- }
1462
- return {
1463
- paypal,
1464
- googlePay,
1465
- applePay,
1466
- };
1467
- }
1468
- async _initializePrimerCheckout() {
1469
- // Get container element
1470
- const containerElement = this.getContainer();
1471
- if (!containerElement) {
1472
- throw new CheckoutError(`Checkout container not found: ${this.checkoutConfig.container}`);
1473
- }
1474
- // Get selectors (either from config or default skin)
1475
- let cardElements;
1476
- let paymentButtonElements;
1477
- let checkoutOptions;
1478
- if (!this.checkoutConfig.cardSelectors ||
1479
- !this.checkoutConfig.paymentButtonSelectors) {
1480
- this.checkoutConfig.paymentMethodOrder =
1481
- this.checkoutConfig.paymentMethodOrder || DEFAULT_PAYMENT_METHOD_ORDER;
1482
- const defaultSkinCheckoutOptions = await this.getDefaultSkinCheckoutOptions();
1483
- if (!defaultSkinCheckoutOptions.cardElements ||
1484
- !defaultSkinCheckoutOptions.paymentButtonElements) {
1485
- throw new CheckoutError('Default skin must provide cardSelectors and paymentButtonSelectors');
1486
- }
1487
- cardElements =
1488
- defaultSkinCheckoutOptions.cardElements;
1489
- paymentButtonElements = defaultSkinCheckoutOptions.paymentButtonElements;
1490
- checkoutOptions = this.getCheckoutOptions(defaultSkinCheckoutOptions);
1491
- }
1492
- else {
1493
- if (this.checkoutConfig.paymentMethodOrder) {
1494
- // eslint-disable-next-line no-console
1495
- console.warn('paymentMethodOrder is using only for default skin and will be ignored if you are using custom checkout');
1496
- }
1497
- cardElements = this.convertCardSelectorsToElements(this.checkoutConfig.cardSelectors, containerElement);
1498
- paymentButtonElements = this.convertPaymentButtonSelectorsToElements(this.checkoutConfig.paymentButtonSelectors);
1499
- checkoutOptions = this.getCheckoutOptions({});
1500
- }
1501
- await this.primerWrapper.renderCheckout(this.clientToken, checkoutOptions, {
1502
- container: containerElement,
1503
- cardElements,
1504
- paymentButtonElements,
1505
- onSubmit: this.handleSubmit,
1506
- onInputChange: this.handleInputChange,
1507
- onMethodRender: this.handleMethodRender,
1508
- onMethodsAvailable: this.handleMethodsAvailable,
1509
- onMethodRenderError: this.handleMethodRenderError,
1510
- });
1511
- }
1512
- async _processPaymentResult(result, primerHandler) {
1513
- if (result.orderId) {
1514
- this.orderId = result.orderId;
1515
- }
1516
- switch (result.type) {
1517
- case 'success':
1518
- this._setState('completed');
1519
- this.emit(EVENTS.SUCCESS, {
1520
- orderId: result.orderId,
1521
- status: result.status,
1522
- });
1523
- primerHandler.handleSuccess();
1524
- break;
1525
- case 'action_required':
1526
- this._setState('action_required');
1527
- this.clientToken = result.clientToken;
1528
- primerHandler.continueWithNewClientToken(result.clientToken);
1529
- break;
1530
- case 'processing':
1531
- this._setState('processing');
1532
- setTimeout(() => {
1533
- primerHandler.handleFailure('Payment is still processing. Please check back later.');
1534
- }, 30000);
1535
- break;
1536
- default:
1537
- throw new CheckoutError(`Unknown payment result type: ${result.type}`);
1538
- }
1539
- }
1540
- getCheckoutOptions(options) {
1541
- let wasPaymentProcessedStarted = false;
1542
- return {
1543
- ...this.checkoutConfig,
1544
- ...options,
1545
- onTokenizeSuccess: this.handleTokenizeSuccess,
1546
- onResumeSuccess: this.handleResumeSuccess,
1547
- onResumeError: error => {
1548
- if (error.stack?.includes('PROCESSOR_3DS') &&
1549
- error.code === 'RESUME_ERROR' &&
1550
- error.message?.includes('fetch resume key')) {
1551
- // Ignore 3DS close error, because it is not understandable by user
1552
- return;
1553
- }
1554
- this.emit(EVENTS.PURCHASE_FAILURE, error);
1555
- },
1556
- onCheckoutFail: error => {
1557
- this.emit(EVENTS.PURCHASE_FAILURE, error);
1558
- },
1559
- onTokenizeError: error => {
1560
- this.emit(EVENTS.PURCHASE_FAILURE, error);
1561
- },
1562
- onTokenizeShouldStart: data => {
1563
- wasPaymentProcessedStarted = true;
1564
- this.emit(EVENTS.ERROR, undefined);
1565
- this.emit(EVENTS.START_PURCHASE, data.paymentMethodType);
1566
- return true;
1567
- },
1568
- onPaymentMethodAction: action => {
1569
- switch (action) {
1570
- case 'PAYMENT_METHOD_SELECTED':
1571
- this.emit(EVENTS.ERROR, undefined);
1572
- break;
1573
- case 'PAYMENT_METHOD_UNSELECTED':
1574
- if (!wasPaymentProcessedStarted) {
1575
- this.emit(EVENTS.PURCHASE_CANCELLED);
1576
- }
1577
- wasPaymentProcessedStarted = false;
1578
- break;
1579
- }
1580
- },
1581
- };
1582
- }
1583
- async updatePrice(newPriceId, clientMetadata) {
1584
- this._ensureNotDestroyed();
1585
- requireString(newPriceId, 'priceId');
1586
- if (this.state === 'processing') {
1587
- throw new CheckoutError('Cannot update price while payment is processing');
1588
- }
1589
- try {
1590
- this._setState('updating');
1591
- // Invalidate session cache
1592
- CheckoutInstance.sessionCache.clear();
1593
- await this.apiClient.updateClientSession({
1594
- orderId: this.orderId,
1595
- clientToken: this.clientToken,
1596
- priceId: newPriceId,
1597
- clientMetadata,
1598
- });
1599
- this.checkoutConfig.priceId = newPriceId;
1600
- this._setState('ready');
1601
- }
1602
- catch (error) {
1603
- this._setState('error');
1604
- this.emit(EVENTS.ERROR, error);
1605
- throw error;
1606
- }
1607
- }
1608
- getStatus() {
1609
- return {
1610
- id: this.id,
1611
- state: this.state,
1612
- orderId: this.orderId,
1613
- priceId: this.checkoutConfig.priceId,
1614
- isDestroyed: this.isDestroyed,
1615
- };
1616
- }
1617
- async destroy() {
1618
- if (this.isDestroyed)
1619
- return;
1620
- try {
1621
- CheckoutInstance.sessionCache.clear();
1622
- await this.primerWrapper.destroy();
1623
- this._setState('destroyed');
1624
- this.orderId = null;
1625
- this.clientToken = null;
1626
- this.isDestroyed = true;
1627
- this.emit(EVENTS.DESTROY);
1628
- this.removeAllListeners();
1629
- }
1630
- catch (error) {
1631
- // eslint-disable-next-line no-console
1632
- console.warn('Error during checkout cleanup:', error);
1633
- }
1634
- }
1635
- _setState(newState) {
1636
- if (this.state !== newState) {
1637
- const oldState = this.state;
1638
- this.state = newState;
1639
- this.emit(EVENTS.STATUS_CHANGE, newState, oldState);
1640
- }
1641
- }
1642
- _ensureNotDestroyed() {
1643
- if (this.isDestroyed) {
1644
- throw new CheckoutError('Checkout instance has been destroyed');
1645
- }
1646
- }
1647
- getContainer() {
1648
- return document.querySelector(this.checkoutConfig.container);
1649
- }
1650
- isInState(state) {
1651
- return this.state === state;
1652
- }
1653
- isReady() {
1654
- return this.state === 'ready' && !this.isDestroyed;
1655
- }
1656
- isProcessing() {
1657
- return ['processing', 'action_required'].includes(this.state);
1658
- }
1659
- // Creates containers to render hosted inputs with labels and error messages,
1660
- // a card holder input with label and error, and a submit button.
1661
- async getDefaultSkinCheckoutOptions() {
1662
- const skinFactory = (await import('./chunk-index.es.js'))
1663
- .default;
1664
- const skin = await skinFactory(this.checkoutConfig);
1665
- this.on(EVENTS.INPUT_ERROR, skin.onInputError);
1666
- this.on(EVENTS.STATUS_CHANGE, skin.onStatusChange);
1667
- this.on(EVENTS.ERROR, (error) => skin.onError(error));
1668
- this.on(EVENTS.LOADER_CHANGE, skin.onLoaderChange);
1669
- this.on(EVENTS.DESTROY, skin.onDestroy);
1670
- this.on(EVENTS.SUCCESS, skin.onSuccess);
1671
- this.on(EVENTS.START_PURCHASE, skin.onStartPurchase);
1672
- this.on(EVENTS.PURCHASE_FAILURE, skin.onPurchaseFailure);
1673
- this.on(EVENTS.PURCHASE_COMPLETED, skin.onPurchaseCompleted);
1674
- this.on(EVENTS.METHODS_AVAILABLE, skin.onMethodsAvailable);
1675
- this.on(EVENTS.METHODS_AVAILABLE, this.hideInitializingLoader);
1676
- return skin.getCheckoutOptions();
1677
- }
1678
- async getCardDefaultSkinCheckoutOptions(node) {
1679
- const CardSkin = (await import('./chunk-index.es2.js')).default;
1680
- const skin = new CardSkin(node, this.checkoutConfig);
1681
- skin.init();
1682
- this.on(EVENTS.INPUT_ERROR, skin.onInputError);
1683
- this.on(EVENTS.METHOD_RENDER, skin.onMethodRender);
1684
- this.on(EVENTS.SUCCESS, skin.onDestroy);
1685
- this.on(EVENTS.DESTROY, skin.onDestroy);
1686
- return skin.getCheckoutOptions();
1687
- }
1688
- showInitializingLoader() {
1689
- renderLoader(this.checkoutConfig.container);
1690
- }
1691
- hideInitializingLoader() {
1692
- hideLoader();
1693
- }
1694
- async initMethod(method, element, callbacks) {
1695
- this._ensureNotDestroyed();
1696
- if (!this.isReady()) {
1697
- await this.createSession();
1698
- }
1699
- if (callbacks.onRenderSuccess) {
1700
- this.on(EVENTS.METHOD_RENDER, callbacks.onRenderSuccess);
1701
- }
1702
- if (callbacks.onRenderError) {
1703
- this.on(EVENTS.METHOD_RENDER_ERROR, callbacks.onRenderError);
1704
- }
1705
- if (callbacks.onLoaderChange) {
1706
- this.on(EVENTS.LOADER_CHANGE, callbacks.onLoaderChange);
1707
- }
1708
- if (callbacks.onPaymentSuccess) {
1709
- this.on(EVENTS.SUCCESS, callbacks.onPaymentSuccess);
1710
- }
1711
- if (callbacks.onPaymentFail) {
1712
- this.on(EVENTS.PURCHASE_FAILURE, callbacks.onPaymentFail);
1713
- }
1714
- if (callbacks.onPaymentCancel) {
1715
- this.on(EVENTS.PURCHASE_CANCELLED, callbacks.onPaymentCancel);
1716
- }
1717
- if (callbacks.onErrorMessageChange) {
1718
- this.on(EVENTS.ERROR, callbacks.onErrorMessageChange);
1719
- }
1720
- if (callbacks.onPaymentStarted) {
1721
- this.on(EVENTS.START_PURCHASE, callbacks.onPaymentStarted);
1722
- }
1723
- if (callbacks.onMethodsAvailable) {
1724
- this.on(EVENTS.METHODS_AVAILABLE, callbacks.onMethodsAvailable);
1725
- }
1726
- let checkoutOptions = this.getCheckoutOptions({});
1727
- let methodOptions = {
1728
- onMethodRender: this.handleMethodRender,
1729
- onMethodRenderError: this.handleMethodRenderError,
1730
- };
1731
- if (method === PaymentMethod.PAYMENT_CARD) {
1732
- const cardDefaultOptions = await this.getCardDefaultSkinCheckoutOptions(element);
1733
- checkoutOptions = this.getCheckoutOptions({
1734
- ...cardDefaultOptions,
1735
- });
1736
- methodOptions = {
1737
- cardElements: cardDefaultOptions.cardElements,
1738
- onSubmit: this.handleSubmit,
1739
- onInputChange: this.handleInputChange,
1740
- onMethodRender: this.handleMethodRender,
1741
- onMethodRenderError: this.handleMethodRenderError,
1742
- };
1743
- }
1744
- await this.primerWrapper.initializeHeadlessCheckout(this.clientToken, checkoutOptions);
1745
- const methodInterface = await this.primerWrapper.initMethod(method, element, methodOptions);
1746
- return {
1747
- ...methodInterface,
1748
- destroy: async () => {
1749
- await methodInterface.destroy();
1750
- await this.destroy();
1751
- },
1752
- };
1753
- }
1754
- }
1755
- CheckoutInstance.sessionCache = new Map();
1756
-
1757
- function getErrorImage(orgId, options) {
1758
- const params = new URLSearchParams({
1759
- message: options.message,
1760
- code: options.code,
1761
- timestamp: Date.now().toString(),
1762
- sdk_version: SDK_VERSION,
1763
- });
1764
- if (options.req_id) {
1765
- params.append('req_id', options.req_id);
1766
- }
1767
- const url = `https://billing.funnelfox.com/sdk_report/${encodeURIComponent(orgId)}/crash?${params.toString()}`;
1768
- const img = new Image();
1769
- img.src = url;
1770
- img.style.display = 'none';
1771
- document.body.appendChild(img);
1772
- }
1773
-
1774
- /**
1775
- * @fileoverview Public API with configuration and orchestration logic
1776
- */
1777
- let defaultConfig = null;
1778
- function configure(config) {
1779
- defaultConfig = config;
1780
- }
1781
- function resolveConfig(options, functionName) {
1782
- const { orgId, apiConfig } = options || {};
1783
- const finalOrgId = orgId || defaultConfig?.orgId;
1784
- if (!finalOrgId) {
1785
- throw new Error(`orgId is required. Pass it to ${functionName}() or call configure() first.`);
1786
- }
1787
- const finalBaseUrl = apiConfig?.baseUrl || defaultConfig?.baseUrl || DEFAULTS.BASE_URL;
1788
- const finalRegion = apiConfig?.region || defaultConfig?.region || DEFAULTS.REGION;
1789
- return {
1790
- orgId: finalOrgId,
1791
- baseUrl: finalBaseUrl,
1792
- region: finalRegion,
1793
- };
1794
- }
1795
- async function createCheckout(options) {
1796
- try {
1797
- const { ...checkoutConfig } = options;
1798
- // Ensure Primer SDK is loaded before creating checkout
1799
- const primerWrapper = new PrimerWrapper();
1800
- await primerWrapper.ensurePrimerLoaded();
1801
- const config = resolveConfig(options, 'createCheckout');
1802
- const checkout = new CheckoutInstance({
1803
- ...config,
1804
- checkoutConfig: {
1805
- ...checkoutConfig,
1806
- },
1807
- });
1808
- await checkout.initialize();
1809
- return checkout;
1810
- }
1811
- catch (error) {
1812
- getErrorImage(options.orgId, {
1813
- message: error.message,
1814
- code: error.code,
1815
- req_id: error?.response?.req_id,
1816
- });
1817
- throw error;
1818
- }
1819
- }
1820
- async function createClientSession(params) {
1821
- const { priceId, externalId, email, clientMetadata, countryCode } = params;
1822
- const config = resolveConfig(params, 'createClientSession');
1823
- const apiClient = new APIClient({
1824
- baseUrl: config.baseUrl,
1825
- orgId: config.orgId,
1826
- timeout: DEFAULTS.REQUEST_TIMEOUT,
1827
- retryAttempts: DEFAULTS.RETRY_ATTEMPTS,
1828
- });
1829
- const sessionResponse = await apiClient.createClientSession({
1830
- priceId,
1831
- externalId,
1832
- email,
1833
- region: config.region,
1834
- clientMetadata,
1835
- countryCode,
1836
- });
1837
- return apiClient.processSessionResponse(sessionResponse);
1838
- }
1839
- async function silentPurchase(options) {
1840
- const { priceId, externalId, clientMetadata, orgId, baseUrl } = options;
1841
- const apiClient = new APIClient({
1842
- baseUrl: baseUrl,
1843
- orgId: orgId,
1844
- timeout: DEFAULTS.REQUEST_TIMEOUT,
1845
- retryAttempts: DEFAULTS.RETRY_ATTEMPTS,
1846
- });
1847
- try {
1848
- const response = await apiClient.oneClick({
1849
- pp_ident: priceId,
1850
- external_id: externalId,
1851
- client_metadata: clientMetadata,
1852
- });
1853
- if (response.status !== 'success' &&
1854
- response.error.some(({ code }) => code === 'double_purchase')) {
1855
- throw new APIError('This product was already purchased');
1856
- }
1857
- else if (response.status !== 'success') {
1858
- return false;
1859
- }
1860
- return true;
1861
- }
1862
- catch (error) {
1863
- getErrorImage(orgId, {
1864
- message: error.message,
1865
- code: error.code,
1866
- req_id: error?.response?.req_id,
1867
- });
1868
- throw error;
1869
- }
1870
- }
1871
- async function initMethod(method, element, options) {
1872
- try {
1873
- const checkoutInstance = new CheckoutInstance({
1874
- orgId: options.orgId,
1875
- baseUrl: options.baseUrl,
1876
- checkoutConfig: {
1877
- priceId: options.priceId,
1878
- customer: {
1879
- externalId: options.externalId,
1880
- email: options.email,
1881
- },
1882
- container: '',
1883
- clientMetadata: options.meta,
1884
- card: options.card,
1885
- style: options.style,
1886
- applePay: options.applePay,
1887
- paypal: options.paypal,
1888
- googlePay: options.googlePay,
1889
- },
1890
- });
1891
- return checkoutInstance.initMethod(method, element, {
1892
- onRenderSuccess: options.onRenderSuccess,
1893
- onRenderError: options.onRenderError,
1894
- onLoaderChange: options.onLoaderChange,
1895
- onPaymentSuccess: options.onPaymentSuccess,
1896
- onPaymentFail: options.onPaymentFail,
1897
- onPaymentCancel: options.onPaymentCancel,
1898
- onErrorMessageChange: options.onErrorMessageChange,
1899
- onPaymentStarted: options.onPaymentStarted,
1900
- onMethodsAvailable: options.onMethodsAvailable,
1901
- });
1902
- }
1903
- catch (error) {
1904
- getErrorImage(options.orgId, {
1905
- message: error.message,
1906
- code: error.code,
1907
- req_id: error?.response?.req_id,
1908
- });
1909
- throw error;
1910
- }
1911
- }
1912
-
1913
- /**
1914
- * @fileoverview Main entry point for @funnelfox/billing
1915
- */
1916
- const Billing = {
1917
- configure: configure,
1918
- createCheckout: createCheckout,
1919
- createClientSession: createClientSession,
1920
- initMethod: initMethod,
1921
- silentPurchase: silentPurchase,
1922
- };
1923
- if (typeof window !== 'undefined') {
1924
- window.Billing = Billing;
1925
- }
1926
-
1927
- export { APIError, Billing, CHECKOUT_STATES, CheckoutError, ConfigurationError, DEFAULTS, ERROR_CODES, EVENTS, FunnefoxSDKError, NetworkError, PaymentMethod, PrimerError, SDK_VERSION, ValidationError, configure, createCheckout, createClientSession, Billing as default };
8
+ export { A as APIError, B as Billing, d as CHECKOUT_STATES, C as CheckoutError, b as ConfigurationError, c as DEFAULTS, e as ERROR_CODES, E as EVENTS, F as FunnefoxSDKError, N as NetworkError, P as PaymentMethod, a as PrimerError, S as SDK_VERSION, V as ValidationError, f as configure, g as createCheckout, h as createClientSession, B as default } from './chunk-index.es.js';