@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.
@@ -169,8 +169,9 @@ var SunbayCheckout = (() => {
169
169
  this.eventHandlers = /* @__PURE__ */ new Map();
170
170
  this.helloHandler = null;
171
171
  /**
172
- * 握手协商出的运行时 id;未注入前为空串。出站 request 信封据此填充,
173
- * main 端点仅处理 runtimeId 与自身一致的请求,空串会被静默丢弃。
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
- * 创建隐藏 0 尺寸 main iframe,append 到 document.body。
187
+ * Create the hidden, 0-size main iframe and append it to document.body.
187
188
  *
188
- * @returns 已挂载的 iframe 元素。
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
- * 发送一条 request 信封,按 requestId 配对响应。
211
+ * Send one request envelope and pair the response by requestId.
211
212
  *
212
- * @param name - 命令名。
213
- * @param payload - 命令载荷(JSON 可序列化)。
214
- * @returns 响应 data;失败(ok:false / 超时 / destroy)reject 一个 CheckoutError。
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
- * 注册事件 handler(分发 kind:'event' 信封的 payload)。
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
- * 注销事件 handler。
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
- * 注册握手(RuntimeHello)回调。hello 阶段尚无 checkoutId,单独处理。
285
+ * Register the handshake (RuntimeHello) callback. The hello phase has no checkoutId yet and is
286
+ * handled separately.
285
287
  *
286
- * @param handler - 收到 hello 时调用。
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
- * 注入握手协商出的运行时 id。必须在发送首个 request(initialize)之前调用,
293
- * 否则出站信封 runtimeId 为空串、被 main 端点静默丢弃导致请求超时。
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 - RuntimeHello 中的 runtimeId。
298
+ * @param runtimeId - The runtimeId from RuntimeHello.
296
299
  */
297
300
  setRuntimeId(runtimeId) {
298
301
  this.runtimeId = runtimeId;
299
302
  }
300
303
  /**
301
- * 销毁:移除 iframe、清监听,未决 request 以 INSTANCE_DESTROYED reject。
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
- * 点对点 port relay(父窗口中继,Stripe controller 模型)。
332
+ * Point-to-point port relay (parent-window relay, Stripe controller model).
330
333
  *
331
- * 兄弟 iframe(main 与各 Element iframe)之间拿不到彼此 window 引用,无法直接
332
- * transfer MessagePort。本层(商户页,唯一同时可达两端且知其 origin 的角色)在
333
- * 收到某 Element iframe 的 `element-online` 时:
334
- * 1. 建 `MessageChannel`;
335
- * 2. `port1` → main iframe(`relay-port`,targetOrigin=hostOrigin);
336
- * 3. `port2` → 发起的 Element iframe(即 `event.source`,targetOrigin=hostOrigin)。
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
- * 两端 origin 均为 hostOrigin,targetOrigin 恒正确(跨域亦不被丢弃)。
339
- * 本层只搬运**空 port**,绝不接触卡值(卡值在 port 建立后端到端传输)。
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 入站 message(origin 已校验为 hostOrigin)
342
- * @returns 是否作为 element-online 处理(是则调用方应 return)
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
- /** 处理入站 message:过滤 origin,识别 relay / hello / Envelope 并分发。 */
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
- /** 分发 response 到配对的未决 request。 */
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
- /** 分发 event 到注册的 handler。 */
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 期间临时订阅的事件 handler(resolve/reject 后解绑)。 */
473
+ /** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */
469
474
  this.readyHandler = null;
470
475
  this.loadErrorHandler = null;
471
- /** 仅 expressCheckout:常驻订阅的 expressAvailabilityChange handler(destroy 时解绑)。 */
476
+ /** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */
472
477
  this.availabilityHandler = null;
473
478
  /**
474
- * iframe 自适应高度:挂载期间常驻的 window 'message' 监听器(iframe 消失/销毁时解绑)。
475
- * 存引用以便 removeEventListener;null 表示未监听。
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
- /** 最近一次已应用的 iframe 高度(px),用于去重,避免冗余 style 写入;null 表示尚未设置。 */
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
- * 仅 expressCheckout:返回当前各钱包可用性快照(其他类型恒为 undefined)。
504
+ * expressCheckout only: return the current snapshot of each wallet's availability (always
505
+ * undefined for other types).
499
506
  *
500
- * 初值来自 elementReady 的 availableWallets,之后由 expressAvailabilityChange 更新。
507
+ * The initial value comes from elementReady's availableWallets, then is updated by
508
+ * expressAvailabilityChange.
501
509
  *
502
- * @returns 可用性快照;尚未就绪或非 express 时为 undefined。
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
- * 挂载到商户 DOM 目标:插入可见 iframe、发 createElement、等 elementReady。
516
+ * Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.
509
517
  *
510
- * @param selectorOrEl - CSS 选择器或 HTMLElement 容器;容器须唯一且为空。
511
- * @returns elementReady 后 resolve;失败移除节点并 reject(loadError / INVALID_OPTIONS)。
512
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 卸载:发 destroyElement、移除 iframe;幂等;保留句柄与配置。
619
+ * Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.
611
620
  *
612
- * @returns 完成后 resolve(未挂载时直接 resolve)。
613
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 销毁句柄:幂等;移除 iframe;此后非 destroy/off 调用抛 INSTANCE_DESTROYED。
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
- * 更新 Element 配置:发 updateElement;非法字段由托管侧整次拒绝。
662
+ * Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.
654
663
  *
655
- * @param patch - 配置增量。
656
- * @returns 成功写回本地 options 后 resolve。
657
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 注册事件 handler(透传到 transport,按事件名分发)。
680
+ * Register an event handler (passed through to the transport, dispatched by event name).
672
681
  *
673
- * @param event - 事件名。
674
- * @param handler - 回调。
675
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 注销事件 handler(销毁后仍可安全调用)。
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
- * 仅 expressCheckout:常驻订阅 expressAvailabilityChange,更新本地可用性快照。
709
+ * expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.
701
710
  *
702
- * 该事件载荷不含 elementId;express 为单例(由 CheckoutInstance 保证),
703
- * 故无需按 elementId 过滤。非 express 类型不订阅。
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
- /** 发送一条输入类动作命令(销毁后同步抛 INSTANCE_DESTROYED)。 */
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
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
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
- * 解析 iframe src:拆分字段走 /sdk/field/,其余走 /sdk/element/,带 checkoutId 与 elementId;
746
- * 若提供了初始 colorScheme,则追加 `&colorScheme=<值>`(首帧无闪);
747
- * 若初始 appearance 含 variables 或 rules,则追加
748
- * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>`(首帧即生效,无闪)。
749
- * 仅含 colorScheme(无 variables/rules)的 appearance 不拼接,避免空外观串。
750
- * 对 expressCheckout,另追加钱包开关 / 每钱包 buttonType / 全局 buttonHeight
751
- * (见 {@link resolveExpressQuery})。
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 随 src 下发,供托管 child 在 iframe-resize 消息中原样回传,父窗口据此在
754
- * 同页多个 iframe 间按句柄分发高度。
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
- if (this.appearance && (this.appearance.variables || this.appearance.rules)) {
764
- query += `&appearance=${encodeURIComponent(JSON.stringify(this.appearance))}`;
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
- * 构造 expressCheckout 专属 query 片段(仅 expressCheckout 调用):
773
- * - 钱包显式置为 `false` → `&applePay=false` / `&googlePay=false`(强制隐藏,保持
774
- * 既有开关语义;`true` / 缺省 → 自动,不拼开关);
775
- * - 钱包以对象形态配置且含 `buttonType` → `&applePayButtonType=<v>` /
776
- * `&googlePayButtonType=<v>`(布尔开关 / 缺省 → 不拼);
777
- * - 全局 `buttonHeight`(有限数)→ `&buttonHeight=<n>`。
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 以 `&` 起始的 query 片段(无可拼项时返回空串)。
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
- * 安装 iframe 自适应高度监听:订阅 window 'message',仅采信来自本 iframe
810
- * `contentWindow` 且 origin === hostOrigin 的合法 {@link IframeResizeMessage},
811
- * 且 elementId 与本句柄匹配时,将外层 iframe 高度调整为内容真实高度(宽度保持 100%)。
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
- * 安全:先校验 `event.source === iframe.contentWindow`(关键来源校验),
814
- * 再校验 `event.origin === hostOrigin`(更严格的同源校验),防跨源伪造。
815
- * 高度去重:与上次相同则跳过,避免冗余 style 写入。
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
- /** 解绑 iframe 自适应高度监听并重置去重状态(幂等)。 */
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
- /** 移除 iframe DOM 节点(若存在),并解绑自适应高度监听。 */
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
- /** 工厂:等价于 `new CheckoutElement(opts)`(供 CheckoutInstance 统一入口)。 */
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
- /** 已创建的 Element(按 elementId 索引)。 */
910
+ /** Created Elements (indexed by elementId). */
883
911
  /**
884
- * 互斥注册表的**单一数据源**:elementId → 已创建的 CheckoutElement。
885
- * 互斥判定由本 Map 实时推导(见 {@link assertNoConflict}),元素 `destroy()` 经
886
- * {@link releaseElement} 从本 Map 移除以释放名额(允许销毁后重建同类互斥 Element)。
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
- /** 当前未决 confirm(同一时刻至多一个)。 */
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
- * 创建一个 Element 句柄(不自动挂载;互斥冲突抛 ELEMENT_CONFLICT)。
935
+ * Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).
906
936
  *
907
- * 互斥规则(spec 6):
908
- * - 聚合 `card` 与任一拆分字段(cardNumber/cardExpiry/cardCvc)互斥。
909
- * - 同 `mode`(billing/shipping)的 address 不可重复。
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 - 初始 Element 配置。
914
- * @returns 新建的 CheckoutElement。
915
- * @throws 已销毁时抛 INSTANCE_DESTROYED;互斥冲突抛 ELEMENT_CONFLICT。
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
- * 发起支付确认。生成 operationId,发 confirm 命令拿 accepted,返回的 Promise
938
- * 由后续同 operationId 的 paymentResult 事件兑现。进行中再次调用 → CHECKOUT_BUSY。
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 - 账单/配送地址、姓名、邮箱、returnUrl 等可选参数。
941
- * @returns 支付终态结果 PaymentOutcome。
942
- * @throws 已销毁时 reject INSTANCE_DESTROYED;进行中 reject CHECKOUT_BUSY。
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
- * 获取最新会话视图(透传 getSession 命令并写回本地快照)。
999
+ * Get the latest session view (passes through the getSession command and writes back the local snapshot).
969
1000
  *
970
- * @returns 最新 PublicSessionView。
971
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
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
- * 刷新会话(透传 refresh 命令并写回本地快照)。
1011
+ * Refresh the session (passes through the refresh command and writes back the local snapshot).
981
1012
  *
982
- * @returns 刷新后的 PublicSessionView。
983
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
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
- * 查询支付状态(原样返回 TxnStatus,不转义)。
1023
+ * Query the payment status (returns TxnStatus as-is, no transformation).
993
1024
  *
994
- * @returns txnStatus(可能含 txnId)。
995
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
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 已销毁时 reject INSTANCE_DESTROYED。
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
- * 注册 Checkout 级事件 handler(透传 transport)。
1044
+ * Register a Checkout-level event handler (passes through the transport).
1014
1045
  *
1015
- * @param event - 事件名。
1016
- * @param handler - 回调。
1017
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 注销事件 handler(销毁后仍可安全调用)。
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
- * 销毁实例:幂等。释放全部 Element、解绑 paymentResult 监听、以
1034
- * INSTANCE_DESTROYED reject 未决 confirm、发 destroy 命令并销毁 transport。
1035
- * 此后迟到的 paymentResult 一律忽略,不改写已结束的 Promise。
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
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
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
- /** 互斥检查:冲突时抛 ELEMENT_CONFLICT(由 elements Map 实时推导,不改变注册表)。 */
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
- * 元素 `destroy()` 回调:从互斥注册表(elements Map)移除该 elementId,释放名额。
1113
- * 幂等;实例整体 destroy 时(已置 destroyed)走统一 `elements.clear()` 路径,此处 no-op,
1114
- * 避免遍历 elements 调 element.destroy() 时的并发修改。
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
- /** 处理 paymentResult 事件:按 operationId 兑现未决 confirm;迟到/不匹配忽略。 */
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 = "dev", override) {
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.1" : "0.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