@sunbay/checkout-sdk-protocol 0.0.0-stage → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +189 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +488 -0
- package/dist/index.d.ts +488 -0
- package/dist/index.mjs +153 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +31 -5
- package/README.md +0 -3
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 协议公开类型(真源)。
|
|
3
|
+
*
|
|
4
|
+
* 这些类型对外公开,供商户侧 SDK(transport/CheckoutInstance/Element)与
|
|
5
|
+
* checkout 托管侧同步副本共同引用。字段与 checkout 后端现有类型对齐,但
|
|
6
|
+
* 对外视图(PublicSessionView)**不含** rsaPublicKey 等敏感/内部字段。
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 交易状态(直接复用 checkout 后端 TxnStatus,不转义)。
|
|
10
|
+
* - `I` = 未支付 / 已创建(非终态)
|
|
11
|
+
* - `S` = 成功(终态)
|
|
12
|
+
* - `F` = 失败(终态)
|
|
13
|
+
* - `R` = 需跳转 / 处理中(非终态)
|
|
14
|
+
*/
|
|
15
|
+
type TxnStatus = 'I' | 'S' | 'F' | 'R';
|
|
16
|
+
/**
|
|
17
|
+
* 地址信息(对齐 checkout 后端 AddressInfo 字段)。
|
|
18
|
+
* 非敏感数据,允许经公共协议传输。
|
|
19
|
+
*/
|
|
20
|
+
interface AddressInfo {
|
|
21
|
+
country: string;
|
|
22
|
+
state?: string;
|
|
23
|
+
city: string;
|
|
24
|
+
line1: string;
|
|
25
|
+
line2?: string;
|
|
26
|
+
postalCode: string;
|
|
27
|
+
district?: string;
|
|
28
|
+
firstName?: string;
|
|
29
|
+
lastName?: string;
|
|
30
|
+
recipientName?: string;
|
|
31
|
+
phone?: string;
|
|
32
|
+
}
|
|
33
|
+
/** 首期支持的支付方式。 */
|
|
34
|
+
type PaymentMethod = 'card' | 'applePay' | 'googlePay';
|
|
35
|
+
/**
|
|
36
|
+
* 公开支付方式视图(对外只暴露 method/type/name/logoUrl,不含内部配置)。
|
|
37
|
+
*/
|
|
38
|
+
interface PublicPaymentMethod {
|
|
39
|
+
method: string;
|
|
40
|
+
type: string;
|
|
41
|
+
name: string;
|
|
42
|
+
logoUrl: string;
|
|
43
|
+
}
|
|
44
|
+
/** 公开商品行。 */
|
|
45
|
+
interface PublicProductLine {
|
|
46
|
+
amount: number;
|
|
47
|
+
name: string;
|
|
48
|
+
num: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 公开 Session 视图:金额 / 币种 / 商品 / collect 标记 / 可用支付方式 / 商户信息。
|
|
52
|
+
* **不含** rsaPublicKey 等敏感或内部字段。
|
|
53
|
+
*/
|
|
54
|
+
interface PublicSessionView {
|
|
55
|
+
sessionId: string;
|
|
56
|
+
sessionType: string;
|
|
57
|
+
currency: string;
|
|
58
|
+
totalAmount: number;
|
|
59
|
+
productList: PublicProductLine[];
|
|
60
|
+
taxAmount: number;
|
|
61
|
+
productAmount: number;
|
|
62
|
+
description: string;
|
|
63
|
+
expiresAt: string;
|
|
64
|
+
collectBillingAddress: boolean;
|
|
65
|
+
collectShippingAddress: boolean;
|
|
66
|
+
paymentMethods: PublicPaymentMethod[];
|
|
67
|
+
merchantName: string;
|
|
68
|
+
merchantLogoUrl: string;
|
|
69
|
+
merchantReturnUrl?: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 支付结果四态(spec 8.2)。
|
|
73
|
+
* `pending` 非终态,消费者不得只接受首条结果。
|
|
74
|
+
*/
|
|
75
|
+
type PaymentOutcome = {
|
|
76
|
+
status: 'succeeded';
|
|
77
|
+
operationId: string;
|
|
78
|
+
txnId: string;
|
|
79
|
+
} | {
|
|
80
|
+
status: 'failed';
|
|
81
|
+
operationId: string;
|
|
82
|
+
txnId?: string;
|
|
83
|
+
error: CheckoutError;
|
|
84
|
+
} | {
|
|
85
|
+
status: 'canceled';
|
|
86
|
+
operationId: string;
|
|
87
|
+
} | {
|
|
88
|
+
status: 'pending';
|
|
89
|
+
operationId: string;
|
|
90
|
+
txnId?: string;
|
|
91
|
+
};
|
|
92
|
+
/** 错误分类(spec 5.4)。 */
|
|
93
|
+
type CheckoutErrorCategory = 'configuration' | 'validation' | 'network' | 'runtime' | 'payment';
|
|
94
|
+
/**
|
|
95
|
+
* 结构化错误(spec 5.4)。
|
|
96
|
+
*/
|
|
97
|
+
interface CheckoutError {
|
|
98
|
+
code: string;
|
|
99
|
+
/** 展示用消息。 */
|
|
100
|
+
message: string;
|
|
101
|
+
category: CheckoutErrorCategory;
|
|
102
|
+
errorId: string;
|
|
103
|
+
elementId?: string;
|
|
104
|
+
field?: string;
|
|
105
|
+
traceId?: string;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 外观定制(appearance)。首期为开放映射,交由托管侧解释。
|
|
109
|
+
*/
|
|
110
|
+
interface Appearance {
|
|
111
|
+
theme?: string;
|
|
112
|
+
variables?: Record<string, string>;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 支付方式过滤配置(spec 9:只做减法/排序,禁止前端增开)。
|
|
116
|
+
*/
|
|
117
|
+
interface PaymentMethodFilter {
|
|
118
|
+
/** 要隐藏的支付方式(method 标识)。 */
|
|
119
|
+
hide?: string[];
|
|
120
|
+
/** 展示顺序(method 标识)。 */
|
|
121
|
+
order?: string[];
|
|
122
|
+
}
|
|
123
|
+
/** 可挂载 Element 类型(spec 6)。 */
|
|
124
|
+
type ElementType = 'card' | 'cardNumber' | 'cardExpiry' | 'cardCvc' | 'address' | 'expressCheckout';
|
|
125
|
+
/** card Element 配置(spec 6)。 */
|
|
126
|
+
interface CardElementOptions {
|
|
127
|
+
showBillingAddress?: boolean;
|
|
128
|
+
showShippingAddress?: boolean;
|
|
129
|
+
showPostalCode?: boolean;
|
|
130
|
+
appearance?: Appearance;
|
|
131
|
+
}
|
|
132
|
+
/** 拆分卡字段配置(spec 6)。 */
|
|
133
|
+
interface CardFieldElementOptions {
|
|
134
|
+
appearance?: Appearance;
|
|
135
|
+
}
|
|
136
|
+
/** address Element 配置(spec 6)。 */
|
|
137
|
+
interface AddressElementOptions {
|
|
138
|
+
mode: 'billing' | 'shipping';
|
|
139
|
+
appearance?: Appearance;
|
|
140
|
+
}
|
|
141
|
+
/** expressCheckout Element 配置(spec 6)。 */
|
|
142
|
+
interface ExpressCheckoutElementOptions {
|
|
143
|
+
/**
|
|
144
|
+
* 各钱包开关(商户显式控制):
|
|
145
|
+
* - `false`:强制隐藏该钱包;
|
|
146
|
+
* - `true` 或缺省:自动模式,按后端业务允许 ∩ 环境可用展示。
|
|
147
|
+
*/
|
|
148
|
+
paymentMethods?: {
|
|
149
|
+
applePay?: boolean;
|
|
150
|
+
googlePay?: boolean;
|
|
151
|
+
};
|
|
152
|
+
/** 布局约束(首期仅最大列数)。 */
|
|
153
|
+
layout?: {
|
|
154
|
+
maxColumns?: number;
|
|
155
|
+
};
|
|
156
|
+
/** 按钮主题(按钱包区分)。 */
|
|
157
|
+
buttonTheme?: {
|
|
158
|
+
applePay?: string;
|
|
159
|
+
googlePay?: string;
|
|
160
|
+
};
|
|
161
|
+
/** 按钮高度(像素)。 */
|
|
162
|
+
buttonHeight?: number;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* express 可用钱包视图(spec 6)。
|
|
166
|
+
* 表示各钱包在当前环境下是否可用,用于 express 可用性事件载荷。
|
|
167
|
+
*/
|
|
168
|
+
interface AvailableWallets {
|
|
169
|
+
applePay: boolean;
|
|
170
|
+
googlePay: boolean;
|
|
171
|
+
}
|
|
172
|
+
/** Element 配置联合(按 type 区分)。 */
|
|
173
|
+
type ElementOptions = CardElementOptions | CardFieldElementOptions | AddressElementOptions | ExpressCheckoutElementOptions;
|
|
174
|
+
/** Element 运行状态机(spec 6)。 */
|
|
175
|
+
type ElementStatus = 'created' | 'loading' | 'ready' | 'destroyed' | 'loadError';
|
|
176
|
+
/** action 载荷(spec 8.3;qrCode/showContent 首期预留)。 */
|
|
177
|
+
interface PaymentAction {
|
|
178
|
+
type: 'qrCode' | 'redirect' | 'showContent';
|
|
179
|
+
redirectUrl?: string;
|
|
180
|
+
qrData?: string;
|
|
181
|
+
content?: string;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 信封与握手契约(spec 5.1,真源)。
|
|
186
|
+
*
|
|
187
|
+
* 协议名 `sunbay-checkout`,`major = 1`。与现有 Native Bridge `{v:1,type,...}`
|
|
188
|
+
* 是不同协议,数字相同不代表兼容。
|
|
189
|
+
*
|
|
190
|
+
* `encodeEnvelope`/`decodeEnvelope`:JSON 序列化 + 最小运行时结构校验
|
|
191
|
+
* (校验 protocol/major/kind/必填字段),非法抛错。解码器**不**校验 request.name
|
|
192
|
+
* 是否为已知 CommandName —— 未知 name 仍解为 kind==='request',由调用方拒绝。
|
|
193
|
+
*/
|
|
194
|
+
|
|
195
|
+
/** 协议名常量。 */
|
|
196
|
+
declare const PROTOCOL_NAME: "sunbay-checkout";
|
|
197
|
+
/** 当前主版本。 */
|
|
198
|
+
declare const PROTOCOL_MAJOR: 1;
|
|
199
|
+
/**
|
|
200
|
+
* 握手消息(spec 5.1)。main iframe 加载后先广播,此时尚无 checkoutId。
|
|
201
|
+
*/
|
|
202
|
+
type RuntimeHello = {
|
|
203
|
+
protocol: 'sunbay-checkout';
|
|
204
|
+
kind: 'hello';
|
|
205
|
+
runtimeId: string;
|
|
206
|
+
supportedMajorVersions: number[];
|
|
207
|
+
/** 首期:['card','splitCard','address','express','paymentMethodFilter']。 */
|
|
208
|
+
capabilities: string[];
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* 初始化后的公共信封(spec 5.1)。
|
|
212
|
+
*/
|
|
213
|
+
type Envelope = {
|
|
214
|
+
protocol: 'sunbay-checkout';
|
|
215
|
+
major: 1;
|
|
216
|
+
runtimeId: string;
|
|
217
|
+
checkoutId: string;
|
|
218
|
+
elementId?: string;
|
|
219
|
+
} & ({
|
|
220
|
+
kind: 'request';
|
|
221
|
+
requestId: string;
|
|
222
|
+
name: string;
|
|
223
|
+
payload: unknown;
|
|
224
|
+
} | {
|
|
225
|
+
kind: 'response';
|
|
226
|
+
requestId: string;
|
|
227
|
+
ok: true;
|
|
228
|
+
data: unknown;
|
|
229
|
+
} | {
|
|
230
|
+
kind: 'response';
|
|
231
|
+
requestId: string;
|
|
232
|
+
ok: false;
|
|
233
|
+
error: CheckoutError;
|
|
234
|
+
} | {
|
|
235
|
+
kind: 'event';
|
|
236
|
+
eventId: string;
|
|
237
|
+
name: string;
|
|
238
|
+
operationId?: string;
|
|
239
|
+
payload: unknown;
|
|
240
|
+
});
|
|
241
|
+
/** 协议校验失败错误。 */
|
|
242
|
+
declare class EnvelopeDecodeError extends Error {
|
|
243
|
+
constructor(message: string);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* 将信封序列化为线缆字符串(JSON)。
|
|
247
|
+
*
|
|
248
|
+
* @param envelope 待发送的信封
|
|
249
|
+
* @returns JSON 字符串
|
|
250
|
+
* @example
|
|
251
|
+
* const wire = encodeEnvelope({ protocol: 'sunbay-checkout', major: 1, ... });
|
|
252
|
+
*/
|
|
253
|
+
declare function encodeEnvelope(envelope: Envelope): string;
|
|
254
|
+
/**
|
|
255
|
+
* 解析并最小结构校验信封。非法(缺 protocol、错 major、非法 kind、缺必填字段、
|
|
256
|
+
* 非 JSON)一律抛 {@link EnvelopeDecodeError}。
|
|
257
|
+
*
|
|
258
|
+
* 注意:解码器不校验 request.name 是否为已知命令 —— 未知 name 仍解为
|
|
259
|
+
* kind==='request',交由调用方(如 Orchestrator)拒绝。
|
|
260
|
+
*
|
|
261
|
+
* @param wire 线缆字符串
|
|
262
|
+
* @returns 已校验结构的信封
|
|
263
|
+
* @example
|
|
264
|
+
* const env = decodeEnvelope(wire);
|
|
265
|
+
* if (env.kind === 'request') { ... }
|
|
266
|
+
*/
|
|
267
|
+
declare function decodeEnvelope(wire: string): Envelope;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* 协议错误码常量(单一真源,spec 5.4)。
|
|
271
|
+
*
|
|
272
|
+
* 不提供可能误导重复付款的通用 `retryable` 布尔。
|
|
273
|
+
* 仅保留实际会被产出的错误码;新增码须同时有产出点。
|
|
274
|
+
*/
|
|
275
|
+
declare const ErrorCode: {
|
|
276
|
+
readonly INVALID_OPTIONS: "INVALID_OPTIONS";
|
|
277
|
+
readonly ELEMENT_CONFLICT: "ELEMENT_CONFLICT";
|
|
278
|
+
readonly INSTANCE_DESTROYED: "INSTANCE_DESTROYED";
|
|
279
|
+
readonly CHECKOUT_BUSY: "CHECKOUT_BUSY";
|
|
280
|
+
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
281
|
+
readonly SESSION_LOAD_FAILED: "SESSION_LOAD_FAILED";
|
|
282
|
+
readonly RUNTIME_LOAD_FAILED: "RUNTIME_LOAD_FAILED";
|
|
283
|
+
readonly PROTOCOL_UNSUPPORTED: "PROTOCOL_UNSUPPORTED";
|
|
284
|
+
readonly REQUEST_TIMEOUT: "REQUEST_TIMEOUT";
|
|
285
|
+
readonly PAYMENT_FLOW_ERROR: "PAYMENT_FLOW_ERROR";
|
|
286
|
+
readonly UNSUPPORTED_CAPABILITY: "UNSUPPORTED_CAPABILITY";
|
|
287
|
+
};
|
|
288
|
+
/** 错误码字面量联合类型。 */
|
|
289
|
+
type ErrorCodeValue = (typeof ErrorCode)[keyof typeof ErrorCode];
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* 命令契约(商户 SDK → main,spec 5.2 表,真源)。
|
|
293
|
+
*
|
|
294
|
+
* 每命令的请求/响应类型在此定义;跨通道只传 JSON 可序列化数据。
|
|
295
|
+
*/
|
|
296
|
+
|
|
297
|
+
/** 全部命令名(spec 5.2)。 */
|
|
298
|
+
type CommandName = 'initialize' | 'createElement' | 'updateElement' | 'destroyElement' | 'focus' | 'blur' | 'clear' | 'updateCheckout' | 'getSession' | 'refresh' | 'getPaymentStatus' | 'confirm' | 'destroy';
|
|
299
|
+
/** 已知命令名集合(供调用方校验 request.name 是否受支持)。 */
|
|
300
|
+
declare const COMMAND_NAMES: readonly CommandName[];
|
|
301
|
+
/** 判断字符串是否为已知命令名。 */
|
|
302
|
+
declare function isCommandName(name: string): name is CommandName;
|
|
303
|
+
interface InitializeReq {
|
|
304
|
+
sessionId: string;
|
|
305
|
+
locale?: string;
|
|
306
|
+
appearance?: Appearance;
|
|
307
|
+
paymentMethods?: PaymentMethodFilter;
|
|
308
|
+
}
|
|
309
|
+
interface InitializeResp {
|
|
310
|
+
capabilities: string[];
|
|
311
|
+
session: PublicSessionView;
|
|
312
|
+
}
|
|
313
|
+
interface CreateElementReq {
|
|
314
|
+
elementId: string;
|
|
315
|
+
type: ElementType;
|
|
316
|
+
options?: ElementOptions;
|
|
317
|
+
}
|
|
318
|
+
interface CreateElementResp {
|
|
319
|
+
elementId: string;
|
|
320
|
+
registered: true;
|
|
321
|
+
}
|
|
322
|
+
interface UpdateElementReq {
|
|
323
|
+
elementId: string;
|
|
324
|
+
patch: Partial<ElementOptions>;
|
|
325
|
+
}
|
|
326
|
+
interface UpdateElementResp {
|
|
327
|
+
elementId: string;
|
|
328
|
+
options: ElementOptions;
|
|
329
|
+
}
|
|
330
|
+
interface DestroyElementReq {
|
|
331
|
+
elementId: string;
|
|
332
|
+
}
|
|
333
|
+
interface DestroyElementResp {
|
|
334
|
+
elementId: string;
|
|
335
|
+
released: true;
|
|
336
|
+
}
|
|
337
|
+
interface ElementActionReq {
|
|
338
|
+
elementId: string;
|
|
339
|
+
}
|
|
340
|
+
interface ElementActionResp {
|
|
341
|
+
elementId: string;
|
|
342
|
+
done: true;
|
|
343
|
+
}
|
|
344
|
+
interface UpdateCheckoutReq {
|
|
345
|
+
locale?: string;
|
|
346
|
+
appearance?: Appearance;
|
|
347
|
+
}
|
|
348
|
+
interface UpdateCheckoutResp {
|
|
349
|
+
updated: true;
|
|
350
|
+
}
|
|
351
|
+
type GetSessionReq = Record<string, never>;
|
|
352
|
+
type GetSessionResp = PublicSessionView;
|
|
353
|
+
type GetPaymentStatusReq = Record<string, never>;
|
|
354
|
+
interface GetPaymentStatusResp {
|
|
355
|
+
txnStatus: TxnStatus;
|
|
356
|
+
txnId?: string;
|
|
357
|
+
}
|
|
358
|
+
interface ConfirmReq {
|
|
359
|
+
operationId: string;
|
|
360
|
+
billingAddress?: AddressInfo;
|
|
361
|
+
shippingAddress?: AddressInfo;
|
|
362
|
+
customerName?: string;
|
|
363
|
+
email?: string;
|
|
364
|
+
returnUrl?: string;
|
|
365
|
+
}
|
|
366
|
+
interface ConfirmResp {
|
|
367
|
+
accepted: true;
|
|
368
|
+
}
|
|
369
|
+
type DestroyReq = Record<string, never>;
|
|
370
|
+
interface DestroyResp {
|
|
371
|
+
released: true;
|
|
372
|
+
}
|
|
373
|
+
/** 命令名 → 请求载荷映射。 */
|
|
374
|
+
interface CommandRequestMap {
|
|
375
|
+
initialize: InitializeReq;
|
|
376
|
+
createElement: CreateElementReq;
|
|
377
|
+
updateElement: UpdateElementReq;
|
|
378
|
+
destroyElement: DestroyElementReq;
|
|
379
|
+
focus: ElementActionReq;
|
|
380
|
+
blur: ElementActionReq;
|
|
381
|
+
clear: ElementActionReq;
|
|
382
|
+
updateCheckout: UpdateCheckoutReq;
|
|
383
|
+
getSession: GetSessionReq;
|
|
384
|
+
refresh: GetSessionReq;
|
|
385
|
+
getPaymentStatus: GetPaymentStatusReq;
|
|
386
|
+
confirm: ConfirmReq;
|
|
387
|
+
destroy: DestroyReq;
|
|
388
|
+
}
|
|
389
|
+
/** 命令名 → 成功响应载荷映射。 */
|
|
390
|
+
interface CommandResponseMap {
|
|
391
|
+
initialize: InitializeResp;
|
|
392
|
+
createElement: CreateElementResp;
|
|
393
|
+
updateElement: UpdateElementResp;
|
|
394
|
+
destroyElement: DestroyElementResp;
|
|
395
|
+
focus: ElementActionResp;
|
|
396
|
+
blur: ElementActionResp;
|
|
397
|
+
clear: ElementActionResp;
|
|
398
|
+
updateCheckout: UpdateCheckoutResp;
|
|
399
|
+
getSession: GetSessionResp;
|
|
400
|
+
refresh: GetSessionResp;
|
|
401
|
+
getPaymentStatus: GetPaymentStatusResp;
|
|
402
|
+
confirm: ConfirmResp;
|
|
403
|
+
destroy: DestroyResp;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* 事件契约(main → 商户 SDK,spec 5.3 表,真源)。
|
|
408
|
+
*
|
|
409
|
+
* 公开事件仅暴露 empty/complete/brand/error 等允许状态,**绝不含卡值**,
|
|
410
|
+
* 不提供卡数据 getValue。`on` 注册后不重放历史事件。
|
|
411
|
+
*/
|
|
412
|
+
|
|
413
|
+
/** 全部事件名(spec 5.3)。 */
|
|
414
|
+
type EventName = 'ready' | 'change' | 'focus' | 'blur' | 'loadError' | 'elementReady' | 'availablePaymentMethodsChange' | 'expressAvailabilityChange' | 'paymentStateChange' | 'paymentResult' | 'error';
|
|
415
|
+
/** 已知事件名集合。 */
|
|
416
|
+
declare const EVENT_NAMES: readonly EventName[];
|
|
417
|
+
/** 判断字符串是否为已知事件名。 */
|
|
418
|
+
declare function isEventName(name: string): name is EventName;
|
|
419
|
+
interface ReadyEvent {
|
|
420
|
+
elementId: string;
|
|
421
|
+
}
|
|
422
|
+
interface ElementReadyEvent {
|
|
423
|
+
elementId: string;
|
|
424
|
+
/** 仅 expressCheckout Element ready 时携带:各钱包可用性。 */
|
|
425
|
+
availableWallets?: AvailableWallets;
|
|
426
|
+
}
|
|
427
|
+
interface ChangeEvent {
|
|
428
|
+
elementId: string;
|
|
429
|
+
empty: boolean;
|
|
430
|
+
complete: boolean;
|
|
431
|
+
error?: CheckoutError;
|
|
432
|
+
/** 仅卡号字段允许携带 brand;绝不含卡值。 */
|
|
433
|
+
brand?: string;
|
|
434
|
+
}
|
|
435
|
+
interface FocusEvent {
|
|
436
|
+
elementId: string;
|
|
437
|
+
}
|
|
438
|
+
interface BlurEvent {
|
|
439
|
+
elementId: string;
|
|
440
|
+
}
|
|
441
|
+
interface LoadErrorEvent {
|
|
442
|
+
elementId: string;
|
|
443
|
+
error: CheckoutError;
|
|
444
|
+
}
|
|
445
|
+
interface AvailablePaymentMethodsChangeEvent {
|
|
446
|
+
availablePaymentMethods: PublicPaymentMethod[];
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* express 可用钱包变化事件(spec 6)。
|
|
450
|
+
* 专用于 expressCheckout:以 AvailableWallets 表达各钱包可用性,
|
|
451
|
+
* 与 AvailablePaymentMethodsChangeEvent(PublicPaymentMethod[])并存,互不破坏。
|
|
452
|
+
*/
|
|
453
|
+
interface ExpressAvailabilityChangeEvent {
|
|
454
|
+
availableWallets: AvailableWallets;
|
|
455
|
+
}
|
|
456
|
+
interface PaymentStateChangeEvent {
|
|
457
|
+
operationId: string;
|
|
458
|
+
method: PaymentMethod;
|
|
459
|
+
/** 过程状态;requiresAction 时携带 action 信息。 */
|
|
460
|
+
state: string;
|
|
461
|
+
action?: PaymentAction;
|
|
462
|
+
}
|
|
463
|
+
interface PaymentResultEvent {
|
|
464
|
+
operationId: string;
|
|
465
|
+
method: PaymentMethod;
|
|
466
|
+
outcome: PaymentOutcome;
|
|
467
|
+
}
|
|
468
|
+
interface ErrorEvent {
|
|
469
|
+
/** 异步主动流程错误;可含 operationId(如已创建)。 */
|
|
470
|
+
operationId?: string;
|
|
471
|
+
error: CheckoutError;
|
|
472
|
+
}
|
|
473
|
+
/** 事件名 → 载荷映射。 */
|
|
474
|
+
interface EventPayloadMap {
|
|
475
|
+
ready: ReadyEvent;
|
|
476
|
+
change: ChangeEvent;
|
|
477
|
+
focus: FocusEvent;
|
|
478
|
+
blur: BlurEvent;
|
|
479
|
+
loadError: LoadErrorEvent;
|
|
480
|
+
elementReady: ElementReadyEvent;
|
|
481
|
+
availablePaymentMethodsChange: AvailablePaymentMethodsChangeEvent;
|
|
482
|
+
expressAvailabilityChange: ExpressAvailabilityChangeEvent;
|
|
483
|
+
paymentStateChange: PaymentStateChangeEvent;
|
|
484
|
+
paymentResult: PaymentResultEvent;
|
|
485
|
+
error: ErrorEvent;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export { type AddressElementOptions, type AddressInfo, type Appearance, type AvailablePaymentMethodsChangeEvent, type AvailableWallets, type BlurEvent, COMMAND_NAMES, type CardElementOptions, type CardFieldElementOptions, type ChangeEvent, type CheckoutError, type CheckoutErrorCategory, type CommandName, type CommandRequestMap, type CommandResponseMap, type ConfirmReq, type ConfirmResp, type CreateElementReq, type CreateElementResp, type DestroyElementReq, type DestroyElementResp, type DestroyReq, type DestroyResp, EVENT_NAMES, type ElementActionReq, type ElementActionResp, type ElementOptions, type ElementReadyEvent, type ElementStatus, type ElementType, type Envelope, EnvelopeDecodeError, ErrorCode, type ErrorCodeValue, type ErrorEvent, type EventName, type EventPayloadMap, type ExpressAvailabilityChangeEvent, type ExpressCheckoutElementOptions, type FocusEvent, type GetPaymentStatusReq, type GetPaymentStatusResp, type GetSessionReq, type GetSessionResp, type InitializeReq, type InitializeResp, type LoadErrorEvent, PROTOCOL_MAJOR, PROTOCOL_NAME, type PaymentAction, type PaymentMethod, type PaymentMethodFilter, type PaymentOutcome, type PaymentResultEvent, type PaymentStateChangeEvent, type PublicPaymentMethod, type PublicProductLine, type PublicSessionView, type ReadyEvent, type RuntimeHello, type TxnStatus, type UpdateCheckoutReq, type UpdateCheckoutResp, type UpdateElementReq, type UpdateElementResp, decodeEnvelope, encodeEnvelope, isCommandName, isEventName };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// src/envelope.ts
|
|
2
|
+
var PROTOCOL_NAME = "sunbay-checkout";
|
|
3
|
+
var PROTOCOL_MAJOR = 1;
|
|
4
|
+
var ENVELOPE_KINDS = ["request", "response", "event"];
|
|
5
|
+
var EnvelopeDecodeError = class extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "EnvelopeDecodeError";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === "object" && value !== null;
|
|
13
|
+
}
|
|
14
|
+
function assertString(obj, key) {
|
|
15
|
+
if (typeof obj[key] !== "string") {
|
|
16
|
+
throw new EnvelopeDecodeError(`Envelope field "${key}" is missing or not a string`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function encodeEnvelope(envelope) {
|
|
20
|
+
return JSON.stringify(envelope);
|
|
21
|
+
}
|
|
22
|
+
function decodeEnvelope(wire) {
|
|
23
|
+
let parsed;
|
|
24
|
+
try {
|
|
25
|
+
parsed = JSON.parse(wire);
|
|
26
|
+
} catch {
|
|
27
|
+
throw new EnvelopeDecodeError("Envelope is not valid JSON");
|
|
28
|
+
}
|
|
29
|
+
if (!isRecord(parsed)) {
|
|
30
|
+
throw new EnvelopeDecodeError("Envelope must be an object");
|
|
31
|
+
}
|
|
32
|
+
if (parsed.protocol !== PROTOCOL_NAME) {
|
|
33
|
+
throw new EnvelopeDecodeError(
|
|
34
|
+
`protocol must be "${PROTOCOL_NAME}", received ${JSON.stringify(parsed.protocol)}`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (parsed.major !== PROTOCOL_MAJOR) {
|
|
38
|
+
throw new EnvelopeDecodeError(
|
|
39
|
+
`major must be ${PROTOCOL_MAJOR}, received ${JSON.stringify(parsed.major)}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
assertString(parsed, "runtimeId");
|
|
43
|
+
assertString(parsed, "checkoutId");
|
|
44
|
+
if (parsed.elementId !== void 0 && typeof parsed.elementId !== "string") {
|
|
45
|
+
throw new EnvelopeDecodeError("elementId must be a string when present");
|
|
46
|
+
}
|
|
47
|
+
const kind = parsed.kind;
|
|
48
|
+
if (typeof kind !== "string" || !ENVELOPE_KINDS.includes(kind)) {
|
|
49
|
+
throw new EnvelopeDecodeError(`Invalid kind: ${JSON.stringify(kind)}`);
|
|
50
|
+
}
|
|
51
|
+
switch (kind) {
|
|
52
|
+
case "request": {
|
|
53
|
+
assertString(parsed, "requestId");
|
|
54
|
+
assertString(parsed, "name");
|
|
55
|
+
if (!("payload" in parsed)) {
|
|
56
|
+
throw new EnvelopeDecodeError("request envelope is missing payload");
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case "response": {
|
|
61
|
+
assertString(parsed, "requestId");
|
|
62
|
+
if (typeof parsed.ok !== "boolean") {
|
|
63
|
+
throw new EnvelopeDecodeError("response envelope is missing boolean ok");
|
|
64
|
+
}
|
|
65
|
+
if (parsed.ok === true) {
|
|
66
|
+
if (!("data" in parsed)) {
|
|
67
|
+
throw new EnvelopeDecodeError("successful response is missing data");
|
|
68
|
+
}
|
|
69
|
+
} else if (!isRecord(parsed.error)) {
|
|
70
|
+
throw new EnvelopeDecodeError("failed response is missing error object");
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
case "event": {
|
|
75
|
+
assertString(parsed, "eventId");
|
|
76
|
+
assertString(parsed, "name");
|
|
77
|
+
if (!("payload" in parsed)) {
|
|
78
|
+
throw new EnvelopeDecodeError("event envelope is missing payload");
|
|
79
|
+
}
|
|
80
|
+
if ("operationId" in parsed && parsed.operationId !== void 0 && typeof parsed.operationId !== "string") {
|
|
81
|
+
throw new EnvelopeDecodeError("operationId must be a string when present");
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return parsed;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// src/errors.ts
|
|
90
|
+
var ErrorCode = {
|
|
91
|
+
INVALID_OPTIONS: "INVALID_OPTIONS",
|
|
92
|
+
ELEMENT_CONFLICT: "ELEMENT_CONFLICT",
|
|
93
|
+
INSTANCE_DESTROYED: "INSTANCE_DESTROYED",
|
|
94
|
+
CHECKOUT_BUSY: "CHECKOUT_BUSY",
|
|
95
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
96
|
+
SESSION_LOAD_FAILED: "SESSION_LOAD_FAILED",
|
|
97
|
+
RUNTIME_LOAD_FAILED: "RUNTIME_LOAD_FAILED",
|
|
98
|
+
PROTOCOL_UNSUPPORTED: "PROTOCOL_UNSUPPORTED",
|
|
99
|
+
REQUEST_TIMEOUT: "REQUEST_TIMEOUT",
|
|
100
|
+
PAYMENT_FLOW_ERROR: "PAYMENT_FLOW_ERROR",
|
|
101
|
+
UNSUPPORTED_CAPABILITY: "UNSUPPORTED_CAPABILITY"
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// src/commands.ts
|
|
105
|
+
var COMMAND_NAMES = [
|
|
106
|
+
"initialize",
|
|
107
|
+
"createElement",
|
|
108
|
+
"updateElement",
|
|
109
|
+
"destroyElement",
|
|
110
|
+
"focus",
|
|
111
|
+
"blur",
|
|
112
|
+
"clear",
|
|
113
|
+
"updateCheckout",
|
|
114
|
+
"getSession",
|
|
115
|
+
"refresh",
|
|
116
|
+
"getPaymentStatus",
|
|
117
|
+
"confirm",
|
|
118
|
+
"destroy"
|
|
119
|
+
];
|
|
120
|
+
function isCommandName(name) {
|
|
121
|
+
return COMMAND_NAMES.includes(name);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/events.ts
|
|
125
|
+
var EVENT_NAMES = [
|
|
126
|
+
"ready",
|
|
127
|
+
"change",
|
|
128
|
+
"focus",
|
|
129
|
+
"blur",
|
|
130
|
+
"loadError",
|
|
131
|
+
"elementReady",
|
|
132
|
+
"availablePaymentMethodsChange",
|
|
133
|
+
"expressAvailabilityChange",
|
|
134
|
+
"paymentStateChange",
|
|
135
|
+
"paymentResult",
|
|
136
|
+
"error"
|
|
137
|
+
];
|
|
138
|
+
function isEventName(name) {
|
|
139
|
+
return EVENT_NAMES.includes(name);
|
|
140
|
+
}
|
|
141
|
+
export {
|
|
142
|
+
COMMAND_NAMES,
|
|
143
|
+
EVENT_NAMES,
|
|
144
|
+
EnvelopeDecodeError,
|
|
145
|
+
ErrorCode,
|
|
146
|
+
PROTOCOL_MAJOR,
|
|
147
|
+
PROTOCOL_NAME,
|
|
148
|
+
decodeEnvelope,
|
|
149
|
+
encodeEnvelope,
|
|
150
|
+
isCommandName,
|
|
151
|
+
isEventName
|
|
152
|
+
};
|
|
153
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/envelope.ts","../src/errors.ts","../src/commands.ts","../src/events.ts"],"sourcesContent":["/**\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 PublicPaymentMethod,\n} from './types';\n\n/** 全部事件名(spec 5.3)。 */\nexport type EventName =\n | 'ready'\n | 'change'\n | 'focus'\n | 'blur'\n | 'loadError'\n | 'elementReady'\n | 'availablePaymentMethodsChange'\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 'availablePaymentMethodsChange',\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 事件 ---\nexport interface AvailablePaymentMethodsChangeEvent {\n availablePaymentMethods: PublicPaymentMethod[];\n}\n\n/**\n * express 可用钱包变化事件(spec 6)。\n * 专用于 expressCheckout:以 AvailableWallets 表达各钱包可用性,\n * 与 AvailablePaymentMethodsChangeEvent(PublicPaymentMethod[])并存,互不破坏。\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 availablePaymentMethodsChange: AvailablePaymentMethodsChangeEvent;\n expressAvailabilityChange: ExpressAvailabilityChangeEvent;\n paymentStateChange: PaymentStateChangeEvent;\n paymentResult: PaymentResultEvent;\n error: ErrorEvent;\n}\n"],"mappings":"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","names":[]}
|