@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.d.ts CHANGED
@@ -2,249 +2,273 @@ import { ElementType, CommandName, EventName, ElementOptions, Appearance, Elemen
2
2
  export { AddressElementOptions, AddressInfo, Appearance, CardElementOptions, CardFieldElementOptions, CheckoutError, CheckoutErrorCategory, ElementOptions, ElementStatus, ElementType, ErrorCode, ErrorCodeValue, ExpressCheckoutElementOptions, PaymentMethod, PaymentMethodFilter, PaymentOutcome, PublicPaymentMethod, PublicProductLine, PublicSessionView, TxnStatus } from '@sunbay/checkout-sdk-protocol';
3
3
 
4
4
  /**
5
- * CheckoutElement —— 商户侧 Element 句柄(spec 6)。
5
+ * CheckoutElement — the merchant-side Element handle (spec 6).
6
6
  *
7
- * 职责:
8
- * - 由 `CheckoutInstance.create` 构造(单测时以 mock transport + 构造参注入)。
9
- * - 在商户 DOM 目标挂载**可见** iframe,并通过 transport 转发 Element 命令。
10
- * - 维护状态机 `created → loading → ready → destroyed`(`loadError` 为旁支)。
11
- * - 订阅 transport 的 `elementReady` / `loadError` 事件,按**自身 elementId** 过滤,
12
- * 驱动 `mount()` 的 resolve / reject。
7
+ * Responsibilities:
8
+ * - Constructed by `CheckoutInstance.create` (in unit tests, injected via a mock transport +
9
+ * constructor args).
10
+ * - Mount a **visible** iframe at the merchant DOM target and forward Element commands through the
11
+ * transport.
12
+ * - Maintain the state machine `created → loading → ready → destroyed` (with `loadError` as a side branch).
13
+ * - Subscribe to the transport's `elementReady` / `loadError` events, filter by **its own elementId**,
14
+ * and drive the resolve / reject of `mount()`.
13
15
  *
14
- * 安全约束(AGENTS/spec):本层只做 DOM 挂载与协议命令转发,**绝不触碰卡值**。
15
- * 卡数据仅存在于托管 iframe 内,不经由本句柄或 transport 传输。
16
+ * Security constraint (AGENTS/spec): this layer only does DOM mounting and protocol command
17
+ * forwarding and **never touches card values**. Card data exists only inside the hosted iframe and
18
+ * is never transmitted via this handle or the transport.
16
19
  */
17
20
 
18
21
  /**
19
- * transport 依赖的最小接口(由 MainFrameTransport 满足)。
22
+ * The minimal interface the transport depends on (satisfied by MainFrameTransport).
20
23
  *
21
- * 仅声明 Element 句柄需要的方法,便于单测注入 mock。
24
+ * Only declares the methods the Element handle needs, to make mock injection in unit tests easy.
22
25
  */
23
26
  interface ElementTransport {
24
27
  request(name: CommandName, payload: unknown): Promise<unknown>;
25
28
  on(event: EventName, handler: (payload: unknown) => void): void;
26
29
  off(event: EventName, handler: (payload: unknown) => void): void;
27
30
  }
28
- /** CheckoutElement 构造参数(由 CheckoutInstance.create 组装)。 */
31
+ /** CheckoutElement constructor parameters (assembled by CheckoutInstance.create). */
29
32
  interface CheckoutElementOptions {
30
- /** Element 唯一 id(由实例生成,用于命令/事件配对)。 */
33
+ /** Unique Element id (generated by the instance, used to pair commands/events). */
31
34
  elementId: string;
32
- /** Element 类型(决定 iframe 路径与可用动作)。 */
35
+ /** Element type (determines the iframe path and available actions). */
33
36
  type: ElementType;
34
- /** 托管页面 origin(不带尾部斜杠)。 */
37
+ /** Hosted page origin (without a trailing slash). */
35
38
  hostOrigin: string;
36
39
  /**
37
- * 当前 checkout 会话 id。随 iframe src 以 `?checkoutId=` 下发,供托管字段/element
38
- * iframe 加入正确的同源信令频道(`sunbay-checkout:<checkoutId>`)与 main 配对;
39
- * 缺失会导致字段频道名不匹配、永不就绪。
40
+ * The current checkout session id. Sent with the iframe src as `?checkoutId=`, so that hosted
41
+ * fields/element iframes join the correct same-origin signaling channel
42
+ * (`sunbay-checkout:<checkoutId>`) and pair with main; missing it causes the field channel name
43
+ * to mismatch and never become ready.
40
44
  */
41
45
  checkoutId: string;
42
- /** 传输层。 */
46
+ /** The transport layer. */
43
47
  transport: ElementTransport;
44
- /** 初始 Element 配置(随 createElement 下发)。 */
48
+ /** Initial Element configuration (sent with createElement). */
45
49
  initialOptions?: ElementOptions;
46
50
  /**
47
- * 句柄销毁回调(由 CheckoutInstance 注入)。`destroy()` 成功销毁时以本 elementId 调用,
48
- * 使实例从互斥注册表(elements Map)移除自身,从而允许销毁后重建同类互斥 Element
49
- * (如切换 expressCheckout 的 buttonType/buttonHeight 需重建 iframe)。`unmount()` 不调用
50
- * (保留句柄、仍占名额、可 remount)。
51
+ * Handle destruction callback (injected by CheckoutInstance). Called with this elementId when
52
+ * `destroy()` successfully destroys the handle, so the instance removes itself from the
53
+ * mutual-exclusion registry (elements Map), thereby allowing a mutually-exclusive Element of the
54
+ * same kind to be recreated after destruction (e.g. switching expressCheckout's
55
+ * buttonType/buttonHeight requires recreating the iframe). Not called by `unmount()` (which
56
+ * keeps the handle, still occupies the slot, and can remount).
51
57
  */
52
58
  onDestroy?: (elementId: string) => void;
53
59
  /**
54
- * 初始配色方案(来自 createCheckout 的 appearance.colorScheme)。
55
- * 仅当提供时拼进 iframe src 的 `&colorScheme=<值>`,使托管 iframe 在首帧即按该
56
- * 配色渲染(无闪)。缺省时不拼接,由托管侧按默认(light)处理。运行时切换走
57
- * updateCheckout 命令,不走此处。
60
+ * Initial color scheme (from createCheckout's appearance.colorScheme).
61
+ * Only when provided is it appended to the iframe src's `&colorScheme=<value>`, so the hosted
62
+ * iframe renders with that scheme on the first frame (no flash). When absent it is not appended
63
+ * and the hosted side handles the default (light). Runtime switching goes through the
64
+ * updateCheckout command, not here.
58
65
  */
59
66
  colorScheme?: 'light' | 'dark' | 'auto';
60
67
  /**
61
- * 初始外观定制(来自 createCheckout 的 appearance)。
62
- * 仅当含 `variables` 或 `rules` 时序列化进 iframe src 的
63
- * `&appearance=<encodeURIComponent(JSON)>`,使托管 iframe 在首帧即按该外观渲染(无闪)。
64
- * 仅含 `colorScheme`(无 variables/rules)时不拼接,避免空外观字符串;colorScheme 走独立
65
- * `&colorScheme=`。运行时切换走 updateCheckout 命令,不走此处。
68
+ * Initial appearance customization (from createCheckout's appearance).
69
+ * Only when it contains `variables` or `rules` is it serialized into the iframe src's
70
+ * `&appearance=<encodeURIComponent(JSON)>`, so the hosted iframe renders with that appearance on
71
+ * the first frame (no flash). When it contains only `colorScheme` (no variables/rules) it is not
72
+ * appended, to avoid an empty appearance string; colorScheme goes via the separate
73
+ * `&colorScheme=`. Runtime switching goes through the updateCheckout command, not here.
66
74
  */
67
75
  appearance?: Appearance;
68
76
  }
69
77
  declare class CheckoutElement {
70
- /** Element 唯一 id。 */
78
+ /** Unique Element id. */
71
79
  readonly elementId: string;
72
- /** Element 类型。 */
80
+ /** Element type. */
73
81
  readonly type: ElementType;
74
- /** 地址 Element 的 mode(billing/shipping;仅 address 有意义),供实例按 mode 判互斥。 */
82
+ /** The address Element's mode (billing/shipping; only meaningful for address), used by the instance to judge mutual exclusion by mode. */
75
83
  readonly mode: string | undefined;
76
84
  private readonly hostOrigin;
77
85
  private readonly checkoutId;
78
86
  private readonly transport;
79
87
  private options;
80
- /** 初始配色方案(拼进 iframe src;缺省不拼)。 */
88
+ /** Initial color scheme (appended to the iframe src; not appended when absent). */
81
89
  private readonly colorScheme;
82
- /** 初始外观定制(含 variables/rules 时拼进 iframe src;缺省或仅 colorScheme 不拼)。 */
90
+ /** Initial appearance customization (appended to the iframe src when it contains variables/rules; not appended when absent or colorScheme-only). */
83
91
  private readonly appearance;
84
- /** 销毁回调(实例注入):destroy() 末尾调用,使实例从互斥注册表移除自身以释放名额。 */
92
+ /** Destruction callback (injected by the instance): called at the end of destroy() so the instance removes itself from the mutual-exclusion registry to free the slot. */
85
93
  private readonly onDestroy;
86
94
  private status;
87
95
  private container;
88
96
  private iframe;
89
- /** mount 期间临时订阅的事件 handler(resolve/reject 后解绑)。 */
97
+ /** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */
90
98
  private readyHandler;
91
99
  private loadErrorHandler;
92
100
  /**
93
- * 仅 expressCheckout:各钱包可用性快照。
94
- * 初次来自 elementReady.availableWallets,之后随 expressAvailabilityChange 更新。
101
+ * expressCheckout only: a snapshot of each wallet's availability.
102
+ * Initially from elementReady.availableWallets, then updated by expressAvailabilityChange.
95
103
  */
96
104
  private availableWallets;
97
- /** 仅 expressCheckout:常驻订阅的 expressAvailabilityChange handler(destroy 时解绑)。 */
105
+ /** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */
98
106
  private availabilityHandler;
99
107
  /**
100
- * iframe 自适应高度:挂载期间常驻的 window 'message' 监听器(iframe 消失/销毁时解绑)。
101
- * 存引用以便 removeEventListener;null 表示未监听。
108
+ * iframe auto-height: the window 'message' listener that stays resident during mount (unbound
109
+ * when the iframe disappears/is destroyed). Keeps a reference so removeEventListener can be
110
+ * called; null means not listening.
102
111
  */
103
112
  private resizeHandler;
104
- /** 最近一次已应用的 iframe 高度(px),用于去重,避免冗余 style 写入;null 表示尚未设置。 */
113
+ /** The most recently applied iframe height (px), used for deduplication to avoid redundant style writes; null means not yet set. */
105
114
  private lastHeight;
106
115
  constructor(opts: CheckoutElementOptions);
107
- /** 当前状态(只读快照,供外部/测试观测)。 */
116
+ /** The current status (read-only snapshot, for external/test observation). */
108
117
  getStatus(): ElementStatus;
109
118
  /**
110
- * 仅 expressCheckout:返回当前各钱包可用性快照(其他类型恒为 undefined)。
119
+ * expressCheckout only: return the current snapshot of each wallet's availability (always
120
+ * undefined for other types).
111
121
  *
112
- * 初值来自 elementReady 的 availableWallets,之后由 expressAvailabilityChange 更新。
122
+ * The initial value comes from elementReady's availableWallets, then is updated by
123
+ * expressAvailabilityChange.
113
124
  *
114
- * @returns 可用性快照;尚未就绪或非 express 时为 undefined。
125
+ * @returns The availability snapshot; undefined when not yet ready or non-express.
115
126
  */
116
127
  getAvailableWallets(): AvailableWallets | undefined;
117
128
  /**
118
- * 挂载到商户 DOM 目标:插入可见 iframe、发 createElement、等 elementReady。
129
+ * Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.
119
130
  *
120
- * @param selectorOrEl - CSS 选择器或 HTMLElement 容器;容器须唯一且为空。
121
- * @returns elementReady 后 resolve;失败移除节点并 reject(loadError / INVALID_OPTIONS)。
122
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
131
+ * @param selectorOrEl - A CSS selector or an HTMLElement container; the container must be unique and empty.
132
+ * @returns Resolves after elementReady; on failure removes the node and rejects (loadError / INVALID_OPTIONS).
133
+ * @throws INSTANCE_DESTROYED when already destroyed.
123
134
  * @example
124
135
  * await element.mount('#card-field');
125
136
  */
126
137
  mount(selectorOrEl: string | HTMLElement): Promise<void>;
127
138
  /**
128
- * 卸载:发 destroyElement、移除 iframe;幂等;保留句柄与配置。
139
+ * Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.
129
140
  *
130
- * @returns 完成后 resolve(未挂载时直接 resolve)。
131
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
141
+ * @returns Resolves on completion (resolves directly when not mounted).
142
+ * @throws INSTANCE_DESTROYED when already destroyed.
132
143
  */
133
144
  unmount(): Promise<void>;
134
145
  /**
135
- * 销毁句柄:幂等;移除 iframe;此后非 destroy/off 调用抛 INSTANCE_DESTROYED。
146
+ * Destroy the handle: idempotent; remove the iframe; afterwards any non-destroy/off call throws INSTANCE_DESTROYED.
136
147
  */
137
148
  destroy(): void;
138
149
  /**
139
- * 更新 Element 配置:发 updateElement;非法字段由托管侧整次拒绝。
150
+ * Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.
140
151
  *
141
- * @param patch - 配置增量。
142
- * @returns 成功写回本地 options 后 resolve。
143
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
152
+ * @param patch - Configuration delta.
153
+ * @returns Resolves after successfully writing back the local options.
154
+ * @throws INSTANCE_DESTROYED when already destroyed.
144
155
  */
145
156
  update(patch: Partial<ElementOptions>): Promise<void>;
146
157
  /**
147
- * 注册事件 handler(透传到 transport,按事件名分发)。
158
+ * Register an event handler (passed through to the transport, dispatched by event name).
148
159
  *
149
- * @param event - 事件名。
150
- * @param handler - 回调。
151
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
160
+ * @param event - Event name.
161
+ * @param handler - Callback.
162
+ * @throws INSTANCE_DESTROYED when already destroyed.
152
163
  */
153
164
  on(event: EventName, handler: (payload: unknown) => void): void;
154
165
  /**
155
- * 注销事件 handler(销毁后仍可安全调用)。
166
+ * Unregister an event handler (still safe to call after destruction).
156
167
  *
157
- * @param event - 事件名。
158
- * @param handler - 先前注册的回调。
168
+ * @param event - Event name.
169
+ * @param handler - The previously registered callback.
159
170
  */
160
171
  off(event: EventName, handler: (payload: unknown) => void): void;
161
- /** 聚焦(仅输入类,expressCheckout 不提供)。 */
172
+ /** Focus (input classes only, not provided by expressCheckout). */
162
173
  focus?: () => Promise<void>;
163
- /** 失焦(仅输入类,expressCheckout 不提供)。 */
174
+ /** Blur (input classes only, not provided by expressCheckout). */
164
175
  blur?: () => Promise<void>;
165
- /** 清空(仅输入类,expressCheckout 不提供)。 */
176
+ /** Clear (input classes only, not provided by expressCheckout). */
166
177
  clear?: () => Promise<void>;
167
- /** 已挂载后安装输入类动作方法(由构造后自动调用)。 */
178
+ /** Install input-class action methods (automatically called after construction). */
168
179
  private installInputActions;
169
180
  /**
170
- * 仅 expressCheckout:常驻订阅 expressAvailabilityChange,更新本地可用性快照。
181
+ * expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.
171
182
  *
172
- * 该事件载荷不含 elementId;express 为单例(由 CheckoutInstance 保证),
173
- * 故无需按 elementId 过滤。非 express 类型不订阅。
183
+ * This event payload has no elementId; express is a singleton (guaranteed by CheckoutInstance),
184
+ * so there is no need to filter by elementId. Non-express types do not subscribe.
174
185
  */
175
186
  private installExpressAvailability;
176
- /** 发送一条输入类动作命令(销毁后同步抛 INSTANCE_DESTROYED)。 */
187
+ /** Send one input-class action command (synchronously throws INSTANCE_DESTROYED after destruction). */
177
188
  private sendAction;
178
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
189
+ /** Throw INSTANCE_DESTROYED if already destroyed. */
179
190
  private assertAlive;
180
- /** 解析挂载容器(选择器须唯一命中一个元素)。 */
191
+ /** Resolve the mount container (the selector must uniquely match a single element). */
181
192
  private resolveContainer;
182
193
  /**
183
- * 解析 iframe src:拆分字段走 /sdk/field/,其余走 /sdk/element/,带 checkoutId 与 elementId;
184
- * 若提供了初始 colorScheme,则追加 `&colorScheme=<值>`(首帧无闪);
185
- * 若初始 appearance 含 variables 或 rules,则追加
186
- * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>`(首帧即生效,无闪)。
187
- * 仅含 colorScheme(无 variables/rules)的 appearance 不拼接,避免空外观串。
188
- * 对 expressCheckout,另追加钱包开关 / 每钱包 buttonType / 全局 buttonHeight
189
- * (见 {@link resolveExpressQuery})。
194
+ * Resolve the iframe src: split fields go to /sdk/field/, the rest to /sdk/element/, with
195
+ * checkoutId and elementId; if an initial colorScheme is provided, append `&colorScheme=<value>`
196
+ * (no flash on the first frame); if the initial appearance contains variables or rules, append
197
+ * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>` (takes effect on the first
198
+ * frame, no flash). An appearance containing only colorScheme (no variables/rules) is not
199
+ * appended, to avoid an empty appearance string. For expressCheckout, additionally append the
200
+ * wallet toggles / per-wallet buttonType / global buttonHeight (see {@link resolveExpressQuery}).
190
201
  *
191
- * elementId 随 src 下发,供托管 child 在 iframe-resize 消息中原样回传,父窗口据此在
192
- * 同页多个 iframe 间按句柄分发高度。
202
+ * elementId is sent with the src so the hosted child echoes it verbatim in iframe-resize
203
+ * messages, letting the parent window dispatch the height per handle among multiple iframes on
204
+ * the same page.
193
205
  */
194
206
  private resolveSrc;
195
207
  /**
196
- * 构造 expressCheckout 专属 query 片段(仅 expressCheckout 调用):
197
- * - 钱包显式置为 `false` → `&applePay=false` / `&googlePay=false`(强制隐藏,保持
198
- * 既有开关语义;`true` / 缺省 → 自动,不拼开关);
199
- * - 钱包以对象形态配置且含 `buttonType` → `&applePayButtonType=<v>` /
200
- * `&googlePayButtonType=<v>`(布尔开关 / 缺省 → 不拼);
201
- * - 全局 `buttonHeight`(有限数)→ `&buttonHeight=<n>`。
208
+ * Extract a per-element appearance (from the Element's own options, i.e. the second
209
+ * argument of create(type, options)). Returns undefined when absent. Used so per-element
210
+ * customization (e.g. CardFieldElementOptions.appearance) is honored on the first frame.
211
+ */
212
+ private perElementAppearance;
213
+ /**
214
+ * Build the expressCheckout-specific query fragment (only called for expressCheckout):
215
+ * - a wallet explicitly set to `false` → `&applePay=false` / `&googlePay=false` (force hidden,
216
+ * preserving the existing toggle semantics; `true` / absent → automatic, no toggle appended);
217
+ * - a wallet configured in object form containing `buttonType` → `&applePayButtonType=<v>` /
218
+ * `&googlePayButtonType=<v>` (boolean toggle / absent → not appended);
219
+ * - global `buttonHeight` (a finite number) → `&buttonHeight=<n>`.
202
220
  *
203
- * 仅在有值时拼接;不拼空串。本片段不触碰卡值,仅承载展示配置。
221
+ * Appended only when there is a value; empty strings are not appended. This fragment never
222
+ * touches card values and only carries display configuration.
204
223
  *
205
- * @returns 以 `&` 起始的 query 片段(无可拼项时返回空串)。
224
+ * @returns A query fragment starting with `&` (returns an empty string when nothing is appendable).
206
225
  */
207
226
  private resolveExpressQuery;
208
227
  /**
209
- * 安装 iframe 自适应高度监听:订阅 window 'message',仅采信来自本 iframe
210
- * `contentWindow` 且 origin === hostOrigin 的合法 {@link IframeResizeMessage},
211
- * 且 elementId 与本句柄匹配时,将外层 iframe 高度调整为内容真实高度(宽度保持 100%)。
228
+ * Install the iframe auto-height listener: subscribe to window 'message', only trust a valid
229
+ * {@link IframeResizeMessage} from this iframe's `contentWindow` with origin === hostOrigin, and
230
+ * when the elementId matches this handle, adjust the outer iframe's height to the content's real
231
+ * height (width stays 100%).
212
232
  *
213
- * 安全:先校验 `event.source === iframe.contentWindow`(关键来源校验),
214
- * 再校验 `event.origin === hostOrigin`(更严格的同源校验),防跨源伪造。
215
- * 高度去重:与上次相同则跳过,避免冗余 style 写入。
233
+ * Security: first verify `event.source === iframe.contentWindow` (the key source check), then
234
+ * verify `event.origin === hostOrigin` (a stricter same-origin check), to prevent cross-origin forgery.
235
+ * Height deduplication: skip if identical to the last one, to avoid redundant style writes.
216
236
  */
217
237
  private installResizeListener;
218
- /** 解绑 iframe 自适应高度监听并重置去重状态(幂等)。 */
238
+ /** Unbind the iframe auto-height listener and reset the deduplication state (idempotent). */
219
239
  private teardownResizeListener;
220
- /** 移除 iframe DOM 节点(若存在),并解绑自适应高度监听。 */
240
+ /** Remove the iframe DOM node (if present) and unbind the auto-height listener. */
221
241
  private removeIframe;
222
- /** 工厂:等价于 `new CheckoutElement(opts)`(供 CheckoutInstance 统一入口)。 */
242
+ /** Factory: equivalent to `new CheckoutElement(opts)` (a unified entry for CheckoutInstance). */
223
243
  static create(opts: CheckoutElementOptions): CheckoutElement;
224
244
  }
225
245
 
226
246
  /**
227
- * CheckoutInstance —— 商户侧结账实例句柄(spec 4 / 7 / 8)。
247
+ * CheckoutInstance — the merchant-side checkout instance handle (spec 4 / 7 / 8).
228
248
  *
229
- * 职责:
230
- * - 由 `createCheckout` 在握手 + initialize 成功后构造(持有已就绪 transport、
231
- * runtimeId、capabilities、初始 PublicSessionView)。
232
- * - `create` 组装并返回 {@link CheckoutElement},维护**互斥注册表**(card 与拆分
233
- * 字段互斥、同 mode 地址互斥、expressCheckout 单例 → `ELEMENT_CONFLICT`)。
234
- * - `confirm` 生成 operationId、发 confirm 命令拿 accepted,Promise 由后续同
235
- * operationId 的 `paymentResult` 事件兑现;进行中再 confirm → `CHECKOUT_BUSY`。
236
- * - `getSession/refresh/getPaymentStatus/updateCheckout` 透传对应命令。
237
- * - `destroy` 幂等:释放 Element、解绑监听、以 INSTANCE_DESTROYED reject 未决 confirm、
238
- * 发 destroy 命令并销毁 transport;此后迟到的 `paymentResult` 一律忽略。
249
+ * Responsibilities:
250
+ * - Constructed by `createCheckout` after a successful handshake + initialize (holds the ready
251
+ * transport, runtimeId, capabilities, and the initial PublicSessionView).
252
+ * - `create` assembles and returns a {@link CheckoutElement}, maintaining a **mutual-exclusion
253
+ * registry** (card and split fields are mutually exclusive, same-mode addresses are mutually
254
+ * exclusive, expressCheckout is a singleton → `ELEMENT_CONFLICT`).
255
+ * - `confirm` generates an operationId, sends the confirm command to obtain accepted, and the
256
+ * Promise is fulfilled by a later `paymentResult` event with the same operationId; calling
257
+ * confirm again while in progress → `CHECKOUT_BUSY`.
258
+ * - `getSession/refresh/getPaymentStatus/updateCheckout` pass through to the corresponding commands.
259
+ * - `destroy` is idempotent: releases Elements, unbinds listeners, rejects the pending confirm with
260
+ * INSTANCE_DESTROYED, sends the destroy command and destroys the transport; any late
261
+ * `paymentResult` afterwards is ignored.
239
262
  *
240
- * 安全约束(AGENTS/spec):本层只搬运协议命令/事件,**绝不触碰卡值**。
263
+ * Security constraint (AGENTS/spec): this layer only ferries protocol commands/events and
264
+ * **never touches card values**.
241
265
  */
242
266
 
243
267
  /**
244
- * CheckoutInstance 依赖的最小 transport 接口(由 MainFrameTransport 满足)。
268
+ * The minimal transport interface CheckoutInstance depends on (satisfied by MainFrameTransport).
245
269
  *
246
- * 比 ElementTransport 多出 `onHello/createHiddenIframe/destroy`,供 createCheckout
247
- * 握手接线与实例销毁使用。单测可注入 mock。
270
+ * Adds `onHello/createHiddenIframe/destroy` beyond ElementTransport, used by createCheckout for
271
+ * handshake wiring and for instance destruction. Tests can inject a mock.
248
272
  */
249
273
  interface InstanceTransport {
250
274
  request(name: CommandName, payload: unknown): Promise<unknown>;
@@ -255,211 +279,219 @@ interface InstanceTransport {
255
279
  createHiddenIframe(): unknown;
256
280
  destroy(): void;
257
281
  }
258
- /** CheckoutInstance 构造参数(由 createCheckout 组装)。 */
282
+ /** CheckoutInstance constructor parameters (assembled by createCheckout). */
259
283
  interface CheckoutInstanceOptions {
260
- /** 结账唯一 id。 */
284
+ /** Unique checkout id. */
261
285
  checkoutId: string;
262
- /** 协商后的运行时 id(来自 RuntimeHello)。 */
286
+ /** Negotiated runtime id (from RuntimeHello). */
263
287
  runtimeId: string;
264
- /** 托管页面 origin(不带尾部斜杠)。 */
288
+ /** Hosted page origin (without a trailing slash). */
265
289
  hostOrigin: string;
266
- /** 已就绪 transport。 */
290
+ /** The ready transport. */
267
291
  transport: InstanceTransport;
268
- /** initialize 返回的 capabilities。 */
292
+ /** The capabilities returned by initialize. */
269
293
  capabilities: string[];
270
- /** initialize 返回的初始会话视图。 */
294
+ /** The initial session view returned by initialize. */
271
295
  session: PublicSessionView;
272
296
  /**
273
- * 初始外观定制(来自 createCheckout 的 appearance)。
274
- * 透传给每个 create 出的 Element,序列化进其 iframe src(首帧无闪):
275
- * - colorScheme 拼 `&colorScheme=`;
276
- * - variables/rules 拼 `&appearance=`(仅当非空)。
277
- * 缺省不拼接。运行时切换走 updateCheckout 命令。
297
+ * Initial appearance customization (from createCheckout's appearance).
298
+ * Passed through to every created Element and serialized into its iframe src (no flash on the
299
+ * first frame):
300
+ * - colorScheme appended as `&colorScheme=`;
301
+ * - variables/rules appended as `&appearance=` (only when non-empty).
302
+ * Not appended when absent. Runtime switching goes through the updateCheckout command.
278
303
  */
279
304
  appearance?: Appearance;
280
305
  }
281
- /** confirm 可选参数(对齐 ConfirmReq,去掉由实例生成的 operationId)。 */
306
+ /** Optional confirm parameters (aligned with ConfirmReq, minus the instance-generated operationId). */
282
307
  type ConfirmOptions = Omit<ConfirmReq, 'operationId'>;
283
308
  declare class CheckoutInstance {
284
- /** 结账唯一 id。 */
309
+ /** Unique checkout id. */
285
310
  readonly checkoutId: string;
286
- /** 协商后的运行时 id。 */
311
+ /** Negotiated runtime id. */
287
312
  readonly runtimeId: string;
288
- /** 协商后的能力集合。 */
313
+ /** Negotiated capability set. */
289
314
  readonly capabilities: readonly string[];
290
315
  private readonly hostOrigin;
291
316
  private readonly transport;
292
317
  private session;
293
- /** 初始外观定制(透传给 create 出的每个 Element 的 iframe src)。 */
318
+ /** Initial appearance customization (passed through to each created Element's iframe src). */
294
319
  private readonly appearance;
295
- /** 已创建的 Element(按 elementId 索引)。 */
320
+ /** Created Elements (indexed by elementId). */
296
321
  /**
297
- * 互斥注册表的**单一数据源**:elementId → 已创建的 CheckoutElement。
298
- * 互斥判定由本 Map 实时推导(见 {@link assertNoConflict}),元素 `destroy()` 经
299
- * {@link releaseElement} 从本 Map 移除以释放名额(允许销毁后重建同类互斥 Element)。
322
+ * The **single source of truth** for the mutual-exclusion registry: elementId → the created
323
+ * CheckoutElement. Mutual-exclusion decisions are derived live from this Map (see
324
+ * {@link assertNoConflict}); an element's `destroy()` removes itself from this Map via
325
+ * {@link releaseElement} to free the slot (allowing a mutually-exclusive Element of the same
326
+ * kind to be recreated after destruction).
300
327
  */
301
328
  private readonly elements;
302
- /** 当前未决 confirm(同一时刻至多一个)。 */
329
+ /** The current pending confirm (at most one at a time). */
303
330
  private pendingConfirm;
304
- /** paymentResult 事件订阅 handler(destroy 时解绑)。 */
331
+ /** The paymentResult event subscription handler (unbound on destroy). */
305
332
  private readonly paymentResultHandler;
306
333
  private destroyed;
307
334
  constructor(opts: CheckoutInstanceOptions);
308
335
  /**
309
- * 创建一个 Element 句柄(不自动挂载;互斥冲突抛 ELEMENT_CONFLICT)。
336
+ * Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).
310
337
  *
311
- * 互斥规则(spec 6):
312
- * - 聚合 `card` 与任一拆分字段(cardNumber/cardExpiry/cardCvc)互斥。
313
- * - 同 `mode`(billing/shipping)的 address 不可重复。
314
- * - `expressCheckout` 单例。
338
+ * Mutual-exclusion rules (spec 6):
339
+ * - The combined `card` and any split field (cardNumber/cardExpiry/cardCvc) are mutually exclusive.
340
+ * - An address with the same `mode` (billing/shipping) cannot be duplicated.
341
+ * - `expressCheckout` is a singleton.
315
342
  *
316
- * @param type - Element 类型。
317
- * @param options - 初始 Element 配置。
318
- * @returns 新建的 CheckoutElement。
319
- * @throws 已销毁时抛 INSTANCE_DESTROYED;互斥冲突抛 ELEMENT_CONFLICT。
343
+ * @param type - Element type.
344
+ * @param options - Initial Element configuration.
345
+ * @returns The newly created CheckoutElement.
346
+ * @throws INSTANCE_DESTROYED when already destroyed; ELEMENT_CONFLICT on a mutual-exclusion conflict.
320
347
  */
321
348
  create(type: ElementType, options?: ElementOptions): CheckoutElement;
322
349
  /**
323
- * 发起支付确认。生成 operationId,发 confirm 命令拿 accepted,返回的 Promise
324
- * 由后续同 operationId 的 paymentResult 事件兑现。进行中再次调用 → CHECKOUT_BUSY。
350
+ * Initiate a payment confirmation. Generates an operationId, sends the confirm command to obtain
351
+ * accepted, and the returned Promise is fulfilled by a later paymentResult event with the same
352
+ * operationId. Calling again while in progress → CHECKOUT_BUSY.
325
353
  *
326
- * @param opts - 账单/配送地址、姓名、邮箱、returnUrl 等可选参数。
327
- * @returns 支付终态结果 PaymentOutcome。
328
- * @throws 已销毁时 reject INSTANCE_DESTROYED;进行中 reject CHECKOUT_BUSY。
354
+ * @param opts - Optional parameters such as billing/shipping address, name, email, returnUrl.
355
+ * @returns The terminal PaymentOutcome.
356
+ * @throws rejects INSTANCE_DESTROYED when destroyed; rejects CHECKOUT_BUSY when in progress.
329
357
  */
330
358
  confirm(opts?: ConfirmOptions): Promise<PaymentOutcome>;
331
359
  /**
332
- * 获取最新会话视图(透传 getSession 命令并写回本地快照)。
360
+ * Get the latest session view (passes through the getSession command and writes back the local snapshot).
333
361
  *
334
- * @returns 最新 PublicSessionView。
335
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
362
+ * @returns The latest PublicSessionView.
363
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
336
364
  */
337
365
  getSession(): Promise<PublicSessionView>;
338
366
  /**
339
- * 刷新会话(透传 refresh 命令并写回本地快照)。
367
+ * Refresh the session (passes through the refresh command and writes back the local snapshot).
340
368
  *
341
- * @returns 刷新后的 PublicSessionView。
342
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
369
+ * @returns The refreshed PublicSessionView.
370
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
343
371
  */
344
372
  refresh(): Promise<PublicSessionView>;
345
373
  /**
346
- * 查询支付状态(原样返回 TxnStatus,不转义)。
374
+ * Query the payment status (returns TxnStatus as-is, no transformation).
347
375
  *
348
- * @returns txnStatus(可能含 txnId)。
349
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
376
+ * @returns txnStatus (may include txnId).
377
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
350
378
  */
351
379
  getPaymentStatus(): Promise<GetPaymentStatusResp>;
352
380
  /**
353
- * 更新结账外观/语言(不改会话或金额)。
381
+ * Update the checkout appearance/locale (does not change the session or the amount).
354
382
  *
355
- * @param patch - locale / appearance 增量。
356
- * @throws 已销毁时 reject INSTANCE_DESTROYED。
383
+ * @param patch - locale / appearance delta.
384
+ * @throws rejects INSTANCE_DESTROYED when destroyed.
357
385
  */
358
386
  updateCheckout(patch: {
359
387
  locale?: string;
360
388
  appearance?: Appearance;
361
389
  }): Promise<void>;
362
390
  /**
363
- * 注册 Checkout 级事件 handler(透传 transport)。
391
+ * Register a Checkout-level event handler (passes through the transport).
364
392
  *
365
- * @param event - 事件名。
366
- * @param handler - 回调。
367
- * @throws 已销毁时抛 INSTANCE_DESTROYED。
393
+ * @param event - Event name.
394
+ * @param handler - Callback.
395
+ * @throws INSTANCE_DESTROYED when destroyed.
368
396
  */
369
397
  on(event: EventName, handler: (payload: unknown) => void): void;
370
398
  /**
371
- * 注销事件 handler(销毁后仍可安全调用)。
399
+ * Unregister an event handler (safe to call after destruction).
372
400
  *
373
- * @param event - 事件名。
374
- * @param handler - 先前注册的回调。
401
+ * @param event - Event name.
402
+ * @param handler - The previously registered callback.
375
403
  */
376
404
  off(event: EventName, handler: (payload: unknown) => void): void;
377
405
  /**
378
- * 销毁实例:幂等。释放全部 Element、解绑 paymentResult 监听、以
379
- * INSTANCE_DESTROYED reject 未决 confirm、发 destroy 命令并销毁 transport。
380
- * 此后迟到的 paymentResult 一律忽略,不改写已结束的 Promise。
406
+ * Destroy the instance: idempotent. Releases all Elements, unbinds the paymentResult listener,
407
+ * rejects the pending confirm with INSTANCE_DESTROYED, sends the destroy command and destroys
408
+ * the transport. Any late paymentResult afterwards is ignored and does not overwrite an already
409
+ * settled Promise.
381
410
  */
382
411
  destroy(): void;
383
- /** 若已销毁则抛 INSTANCE_DESTROYED。 */
412
+ /** Throw INSTANCE_DESTROYED if already destroyed. */
384
413
  private assertAlive;
385
- /** 互斥检查:冲突时抛 ELEMENT_CONFLICT(由 elements Map 实时推导,不改变注册表)。 */
414
+ /** Mutual-exclusion check: throws ELEMENT_CONFLICT on conflict (derived live from the elements Map, does not mutate the registry). */
386
415
  private assertNoConflict;
387
416
  /**
388
- * 元素 `destroy()` 回调:从互斥注册表(elements Map)移除该 elementId,释放名额。
389
- * 幂等;实例整体 destroy 时(已置 destroyed)走统一 `elements.clear()` 路径,此处 no-op,
390
- * 避免遍历 elements 调 element.destroy() 时的并发修改。
417
+ * Element `destroy()` callback: remove this elementId from the mutual-exclusion registry (the
418
+ * elements Map) to free the slot. Idempotent; when the whole instance is destroyed (destroyed
419
+ * already set) it goes through the unified `elements.clear()` path, so this is a no-op here,
420
+ * avoiding concurrent modification while iterating elements to call element.destroy().
391
421
  */
392
422
  private releaseElement;
393
- /** 处理 paymentResult 事件:按 operationId 兑现未决 confirm;迟到/不匹配忽略。 */
423
+ /** Handle the paymentResult event: fulfill the pending confirm by operationId; late/mismatched ones are ignored. */
394
424
  private handlePaymentResult;
395
425
  }
396
426
 
397
427
  /**
398
- * 运行环境标识。
399
- * - `dev`:开发/测试托管页面。
400
- * - `prod`:生产托管页面。
428
+ * Runtime environment identifier.
429
+ * - `dev`: development hosted page.
430
+ * - `test`: UAT/test hosted page.
431
+ * - `prod`: production hosted page.
401
432
  */
402
- type Env = 'dev' | 'prod';
433
+ type Env = 'dev' | 'test' | 'prod';
403
434
 
404
435
  /**
405
- * createCheckout —— 商户 SDK 的唯一异步入口(spec 4)。
436
+ * createCheckout — the merchant SDK's single async entry (spec 4).
406
437
  *
407
- * 流程:
408
- * 1. 生成 `checkoutId`,`resolveHostOrigin(env, hostOrigin)` 得托管 origin。
409
- * 2. 建 transport、`createHiddenIframe()` 加载托管 main iframe。
410
- * 3. 注册 onHello;等 RuntimeHello(超时 15s → reject RUNTIME_LOAD_FAILED)。
411
- * 4. 选共同主版本(无交集 → reject PROTOCOL_UNSUPPORTED)。
412
- * 5. 发 `initialize`(sessionId/locale/appearance/paymentMethods),拿
413
- * capabilities + PublicSessionView;任一步失败 reject 并销毁 transport。
414
- * 6. 用握手 runtimeId 注入 transport,构造并 resolve {@link CheckoutInstance}。
438
+ * Flow:
439
+ * 1. Generate `checkoutId`, derive the hosted origin via `resolveHostOrigin(env, hostOrigin)`.
440
+ * 2. Build the transport, `createHiddenIframe()` to load the hosted main iframe.
441
+ * 3. Register onHello; wait for RuntimeHello (timeout 15s → reject RUNTIME_LOAD_FAILED).
442
+ * 4. Pick a common major version (no intersection → reject PROTOCOL_UNSUPPORTED).
443
+ * 5. Send `initialize` (sessionId/locale/appearance/paymentMethods) to obtain
444
+ * capabilities + PublicSessionView; any step failing rejects and destroys the transport.
445
+ * 6. Inject the handshake runtimeId into the transport, construct and resolve {@link CheckoutInstance}.
415
446
  *
416
- * 安全约束(AGENTS/spec):本层只做握手/初始化编排,**绝不触碰卡值**。
447
+ * Security constraint (AGENTS/spec): this layer only orchestrates handshake/initialization and
448
+ * **never touches card values**.
417
449
  */
418
450
 
419
451
  /**
420
- * 创建 CheckoutInstance 时可注入的内部 transport 工厂(仅供测试/高级定制)。
452
+ * Internal transport factory injectable when creating a CheckoutInstance (test/advanced use only).
421
453
  *
422
- * 真实场景使用内置 MainFrameTransport;测试可注入 mock 以驱动 hello/事件。
454
+ * Real scenarios use the built-in MainFrameTransport; tests may inject a mock to drive hello/events.
423
455
  */
424
456
  type TransportFactory = (args: {
425
457
  checkoutId: string;
426
458
  hostOrigin: string;
427
459
  }) => InstanceTransport;
428
- /** createCheckout 参数。 */
460
+ /** createCheckout parameters. */
429
461
  interface CreateCheckoutOptions {
430
- /** 后端创建的结账会话 id(必填)。 */
462
+ /** Checkout session id created by the backend (required). */
431
463
  sessionId: string;
432
- /** 运行环境(dev/prod),默认 dev。 */
464
+ /** Runtime environment (dev/test/prod), defaults to prod. */
433
465
  env?: Env;
434
- /** 覆盖托管 origin(优先于 env)。 */
466
+ /** Override the hosted origin (takes precedence over env). */
435
467
  hostOrigin?: string;
436
- /** 语言。 */
468
+ /** Locale. */
437
469
  locale?: string;
438
- /** 外观定制。 */
470
+ /** Appearance customization. */
439
471
  appearance?: Appearance;
440
- /** 支付方式过滤(只做减法/排序)。 */
472
+ /** Payment method filter (subtractive/reordering only). */
441
473
  paymentMethods?: PaymentMethodFilter;
442
- /** 内部注入点:自定义 transport 工厂(测试用,不属于公开 API 契约)。 */
474
+ /** Internal injection point: custom transport factory (test use; not part of the public API contract). */
443
475
  __transportFactory?: TransportFactory;
444
476
  }
445
477
  /**
446
- * 创建一个结账实例:握手 + 初始化成功后 resolve。
478
+ * Create a checkout instance: resolves after a successful handshake + initialization.
447
479
  *
448
- * @param opts - 结账参数。
449
- * @returns 就绪的 CheckoutInstance。
450
- * @throws reject RUNTIME_LOAD_FAILED(握手超时)/ PROTOCOL_UNSUPPORTED(无共同主版本)
451
- * / 或 initialize 的业务错误。
480
+ * @param opts - Checkout parameters.
481
+ * @returns A ready CheckoutInstance.
482
+ * @throws reject RUNTIME_LOAD_FAILED (handshake timeout) / PROTOCOL_UNSUPPORTED (no common major
483
+ * version) / or the initialize business error.
452
484
  * @example
453
485
  * const checkout = await createCheckout({ sessionId: 'sess_x', env: 'prod' });
454
486
  */
455
487
  declare function createCheckout(opts: CreateCheckoutOptions): Promise<CheckoutInstance>;
456
488
 
457
489
  /**
458
- * @sunbay/checkout-js 公共入口(真源)。
490
+ * @sunbay/checkout-js public entry (source of truth).
459
491
  *
460
- * 导出 `createCheckout` 入口、`CheckoutInstance` / `CheckoutElement` 句柄类型,
461
- * 以及协议公开类型(供 TS 使用者消费)。
462
- * IIFE 产物下,额外在 `window.SunbayCheckout` 暴露 `{ createCheckout }`。
492
+ * Exports the `createCheckout` entry, the `CheckoutInstance` / `CheckoutElement` handle types,
493
+ * and the public protocol types (for TS consumers).
494
+ * In the IIFE build, additionally exposes `{ createCheckout }` on `window.SunbayCheckout`.
463
495
  */
464
496
 
465
497
  declare const version: string;