@sunbay/checkout-sdk-js 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -37,8 +37,9 @@ var MainFrameTransport = class {
37
37
  this.eventHandlers = /* @__PURE__ */ new Map();
38
38
  this.helloHandler = null;
39
39
  /**
40
- * 握手协商出的运行时 id;未注入前为空串。出站 request 信封据此填充,
41
- * main 端点仅处理 runtimeId 与自身一致的请求,空串会被静默丢弃。
40
+ * Runtime id negotiated during the handshake; empty string before injection. Outbound request
41
+ * envelopes are filled from it; the main endpoint only processes requests whose runtimeId
42
+ * matches its own, and an empty string is silently dropped.
42
43
  */
43
44
  this.runtimeId = "";
44
45
  this.destroyed = false;
@@ -51,9 +52,9 @@ var MainFrameTransport = class {
51
52
  window.addEventListener("message", this.messageListener);
52
53
  }
53
54
  /**
54
- * 创建隐藏 0 尺寸 main iframe,append 到 document.body。
55
+ * Create the hidden, 0-size main iframe and append it to document.body.
55
56
  *
56
- * @returns 已挂载的 iframe 元素。
57
+ * @returns The mounted iframe element.
57
58
  * @example
58
59
  * const iframe = transport.createHiddenIframe();
59
60
  */
@@ -75,11 +76,11 @@ var MainFrameTransport = class {
75
76
  return iframe;
76
77
  }
77
78
  /**
78
- * 发送一条 request 信封,按 requestId 配对响应。
79
+ * Send one request envelope and pair the response by requestId.
79
80
  *
80
- * @param name - 命令名。
81
- * @param payload - 命令载荷(JSON 可序列化)。
82
- * @returns 响应 data;失败(ok:false / 超时 / destroy)reject 一个 CheckoutError。
81
+ * @param name - Command name.
82
+ * @param payload - Command payload (JSON-serializable).
83
+ * @returns The response data; on failure (ok:false / timeout / destroy) rejects with a CheckoutError.
83
84
  * @example
84
85
  * const session = await transport.request('getSession', {});
85
86
  */
@@ -120,10 +121,10 @@ var MainFrameTransport = class {
120
121
  });
121
122
  }
122
123
  /**
123
- * 注册事件 handler(分发 kind:'event' 信封的 payload)。
124
+ * Register an event handler (dispatches the payload of kind:'event' envelopes).
124
125
  *
125
- * @param event - 事件名。
126
- * @param handler - 回调。
126
+ * @param event - Event name.
127
+ * @param handler - Callback.
127
128
  */
128
129
  on(event, handler) {
129
130
  let set = this.eventHandlers.get(event);
@@ -134,10 +135,10 @@ var MainFrameTransport = class {
134
135
  set.add(handler);
135
136
  }
136
137
  /**
137
- * 注销事件 handler。
138
+ * Unregister an event handler.
138
139
  *
139
- * @param event - 事件名。
140
- * @param handler - 先前注册的回调。
140
+ * @param event - Event name.
141
+ * @param handler - The previously registered callback.
141
142
  */
142
143
  off(event, handler) {
143
144
  const set = this.eventHandlers.get(event);
@@ -149,24 +150,26 @@ var MainFrameTransport = class {
149
150
  }
150
151
  }
151
152
  /**
152
- * 注册握手(RuntimeHello)回调。hello 阶段尚无 checkoutId,单独处理。
153
+ * Register the handshake (RuntimeHello) callback. The hello phase has no checkoutId yet and is
154
+ * handled separately.
153
155
  *
154
- * @param handler - 收到 hello 时调用。
156
+ * @param handler - Called when a hello is received.
155
157
  */
156
158
  onHello(handler) {
157
159
  this.helloHandler = handler;
158
160
  }
159
161
  /**
160
- * 注入握手协商出的运行时 id。必须在发送首个 request(initialize)之前调用,
161
- * 否则出站信封 runtimeId 为空串、被 main 端点静默丢弃导致请求超时。
162
+ * Inject the runtime id negotiated during the handshake. Must be called before sending the first
163
+ * request (initialize), otherwise the outbound envelope's runtimeId is an empty string, gets
164
+ * silently dropped by the main endpoint, and the request times out.
162
165
  *
163
- * @param runtimeId - RuntimeHello 中的 runtimeId。
166
+ * @param runtimeId - The runtimeId from RuntimeHello.
164
167
  */
165
168
  setRuntimeId(runtimeId) {
166
169
  this.runtimeId = runtimeId;
167
170
  }
168
171
  /**
169
- * 销毁:移除 iframe、清监听,未决 request 以 INSTANCE_DESTROYED reject。
172
+ * Destroy: remove the iframe, clear listeners, and reject pending requests with INSTANCE_DESTROYED.
170
173
  */
171
174
  destroy() {
172
175
  if (this.destroyed) {
@@ -186,7 +189,7 @@ var MainFrameTransport = class {
186
189
  }
187
190
  this.iframe = null;
188
191
  }
189
- /** 按命令名解析超时时长。 */
192
+ /** Resolve the timeout by command name. */
190
193
  resolveTimeout(name) {
191
194
  if (name === "initialize") {
192
195
  return this.timeouts.initialize;
@@ -194,20 +197,22 @@ var MainFrameTransport = class {
194
197
  return this.timeouts.default;
195
198
  }
196
199
  /**
197
- * 点对点 port relay(父窗口中继,Stripe controller 模型)。
200
+ * Point-to-point port relay (parent-window relay, Stripe controller model).
198
201
  *
199
- * 兄弟 iframe(main 与各 Element iframe)之间拿不到彼此 window 引用,无法直接
200
- * transfer MessagePort。本层(商户页,唯一同时可达两端且知其 origin 的角色)在
201
- * 收到某 Element iframe 的 `element-online` 时:
202
- * 1. 建 `MessageChannel`;
203
- * 2. `port1` → main iframe(`relay-port`,targetOrigin=hostOrigin);
204
- * 3. `port2` → 发起的 Element iframe(即 `event.source`,targetOrigin=hostOrigin)。
202
+ * Sibling iframes (main and the various Element iframes) cannot obtain each other's window
203
+ * references, so they cannot transfer a MessagePort directly. This layer (the merchant page,
204
+ * the only party that can reach both ends and knows their origin) does the following when it
205
+ * receives an `element-online` from some Element iframe:
206
+ * 1. Create a `MessageChannel`;
207
+ * 2. `port1` → main iframe (`relay-port`, targetOrigin=hostOrigin);
208
+ * 3. `port2` → the originating Element iframe (i.e. `event.source`, targetOrigin=hostOrigin).
205
209
  *
206
- * 两端 origin 均为 hostOrigin,targetOrigin 恒正确(跨域亦不被丢弃)。
207
- * 本层只搬运**空 port**,绝不接触卡值(卡值在 port 建立后端到端传输)。
210
+ * Both ends' origin is hostOrigin, so targetOrigin is always correct (and not dropped across
211
+ * origins). This layer only carries **empty ports** and never touches card values (which are
212
+ * transmitted end-to-end after the port is established).
208
213
  *
209
- * @param event 入站 message(origin 已校验为 hostOrigin)
210
- * @returns 是否作为 element-online 处理(是则调用方应 return)
214
+ * @param event Inbound message (origin already verified as hostOrigin)
215
+ * @returns Whether it was handled as element-online (if so, the caller should return)
211
216
  */
212
217
  tryHandleElementOnline(event) {
213
218
  const data = event.data;
@@ -236,7 +241,7 @@ var MainFrameTransport = class {
236
241
  elementWin.postMessage(relayMsg, this.hostOrigin, [channel.port2]);
237
242
  return true;
238
243
  }
239
- /** 处理入站 message:过滤 origin,识别 relay / hello / Envelope 并分发。 */
244
+ /** Handle an inbound message: filter origin, recognize relay / hello / Envelope and dispatch. */
240
245
  handleMessage(event) {
241
246
  if (this.destroyed) {
242
247
  return;
@@ -277,7 +282,7 @@ var MainFrameTransport = class {
277
282
  this.handleEvent(envelope);
278
283
  }
279
284
  }
280
- /** 分发 response 到配对的未决 request。 */
285
+ /** Dispatch a response to the paired pending request. */
281
286
  handleResponse(envelope) {
282
287
  const entry = this.pending.get(envelope.requestId);
283
288
  if (!entry) {
@@ -291,7 +296,7 @@ var MainFrameTransport = class {
291
296
  entry.reject(envelope.error);
292
297
  }
293
298
  }
294
- /** 分发 event 到注册的 handler。 */
299
+ /** Dispatch an event to the registered handlers. */
295
300
  handleEvent(envelope) {
296
301
  const set = this.eventHandlers.get(envelope.name);
297
302
  if (!set) {
@@ -337,17 +342,18 @@ var CheckoutElement = class _CheckoutElement {
337
342
  this.status = "created";
338
343
  this.container = null;
339
344
  this.iframe = null;
340
- /** mount 期间临时订阅的事件 handler(resolve/reject 后解绑)。 */
345
+ /** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */
341
346
  this.readyHandler = null;
342
347
  this.loadErrorHandler = null;
343
- /** 仅 expressCheckout:常驻订阅的 expressAvailabilityChange handler(destroy 时解绑)。 */
348
+ /** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */
344
349
  this.availabilityHandler = null;
345
350
  /**
346
- * iframe 自适应高度:挂载期间常驻的 window 'message' 监听器(iframe 消失/销毁时解绑)。
347
- * 存引用以便 removeEventListener;null 表示未监听。
351
+ * iframe auto-height: the window 'message' listener that stays resident during mount (unbound
352
+ * when the iframe disappears/is destroyed). Keeps a reference so removeEventListener can be
353
+ * called; null means not listening.
348
354
  */
349
355
  this.resizeHandler = null;
350
- /** 最近一次已应用的 iframe 高度(px),用于去重,避免冗余 style 写入;null 表示尚未设置。 */
356
+ /** The most recently applied iframe height (px), used for deduplication to avoid redundant style writes; null means not yet set. */
351
357
  this.lastHeight = null;
352
358
  this.elementId = opts.elementId;
353
359
  this.type = opts.type;
@@ -362,26 +368,28 @@ var CheckoutElement = class _CheckoutElement {
362
368
  this.installInputActions();
363
369
  this.installExpressAvailability();
364
370
  }
365
- /** 当前状态(只读快照,供外部/测试观测)。 */
371
+ /** The current status (read-only snapshot, for external/test observation). */
366
372
  getStatus() {
367
373
  return this.status;
368
374
  }
369
375
  /**
370
- * 仅 expressCheckout:返回当前各钱包可用性快照(其他类型恒为 undefined)。
376
+ * expressCheckout only: return the current snapshot of each wallet's availability (always
377
+ * undefined for other types).
371
378
  *
372
- * 初值来自 elementReady 的 availableWallets,之后由 expressAvailabilityChange 更新。
379
+ * The initial value comes from elementReady's availableWallets, then is updated by
380
+ * expressAvailabilityChange.
373
381
  *
374
- * @returns 可用性快照;尚未就绪或非 express 时为 undefined。
382
+ * @returns The availability snapshot; undefined when not yet ready or non-express.
375
383
  */
376
384
  getAvailableWallets() {
377
385
  return this.availableWallets;
378
386
  }
379
387
  /**
380
- * 挂载到商户 DOM 目标:插入可见 iframe、发 createElement、等 elementReady。
388
+ * Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.
381
389
  *
382
- * @param selectorOrEl - CSS 选择器或 HTMLElement 容器;容器须唯一且为空。
383
- * @returns elementReady 后 resolve;失败移除节点并 reject(loadError / INVALID_OPTIONS)。
384
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
390
+ * @param selectorOrEl - A CSS selector or an HTMLElement container; the container must be unique and empty.
391
+ * @returns Resolves after elementReady; on failure removes the node and rejects (loadError / INVALID_OPTIONS).
392
+ * @throws INSTANCE_DESTROYED when already destroyed.
385
393
  * @example
386
394
  * await element.mount('#card-field');
387
395
  */
@@ -420,6 +428,7 @@ var CheckoutElement = class _CheckoutElement {
420
428
  iframe.style.border = "0";
421
429
  iframe.style.width = "100%";
422
430
  iframe.style.display = "block";
431
+ iframe.setAttribute("scrolling", "no");
423
432
  container.appendChild(iframe);
424
433
  this.iframe = iframe;
425
434
  this.installResizeListener();
@@ -479,10 +488,10 @@ var CheckoutElement = class _CheckoutElement {
479
488
  });
480
489
  }
481
490
  /**
482
- * 卸载:发 destroyElement、移除 iframe;幂等;保留句柄与配置。
491
+ * Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.
483
492
  *
484
- * @returns 完成后 resolve(未挂载时直接 resolve)。
485
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
493
+ * @returns Resolves on completion (resolves directly when not mounted).
494
+ * @throws INSTANCE_DESTROYED when already destroyed.
486
495
  */
487
496
  async unmount() {
488
497
  this.assertAlive();
@@ -498,7 +507,7 @@ var CheckoutElement = class _CheckoutElement {
498
507
  }
499
508
  }
500
509
  /**
501
- * 销毁句柄:幂等;移除 iframe;此后非 destroy/off 调用抛 INSTANCE_DESTROYED。
510
+ * Destroy the handle: idempotent; remove the iframe; afterwards any non-destroy/off call throws INSTANCE_DESTROYED.
502
511
  */
503
512
  destroy() {
504
513
  if (this.status === "destroyed") {
@@ -522,11 +531,11 @@ var CheckoutElement = class _CheckoutElement {
522
531
  this.onDestroy?.(this.elementId);
523
532
  }
524
533
  /**
525
- * 更新 Element 配置:发 updateElement;非法字段由托管侧整次拒绝。
534
+ * Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.
526
535
  *
527
- * @param patch - 配置增量。
528
- * @returns 成功写回本地 options 后 resolve。
529
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
536
+ * @param patch - Configuration delta.
537
+ * @returns Resolves after successfully writing back the local options.
538
+ * @throws INSTANCE_DESTROYED when already destroyed.
530
539
  */
531
540
  async update(patch) {
532
541
  this.assertAlive();
@@ -540,26 +549,26 @@ var CheckoutElement = class _CheckoutElement {
540
549
  }
541
550
  }
542
551
  /**
543
- * 注册事件 handler(透传到 transport,按事件名分发)。
552
+ * Register an event handler (passed through to the transport, dispatched by event name).
544
553
  *
545
- * @param event - 事件名。
546
- * @param handler - 回调。
547
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
554
+ * @param event - Event name.
555
+ * @param handler - Callback.
556
+ * @throws INSTANCE_DESTROYED when already destroyed.
548
557
  */
549
558
  on(event, handler) {
550
559
  this.assertAlive();
551
560
  this.transport.on(event, handler);
552
561
  }
553
562
  /**
554
- * 注销事件 handler(销毁后仍可安全调用)。
563
+ * Unregister an event handler (still safe to call after destruction).
555
564
  *
556
- * @param event - 事件名。
557
- * @param handler - 先前注册的回调。
565
+ * @param event - Event name.
566
+ * @param handler - The previously registered callback.
558
567
  */
559
568
  off(event, handler) {
560
569
  this.transport.off(event, handler);
561
570
  }
562
- /** 已挂载后安装输入类动作方法(由构造后自动调用)。 */
571
+ /** Install input-class action methods (automatically called after construction). */
563
572
  installInputActions() {
564
573
  if (!INPUT_TYPES.includes(this.type)) {
565
574
  return;
@@ -569,10 +578,10 @@ var CheckoutElement = class _CheckoutElement {
569
578
  this.clear = () => this.sendAction("clear");
570
579
  }
571
580
  /**
572
- * 仅 expressCheckout:常驻订阅 expressAvailabilityChange,更新本地可用性快照。
581
+ * expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.
573
582
  *
574
- * 该事件载荷不含 elementId;express 为单例(由 CheckoutInstance 保证),
575
- * 故无需按 elementId 过滤。非 express 类型不订阅。
583
+ * This event payload has no elementId; express is a singleton (guaranteed by CheckoutInstance),
584
+ * so there is no need to filter by elementId. Non-express types do not subscribe.
576
585
  */
577
586
  installExpressAvailability() {
578
587
  if (this.type !== "expressCheckout") {
@@ -590,18 +599,18 @@ var CheckoutElement = class _CheckoutElement {
590
599
  this.availabilityHandler = handler;
591
600
  this.transport.on("expressAvailabilityChange", handler);
592
601
  }
593
- /** 发送一条输入类动作命令(销毁后同步抛 INSTANCE_DESTROYED)。 */
602
+ /** Send one input-class action command (synchronously throws INSTANCE_DESTROYED after destruction). */
594
603
  sendAction(name) {
595
604
  this.assertAlive();
596
605
  return this.transport.request(name, { elementId: this.elementId }).then(() => void 0);
597
606
  }
598
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
607
+ /** Throw INSTANCE_DESTROYED if already destroyed. */
599
608
  assertAlive() {
600
609
  if (this.status === "destroyed") {
601
610
  throw makeError2(ErrorCode2.INSTANCE_DESTROYED, "Checkout instance has been destroyed", this.elementId);
602
611
  }
603
612
  }
604
- /** 解析挂载容器(选择器须唯一命中一个元素)。 */
613
+ /** Resolve the mount container (the selector must uniquely match a single element). */
605
614
  resolveContainer(selectorOrEl) {
606
615
  if (typeof selectorOrEl !== "string") {
607
616
  return selectorOrEl;
@@ -614,26 +623,34 @@ var CheckoutElement = class _CheckoutElement {
614
623
  return node instanceof HTMLElement ? node : null;
615
624
  }
616
625
  /**
617
- * 解析 iframe src:拆分字段走 /sdk/field/,其余走 /sdk/element/,带 checkoutId 与 elementId;
618
- * 若提供了初始 colorScheme,则追加 `&colorScheme=<值>`(首帧无闪);
619
- * 若初始 appearance 含 variables 或 rules,则追加
620
- * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>`(首帧即生效,无闪)。
621
- * 仅含 colorScheme(无 variables/rules)的 appearance 不拼接,避免空外观串。
622
- * 对 expressCheckout,另追加钱包开关 / 每钱包 buttonType / 全局 buttonHeight
623
- * (见 {@link resolveExpressQuery})。
626
+ * Resolve the iframe src: split fields go to /sdk/field/, the rest to /sdk/element/, with
627
+ * checkoutId and elementId; if an initial colorScheme is provided, append `&colorScheme=<value>`
628
+ * (no flash on the first frame); if the initial appearance contains variables or rules, append
629
+ * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>` (takes effect on the first
630
+ * frame, no flash). An appearance containing only colorScheme (no variables/rules) is not
631
+ * appended, to avoid an empty appearance string. For expressCheckout, additionally append the
632
+ * wallet toggles / per-wallet buttonType / global buttonHeight (see {@link resolveExpressQuery}).
624
633
  *
625
- * elementId 随 src 下发,供托管 child 在 iframe-resize 消息中原样回传,父窗口据此在
626
- * 同页多个 iframe 间按句柄分发高度。
634
+ * elementId is sent with the src so the hosted child echoes it verbatim in iframe-resize
635
+ * messages, letting the parent window dispatch the height per handle among multiple iframes on
636
+ * the same page.
627
637
  */
628
638
  resolveSrc() {
629
639
  const segment = FIELD_TYPES.includes(this.type) ? "field" : "element";
630
640
  let query = `?checkoutId=${encodeURIComponent(this.checkoutId)}`;
631
641
  query += `&elementId=${encodeURIComponent(this.elementId)}`;
642
+ if (this.type === "address") {
643
+ const mode = this.options?.mode;
644
+ if (mode) {
645
+ query += `&mode=${encodeURIComponent(mode)}`;
646
+ }
647
+ }
632
648
  if (this.colorScheme) {
633
649
  query += `&colorScheme=${encodeURIComponent(this.colorScheme)}`;
634
650
  }
635
- if (this.appearance && (this.appearance.variables || this.appearance.rules)) {
636
- query += `&appearance=${encodeURIComponent(JSON.stringify(this.appearance))}`;
651
+ const effectiveAppearance = this.perElementAppearance() ?? this.appearance;
652
+ if (effectiveAppearance && (effectiveAppearance.variables || effectiveAppearance.rules)) {
653
+ query += `&appearance=${encodeURIComponent(JSON.stringify(effectiveAppearance))}`;
637
654
  }
638
655
  if (this.type === "expressCheckout") {
639
656
  query += this.resolveExpressQuery();
@@ -641,16 +658,26 @@ var CheckoutElement = class _CheckoutElement {
641
658
  return `${this.hostOrigin}/sdk/${segment}/${this.type}${query}`;
642
659
  }
643
660
  /**
644
- * 构造 expressCheckout 专属 query 片段(仅 expressCheckout 调用):
645
- * - 钱包显式置为 `false` → `&applePay=false` / `&googlePay=false`(强制隐藏,保持
646
- * 既有开关语义;`true` / 缺省 → 自动,不拼开关);
647
- * - 钱包以对象形态配置且含 `buttonType` → `&applePayButtonType=<v>` /
648
- * `&googlePayButtonType=<v>`(布尔开关 / 缺省 → 不拼);
649
- * - 全局 `buttonHeight`(有限数)→ `&buttonHeight=<n>`。
661
+ * Extract a per-element appearance (from the Element's own options, i.e. the second
662
+ * argument of create(type, options)). Returns undefined when absent. Used so per-element
663
+ * customization (e.g. CardFieldElementOptions.appearance) is honored on the first frame.
664
+ */
665
+ perElementAppearance() {
666
+ const opts = this.options;
667
+ return opts?.appearance;
668
+ }
669
+ /**
670
+ * Build the expressCheckout-specific query fragment (only called for expressCheckout):
671
+ * - a wallet explicitly set to `false` → `&applePay=false` / `&googlePay=false` (force hidden,
672
+ * preserving the existing toggle semantics; `true` / absent → automatic, no toggle appended);
673
+ * - a wallet configured in object form containing `buttonType` → `&applePayButtonType=<v>` /
674
+ * `&googlePayButtonType=<v>` (boolean toggle / absent → not appended);
675
+ * - global `buttonHeight` (a finite number) → `&buttonHeight=<n>`.
650
676
  *
651
- * 仅在有值时拼接;不拼空串。本片段不触碰卡值,仅承载展示配置。
677
+ * Appended only when there is a value; empty strings are not appended. This fragment never
678
+ * touches card values and only carries display configuration.
652
679
  *
653
- * @returns 以 `&` 起始的 query 片段(无可拼项时返回空串)。
680
+ * @returns A query fragment starting with `&` (returns an empty string when nothing is appendable).
654
681
  */
655
682
  resolveExpressQuery() {
656
683
  const options = this.options;
@@ -678,13 +705,14 @@ var CheckoutElement = class _CheckoutElement {
678
705
  return query;
679
706
  }
680
707
  /**
681
- * 安装 iframe 自适应高度监听:订阅 window 'message',仅采信来自本 iframe
682
- * `contentWindow` 且 origin === hostOrigin 的合法 {@link IframeResizeMessage},
683
- * 且 elementId 与本句柄匹配时,将外层 iframe 高度调整为内容真实高度(宽度保持 100%)。
708
+ * Install the iframe auto-height listener: subscribe to window 'message', only trust a valid
709
+ * {@link IframeResizeMessage} from this iframe's `contentWindow` with origin === hostOrigin, and
710
+ * when the elementId matches this handle, adjust the outer iframe's height to the content's real
711
+ * height (width stays 100%).
684
712
  *
685
- * 安全:先校验 `event.source === iframe.contentWindow`(关键来源校验),
686
- * 再校验 `event.origin === hostOrigin`(更严格的同源校验),防跨源伪造。
687
- * 高度去重:与上次相同则跳过,避免冗余 style 写入。
713
+ * Security: first verify `event.source === iframe.contentWindow` (the key source check), then
714
+ * verify `event.origin === hostOrigin` (a stricter same-origin check), to prevent cross-origin forgery.
715
+ * Height deduplication: skip if identical to the last one, to avoid redundant style writes.
688
716
  */
689
717
  installResizeListener() {
690
718
  const handler = (event) => {
@@ -713,7 +741,7 @@ var CheckoutElement = class _CheckoutElement {
713
741
  this.resizeHandler = handler;
714
742
  window.addEventListener("message", handler);
715
743
  }
716
- /** 解绑 iframe 自适应高度监听并重置去重状态(幂等)。 */
744
+ /** Unbind the iframe auto-height listener and reset the deduplication state (idempotent). */
717
745
  teardownResizeListener() {
718
746
  if (this.resizeHandler) {
719
747
  window.removeEventListener("message", this.resizeHandler);
@@ -721,7 +749,7 @@ var CheckoutElement = class _CheckoutElement {
721
749
  }
722
750
  this.lastHeight = null;
723
751
  }
724
- /** 移除 iframe DOM 节点(若存在),并解绑自适应高度监听。 */
752
+ /** Remove the iframe DOM node (if present) and unbind the auto-height listener. */
725
753
  removeIframe() {
726
754
  this.teardownResizeListener();
727
755
  if (this.iframe && this.iframe.parentNode) {
@@ -729,7 +757,7 @@ var CheckoutElement = class _CheckoutElement {
729
757
  }
730
758
  this.iframe = null;
731
759
  }
732
- /** 工厂:等价于 `new CheckoutElement(opts)`(供 CheckoutInstance 统一入口)。 */
760
+ /** Factory: equivalent to `new CheckoutElement(opts)` (a unified entry for CheckoutInstance). */
733
761
  static create(opts) {
734
762
  return new _CheckoutElement(opts);
735
763
  }
@@ -754,14 +782,16 @@ function makeError3(code, message, category = "configuration") {
754
782
  }
755
783
  var CheckoutInstance = class {
756
784
  constructor(opts) {
757
- /** 已创建的 Element(按 elementId 索引)。 */
785
+ /** Created Elements (indexed by elementId). */
758
786
  /**
759
- * 互斥注册表的**单一数据源**:elementId → 已创建的 CheckoutElement。
760
- * 互斥判定由本 Map 实时推导(见 {@link assertNoConflict}),元素 `destroy()` 经
761
- * {@link releaseElement} 从本 Map 移除以释放名额(允许销毁后重建同类互斥 Element)。
787
+ * The **single source of truth** for the mutual-exclusion registry: elementId → the created
788
+ * CheckoutElement. Mutual-exclusion decisions are derived live from this Map (see
789
+ * {@link assertNoConflict}); an element's `destroy()` removes itself from this Map via
790
+ * {@link releaseElement} to free the slot (allowing a mutually-exclusive Element of the same
791
+ * kind to be recreated after destruction).
762
792
  */
763
793
  this.elements = /* @__PURE__ */ new Map();
764
- /** 当前未决 confirm(同一时刻至多一个)。 */
794
+ /** The current pending confirm (at most one at a time). */
765
795
  this.pendingConfirm = null;
766
796
  this.destroyed = false;
767
797
  this.checkoutId = opts.checkoutId;
@@ -777,17 +807,17 @@ var CheckoutInstance = class {
777
807
  this.transport.on("paymentResult", this.paymentResultHandler);
778
808
  }
779
809
  /**
780
- * 创建一个 Element 句柄(不自动挂载;互斥冲突抛 ELEMENT_CONFLICT)。
810
+ * Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).
781
811
  *
782
- * 互斥规则(spec 6):
783
- * - 聚合 `card` 与任一拆分字段(cardNumber/cardExpiry/cardCvc)互斥。
784
- * - 同 `mode`(billing/shipping)的 address 不可重复。
785
- * - `expressCheckout` 单例。
812
+ * Mutual-exclusion rules (spec 6):
813
+ * - The combined `card` and any split field (cardNumber/cardExpiry/cardCvc) are mutually exclusive.
814
+ * - An address with the same `mode` (billing/shipping) cannot be duplicated.
815
+ * - `expressCheckout` is a singleton.
786
816
  *
787
- * @param type - Element 类型。
788
- * @param options - 初始 Element 配置。
789
- * @returns 新建的 CheckoutElement。
790
- * @throws 已销毁时抛 INSTANCE_DESTROYED;互斥冲突抛 ELEMENT_CONFLICT。
817
+ * @param type - Element type.
818
+ * @param options - Initial Element configuration.
819
+ * @returns The newly created CheckoutElement.
820
+ * @throws INSTANCE_DESTROYED when already destroyed; ELEMENT_CONFLICT on a mutual-exclusion conflict.
791
821
  */
792
822
  create(type, options) {
793
823
  this.assertAlive();
@@ -809,12 +839,13 @@ var CheckoutInstance = class {
809
839
  return element;
810
840
  }
811
841
  /**
812
- * 发起支付确认。生成 operationId,发 confirm 命令拿 accepted,返回的 Promise
813
- * 由后续同 operationId 的 paymentResult 事件兑现。进行中再次调用 → CHECKOUT_BUSY。
842
+ * Initiate a payment confirmation. Generates an operationId, sends the confirm command to obtain
843
+ * accepted, and the returned Promise is fulfilled by a later paymentResult event with the same
844
+ * operationId. Calling again while in progress → CHECKOUT_BUSY.
814
845
  *
815
- * @param opts - 账单/配送地址、姓名、邮箱、returnUrl 等可选参数。
816
- * @returns 支付终态结果 PaymentOutcome。
817
- * @throws 已销毁时 reject INSTANCE_DESTROYED;进行中 reject CHECKOUT_BUSY。
846
+ * @param opts - Optional parameters such as billing/shipping address, name, email, returnUrl.
847
+ * @returns The terminal PaymentOutcome.
848
+ * @throws rejects INSTANCE_DESTROYED when destroyed; rejects CHECKOUT_BUSY when in progress.
818
849
  */
819
850
  confirm(opts) {
820
851
  if (this.destroyed) {
@@ -840,10 +871,10 @@ var CheckoutInstance = class {
840
871
  });
841
872
  }
842
873
  /**
843
- * 获取最新会话视图(透传 getSession 命令并写回本地快照)。
874
+ * Get the latest session view (passes through the getSession command and writes back the local snapshot).
844
875
  *
845
- * @returns 最新 PublicSessionView。
846
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
876
+ * @returns The latest PublicSessionView.
877
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
847
878
  */
848
879
  async getSession() {
849
880
  this.assertAlive();
@@ -852,10 +883,10 @@ var CheckoutInstance = class {
852
883
  return resp;
853
884
  }
854
885
  /**
855
- * 刷新会话(透传 refresh 命令并写回本地快照)。
886
+ * Refresh the session (passes through the refresh command and writes back the local snapshot).
856
887
  *
857
- * @returns 刷新后的 PublicSessionView。
858
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
888
+ * @returns The refreshed PublicSessionView.
889
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
859
890
  */
860
891
  async refresh() {
861
892
  this.assertAlive();
@@ -864,20 +895,20 @@ var CheckoutInstance = class {
864
895
  return resp;
865
896
  }
866
897
  /**
867
- * 查询支付状态(原样返回 TxnStatus,不转义)。
898
+ * Query the payment status (returns TxnStatus as-is, no transformation).
868
899
  *
869
- * @returns txnStatus(可能含 txnId)。
870
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
900
+ * @returns txnStatus (may include txnId).
901
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
871
902
  */
872
903
  async getPaymentStatus() {
873
904
  this.assertAlive();
874
905
  return await this.transport.request("getPaymentStatus", {});
875
906
  }
876
907
  /**
877
- * 更新结账外观/语言(不改会话或金额)。
908
+ * Update the checkout appearance/locale (does not change the session or the amount).
878
909
  *
879
- * @param patch - locale / appearance 增量。
880
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
910
+ * @param patch - locale / appearance delta.
911
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
881
912
  */
882
913
  async updateCheckout(patch) {
883
914
  this.assertAlive();
@@ -885,29 +916,30 @@ var CheckoutInstance = class {
885
916
  await this.transport.request("updateCheckout", payload);
886
917
  }
887
918
  /**
888
- * 注册 Checkout 级事件 handler(透传 transport)。
919
+ * Register a Checkout-level event handler (passes through the transport).
889
920
  *
890
- * @param event - 事件名。
891
- * @param handler - 回调。
892
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
921
+ * @param event - Event name.
922
+ * @param handler - Callback.
923
+ * @throws INSTANCE_DESTROYED when destroyed.
893
924
  */
894
925
  on(event, handler) {
895
926
  this.assertAlive();
896
927
  this.transport.on(event, handler);
897
928
  }
898
929
  /**
899
- * 注销事件 handler(销毁后仍可安全调用)。
930
+ * Unregister an event handler (safe to call after destruction).
900
931
  *
901
- * @param event - 事件名。
902
- * @param handler - 先前注册的回调。
932
+ * @param event - Event name.
933
+ * @param handler - The previously registered callback.
903
934
  */
904
935
  off(event, handler) {
905
936
  this.transport.off(event, handler);
906
937
  }
907
938
  /**
908
- * 销毁实例:幂等。释放全部 Element、解绑 paymentResult 监听、以
909
- * INSTANCE_DESTROYED reject 未决 confirm、发 destroy 命令并销毁 transport。
910
- * 此后迟到的 paymentResult 一律忽略,不改写已结束的 Promise。
939
+ * Destroy the instance: idempotent. Releases all Elements, unbinds the paymentResult listener,
940
+ * rejects the pending confirm with INSTANCE_DESTROYED, sends the destroy command and destroys
941
+ * the transport. Any late paymentResult afterwards is ignored and does not overwrite an already
942
+ * settled Promise.
911
943
  */
912
944
  destroy() {
913
945
  if (this.destroyed) {
@@ -928,13 +960,13 @@ var CheckoutInstance = class {
928
960
  void Promise.resolve(this.transport.request("destroy", {})).catch(() => void 0);
929
961
  this.transport.destroy();
930
962
  }
931
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
963
+ /** Throw INSTANCE_DESTROYED if already destroyed. */
932
964
  assertAlive() {
933
965
  if (this.destroyed) {
934
966
  throw makeError3(ErrorCode3.INSTANCE_DESTROYED, "Checkout instance has been destroyed", "runtime");
935
967
  }
936
968
  }
937
- /** 互斥检查:冲突时抛 ELEMENT_CONFLICT(由 elements Map 实时推导,不改变注册表)。 */
969
+ /** Mutual-exclusion check: throws ELEMENT_CONFLICT on conflict (derived live from the elements Map, does not mutate the registry). */
938
970
  assertNoConflict(type, options) {
939
971
  const existing = [...this.elements.values()];
940
972
  const has = (t) => existing.some((e) => e.type === t);
@@ -984,9 +1016,10 @@ var CheckoutInstance = class {
984
1016
  }
985
1017
  }
986
1018
  /**
987
- * 元素 `destroy()` 回调:从互斥注册表(elements Map)移除该 elementId,释放名额。
988
- * 幂等;实例整体 destroy 时(已置 destroyed)走统一 `elements.clear()` 路径,此处 no-op,
989
- * 避免遍历 elements 调 element.destroy() 时的并发修改。
1019
+ * Element `destroy()` callback: remove this elementId from the mutual-exclusion registry (the
1020
+ * elements Map) to free the slot. Idempotent; when the whole instance is destroyed (destroyed
1021
+ * already set) it goes through the unified `elements.clear()` path, so this is a no-op here,
1022
+ * avoiding concurrent modification while iterating elements to call element.destroy().
990
1023
  */
991
1024
  releaseElement(elementId) {
992
1025
  if (this.destroyed) {
@@ -994,7 +1027,7 @@ var CheckoutInstance = class {
994
1027
  }
995
1028
  this.elements.delete(elementId);
996
1029
  }
997
- /** 处理 paymentResult 事件:按 operationId 兑现未决 confirm;迟到/不匹配忽略。 */
1030
+ /** Handle the paymentResult event: fulfill the pending confirm by operationId; late/mismatched ones are ignored. */
998
1031
  handlePaymentResult(payload) {
999
1032
  if (this.destroyed) {
1000
1033
  return;
@@ -1021,9 +1054,10 @@ var CheckoutInstance = class {
1021
1054
  // src/config/env.ts
1022
1055
  var HOST_ORIGINS = {
1023
1056
  dev: "https://checkout.sunbay-dev.com",
1057
+ test: "https://checkout.sunbay-uat.us",
1024
1058
  prod: "https://checkout.sunbay.us"
1025
1059
  };
1026
- function resolveHostOrigin(env = "dev", override) {
1060
+ function resolveHostOrigin(env = "prod", override) {
1027
1061
  if (override) {
1028
1062
  return override;
1029
1063
  }
@@ -1122,7 +1156,7 @@ if (typeof window !== "undefined") {
1122
1156
  const w = window;
1123
1157
  w.SunbayCheckout = { ...w.SunbayCheckout ?? {}, createCheckout };
1124
1158
  }
1125
- var version = true ? "0.0.1" : "0.0.0";
1159
+ var version = true ? "0.0.3" : "0.0.0";
1126
1160
  export {
1127
1161
  CheckoutElement,
1128
1162
  CheckoutInstance,