@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/README.md +72 -0
- package/dist/index.cjs +181 -147
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +250 -218
- package/dist/index.d.ts +250 -218
- package/dist/index.global.js +181 -147
- package/dist/index.global.js.map +1 -1
- package/dist/index.mjs +181 -147
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/MainFrameTransport.ts","../src/Element.ts","../src/CheckoutInstance.ts","../src/config/env.ts","../src/createCheckout.ts","../src/index.ts"],"sourcesContent":["/**\n * MainFrameTransport —— 商户 SDK 与托管 main iframe 的传输层(spec 5)。\n *\n * 职责:\n * - 创建隐藏 0 尺寸的 main iframe(`${hostOrigin}/sdk/main?checkoutId=...`)。\n * - 发送 request 信封并按 `requestId` 配对响应(resolve/reject)。\n * - 分发 `kind:'event'` 信封到 `on` 注册的 handler。\n * - 过滤安全边界:仅接受 `event.origin === hostOrigin` 且信封 `checkoutId` 匹配的消息。\n * - `destroy()` 清理 iframe/监听,并以 INSTANCE_DESTROYED reject 所有未决 request。\n *\n * 安全约束(AGENTS/spec):信封中**绝不含卡值**;本层只搬运协议信封,不触碰卡数据。\n * RuntimeHello(`kind:'hello'`)在握手阶段尚无 checkoutId,由本层**单独**识别处理,\n * 不交给 `decodeEnvelope`(后者要求 checkoutId 必填)。\n */\n\nimport {\n decodeEnvelope,\n encodeEnvelope,\n PROTOCOL_MAJOR,\n PROTOCOL_NAME,\n type CheckoutError,\n type CommandName,\n type Envelope,\n type EventName,\n type RuntimeHello,\n} from '@sunbay/checkout-sdk-protocol';\nimport { ErrorCode } from '@sunbay/checkout-sdk-protocol';\n\n/** 构造参数。 */\nexport interface MainFrameTransportOptions {\n checkoutId: string;\n hostOrigin: string;\n}\n\n/** 可配置超时(毫秒)。 */\nexport interface RequestTimeoutConfig {\n /** 普通命令默认超时。 */\n default: number;\n /** initialize 命令超时。 */\n initialize: number;\n /** 握手(hello)超时。 */\n handshake: number;\n}\n\n/** 默认超时时长:普通 10s、initialize 30s、握手 15s。 */\nexport const DEFAULT_REQUEST_TIMEOUTS: RequestTimeoutConfig = {\n default: 10_000,\n initialize: 30_000,\n handshake: 15_000,\n};\n\n/** 事件 handler 签名(payload 为未知结构,由调用方按 EventName 细化)。 */\nexport type EventHandler = (payload: unknown) => void;\n\n/** 未决请求的配对记录。 */\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: CheckoutError) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/** 生成唯一 id(requestId / errorId)。 */\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2);\n return `${prefix}_${Date.now().toString(36)}_${rand}`;\n}\n\n/** 构造一个结构化 CheckoutError(运行时分类)。 */\nfunction makeError(code: string, message: string): CheckoutError {\n return {\n code,\n message,\n category: 'runtime',\n errorId: genId('err'),\n };\n}\n\n/**\n * 判断一个未知对象是否为 RuntimeHello(握手阶段,无 checkoutId)。\n *\n * 不依赖 decodeEnvelope —— hello 不属于 Envelope 联合。\n */\nfunction isRuntimeHello(value: unknown): value is RuntimeHello {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return record.protocol === PROTOCOL_NAME && record.kind === 'hello';\n}\n\nexport class MainFrameTransport {\n private readonly checkoutId: string;\n private readonly hostOrigin: string;\n private readonly timeouts: RequestTimeoutConfig;\n\n private iframe: HTMLIFrameElement | null = null;\n private readonly pending = new Map<string, PendingRequest>();\n private readonly eventHandlers = new Map<EventName, Set<EventHandler>>();\n private helloHandler: ((hello: RuntimeHello) => void) | null = null;\n /**\n * 握手协商出的运行时 id;未注入前为空串。出站 request 信封据此填充,\n * main 端点仅处理 runtimeId 与自身一致的请求,空串会被静默丢弃。\n */\n private runtimeId = '';\n\n private readonly messageListener: (event: MessageEvent) => void;\n private destroyed = false;\n\n constructor(\n options: MainFrameTransportOptions,\n timeouts: RequestTimeoutConfig = DEFAULT_REQUEST_TIMEOUTS\n ) {\n this.checkoutId = options.checkoutId;\n this.hostOrigin = options.hostOrigin;\n this.timeouts = timeouts;\n this.messageListener = (event: MessageEvent): void => {\n this.handleMessage(event);\n };\n window.addEventListener('message', this.messageListener);\n }\n\n /**\n * 创建隐藏 0 尺寸 main iframe,append 到 document.body。\n *\n * @returns 已挂载的 iframe 元素。\n * @example\n * const iframe = transport.createHiddenIframe();\n */\n createHiddenIframe(): HTMLIFrameElement {\n if (this.iframe) {\n return this.iframe;\n }\n const iframe = document.createElement('iframe');\n iframe.src = `${this.hostOrigin}/sdk/main?checkoutId=${this.checkoutId}`;\n iframe.setAttribute('aria-hidden', 'true');\n iframe.title = 'sunbay-checkout-main';\n iframe.style.position = 'absolute';\n iframe.style.width = '0';\n iframe.style.height = '0';\n iframe.style.border = '0';\n iframe.style.visibility = 'hidden';\n document.body.appendChild(iframe);\n this.iframe = iframe;\n return iframe;\n }\n\n /**\n * 发送一条 request 信封,按 requestId 配对响应。\n *\n * @param name - 命令名。\n * @param payload - 命令载荷(JSON 可序列化)。\n * @returns 响应 data;失败(ok:false / 超时 / destroy)reject 一个 CheckoutError。\n * @example\n * const session = await transport.request('getSession', {});\n */\n request(name: CommandName, payload: unknown): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n if (this.destroyed) {\n reject(makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed'));\n return;\n }\n const contentWindow = this.iframe?.contentWindow;\n if (!contentWindow) {\n reject(\n makeError(\n ErrorCode.RUNTIME_LOAD_FAILED,\n 'main iframe not created or contentWindow unavailable'\n )\n );\n return;\n }\n\n const requestId = genId('req');\n const envelope: Envelope = {\n protocol: PROTOCOL_NAME,\n major: PROTOCOL_MAJOR,\n runtimeId: this.runtimeId,\n checkoutId: this.checkoutId,\n kind: 'request',\n requestId,\n name,\n payload,\n };\n\n const timeoutMs = this.resolveTimeout(name);\n const timer = setTimeout(() => {\n this.pending.delete(requestId);\n reject(makeError(ErrorCode.REQUEST_TIMEOUT, `Command ${name} timed out`));\n }, timeoutMs);\n\n this.pending.set(requestId, { resolve, reject, timer });\n contentWindow.postMessage(encodeEnvelope(envelope), this.hostOrigin);\n });\n }\n\n /**\n * 注册事件 handler(分发 kind:'event' 信封的 payload)。\n *\n * @param event - 事件名。\n * @param handler - 回调。\n */\n on(event: EventName, handler: EventHandler): void {\n let set = this.eventHandlers.get(event);\n if (!set) {\n set = new Set<EventHandler>();\n this.eventHandlers.set(event, set);\n }\n set.add(handler);\n }\n\n /**\n * 注销事件 handler。\n *\n * @param event - 事件名。\n * @param handler - 先前注册的回调。\n */\n off(event: EventName, handler: EventHandler): void {\n const set = this.eventHandlers.get(event);\n if (set) {\n set.delete(handler);\n if (set.size === 0) {\n this.eventHandlers.delete(event);\n }\n }\n }\n\n /**\n * 注册握手(RuntimeHello)回调。hello 阶段尚无 checkoutId,单独处理。\n *\n * @param handler - 收到 hello 时调用。\n */\n onHello(handler: (hello: RuntimeHello) => void): void {\n this.helloHandler = handler;\n }\n\n /**\n * 注入握手协商出的运行时 id。必须在发送首个 request(initialize)之前调用,\n * 否则出站信封 runtimeId 为空串、被 main 端点静默丢弃导致请求超时。\n *\n * @param runtimeId - RuntimeHello 中的 runtimeId。\n */\n setRuntimeId(runtimeId: string): void {\n this.runtimeId = runtimeId;\n }\n\n /**\n * 销毁:移除 iframe、清监听,未决 request 以 INSTANCE_DESTROYED reject。\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n window.removeEventListener('message', this.messageListener);\n\n for (const [, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed'));\n }\n this.pending.clear();\n this.eventHandlers.clear();\n this.helloHandler = null;\n\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n this.iframe = null;\n }\n\n /** 按命令名解析超时时长。 */\n private resolveTimeout(name: CommandName): number {\n if (name === 'initialize') {\n return this.timeouts.initialize;\n }\n return this.timeouts.default;\n }\n\n /**\n * 点对点 port relay(父窗口中继,Stripe controller 模型)。\n *\n * 兄弟 iframe(main 与各 Element iframe)之间拿不到彼此 window 引用,无法直接\n * transfer MessagePort。本层(商户页,唯一同时可达两端且知其 origin 的角色)在\n * 收到某 Element iframe 的 `element-online` 时:\n * 1. 建 `MessageChannel`;\n * 2. `port1` → main iframe(`relay-port`,targetOrigin=hostOrigin);\n * 3. `port2` → 发起的 Element iframe(即 `event.source`,targetOrigin=hostOrigin)。\n *\n * 两端 origin 均为 hostOrigin,targetOrigin 恒正确(跨域亦不被丢弃)。\n * 本层只搬运**空 port**,绝不接触卡值(卡值在 port 建立后端到端传输)。\n *\n * @param event 入站 message(origin 已校验为 hostOrigin)\n * @returns 是否作为 element-online 处理(是则调用方应 return)\n */\n private tryHandleElementOnline(event: MessageEvent): boolean {\n const data = event.data as unknown;\n if (typeof data !== 'object' || data === null) {\n return false;\n }\n const msg = data as Record<string, unknown>;\n if (msg.kind !== 'element-online') {\n return false;\n }\n // 校验:checkoutId 匹配、fieldType 合法、来源窗口存在。\n if (msg.checkoutId !== this.checkoutId || typeof msg.fieldType !== 'string') {\n return true;\n }\n const mainWin = this.iframe?.contentWindow;\n const elementWin = event.source as Window | null;\n if (!mainWin || !elementWin) {\n return true;\n }\n\n const channel = new MessageChannel();\n const relayMsg = {\n kind: 'relay-port' as const,\n checkoutId: this.checkoutId,\n fieldType: msg.fieldType,\n };\n // port1 → main iframe;port2 → 发起的 Element iframe。\n mainWin.postMessage(relayMsg, this.hostOrigin, [channel.port1]);\n elementWin.postMessage(relayMsg, this.hostOrigin, [channel.port2]);\n return true;\n }\n\n /** 处理入站 message:过滤 origin,识别 relay / hello / Envelope 并分发。 */\n private handleMessage(event: MessageEvent): void {\n if (this.destroyed) {\n return;\n }\n // 安全边界:只接受来自托管页面 origin 的消息。\n if (event.origin !== this.hostOrigin) {\n return;\n }\n\n // 点对点 port relay:Element iframe 上线 → 本层(父窗口)建 channel,\n // 把两个 port 分别 transfer 给 main iframe 与该 Element iframe(见下)。\n // relay 消息为对象(非 string 信封),且须先于 string 校验识别。\n if (this.tryHandleElementOnline(event)) {\n return;\n }\n\n if (typeof event.data !== 'string') {\n return;\n }\n\n // 握手 hello 单独解析(不走 Envelope 联合,此阶段无 checkoutId)。\n let raw: unknown;\n try {\n raw = JSON.parse(event.data);\n } catch {\n return;\n }\n if (isRuntimeHello(raw)) {\n if (this.helloHandler) {\n this.helloHandler(raw);\n }\n return;\n }\n\n // 其余按 Envelope 校验解码。\n let envelope: Envelope;\n try {\n envelope = decodeEnvelope(event.data);\n } catch {\n return;\n }\n\n // 安全边界:信封 checkoutId 必须匹配本实例。\n if (envelope.checkoutId !== this.checkoutId) {\n return;\n }\n\n if (envelope.kind === 'response') {\n this.handleResponse(envelope);\n } else if (envelope.kind === 'event') {\n this.handleEvent(envelope);\n }\n // kind:'request' 由 main → SDK 不应出现,忽略。\n }\n\n /** 分发 response 到配对的未决 request。 */\n private handleResponse(envelope: Extract<Envelope, { kind: 'response' }>): void {\n const entry = this.pending.get(envelope.requestId);\n if (!entry) {\n return;\n }\n this.pending.delete(envelope.requestId);\n clearTimeout(entry.timer);\n if (envelope.ok) {\n entry.resolve(envelope.data);\n } else {\n entry.reject(envelope.error);\n }\n }\n\n /** 分发 event 到注册的 handler。 */\n private handleEvent(envelope: Extract<Envelope, { kind: 'event' }>): void {\n const set = this.eventHandlers.get(envelope.name as EventName);\n if (!set) {\n return;\n }\n for (const handler of set) {\n handler(envelope.payload);\n }\n }\n}\n","/**\n * CheckoutElement —— 商户侧 Element 句柄(spec 6)。\n *\n * 职责:\n * - 由 `CheckoutInstance.create` 构造(单测时以 mock transport + 构造参注入)。\n * - 在商户 DOM 目标挂载**可见** iframe,并通过 transport 转发 Element 命令。\n * - 维护状态机 `created → loading → ready → destroyed`(`loadError` 为旁支)。\n * - 订阅 transport 的 `elementReady` / `loadError` 事件,按**自身 elementId** 过滤,\n * 驱动 `mount()` 的 resolve / reject。\n *\n * 安全约束(AGENTS/spec):本层只做 DOM 挂载与协议命令转发,**绝不触碰卡值**。\n * 卡数据仅存在于托管 iframe 内,不经由本句柄或 transport 传输。\n */\n\nimport {\n ErrorCode,\n isIframeResizeMessage,\n type Appearance,\n type AvailableWallets,\n type CheckoutError,\n type CommandName,\n type ElementOptions,\n type ElementStatus,\n type ElementType,\n type EventName,\n type ExpressAvailabilityChangeEvent,\n type ExpressCheckoutElementOptions,\n} from '@sunbay/checkout-sdk-protocol';\n\n/**\n * transport 依赖的最小接口(由 MainFrameTransport 满足)。\n *\n * 仅声明 Element 句柄需要的方法,便于单测注入 mock。\n */\nexport interface ElementTransport {\n request(name: CommandName, payload: unknown): Promise<unknown>;\n on(event: EventName, handler: (payload: unknown) => void): void;\n off(event: EventName, handler: (payload: unknown) => void): void;\n}\n\n/** CheckoutElement 构造参数(由 CheckoutInstance.create 组装)。 */\nexport interface CheckoutElementOptions {\n /** Element 唯一 id(由实例生成,用于命令/事件配对)。 */\n elementId: string;\n /** Element 类型(决定 iframe 路径与可用动作)。 */\n type: ElementType;\n /** 托管页面 origin(不带尾部斜杠)。 */\n hostOrigin: string;\n /**\n * 当前 checkout 会话 id。随 iframe src 以 `?checkoutId=` 下发,供托管字段/element\n * iframe 加入正确的同源信令频道(`sunbay-checkout:<checkoutId>`)与 main 配对;\n * 缺失会导致字段频道名不匹配、永不就绪。\n */\n checkoutId: string;\n /** 传输层。 */\n transport: ElementTransport;\n /** 初始 Element 配置(随 createElement 下发)。 */\n initialOptions?: ElementOptions;\n /**\n * 句柄销毁回调(由 CheckoutInstance 注入)。`destroy()` 成功销毁时以本 elementId 调用,\n * 使实例从互斥注册表(elements Map)移除自身,从而允许销毁后重建同类互斥 Element\n * (如切换 expressCheckout 的 buttonType/buttonHeight 需重建 iframe)。`unmount()` 不调用\n * (保留句柄、仍占名额、可 remount)。\n */\n onDestroy?: (elementId: string) => void;\n /**\n * 初始配色方案(来自 createCheckout 的 appearance.colorScheme)。\n * 仅当提供时拼进 iframe src 的 `&colorScheme=<值>`,使托管 iframe 在首帧即按该\n * 配色渲染(无闪)。缺省时不拼接,由托管侧按默认(light)处理。运行时切换走\n * updateCheckout 命令,不走此处。\n */\n colorScheme?: 'light' | 'dark' | 'auto';\n /**\n * 初始外观定制(来自 createCheckout 的 appearance)。\n * 仅当含 `variables` 或 `rules` 时序列化进 iframe src 的\n * `&appearance=<encodeURIComponent(JSON)>`,使托管 iframe 在首帧即按该外观渲染(无闪)。\n * 仅含 `colorScheme`(无 variables/rules)时不拼接,避免空外观字符串;colorScheme 走独立\n * `&colorScheme=`。运行时切换走 updateCheckout 命令,不走此处。\n */\n appearance?: Appearance;\n}\n\n/** 事件 payload 的公共结构(含 elementId,用于按自身句柄过滤)。 */\ninterface WithElementId {\n elementId?: string;\n}\n\n/** loadError 事件 payload 结构。 */\ninterface LoadErrorPayload extends WithElementId {\n error: CheckoutError;\n}\n\n/** elementReady 事件 payload 结构(expressCheckout 额外携 availableWallets)。 */\ninterface ElementReadyPayload extends WithElementId {\n availableWallets?: AvailableWallets;\n}\n\n/** 拆分字段类型(走 /sdk/field/<type>)。 */\nconst FIELD_TYPES: readonly ElementType[] = ['cardNumber', 'cardExpiry', 'cardCvc'];\n\n/** 提供 focus/blur/clear 的输入类(expressCheckout 不含)。 */\nconst INPUT_TYPES: readonly ElementType[] = [\n 'cardNumber',\n 'cardExpiry',\n 'cardCvc',\n 'card',\n 'address',\n];\n\n/** 生成一个结构化 CheckoutError(configuration 分类,供句柄本地报错)。 */\nfunction makeError(code: string, message: string, elementId?: string): CheckoutError {\n const rand = Math.random().toString(36).slice(2);\n return {\n code,\n message,\n category: 'configuration',\n errorId: `err_${Date.now().toString(36)}_${rand}`,\n elementId,\n };\n}\n\n/**\n * 判断 payload 是否指向本 elementId(**严格匹配**)。\n *\n * 仅当 `payload.elementId === elementId` 才视为本 Element 的 elementReady/loadError。\n * 无 elementId 或不等均不匹配,避免 T6 多 Element 共享同一 transport 时事件串扰。\n * (协议保证 elementReady/loadError 始终携带 elementId。)\n */\nfunction matchesElement(payload: unknown, elementId: string): boolean {\n if (typeof payload !== 'object' || payload === null) {\n return false;\n }\n return (payload as WithElementId).elementId === elementId;\n}\n\nexport class CheckoutElement {\n /** Element 唯一 id。 */\n readonly elementId: string;\n /** Element 类型。 */\n readonly type: ElementType;\n /** 地址 Element 的 mode(billing/shipping;仅 address 有意义),供实例按 mode 判互斥。 */\n readonly mode: string | undefined;\n\n private readonly hostOrigin: string;\n private readonly checkoutId: string;\n private readonly transport: ElementTransport;\n private options: ElementOptions | undefined;\n /** 初始配色方案(拼进 iframe src;缺省不拼)。 */\n private readonly colorScheme: 'light' | 'dark' | 'auto' | undefined;\n /** 初始外观定制(含 variables/rules 时拼进 iframe src;缺省或仅 colorScheme 不拼)。 */\n private readonly appearance: Appearance | undefined;\n /** 销毁回调(实例注入):destroy() 末尾调用,使实例从互斥注册表移除自身以释放名额。 */\n private readonly onDestroy: ((elementId: string) => void) | undefined;\n\n private status: ElementStatus = 'created';\n private container: HTMLElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n\n /** mount 期间临时订阅的事件 handler(resolve/reject 后解绑)。 */\n private readyHandler: ((payload: unknown) => void) | null = null;\n private loadErrorHandler: ((payload: unknown) => void) | null = null;\n\n /**\n * 仅 expressCheckout:各钱包可用性快照。\n * 初次来自 elementReady.availableWallets,之后随 expressAvailabilityChange 更新。\n */\n private availableWallets: AvailableWallets | undefined;\n /** 仅 expressCheckout:常驻订阅的 expressAvailabilityChange handler(destroy 时解绑)。 */\n private availabilityHandler: ((payload: unknown) => void) | null = null;\n\n /**\n * iframe 自适应高度:挂载期间常驻的 window 'message' 监听器(iframe 消失/销毁时解绑)。\n * 存引用以便 removeEventListener;null 表示未监听。\n */\n private resizeHandler: ((e: MessageEvent) => void) | null = null;\n /** 最近一次已应用的 iframe 高度(px),用于去重,避免冗余 style 写入;null 表示尚未设置。 */\n private lastHeight: number | null = null;\n\n constructor(opts: CheckoutElementOptions) {\n this.elementId = opts.elementId;\n this.type = opts.type;\n this.hostOrigin = opts.hostOrigin;\n this.checkoutId = opts.checkoutId;\n this.transport = opts.transport;\n this.options = opts.initialOptions;\n this.colorScheme = opts.colorScheme;\n this.appearance = opts.appearance;\n this.mode = (opts.initialOptions as { mode?: string } | undefined)?.mode;\n this.onDestroy = opts.onDestroy;\n this.installInputActions();\n this.installExpressAvailability();\n }\n\n /** 当前状态(只读快照,供外部/测试观测)。 */\n getStatus(): ElementStatus {\n return this.status;\n }\n\n /**\n * 仅 expressCheckout:返回当前各钱包可用性快照(其他类型恒为 undefined)。\n *\n * 初值来自 elementReady 的 availableWallets,之后由 expressAvailabilityChange 更新。\n *\n * @returns 可用性快照;尚未就绪或非 express 时为 undefined。\n */\n getAvailableWallets(): AvailableWallets | undefined {\n return this.availableWallets;\n }\n\n /**\n * 挂载到商户 DOM 目标:插入可见 iframe、发 createElement、等 elementReady。\n *\n * @param selectorOrEl - CSS 选择器或 HTMLElement 容器;容器须唯一且为空。\n * @returns elementReady 后 resolve;失败移除节点并 reject(loadError / INVALID_OPTIONS)。\n * @throws 已销毁时抛 INSTANCE_DESTROYED。\n * @example\n * await element.mount('#card-field');\n */\n mount(selectorOrEl: string | HTMLElement): Promise<void> {\n this.assertAlive();\n\n if (this.status === 'loading' || this.status === 'ready') {\n return Promise.reject(\n makeError(\n ErrorCode.ELEMENT_CONFLICT,\n 'This Element is already mounted; mount cannot be called twice',\n this.elementId\n )\n );\n }\n\n const container = this.resolveContainer(selectorOrEl);\n if (!container) {\n return Promise.reject(\n makeError(\n ErrorCode.INVALID_OPTIONS,\n 'Target container not found or not uniquely resolvable',\n this.elementId\n )\n );\n }\n if (container.childElementCount > 0) {\n return Promise.reject(\n makeError(ErrorCode.INVALID_OPTIONS, 'Target container is not empty', this.elementId)\n );\n }\n\n this.status = 'loading';\n this.container = container;\n\n const iframe = document.createElement('iframe');\n iframe.src = this.resolveSrc();\n iframe.title = `sunbay-checkout-${this.type}`;\n iframe.setAttribute('data-element-id', this.elementId);\n iframe.style.border = '0';\n iframe.style.width = '100%';\n iframe.style.display = 'block';\n container.appendChild(iframe);\n this.iframe = iframe;\n this.installResizeListener();\n\n return new Promise<void>((resolve, reject) => {\n const cleanup = (): void => {\n if (this.readyHandler) {\n this.transport.off('elementReady', this.readyHandler);\n this.readyHandler = null;\n }\n if (this.loadErrorHandler) {\n this.transport.off('loadError', this.loadErrorHandler);\n this.loadErrorHandler = null;\n }\n };\n\n const onReady = (payload: unknown): void => {\n if (!matchesElement(payload, this.elementId)) {\n return;\n }\n cleanup();\n // expressCheckout:捕获 ready 携带的初始钱包可用性。\n const wallets = (payload as ElementReadyPayload).availableWallets;\n if (wallets) {\n this.availableWallets = wallets;\n }\n this.status = 'ready';\n resolve();\n };\n\n const onLoadError = (payload: unknown): void => {\n if (!matchesElement(payload, this.elementId)) {\n return;\n }\n cleanup();\n this.status = 'loadError';\n this.removeIframe();\n const err = (payload as LoadErrorPayload)?.error;\n reject(\n err ??\n makeError(\n ErrorCode.RUNTIME_LOAD_FAILED,\n 'Failed to load Element',\n this.elementId\n )\n );\n };\n\n this.readyHandler = onReady;\n this.loadErrorHandler = onLoadError;\n this.transport.on('elementReady', onReady);\n this.transport.on('loadError', onLoadError);\n\n // 发 createElement 命令;命令级失败同样走 loadError 旁支。\n this.transport\n .request('createElement', {\n elementId: this.elementId,\n type: this.type,\n options: this.options,\n })\n .catch((cmdErr: unknown) => {\n cleanup();\n this.status = 'loadError';\n this.removeIframe();\n reject(cmdErr as CheckoutError);\n });\n });\n }\n\n /**\n * 卸载:发 destroyElement、移除 iframe;幂等;保留句柄与配置。\n *\n * @returns 完成后 resolve(未挂载时直接 resolve)。\n * @throws 已销毁时抛 INSTANCE_DESTROYED。\n */\n async unmount(): Promise<void> {\n this.assertAlive();\n if (this.status !== 'ready' && this.status !== 'loading') {\n // 未挂载:幂等 no-op。\n return;\n }\n try {\n await this.transport.request('destroyElement', { elementId: this.elementId });\n } finally {\n this.removeIframe();\n this.container = null;\n this.status = 'created';\n }\n }\n\n /**\n * 销毁句柄:幂等;移除 iframe;此后非 destroy/off 调用抛 INSTANCE_DESTROYED。\n */\n destroy(): void {\n if (this.status === 'destroyed') {\n return;\n }\n if (this.readyHandler) {\n this.transport.off('elementReady', this.readyHandler);\n this.readyHandler = null;\n }\n if (this.loadErrorHandler) {\n this.transport.off('loadError', this.loadErrorHandler);\n this.loadErrorHandler = null;\n }\n if (this.availabilityHandler) {\n this.transport.off('expressAvailabilityChange', this.availabilityHandler);\n this.availabilityHandler = null;\n }\n this.removeIframe();\n this.container = null;\n this.status = 'destroyed';\n // 通知实例从互斥注册表移除自身,释放名额以允许销毁后重建同类互斥 Element。\n this.onDestroy?.(this.elementId);\n }\n\n /**\n * 更新 Element 配置:发 updateElement;非法字段由托管侧整次拒绝。\n *\n * @param patch - 配置增量。\n * @returns 成功写回本地 options 后 resolve。\n * @throws 已销毁时抛 INSTANCE_DESTROYED。\n */\n async update(patch: Partial<ElementOptions>): Promise<void> {\n this.assertAlive();\n const resp = await this.transport.request('updateElement', {\n elementId: this.elementId,\n patch,\n });\n const next = (resp as { options?: ElementOptions } | undefined)?.options;\n if (next) {\n this.options = next;\n }\n }\n\n /**\n * 注册事件 handler(透传到 transport,按事件名分发)。\n *\n * @param event - 事件名。\n * @param handler - 回调。\n * @throws 已销毁时抛 INSTANCE_DESTROYED。\n */\n on(event: EventName, handler: (payload: unknown) => void): void {\n this.assertAlive();\n this.transport.on(event, handler);\n }\n\n /**\n * 注销事件 handler(销毁后仍可安全调用)。\n *\n * @param event - 事件名。\n * @param handler - 先前注册的回调。\n */\n off(event: EventName, handler: (payload: unknown) => void): void {\n this.transport.off(event, handler);\n }\n\n /** 聚焦(仅输入类,expressCheckout 不提供)。 */\n focus?: () => Promise<void>;\n /** 失焦(仅输入类,expressCheckout 不提供)。 */\n blur?: () => Promise<void>;\n /** 清空(仅输入类,expressCheckout 不提供)。 */\n clear?: () => Promise<void>;\n\n /** 已挂载后安装输入类动作方法(由构造后自动调用)。 */\n private installInputActions(): void {\n if (!INPUT_TYPES.includes(this.type)) {\n return;\n }\n this.focus = (): Promise<void> => this.sendAction('focus');\n this.blur = (): Promise<void> => this.sendAction('blur');\n this.clear = (): Promise<void> => this.sendAction('clear');\n }\n\n /**\n * 仅 expressCheckout:常驻订阅 expressAvailabilityChange,更新本地可用性快照。\n *\n * 该事件载荷不含 elementId;express 为单例(由 CheckoutInstance 保证),\n * 故无需按 elementId 过滤。非 express 类型不订阅。\n */\n private installExpressAvailability(): void {\n if (this.type !== 'expressCheckout') {\n return;\n }\n const handler = (payload: unknown): void => {\n if (typeof payload !== 'object' || payload === null) {\n return;\n }\n const wallets = (payload as ExpressAvailabilityChangeEvent).availableWallets;\n if (wallets) {\n this.availableWallets = wallets;\n }\n };\n this.availabilityHandler = handler;\n this.transport.on('expressAvailabilityChange', handler);\n }\n\n /** 发送一条输入类动作命令(销毁后同步抛 INSTANCE_DESTROYED)。 */\n private sendAction(name: 'focus' | 'blur' | 'clear'): Promise<void> {\n this.assertAlive();\n return this.transport.request(name, { elementId: this.elementId }).then(() => undefined);\n }\n\n /** 若已销毁则抛 INSTANCE_DESTROYED。 */\n private assertAlive(): void {\n if (this.status === 'destroyed') {\n throw makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', this.elementId);\n }\n }\n\n /** 解析挂载容器(选择器须唯一命中一个元素)。 */\n private resolveContainer(selectorOrEl: string | HTMLElement): HTMLElement | null {\n if (typeof selectorOrEl !== 'string') {\n return selectorOrEl;\n }\n const nodes = document.querySelectorAll(selectorOrEl);\n if (nodes.length !== 1) {\n return null;\n }\n const node = nodes[0];\n return node instanceof HTMLElement ? node : null;\n }\n\n /**\n * 解析 iframe src:拆分字段走 /sdk/field/,其余走 /sdk/element/,带 checkoutId 与 elementId;\n * 若提供了初始 colorScheme,则追加 `&colorScheme=<值>`(首帧无闪);\n * 若初始 appearance 含 variables 或 rules,则追加\n * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>`(首帧即生效,无闪)。\n * 仅含 colorScheme(无 variables/rules)的 appearance 不拼接,避免空外观串。\n * 对 expressCheckout,另追加钱包开关 / 每钱包 buttonType / 全局 buttonHeight\n * (见 {@link resolveExpressQuery})。\n *\n * elementId 随 src 下发,供托管 child 在 iframe-resize 消息中原样回传,父窗口据此在\n * 同页多个 iframe 间按句柄分发高度。\n */\n private resolveSrc(): string {\n const segment = FIELD_TYPES.includes(this.type) ? 'field' : 'element';\n let query = `?checkoutId=${encodeURIComponent(this.checkoutId)}`;\n query += `&elementId=${encodeURIComponent(this.elementId)}`;\n if (this.colorScheme) {\n query += `&colorScheme=${encodeURIComponent(this.colorScheme)}`;\n }\n // 仅当 appearance 含 variables 或 rules 时序列化(避免空外观串;colorScheme 已单独透传)。\n if (this.appearance && (this.appearance.variables || this.appearance.rules)) {\n query += `&appearance=${encodeURIComponent(JSON.stringify(this.appearance))}`;\n }\n // expressCheckout:把 create 第二参的 express options(钱包开关 / buttonType /\n // buttonHeight)透传给托管 iframe,使商户配置在首帧即生效(C1 断链修复)。\n if (this.type === 'expressCheckout') {\n query += this.resolveExpressQuery();\n }\n return `${this.hostOrigin}/sdk/${segment}/${this.type}${query}`;\n }\n\n /**\n * 构造 expressCheckout 专属 query 片段(仅 expressCheckout 调用):\n * - 钱包显式置为 `false` → `&applePay=false` / `&googlePay=false`(强制隐藏,保持\n * 既有开关语义;`true` / 缺省 → 自动,不拼开关);\n * - 钱包以对象形态配置且含 `buttonType` → `&applePayButtonType=<v>` /\n * `&googlePayButtonType=<v>`(布尔开关 / 缺省 → 不拼);\n * - 全局 `buttonHeight`(有限数)→ `&buttonHeight=<n>`。\n *\n * 仅在有值时拼接;不拼空串。本片段不触碰卡值,仅承载展示配置。\n *\n * @returns 以 `&` 起始的 query 片段(无可拼项时返回空串)。\n */\n private resolveExpressQuery(): string {\n const options = this.options as ExpressCheckoutElementOptions | undefined;\n if (!options) {\n return '';\n }\n let query = '';\n const applePay = options.paymentMethods?.applePay;\n const googlePay = options.paymentMethods?.googlePay;\n // 钱包开关:仅显式 false(强制隐藏)才下发,保持既有开关语义。\n if (applePay === false) {\n query += `&applePay=false`;\n }\n if (googlePay === false) {\n query += `&googlePay=false`;\n }\n // 每钱包 buttonType:仅对象形态且含 buttonType 时下发。\n if (typeof applePay === 'object' && applePay.buttonType) {\n query += `&applePayButtonType=${encodeURIComponent(applePay.buttonType)}`;\n }\n if (typeof googlePay === 'object' && googlePay.buttonType) {\n query += `&googlePayButtonType=${encodeURIComponent(googlePay.buttonType)}`;\n }\n // 全局按钮高度:仅有限数时下发(托管侧再 clamp 到 [30,64])。\n if (typeof options.buttonHeight === 'number' && Number.isFinite(options.buttonHeight)) {\n query += `&buttonHeight=${encodeURIComponent(String(options.buttonHeight))}`;\n }\n return query;\n }\n\n /**\n * 安装 iframe 自适应高度监听:订阅 window 'message',仅采信来自本 iframe\n * `contentWindow` 且 origin === hostOrigin 的合法 {@link IframeResizeMessage},\n * 且 elementId 与本句柄匹配时,将外层 iframe 高度调整为内容真实高度(宽度保持 100%)。\n *\n * 安全:先校验 `event.source === iframe.contentWindow`(关键来源校验),\n * 再校验 `event.origin === hostOrigin`(更严格的同源校验),防跨源伪造。\n * 高度去重:与上次相同则跳过,避免冗余 style 写入。\n */\n private installResizeListener(): void {\n const handler = (event: MessageEvent): void => {\n if (!this.iframe) {\n return;\n }\n if (event.source !== this.iframe.contentWindow) {\n return;\n }\n if (event.origin !== this.hostOrigin) {\n return;\n }\n if (!isIframeResizeMessage(event.data)) {\n return;\n }\n if (event.data.elementId !== this.elementId) {\n return;\n }\n const { height } = event.data;\n if (height === this.lastHeight) {\n return;\n }\n this.lastHeight = height;\n this.iframe.style.height = `${height}px`;\n };\n this.resizeHandler = handler;\n window.addEventListener('message', handler);\n }\n\n /** 解绑 iframe 自适应高度监听并重置去重状态(幂等)。 */\n private teardownResizeListener(): void {\n if (this.resizeHandler) {\n window.removeEventListener('message', this.resizeHandler);\n this.resizeHandler = null;\n }\n this.lastHeight = null;\n }\n\n /** 移除 iframe DOM 节点(若存在),并解绑自适应高度监听。 */\n private removeIframe(): void {\n this.teardownResizeListener();\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n this.iframe = null;\n }\n\n /** 工厂:等价于 `new CheckoutElement(opts)`(供 CheckoutInstance 统一入口)。 */\n static create(opts: CheckoutElementOptions): CheckoutElement {\n return new CheckoutElement(opts);\n }\n}\n","/**\n * CheckoutInstance —— 商户侧结账实例句柄(spec 4 / 7 / 8)。\n *\n * 职责:\n * - 由 `createCheckout` 在握手 + initialize 成功后构造(持有已就绪 transport、\n * runtimeId、capabilities、初始 PublicSessionView)。\n * - `create` 组装并返回 {@link CheckoutElement},维护**互斥注册表**(card 与拆分\n * 字段互斥、同 mode 地址互斥、expressCheckout 单例 → `ELEMENT_CONFLICT`)。\n * - `confirm` 生成 operationId、发 confirm 命令拿 accepted,Promise 由后续同\n * operationId 的 `paymentResult` 事件兑现;进行中再 confirm → `CHECKOUT_BUSY`。\n * - `getSession/refresh/getPaymentStatus/updateCheckout` 透传对应命令。\n * - `destroy` 幂等:释放 Element、解绑监听、以 INSTANCE_DESTROYED reject 未决 confirm、\n * 发 destroy 命令并销毁 transport;此后迟到的 `paymentResult` 一律忽略。\n *\n * 安全约束(AGENTS/spec):本层只搬运协议命令/事件,**绝不触碰卡值**。\n */\n\nimport { CheckoutElement, type CheckoutElementOptions } from './Element';\nimport {\n ErrorCode,\n type Appearance,\n type CheckoutError,\n type CommandName,\n type ConfirmReq,\n type ElementOptions,\n type ElementType,\n type EventName,\n type GetPaymentStatusResp,\n type PaymentOutcome,\n type PaymentResultEvent,\n type PublicSessionView,\n type UpdateCheckoutReq,\n} from '@sunbay/checkout-sdk-protocol';\n\n/**\n * CheckoutInstance 依赖的最小 transport 接口(由 MainFrameTransport 满足)。\n *\n * 比 ElementTransport 多出 `onHello/createHiddenIframe/destroy`,供 createCheckout\n * 握手接线与实例销毁使用。单测可注入 mock。\n */\nexport interface InstanceTransport {\n request(name: CommandName, payload: unknown): Promise<unknown>;\n on(event: EventName, handler: (payload: unknown) => void): void;\n off(event: EventName, handler: (payload: unknown) => void): void;\n onHello(handler: (hello: unknown) => void): void;\n setRuntimeId(runtimeId: string): void;\n createHiddenIframe(): unknown;\n destroy(): void;\n}\n\n/** CheckoutInstance 构造参数(由 createCheckout 组装)。 */\nexport interface CheckoutInstanceOptions {\n /** 结账唯一 id。 */\n checkoutId: string;\n /** 协商后的运行时 id(来自 RuntimeHello)。 */\n runtimeId: string;\n /** 托管页面 origin(不带尾部斜杠)。 */\n hostOrigin: string;\n /** 已就绪 transport。 */\n transport: InstanceTransport;\n /** initialize 返回的 capabilities。 */\n capabilities: string[];\n /** initialize 返回的初始会话视图。 */\n session: PublicSessionView;\n /**\n * 初始外观定制(来自 createCheckout 的 appearance)。\n * 透传给每个 create 出的 Element,序列化进其 iframe src(首帧无闪):\n * - colorScheme 拼 `&colorScheme=`;\n * - variables/rules 拼 `&appearance=`(仅当非空)。\n * 缺省不拼接。运行时切换走 updateCheckout 命令。\n */\n appearance?: Appearance;\n}\n\n/** confirm 可选参数(对齐 ConfirmReq,去掉由实例生成的 operationId)。 */\nexport type ConfirmOptions = Omit<ConfirmReq, 'operationId'>;\n\n/** 未决 confirm 的配对记录。 */\ninterface PendingConfirm {\n operationId: string;\n resolve: (outcome: PaymentOutcome) => void;\n reject: (reason: CheckoutError) => void;\n}\n\n/** 拆分卡字段类型集合。 */\nconst CARD_FIELD_TYPES: readonly ElementType[] = ['cardNumber', 'cardExpiry', 'cardCvc'];\n\n/** 生成唯一 id。 */\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2);\n return `${prefix}_${Date.now().toString(36)}_${rand}`;\n}\n\n/** 构造一个结构化 CheckoutError。 */\nfunction makeError(\n code: string,\n message: string,\n category: CheckoutError['category'] = 'configuration'\n): CheckoutError {\n return {\n code,\n message,\n category,\n errorId: genId('err'),\n };\n}\n\nexport class CheckoutInstance {\n /** 结账唯一 id。 */\n readonly checkoutId: string;\n /** 协商后的运行时 id。 */\n readonly runtimeId: string;\n /** 协商后的能力集合。 */\n readonly capabilities: readonly string[];\n\n private readonly hostOrigin: string;\n private readonly transport: InstanceTransport;\n private session: PublicSessionView;\n /** 初始外观定制(透传给 create 出的每个 Element 的 iframe src)。 */\n private readonly appearance: Appearance | undefined;\n\n /** 已创建的 Element(按 elementId 索引)。 */\n /**\n * 互斥注册表的**单一数据源**:elementId → 已创建的 CheckoutElement。\n * 互斥判定由本 Map 实时推导(见 {@link assertNoConflict}),元素 `destroy()` 经\n * {@link releaseElement} 从本 Map 移除以释放名额(允许销毁后重建同类互斥 Element)。\n */\n private readonly elements = new Map<string, CheckoutElement>();\n\n /** 当前未决 confirm(同一时刻至多一个)。 */\n private pendingConfirm: PendingConfirm | null = null;\n /** paymentResult 事件订阅 handler(destroy 时解绑)。 */\n private readonly paymentResultHandler: (payload: unknown) => void;\n\n private destroyed = false;\n\n constructor(opts: CheckoutInstanceOptions) {\n this.checkoutId = opts.checkoutId;\n this.runtimeId = opts.runtimeId;\n this.capabilities = opts.capabilities;\n this.hostOrigin = opts.hostOrigin;\n this.transport = opts.transport;\n this.session = opts.session;\n this.appearance = opts.appearance;\n\n this.paymentResultHandler = (payload: unknown): void => {\n this.handlePaymentResult(payload);\n };\n this.transport.on('paymentResult', this.paymentResultHandler);\n }\n\n /**\n * 创建一个 Element 句柄(不自动挂载;互斥冲突抛 ELEMENT_CONFLICT)。\n *\n * 互斥规则(spec 6):\n * - 聚合 `card` 与任一拆分字段(cardNumber/cardExpiry/cardCvc)互斥。\n * - 同 `mode`(billing/shipping)的 address 不可重复。\n * - `expressCheckout` 单例。\n *\n * @param type - Element 类型。\n * @param options - 初始 Element 配置。\n * @returns 新建的 CheckoutElement。\n * @throws 已销毁时抛 INSTANCE_DESTROYED;互斥冲突抛 ELEMENT_CONFLICT。\n */\n create(type: ElementType, options?: ElementOptions): CheckoutElement {\n this.assertAlive();\n this.assertNoConflict(type, options);\n\n const elementId = genId('el');\n const elementOpts: CheckoutElementOptions = {\n elementId,\n type,\n hostOrigin: this.hostOrigin,\n checkoutId: this.checkoutId,\n transport: this.transport,\n initialOptions: options,\n colorScheme: this.appearance?.colorScheme,\n appearance: this.appearance,\n onDestroy: (id: string) => this.releaseElement(id),\n };\n const element = CheckoutElement.create(elementOpts);\n this.elements.set(elementId, element);\n return element;\n }\n\n /**\n * 发起支付确认。生成 operationId,发 confirm 命令拿 accepted,返回的 Promise\n * 由后续同 operationId 的 paymentResult 事件兑现。进行中再次调用 → CHECKOUT_BUSY。\n *\n * @param opts - 账单/配送地址、姓名、邮箱、returnUrl 等可选参数。\n * @returns 支付终态结果 PaymentOutcome。\n * @throws 已销毁时 reject INSTANCE_DESTROYED;进行中 reject CHECKOUT_BUSY。\n */\n confirm(opts?: ConfirmOptions): Promise<PaymentOutcome> {\n if (this.destroyed) {\n return Promise.reject(\n makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', 'runtime')\n );\n }\n if (this.pendingConfirm) {\n return Promise.reject(\n makeError(ErrorCode.CHECKOUT_BUSY, 'A payment confirmation is already in progress', 'runtime')\n );\n }\n\n const operationId = genId('op');\n const payload: ConfirmReq = { ...opts, operationId };\n\n return new Promise<PaymentOutcome>((resolve, reject) => {\n // 先占位,防止 accepted 之前并发 confirm。\n this.pendingConfirm = { operationId, resolve, reject };\n this.transport.request('confirm', payload).catch((err: unknown) => {\n // confirm 命令本身失败:清理占位并 reject(paymentResult 不会到来)。\n if (this.pendingConfirm && this.pendingConfirm.operationId === operationId) {\n this.pendingConfirm = null;\n }\n reject(err as CheckoutError);\n });\n });\n }\n\n /**\n * 获取最新会话视图(透传 getSession 命令并写回本地快照)。\n *\n * @returns 最新 PublicSessionView。\n * @throws 已销毁时 reject INSTANCE_DESTROYED。\n */\n async getSession(): Promise<PublicSessionView> {\n this.assertAlive();\n const resp = (await this.transport.request('getSession', {})) as PublicSessionView;\n this.session = resp;\n return resp;\n }\n\n /**\n * 刷新会话(透传 refresh 命令并写回本地快照)。\n *\n * @returns 刷新后的 PublicSessionView。\n * @throws 已销毁时 reject INSTANCE_DESTROYED。\n */\n async refresh(): Promise<PublicSessionView> {\n this.assertAlive();\n const resp = (await this.transport.request('refresh', {})) as PublicSessionView;\n this.session = resp;\n return resp;\n }\n\n /**\n * 查询支付状态(原样返回 TxnStatus,不转义)。\n *\n * @returns txnStatus(可能含 txnId)。\n * @throws 已销毁时 reject INSTANCE_DESTROYED。\n */\n async getPaymentStatus(): Promise<GetPaymentStatusResp> {\n this.assertAlive();\n return (await this.transport.request('getPaymentStatus', {})) as GetPaymentStatusResp;\n }\n\n /**\n * 更新结账外观/语言(不改会话或金额)。\n *\n * @param patch - locale / appearance 增量。\n * @throws 已销毁时 reject INSTANCE_DESTROYED。\n */\n async updateCheckout(patch: { locale?: string; appearance?: Appearance }): Promise<void> {\n this.assertAlive();\n const payload: UpdateCheckoutReq = patch;\n await this.transport.request('updateCheckout', payload);\n }\n\n /**\n * 注册 Checkout 级事件 handler(透传 transport)。\n *\n * @param event - 事件名。\n * @param handler - 回调。\n * @throws 已销毁时抛 INSTANCE_DESTROYED。\n */\n on(event: EventName, handler: (payload: unknown) => void): void {\n this.assertAlive();\n this.transport.on(event, handler);\n }\n\n /**\n * 注销事件 handler(销毁后仍可安全调用)。\n *\n * @param event - 事件名。\n * @param handler - 先前注册的回调。\n */\n off(event: EventName, handler: (payload: unknown) => void): void {\n this.transport.off(event, handler);\n }\n\n /**\n * 销毁实例:幂等。释放全部 Element、解绑 paymentResult 监听、以\n * INSTANCE_DESTROYED reject 未决 confirm、发 destroy 命令并销毁 transport。\n * 此后迟到的 paymentResult 一律忽略,不改写已结束的 Promise。\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n\n // 释放全部 Element 句柄。\n for (const element of this.elements.values()) {\n element.destroy();\n }\n this.elements.clear();\n\n // 解绑 paymentResult 监听(之后迟到结果无 handler 可达)。\n this.transport.off('paymentResult', this.paymentResultHandler);\n\n // 以 INSTANCE_DESTROYED reject 未决 confirm。\n if (this.pendingConfirm) {\n this.pendingConfirm.reject(\n makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', 'runtime')\n );\n this.pendingConfirm = null;\n }\n\n // 通知托管侧并销毁 transport(request 在 destroy 后由 transport 自行拒绝)。\n void Promise.resolve(this.transport.request('destroy', {})).catch(() => undefined);\n this.transport.destroy();\n }\n\n /** 若已销毁则抛 INSTANCE_DESTROYED。 */\n private assertAlive(): void {\n if (this.destroyed) {\n throw makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', 'runtime');\n }\n }\n\n /** 互斥检查:冲突时抛 ELEMENT_CONFLICT(由 elements Map 实时推导,不改变注册表)。 */\n private assertNoConflict(type: ElementType, options?: ElementOptions): void {\n const existing = [...this.elements.values()];\n const has = (t: ElementType): boolean => existing.some((e) => e.type === t);\n const hasCardField = (): boolean => existing.some((e) => CARD_FIELD_TYPES.includes(e.type));\n\n if (type === 'card') {\n if (has('card')) {\n throw makeError(ErrorCode.ELEMENT_CONFLICT, 'A combined card Element already exists');\n }\n if (hasCardField()) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n 'Combined card and split card fields are mutually exclusive; split card fields already exist'\n );\n }\n return;\n }\n if (CARD_FIELD_TYPES.includes(type)) {\n if (has('card')) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n 'Split card fields and combined card are mutually exclusive; a combined card already exists'\n );\n }\n // 同一拆分字段类型不可重复。\n if (has(type)) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n `A split card field of type ${type} already exists`\n );\n }\n return;\n }\n if (type === 'address') {\n const mode = (options as { mode?: string } | undefined)?.mode;\n if (mode && existing.some((e) => e.type === 'address' && e.mode === mode)) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n `An address Element with mode=${mode} already exists`\n );\n }\n return;\n }\n if (type === 'expressCheckout') {\n if (has('expressCheckout')) {\n throw makeError(ErrorCode.ELEMENT_CONFLICT, 'An expressCheckout Element already exists');\n }\n return;\n }\n }\n\n /**\n * 元素 `destroy()` 回调:从互斥注册表(elements Map)移除该 elementId,释放名额。\n * 幂等;实例整体 destroy 时(已置 destroyed)走统一 `elements.clear()` 路径,此处 no-op,\n * 避免遍历 elements 调 element.destroy() 时的并发修改。\n */\n private releaseElement(elementId: string): void {\n if (this.destroyed) {\n return;\n }\n this.elements.delete(elementId);\n }\n\n /** 处理 paymentResult 事件:按 operationId 兑现未决 confirm;迟到/不匹配忽略。 */\n private handlePaymentResult(payload: unknown): void {\n if (this.destroyed) {\n return;\n }\n if (typeof payload !== 'object' || payload === null) {\n return;\n }\n const evt = payload as Partial<PaymentResultEvent>;\n const pending = this.pendingConfirm;\n if (!pending) {\n return;\n }\n if (evt.operationId !== pending.operationId) {\n return;\n }\n if (!evt.outcome) {\n return;\n }\n this.pendingConfirm = null;\n pending.resolve(evt.outcome);\n }\n}\n","/**\n * 运行环境标识。\n * - `dev`:开发/测试托管页面。\n * - `prod`:生产托管页面。\n */\nexport type Env = 'dev' | 'prod';\n\n/**\n * 内置托管页面 origin(统一不带尾部斜杠)。\n */\nconst HOST_ORIGINS: Record<Env, string> = {\n dev: 'https://checkout.sunbay-dev.com',\n prod: 'https://checkout.sunbay.us',\n};\n\n/**\n * 解析托管页面 origin。\n *\n * @param env - 运行环境,默认 `'dev'`。\n * @param override - 覆盖地址(供环境变量/测试使用);非空时优先返回。\n * @returns 托管页面 origin(不带尾部斜杠)。\n * @example\n * resolveHostOrigin(); // 'https://checkout.sunbay-dev.com'\n * resolveHostOrigin('prod'); // 'https://checkout.sunbay.us'\n * resolveHostOrigin('dev', 'http://localhost:3000'); // 'http://localhost:3000'\n */\nexport function resolveHostOrigin(env: Env = 'dev', override?: string): string {\n if (override) {\n return override;\n }\n return HOST_ORIGINS[env];\n}\n","/**\n * createCheckout —— 商户 SDK 的唯一异步入口(spec 4)。\n *\n * 流程:\n * 1. 生成 `checkoutId`,`resolveHostOrigin(env, hostOrigin)` 得托管 origin。\n * 2. 建 transport、`createHiddenIframe()` 加载托管 main iframe。\n * 3. 注册 onHello;等 RuntimeHello(超时 15s → reject RUNTIME_LOAD_FAILED)。\n * 4. 选共同主版本(无交集 → reject PROTOCOL_UNSUPPORTED)。\n * 5. 发 `initialize`(sessionId/locale/appearance/paymentMethods),拿\n * capabilities + PublicSessionView;任一步失败 reject 并销毁 transport。\n * 6. 用握手 runtimeId 注入 transport,构造并 resolve {@link CheckoutInstance}。\n *\n * 安全约束(AGENTS/spec):本层只做握手/初始化编排,**绝不触碰卡值**。\n */\n\nimport { MainFrameTransport } from './transport/MainFrameTransport';\nimport { CheckoutInstance, type InstanceTransport } from './CheckoutInstance';\nimport { resolveHostOrigin, type Env } from './config/env';\nimport {\n ErrorCode,\n PROTOCOL_MAJOR,\n type Appearance,\n type CheckoutError,\n type InitializeReq,\n type InitializeResp,\n type PaymentMethodFilter,\n type RuntimeHello,\n} from '@sunbay/checkout-sdk-protocol';\n\n/** 握手超时(毫秒):与 transport handshake 超时一致(15s)。 */\nconst HANDSHAKE_TIMEOUT_MS = 15_000;\n\n/**\n * 创建 CheckoutInstance 时可注入的内部 transport 工厂(仅供测试/高级定制)。\n *\n * 真实场景使用内置 MainFrameTransport;测试可注入 mock 以驱动 hello/事件。\n */\nexport type TransportFactory = (args: {\n checkoutId: string;\n hostOrigin: string;\n}) => InstanceTransport;\n\n/** createCheckout 参数。 */\nexport interface CreateCheckoutOptions {\n /** 后端创建的结账会话 id(必填)。 */\n sessionId: string;\n /** 运行环境(dev/prod),默认 dev。 */\n env?: Env;\n /** 覆盖托管 origin(优先于 env)。 */\n hostOrigin?: string;\n /** 语言。 */\n locale?: string;\n /** 外观定制。 */\n appearance?: Appearance;\n /** 支付方式过滤(只做减法/排序)。 */\n paymentMethods?: PaymentMethodFilter;\n /** 内部注入点:自定义 transport 工厂(测试用,不属于公开 API 契约)。 */\n __transportFactory?: TransportFactory;\n}\n\n/** 生成唯一 id。 */\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2);\n return `${prefix}_${Date.now().toString(36)}_${rand}`;\n}\n\n/** 构造一个结构化 CheckoutError(runtime 分类)。 */\nfunction makeError(code: string, message: string): CheckoutError {\n return {\n code,\n message,\n category: 'runtime',\n errorId: genId('err'),\n };\n}\n\n/** 默认 transport 工厂:内置 MainFrameTransport。 */\nconst defaultTransportFactory: TransportFactory = ({ checkoutId, hostOrigin }) =>\n new MainFrameTransport({ checkoutId, hostOrigin });\n\n/**\n * 创建一个结账实例:握手 + 初始化成功后 resolve。\n *\n * @param opts - 结账参数。\n * @returns 就绪的 CheckoutInstance。\n * @throws reject RUNTIME_LOAD_FAILED(握手超时)/ PROTOCOL_UNSUPPORTED(无共同主版本)\n * / 或 initialize 的业务错误。\n * @example\n * const checkout = await createCheckout({ sessionId: 'sess_x', env: 'prod' });\n */\nexport async function createCheckout(opts: CreateCheckoutOptions): Promise<CheckoutInstance> {\n const checkoutId = genId('co');\n const hostOrigin = resolveHostOrigin(opts.env, opts.hostOrigin);\n const factory = opts.__transportFactory ?? defaultTransportFactory;\n const transport = factory({ checkoutId, hostOrigin });\n\n // 启动托管 main iframe(隐藏 0 尺寸)。\n transport.createHiddenIframe();\n\n let hello: RuntimeHello;\n try {\n hello = await waitForHello(transport);\n } catch (err) {\n transport.destroy();\n throw err;\n }\n\n // 选共同主版本。\n if (!hello.supportedMajorVersions.includes(PROTOCOL_MAJOR)) {\n transport.destroy();\n throw makeError(\n ErrorCode.PROTOCOL_UNSUPPORTED,\n `No common major protocol version: SDK=${PROTOCOL_MAJOR}, runtime=${JSON.stringify(hello.supportedMajorVersions)}`\n );\n }\n\n // 注入协商出的 runtimeId:必须在发送 initialize 之前,否则出站信封 runtimeId\n // 为空串,被 main 端点静默丢弃,导致 initialize 超时。\n transport.setRuntimeId(hello.runtimeId);\n\n // 发 initialize。\n const initReq: InitializeReq = {\n sessionId: opts.sessionId,\n locale: opts.locale,\n appearance: opts.appearance,\n paymentMethods: opts.paymentMethods,\n };\n let initResp: InitializeResp;\n try {\n initResp = (await transport.request('initialize', initReq)) as InitializeResp;\n } catch (err) {\n transport.destroy();\n throw err as CheckoutError;\n }\n\n return new CheckoutInstance({\n checkoutId,\n runtimeId: hello.runtimeId,\n hostOrigin,\n transport,\n capabilities: initResp.capabilities,\n session: initResp.session,\n appearance: opts.appearance,\n });\n}\n\n/** 等待 RuntimeHello;超时 reject RUNTIME_LOAD_FAILED。 */\nfunction waitForHello(transport: InstanceTransport): Promise<RuntimeHello> {\n return new Promise<RuntimeHello>((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n reject(makeError(ErrorCode.RUNTIME_LOAD_FAILED, 'Timed out waiting for hosted runtime handshake'));\n }, HANDSHAKE_TIMEOUT_MS);\n\n transport.onHello((raw: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolve(raw as RuntimeHello);\n });\n });\n}\n","/**\n * @sunbay/checkout-js 公共入口(真源)。\n *\n * 导出 `createCheckout` 入口、`CheckoutInstance` / `CheckoutElement` 句柄类型,\n * 以及协议公开类型(供 TS 使用者消费)。\n * IIFE 产物下,额外在 `window.SunbayCheckout` 暴露 `{ createCheckout }`。\n */\n\nexport { createCheckout } from './createCheckout';\nexport type {\n CreateCheckoutOptions,\n TransportFactory,\n} from './createCheckout';\n\nexport { CheckoutInstance } from './CheckoutInstance';\nexport type {\n CheckoutInstanceOptions,\n ConfirmOptions,\n InstanceTransport,\n} from './CheckoutInstance';\n\nexport { CheckoutElement } from './Element';\nexport type { CheckoutElementOptions, ElementTransport } from './Element';\n\nexport type { Env } from './config/env';\n\n// 协议公开类型(面向 TS 使用者的契约)。\nexport {\n ErrorCode,\n type ErrorCodeValue,\n type CheckoutError,\n type CheckoutErrorCategory,\n type PaymentOutcome,\n type PublicSessionView,\n type PublicPaymentMethod,\n type PublicProductLine,\n type PaymentMethod,\n type PaymentMethodFilter,\n type Appearance,\n type AddressInfo,\n type TxnStatus,\n type ElementType,\n type ElementOptions,\n type ElementStatus,\n type CardElementOptions,\n type CardFieldElementOptions,\n type AddressElementOptions,\n type ExpressCheckoutElementOptions,\n} from '@sunbay/checkout-sdk-protocol';\n\nimport { createCheckout } from './createCheckout';\n\n/**\n * IIFE / 浏览器全局:挂载 `window.SunbayCheckout = { createCheckout }`。\n *\n * tsup 的 IIFE 产物已以 `globalName: 'SunbayCheckout'` 暴露模块导出,此处显式确保\n * `window.SunbayCheckout.createCheckout` 可用(同时兼容未来再导出更多入口)。\n */\nif (typeof window !== 'undefined') {\n const w = window as typeof window & {\n SunbayCheckout?: { createCheckout: typeof createCheckout };\n };\n w.SunbayCheckout = { ...(w.SunbayCheckout ?? {}), createCheckout };\n}\n\n/**\n * 包版本。构建期由 tsup 的 `define` 从 `package.json` 注入 `__SDK_VERSION__`\n * (见 tsup.config.ts),使代码版本号永远与 package.json 同步。未经构建的环境\n * (如 vitest 直接跑 src)无此 define,回退到 `'0.0.0'`。\n */\ndeclare const __SDK_VERSION__: string;\nexport const version: string =\n typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : '0.0.0';\n"],"mappings":"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","names":["ErrorCode","makeError","ErrorCode","genId","makeError","ErrorCode","PROTOCOL_MAJOR","genId","makeError","ErrorCode"]}
|
|
1
|
+
{"version":3,"sources":["../src/transport/MainFrameTransport.ts","../src/Element.ts","../src/CheckoutInstance.ts","../src/config/env.ts","../src/createCheckout.ts","../src/index.ts"],"sourcesContent":["/**\n * MainFrameTransport — the transport layer between the merchant SDK and the hosted main iframe (spec 5).\n *\n * Responsibilities:\n * - Create a hidden, 0-size main iframe (`${hostOrigin}/sdk/main?checkoutId=...`).\n * - Send request envelopes and pair responses by `requestId` (resolve/reject).\n * - Dispatch `kind:'event'` envelopes to handlers registered via `on`.\n * - Enforce the security boundary: only accept messages whose `event.origin === hostOrigin` and\n * whose envelope `checkoutId` matches.\n * - `destroy()` cleans up the iframe/listener and rejects all pending requests with INSTANCE_DESTROYED.\n *\n * Security constraint (AGENTS/spec): envelopes **never contain card values**; this layer only\n * carries protocol envelopes and never touches card data.\n * RuntimeHello (`kind:'hello'`) has no checkoutId yet during the handshake phase, so it is\n * recognized and handled **separately** by this layer rather than passed to `decodeEnvelope`\n * (which requires checkoutId).\n */\n\nimport {\n decodeEnvelope,\n encodeEnvelope,\n PROTOCOL_MAJOR,\n PROTOCOL_NAME,\n type CheckoutError,\n type CommandName,\n type Envelope,\n type EventName,\n type RuntimeHello,\n} from '@sunbay/checkout-sdk-protocol';\nimport { ErrorCode } from '@sunbay/checkout-sdk-protocol';\n\n/** Constructor parameters. */\nexport interface MainFrameTransportOptions {\n checkoutId: string;\n hostOrigin: string;\n}\n\n/** Configurable timeouts (ms). */\nexport interface RequestTimeoutConfig {\n /** Default timeout for regular commands. */\n default: number;\n /** Timeout for the initialize command. */\n initialize: number;\n /** Handshake (hello) timeout. */\n handshake: number;\n}\n\n/** Default timeouts: regular 10s, initialize 30s, handshake 15s. */\nexport const DEFAULT_REQUEST_TIMEOUTS: RequestTimeoutConfig = {\n default: 10_000,\n initialize: 30_000,\n handshake: 15_000,\n};\n\n/** Event handler signature (payload is of unknown shape, refined by the caller per EventName). */\nexport type EventHandler = (payload: unknown) => void;\n\n/** Pairing record for a pending request. */\ninterface PendingRequest {\n resolve: (value: unknown) => void;\n reject: (reason: CheckoutError) => void;\n timer: ReturnType<typeof setTimeout>;\n}\n\n/** Generate a unique id (requestId / errorId). */\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2);\n return `${prefix}_${Date.now().toString(36)}_${rand}`;\n}\n\n/** Construct a structured CheckoutError (runtime category). */\nfunction makeError(code: string, message: string): CheckoutError {\n return {\n code,\n message,\n category: 'runtime',\n errorId: genId('err'),\n };\n}\n\n/**\n * Determine whether an unknown object is a RuntimeHello (handshake phase, no checkoutId).\n *\n * Does not rely on decodeEnvelope — hello is not part of the Envelope union.\n */\nfunction isRuntimeHello(value: unknown): value is RuntimeHello {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const record = value as Record<string, unknown>;\n return record.protocol === PROTOCOL_NAME && record.kind === 'hello';\n}\n\nexport class MainFrameTransport {\n private readonly checkoutId: string;\n private readonly hostOrigin: string;\n private readonly timeouts: RequestTimeoutConfig;\n\n private iframe: HTMLIFrameElement | null = null;\n private readonly pending = new Map<string, PendingRequest>();\n private readonly eventHandlers = new Map<EventName, Set<EventHandler>>();\n private helloHandler: ((hello: RuntimeHello) => void) | null = null;\n /**\n * Runtime id negotiated during the handshake; empty string before injection. Outbound request\n * envelopes are filled from it; the main endpoint only processes requests whose runtimeId\n * matches its own, and an empty string is silently dropped.\n */\n private runtimeId = '';\n\n private readonly messageListener: (event: MessageEvent) => void;\n private destroyed = false;\n\n constructor(\n options: MainFrameTransportOptions,\n timeouts: RequestTimeoutConfig = DEFAULT_REQUEST_TIMEOUTS\n ) {\n this.checkoutId = options.checkoutId;\n this.hostOrigin = options.hostOrigin;\n this.timeouts = timeouts;\n this.messageListener = (event: MessageEvent): void => {\n this.handleMessage(event);\n };\n window.addEventListener('message', this.messageListener);\n }\n\n /**\n * Create the hidden, 0-size main iframe and append it to document.body.\n *\n * @returns The mounted iframe element.\n * @example\n * const iframe = transport.createHiddenIframe();\n */\n createHiddenIframe(): HTMLIFrameElement {\n if (this.iframe) {\n return this.iframe;\n }\n const iframe = document.createElement('iframe');\n iframe.src = `${this.hostOrigin}/sdk/main?checkoutId=${this.checkoutId}`;\n iframe.setAttribute('aria-hidden', 'true');\n iframe.title = 'sunbay-checkout-main';\n iframe.style.position = 'absolute';\n iframe.style.width = '0';\n iframe.style.height = '0';\n iframe.style.border = '0';\n iframe.style.visibility = 'hidden';\n document.body.appendChild(iframe);\n this.iframe = iframe;\n return iframe;\n }\n\n /**\n * Send one request envelope and pair the response by requestId.\n *\n * @param name - Command name.\n * @param payload - Command payload (JSON-serializable).\n * @returns The response data; on failure (ok:false / timeout / destroy) rejects with a CheckoutError.\n * @example\n * const session = await transport.request('getSession', {});\n */\n request(name: CommandName, payload: unknown): Promise<unknown> {\n return new Promise<unknown>((resolve, reject) => {\n if (this.destroyed) {\n reject(makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed'));\n return;\n }\n const contentWindow = this.iframe?.contentWindow;\n if (!contentWindow) {\n reject(\n makeError(\n ErrorCode.RUNTIME_LOAD_FAILED,\n 'main iframe not created or contentWindow unavailable'\n )\n );\n return;\n }\n\n const requestId = genId('req');\n const envelope: Envelope = {\n protocol: PROTOCOL_NAME,\n major: PROTOCOL_MAJOR,\n runtimeId: this.runtimeId,\n checkoutId: this.checkoutId,\n kind: 'request',\n requestId,\n name,\n payload,\n };\n\n const timeoutMs = this.resolveTimeout(name);\n const timer = setTimeout(() => {\n this.pending.delete(requestId);\n reject(makeError(ErrorCode.REQUEST_TIMEOUT, `Command ${name} timed out`));\n }, timeoutMs);\n\n this.pending.set(requestId, { resolve, reject, timer });\n contentWindow.postMessage(encodeEnvelope(envelope), this.hostOrigin);\n });\n }\n\n /**\n * Register an event handler (dispatches the payload of kind:'event' envelopes).\n *\n * @param event - Event name.\n * @param handler - Callback.\n */\n on(event: EventName, handler: EventHandler): void {\n let set = this.eventHandlers.get(event);\n if (!set) {\n set = new Set<EventHandler>();\n this.eventHandlers.set(event, set);\n }\n set.add(handler);\n }\n\n /**\n * Unregister an event handler.\n *\n * @param event - Event name.\n * @param handler - The previously registered callback.\n */\n off(event: EventName, handler: EventHandler): void {\n const set = this.eventHandlers.get(event);\n if (set) {\n set.delete(handler);\n if (set.size === 0) {\n this.eventHandlers.delete(event);\n }\n }\n }\n\n /**\n * Register the handshake (RuntimeHello) callback. The hello phase has no checkoutId yet and is\n * handled separately.\n *\n * @param handler - Called when a hello is received.\n */\n onHello(handler: (hello: RuntimeHello) => void): void {\n this.helloHandler = handler;\n }\n\n /**\n * Inject the runtime id negotiated during the handshake. Must be called before sending the first\n * request (initialize), otherwise the outbound envelope's runtimeId is an empty string, gets\n * silently dropped by the main endpoint, and the request times out.\n *\n * @param runtimeId - The runtimeId from RuntimeHello.\n */\n setRuntimeId(runtimeId: string): void {\n this.runtimeId = runtimeId;\n }\n\n /**\n * Destroy: remove the iframe, clear listeners, and reject pending requests with INSTANCE_DESTROYED.\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n window.removeEventListener('message', this.messageListener);\n\n for (const [, entry] of this.pending) {\n clearTimeout(entry.timer);\n entry.reject(makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed'));\n }\n this.pending.clear();\n this.eventHandlers.clear();\n this.helloHandler = null;\n\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n this.iframe = null;\n }\n\n /** Resolve the timeout by command name. */\n private resolveTimeout(name: CommandName): number {\n if (name === 'initialize') {\n return this.timeouts.initialize;\n }\n return this.timeouts.default;\n }\n\n /**\n * Point-to-point port relay (parent-window relay, Stripe controller model).\n *\n * Sibling iframes (main and the various Element iframes) cannot obtain each other's window\n * references, so they cannot transfer a MessagePort directly. This layer (the merchant page,\n * the only party that can reach both ends and knows their origin) does the following when it\n * receives an `element-online` from some Element iframe:\n * 1. Create a `MessageChannel`;\n * 2. `port1` → main iframe (`relay-port`, targetOrigin=hostOrigin);\n * 3. `port2` → the originating Element iframe (i.e. `event.source`, targetOrigin=hostOrigin).\n *\n * Both ends' origin is hostOrigin, so targetOrigin is always correct (and not dropped across\n * origins). This layer only carries **empty ports** and never touches card values (which are\n * transmitted end-to-end after the port is established).\n *\n * @param event Inbound message (origin already verified as hostOrigin)\n * @returns Whether it was handled as element-online (if so, the caller should return)\n */\n private tryHandleElementOnline(event: MessageEvent): boolean {\n const data = event.data as unknown;\n if (typeof data !== 'object' || data === null) {\n return false;\n }\n const msg = data as Record<string, unknown>;\n if (msg.kind !== 'element-online') {\n return false;\n }\n // Validate: checkoutId matches, fieldType is valid, source window exists.\n if (msg.checkoutId !== this.checkoutId || typeof msg.fieldType !== 'string') {\n return true;\n }\n const mainWin = this.iframe?.contentWindow;\n const elementWin = event.source as Window | null;\n if (!mainWin || !elementWin) {\n return true;\n }\n\n const channel = new MessageChannel();\n const relayMsg = {\n kind: 'relay-port' as const,\n checkoutId: this.checkoutId,\n fieldType: msg.fieldType,\n };\n // port1 → main iframe; port2 → the originating Element iframe.\n mainWin.postMessage(relayMsg, this.hostOrigin, [channel.port1]);\n elementWin.postMessage(relayMsg, this.hostOrigin, [channel.port2]);\n return true;\n }\n\n /** Handle an inbound message: filter origin, recognize relay / hello / Envelope and dispatch. */\n private handleMessage(event: MessageEvent): void {\n if (this.destroyed) {\n return;\n }\n // Security boundary: only accept messages from the hosted page origin.\n if (event.origin !== this.hostOrigin) {\n return;\n }\n\n // Point-to-point port relay: an Element iframe comes online → this layer (parent window)\n // creates a channel and transfers the two ports to the main iframe and that Element iframe\n // respectively (see below). The relay message is an object (not a string envelope) and must\n // be recognized before the string check.\n if (this.tryHandleElementOnline(event)) {\n return;\n }\n\n if (typeof event.data !== 'string') {\n return;\n }\n\n // The handshake hello is parsed separately (not via the Envelope union; no checkoutId at this phase).\n let raw: unknown;\n try {\n raw = JSON.parse(event.data);\n } catch {\n return;\n }\n if (isRuntimeHello(raw)) {\n if (this.helloHandler) {\n this.helloHandler(raw);\n }\n return;\n }\n\n // Everything else is validated and decoded as an Envelope.\n let envelope: Envelope;\n try {\n envelope = decodeEnvelope(event.data);\n } catch {\n return;\n }\n\n // Security boundary: the envelope checkoutId must match this instance.\n if (envelope.checkoutId !== this.checkoutId) {\n return;\n }\n\n if (envelope.kind === 'response') {\n this.handleResponse(envelope);\n } else if (envelope.kind === 'event') {\n this.handleEvent(envelope);\n }\n // kind:'request' from main → SDK should not occur; ignore.\n }\n\n /** Dispatch a response to the paired pending request. */\n private handleResponse(envelope: Extract<Envelope, { kind: 'response' }>): void {\n const entry = this.pending.get(envelope.requestId);\n if (!entry) {\n return;\n }\n this.pending.delete(envelope.requestId);\n clearTimeout(entry.timer);\n if (envelope.ok) {\n entry.resolve(envelope.data);\n } else {\n entry.reject(envelope.error);\n }\n }\n\n /** Dispatch an event to the registered handlers. */\n private handleEvent(envelope: Extract<Envelope, { kind: 'event' }>): void {\n const set = this.eventHandlers.get(envelope.name as EventName);\n if (!set) {\n return;\n }\n for (const handler of set) {\n handler(envelope.payload);\n }\n }\n}\n","/**\n * CheckoutElement — the merchant-side Element handle (spec 6).\n *\n * Responsibilities:\n * - Constructed by `CheckoutInstance.create` (in unit tests, injected via a mock transport +\n * constructor args).\n * - Mount a **visible** iframe at the merchant DOM target and forward Element commands through the\n * transport.\n * - Maintain the state machine `created → loading → ready → destroyed` (with `loadError` as a side branch).\n * - Subscribe to the transport's `elementReady` / `loadError` events, filter by **its own elementId**,\n * and drive the resolve / reject of `mount()`.\n *\n * Security constraint (AGENTS/spec): this layer only does DOM mounting and protocol command\n * forwarding and **never touches card values**. Card data exists only inside the hosted iframe and\n * is never transmitted via this handle or the transport.\n */\n\nimport {\n ErrorCode,\n isIframeResizeMessage,\n type Appearance,\n type AvailableWallets,\n type CheckoutError,\n type CommandName,\n type ElementOptions,\n type ElementStatus,\n type ElementType,\n type EventName,\n type ExpressAvailabilityChangeEvent,\n type ExpressCheckoutElementOptions,\n} from '@sunbay/checkout-sdk-protocol';\n\n/**\n * The minimal interface the transport depends on (satisfied by MainFrameTransport).\n *\n * Only declares the methods the Element handle needs, to make mock injection in unit tests easy.\n */\nexport interface ElementTransport {\n request(name: CommandName, payload: unknown): Promise<unknown>;\n on(event: EventName, handler: (payload: unknown) => void): void;\n off(event: EventName, handler: (payload: unknown) => void): void;\n}\n\n/** CheckoutElement constructor parameters (assembled by CheckoutInstance.create). */\nexport interface CheckoutElementOptions {\n /** Unique Element id (generated by the instance, used to pair commands/events). */\n elementId: string;\n /** Element type (determines the iframe path and available actions). */\n type: ElementType;\n /** Hosted page origin (without a trailing slash). */\n hostOrigin: string;\n /**\n * The current checkout session id. Sent with the iframe src as `?checkoutId=`, so that hosted\n * fields/element iframes join the correct same-origin signaling channel\n * (`sunbay-checkout:<checkoutId>`) and pair with main; missing it causes the field channel name\n * to mismatch and never become ready.\n */\n checkoutId: string;\n /** The transport layer. */\n transport: ElementTransport;\n /** Initial Element configuration (sent with createElement). */\n initialOptions?: ElementOptions;\n /**\n * Handle destruction callback (injected by CheckoutInstance). Called with this elementId when\n * `destroy()` successfully destroys the handle, so the instance removes itself from the\n * mutual-exclusion registry (elements Map), thereby allowing a mutually-exclusive Element of the\n * same kind to be recreated after destruction (e.g. switching expressCheckout's\n * buttonType/buttonHeight requires recreating the iframe). Not called by `unmount()` (which\n * keeps the handle, still occupies the slot, and can remount).\n */\n onDestroy?: (elementId: string) => void;\n /**\n * Initial color scheme (from createCheckout's appearance.colorScheme).\n * Only when provided is it appended to the iframe src's `&colorScheme=<value>`, so the hosted\n * iframe renders with that scheme on the first frame (no flash). When absent it is not appended\n * and the hosted side handles the default (light). Runtime switching goes through the\n * updateCheckout command, not here.\n */\n colorScheme?: 'light' | 'dark' | 'auto';\n /**\n * Initial appearance customization (from createCheckout's appearance).\n * Only when it contains `variables` or `rules` is it serialized into the iframe src's\n * `&appearance=<encodeURIComponent(JSON)>`, so the hosted iframe renders with that appearance on\n * the first frame (no flash). When it contains only `colorScheme` (no variables/rules) it is not\n * appended, to avoid an empty appearance string; colorScheme goes via the separate\n * `&colorScheme=`. Runtime switching goes through the updateCheckout command, not here.\n */\n appearance?: Appearance;\n}\n\n/** The common structure of an event payload (contains elementId, used to filter by the own handle). */\ninterface WithElementId {\n elementId?: string;\n}\n\n/** The loadError event payload structure. */\ninterface LoadErrorPayload extends WithElementId {\n error: CheckoutError;\n}\n\n/** The elementReady event payload structure (expressCheckout additionally carries availableWallets). */\ninterface ElementReadyPayload extends WithElementId {\n availableWallets?: AvailableWallets;\n}\n\n/** The split field types (routed to /sdk/field/<type>). */\nconst FIELD_TYPES: readonly ElementType[] = ['cardNumber', 'cardExpiry', 'cardCvc'];\n\n/** The input classes that provide focus/blur/clear (expressCheckout excluded). */\nconst INPUT_TYPES: readonly ElementType[] = [\n 'cardNumber',\n 'cardExpiry',\n 'cardCvc',\n 'card',\n 'address',\n];\n\n/** Build a structured CheckoutError (configuration category, for the handle's local errors). */\nfunction makeError(code: string, message: string, elementId?: string): CheckoutError {\n const rand = Math.random().toString(36).slice(2);\n return {\n code,\n message,\n category: 'configuration',\n errorId: `err_${Date.now().toString(36)}_${rand}`,\n elementId,\n };\n}\n\n/**\n * Determine whether the payload targets this elementId (**strict match**).\n *\n * Only when `payload.elementId === elementId` is it considered this Element's elementReady/loadError.\n * Missing elementId or inequality both fail to match, avoiding event cross-talk when multiple\n * Elements (T6) share the same transport.\n * (The protocol guarantees elementReady/loadError always carry an elementId.)\n */\nfunction matchesElement(payload: unknown, elementId: string): boolean {\n if (typeof payload !== 'object' || payload === null) {\n return false;\n }\n return (payload as WithElementId).elementId === elementId;\n}\n\nexport class CheckoutElement {\n /** Unique Element id. */\n readonly elementId: string;\n /** Element type. */\n readonly type: ElementType;\n /** The address Element's mode (billing/shipping; only meaningful for address), used by the instance to judge mutual exclusion by mode. */\n readonly mode: string | undefined;\n\n private readonly hostOrigin: string;\n private readonly checkoutId: string;\n private readonly transport: ElementTransport;\n private options: ElementOptions | undefined;\n /** Initial color scheme (appended to the iframe src; not appended when absent). */\n private readonly colorScheme: 'light' | 'dark' | 'auto' | undefined;\n /** Initial appearance customization (appended to the iframe src when it contains variables/rules; not appended when absent or colorScheme-only). */\n private readonly appearance: Appearance | undefined;\n /** 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. */\n private readonly onDestroy: ((elementId: string) => void) | undefined;\n\n private status: ElementStatus = 'created';\n private container: HTMLElement | null = null;\n private iframe: HTMLIFrameElement | null = null;\n\n /** Event handlers temporarily subscribed during mount (unbound after resolve/reject). */\n private readyHandler: ((payload: unknown) => void) | null = null;\n private loadErrorHandler: ((payload: unknown) => void) | null = null;\n\n /**\n * expressCheckout only: a snapshot of each wallet's availability.\n * Initially from elementReady.availableWallets, then updated by expressAvailabilityChange.\n */\n private availableWallets: AvailableWallets | undefined;\n /** expressCheckout only: the persistently subscribed expressAvailabilityChange handler (unbound on destroy). */\n private availabilityHandler: ((payload: unknown) => void) | null = null;\n\n /**\n * iframe auto-height: the window 'message' listener that stays resident during mount (unbound\n * when the iframe disappears/is destroyed). Keeps a reference so removeEventListener can be\n * called; null means not listening.\n */\n private resizeHandler: ((e: MessageEvent) => void) | null = null;\n /** The most recently applied iframe height (px), used for deduplication to avoid redundant style writes; null means not yet set. */\n private lastHeight: number | null = null;\n\n constructor(opts: CheckoutElementOptions) {\n this.elementId = opts.elementId;\n this.type = opts.type;\n this.hostOrigin = opts.hostOrigin;\n this.checkoutId = opts.checkoutId;\n this.transport = opts.transport;\n this.options = opts.initialOptions;\n this.colorScheme = opts.colorScheme;\n this.appearance = opts.appearance;\n this.mode = (opts.initialOptions as { mode?: string } | undefined)?.mode;\n this.onDestroy = opts.onDestroy;\n this.installInputActions();\n this.installExpressAvailability();\n }\n\n /** The current status (read-only snapshot, for external/test observation). */\n getStatus(): ElementStatus {\n return this.status;\n }\n\n /**\n * expressCheckout only: return the current snapshot of each wallet's availability (always\n * undefined for other types).\n *\n * The initial value comes from elementReady's availableWallets, then is updated by\n * expressAvailabilityChange.\n *\n * @returns The availability snapshot; undefined when not yet ready or non-express.\n */\n getAvailableWallets(): AvailableWallets | undefined {\n return this.availableWallets;\n }\n\n /**\n * Mount at the merchant DOM target: insert a visible iframe, send createElement, await elementReady.\n *\n * @param selectorOrEl - A CSS selector or an HTMLElement container; the container must be unique and empty.\n * @returns Resolves after elementReady; on failure removes the node and rejects (loadError / INVALID_OPTIONS).\n * @throws INSTANCE_DESTROYED when already destroyed.\n * @example\n * await element.mount('#card-field');\n */\n mount(selectorOrEl: string | HTMLElement): Promise<void> {\n this.assertAlive();\n\n if (this.status === 'loading' || this.status === 'ready') {\n return Promise.reject(\n makeError(\n ErrorCode.ELEMENT_CONFLICT,\n 'This Element is already mounted; mount cannot be called twice',\n this.elementId\n )\n );\n }\n\n const container = this.resolveContainer(selectorOrEl);\n if (!container) {\n return Promise.reject(\n makeError(\n ErrorCode.INVALID_OPTIONS,\n 'Target container not found or not uniquely resolvable',\n this.elementId\n )\n );\n }\n if (container.childElementCount > 0) {\n return Promise.reject(\n makeError(ErrorCode.INVALID_OPTIONS, 'Target container is not empty', this.elementId)\n );\n }\n\n this.status = 'loading';\n this.container = container;\n\n const iframe = document.createElement('iframe');\n iframe.src = this.resolveSrc();\n iframe.title = `sunbay-checkout-${this.type}`;\n iframe.setAttribute('data-element-id', this.elementId);\n iframe.style.border = '0';\n iframe.style.width = '100%';\n iframe.style.display = 'block';\n // Height is fully driven by the iframe auto-resize protocol (child reports a ceil()'d\n // height via postMessage; see installResizeListener). Disable the iframe's own scrollbar\n // so sub-pixel content height differences on high-DPR browsers never surface a spurious\n // scrollbar (observed on the express button at devicePixelRatio=2).\n iframe.setAttribute('scrolling', 'no');\n container.appendChild(iframe);\n this.iframe = iframe;\n this.installResizeListener();\n\n return new Promise<void>((resolve, reject) => {\n const cleanup = (): void => {\n if (this.readyHandler) {\n this.transport.off('elementReady', this.readyHandler);\n this.readyHandler = null;\n }\n if (this.loadErrorHandler) {\n this.transport.off('loadError', this.loadErrorHandler);\n this.loadErrorHandler = null;\n }\n };\n\n const onReady = (payload: unknown): void => {\n if (!matchesElement(payload, this.elementId)) {\n return;\n }\n cleanup();\n // expressCheckout: capture the initial wallet availability carried by ready.\n const wallets = (payload as ElementReadyPayload).availableWallets;\n if (wallets) {\n this.availableWallets = wallets;\n }\n this.status = 'ready';\n resolve();\n };\n\n const onLoadError = (payload: unknown): void => {\n if (!matchesElement(payload, this.elementId)) {\n return;\n }\n cleanup();\n this.status = 'loadError';\n this.removeIframe();\n const err = (payload as LoadErrorPayload)?.error;\n reject(\n err ??\n makeError(\n ErrorCode.RUNTIME_LOAD_FAILED,\n 'Failed to load Element',\n this.elementId\n )\n );\n };\n\n this.readyHandler = onReady;\n this.loadErrorHandler = onLoadError;\n this.transport.on('elementReady', onReady);\n this.transport.on('loadError', onLoadError);\n\n // Send the createElement command; a command-level failure also goes through the loadError side branch.\n this.transport\n .request('createElement', {\n elementId: this.elementId,\n type: this.type,\n options: this.options,\n })\n .catch((cmdErr: unknown) => {\n cleanup();\n this.status = 'loadError';\n this.removeIframe();\n reject(cmdErr as CheckoutError);\n });\n });\n }\n\n /**\n * Unmount: send destroyElement, remove the iframe; idempotent; keeps the handle and configuration.\n *\n * @returns Resolves on completion (resolves directly when not mounted).\n * @throws INSTANCE_DESTROYED when already destroyed.\n */\n async unmount(): Promise<void> {\n this.assertAlive();\n if (this.status !== 'ready' && this.status !== 'loading') {\n // Not mounted: idempotent no-op.\n return;\n }\n try {\n await this.transport.request('destroyElement', { elementId: this.elementId });\n } finally {\n this.removeIframe();\n this.container = null;\n this.status = 'created';\n }\n }\n\n /**\n * Destroy the handle: idempotent; remove the iframe; afterwards any non-destroy/off call throws INSTANCE_DESTROYED.\n */\n destroy(): void {\n if (this.status === 'destroyed') {\n return;\n }\n if (this.readyHandler) {\n this.transport.off('elementReady', this.readyHandler);\n this.readyHandler = null;\n }\n if (this.loadErrorHandler) {\n this.transport.off('loadError', this.loadErrorHandler);\n this.loadErrorHandler = null;\n }\n if (this.availabilityHandler) {\n this.transport.off('expressAvailabilityChange', this.availabilityHandler);\n this.availabilityHandler = null;\n }\n this.removeIframe();\n this.container = null;\n this.status = 'destroyed';\n // Notify the instance to remove itself from the mutual-exclusion registry, freeing the slot to allow recreating a mutually-exclusive Element of the same kind after destruction.\n this.onDestroy?.(this.elementId);\n }\n\n /**\n * Update the Element configuration: send updateElement; illegal fields are rejected wholesale by the hosted side.\n *\n * @param patch - Configuration delta.\n * @returns Resolves after successfully writing back the local options.\n * @throws INSTANCE_DESTROYED when already destroyed.\n */\n async update(patch: Partial<ElementOptions>): Promise<void> {\n this.assertAlive();\n const resp = await this.transport.request('updateElement', {\n elementId: this.elementId,\n patch,\n });\n const next = (resp as { options?: ElementOptions } | undefined)?.options;\n if (next) {\n this.options = next;\n }\n }\n\n /**\n * Register an event handler (passed through to the transport, dispatched by event name).\n *\n * @param event - Event name.\n * @param handler - Callback.\n * @throws INSTANCE_DESTROYED when already destroyed.\n */\n on(event: EventName, handler: (payload: unknown) => void): void {\n this.assertAlive();\n this.transport.on(event, handler);\n }\n\n /**\n * Unregister an event handler (still safe to call after destruction).\n *\n * @param event - Event name.\n * @param handler - The previously registered callback.\n */\n off(event: EventName, handler: (payload: unknown) => void): void {\n this.transport.off(event, handler);\n }\n\n /** Focus (input classes only, not provided by expressCheckout). */\n focus?: () => Promise<void>;\n /** Blur (input classes only, not provided by expressCheckout). */\n blur?: () => Promise<void>;\n /** Clear (input classes only, not provided by expressCheckout). */\n clear?: () => Promise<void>;\n\n /** Install input-class action methods (automatically called after construction). */\n private installInputActions(): void {\n if (!INPUT_TYPES.includes(this.type)) {\n return;\n }\n this.focus = (): Promise<void> => this.sendAction('focus');\n this.blur = (): Promise<void> => this.sendAction('blur');\n this.clear = (): Promise<void> => this.sendAction('clear');\n }\n\n /**\n * expressCheckout only: persistently subscribe to expressAvailabilityChange and update the local availability snapshot.\n *\n * This event payload has no elementId; express is a singleton (guaranteed by CheckoutInstance),\n * so there is no need to filter by elementId. Non-express types do not subscribe.\n */\n private installExpressAvailability(): void {\n if (this.type !== 'expressCheckout') {\n return;\n }\n const handler = (payload: unknown): void => {\n if (typeof payload !== 'object' || payload === null) {\n return;\n }\n const wallets = (payload as ExpressAvailabilityChangeEvent).availableWallets;\n if (wallets) {\n this.availableWallets = wallets;\n }\n };\n this.availabilityHandler = handler;\n this.transport.on('expressAvailabilityChange', handler);\n }\n\n /** Send one input-class action command (synchronously throws INSTANCE_DESTROYED after destruction). */\n private sendAction(name: 'focus' | 'blur' | 'clear'): Promise<void> {\n this.assertAlive();\n return this.transport.request(name, { elementId: this.elementId }).then(() => undefined);\n }\n\n /** Throw INSTANCE_DESTROYED if already destroyed. */\n private assertAlive(): void {\n if (this.status === 'destroyed') {\n throw makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', this.elementId);\n }\n }\n\n /** Resolve the mount container (the selector must uniquely match a single element). */\n private resolveContainer(selectorOrEl: string | HTMLElement): HTMLElement | null {\n if (typeof selectorOrEl !== 'string') {\n return selectorOrEl;\n }\n const nodes = document.querySelectorAll(selectorOrEl);\n if (nodes.length !== 1) {\n return null;\n }\n const node = nodes[0];\n return node instanceof HTMLElement ? node : null;\n }\n\n /**\n * Resolve the iframe src: split fields go to /sdk/field/, the rest to /sdk/element/, with\n * checkoutId and elementId; if an initial colorScheme is provided, append `&colorScheme=<value>`\n * (no flash on the first frame); if the initial appearance contains variables or rules, append\n * `&appearance=<encodeURIComponent(JSON.stringify(appearance))>` (takes effect on the first\n * frame, no flash). An appearance containing only colorScheme (no variables/rules) is not\n * appended, to avoid an empty appearance string. For expressCheckout, additionally append the\n * wallet toggles / per-wallet buttonType / global buttonHeight (see {@link resolveExpressQuery}).\n *\n * elementId is sent with the src so the hosted child echoes it verbatim in iframe-resize\n * messages, letting the parent window dispatch the height per handle among multiple iframes on\n * the same page.\n */\n private resolveSrc(): string {\n const segment = FIELD_TYPES.includes(this.type) ? 'field' : 'element';\n let query = `?checkoutId=${encodeURIComponent(this.checkoutId)}`;\n query += `&elementId=${encodeURIComponent(this.elementId)}`;\n // address: pass the mode (billing/shipping) through so the hosted page binds the\n // correct same-origin field channel (address:<mode>). Without it, both address\n // Elements fall back to the default mode and the second never pairs/readies.\n if (this.type === 'address') {\n const mode = (this.options as { mode?: string } | undefined)?.mode;\n if (mode) {\n query += `&mode=${encodeURIComponent(mode)}`;\n }\n }\n if (this.colorScheme) {\n query += `&colorScheme=${encodeURIComponent(this.colorScheme)}`;\n }\n // Effective appearance for the first frame: a per-element appearance (from\n // create(type, { appearance })) takes precedence over the instance-level appearance\n // (from createCheckout). Serialize only when it contains variables or rules (avoid an\n // empty appearance string; colorScheme is passed through separately).\n const effectiveAppearance = this.perElementAppearance() ?? this.appearance;\n if (effectiveAppearance && (effectiveAppearance.variables || effectiveAppearance.rules)) {\n query += `&appearance=${encodeURIComponent(JSON.stringify(effectiveAppearance))}`;\n }\n // expressCheckout: pass the express options from create's second argument (wallet toggles /\n // buttonType / buttonHeight) through to the hosted iframe, so the merchant's configuration\n // takes effect on the first frame (C1 broken-link fix).\n if (this.type === 'expressCheckout') {\n query += this.resolveExpressQuery();\n }\n return `${this.hostOrigin}/sdk/${segment}/${this.type}${query}`;\n }\n\n /**\n * Extract a per-element appearance (from the Element's own options, i.e. the second\n * argument of create(type, options)). Returns undefined when absent. Used so per-element\n * customization (e.g. CardFieldElementOptions.appearance) is honored on the first frame.\n */\n private perElementAppearance(): Appearance | undefined {\n const opts = this.options as { appearance?: Appearance } | undefined;\n return opts?.appearance;\n }\n\n /**\n * Build the expressCheckout-specific query fragment (only called for expressCheckout):\n * - a wallet explicitly set to `false` → `&applePay=false` / `&googlePay=false` (force hidden,\n * preserving the existing toggle semantics; `true` / absent → automatic, no toggle appended);\n * - a wallet configured in object form containing `buttonType` → `&applePayButtonType=<v>` /\n * `&googlePayButtonType=<v>` (boolean toggle / absent → not appended);\n * - global `buttonHeight` (a finite number) → `&buttonHeight=<n>`.\n *\n * Appended only when there is a value; empty strings are not appended. This fragment never\n * touches card values and only carries display configuration.\n *\n * @returns A query fragment starting with `&` (returns an empty string when nothing is appendable).\n */\n private resolveExpressQuery(): string {\n const options = this.options as ExpressCheckoutElementOptions | undefined;\n if (!options) {\n return '';\n }\n let query = '';\n const applePay = options.paymentMethods?.applePay;\n const googlePay = options.paymentMethods?.googlePay;\n // Wallet toggle: only an explicit false (force hidden) is sent, preserving the existing toggle semantics.\n if (applePay === false) {\n query += `&applePay=false`;\n }\n if (googlePay === false) {\n query += `&googlePay=false`;\n }\n // Per-wallet buttonType: sent only when in object form and containing buttonType.\n if (typeof applePay === 'object' && applePay.buttonType) {\n query += `&applePayButtonType=${encodeURIComponent(applePay.buttonType)}`;\n }\n if (typeof googlePay === 'object' && googlePay.buttonType) {\n query += `&googlePayButtonType=${encodeURIComponent(googlePay.buttonType)}`;\n }\n // Global button height: sent only when a finite number (the hosted side then clamps to [30,64]).\n if (typeof options.buttonHeight === 'number' && Number.isFinite(options.buttonHeight)) {\n query += `&buttonHeight=${encodeURIComponent(String(options.buttonHeight))}`;\n }\n return query;\n }\n\n /**\n * Install the iframe auto-height listener: subscribe to window 'message', only trust a valid\n * {@link IframeResizeMessage} from this iframe's `contentWindow` with origin === hostOrigin, and\n * when the elementId matches this handle, adjust the outer iframe's height to the content's real\n * height (width stays 100%).\n *\n * Security: first verify `event.source === iframe.contentWindow` (the key source check), then\n * verify `event.origin === hostOrigin` (a stricter same-origin check), to prevent cross-origin forgery.\n * Height deduplication: skip if identical to the last one, to avoid redundant style writes.\n */\n private installResizeListener(): void {\n const handler = (event: MessageEvent): void => {\n if (!this.iframe) {\n return;\n }\n if (event.source !== this.iframe.contentWindow) {\n return;\n }\n if (event.origin !== this.hostOrigin) {\n return;\n }\n if (!isIframeResizeMessage(event.data)) {\n return;\n }\n if (event.data.elementId !== this.elementId) {\n return;\n }\n const { height } = event.data;\n if (height === this.lastHeight) {\n return;\n }\n this.lastHeight = height;\n this.iframe.style.height = `${height}px`;\n };\n this.resizeHandler = handler;\n window.addEventListener('message', handler);\n }\n\n /** Unbind the iframe auto-height listener and reset the deduplication state (idempotent). */\n private teardownResizeListener(): void {\n if (this.resizeHandler) {\n window.removeEventListener('message', this.resizeHandler);\n this.resizeHandler = null;\n }\n this.lastHeight = null;\n }\n\n /** Remove the iframe DOM node (if present) and unbind the auto-height listener. */\n private removeIframe(): void {\n this.teardownResizeListener();\n if (this.iframe && this.iframe.parentNode) {\n this.iframe.parentNode.removeChild(this.iframe);\n }\n this.iframe = null;\n }\n\n /** Factory: equivalent to `new CheckoutElement(opts)` (a unified entry for CheckoutInstance). */\n static create(opts: CheckoutElementOptions): CheckoutElement {\n return new CheckoutElement(opts);\n }\n}\n","/**\n * CheckoutInstance — the merchant-side checkout instance handle (spec 4 / 7 / 8).\n *\n * Responsibilities:\n * - Constructed by `createCheckout` after a successful handshake + initialize (holds the ready\n * transport, runtimeId, capabilities, and the initial PublicSessionView).\n * - `create` assembles and returns a {@link CheckoutElement}, maintaining a **mutual-exclusion\n * registry** (card and split fields are mutually exclusive, same-mode addresses are mutually\n * exclusive, expressCheckout is a singleton → `ELEMENT_CONFLICT`).\n * - `confirm` generates an operationId, sends the confirm command to obtain accepted, and the\n * Promise is fulfilled by a later `paymentResult` event with the same operationId; calling\n * confirm again while in progress → `CHECKOUT_BUSY`.\n * - `getSession/refresh/getPaymentStatus/updateCheckout` pass through to the corresponding commands.\n * - `destroy` is idempotent: releases Elements, unbinds listeners, rejects the pending confirm with\n * INSTANCE_DESTROYED, sends the destroy command and destroys the transport; any late\n * `paymentResult` afterwards is ignored.\n *\n * Security constraint (AGENTS/spec): this layer only ferries protocol commands/events and\n * **never touches card values**.\n */\n\nimport { CheckoutElement, type CheckoutElementOptions } from './Element';\nimport {\n ErrorCode,\n type Appearance,\n type CheckoutError,\n type CommandName,\n type ConfirmReq,\n type ElementOptions,\n type ElementType,\n type EventName,\n type GetPaymentStatusResp,\n type PaymentOutcome,\n type PaymentResultEvent,\n type PublicSessionView,\n type UpdateCheckoutReq,\n} from '@sunbay/checkout-sdk-protocol';\n\n/**\n * The minimal transport interface CheckoutInstance depends on (satisfied by MainFrameTransport).\n *\n * Adds `onHello/createHiddenIframe/destroy` beyond ElementTransport, used by createCheckout for\n * handshake wiring and for instance destruction. Tests can inject a mock.\n */\nexport interface InstanceTransport {\n request(name: CommandName, payload: unknown): Promise<unknown>;\n on(event: EventName, handler: (payload: unknown) => void): void;\n off(event: EventName, handler: (payload: unknown) => void): void;\n onHello(handler: (hello: unknown) => void): void;\n setRuntimeId(runtimeId: string): void;\n createHiddenIframe(): unknown;\n destroy(): void;\n}\n\n/** CheckoutInstance constructor parameters (assembled by createCheckout). */\nexport interface CheckoutInstanceOptions {\n /** Unique checkout id. */\n checkoutId: string;\n /** Negotiated runtime id (from RuntimeHello). */\n runtimeId: string;\n /** Hosted page origin (without a trailing slash). */\n hostOrigin: string;\n /** The ready transport. */\n transport: InstanceTransport;\n /** The capabilities returned by initialize. */\n capabilities: string[];\n /** The initial session view returned by initialize. */\n session: PublicSessionView;\n /**\n * Initial appearance customization (from createCheckout's appearance).\n * Passed through to every created Element and serialized into its iframe src (no flash on the\n * first frame):\n * - colorScheme appended as `&colorScheme=`;\n * - variables/rules appended as `&appearance=` (only when non-empty).\n * Not appended when absent. Runtime switching goes through the updateCheckout command.\n */\n appearance?: Appearance;\n}\n\n/** Optional confirm parameters (aligned with ConfirmReq, minus the instance-generated operationId). */\nexport type ConfirmOptions = Omit<ConfirmReq, 'operationId'>;\n\n/** Pairing record for a pending confirm. */\ninterface PendingConfirm {\n operationId: string;\n resolve: (outcome: PaymentOutcome) => void;\n reject: (reason: CheckoutError) => void;\n}\n\n/** The set of split card field types. */\nconst CARD_FIELD_TYPES: readonly ElementType[] = ['cardNumber', 'cardExpiry', 'cardCvc'];\n\n/** Generate a unique id. */\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2);\n return `${prefix}_${Date.now().toString(36)}_${rand}`;\n}\n\n/** Construct a structured CheckoutError. */\nfunction makeError(\n code: string,\n message: string,\n category: CheckoutError['category'] = 'configuration'\n): CheckoutError {\n return {\n code,\n message,\n category,\n errorId: genId('err'),\n };\n}\n\nexport class CheckoutInstance {\n /** Unique checkout id. */\n readonly checkoutId: string;\n /** Negotiated runtime id. */\n readonly runtimeId: string;\n /** Negotiated capability set. */\n readonly capabilities: readonly string[];\n\n private readonly hostOrigin: string;\n private readonly transport: InstanceTransport;\n private session: PublicSessionView;\n /** Initial appearance customization (passed through to each created Element's iframe src). */\n private readonly appearance: Appearance | undefined;\n\n /** Created Elements (indexed by elementId). */\n /**\n * The **single source of truth** for the mutual-exclusion registry: elementId → the created\n * CheckoutElement. Mutual-exclusion decisions are derived live from this Map (see\n * {@link assertNoConflict}); an element's `destroy()` removes itself from this Map via\n * {@link releaseElement} to free the slot (allowing a mutually-exclusive Element of the same\n * kind to be recreated after destruction).\n */\n private readonly elements = new Map<string, CheckoutElement>();\n\n /** The current pending confirm (at most one at a time). */\n private pendingConfirm: PendingConfirm | null = null;\n /** The paymentResult event subscription handler (unbound on destroy). */\n private readonly paymentResultHandler: (payload: unknown) => void;\n\n private destroyed = false;\n\n constructor(opts: CheckoutInstanceOptions) {\n this.checkoutId = opts.checkoutId;\n this.runtimeId = opts.runtimeId;\n this.capabilities = opts.capabilities;\n this.hostOrigin = opts.hostOrigin;\n this.transport = opts.transport;\n this.session = opts.session;\n this.appearance = opts.appearance;\n\n this.paymentResultHandler = (payload: unknown): void => {\n this.handlePaymentResult(payload);\n };\n this.transport.on('paymentResult', this.paymentResultHandler);\n }\n\n /**\n * Create an Element handle (not auto-mounted; a mutual-exclusion conflict throws ELEMENT_CONFLICT).\n *\n * Mutual-exclusion rules (spec 6):\n * - The combined `card` and any split field (cardNumber/cardExpiry/cardCvc) are mutually exclusive.\n * - An address with the same `mode` (billing/shipping) cannot be duplicated.\n * - `expressCheckout` is a singleton.\n *\n * @param type - Element type.\n * @param options - Initial Element configuration.\n * @returns The newly created CheckoutElement.\n * @throws INSTANCE_DESTROYED when already destroyed; ELEMENT_CONFLICT on a mutual-exclusion conflict.\n */\n create(type: ElementType, options?: ElementOptions): CheckoutElement {\n this.assertAlive();\n this.assertNoConflict(type, options);\n\n const elementId = genId('el');\n const elementOpts: CheckoutElementOptions = {\n elementId,\n type,\n hostOrigin: this.hostOrigin,\n checkoutId: this.checkoutId,\n transport: this.transport,\n initialOptions: options,\n colorScheme: this.appearance?.colorScheme,\n appearance: this.appearance,\n onDestroy: (id: string) => this.releaseElement(id),\n };\n const element = CheckoutElement.create(elementOpts);\n this.elements.set(elementId, element);\n return element;\n }\n\n /**\n * Initiate a payment confirmation. Generates an operationId, sends the confirm command to obtain\n * accepted, and the returned Promise is fulfilled by a later paymentResult event with the same\n * operationId. Calling again while in progress → CHECKOUT_BUSY.\n *\n * @param opts - Optional parameters such as billing/shipping address, name, email, returnUrl.\n * @returns The terminal PaymentOutcome.\n * @throws rejects INSTANCE_DESTROYED when destroyed; rejects CHECKOUT_BUSY when in progress.\n */\n confirm(opts?: ConfirmOptions): Promise<PaymentOutcome> {\n if (this.destroyed) {\n return Promise.reject(\n makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', 'runtime')\n );\n }\n if (this.pendingConfirm) {\n return Promise.reject(\n makeError(ErrorCode.CHECKOUT_BUSY, 'A payment confirmation is already in progress', 'runtime')\n );\n }\n\n const operationId = genId('op');\n const payload: ConfirmReq = { ...opts, operationId };\n\n return new Promise<PaymentOutcome>((resolve, reject) => {\n // Reserve the slot first, to prevent a concurrent confirm before accepted.\n this.pendingConfirm = { operationId, resolve, reject };\n this.transport.request('confirm', payload).catch((err: unknown) => {\n // The confirm command itself failed: clear the reservation and reject (paymentResult won't arrive).\n if (this.pendingConfirm && this.pendingConfirm.operationId === operationId) {\n this.pendingConfirm = null;\n }\n reject(err as CheckoutError);\n });\n });\n }\n\n /**\n * Get the latest session view (passes through the getSession command and writes back the local snapshot).\n *\n * @returns The latest PublicSessionView.\n * @throws rejects INSTANCE_DESTROYED when destroyed.\n */\n async getSession(): Promise<PublicSessionView> {\n this.assertAlive();\n const resp = (await this.transport.request('getSession', {})) as PublicSessionView;\n this.session = resp;\n return resp;\n }\n\n /**\n * Refresh the session (passes through the refresh command and writes back the local snapshot).\n *\n * @returns The refreshed PublicSessionView.\n * @throws rejects INSTANCE_DESTROYED when destroyed.\n */\n async refresh(): Promise<PublicSessionView> {\n this.assertAlive();\n const resp = (await this.transport.request('refresh', {})) as PublicSessionView;\n this.session = resp;\n return resp;\n }\n\n /**\n * Query the payment status (returns TxnStatus as-is, no transformation).\n *\n * @returns txnStatus (may include txnId).\n * @throws rejects INSTANCE_DESTROYED when destroyed.\n */\n async getPaymentStatus(): Promise<GetPaymentStatusResp> {\n this.assertAlive();\n return (await this.transport.request('getPaymentStatus', {})) as GetPaymentStatusResp;\n }\n\n /**\n * Update the checkout appearance/locale (does not change the session or the amount).\n *\n * @param patch - locale / appearance delta.\n * @throws rejects INSTANCE_DESTROYED when destroyed.\n */\n async updateCheckout(patch: { locale?: string; appearance?: Appearance }): Promise<void> {\n this.assertAlive();\n const payload: UpdateCheckoutReq = patch;\n await this.transport.request('updateCheckout', payload);\n }\n\n /**\n * Register a Checkout-level event handler (passes through the transport).\n *\n * @param event - Event name.\n * @param handler - Callback.\n * @throws INSTANCE_DESTROYED when destroyed.\n */\n on(event: EventName, handler: (payload: unknown) => void): void {\n this.assertAlive();\n this.transport.on(event, handler);\n }\n\n /**\n * Unregister an event handler (safe to call after destruction).\n *\n * @param event - Event name.\n * @param handler - The previously registered callback.\n */\n off(event: EventName, handler: (payload: unknown) => void): void {\n this.transport.off(event, handler);\n }\n\n /**\n * Destroy the instance: idempotent. Releases all Elements, unbinds the paymentResult listener,\n * rejects the pending confirm with INSTANCE_DESTROYED, sends the destroy command and destroys\n * the transport. Any late paymentResult afterwards is ignored and does not overwrite an already\n * settled Promise.\n */\n destroy(): void {\n if (this.destroyed) {\n return;\n }\n this.destroyed = true;\n\n // Release all Element handles.\n for (const element of this.elements.values()) {\n element.destroy();\n }\n this.elements.clear();\n\n // Unbind the paymentResult listener (no handler can be reached by a late result afterwards).\n this.transport.off('paymentResult', this.paymentResultHandler);\n\n // Reject the pending confirm with INSTANCE_DESTROYED.\n if (this.pendingConfirm) {\n this.pendingConfirm.reject(\n makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', 'runtime')\n );\n this.pendingConfirm = null;\n }\n\n // Notify the hosted side and destroy the transport (request after destroy is rejected by the transport itself).\n void Promise.resolve(this.transport.request('destroy', {})).catch(() => undefined);\n this.transport.destroy();\n }\n\n /** Throw INSTANCE_DESTROYED if already destroyed. */\n private assertAlive(): void {\n if (this.destroyed) {\n throw makeError(ErrorCode.INSTANCE_DESTROYED, 'Checkout instance has been destroyed', 'runtime');\n }\n }\n\n /** Mutual-exclusion check: throws ELEMENT_CONFLICT on conflict (derived live from the elements Map, does not mutate the registry). */\n private assertNoConflict(type: ElementType, options?: ElementOptions): void {\n const existing = [...this.elements.values()];\n const has = (t: ElementType): boolean => existing.some((e) => e.type === t);\n const hasCardField = (): boolean => existing.some((e) => CARD_FIELD_TYPES.includes(e.type));\n\n if (type === 'card') {\n if (has('card')) {\n throw makeError(ErrorCode.ELEMENT_CONFLICT, 'A combined card Element already exists');\n }\n if (hasCardField()) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n 'Combined card and split card fields are mutually exclusive; split card fields already exist'\n );\n }\n return;\n }\n if (CARD_FIELD_TYPES.includes(type)) {\n if (has('card')) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n 'Split card fields and combined card are mutually exclusive; a combined card already exists'\n );\n }\n // The same split field type cannot be duplicated.\n if (has(type)) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n `A split card field of type ${type} already exists`\n );\n }\n return;\n }\n if (type === 'address') {\n const mode = (options as { mode?: string } | undefined)?.mode;\n if (mode && existing.some((e) => e.type === 'address' && e.mode === mode)) {\n throw makeError(\n ErrorCode.ELEMENT_CONFLICT,\n `An address Element with mode=${mode} already exists`\n );\n }\n return;\n }\n if (type === 'expressCheckout') {\n if (has('expressCheckout')) {\n throw makeError(ErrorCode.ELEMENT_CONFLICT, 'An expressCheckout Element already exists');\n }\n return;\n }\n }\n\n /**\n * Element `destroy()` callback: remove this elementId from the mutual-exclusion registry (the\n * elements Map) to free the slot. Idempotent; when the whole instance is destroyed (destroyed\n * already set) it goes through the unified `elements.clear()` path, so this is a no-op here,\n * avoiding concurrent modification while iterating elements to call element.destroy().\n */\n private releaseElement(elementId: string): void {\n if (this.destroyed) {\n return;\n }\n this.elements.delete(elementId);\n }\n\n /** Handle the paymentResult event: fulfill the pending confirm by operationId; late/mismatched ones are ignored. */\n private handlePaymentResult(payload: unknown): void {\n if (this.destroyed) {\n return;\n }\n if (typeof payload !== 'object' || payload === null) {\n return;\n }\n const evt = payload as Partial<PaymentResultEvent>;\n const pending = this.pendingConfirm;\n if (!pending) {\n return;\n }\n if (evt.operationId !== pending.operationId) {\n return;\n }\n if (!evt.outcome) {\n return;\n }\n this.pendingConfirm = null;\n pending.resolve(evt.outcome);\n }\n}\n","/**\n * Runtime environment identifier.\n * - `dev`: development hosted page.\n * - `test`: UAT/test hosted page.\n * - `prod`: production hosted page.\n */\nexport type Env = 'dev' | 'test' | 'prod';\n\n/**\n * Built-in hosted page origins (consistently without a trailing slash).\n */\nconst HOST_ORIGINS: Record<Env, string> = {\n dev: 'https://checkout.sunbay-dev.com',\n test: 'https://checkout.sunbay-uat.us',\n prod: 'https://checkout.sunbay.us',\n};\n\n/**\n * Resolve the hosted page origin.\n *\n * @param env - Runtime environment, defaults to `'prod'`.\n * @param override - Override origin (for env vars/tests); takes precedence when non-empty.\n * @returns Hosted page origin (without a trailing slash).\n * @example\n * resolveHostOrigin(); // 'https://checkout.sunbay.us'\n * resolveHostOrigin('dev'); // 'https://checkout.sunbay-dev.com'\n * resolveHostOrigin('test'); // 'https://checkout.sunbay-uat.us'\n * resolveHostOrigin('dev', 'http://localhost:3000'); // 'http://localhost:3000'\n */\nexport function resolveHostOrigin(env: Env = 'prod', override?: string): string {\n if (override) {\n return override;\n }\n return HOST_ORIGINS[env];\n}\n","/**\n * createCheckout — the merchant SDK's single async entry (spec 4).\n *\n * Flow:\n * 1. Generate `checkoutId`, derive the hosted origin via `resolveHostOrigin(env, hostOrigin)`.\n * 2. Build the transport, `createHiddenIframe()` to load the hosted main iframe.\n * 3. Register onHello; wait for RuntimeHello (timeout 15s → reject RUNTIME_LOAD_FAILED).\n * 4. Pick a common major version (no intersection → reject PROTOCOL_UNSUPPORTED).\n * 5. Send `initialize` (sessionId/locale/appearance/paymentMethods) to obtain\n * capabilities + PublicSessionView; any step failing rejects and destroys the transport.\n * 6. Inject the handshake runtimeId into the transport, construct and resolve {@link CheckoutInstance}.\n *\n * Security constraint (AGENTS/spec): this layer only orchestrates handshake/initialization and\n * **never touches card values**.\n */\n\nimport { MainFrameTransport } from './transport/MainFrameTransport';\nimport { CheckoutInstance, type InstanceTransport } from './CheckoutInstance';\nimport { resolveHostOrigin, type Env } from './config/env';\nimport {\n ErrorCode,\n PROTOCOL_MAJOR,\n type Appearance,\n type CheckoutError,\n type InitializeReq,\n type InitializeResp,\n type PaymentMethodFilter,\n type RuntimeHello,\n} from '@sunbay/checkout-sdk-protocol';\n\n/** Handshake timeout (ms): matches the transport handshake timeout (15s). */\nconst HANDSHAKE_TIMEOUT_MS = 15_000;\n\n/**\n * Internal transport factory injectable when creating a CheckoutInstance (test/advanced use only).\n *\n * Real scenarios use the built-in MainFrameTransport; tests may inject a mock to drive hello/events.\n */\nexport type TransportFactory = (args: {\n checkoutId: string;\n hostOrigin: string;\n}) => InstanceTransport;\n\n/** createCheckout parameters. */\nexport interface CreateCheckoutOptions {\n /** Checkout session id created by the backend (required). */\n sessionId: string;\n /** Runtime environment (dev/test/prod), defaults to prod. */\n env?: Env;\n /** Override the hosted origin (takes precedence over env). */\n hostOrigin?: string;\n /** Locale. */\n locale?: string;\n /** Appearance customization. */\n appearance?: Appearance;\n /** Payment method filter (subtractive/reordering only). */\n paymentMethods?: PaymentMethodFilter;\n /** Internal injection point: custom transport factory (test use; not part of the public API contract). */\n __transportFactory?: TransportFactory;\n}\n\n/** Generate a unique id. */\nfunction genId(prefix: string): string {\n const rand = Math.random().toString(36).slice(2);\n return `${prefix}_${Date.now().toString(36)}_${rand}`;\n}\n\n/** Construct a structured CheckoutError (runtime category). */\nfunction makeError(code: string, message: string): CheckoutError {\n return {\n code,\n message,\n category: 'runtime',\n errorId: genId('err'),\n };\n}\n\n/** Default transport factory: the built-in MainFrameTransport. */\nconst defaultTransportFactory: TransportFactory = ({ checkoutId, hostOrigin }) =>\n new MainFrameTransport({ checkoutId, hostOrigin });\n\n/**\n * Create a checkout instance: resolves after a successful handshake + initialization.\n *\n * @param opts - Checkout parameters.\n * @returns A ready CheckoutInstance.\n * @throws reject RUNTIME_LOAD_FAILED (handshake timeout) / PROTOCOL_UNSUPPORTED (no common major\n * version) / or the initialize business error.\n * @example\n * const checkout = await createCheckout({ sessionId: 'sess_x', env: 'prod' });\n */\nexport async function createCheckout(opts: CreateCheckoutOptions): Promise<CheckoutInstance> {\n const checkoutId = genId('co');\n const hostOrigin = resolveHostOrigin(opts.env, opts.hostOrigin);\n const factory = opts.__transportFactory ?? defaultTransportFactory;\n const transport = factory({ checkoutId, hostOrigin });\n\n // Start the hosted main iframe (hidden, 0 size).\n transport.createHiddenIframe();\n\n let hello: RuntimeHello;\n try {\n hello = await waitForHello(transport);\n } catch (err) {\n transport.destroy();\n throw err;\n }\n\n // Pick a common major version.\n if (!hello.supportedMajorVersions.includes(PROTOCOL_MAJOR)) {\n transport.destroy();\n throw makeError(\n ErrorCode.PROTOCOL_UNSUPPORTED,\n `No common major protocol version: SDK=${PROTOCOL_MAJOR}, runtime=${JSON.stringify(hello.supportedMajorVersions)}`\n );\n }\n\n // Inject the negotiated runtimeId: must happen before sending initialize, otherwise the\n // outbound envelope's runtimeId is an empty string, gets silently dropped by the main\n // endpoint, and causes initialize to time out.\n transport.setRuntimeId(hello.runtimeId);\n\n // Send initialize.\n const initReq: InitializeReq = {\n sessionId: opts.sessionId,\n locale: opts.locale,\n appearance: opts.appearance,\n paymentMethods: opts.paymentMethods,\n };\n let initResp: InitializeResp;\n try {\n initResp = (await transport.request('initialize', initReq)) as InitializeResp;\n } catch (err) {\n transport.destroy();\n throw err as CheckoutError;\n }\n\n return new CheckoutInstance({\n checkoutId,\n runtimeId: hello.runtimeId,\n hostOrigin,\n transport,\n capabilities: initResp.capabilities,\n session: initResp.session,\n appearance: opts.appearance,\n });\n}\n\n/** Wait for RuntimeHello; reject RUNTIME_LOAD_FAILED on timeout. */\nfunction waitForHello(transport: InstanceTransport): Promise<RuntimeHello> {\n return new Promise<RuntimeHello>((resolve, reject) => {\n let settled = false;\n const timer = setTimeout(() => {\n if (settled) {\n return;\n }\n settled = true;\n reject(makeError(ErrorCode.RUNTIME_LOAD_FAILED, 'Timed out waiting for hosted runtime handshake'));\n }, HANDSHAKE_TIMEOUT_MS);\n\n transport.onHello((raw: unknown) => {\n if (settled) {\n return;\n }\n settled = true;\n clearTimeout(timer);\n resolve(raw as RuntimeHello);\n });\n });\n}\n","/**\n * @sunbay/checkout-js public entry (source of truth).\n *\n * Exports the `createCheckout` entry, the `CheckoutInstance` / `CheckoutElement` handle types,\n * and the public protocol types (for TS consumers).\n * In the IIFE build, additionally exposes `{ createCheckout }` on `window.SunbayCheckout`.\n */\n\nexport { createCheckout } from './createCheckout';\nexport type {\n CreateCheckoutOptions,\n TransportFactory,\n} from './createCheckout';\n\nexport { CheckoutInstance } from './CheckoutInstance';\nexport type {\n CheckoutInstanceOptions,\n ConfirmOptions,\n InstanceTransport,\n} from './CheckoutInstance';\n\nexport { CheckoutElement } from './Element';\nexport type { CheckoutElementOptions, ElementTransport } from './Element';\n\nexport type { Env } from './config/env';\n\n// Public protocol types (the contract for TS consumers).\nexport {\n ErrorCode,\n type ErrorCodeValue,\n type CheckoutError,\n type CheckoutErrorCategory,\n type PaymentOutcome,\n type PublicSessionView,\n type PublicPaymentMethod,\n type PublicProductLine,\n type PaymentMethod,\n type PaymentMethodFilter,\n type Appearance,\n type AddressInfo,\n type TxnStatus,\n type ElementType,\n type ElementOptions,\n type ElementStatus,\n type CardElementOptions,\n type CardFieldElementOptions,\n type AddressElementOptions,\n type ExpressCheckoutElementOptions,\n} from '@sunbay/checkout-sdk-protocol';\n\nimport { createCheckout } from './createCheckout';\n\n/**\n * IIFE / browser global: mount `window.SunbayCheckout = { createCheckout }`.\n *\n * The tsup IIFE build already exposes the module exports via `globalName: 'SunbayCheckout'`;\n * this explicitly ensures `window.SunbayCheckout.createCheckout` is available (while staying\n * compatible with re-exporting more entries in the future).\n */\nif (typeof window !== 'undefined') {\n const w = window as typeof window & {\n SunbayCheckout?: { createCheckout: typeof createCheckout };\n };\n w.SunbayCheckout = { ...(w.SunbayCheckout ?? {}), createCheckout };\n}\n\n/**\n * Package version. Injected at build time by tsup's `define` from `package.json` as\n * `__SDK_VERSION__` (see tsup.config.ts), so the code's version always stays in sync with\n * package.json. Environments without a build (e.g. vitest running src directly) have no such\n * define and fall back to `'0.0.0'`.\n */\ndeclare const __SDK_VERSION__: string;\nexport const version: string =\n typeof __SDK_VERSION__ !== 'undefined' ? __SDK_VERSION__ : '0.0.0';\n"],"mappings":"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","names":["ErrorCode","makeError","ErrorCode","genId","makeError","ErrorCode","PROTOCOL_MAJOR","genId","makeError","ErrorCode"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sunbay/checkout-sdk-js",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Browser JS SDK for embedding Sunbay Checkout — the createCheckout entry point merchants use to render card fields and express wallet buttons in secure, Sunbay-hosted iframes.",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "./dist/index.cjs",
|
|
6
7
|
"module": "./dist/index.mjs",
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
"access": "public"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
|
22
|
-
"@sunbay/checkout-sdk-protocol": "0.0.
|
|
23
|
+
"@sunbay/checkout-sdk-protocol": "0.0.3"
|
|
23
24
|
},
|
|
24
25
|
"devDependencies": {
|
|
25
26
|
"jsdom": "^29.1.1",
|