@sunbay/checkout-sdk-js 0.0.0-stage → 0.0.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../../protocol/src/envelope.ts","../../protocol/src/errors.ts","../../protocol/src/commands.ts","../../protocol/src/events.ts","../../protocol/src/iframeLayout.ts","../src/transport/MainFrameTransport.ts","../src/Element.ts","../src/CheckoutInstance.ts","../src/config/env.ts","../src/createCheckout.ts"],"sourcesContent":["/**\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","/**\n * 信封与握手契约(spec 5.1,真源)。\n *\n * 协议名 `sunbay-checkout`,`major = 1`。与现有 Native Bridge `{v:1,type,...}`\n * 是不同协议,数字相同不代表兼容。\n *\n * `encodeEnvelope`/`decodeEnvelope`:JSON 序列化 + 最小运行时结构校验\n * (校验 protocol/major/kind/必填字段),非法抛错。解码器**不**校验 request.name\n * 是否为已知 CommandName —— 未知 name 仍解为 kind==='request',由调用方拒绝。\n */\n\nimport type { CheckoutError } from './types';\n\n/** 协议名常量。 */\nexport const PROTOCOL_NAME = 'sunbay-checkout' as const;\n/** 当前主版本。 */\nexport const PROTOCOL_MAJOR = 1 as const;\n\n/**\n * 握手消息(spec 5.1)。main iframe 加载后先广播,此时尚无 checkoutId。\n */\nexport type RuntimeHello = {\n protocol: 'sunbay-checkout';\n kind: 'hello';\n runtimeId: string;\n supportedMajorVersions: number[];\n /** 首期:['card','splitCard','address','express','paymentMethodFilter']。 */\n capabilities: string[];\n};\n\n/**\n * 初始化后的公共信封(spec 5.1)。\n */\nexport type Envelope = {\n protocol: 'sunbay-checkout';\n major: 1;\n runtimeId: string;\n checkoutId: string;\n elementId?: string;\n} & (\n | { kind: 'request'; requestId: string; name: string; payload: unknown }\n | { kind: 'response'; requestId: string; ok: true; data: unknown }\n | { kind: 'response'; requestId: string; ok: false; error: CheckoutError }\n | { kind: 'event'; eventId: string; name: string; operationId?: string; payload: unknown }\n);\n\n/** 合法 kind 集合。 */\nconst ENVELOPE_KINDS = ['request', 'response', 'event'] as const;\n\n/** 协议校验失败错误。 */\nexport class EnvelopeDecodeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'EnvelopeDecodeError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction assertString(obj: Record<string, unknown>, key: string): void {\n if (typeof obj[key] !== 'string') {\n throw new EnvelopeDecodeError(`Envelope field \"${key}\" is missing or not a string`);\n }\n}\n\n/**\n * 将信封序列化为线缆字符串(JSON)。\n *\n * @param envelope 待发送的信封\n * @returns JSON 字符串\n * @example\n * const wire = encodeEnvelope({ protocol: 'sunbay-checkout', major: 1, ... });\n */\nexport function encodeEnvelope(envelope: Envelope): string {\n return JSON.stringify(envelope);\n}\n\n/**\n * 解析并最小结构校验信封。非法(缺 protocol、错 major、非法 kind、缺必填字段、\n * 非 JSON)一律抛 {@link EnvelopeDecodeError}。\n *\n * 注意:解码器不校验 request.name 是否为已知命令 —— 未知 name 仍解为\n * kind==='request',交由调用方(如 Orchestrator)拒绝。\n *\n * @param wire 线缆字符串\n * @returns 已校验结构的信封\n * @example\n * const env = decodeEnvelope(wire);\n * if (env.kind === 'request') { ... }\n */\nexport function decodeEnvelope(wire: string): Envelope {\n let parsed: unknown;\n try {\n parsed = JSON.parse(wire);\n } catch {\n throw new EnvelopeDecodeError('Envelope is not valid JSON');\n }\n\n if (!isRecord(parsed)) {\n throw new EnvelopeDecodeError('Envelope must be an object');\n }\n\n if (parsed.protocol !== PROTOCOL_NAME) {\n throw new EnvelopeDecodeError(\n `protocol must be \"${PROTOCOL_NAME}\", received ${JSON.stringify(parsed.protocol)}`\n );\n }\n if (parsed.major !== PROTOCOL_MAJOR) {\n throw new EnvelopeDecodeError(\n `major must be ${PROTOCOL_MAJOR}, received ${JSON.stringify(parsed.major)}`\n );\n }\n\n assertString(parsed, 'runtimeId');\n assertString(parsed, 'checkoutId');\n if (parsed.elementId !== undefined && typeof parsed.elementId !== 'string') {\n throw new EnvelopeDecodeError('elementId must be a string when present');\n }\n\n const kind = parsed.kind;\n if (typeof kind !== 'string' || !(ENVELOPE_KINDS as readonly string[]).includes(kind)) {\n throw new EnvelopeDecodeError(`Invalid kind: ${JSON.stringify(kind)}`);\n }\n\n switch (kind) {\n case 'request': {\n assertString(parsed, 'requestId');\n assertString(parsed, 'name');\n if (!('payload' in parsed)) {\n throw new EnvelopeDecodeError('request envelope is missing payload');\n }\n break;\n }\n case 'response': {\n assertString(parsed, 'requestId');\n if (typeof parsed.ok !== 'boolean') {\n throw new EnvelopeDecodeError('response envelope is missing boolean ok');\n }\n if (parsed.ok === true) {\n if (!('data' in parsed)) {\n throw new EnvelopeDecodeError('successful response is missing data');\n }\n } else if (!isRecord(parsed.error)) {\n throw new EnvelopeDecodeError('failed response is missing error object');\n }\n break;\n }\n case 'event': {\n assertString(parsed, 'eventId');\n assertString(parsed, 'name');\n if (!('payload' in parsed)) {\n throw new EnvelopeDecodeError('event envelope is missing payload');\n }\n if (\n 'operationId' in parsed &&\n parsed.operationId !== undefined &&\n typeof parsed.operationId !== 'string'\n ) {\n throw new EnvelopeDecodeError('operationId must be a string when present');\n }\n break;\n }\n }\n\n // 结构已校验,确认为 Envelope 形状。\n return parsed as unknown as Envelope;\n}\n","/**\n * 协议错误码常量(单一真源,spec 5.4)。\n *\n * 不提供可能误导重复付款的通用 `retryable` 布尔。\n * 仅保留实际会被产出的错误码;新增码须同时有产出点。\n */\n\nexport const ErrorCode = {\n INVALID_OPTIONS: 'INVALID_OPTIONS',\n ELEMENT_CONFLICT: 'ELEMENT_CONFLICT',\n INSTANCE_DESTROYED: 'INSTANCE_DESTROYED',\n CHECKOUT_BUSY: 'CHECKOUT_BUSY',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n SESSION_LOAD_FAILED: 'SESSION_LOAD_FAILED',\n RUNTIME_LOAD_FAILED: 'RUNTIME_LOAD_FAILED',\n PROTOCOL_UNSUPPORTED: 'PROTOCOL_UNSUPPORTED',\n REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',\n PAYMENT_FLOW_ERROR: 'PAYMENT_FLOW_ERROR',\n UNSUPPORTED_CAPABILITY: 'UNSUPPORTED_CAPABILITY',\n} as const;\n\n/** 错误码字面量联合类型。 */\nexport type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode];\n","/**\n * 命令契约(商户 SDK → main,spec 5.2 表,真源)。\n *\n * 每命令的请求/响应类型在此定义;跨通道只传 JSON 可序列化数据。\n */\n\nimport type {\n Appearance,\n AddressInfo,\n ElementOptions,\n ElementType,\n PaymentMethodFilter,\n PublicSessionView,\n TxnStatus,\n} from './types';\n\n/** 全部命令名(spec 5.2)。 */\nexport type CommandName =\n | 'initialize'\n | 'createElement'\n | 'updateElement'\n | 'destroyElement'\n | 'focus'\n | 'blur'\n | 'clear'\n | 'updateCheckout'\n | 'getSession'\n | 'refresh'\n | 'getPaymentStatus'\n | 'confirm'\n | 'destroy';\n\n/** 已知命令名集合(供调用方校验 request.name 是否受支持)。 */\nexport const COMMAND_NAMES: readonly CommandName[] = [\n 'initialize',\n 'createElement',\n 'updateElement',\n 'destroyElement',\n 'focus',\n 'blur',\n 'clear',\n 'updateCheckout',\n 'getSession',\n 'refresh',\n 'getPaymentStatus',\n 'confirm',\n 'destroy',\n];\n\n/** 判断字符串是否为已知命令名。 */\nexport function isCommandName(name: string): name is CommandName {\n return (COMMAND_NAMES as readonly string[]).includes(name);\n}\n\n// --- initialize ---\nexport interface InitializeReq {\n sessionId: string;\n locale?: string;\n appearance?: Appearance;\n paymentMethods?: PaymentMethodFilter;\n}\nexport interface InitializeResp {\n capabilities: string[];\n session: PublicSessionView;\n}\n\n// --- createElement ---\nexport interface CreateElementReq {\n elementId: string;\n type: ElementType;\n options?: ElementOptions;\n}\nexport interface CreateElementResp {\n elementId: string;\n registered: true;\n}\n\n// --- updateElement ---\nexport interface UpdateElementReq {\n elementId: string;\n patch: Partial<ElementOptions>;\n}\nexport interface UpdateElementResp {\n elementId: string;\n options: ElementOptions;\n}\n\n// --- destroyElement ---\nexport interface DestroyElementReq {\n elementId: string;\n}\nexport interface DestroyElementResp {\n elementId: string;\n released: true;\n}\n\n// --- focus / blur / clear(仅输入类 Element)---\nexport interface ElementActionReq {\n elementId: string;\n}\nexport interface ElementActionResp {\n elementId: string;\n done: true;\n}\n\n// --- updateCheckout(不改会话/金额)---\nexport interface UpdateCheckoutReq {\n locale?: string;\n appearance?: Appearance;\n}\nexport interface UpdateCheckoutResp {\n updated: true;\n}\n\n// --- getSession / refresh ---\nexport type GetSessionReq = Record<string, never>;\nexport type GetSessionResp = PublicSessionView;\n\n// --- getPaymentStatus(原样返回 TxnStatus,不转义)---\nexport type GetPaymentStatusReq = Record<string, never>;\nexport interface GetPaymentStatusResp {\n txnStatus: TxnStatus;\n txnId?: string;\n}\n\n// --- confirm(仅表示开始执行)---\nexport interface ConfirmReq {\n operationId: string;\n billingAddress?: AddressInfo;\n shippingAddress?: AddressInfo;\n customerName?: string;\n email?: string;\n returnUrl?: string;\n}\nexport interface ConfirmResp {\n accepted: true;\n}\n\n// --- destroy ---\nexport type DestroyReq = Record<string, never>;\nexport interface DestroyResp {\n released: true;\n}\n\n/** 命令名 → 请求载荷映射。 */\nexport interface CommandRequestMap {\n initialize: InitializeReq;\n createElement: CreateElementReq;\n updateElement: UpdateElementReq;\n destroyElement: DestroyElementReq;\n focus: ElementActionReq;\n blur: ElementActionReq;\n clear: ElementActionReq;\n updateCheckout: UpdateCheckoutReq;\n getSession: GetSessionReq;\n refresh: GetSessionReq;\n getPaymentStatus: GetPaymentStatusReq;\n confirm: ConfirmReq;\n destroy: DestroyReq;\n}\n\n/** 命令名 → 成功响应载荷映射。 */\nexport interface CommandResponseMap {\n initialize: InitializeResp;\n createElement: CreateElementResp;\n updateElement: UpdateElementResp;\n destroyElement: DestroyElementResp;\n focus: ElementActionResp;\n blur: ElementActionResp;\n clear: ElementActionResp;\n updateCheckout: UpdateCheckoutResp;\n getSession: GetSessionResp;\n refresh: GetSessionResp;\n getPaymentStatus: GetPaymentStatusResp;\n confirm: ConfirmResp;\n destroy: DestroyResp;\n}\n","/**\n * 事件契约(main → 商户 SDK,spec 5.3 表,真源)。\n *\n * 公开事件仅暴露 empty/complete/brand/error 等允许状态,**绝不含卡值**,\n * 不提供卡数据 getValue。`on` 注册后不重放历史事件。\n */\n\nimport type {\n AvailableWallets,\n CheckoutError,\n PaymentAction,\n PaymentMethod,\n PaymentOutcome,\n} from './types';\n\n/** 全部事件名(spec 5.3)。 */\nexport type EventName =\n | 'ready'\n | 'change'\n | 'focus'\n | 'blur'\n | 'loadError'\n | 'elementReady'\n | 'expressAvailabilityChange'\n | 'paymentStateChange'\n | 'paymentResult'\n | 'error';\n\n/** 已知事件名集合。 */\nexport const EVENT_NAMES: readonly EventName[] = [\n 'ready',\n 'change',\n 'focus',\n 'blur',\n 'loadError',\n 'elementReady',\n 'expressAvailabilityChange',\n 'paymentStateChange',\n 'paymentResult',\n 'error',\n];\n\n/** 判断字符串是否为已知事件名。 */\nexport function isEventName(name: string): name is EventName {\n return (EVENT_NAMES as readonly string[]).includes(name);\n}\n\n// --- 输入 Element 事件 ---\nexport interface ReadyEvent {\n elementId: string;\n}\nexport interface ElementReadyEvent {\n elementId: string;\n /** 仅 expressCheckout Element ready 时携带:各钱包可用性。 */\n availableWallets?: AvailableWallets;\n}\nexport interface ChangeEvent {\n elementId: string;\n empty: boolean;\n complete: boolean;\n error?: CheckoutError;\n /** 仅卡号字段允许携带 brand;绝不含卡值。 */\n brand?: string;\n}\nexport interface FocusEvent {\n elementId: string;\n}\nexport interface BlurEvent {\n elementId: string;\n}\nexport interface LoadErrorEvent {\n elementId: string;\n error: CheckoutError;\n}\n\n// --- Express 事件 ---\n/**\n * express 可用钱包变化事件(spec 6)。\n * 专用于 expressCheckout:以 AvailableWallets 表达各钱包(Apple/Google Pay)可用性。\n */\nexport interface ExpressAvailabilityChangeEvent {\n availableWallets: AvailableWallets;\n}\n\n// --- Checkout 级事件 ---\nexport interface PaymentStateChangeEvent {\n operationId: string;\n method: PaymentMethod;\n /** 过程状态;requiresAction 时携带 action 信息。 */\n state: string;\n action?: PaymentAction;\n}\nexport interface PaymentResultEvent {\n operationId: string;\n method: PaymentMethod;\n outcome: PaymentOutcome;\n}\nexport interface ErrorEvent {\n /** 异步主动流程错误;可含 operationId(如已创建)。 */\n operationId?: string;\n error: CheckoutError;\n}\n\n/** 事件名 → 载荷映射。 */\nexport interface EventPayloadMap {\n ready: ReadyEvent;\n change: ChangeEvent;\n focus: FocusEvent;\n blur: BlurEvent;\n loadError: LoadErrorEvent;\n elementReady: ElementReadyEvent;\n expressAvailabilityChange: ExpressAvailabilityChangeEvent;\n paymentStateChange: PaymentStateChangeEvent;\n paymentResult: PaymentResultEvent;\n error: ErrorEvent;\n}\n","/**\n * iframe 布局协议:托管 iframe(checkout,child)→ 父窗口(商户 SDK,parent)的\n * **直连 postMessage** 契约,用于 iframe 自适应高度。\n *\n * 与外层 Envelope(商户 SDK ↔ main iframe)及内层点对点 port(element ↔ main)均区分:\n * 本消息由每个可见 field / element iframe 直接 postMessage 到其 `window.parent`(商户页),\n * 商户 SDK 的 Element 句柄监听后把外层 iframe 高度调整为内容真实高度(宽度保持 100%)。\n *\n * 安全约束(AGENTS/spec):本消息**绝不含任何卡值**(明文或密文),仅携带 height 与\n * elementId。父窗口侧须校验来源(`event.source === iframe.contentWindow` 且\n * `event.origin === hostOrigin`)后方可采信,防止跨源伪造。\n */\n\n/** iframe 自适应高度消息的判别字面量(命名空间化,避免与其它 postMessage 碰撞)。 */\nexport const IFRAME_RESIZE_MESSAGE_TYPE = 'sunbay:checkout:iframe-resize' as const;\n\n/**\n * iframe(child)→ 父窗口(SDK)的内容高度变更通知。\n *\n * child 用 `ResizeObserver` 观测内容高度,变化时发送(去重);parent 据此设\n * `iframe.style.height`。绝不含卡值。\n */\nexport interface IframeResizeMessage {\n /** 判别字段,固定为 {@link IFRAME_RESIZE_MESSAGE_TYPE}。 */\n type: typeof IFRAME_RESIZE_MESSAGE_TYPE;\n /**\n * 目标 Element 的唯一 id。由商户 SDK 经 iframe src 的 `&elementId=` 下发,child 原样\n * 回传,供父窗口在同一页面多个 iframe 间按句柄分发。\n */\n elementId: string;\n /** 内容高度(CSS px,已 `Math.ceil`;有限且非负)。 */\n height: number;\n}\n\n/**\n * 是否为合法的 {@link IframeResizeMessage}。\n *\n * 校验:非空对象 + type 字面量匹配 + elementId 为非空字符串 + height 为有限非负数。\n *\n * @param data - 任意来源数据(通常是 `MessageEvent.data`)。\n * @returns data 是否为合法的 iframe-resize 消息。\n */\nexport function isIframeResizeMessage(data: unknown): data is IframeResizeMessage {\n if (typeof data !== 'object' || data === null) {\n return false;\n }\n const msg = data as Partial<IframeResizeMessage>;\n return (\n msg.type === IFRAME_RESIZE_MESSAGE_TYPE &&\n typeof msg.elementId === 'string' &&\n msg.elementId.length > 0 &&\n typeof msg.height === 'number' &&\n Number.isFinite(msg.height) &&\n msg.height >= 0\n );\n}\n","/**\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"],"mappings":"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","names":["makeError","genId","makeError","genId","makeError"]}