@sunbay/checkout-sdk-js 0.0.2 → 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.global.js
CHANGED
|
@@ -169,8 +169,9 @@ var SunbayCheckout = (() => {
|
|
|
169
169
|
this.eventHandlers = /* @__PURE__ */ new Map();
|
|
170
170
|
this.helloHandler = null;
|
|
171
171
|
/**
|
|
172
|
-
*
|
|
173
|
-
* main
|
|
172
|
+
* Runtime id negotiated during the handshake; empty string before injection. Outbound request
|
|
173
|
+
* envelopes are filled from it; the main endpoint only processes requests whose runtimeId
|
|
174
|
+
* matches its own, and an empty string is silently dropped.
|
|
174
175
|
*/
|
|
175
176
|
this.runtimeId = "";
|
|
176
177
|
this.destroyed = false;
|
|
@@ -183,9 +184,9 @@ var SunbayCheckout = (() => {
|
|
|
183
184
|
window.addEventListener("message", this.messageListener);
|
|
184
185
|
}
|
|
185
186
|
/**
|
|
186
|
-
*
|
|
187
|
+
* Create the hidden, 0-size main iframe and append it to document.body.
|
|
187
188
|
*
|
|
188
|
-
* @returns
|
|
189
|
+
* @returns The mounted iframe element.
|
|
189
190
|
* @example
|
|
190
191
|
* const iframe = transport.createHiddenIframe();
|
|
191
192
|
*/
|
|
@@ -207,11 +208,11 @@ var SunbayCheckout = (() => {
|
|
|
207
208
|
return iframe;
|
|
208
209
|
}
|
|
209
210
|
/**
|
|
210
|
-
*
|
|
211
|
+
* Send one request envelope and pair the response by requestId.
|
|
211
212
|
*
|
|
212
|
-
* @param name -
|
|
213
|
-
* @param payload -
|
|
214
|
-
* @returns
|
|
213
|
+
* @param name - Command name.
|
|
214
|
+
* @param payload - Command payload (JSON-serializable).
|
|
215
|
+
* @returns The response data; on failure (ok:false / timeout / destroy) rejects with a CheckoutError.
|
|
215
216
|
* @example
|
|
216
217
|
* const session = await transport.request('getSession', {});
|
|
217
218
|
*/
|
|
@@ -252,10 +253,10 @@ var SunbayCheckout = (() => {
|
|
|
252
253
|
});
|
|
253
254
|
}
|
|
254
255
|
/**
|
|
255
|
-
*
|
|
256
|
+
* Register an event handler (dispatches the payload of kind:'event' envelopes).
|
|
256
257
|
*
|
|
257
|
-
* @param event -
|
|
258
|
-
* @param handler -
|
|
258
|
+
* @param event - Event name.
|
|
259
|
+
* @param handler - Callback.
|
|
259
260
|
*/
|
|
260
261
|
on(event, handler) {
|
|
261
262
|
let set = this.eventHandlers.get(event);
|
|
@@ -266,10 +267,10 @@ var SunbayCheckout = (() => {
|
|
|
266
267
|
set.add(handler);
|
|
267
268
|
}
|
|
268
269
|
/**
|
|
269
|
-
*
|
|
270
|
+
* Unregister an event handler.
|
|
270
271
|
*
|
|
271
|
-
* @param event -
|
|
272
|
-
* @param handler -
|
|
272
|
+
* @param event - Event name.
|
|
273
|
+
* @param handler - The previously registered callback.
|
|
273
274
|
*/
|
|
274
275
|
off(event, handler) {
|
|
275
276
|
const set = this.eventHandlers.get(event);
|
|
@@ -281,24 +282,26 @@ var SunbayCheckout = (() => {
|
|
|
281
282
|
}
|
|
282
283
|
}
|
|
283
284
|
/**
|
|
284
|
-
*
|
|
285
|
+
* Register the handshake (RuntimeHello) callback. The hello phase has no checkoutId yet and is
|
|
286
|
+
* handled separately.
|
|
285
287
|
*
|
|
286
|
-
* @param handler -
|
|
288
|
+
* @param handler - Called when a hello is received.
|
|
287
289
|
*/
|
|
288
290
|
onHello(handler) {
|
|
289
291
|
this.helloHandler = handler;
|
|
290
292
|
}
|
|
291
293
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
+
* Inject the runtime id negotiated during the handshake. Must be called before sending the first
|
|
295
|
+
* request (initialize), otherwise the outbound envelope's runtimeId is an empty string, gets
|
|
296
|
+
* silently dropped by the main endpoint, and the request times out.
|
|
294
297
|
*
|
|
295
|
-
* @param runtimeId -
|
|
298
|
+
* @param runtimeId - The runtimeId from RuntimeHello.
|
|
296
299
|
*/
|
|
297
300
|
setRuntimeId(runtimeId) {
|
|
298
301
|
this.runtimeId = runtimeId;
|
|
299
302
|
}
|
|
300
303
|
/**
|
|
301
|
-
*
|
|
304
|
+
* Destroy: remove the iframe, clear listeners, and reject pending requests with INSTANCE_DESTROYED.
|
|
302
305
|
*/
|
|
303
306
|
destroy() {
|
|
304
307
|
if (this.destroyed) {
|
|
@@ -318,7 +321,7 @@ var SunbayCheckout = (() => {
|
|
|
318
321
|
}
|
|
319
322
|
this.iframe = null;
|
|
320
323
|
}
|
|
321
|
-
/**
|
|
324
|
+
/** Resolve the timeout by command name. */
|
|
322
325
|
resolveTimeout(name) {
|
|
323
326
|
if (name === "initialize") {
|
|
324
327
|
return this.timeouts.initialize;
|
|
@@ -326,20 +329,22 @@ var SunbayCheckout = (() => {
|
|
|
326
329
|
return this.timeouts.default;
|
|
327
330
|
}
|
|
328
331
|
/**
|
|
329
|
-
*
|
|
332
|
+
* Point-to-point port relay (parent-window relay, Stripe controller model).
|
|
330
333
|
*
|
|
331
|
-
*
|
|
332
|
-
* transfer MessagePort
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
334
|
+
* Sibling iframes (main and the various Element iframes) cannot obtain each other's window
|
|
335
|
+
* references, so they cannot transfer a MessagePort directly. This layer (the merchant page,
|
|
336
|
+
* the only party that can reach both ends and knows their origin) does the following when it
|
|
337
|
+
* receives an `element-online` from some Element iframe:
|
|
338
|
+
* 1. Create a `MessageChannel`;
|
|
339
|
+
* 2. `port1` → main iframe (`relay-port`, targetOrigin=hostOrigin);
|
|
340
|
+
* 3. `port2` → the originating Element iframe (i.e. `event.source`, targetOrigin=hostOrigin).
|
|
337
341
|
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
342
|
+
* Both ends' origin is hostOrigin, so targetOrigin is always correct (and not dropped across
|
|
343
|
+
* origins). This layer only carries **empty ports** and never touches card values (which are
|
|
344
|
+
* transmitted end-to-end after the port is established).
|
|
340
345
|
*
|
|
341
|
-
* @param event
|
|
342
|
-
* @returns
|
|
346
|
+
* @param event Inbound message (origin already verified as hostOrigin)
|
|
347
|
+
* @returns Whether it was handled as element-online (if so, the caller should return)
|
|
343
348
|
*/
|
|
344
349
|
tryHandleElementOnline(event) {
|
|
345
350
|
const data = event.data;
|
|
@@ -368,7 +373,7 @@ var SunbayCheckout = (() => {
|
|
|
368
373
|
elementWin.postMessage(relayMsg, this.hostOrigin, [channel.port2]);
|
|
369
374
|
return true;
|
|
370
375
|
}
|
|
371
|
-
/**
|
|
376
|
+
/** Handle an inbound message: filter origin, recognize relay / hello / Envelope and dispatch. */
|
|
372
377
|
handleMessage(event) {
|
|
373
378
|
if (this.destroyed) {
|
|
374
379
|
return;
|
|
@@ -409,7 +414,7 @@ var SunbayCheckout = (() => {
|
|
|
409
414
|
this.handleEvent(envelope);
|
|
410
415
|
}
|
|
411
416
|
}
|
|
412
|
-
/**
|
|
417
|
+
/** Dispatch a response to the paired pending request. */
|
|
413
418
|
handleResponse(envelope) {
|
|
414
419
|
const entry = this.pending.get(envelope.requestId);
|
|
415
420
|
if (!entry) {
|
|
@@ -423,7 +428,7 @@ var SunbayCheckout = (() => {
|
|
|
423
428
|
entry.reject(envelope.error);
|
|
424
429
|
}
|
|
425
430
|
}
|
|
426
|
-
/**
|
|
431
|
+
/** Dispatch an event to the registered handlers. */
|
|
427
432
|
handleEvent(envelope) {
|
|
428
433
|
const set = this.eventHandlers.get(envelope.name);
|
|
429
434
|
if (!set) {
|
|
@@ -465,17 +470,18 @@ var SunbayCheckout = (() => {
|
|
|
465
470
|
this.status = "created";
|
|
466
471
|
this.container = null;
|
|
467
472
|
this.iframe = null;
|
|
468
|
-
/** mount
|
|
473
|
+
/** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */
|
|
469
474
|
this.readyHandler = null;
|
|
470
475
|
this.loadErrorHandler = null;
|
|
471
|
-
/**
|
|
476
|
+
/** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */
|
|
472
477
|
this.availabilityHandler = null;
|
|
473
478
|
/**
|
|
474
|
-
* iframe
|
|
475
|
-
*
|
|
479
|
+
* iframe auto-height: the window 'message' listener that stays resident during mount (unbound
|
|
480
|
+
* when the iframe disappears/is destroyed). Keeps a reference so removeEventListener can be
|
|
481
|
+
* called; null means not listening.
|
|
476
482
|
*/
|
|
477
483
|
this.resizeHandler = null;
|
|
478
|
-
/**
|
|
484
|
+
/** The most recently applied iframe height (px), used for deduplication to avoid redundant style writes; null means not yet set. */
|
|
479
485
|
this.lastHeight = null;
|
|
480
486
|
this.elementId = opts.elementId;
|
|
481
487
|
this.type = opts.type;
|
|
@@ -490,26 +496,28 @@ var SunbayCheckout = (() => {
|
|
|
490
496
|
this.installInputActions();
|
|
491
497
|
this.installExpressAvailability();
|
|
492
498
|
}
|
|
493
|
-
/**
|
|
499
|
+
/** The current status (read-only snapshot, for external/test observation). */
|
|
494
500
|
getStatus() {
|
|
495
501
|
return this.status;
|
|
496
502
|
}
|
|
497
503
|
/**
|
|
498
|
-
*
|
|
504
|
+
* expressCheckout only: return the current snapshot of each wallet's availability (always
|
|
505
|
+
* undefined for other types).
|
|
499
506
|
*
|
|
500
|
-
*
|
|
507
|
+
* The initial value comes from elementReady's availableWallets, then is updated by
|
|
508
|
+
* expressAvailabilityChange.
|
|
501
509
|
*
|
|
502
|
-
* @returns
|
|
510
|
+
* @returns The availability snapshot; undefined when not yet ready or non-express.
|
|
503
511
|
*/
|
|
504
512
|
getAvailableWallets() {
|
|
505
513
|
return this.availableWallets;
|
|
506
514
|
}
|
|
507
515
|
/**
|
|
508
|
-
*
|
|
516
|
+
* Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.
|
|
509
517
|
*
|
|
510
|
-
* @param selectorOrEl - CSS
|
|
511
|
-
* @returns elementReady
|
|
512
|
-
* @throws
|
|
518
|
+
* @param selectorOrEl - A CSS selector or an HTMLElement container; the container must be unique and empty.
|
|
519
|
+
* @returns Resolves after elementReady; on failure removes the node and rejects (loadError / INVALID_OPTIONS).
|
|
520
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
513
521
|
* @example
|
|
514
522
|
* await element.mount('#card-field');
|
|
515
523
|
*/
|
|
@@ -548,6 +556,7 @@ var SunbayCheckout = (() => {
|
|
|
548
556
|
iframe.style.border = "0";
|
|
549
557
|
iframe.style.width = "100%";
|
|
550
558
|
iframe.style.display = "block";
|
|
559
|
+
iframe.setAttribute("scrolling", "no");
|
|
551
560
|
container.appendChild(iframe);
|
|
552
561
|
this.iframe = iframe;
|
|
553
562
|
this.installResizeListener();
|
|
@@ -607,10 +616,10 @@ var SunbayCheckout = (() => {
|
|
|
607
616
|
});
|
|
608
617
|
}
|
|
609
618
|
/**
|
|
610
|
-
*
|
|
619
|
+
* Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.
|
|
611
620
|
*
|
|
612
|
-
* @returns
|
|
613
|
-
* @throws
|
|
621
|
+
* @returns Resolves on completion (resolves directly when not mounted).
|
|
622
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
614
623
|
*/
|
|
615
624
|
async unmount() {
|
|
616
625
|
this.assertAlive();
|
|
@@ -626,7 +635,7 @@ var SunbayCheckout = (() => {
|
|
|
626
635
|
}
|
|
627
636
|
}
|
|
628
637
|
/**
|
|
629
|
-
*
|
|
638
|
+
* Destroy the handle: idempotent; remove the iframe; afterwards any non-destroy/off call throws INSTANCE_DESTROYED.
|
|
630
639
|
*/
|
|
631
640
|
destroy() {
|
|
632
641
|
if (this.status === "destroyed") {
|
|
@@ -650,11 +659,11 @@ var SunbayCheckout = (() => {
|
|
|
650
659
|
this.onDestroy?.(this.elementId);
|
|
651
660
|
}
|
|
652
661
|
/**
|
|
653
|
-
*
|
|
662
|
+
* Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.
|
|
654
663
|
*
|
|
655
|
-
* @param patch -
|
|
656
|
-
* @returns
|
|
657
|
-
* @throws
|
|
664
|
+
* @param patch - Configuration delta.
|
|
665
|
+
* @returns Resolves after successfully writing back the local options.
|
|
666
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
658
667
|
*/
|
|
659
668
|
async update(patch) {
|
|
660
669
|
this.assertAlive();
|
|
@@ -668,26 +677,26 @@ var SunbayCheckout = (() => {
|
|
|
668
677
|
}
|
|
669
678
|
}
|
|
670
679
|
/**
|
|
671
|
-
*
|
|
680
|
+
* Register an event handler (passed through to the transport, dispatched by event name).
|
|
672
681
|
*
|
|
673
|
-
* @param event -
|
|
674
|
-
* @param handler -
|
|
675
|
-
* @throws
|
|
682
|
+
* @param event - Event name.
|
|
683
|
+
* @param handler - Callback.
|
|
684
|
+
* @throws INSTANCE_DESTROYED when already destroyed.
|
|
676
685
|
*/
|
|
677
686
|
on(event, handler) {
|
|
678
687
|
this.assertAlive();
|
|
679
688
|
this.transport.on(event, handler);
|
|
680
689
|
}
|
|
681
690
|
/**
|
|
682
|
-
*
|
|
691
|
+
* Unregister an event handler (still safe to call after destruction).
|
|
683
692
|
*
|
|
684
|
-
* @param event -
|
|
685
|
-
* @param handler -
|
|
693
|
+
* @param event - Event name.
|
|
694
|
+
* @param handler - The previously registered callback.
|
|
686
695
|
*/
|
|
687
696
|
off(event, handler) {
|
|
688
697
|
this.transport.off(event, handler);
|
|
689
698
|
}
|
|
690
|
-
/**
|
|
699
|
+
/** Install input-class action methods (automatically called after construction). */
|
|
691
700
|
installInputActions() {
|
|
692
701
|
if (!INPUT_TYPES.includes(this.type)) {
|
|
693
702
|
return;
|
|
@@ -697,10 +706,10 @@ var SunbayCheckout = (() => {
|
|
|
697
706
|
this.clear = () => this.sendAction("clear");
|
|
698
707
|
}
|
|
699
708
|
/**
|
|
700
|
-
*
|
|
709
|
+
* expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.
|
|
701
710
|
*
|
|
702
|
-
*
|
|
703
|
-
*
|
|
711
|
+
* This event payload has no elementId; express is a singleton (guaranteed by CheckoutInstance),
|
|
712
|
+
* so there is no need to filter by elementId. Non-express types do not subscribe.
|
|
704
713
|
*/
|
|
705
714
|
installExpressAvailability() {
|
|
706
715
|
if (this.type !== "expressCheckout") {
|
|
@@ -718,18 +727,18 @@ var SunbayCheckout = (() => {
|
|
|
718
727
|
this.availabilityHandler = handler;
|
|
719
728
|
this.transport.on("expressAvailabilityChange", handler);
|
|
720
729
|
}
|
|
721
|
-
/**
|
|
730
|
+
/** Send one input-class action command (synchronously throws INSTANCE_DESTROYED after destruction). */
|
|
722
731
|
sendAction(name) {
|
|
723
732
|
this.assertAlive();
|
|
724
733
|
return this.transport.request(name, { elementId: this.elementId }).then(() => void 0);
|
|
725
734
|
}
|
|
726
|
-
/**
|
|
735
|
+
/** Throw INSTANCE_DESTROYED if already destroyed. */
|
|
727
736
|
assertAlive() {
|
|
728
737
|
if (this.status === "destroyed") {
|
|
729
738
|
throw makeError2(ErrorCode.INSTANCE_DESTROYED, "Checkout instance has been destroyed", this.elementId);
|
|
730
739
|
}
|
|
731
740
|
}
|
|
732
|
-
/**
|
|
741
|
+
/** Resolve the mount container (the selector must uniquely match a single element). */
|
|
733
742
|
resolveContainer(selectorOrEl) {
|
|
734
743
|
if (typeof selectorOrEl !== "string") {
|
|
735
744
|
return selectorOrEl;
|
|
@@ -742,26 +751,34 @@ var SunbayCheckout = (() => {
|
|
|
742
751
|
return node instanceof HTMLElement ? node : null;
|
|
743
752
|
}
|
|
744
753
|
/**
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
*
|
|
748
|
-
* `&appearance=<encodeURIComponent(JSON.stringify(appearance))
|
|
749
|
-
*
|
|
750
|
-
*
|
|
751
|
-
*
|
|
754
|
+
* Resolve the iframe src: split fields go to /sdk/field/, the rest to /sdk/element/, with
|
|
755
|
+
* checkoutId and elementId; if an initial colorScheme is provided, append `&colorScheme=<value>`
|
|
756
|
+
* (no flash on the first frame); if the initial appearance contains variables or rules, append
|
|
757
|
+
* `&appearance=<encodeURIComponent(JSON.stringify(appearance))>` (takes effect on the first
|
|
758
|
+
* frame, no flash). An appearance containing only colorScheme (no variables/rules) is not
|
|
759
|
+
* appended, to avoid an empty appearance string. For expressCheckout, additionally append the
|
|
760
|
+
* wallet toggles / per-wallet buttonType / global buttonHeight (see {@link resolveExpressQuery}).
|
|
752
761
|
*
|
|
753
|
-
* elementId
|
|
754
|
-
*
|
|
762
|
+
* elementId is sent with the src so the hosted child echoes it verbatim in iframe-resize
|
|
763
|
+
* messages, letting the parent window dispatch the height per handle among multiple iframes on
|
|
764
|
+
* the same page.
|
|
755
765
|
*/
|
|
756
766
|
resolveSrc() {
|
|
757
767
|
const segment = FIELD_TYPES.includes(this.type) ? "field" : "element";
|
|
758
768
|
let query = `?checkoutId=${encodeURIComponent(this.checkoutId)}`;
|
|
759
769
|
query += `&elementId=${encodeURIComponent(this.elementId)}`;
|
|
770
|
+
if (this.type === "address") {
|
|
771
|
+
const mode = this.options?.mode;
|
|
772
|
+
if (mode) {
|
|
773
|
+
query += `&mode=${encodeURIComponent(mode)}`;
|
|
774
|
+
}
|
|
775
|
+
}
|
|
760
776
|
if (this.colorScheme) {
|
|
761
777
|
query += `&colorScheme=${encodeURIComponent(this.colorScheme)}`;
|
|
762
778
|
}
|
|
763
|
-
|
|
764
|
-
|
|
779
|
+
const effectiveAppearance = this.perElementAppearance() ?? this.appearance;
|
|
780
|
+
if (effectiveAppearance && (effectiveAppearance.variables || effectiveAppearance.rules)) {
|
|
781
|
+
query += `&appearance=${encodeURIComponent(JSON.stringify(effectiveAppearance))}`;
|
|
765
782
|
}
|
|
766
783
|
if (this.type === "expressCheckout") {
|
|
767
784
|
query += this.resolveExpressQuery();
|
|
@@ -769,16 +786,26 @@ var SunbayCheckout = (() => {
|
|
|
769
786
|
return `${this.hostOrigin}/sdk/${segment}/${this.type}${query}`;
|
|
770
787
|
}
|
|
771
788
|
/**
|
|
772
|
-
*
|
|
773
|
-
*
|
|
774
|
-
*
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
789
|
+
* Extract a per-element appearance (from the Element's own options, i.e. the second
|
|
790
|
+
* argument of create(type, options)). Returns undefined when absent. Used so per-element
|
|
791
|
+
* customization (e.g. CardFieldElementOptions.appearance) is honored on the first frame.
|
|
792
|
+
*/
|
|
793
|
+
perElementAppearance() {
|
|
794
|
+
const opts = this.options;
|
|
795
|
+
return opts?.appearance;
|
|
796
|
+
}
|
|
797
|
+
/**
|
|
798
|
+
* Build the expressCheckout-specific query fragment (only called for expressCheckout):
|
|
799
|
+
* - a wallet explicitly set to `false` → `&applePay=false` / `&googlePay=false` (force hidden,
|
|
800
|
+
* preserving the existing toggle semantics; `true` / absent → automatic, no toggle appended);
|
|
801
|
+
* - a wallet configured in object form containing `buttonType` → `&applePayButtonType=<v>` /
|
|
802
|
+
* `&googlePayButtonType=<v>` (boolean toggle / absent → not appended);
|
|
803
|
+
* - global `buttonHeight` (a finite number) → `&buttonHeight=<n>`.
|
|
778
804
|
*
|
|
779
|
-
*
|
|
805
|
+
* Appended only when there is a value; empty strings are not appended. This fragment never
|
|
806
|
+
* touches card values and only carries display configuration.
|
|
780
807
|
*
|
|
781
|
-
* @returns
|
|
808
|
+
* @returns A query fragment starting with `&` (returns an empty string when nothing is appendable).
|
|
782
809
|
*/
|
|
783
810
|
resolveExpressQuery() {
|
|
784
811
|
const options = this.options;
|
|
@@ -806,13 +833,14 @@ var SunbayCheckout = (() => {
|
|
|
806
833
|
return query;
|
|
807
834
|
}
|
|
808
835
|
/**
|
|
809
|
-
*
|
|
810
|
-
* `contentWindow`
|
|
811
|
-
*
|
|
836
|
+
* Install the iframe auto-height listener: subscribe to window 'message', only trust a valid
|
|
837
|
+
* {@link IframeResizeMessage} from this iframe's `contentWindow` with origin === hostOrigin, and
|
|
838
|
+
* when the elementId matches this handle, adjust the outer iframe's height to the content's real
|
|
839
|
+
* height (width stays 100%).
|
|
812
840
|
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
*
|
|
841
|
+
* Security: first verify `event.source === iframe.contentWindow` (the key source check), then
|
|
842
|
+
* verify `event.origin === hostOrigin` (a stricter same-origin check), to prevent cross-origin forgery.
|
|
843
|
+
* Height deduplication: skip if identical to the last one, to avoid redundant style writes.
|
|
816
844
|
*/
|
|
817
845
|
installResizeListener() {
|
|
818
846
|
const handler = (event) => {
|
|
@@ -841,7 +869,7 @@ var SunbayCheckout = (() => {
|
|
|
841
869
|
this.resizeHandler = handler;
|
|
842
870
|
window.addEventListener("message", handler);
|
|
843
871
|
}
|
|
844
|
-
/**
|
|
872
|
+
/** Unbind the iframe auto-height listener and reset the deduplication state (idempotent). */
|
|
845
873
|
teardownResizeListener() {
|
|
846
874
|
if (this.resizeHandler) {
|
|
847
875
|
window.removeEventListener("message", this.resizeHandler);
|
|
@@ -849,7 +877,7 @@ var SunbayCheckout = (() => {
|
|
|
849
877
|
}
|
|
850
878
|
this.lastHeight = null;
|
|
851
879
|
}
|
|
852
|
-
/**
|
|
880
|
+
/** Remove the iframe DOM node (if present) and unbind the auto-height listener. */
|
|
853
881
|
removeIframe() {
|
|
854
882
|
this.teardownResizeListener();
|
|
855
883
|
if (this.iframe && this.iframe.parentNode) {
|
|
@@ -857,7 +885,7 @@ var SunbayCheckout = (() => {
|
|
|
857
885
|
}
|
|
858
886
|
this.iframe = null;
|
|
859
887
|
}
|
|
860
|
-
/**
|
|
888
|
+
/** Factory: equivalent to `new CheckoutElement(opts)` (a unified entry for CheckoutInstance). */
|
|
861
889
|
static create(opts) {
|
|
862
890
|
return new _CheckoutElement(opts);
|
|
863
891
|
}
|
|
@@ -879,14 +907,16 @@ var SunbayCheckout = (() => {
|
|
|
879
907
|
}
|
|
880
908
|
var CheckoutInstance = class {
|
|
881
909
|
constructor(opts) {
|
|
882
|
-
/**
|
|
910
|
+
/** Created Elements (indexed by elementId). */
|
|
883
911
|
/**
|
|
884
|
-
*
|
|
885
|
-
*
|
|
886
|
-
* {@link
|
|
912
|
+
* The **single source of truth** for the mutual-exclusion registry: elementId → the created
|
|
913
|
+
* CheckoutElement. Mutual-exclusion decisions are derived live from this Map (see
|
|
914
|
+
* {@link assertNoConflict}); an element's `destroy()` removes itself from this Map via
|
|
915
|
+
* {@link releaseElement} to free the slot (allowing a mutually-exclusive Element of the same
|
|
916
|
+
* kind to be recreated after destruction).
|
|
887
917
|
*/
|
|
888
918
|
this.elements = /* @__PURE__ */ new Map();
|
|
889
|
-
/**
|
|
919
|
+
/** The current pending confirm (at most one at a time). */
|
|
890
920
|
this.pendingConfirm = null;
|
|
891
921
|
this.destroyed = false;
|
|
892
922
|
this.checkoutId = opts.checkoutId;
|
|
@@ -902,17 +932,17 @@ var SunbayCheckout = (() => {
|
|
|
902
932
|
this.transport.on("paymentResult", this.paymentResultHandler);
|
|
903
933
|
}
|
|
904
934
|
/**
|
|
905
|
-
*
|
|
935
|
+
* Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).
|
|
906
936
|
*
|
|
907
|
-
*
|
|
908
|
-
* -
|
|
909
|
-
* -
|
|
910
|
-
* - `expressCheckout`
|
|
937
|
+
* Mutual-exclusion rules (spec 6):
|
|
938
|
+
* - The combined `card` and any split field (cardNumber/cardExpiry/cardCvc) are mutually exclusive.
|
|
939
|
+
* - An address with the same `mode` (billing/shipping) cannot be duplicated.
|
|
940
|
+
* - `expressCheckout` is a singleton.
|
|
911
941
|
*
|
|
912
|
-
* @param type - Element
|
|
913
|
-
* @param options -
|
|
914
|
-
* @returns
|
|
915
|
-
* @throws
|
|
942
|
+
* @param type - Element type.
|
|
943
|
+
* @param options - Initial Element configuration.
|
|
944
|
+
* @returns The newly created CheckoutElement.
|
|
945
|
+
* @throws INSTANCE_DESTROYED when already destroyed; ELEMENT_CONFLICT on a mutual-exclusion conflict.
|
|
916
946
|
*/
|
|
917
947
|
create(type, options) {
|
|
918
948
|
this.assertAlive();
|
|
@@ -934,12 +964,13 @@ var SunbayCheckout = (() => {
|
|
|
934
964
|
return element;
|
|
935
965
|
}
|
|
936
966
|
/**
|
|
937
|
-
*
|
|
938
|
-
*
|
|
967
|
+
* Initiate a payment confirmation. Generates an operationId, sends the confirm command to obtain
|
|
968
|
+
* accepted, and the returned Promise is fulfilled by a later paymentResult event with the same
|
|
969
|
+
* operationId. Calling again while in progress → CHECKOUT_BUSY.
|
|
939
970
|
*
|
|
940
|
-
* @param opts -
|
|
941
|
-
* @returns
|
|
942
|
-
* @throws
|
|
971
|
+
* @param opts - Optional parameters such as billing/shipping address, name, email, returnUrl.
|
|
972
|
+
* @returns The terminal PaymentOutcome.
|
|
973
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed; rejects CHECKOUT_BUSY when in progress.
|
|
943
974
|
*/
|
|
944
975
|
confirm(opts) {
|
|
945
976
|
if (this.destroyed) {
|
|
@@ -965,10 +996,10 @@ var SunbayCheckout = (() => {
|
|
|
965
996
|
});
|
|
966
997
|
}
|
|
967
998
|
/**
|
|
968
|
-
*
|
|
999
|
+
* Get the latest session view (passes through the getSession command and writes back the local snapshot).
|
|
969
1000
|
*
|
|
970
|
-
* @returns
|
|
971
|
-
* @throws
|
|
1001
|
+
* @returns The latest PublicSessionView.
|
|
1002
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
972
1003
|
*/
|
|
973
1004
|
async getSession() {
|
|
974
1005
|
this.assertAlive();
|
|
@@ -977,10 +1008,10 @@ var SunbayCheckout = (() => {
|
|
|
977
1008
|
return resp;
|
|
978
1009
|
}
|
|
979
1010
|
/**
|
|
980
|
-
*
|
|
1011
|
+
* Refresh the session (passes through the refresh command and writes back the local snapshot).
|
|
981
1012
|
*
|
|
982
|
-
* @returns
|
|
983
|
-
* @throws
|
|
1013
|
+
* @returns The refreshed PublicSessionView.
|
|
1014
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
984
1015
|
*/
|
|
985
1016
|
async refresh() {
|
|
986
1017
|
this.assertAlive();
|
|
@@ -989,20 +1020,20 @@ var SunbayCheckout = (() => {
|
|
|
989
1020
|
return resp;
|
|
990
1021
|
}
|
|
991
1022
|
/**
|
|
992
|
-
*
|
|
1023
|
+
* Query the payment status (returns TxnStatus as-is, no transformation).
|
|
993
1024
|
*
|
|
994
|
-
* @returns txnStatus
|
|
995
|
-
* @throws
|
|
1025
|
+
* @returns txnStatus (may include txnId).
|
|
1026
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
996
1027
|
*/
|
|
997
1028
|
async getPaymentStatus() {
|
|
998
1029
|
this.assertAlive();
|
|
999
1030
|
return await this.transport.request("getPaymentStatus", {});
|
|
1000
1031
|
}
|
|
1001
1032
|
/**
|
|
1002
|
-
*
|
|
1033
|
+
* Update the checkout appearance/locale (does not change the session or the amount).
|
|
1003
1034
|
*
|
|
1004
|
-
* @param patch - locale / appearance
|
|
1005
|
-
* @throws
|
|
1035
|
+
* @param patch - locale / appearance delta.
|
|
1036
|
+
* @throws rejects INSTANCE_DESTROYED when destroyed.
|
|
1006
1037
|
*/
|
|
1007
1038
|
async updateCheckout(patch) {
|
|
1008
1039
|
this.assertAlive();
|
|
@@ -1010,29 +1041,30 @@ var SunbayCheckout = (() => {
|
|
|
1010
1041
|
await this.transport.request("updateCheckout", payload);
|
|
1011
1042
|
}
|
|
1012
1043
|
/**
|
|
1013
|
-
*
|
|
1044
|
+
* Register a Checkout-level event handler (passes through the transport).
|
|
1014
1045
|
*
|
|
1015
|
-
* @param event -
|
|
1016
|
-
* @param handler -
|
|
1017
|
-
* @throws
|
|
1046
|
+
* @param event - Event name.
|
|
1047
|
+
* @param handler - Callback.
|
|
1048
|
+
* @throws INSTANCE_DESTROYED when destroyed.
|
|
1018
1049
|
*/
|
|
1019
1050
|
on(event, handler) {
|
|
1020
1051
|
this.assertAlive();
|
|
1021
1052
|
this.transport.on(event, handler);
|
|
1022
1053
|
}
|
|
1023
1054
|
/**
|
|
1024
|
-
*
|
|
1055
|
+
* Unregister an event handler (safe to call after destruction).
|
|
1025
1056
|
*
|
|
1026
|
-
* @param event -
|
|
1027
|
-
* @param handler -
|
|
1057
|
+
* @param event - Event name.
|
|
1058
|
+
* @param handler - The previously registered callback.
|
|
1028
1059
|
*/
|
|
1029
1060
|
off(event, handler) {
|
|
1030
1061
|
this.transport.off(event, handler);
|
|
1031
1062
|
}
|
|
1032
1063
|
/**
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
1064
|
+
* Destroy the instance: idempotent. Releases all Elements, unbinds the paymentResult listener,
|
|
1065
|
+
* rejects the pending confirm with INSTANCE_DESTROYED, sends the destroy command and destroys
|
|
1066
|
+
* the transport. Any late paymentResult afterwards is ignored and does not overwrite an already
|
|
1067
|
+
* settled Promise.
|
|
1036
1068
|
*/
|
|
1037
1069
|
destroy() {
|
|
1038
1070
|
if (this.destroyed) {
|
|
@@ -1053,13 +1085,13 @@ var SunbayCheckout = (() => {
|
|
|
1053
1085
|
void Promise.resolve(this.transport.request("destroy", {})).catch(() => void 0);
|
|
1054
1086
|
this.transport.destroy();
|
|
1055
1087
|
}
|
|
1056
|
-
/**
|
|
1088
|
+
/** Throw INSTANCE_DESTROYED if already destroyed. */
|
|
1057
1089
|
assertAlive() {
|
|
1058
1090
|
if (this.destroyed) {
|
|
1059
1091
|
throw makeError3(ErrorCode.INSTANCE_DESTROYED, "Checkout instance has been destroyed", "runtime");
|
|
1060
1092
|
}
|
|
1061
1093
|
}
|
|
1062
|
-
/**
|
|
1094
|
+
/** Mutual-exclusion check: throws ELEMENT_CONFLICT on conflict (derived live from the elements Map, does not mutate the registry). */
|
|
1063
1095
|
assertNoConflict(type, options) {
|
|
1064
1096
|
const existing = [...this.elements.values()];
|
|
1065
1097
|
const has = (t) => existing.some((e) => e.type === t);
|
|
@@ -1109,9 +1141,10 @@ var SunbayCheckout = (() => {
|
|
|
1109
1141
|
}
|
|
1110
1142
|
}
|
|
1111
1143
|
/**
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
*
|
|
1144
|
+
* Element `destroy()` callback: remove this elementId from the mutual-exclusion registry (the
|
|
1145
|
+
* elements Map) to free the slot. Idempotent; when the whole instance is destroyed (destroyed
|
|
1146
|
+
* already set) it goes through the unified `elements.clear()` path, so this is a no-op here,
|
|
1147
|
+
* avoiding concurrent modification while iterating elements to call element.destroy().
|
|
1115
1148
|
*/
|
|
1116
1149
|
releaseElement(elementId) {
|
|
1117
1150
|
if (this.destroyed) {
|
|
@@ -1119,7 +1152,7 @@ var SunbayCheckout = (() => {
|
|
|
1119
1152
|
}
|
|
1120
1153
|
this.elements.delete(elementId);
|
|
1121
1154
|
}
|
|
1122
|
-
/**
|
|
1155
|
+
/** Handle the paymentResult event: fulfill the pending confirm by operationId; late/mismatched ones are ignored. */
|
|
1123
1156
|
handlePaymentResult(payload) {
|
|
1124
1157
|
if (this.destroyed) {
|
|
1125
1158
|
return;
|
|
@@ -1146,9 +1179,10 @@ var SunbayCheckout = (() => {
|
|
|
1146
1179
|
// src/config/env.ts
|
|
1147
1180
|
var HOST_ORIGINS = {
|
|
1148
1181
|
dev: "https://checkout.sunbay-dev.com",
|
|
1182
|
+
test: "https://checkout.sunbay-uat.us",
|
|
1149
1183
|
prod: "https://checkout.sunbay.us"
|
|
1150
1184
|
};
|
|
1151
|
-
function resolveHostOrigin(env = "
|
|
1185
|
+
function resolveHostOrigin(env = "prod", override) {
|
|
1152
1186
|
if (override) {
|
|
1153
1187
|
return override;
|
|
1154
1188
|
}
|
|
@@ -1240,7 +1274,7 @@ var SunbayCheckout = (() => {
|
|
|
1240
1274
|
const w = window;
|
|
1241
1275
|
w.SunbayCheckout = { ...w.SunbayCheckout ?? {}, createCheckout };
|
|
1242
1276
|
}
|
|
1243
|
-
var version = true ? "0.0.
|
|
1277
|
+
var version = true ? "0.0.3" : "0.0.0";
|
|
1244
1278
|
return __toCommonJS(index_exports);
|
|
1245
1279
|
})();
|
|
1246
1280
|
//# sourceMappingURL=index.global.js.map
|