@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/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
- * 握手协商出的运行时 id;未注入前为空串。出站 request 信封据此填充,
66
- * main 端点仅处理 runtimeId 与自身一致的请求,空串会被静默丢弃。
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
- * 创建隐藏 0 尺寸 main iframe,append 到 document.body。
80
+ * Create the hidden, 0-size main iframe and append it to document.body.
80
81
  *
81
- * @returns 已挂载的 iframe 元素。
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
- * 发送一条 request 信封,按 requestId 配对响应。
104
+ * Send one request envelope and pair the response by requestId.
104
105
  *
105
- * @param name - 命令名。
106
- * @param payload - 命令载荷(JSON 可序列化)。
107
- * @returns 响应 data;失败(ok:false / 超时 / destroy)reject 一个 CheckoutError。
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
- * 注册事件 handler(分发 kind:'event' 信封的 payload)。
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
- * 注销事件 handler。
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
- * 注册握手(RuntimeHello)回调。hello 阶段尚无 checkoutId,单独处理。
178
+ * Register the handshake (RuntimeHello) callback. The hello phase has no checkoutId yet and is
179
+ * handled separately.
178
180
  *
179
- * @param handler - 收到 hello 时调用。
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
- * 注入握手协商出的运行时 id。必须在发送首个 request(initialize)之前调用,
186
- * 否则出站信封 runtimeId 为空串、被 main 端点静默丢弃导致请求超时。
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 - RuntimeHello 中的 runtimeId。
191
+ * @param runtimeId - The runtimeId from RuntimeHello.
189
192
  */
190
193
  setRuntimeId(runtimeId) {
191
194
  this.runtimeId = runtimeId;
192
195
  }
193
196
  /**
194
- * 销毁:移除 iframe、清监听,未决 request 以 INSTANCE_DESTROYED reject。
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
- * 点对点 port relay(父窗口中继,Stripe controller 模型)。
225
+ * Point-to-point port relay (parent-window relay, Stripe controller model).
223
226
  *
224
- * 兄弟 iframe(main 与各 Element iframe)之间拿不到彼此 window 引用,无法直接
225
- * transfer MessagePort。本层(商户页,唯一同时可达两端且知其 origin 的角色)在
226
- * 收到某 Element iframe 的 `element-online` 时:
227
- * 1. 建 `MessageChannel`;
228
- * 2. `port1` → main iframe(`relay-port`,targetOrigin=hostOrigin);
229
- * 3. `port2` → 发起的 Element iframe(即 `event.source`,targetOrigin=hostOrigin)。
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
- * 两端 origin 均为 hostOrigin,targetOrigin 恒正确(跨域亦不被丢弃)。
232
- * 本层只搬运**空 port**,绝不接触卡值(卡值在 port 建立后端到端传输)。
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 入站 message(origin 已校验为 hostOrigin)
235
- * @returns 是否作为 element-online 处理(是则调用方应 return)
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
- /** 处理入站 message:过滤 origin,识别 relay / hello / Envelope 并分发。 */
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
- /** 分发 response 到配对的未决 request。 */
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
- /** 分发 event 到注册的 handler。 */
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 期间临时订阅的事件 handler(resolve/reject 后解绑)。 */
367
+ /** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */
363
368
  this.readyHandler = null;
364
369
  this.loadErrorHandler = null;
365
- /** 仅 expressCheckout:常驻订阅的 expressAvailabilityChange handler(destroy 时解绑)。 */
370
+ /** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */
366
371
  this.availabilityHandler = null;
367
372
  /**
368
- * iframe 自适应高度:挂载期间常驻的 window 'message' 监听器(iframe 消失/销毁时解绑)。
369
- * 存引用以便 removeEventListener;null 表示未监听。
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
- /** 最近一次已应用的 iframe 高度(px),用于去重,避免冗余 style 写入;null 表示尚未设置。 */
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
- * 仅 expressCheckout:返回当前各钱包可用性快照(其他类型恒为 undefined)。
398
+ * expressCheckout only: return the current snapshot of each wallet's availability (always
399
+ * undefined for other types).
393
400
  *
394
- * 初值来自 elementReady 的 availableWallets,之后由 expressAvailabilityChange 更新。
401
+ * The initial value comes from elementReady's availableWallets, then is updated by
402
+ * expressAvailabilityChange.
395
403
  *
396
- * @returns 可用性快照;尚未就绪或非 express 时为 undefined。
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
- * 挂载到商户 DOM 目标:插入可见 iframe、发 createElement、等 elementReady。
410
+ * Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.
403
411
  *
404
- * @param selectorOrEl - CSS 选择器或 HTMLElement 容器;容器须唯一且为空。
405
- * @returns elementReady 后 resolve;失败移除节点并 reject(loadError / INVALID_OPTIONS)。
406
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 卸载:发 destroyElement、移除 iframe;幂等;保留句柄与配置。
513
+ * Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.
505
514
  *
506
- * @returns 完成后 resolve(未挂载时直接 resolve)。
507
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 销毁句柄:幂等;移除 iframe;此后非 destroy/off 调用抛 INSTANCE_DESTROYED。
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
- * 更新 Element 配置:发 updateElement;非法字段由托管侧整次拒绝。
556
+ * Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.
548
557
  *
549
- * @param patch - 配置增量。
550
- * @returns 成功写回本地 options 后 resolve。
551
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 注册事件 handler(透传到 transport,按事件名分发)。
574
+ * Register an event handler (passed through to the transport, dispatched by event name).
566
575
  *
567
- * @param event - 事件名。
568
- * @param handler - 回调。
569
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 注销事件 handler(销毁后仍可安全调用)。
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
- * 仅 expressCheckout:常驻订阅 expressAvailabilityChange,更新本地可用性快照。
603
+ * expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.
595
604
  *
596
- * 该事件载荷不含 elementId;express 为单例(由 CheckoutInstance 保证),
597
- * 故无需按 elementId 过滤。非 express 类型不订阅。
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
- /** 发送一条输入类动作命令(销毁后同步抛 INSTANCE_DESTROYED)。 */
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
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
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
- * 解析 iframe src:拆分字段走 /sdk/field/,其余走 /sdk/element/,带 checkoutId 与 elementId;
640
- * 若提供了初始 colorScheme,则追加 `&colorScheme=<值>`(首帧无闪);
641
- * 若初始 appearance 含 variables 或 rules,则追加
642
- * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>`(首帧即生效,无闪)。
643
- * 仅含 colorScheme(无 variables/rules)的 appearance 不拼接,避免空外观串。
644
- * 对 expressCheckout,另追加钱包开关 / 每钱包 buttonType / 全局 buttonHeight
645
- * (见 {@link resolveExpressQuery})。
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 随 src 下发,供托管 child 在 iframe-resize 消息中原样回传,父窗口据此在
648
- * 同页多个 iframe 间按句柄分发高度。
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
- if (this.appearance && (this.appearance.variables || this.appearance.rules)) {
658
- query += `&appearance=${encodeURIComponent(JSON.stringify(this.appearance))}`;
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
- * 构造 expressCheckout 专属 query 片段(仅 expressCheckout 调用):
667
- * - 钱包显式置为 `false` → `&applePay=false` / `&googlePay=false`(强制隐藏,保持
668
- * 既有开关语义;`true` / 缺省 → 自动,不拼开关);
669
- * - 钱包以对象形态配置且含 `buttonType` → `&applePayButtonType=<v>` /
670
- * `&googlePayButtonType=<v>`(布尔开关 / 缺省 → 不拼);
671
- * - 全局 `buttonHeight`(有限数)→ `&buttonHeight=<n>`。
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 以 `&` 起始的 query 片段(无可拼项时返回空串)。
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
- * 安装 iframe 自适应高度监听:订阅 window 'message',仅采信来自本 iframe
704
- * `contentWindow` 且 origin === hostOrigin 的合法 {@link IframeResizeMessage},
705
- * 且 elementId 与本句柄匹配时,将外层 iframe 高度调整为内容真实高度(宽度保持 100%)。
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
- * 安全:先校验 `event.source === iframe.contentWindow`(关键来源校验),
708
- * 再校验 `event.origin === hostOrigin`(更严格的同源校验),防跨源伪造。
709
- * 高度去重:与上次相同则跳过,避免冗余 style 写入。
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
- /** 解绑 iframe 自适应高度监听并重置去重状态(幂等)。 */
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
- /** 移除 iframe DOM 节点(若存在),并解绑自适应高度监听。 */
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
- /** 工厂:等价于 `new CheckoutElement(opts)`(供 CheckoutInstance 统一入口)。 */
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
- /** 已创建的 Element(按 elementId 索引)。 */
805
+ /** Created Elements (indexed by elementId). */
778
806
  /**
779
- * 互斥注册表的**单一数据源**:elementId → 已创建的 CheckoutElement。
780
- * 互斥判定由本 Map 实时推导(见 {@link assertNoConflict}),元素 `destroy()` 经
781
- * {@link releaseElement} 从本 Map 移除以释放名额(允许销毁后重建同类互斥 Element)。
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
- /** 当前未决 confirm(同一时刻至多一个)。 */
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
- * 创建一个 Element 句柄(不自动挂载;互斥冲突抛 ELEMENT_CONFLICT)。
830
+ * Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).
801
831
  *
802
- * 互斥规则(spec 6):
803
- * - 聚合 `card` 与任一拆分字段(cardNumber/cardExpiry/cardCvc)互斥。
804
- * - 同 `mode`(billing/shipping)的 address 不可重复。
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 - 初始 Element 配置。
809
- * @returns 新建的 CheckoutElement。
810
- * @throws 已销毁时抛 INSTANCE_DESTROYED;互斥冲突抛 ELEMENT_CONFLICT。
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
- * 发起支付确认。生成 operationId,发 confirm 命令拿 accepted,返回的 Promise
833
- * 由后续同 operationId 的 paymentResult 事件兑现。进行中再次调用 → CHECKOUT_BUSY。
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 - 账单/配送地址、姓名、邮箱、returnUrl 等可选参数。
836
- * @returns 支付终态结果 PaymentOutcome。
837
- * @throws 已销毁时 reject INSTANCE_DESTROYED;进行中 reject CHECKOUT_BUSY。
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
- * 获取最新会话视图(透传 getSession 命令并写回本地快照)。
894
+ * Get the latest session view (passes through the getSession command and writes back the local snapshot).
864
895
  *
865
- * @returns 最新 PublicSessionView。
866
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
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
- * 刷新会话(透传 refresh 命令并写回本地快照)。
906
+ * Refresh the session (passes through the refresh command and writes back the local snapshot).
876
907
  *
877
- * @returns 刷新后的 PublicSessionView。
878
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
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
- * 查询支付状态(原样返回 TxnStatus,不转义)。
918
+ * Query the payment status (returns TxnStatus as-is, no transformation).
888
919
  *
889
- * @returns txnStatus(可能含 txnId)。
890
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
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 已销毁时 reject INSTANCE_DESTROYED。
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
- * 注册 Checkout 级事件 handler(透传 transport)。
939
+ * Register a Checkout-level event handler (passes through the transport).
909
940
  *
910
- * @param event - 事件名。
911
- * @param handler - 回调。
912
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
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
- * 注销事件 handler(销毁后仍可安全调用)。
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
- * 销毁实例:幂等。释放全部 Element、解绑 paymentResult 监听、以
929
- * INSTANCE_DESTROYED reject 未决 confirm、发 destroy 命令并销毁 transport。
930
- * 此后迟到的 paymentResult 一律忽略,不改写已结束的 Promise。
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
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
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
- /** 互斥检查:冲突时抛 ELEMENT_CONFLICT(由 elements Map 实时推导,不改变注册表)。 */
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
- * 元素 `destroy()` 回调:从互斥注册表(elements Map)移除该 elementId,释放名额。
1008
- * 幂等;实例整体 destroy 时(已置 destroyed)走统一 `elements.clear()` 路径,此处 no-op,
1009
- * 避免遍历 elements 调 element.destroy() 时的并发修改。
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
- /** 处理 paymentResult 事件:按 operationId 兑现未决 confirm;迟到/不匹配忽略。 */
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 = "dev", override) {
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.2" : "0.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,