@sunbay/checkout-sdk-js 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/index.cjs +181 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +250 -218
- package/dist/index.d.ts +250 -218
- package/dist/index.global.js +181 -147
- package/dist/index.global.js.map +1 -1
- package/dist/index.mjs +181 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.cjs
CHANGED
|
@@ -62,8 +62,9 @@ var MainFrameTransport = class {
|
|
|
62
62
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
63
63
|
this.helloHandler = null;
|
|
64
64
|
/**
|
|
65
|
-
*
|
|
66
|
-
* main
|
|
65
|
+
* Runtime id negotiated during the handshake; empty string before injection. Outbound request
|
|
66
|
+
* envelopes are filled from it; the main endpoint only processes requests whose runtimeId
|
|
67
|
+
* matches its own, and an empty string is silently dropped.
|
|
67
68
|
*/
|
|
68
69
|
this.runtimeId = "";
|
|
69
70
|
this.destroyed = false;
|
|
@@ -76,9 +77,9 @@ var MainFrameTransport = class {
|
|
|
76
77
|
window.addEventListener("message", this.messageListener);
|
|
77
78
|
}
|
|
78
79
|
/**
|
|
79
|
-
*
|
|
80
|
+
* Create the hidden, 0-size main iframe and append it to document.body.
|
|
80
81
|
*
|
|
81
|
-
* @returns
|
|
82
|
+
* @returns The mounted iframe element.
|
|
82
83
|
* @example
|
|
83
84
|
* const iframe = transport.createHiddenIframe();
|
|
84
85
|
*/
|
|
@@ -100,11 +101,11 @@ var MainFrameTransport = class {
|
|
|
100
101
|
return iframe;
|
|
101
102
|
}
|
|
102
103
|
/**
|
|
103
|
-
*
|
|
104
|
+
* Send one request envelope and pair the response by requestId.
|
|
104
105
|
*
|
|
105
|
-
* @param name -
|
|
106
|
-
* @param payload -
|
|
107
|
-
* @returns
|
|
106
|
+
* @param name - Command name.
|
|
107
|
+
* @param payload - Command payload (JSON-serializable).
|
|
108
|
+
* @returns The response data; on failure (ok:false / timeout / destroy) rejects with a CheckoutError.
|
|
108
109
|
* @example
|
|
109
110
|
* const session = await transport.request('getSession', {});
|
|
110
111
|
*/
|
|
@@ -145,10 +146,10 @@ var MainFrameTransport = class {
|
|
|
145
146
|
});
|
|
146
147
|
}
|
|
147
148
|
/**
|
|
148
|
-
*
|
|
149
|
+
* Register an event handler (dispatches the payload of kind:'event' envelopes).
|
|
149
150
|
*
|
|
150
|
-
* @param event -
|
|
151
|
-
* @param handler -
|
|
151
|
+
* @param event - Event name.
|
|
152
|
+
* @param handler - Callback.
|
|
152
153
|
*/
|
|
153
154
|
on(event, handler) {
|
|
154
155
|
let set = this.eventHandlers.get(event);
|
|
@@ -159,10 +160,10 @@ var MainFrameTransport = class {
|
|
|
159
160
|
set.add(handler);
|
|
160
161
|
}
|
|
161
162
|
/**
|
|
162
|
-
*
|
|
163
|
+
* Unregister an event handler.
|
|
163
164
|
*
|
|
164
|
-
* @param event -
|
|
165
|
-
* @param handler -
|
|
165
|
+
* @param event - Event name.
|
|
166
|
+
* @param handler - The previously registered callback.
|
|
166
167
|
*/
|
|
167
168
|
off(event, handler) {
|
|
168
169
|
const set = this.eventHandlers.get(event);
|
|
@@ -174,24 +175,26 @@ var MainFrameTransport = class {
|
|
|
174
175
|
}
|
|
175
176
|
}
|
|
176
177
|
/**
|
|
177
|
-
*
|
|
178
|
+
* Register the handshake (RuntimeHello) callback. The hello phase has no checkoutId yet and is
|
|
179
|
+
* handled separately.
|
|
178
180
|
*
|
|
179
|
-
* @param handler -
|
|
181
|
+
* @param handler - Called when a hello is received.
|
|
180
182
|
*/
|
|
181
183
|
onHello(handler) {
|
|
182
184
|
this.helloHandler = handler;
|
|
183
185
|
}
|
|
184
186
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
+
* Inject the runtime id negotiated during the handshake. Must be called before sending the first
|
|
188
|
+
* request (initialize), otherwise the outbound envelope's runtimeId is an empty string, gets
|
|
189
|
+
* silently dropped by the main endpoint, and the request times out.
|
|
187
190
|
*
|
|
188
|
-
* @param runtimeId -
|
|
191
|
+
* @param runtimeId - The runtimeId from RuntimeHello.
|
|
189
192
|
*/
|
|
190
193
|
setRuntimeId(runtimeId) {
|
|
191
194
|
this.runtimeId = runtimeId;
|
|
192
195
|
}
|
|
193
196
|
/**
|
|
194
|
-
*
|
|
197
|
+
* Destroy: remove the iframe, clear listeners, and reject pending requests with INSTANCE_DESTROYED.
|
|
195
198
|
*/
|
|
196
199
|
destroy() {
|
|
197
200
|
if (this.destroyed) {
|
|
@@ -211,7 +214,7 @@ var MainFrameTransport = class {
|
|
|
211
214
|
}
|
|
212
215
|
this.iframe = null;
|
|
213
216
|
}
|
|
214
|
-
/**
|
|
217
|
+
/** Resolve the timeout by command name. */
|
|
215
218
|
resolveTimeout(name) {
|
|
216
219
|
if (name === "initialize") {
|
|
217
220
|
return this.timeouts.initialize;
|
|
@@ -219,20 +222,22 @@ var MainFrameTransport = class {
|
|
|
219
222
|
return this.timeouts.default;
|
|
220
223
|
}
|
|
221
224
|
/**
|
|
222
|
-
*
|
|
225
|
+
* Point-to-point port relay (parent-window relay, Stripe controller model).
|
|
223
226
|
*
|
|
224
|
-
*
|
|
225
|
-
* transfer MessagePort
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
227
|
+
* Sibling iframes (main and the various Element iframes) cannot obtain each other's window
|
|
228
|
+
* references, so they cannot transfer a MessagePort directly. This layer (the merchant page,
|
|
229
|
+
* the only party that can reach both ends and knows their origin) does the following when it
|
|
230
|
+
* receives an `element-online` from some Element iframe:
|
|
231
|
+
* 1. Create a `MessageChannel`;
|
|
232
|
+
* 2. `port1` → main iframe (`relay-port`, targetOrigin=hostOrigin);
|
|
233
|
+
* 3. `port2` → the originating Element iframe (i.e. `event.source`, targetOrigin=hostOrigin).
|
|
230
234
|
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
235
|
+
* Both ends' origin is hostOrigin, so targetOrigin is always correct (and not dropped across
|
|
236
|
+
* origins). This layer only carries **empty ports** and never touches card values (which are
|
|
237
|
+
* transmitted end-to-end after the port is established).
|
|
233
238
|
*
|
|
234
|
-
* @param event
|
|
235
|
-
* @returns
|
|
239
|
+
* @param event Inbound message (origin already verified as hostOrigin)
|
|
240
|
+
* @returns Whether it was handled as element-online (if so, the caller should return)
|
|
236
241
|
*/
|
|
237
242
|
tryHandleElementOnline(event) {
|
|
238
243
|
const data = event.data;
|
|
@@ -261,7 +266,7 @@ var MainFrameTransport = class {
|
|
|
261
266
|
elementWin.postMessage(relayMsg, this.hostOrigin, [channel.port2]);
|
|
262
267
|
return true;
|
|
263
268
|
}
|
|
264
|
-
/**
|
|
269
|
+
/** Handle an inbound message: filter origin, recognize relay / hello / Envelope and dispatch. */
|
|
265
270
|
handleMessage(event) {
|
|
266
271
|
if (this.destroyed) {
|
|
267
272
|
return;
|
|
@@ -302,7 +307,7 @@ var MainFrameTransport = class {
|
|
|
302
307
|
this.handleEvent(envelope);
|
|
303
308
|
}
|
|
304
309
|
}
|
|
305
|
-
/**
|
|
310
|
+
/** Dispatch a response to the paired pending request. */
|
|
306
311
|
handleResponse(envelope) {
|
|
307
312
|
const entry = this.pending.get(envelope.requestId);
|
|
308
313
|
if (!entry) {
|
|
@@ -316,7 +321,7 @@ var MainFrameTransport = class {
|
|
|
316
321
|
entry.reject(envelope.error);
|
|
317
322
|
}
|
|
318
323
|
}
|
|
319
|
-
/**
|
|
324
|
+
/** Dispatch an event to the registered handlers. */
|
|
320
325
|
handleEvent(envelope) {
|
|
321
326
|
const set = this.eventHandlers.get(envelope.name);
|
|
322
327
|
if (!set) {
|
|
@@ -359,17 +364,18 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
359
364
|
this.status = "created";
|
|
360
365
|
this.container = null;
|
|
361
366
|
this.iframe = null;
|
|
362
|
-
/** mount
|
|
367
|
+
/** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */
|
|
363
368
|
this.readyHandler = null;
|
|
364
369
|
this.loadErrorHandler = null;
|
|
365
|
-
/**
|
|
370
|
+
/** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */
|
|
366
371
|
this.availabilityHandler = null;
|
|
367
372
|
/**
|
|
368
|
-
* iframe
|
|
369
|
-
*
|
|
373
|
+
* iframe auto-height: the window 'message' listener that stays resident during mount (unbound
|
|
374
|
+
* when the iframe disappears/is destroyed). Keeps a reference so removeEventListener can be
|
|
375
|
+
* called; null means not listening.
|
|
370
376
|
*/
|
|
371
377
|
this.resizeHandler = null;
|
|
372
|
-
/**
|
|
378
|
+
/** The most recently applied iframe height (px), used for deduplication to avoid redundant style writes; null means not yet set. */
|
|
373
379
|
this.lastHeight = null;
|
|
374
380
|
this.elementId = opts.elementId;
|
|
375
381
|
this.type = opts.type;
|
|
@@ -384,26 +390,28 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
384
390
|
this.installInputActions();
|
|
385
391
|
this.installExpressAvailability();
|
|
386
392
|
}
|
|
387
|
-
/**
|
|
393
|
+
/** The current status (read-only snapshot, for external/test observation). */
|
|
388
394
|
getStatus() {
|
|
389
395
|
return this.status;
|
|
390
396
|
}
|
|
391
397
|
/**
|
|
392
|
-
*
|
|
398
|
+
* expressCheckout only: return the current snapshot of each wallet's availability (always
|
|
399
|
+
* undefined for other types).
|
|
393
400
|
*
|
|
394
|
-
*
|
|
401
|
+
* The initial value comes from elementReady's availableWallets, then is updated by
|
|
402
|
+
* expressAvailabilityChange.
|
|
395
403
|
*
|
|
396
|
-
* @returns
|
|
404
|
+
* @returns The availability snapshot; undefined when not yet ready or non-express.
|
|
397
405
|
*/
|
|
398
406
|
getAvailableWallets() {
|
|
399
407
|
return this.availableWallets;
|
|
400
408
|
}
|
|
401
409
|
/**
|
|
402
|
-
*
|
|
410
|
+
* Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.
|
|
403
411
|
*
|
|
404
|
-
* @param selectorOrEl - CSS
|
|
405
|
-
* @returns elementReady
|
|
406
|
-
* @throws
|
|
412
|
+
* @param selectorOrEl - A CSS selector or an HTMLElement container; the container must be unique and empty.
|
|
413
|
+
* @returns Resolves after elementReady; on failure removes the node and rejects (loadError / INVALID_OPTIONS).
|
|
414
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
407
415
|
* @example
|
|
408
416
|
* await element.mount('#card-field');
|
|
409
417
|
*/
|
|
@@ -442,6 +450,7 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
442
450
|
iframe.style.border = "0";
|
|
443
451
|
iframe.style.width = "100%";
|
|
444
452
|
iframe.style.display = "block";
|
|
453
|
+
iframe.setAttribute("scrolling", "no");
|
|
445
454
|
container.appendChild(iframe);
|
|
446
455
|
this.iframe = iframe;
|
|
447
456
|
this.installResizeListener();
|
|
@@ -501,10 +510,10 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
501
510
|
});
|
|
502
511
|
}
|
|
503
512
|
/**
|
|
504
|
-
*
|
|
513
|
+
* Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.
|
|
505
514
|
*
|
|
506
|
-
* @returns
|
|
507
|
-
* @throws
|
|
515
|
+
* @returns Resolves on completion (resolves directly when not mounted).
|
|
516
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
508
517
|
*/
|
|
509
518
|
async unmount() {
|
|
510
519
|
this.assertAlive();
|
|
@@ -520,7 +529,7 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
520
529
|
}
|
|
521
530
|
}
|
|
522
531
|
/**
|
|
523
|
-
*
|
|
532
|
+
* Destroy the handle: idempotent; remove the iframe; afterwards any non-destroy/off call throws INSTANCE_DESTROYED.
|
|
524
533
|
*/
|
|
525
534
|
destroy() {
|
|
526
535
|
if (this.status === "destroyed") {
|
|
@@ -544,11 +553,11 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
544
553
|
this.onDestroy?.(this.elementId);
|
|
545
554
|
}
|
|
546
555
|
/**
|
|
547
|
-
*
|
|
556
|
+
* Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.
|
|
548
557
|
*
|
|
549
|
-
* @param patch -
|
|
550
|
-
* @returns
|
|
551
|
-
* @throws
|
|
558
|
+
* @param patch - Configuration delta.
|
|
559
|
+
* @returns Resolves after successfully writing back the local options.
|
|
560
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
552
561
|
*/
|
|
553
562
|
async update(patch) {
|
|
554
563
|
this.assertAlive();
|
|
@@ -562,26 +571,26 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
562
571
|
}
|
|
563
572
|
}
|
|
564
573
|
/**
|
|
565
|
-
*
|
|
574
|
+
* Register an event handler (passed through to the transport, dispatched by event name).
|
|
566
575
|
*
|
|
567
|
-
* @param event -
|
|
568
|
-
* @param handler -
|
|
569
|
-
* @throws
|
|
576
|
+
* @param event - Event name.
|
|
577
|
+
* @param handler - Callback.
|
|
578
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
570
579
|
*/
|
|
571
580
|
on(event, handler) {
|
|
572
581
|
this.assertAlive();
|
|
573
582
|
this.transport.on(event, handler);
|
|
574
583
|
}
|
|
575
584
|
/**
|
|
576
|
-
*
|
|
585
|
+
* Unregister an event handler (still safe to call after destruction).
|
|
577
586
|
*
|
|
578
|
-
* @param event -
|
|
579
|
-
* @param handler -
|
|
587
|
+
* @param event - Event name.
|
|
588
|
+
* @param handler - The previously registered callback.
|
|
580
589
|
*/
|
|
581
590
|
off(event, handler) {
|
|
582
591
|
this.transport.off(event, handler);
|
|
583
592
|
}
|
|
584
|
-
/**
|
|
593
|
+
/** Install input-class action methods (automatically called after construction). */
|
|
585
594
|
installInputActions() {
|
|
586
595
|
if (!INPUT_TYPES.includes(this.type)) {
|
|
587
596
|
return;
|
|
@@ -591,10 +600,10 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
591
600
|
this.clear = () => this.sendAction("clear");
|
|
592
601
|
}
|
|
593
602
|
/**
|
|
594
|
-
*
|
|
603
|
+
* expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.
|
|
595
604
|
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
605
|
+
* This event payload has no elementId; express is a singleton (guaranteed by CheckoutInstance),
|
|
606
|
+
* so there is no need to filter by elementId. Non-express types do not subscribe.
|
|
598
607
|
*/
|
|
599
608
|
installExpressAvailability() {
|
|
600
609
|
if (this.type !== "expressCheckout") {
|
|
@@ -612,18 +621,18 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
612
621
|
this.availabilityHandler = handler;
|
|
613
622
|
this.transport.on("expressAvailabilityChange", handler);
|
|
614
623
|
}
|
|
615
|
-
/**
|
|
624
|
+
/** Send one input-class action command (synchronously throws INSTANCE_DESTROYED after destruction). */
|
|
616
625
|
sendAction(name) {
|
|
617
626
|
this.assertAlive();
|
|
618
627
|
return this.transport.request(name, { elementId: this.elementId }).then(() => void 0);
|
|
619
628
|
}
|
|
620
|
-
/**
|
|
629
|
+
/** Throw INSTANCE_DESTROYED if already destroyed. */
|
|
621
630
|
assertAlive() {
|
|
622
631
|
if (this.status === "destroyed") {
|
|
623
632
|
throw makeError2(import_checkout_sdk_protocol3.ErrorCode.INSTANCE_DESTROYED, "Checkout instance has been destroyed", this.elementId);
|
|
624
633
|
}
|
|
625
634
|
}
|
|
626
|
-
/**
|
|
635
|
+
/** Resolve the mount container (the selector must uniquely match a single element). */
|
|
627
636
|
resolveContainer(selectorOrEl) {
|
|
628
637
|
if (typeof selectorOrEl !== "string") {
|
|
629
638
|
return selectorOrEl;
|
|
@@ -636,26 +645,34 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
636
645
|
return node instanceof HTMLElement ? node : null;
|
|
637
646
|
}
|
|
638
647
|
/**
|
|
639
|
-
*
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
* `&appearance=<encodeURIComponent(JSON.stringify(appearance))
|
|
643
|
-
*
|
|
644
|
-
*
|
|
645
|
-
*
|
|
648
|
+
* Resolve the iframe src: split fields go to /sdk/field/, the rest to /sdk/element/, with
|
|
649
|
+
* checkoutId and elementId; if an initial colorScheme is provided, append `&colorScheme=<value>`
|
|
650
|
+
* (no flash on the first frame); if the initial appearance contains variables or rules, append
|
|
651
|
+
* `&appearance=<encodeURIComponent(JSON.stringify(appearance))>` (takes effect on the first
|
|
652
|
+
* frame, no flash). An appearance containing only colorScheme (no variables/rules) is not
|
|
653
|
+
* appended, to avoid an empty appearance string. For expressCheckout, additionally append the
|
|
654
|
+
* wallet toggles / per-wallet buttonType / global buttonHeight (see {@link resolveExpressQuery}).
|
|
646
655
|
*
|
|
647
|
-
* elementId
|
|
648
|
-
*
|
|
656
|
+
* elementId is sent with the src so the hosted child echoes it verbatim in iframe-resize
|
|
657
|
+
* messages, letting the parent window dispatch the height per handle among multiple iframes on
|
|
658
|
+
* the same page.
|
|
649
659
|
*/
|
|
650
660
|
resolveSrc() {
|
|
651
661
|
const segment = FIELD_TYPES.includes(this.type) ? "field" : "element";
|
|
652
662
|
let query = `?checkoutId=${encodeURIComponent(this.checkoutId)}`;
|
|
653
663
|
query += `&elementId=${encodeURIComponent(this.elementId)}`;
|
|
664
|
+
if (this.type === "address") {
|
|
665
|
+
const mode = this.options?.mode;
|
|
666
|
+
if (mode) {
|
|
667
|
+
query += `&mode=${encodeURIComponent(mode)}`;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
654
670
|
if (this.colorScheme) {
|
|
655
671
|
query += `&colorScheme=${encodeURIComponent(this.colorScheme)}`;
|
|
656
672
|
}
|
|
657
|
-
|
|
658
|
-
|
|
673
|
+
const effectiveAppearance = this.perElementAppearance() ?? this.appearance;
|
|
674
|
+
if (effectiveAppearance && (effectiveAppearance.variables || effectiveAppearance.rules)) {
|
|
675
|
+
query += `&appearance=${encodeURIComponent(JSON.stringify(effectiveAppearance))}`;
|
|
659
676
|
}
|
|
660
677
|
if (this.type === "expressCheckout") {
|
|
661
678
|
query += this.resolveExpressQuery();
|
|
@@ -663,16 +680,26 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
663
680
|
return `${this.hostOrigin}/sdk/${segment}/${this.type}${query}`;
|
|
664
681
|
}
|
|
665
682
|
/**
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
683
|
+
* Extract a per-element appearance (from the Element's own options, i.e. the second
|
|
684
|
+
* argument of create(type, options)). Returns undefined when absent. Used so per-element
|
|
685
|
+
* customization (e.g. CardFieldElementOptions.appearance) is honored on the first frame.
|
|
686
|
+
*/
|
|
687
|
+
perElementAppearance() {
|
|
688
|
+
const opts = this.options;
|
|
689
|
+
return opts?.appearance;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Build the expressCheckout-specific query fragment (only called for expressCheckout):
|
|
693
|
+
* - a wallet explicitly set to `false` → `&applePay=false` / `&googlePay=false` (force hidden,
|
|
694
|
+
* preserving the existing toggle semantics; `true` / absent → automatic, no toggle appended);
|
|
695
|
+
* - a wallet configured in object form containing `buttonType` → `&applePayButtonType=<v>` /
|
|
696
|
+
* `&googlePayButtonType=<v>` (boolean toggle / absent → not appended);
|
|
697
|
+
* - global `buttonHeight` (a finite number) → `&buttonHeight=<n>`.
|
|
672
698
|
*
|
|
673
|
-
*
|
|
699
|
+
* Appended only when there is a value; empty strings are not appended. This fragment never
|
|
700
|
+
* touches card values and only carries display configuration.
|
|
674
701
|
*
|
|
675
|
-
* @returns
|
|
702
|
+
* @returns A query fragment starting with `&` (returns an empty string when nothing is appendable).
|
|
676
703
|
*/
|
|
677
704
|
resolveExpressQuery() {
|
|
678
705
|
const options = this.options;
|
|
@@ -700,13 +727,14 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
700
727
|
return query;
|
|
701
728
|
}
|
|
702
729
|
/**
|
|
703
|
-
*
|
|
704
|
-
* `contentWindow`
|
|
705
|
-
*
|
|
730
|
+
* Install the iframe auto-height listener: subscribe to window 'message', only trust a valid
|
|
731
|
+
* {@link IframeResizeMessage} from this iframe's `contentWindow` with origin === hostOrigin, and
|
|
732
|
+
* when the elementId matches this handle, adjust the outer iframe's height to the content's real
|
|
733
|
+
* height (width stays 100%).
|
|
706
734
|
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
*
|
|
735
|
+
* Security: first verify `event.source === iframe.contentWindow` (the key source check), then
|
|
736
|
+
* verify `event.origin === hostOrigin` (a stricter same-origin check), to prevent cross-origin forgery.
|
|
737
|
+
* Height deduplication: skip if identical to the last one, to avoid redundant style writes.
|
|
710
738
|
*/
|
|
711
739
|
installResizeListener() {
|
|
712
740
|
const handler = (event) => {
|
|
@@ -735,7 +763,7 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
735
763
|
this.resizeHandler = handler;
|
|
736
764
|
window.addEventListener("message", handler);
|
|
737
765
|
}
|
|
738
|
-
/**
|
|
766
|
+
/** Unbind the iframe auto-height listener and reset the deduplication state (idempotent). */
|
|
739
767
|
teardownResizeListener() {
|
|
740
768
|
if (this.resizeHandler) {
|
|
741
769
|
window.removeEventListener("message", this.resizeHandler);
|
|
@@ -743,7 +771,7 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
743
771
|
}
|
|
744
772
|
this.lastHeight = null;
|
|
745
773
|
}
|
|
746
|
-
/**
|
|
774
|
+
/** Remove the iframe DOM node (if present) and unbind the auto-height listener. */
|
|
747
775
|
removeIframe() {
|
|
748
776
|
this.teardownResizeListener();
|
|
749
777
|
if (this.iframe && this.iframe.parentNode) {
|
|
@@ -751,7 +779,7 @@ var CheckoutElement = class _CheckoutElement {
|
|
|
751
779
|
}
|
|
752
780
|
this.iframe = null;
|
|
753
781
|
}
|
|
754
|
-
/**
|
|
782
|
+
/** Factory: equivalent to `new CheckoutElement(opts)` (a unified entry for CheckoutInstance). */
|
|
755
783
|
static create(opts) {
|
|
756
784
|
return new _CheckoutElement(opts);
|
|
757
785
|
}
|
|
@@ -774,14 +802,16 @@ function makeError3(code, message, category = "configuration") {
|
|
|
774
802
|
}
|
|
775
803
|
var CheckoutInstance = class {
|
|
776
804
|
constructor(opts) {
|
|
777
|
-
/**
|
|
805
|
+
/** Created Elements (indexed by elementId). */
|
|
778
806
|
/**
|
|
779
|
-
*
|
|
780
|
-
*
|
|
781
|
-
* {@link
|
|
807
|
+
* The **single source of truth** for the mutual-exclusion registry: elementId → the created
|
|
808
|
+
* CheckoutElement. Mutual-exclusion decisions are derived live from this Map (see
|
|
809
|
+
* {@link assertNoConflict}); an element's `destroy()` removes itself from this Map via
|
|
810
|
+
* {@link releaseElement} to free the slot (allowing a mutually-exclusive Element of the same
|
|
811
|
+
* kind to be recreated after destruction).
|
|
782
812
|
*/
|
|
783
813
|
this.elements = /* @__PURE__ */ new Map();
|
|
784
|
-
/**
|
|
814
|
+
/** The current pending confirm (at most one at a time). */
|
|
785
815
|
this.pendingConfirm = null;
|
|
786
816
|
this.destroyed = false;
|
|
787
817
|
this.checkoutId = opts.checkoutId;
|
|
@@ -797,17 +827,17 @@ var CheckoutInstance = class {
|
|
|
797
827
|
this.transport.on("paymentResult", this.paymentResultHandler);
|
|
798
828
|
}
|
|
799
829
|
/**
|
|
800
|
-
*
|
|
830
|
+
* Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).
|
|
801
831
|
*
|
|
802
|
-
*
|
|
803
|
-
* -
|
|
804
|
-
* -
|
|
805
|
-
* - `expressCheckout`
|
|
832
|
+
* Mutual-exclusion rules (spec 6):
|
|
833
|
+
* - The combined `card` and any split field (cardNumber/cardExpiry/cardCvc) are mutually exclusive.
|
|
834
|
+
* - An address with the same `mode` (billing/shipping) cannot be duplicated.
|
|
835
|
+
* - `expressCheckout` is a singleton.
|
|
806
836
|
*
|
|
807
|
-
* @param type - Element
|
|
808
|
-
* @param options -
|
|
809
|
-
* @returns
|
|
810
|
-
* @throws
|
|
837
|
+
* @param type - Element type.
|
|
838
|
+
* @param options - Initial Element configuration.
|
|
839
|
+
* @returns The newly created CheckoutElement.
|
|
840
|
+
* @throws INSTANCE_DESTROYED when already destroyed; ELEMENT_CONFLICT on a mutual-exclusion conflict.
|
|
811
841
|
*/
|
|
812
842
|
create(type, options) {
|
|
813
843
|
this.assertAlive();
|
|
@@ -829,12 +859,13 @@ var CheckoutInstance = class {
|
|
|
829
859
|
return element;
|
|
830
860
|
}
|
|
831
861
|
/**
|
|
832
|
-
*
|
|
833
|
-
*
|
|
862
|
+
* Initiate a payment confirmation. Generates an operationId, sends the confirm command to obtain
|
|
863
|
+
* accepted, and the returned Promise is fulfilled by a later paymentResult event with the same
|
|
864
|
+
* operationId. Calling again while in progress → CHECKOUT_BUSY.
|
|
834
865
|
*
|
|
835
|
-
* @param opts -
|
|
836
|
-
* @returns
|
|
837
|
-
* @throws
|
|
866
|
+
* @param opts - Optional parameters such as billing/shipping address, name, email, returnUrl.
|
|
867
|
+
* @returns The terminal PaymentOutcome.
|
|
868
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed; rejects CHECKOUT_BUSY when in progress.
|
|
838
869
|
*/
|
|
839
870
|
confirm(opts) {
|
|
840
871
|
if (this.destroyed) {
|
|
@@ -860,10 +891,10 @@ var CheckoutInstance = class {
|
|
|
860
891
|
});
|
|
861
892
|
}
|
|
862
893
|
/**
|
|
863
|
-
*
|
|
894
|
+
* Get the latest session view (passes through the getSession command and writes back the local snapshot).
|
|
864
895
|
*
|
|
865
|
-
* @returns
|
|
866
|
-
* @throws
|
|
896
|
+
* @returns The latest PublicSessionView.
|
|
897
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
867
898
|
*/
|
|
868
899
|
async getSession() {
|
|
869
900
|
this.assertAlive();
|
|
@@ -872,10 +903,10 @@ var CheckoutInstance = class {
|
|
|
872
903
|
return resp;
|
|
873
904
|
}
|
|
874
905
|
/**
|
|
875
|
-
*
|
|
906
|
+
* Refresh the session (passes through the refresh command and writes back the local snapshot).
|
|
876
907
|
*
|
|
877
|
-
* @returns
|
|
878
|
-
* @throws
|
|
908
|
+
* @returns The refreshed PublicSessionView.
|
|
909
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
879
910
|
*/
|
|
880
911
|
async refresh() {
|
|
881
912
|
this.assertAlive();
|
|
@@ -884,20 +915,20 @@ var CheckoutInstance = class {
|
|
|
884
915
|
return resp;
|
|
885
916
|
}
|
|
886
917
|
/**
|
|
887
|
-
*
|
|
918
|
+
* Query the payment status (returns TxnStatus as-is, no transformation).
|
|
888
919
|
*
|
|
889
|
-
* @returns txnStatus
|
|
890
|
-
* @throws
|
|
920
|
+
* @returns txnStatus (may include txnId).
|
|
921
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
891
922
|
*/
|
|
892
923
|
async getPaymentStatus() {
|
|
893
924
|
this.assertAlive();
|
|
894
925
|
return await this.transport.request("getPaymentStatus", {});
|
|
895
926
|
}
|
|
896
927
|
/**
|
|
897
|
-
*
|
|
928
|
+
* Update the checkout appearance/locale (does not change the session or the amount).
|
|
898
929
|
*
|
|
899
|
-
* @param patch - locale / appearance
|
|
900
|
-
* @throws
|
|
930
|
+
* @param patch - locale / appearance delta.
|
|
931
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
901
932
|
*/
|
|
902
933
|
async updateCheckout(patch) {
|
|
903
934
|
this.assertAlive();
|
|
@@ -905,29 +936,30 @@ var CheckoutInstance = class {
|
|
|
905
936
|
await this.transport.request("updateCheckout", payload);
|
|
906
937
|
}
|
|
907
938
|
/**
|
|
908
|
-
*
|
|
939
|
+
* Register a Checkout-level event handler (passes through the transport).
|
|
909
940
|
*
|
|
910
|
-
* @param event -
|
|
911
|
-
* @param handler -
|
|
912
|
-
* @throws
|
|
941
|
+
* @param event - Event name.
|
|
942
|
+
* @param handler - Callback.
|
|
943
|
+
* @throws INSTANCE_DESTROYED when destroyed.
|
|
913
944
|
*/
|
|
914
945
|
on(event, handler) {
|
|
915
946
|
this.assertAlive();
|
|
916
947
|
this.transport.on(event, handler);
|
|
917
948
|
}
|
|
918
949
|
/**
|
|
919
|
-
*
|
|
950
|
+
* Unregister an event handler (safe to call after destruction).
|
|
920
951
|
*
|
|
921
|
-
* @param event -
|
|
922
|
-
* @param handler -
|
|
952
|
+
* @param event - Event name.
|
|
953
|
+
* @param handler - The previously registered callback.
|
|
923
954
|
*/
|
|
924
955
|
off(event, handler) {
|
|
925
956
|
this.transport.off(event, handler);
|
|
926
957
|
}
|
|
927
958
|
/**
|
|
928
|
-
*
|
|
929
|
-
*
|
|
930
|
-
*
|
|
959
|
+
* Destroy the instance: idempotent. Releases all Elements, unbinds the paymentResult listener,
|
|
960
|
+
* rejects the pending confirm with INSTANCE_DESTROYED, sends the destroy command and destroys
|
|
961
|
+
* the transport. Any late paymentResult afterwards is ignored and does not overwrite an already
|
|
962
|
+
* settled Promise.
|
|
931
963
|
*/
|
|
932
964
|
destroy() {
|
|
933
965
|
if (this.destroyed) {
|
|
@@ -948,13 +980,13 @@ var CheckoutInstance = class {
|
|
|
948
980
|
void Promise.resolve(this.transport.request("destroy", {})).catch(() => void 0);
|
|
949
981
|
this.transport.destroy();
|
|
950
982
|
}
|
|
951
|
-
/**
|
|
983
|
+
/** Throw INSTANCE_DESTROYED if already destroyed. */
|
|
952
984
|
assertAlive() {
|
|
953
985
|
if (this.destroyed) {
|
|
954
986
|
throw makeError3(import_checkout_sdk_protocol4.ErrorCode.INSTANCE_DESTROYED, "Checkout instance has been destroyed", "runtime");
|
|
955
987
|
}
|
|
956
988
|
}
|
|
957
|
-
/**
|
|
989
|
+
/** Mutual-exclusion check: throws ELEMENT_CONFLICT on conflict (derived live from the elements Map, does not mutate the registry). */
|
|
958
990
|
assertNoConflict(type, options) {
|
|
959
991
|
const existing = [...this.elements.values()];
|
|
960
992
|
const has = (t) => existing.some((e) => e.type === t);
|
|
@@ -1004,9 +1036,10 @@ var CheckoutInstance = class {
|
|
|
1004
1036
|
}
|
|
1005
1037
|
}
|
|
1006
1038
|
/**
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1009
|
-
*
|
|
1039
|
+
* Element `destroy()` callback: remove this elementId from the mutual-exclusion registry (the
|
|
1040
|
+
* elements Map) to free the slot. Idempotent; when the whole instance is destroyed (destroyed
|
|
1041
|
+
* already set) it goes through the unified `elements.clear()` path, so this is a no-op here,
|
|
1042
|
+
* avoiding concurrent modification while iterating elements to call element.destroy().
|
|
1010
1043
|
*/
|
|
1011
1044
|
releaseElement(elementId) {
|
|
1012
1045
|
if (this.destroyed) {
|
|
@@ -1014,7 +1047,7 @@ var CheckoutInstance = class {
|
|
|
1014
1047
|
}
|
|
1015
1048
|
this.elements.delete(elementId);
|
|
1016
1049
|
}
|
|
1017
|
-
/**
|
|
1050
|
+
/** Handle the paymentResult event: fulfill the pending confirm by operationId; late/mismatched ones are ignored. */
|
|
1018
1051
|
handlePaymentResult(payload) {
|
|
1019
1052
|
if (this.destroyed) {
|
|
1020
1053
|
return;
|
|
@@ -1041,9 +1074,10 @@ var CheckoutInstance = class {
|
|
|
1041
1074
|
// src/config/env.ts
|
|
1042
1075
|
var HOST_ORIGINS = {
|
|
1043
1076
|
dev: "https://checkout.sunbay-dev.com",
|
|
1077
|
+
test: "https://checkout.sunbay-uat.us",
|
|
1044
1078
|
prod: "https://checkout.sunbay.us"
|
|
1045
1079
|
};
|
|
1046
|
-
function resolveHostOrigin(env = "
|
|
1080
|
+
function resolveHostOrigin(env = "prod", override) {
|
|
1047
1081
|
if (override) {
|
|
1048
1082
|
return override;
|
|
1049
1083
|
}
|
|
@@ -1137,7 +1171,7 @@ if (typeof window !== "undefined") {
|
|
|
1137
1171
|
const w = window;
|
|
1138
1172
|
w.SunbayCheckout = { ...w.SunbayCheckout ?? {}, createCheckout };
|
|
1139
1173
|
}
|
|
1140
|
-
var version = true ? "0.0.
|
|
1174
|
+
var version = true ? "0.0.3" : "0.0.0";
|
|
1141
1175
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1142
1176
|
0 && (module.exports = {
|
|
1143
1177
|
CheckoutElement,
|