@h-ai/payment 0.1.0-alpha.66 → 0.1.0-alpha.68

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,500 @@
1
+ import * as _h_ai_core from '@h-ai/core';
2
+ import { HaiResult } from '@h-ai/core';
3
+ import { z } from 'zod';
4
+
5
+ /**
6
+ * @h-ai/payment — 错误码 + 配置 Schema
7
+ *
8
+ * 支付模块的错误码、Zod 配置 Schema 与配置类型。
9
+ * @module payment-config
10
+ */
11
+
12
+ /**
13
+ * 微信公众号 JSAPI 配置 Schema;平台密钥按受信 serial 精确配置。
14
+ * @example
15
+ * ```ts
16
+ * import { WechatPayConfigSchema } from '@h-ai/payment'
17
+ * // 部署系统注入可信配置;Schema 校验形状,不证明密钥、证书或渠道账户可用。
18
+ * const platformKeys: unknown = JSON.parse(process.env.HAI_PAYMENT_WECHAT_PLATFORMKEYS ?? '{}')
19
+ * const parsed = WechatPayConfigSchema.safeParse({
20
+ * mchId: process.env.HAI_PAYMENT_WECHAT_MCHID,
21
+ * apiV3Key: process.env.HAI_PAYMENT_WECHAT_APIV3KEY,
22
+ * serialNo: process.env.HAI_PAYMENT_WECHAT_SERIALNO,
23
+ * privateKey: process.env.HAI_PAYMENT_WECHAT_PRIVATEKEY,
24
+ * platformKeys,
25
+ * appId: process.env.HAI_PAYMENT_WECHAT_APPID,
26
+ * })
27
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
28
+ * const config = parsed.data
29
+ * ```
30
+ */
31
+ declare const WechatPayConfigSchema: z.ZodObject<{
32
+ mchId: z.ZodString;
33
+ apiV3Key: z.ZodString;
34
+ serialNo: z.ZodString;
35
+ privateKey: z.ZodString;
36
+ platformKeys: z.ZodRecord<z.ZodString, z.ZodString>;
37
+ appId: z.ZodString;
38
+ }, z.core.$strip>;
39
+ /**
40
+ * 支付宝手机网站支付配置 Schema;returnUrl 为可信 HTTPS 导航地址。
41
+ * @example
42
+ * ```ts
43
+ * import { AlipayConfigSchema } from '@h-ai/payment'
44
+ * // 部署系统注入可信配置;Schema 校验形状,不证明密钥、证书或渠道账户可用。
45
+ * const parsed = AlipayConfigSchema.safeParse({
46
+ * appId: process.env.HAI_PAYMENT_ALIPAY_APPID,
47
+ * sellerId: process.env.HAI_PAYMENT_ALIPAY_SELLERID,
48
+ * privateKey: process.env.HAI_PAYMENT_ALIPAY_PRIVATEKEY,
49
+ * alipayPublicKey: process.env.HAI_PAYMENT_ALIPAY_ALIPAYPUBLICKEY,
50
+ * returnUrl: 'https://shop.example.com/payment/result',
51
+ * })
52
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
53
+ * const config = parsed.data
54
+ * ```
55
+ */
56
+ declare const AlipayConfigSchema: z.ZodObject<{
57
+ appId: z.ZodString;
58
+ sellerId: z.ZodString;
59
+ privateKey: z.ZodString;
60
+ alipayPublicKey: z.ZodString;
61
+ returnUrl: z.ZodURL;
62
+ signType: z.ZodDefault<z.ZodEnum<{
63
+ RSA2: "RSA2";
64
+ RSA: "RSA";
65
+ }>>;
66
+ sandbox: z.ZodDefault<z.ZodBoolean>;
67
+ }, z.core.$strip>;
68
+ /**
69
+ * Stripe 托管 Checkout 配置 Schema;初始化前要求共享 reldb 就绪。
70
+ * @example
71
+ * ```ts
72
+ * import { StripeConfigSchema } from '@h-ai/payment'
73
+ * // 部署系统注入可信配置;Schema 校验形状,不证明密钥、证书或渠道账户可用。
74
+ * const parsed = StripeConfigSchema.safeParse({
75
+ * secretKey: process.env.HAI_PAYMENT_STRIPE_SECRETKEY,
76
+ * webhookSecret: process.env.HAI_PAYMENT_STRIPE_WEBHOOKSECRET,
77
+ * successUrl: 'https://shop.example.com/payment/result?session={CHECKOUT_SESSION_ID}',
78
+ * })
79
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
80
+ * const config = parsed.data
81
+ * ```
82
+ */
83
+ declare const StripeConfigSchema: z.ZodObject<{
84
+ secretKey: z.ZodString;
85
+ webhookSecret: z.ZodString;
86
+ successUrl: z.ZodURL;
87
+ cancelUrl: z.ZodOptional<z.ZodURL>;
88
+ webhookToleranceSeconds: z.ZodDefault<z.ZodNumber>;
89
+ }, z.core.$strict>;
90
+ /**
91
+ * 支付模块配置 Schema;按配置注册渠道,密钥与运行时依赖由宿主提供。
92
+ * @example
93
+ * ```ts
94
+ * import { PaymentConfigSchema } from '@h-ai/payment'
95
+ * const parsed = PaymentConfigSchema.safeParse({ stripe: {
96
+ * secretKey: process.env.HAI_PAYMENT_STRIPE_SECRETKEY,
97
+ * webhookSecret: process.env.HAI_PAYMENT_STRIPE_WEBHOOKSECRET,
98
+ * successUrl: 'https://shop.example.com/payment/result?session={CHECKOUT_SESSION_ID}',
99
+ * } })
100
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
101
+ * const toleranceSeconds = parsed.data.stripe?.webhookToleranceSeconds
102
+ * ```
103
+ */
104
+ declare const PaymentConfigSchema: z.ZodObject<{
105
+ wechat: z.ZodOptional<z.ZodObject<{
106
+ mchId: z.ZodString;
107
+ apiV3Key: z.ZodString;
108
+ serialNo: z.ZodString;
109
+ privateKey: z.ZodString;
110
+ platformKeys: z.ZodRecord<z.ZodString, z.ZodString>;
111
+ appId: z.ZodString;
112
+ }, z.core.$strip>>;
113
+ alipay: z.ZodOptional<z.ZodObject<{
114
+ appId: z.ZodString;
115
+ sellerId: z.ZodString;
116
+ privateKey: z.ZodString;
117
+ alipayPublicKey: z.ZodString;
118
+ returnUrl: z.ZodURL;
119
+ signType: z.ZodDefault<z.ZodEnum<{
120
+ RSA2: "RSA2";
121
+ RSA: "RSA";
122
+ }>>;
123
+ sandbox: z.ZodDefault<z.ZodBoolean>;
124
+ }, z.core.$strip>>;
125
+ stripe: z.ZodOptional<z.ZodObject<{
126
+ secretKey: z.ZodString;
127
+ webhookSecret: z.ZodString;
128
+ successUrl: z.ZodURL;
129
+ cancelUrl: z.ZodOptional<z.ZodURL>;
130
+ webhookToleranceSeconds: z.ZodDefault<z.ZodNumber>;
131
+ }, z.core.$strict>>;
132
+ }, z.core.$strip>;
133
+ /**
134
+ * PaymentConfigSchema 解析后的配置类型;不能把未解析输入当作该类型。
135
+ * @example
136
+ * ```ts
137
+ * import { PaymentConfigSchema, type PaymentConfig } from '@h-ai/payment'
138
+ * const parsed = PaymentConfigSchema.safeParse({ stripe: {
139
+ * secretKey: process.env.HAI_PAYMENT_STRIPE_SECRETKEY,
140
+ * webhookSecret: process.env.HAI_PAYMENT_STRIPE_WEBHOOKSECRET,
141
+ * successUrl: 'https://shop.example.com/payment/result?session={CHECKOUT_SESSION_ID}',
142
+ * } })
143
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
144
+ * const config: PaymentConfig = parsed.data
145
+ * config.stripe?.webhookToleranceSeconds // 解析输出包含默认的签名容差。
146
+ * ```
147
+ */
148
+ type PaymentConfig = z.infer<typeof PaymentConfigSchema>;
149
+ /**
150
+ * 支付初始化输入;允许省略由 Schema 补齐的配置默认值。
151
+ * @example
152
+ * ```ts
153
+ * import { payment, type PaymentConfigInput } from '@h-ai/payment'
154
+ * async function initializePayment(input: PaymentConfigInput) {
155
+ * // 启用 Stripe 时,宿主已经先初始化共享 reldb 并检查成功。
156
+ * const result = await payment.init(input)
157
+ * if (!result.success) throw new Error(result.error.message)
158
+ * }
159
+ * ```
160
+ */
161
+ type PaymentConfigInput = z.input<typeof PaymentConfigSchema>;
162
+ /**
163
+ * 微信配置的解析输出类型;默认值已经由对应 Schema 补齐。
164
+ * @example
165
+ * ```ts
166
+ * import { WechatPayConfigSchema, type WechatPayConfig } from '@h-ai/payment'
167
+ * const platformKeys: unknown = JSON.parse(process.env.HAI_PAYMENT_WECHAT_PLATFORMKEYS ?? '{}')
168
+ * const parsed = WechatPayConfigSchema.safeParse({
169
+ * mchId: process.env.HAI_PAYMENT_WECHAT_MCHID,
170
+ * apiV3Key: process.env.HAI_PAYMENT_WECHAT_APIV3KEY,
171
+ * serialNo: process.env.HAI_PAYMENT_WECHAT_SERIALNO,
172
+ * privateKey: process.env.HAI_PAYMENT_WECHAT_PRIVATEKEY,
173
+ * platformKeys,
174
+ * appId: process.env.HAI_PAYMENT_WECHAT_APPID,
175
+ * })
176
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
177
+ * const config: WechatPayConfig = parsed.data
178
+ * ```
179
+ */
180
+ type WechatPayConfig = z.infer<typeof WechatPayConfigSchema>;
181
+ /**
182
+ * 支付宝配置的解析输出类型;默认值已经由对应 Schema 补齐。
183
+ * @example
184
+ * ```ts
185
+ * import { AlipayConfigSchema, type AlipayConfig } from '@h-ai/payment'
186
+ * const parsed = AlipayConfigSchema.safeParse({
187
+ * appId: process.env.HAI_PAYMENT_ALIPAY_APPID,
188
+ * sellerId: process.env.HAI_PAYMENT_ALIPAY_SELLERID,
189
+ * privateKey: process.env.HAI_PAYMENT_ALIPAY_PRIVATEKEY,
190
+ * alipayPublicKey: process.env.HAI_PAYMENT_ALIPAY_ALIPAYPUBLICKEY,
191
+ * returnUrl: 'https://shop.example.com/payment/result',
192
+ * })
193
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
194
+ * const config: AlipayConfig = parsed.data
195
+ * ```
196
+ */
197
+ type AlipayConfig = z.infer<typeof AlipayConfigSchema>;
198
+ /**
199
+ * Stripe配置的解析输出类型;默认值已经由对应 Schema 补齐。
200
+ * @example
201
+ * ```ts
202
+ * import { StripeConfigSchema, type StripeConfig } from '@h-ai/payment'
203
+ * const parsed = StripeConfigSchema.safeParse({
204
+ * secretKey: process.env.HAI_PAYMENT_STRIPE_SECRETKEY,
205
+ * webhookSecret: process.env.HAI_PAYMENT_STRIPE_WEBHOOKSECRET,
206
+ * successUrl: 'https://shop.example.com/payment/result?session={CHECKOUT_SESSION_ID}',
207
+ * })
208
+ * if (!parsed.success) throw new Error('Payment configuration is invalid')
209
+ * const config: StripeConfig = parsed.data
210
+ * ```
211
+ */
212
+ type StripeConfig = z.infer<typeof StripeConfigSchema>;
213
+
214
+ /**
215
+ * 支付错误码定义;调用方先判断 HaiResult.success,再按 code 分类处理。
216
+ * @example
217
+ * ```ts
218
+ * import { payment, HaiPaymentError } from '@h-ai/payment'
219
+ * const result = await payment.queryOrder('stripe', 'ORDER_1')
220
+ * if (!result.success && result.error.code === HaiPaymentError.NOT_INITIALIZED.code) {
221
+ * // 应用启动流程应检查 init 结果,不能把未就绪查询当成未付款。
222
+ * throw new Error(result.error.message)
223
+ * }
224
+ * ```
225
+ */
226
+ declare const HaiPaymentError: {
227
+ readonly CREATE_ORDER_FAILED: _h_ai_core.HaiErrorDef;
228
+ readonly QUERY_ORDER_FAILED: _h_ai_core.HaiErrorDef;
229
+ readonly REFUND_FAILED: _h_ai_core.HaiErrorDef;
230
+ readonly CLOSE_ORDER_FAILED: _h_ai_core.HaiErrorDef;
231
+ readonly NOT_INITIALIZED: _h_ai_core.HaiErrorDef;
232
+ readonly SIGN_FAILED: _h_ai_core.HaiErrorDef;
233
+ readonly PROVIDER_NOT_FOUND: _h_ai_core.HaiErrorDef;
234
+ readonly INVALID_AMOUNT: _h_ai_core.HaiErrorDef;
235
+ readonly UNSUPPORTED_TRADE_TYPE: _h_ai_core.HaiErrorDef;
236
+ readonly INVALID_INPUT: _h_ai_core.HaiErrorDef;
237
+ readonly NOTIFY_VERIFY_FAILED: _h_ai_core.HaiErrorDef;
238
+ readonly NOTIFY_PARSE_FAILED: _h_ai_core.HaiErrorDef;
239
+ readonly INVOKE_WEB_FAILED: _h_ai_core.HaiErrorDef;
240
+ readonly CONFIG_ERROR: _h_ai_core.HaiErrorDef;
241
+ };
242
+ /**
243
+ * 已实现的支付场景:微信 jsapi,支付宝/Stripe h5;各 Provider 不接受其它组合。
244
+ * @example
245
+ * ```ts
246
+ * import type { TradeType } from '@h-ai/payment'
247
+ * const wechatScene: TradeType = 'jsapi'
248
+ * const hostedPageScene: TradeType = 'h5'
249
+ * ```
250
+ */
251
+ type TradeType = 'jsapi' | 'h5';
252
+ /**
253
+ * 订单状态
254
+ * @example
255
+ * ```ts
256
+ * import type { OrderStatusValue } from '@h-ai/payment'
257
+ * function isPaid(status: OrderStatusValue): boolean {
258
+ * return status === 'paid' // pending、closed 和客户端跳转均不能证明付款。
259
+ * }
260
+ * ```
261
+ */
262
+ type OrderStatusValue = 'pending' | 'paid' | 'closed' | 'refunded' | 'failed';
263
+ /**
264
+ * 创建订单入参
265
+ * @example
266
+ * ```ts
267
+ * import type { CreateOrderInput } from '@h-ai/payment'
268
+ * const input: CreateOrderInput = {
269
+ * orderNo: 'ORDER_1', amount: 100, currency: 'CNY', description: '测试商品',
270
+ * tradeType: 'h5', notifyUrl: 'https://api.example.com/payment/notify/stripe',
271
+ * }
272
+ * // 微信改为 jsapi,并提供属于服务端 appId 的 openid;Stripe/支付宝使用 h5。
273
+ * ```
274
+ */
275
+ interface CreateOrderInput {
276
+ /** 商户订单号 */
277
+ orderNo: string;
278
+ /** 金额(分) */
279
+ amount: number;
280
+ /** 大写币种;微信/支付宝使用 CNY,Stripe 省略时使用 USD,必须与业务订单一致。 */
281
+ currency?: string;
282
+ /** 商品描述 */
283
+ description: string;
284
+ /** 支付场景:微信仅 jsapi,支付宝仅 h5,Stripe 仅 h5(托管 Checkout 页面)。 */
285
+ tradeType: TradeType;
286
+ /** 微信公众号 openid,微信 jsapi 必填;必须属于服务端配置的 appId。 */
287
+ userId?: string;
288
+ /** 异步通知 URL */
289
+ notifyUrl: string;
290
+ /** 附加数据(透传) */
291
+ metadata?: Record<string, string>;
292
+ }
293
+ /**
294
+ * 支付订单(返回给客户端调起支付)
295
+ * @example
296
+ * ```ts
297
+ * import { payment, type PaymentOrder } from '@h-ai/payment'
298
+ * // 应用已初始化 Stripe 与其共享 reldb;订单号由业务持久化生成。
299
+ * const created = await payment.createOrder('stripe', {
300
+ * orderNo: 'ORDER_1', amount: 100, currency: 'CNY', description: '测试商品',
301
+ * tradeType: 'h5', notifyUrl: 'https://api.example.com/payment/notify/stripe',
302
+ * })
303
+ * if (!created.success) throw new Error(created.error.message)
304
+ * const order: PaymentOrder = created.data
305
+ * order.clientParams // 经受信应用接口交给 @h-ai/payment/client,不含商户私钥。
306
+ * ```
307
+ */
308
+ interface PaymentOrder {
309
+ /** Provider 名称 */
310
+ provider: string;
311
+ /** 支付场景 */
312
+ tradeType: TradeType;
313
+ /** 微信 JSAPI 签名字段、支付宝 { gateway, fields } POST 参数或 Stripe { checkoutUrl };不含 HTML。 */
314
+ clientParams: Record<string, unknown>;
315
+ /** 预支付 ID(如微信 prepay_id) */
316
+ prepayId?: string;
317
+ }
318
+ /**
319
+ * 回调请求(原始 HTTP 数据)
320
+ * @example
321
+ * ```ts
322
+ * import type { PaymentNotifyRequest } from '@h-ai/payment'
323
+ * async function rawNotification(request: Request): Promise<PaymentNotifyRequest> {
324
+ * return { body: await request.text(), headers: Object.fromEntries(request.headers) }
325
+ * // 必须保留原始 body;不能先 JSON.parse 再序列化后验签。
326
+ * }
327
+ * ```
328
+ */
329
+ interface PaymentNotifyRequest {
330
+ /** 请求 body */
331
+ body: string;
332
+ /** 请求 headers */
333
+ headers: Record<string, string>;
334
+ }
335
+ /**
336
+ * 回调解析结果;验签与解析成功不等于已付款,履约前必须检查 status。
337
+ *
338
+ * 宿主需用共享持久存储核对订单、金额、币种并去重,阻止乱序事件倒退已完成状态。
339
+ * @example
340
+ * ```ts
341
+ * import { payment, type PaymentNotifyRequest } from '@h-ai/payment'
342
+ * async function verifyNotification(request: PaymentNotifyRequest) {
343
+ * const result = await payment.handleNotify('stripe', request)
344
+ * if (!result.success) return result
345
+ * if (result.data.status === 'paid') {
346
+ * // 宿主在共享业务事务中核对商户订单、金额、币种,并按 eventId 持久去重后履约。
347
+ * }
348
+ * return result // 验签成功的 pending 结果仍然不能发货。
349
+ * }
350
+ * ```
351
+ */
352
+ interface PaymentNotifyResult {
353
+ /** 商户订单号 */
354
+ orderNo: string;
355
+ /** 支付渠道交易号 */
356
+ transactionId: string;
357
+ /** 渠道通知事件 ID:微信 id、支付宝 notify_id、Stripe event.id;宿主按渠道/商户命名空间持久去重。 */
358
+ eventId: string;
359
+ /** 支付金额(分) */
360
+ amount: number;
361
+ /** ISO 4217 大写币种;支付宝为 CNY,宿主必须与订单核对。 */
362
+ currency: string;
363
+ /** 支付状态 */
364
+ status: OrderStatusValue;
365
+ /** 渠道支付时间;Stripe 为支付成功事件的创建时间,非本次通知接收时间。 */
366
+ paidAt?: Date;
367
+ /** 原始数据 */
368
+ raw?: Record<string, unknown>;
369
+ }
370
+ /**
371
+ * 订单状态查询结果
372
+ * @example
373
+ * ```ts
374
+ * import { payment, type OrderStatus } from '@h-ai/payment'
375
+ * const result = await payment.queryOrder('stripe', 'ORDER_1')
376
+ * if (!result.success) throw new Error(result.error.message)
377
+ * const status: OrderStatus = result.data
378
+ * const hasPaymentConfirmation = status.status === 'paid'
379
+ * ```
380
+ */
381
+ interface OrderStatus {
382
+ /** 商户订单号 */
383
+ orderNo: string;
384
+ /** 渠道交易号 */
385
+ transactionId?: string;
386
+ /** 订单状态 */
387
+ status: OrderStatusValue;
388
+ /** 金额(分) */
389
+ amount: number;
390
+ /** 支付时间 */
391
+ paidAt?: Date;
392
+ }
393
+ /**
394
+ * 退款入参
395
+ * @example
396
+ * ```ts
397
+ * import type { RefundInput } from '@h-ai/payment'
398
+ * const refund: RefundInput = {
399
+ * orderNo: 'ORDER_1', refundNo: 'REFUND_1', amount: 50, totalAmount: 100, reason: '部分退货',
400
+ * }
401
+ * // 同一业务退款重试复用 refundNo,不能变更金额;微信必须提供原订单总额。
402
+ * ```
403
+ */
404
+ interface RefundInput {
405
+ /** 商户订单号 */
406
+ orderNo: string;
407
+ /** 退款单号 */
408
+ refundNo: string;
409
+ /** 退款金额(分) */
410
+ amount: number;
411
+ /** 订单总金额(分),微信退款必填 */
412
+ totalAmount?: number;
413
+ /** 退款原因 */
414
+ reason?: string;
415
+ }
416
+ /**
417
+ * 退款结果
418
+ * @example
419
+ * ```ts
420
+ * import { payment, type RefundResult } from '@h-ai/payment'
421
+ * const result = await payment.refund('stripe', { orderNo: 'ORDER_1', refundNo: 'REFUND_1', amount: 50 })
422
+ * if (!result.success) throw new Error(result.error.message)
423
+ * const refund: RefundResult = result.data
424
+ * const confirmed = refund.status === 'success' // processing 仅表示仍待渠道确认。
425
+ * ```
426
+ */
427
+ interface RefundResult {
428
+ /** 退款单号 */
429
+ refundNo: string;
430
+ /** 渠道退款标识;支付宝返回原交易号,需同时使用 refundNo 标识本次退款。 */
431
+ refundId: string;
432
+ /** 退款状态;支付宝 fund_change 为 N 或缺失时是 processing,须向渠道确认后才能判定退款成功。 */
433
+ status: 'processing' | 'success' | 'failed';
434
+ }
435
+ /**
436
+ * 支付 Provider 接口
437
+ *
438
+ * 各支付渠道(微信/支付宝/Stripe)实现此接口,
439
+ * 由 payment 模块统一调度。
440
+ * @example
441
+ * ```ts
442
+ * import { payment, type PaymentProvider } from '@h-ai/payment'
443
+ * function registerPartner(provider: PaymentProvider) {
444
+ * // 实现方负责完整的创建、验签解析、查单、退款和关单契约,并返回 HaiResult。
445
+ * payment.registerProvider(provider)
446
+ * return payment.getProvider(provider.name)
447
+ * }
448
+ * ```
449
+ */
450
+ interface PaymentProvider {
451
+ /** Provider 名称(如 'wechat', 'alipay', 'stripe') */
452
+ readonly name: string;
453
+ /** 创建完整客户端契约的订单;unsupported 场景返回失败,不能以空参数占位成功。 */
454
+ createOrder: (input: CreateOrderInput) => Promise<HaiResult<PaymentOrder>>;
455
+ /** 对原始通知验签并解析;成功可为 pending,不能代替业务核对、持久去重和履约事务。 */
456
+ handleNotify: (request: PaymentNotifyRequest) => Promise<HaiResult<PaymentNotifyResult>>;
457
+ /** 读取并核对指定商户订单的渠道状态;未知订单或未知状态必须返回失败。 */
458
+ queryOrder: (orderNo: string) => Promise<HaiResult<OrderStatus>>;
459
+ /** 申请指定 refundNo 的退款;成功后还要区分 processing 与资金确认 success。 */
460
+ refund: (input: RefundInput) => Promise<HaiResult<RefundResult>>;
461
+ /** 只有渠道确认关闭/过期才成功;禁止无操作返回成功或关闭已付款订单。 */
462
+ closeOrder: (orderNo: string) => Promise<HaiResult<void>>;
463
+ }
464
+ /**
465
+ * 支付模块函数接口
466
+ * @example
467
+ * ```ts
468
+ * import { payment, type PaymentFunctions } from '@h-ai/payment'
469
+ * const service: PaymentFunctions = payment
470
+ * if (!service.isInitialized) throw new Error('Payment is not initialized')
471
+ * const result = await service.queryOrder('stripe', 'ORDER_1')
472
+ * if (!result.success) throw new Error(result.error.message)
473
+ * ```
474
+ */
475
+ interface PaymentFunctions {
476
+ /** 按 Schema 注册渠道;Stripe 要求共享 reldb 已初始化,失败后不能启动依赖支付的业务。 */
477
+ init: (config: PaymentConfigInput) => Promise<HaiResult<void>>;
478
+ /** 清理本进程注册表与配置;不会删除持久幂等记录或关闭宿主 reldb,不返回 HaiResult。 */
479
+ close: () => Promise<void>;
480
+ /** 当前解析后的配置,关闭或未初始化时为 null;包含密钥,禁止下发浏览器或记录日志。 */
481
+ readonly config: PaymentConfig | null;
482
+ /** 是否已有成功发布的配置;不代表渠道实时可用或商户凭据已被远端验证。 */
483
+ readonly isInitialized: boolean;
484
+ /** 创建订单并返回对应场景的最小客户端参数;调用方必须先检查 success。 */
485
+ createOrder: (providerName: string, input: CreateOrderInput) => Promise<HaiResult<PaymentOrder>>;
486
+ /** 验签并解析原始回调;宿主核对金额/币种并持久去重后才可确认业务履约。 */
487
+ handleNotify: (providerName: string, request: PaymentNotifyRequest) => Promise<HaiResult<PaymentNotifyResult>>;
488
+ /** 查渠道状态;失败不能映射成未付款或直接重建订单。 */
489
+ queryOrder: (providerName: string, orderNo: string) => Promise<HaiResult<OrderStatus>>;
490
+ /** 退款标识与参数保持稳定;processing 仍需渠道核对,不能视作最终退款成功。 */
491
+ refund: (providerName: string, input: RefundInput) => Promise<HaiResult<RefundResult>>;
492
+ /** 主动关单;Stripe 只在确认 Session expired 后成功,页面返回不是关单证明。 */
493
+ closeOrder: (providerName: string, orderNo: string) => Promise<HaiResult<void>>;
494
+ /** 按名称读取本进程 Provider;未注册时为 undefined,应用通常使用统一操作入口。 */
495
+ getProvider: (name: string) => PaymentProvider | undefined;
496
+ /** 注册完整实现 PaymentProvider 的自定义渠道;同名会替换本进程注册项,生命周期由宿主协调。 */
497
+ registerProvider: (provider: PaymentProvider) => void;
498
+ }
499
+
500
+ export { type AlipayConfig as A, type CreateOrderInput as C, HaiPaymentError as H, type OrderStatus as O, type PaymentOrder as P, type RefundInput as R, type StripeConfig as S, type TradeType as T, type WechatPayConfig as W, type PaymentFunctions as a, AlipayConfigSchema as b, type OrderStatusValue as c, type PaymentConfig as d, type PaymentConfigInput as e, PaymentConfigSchema as f, type PaymentNotifyRequest as g, type PaymentNotifyResult as h, type PaymentProvider as i, type RefundResult as j, StripeConfigSchema as k, WechatPayConfigSchema as l };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/payment",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.66",
4
+ "version": "0.1.0-alpha.68",
5
5
  "description": "Hai Framework payment module for WeChat Pay, Alipay, and Stripe.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -26,12 +26,14 @@
26
26
  "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
27
27
  },
28
28
  "dependencies": {
29
- "@h-ai/audit": "0.1.0-alpha.66",
30
- "@h-ai/core": "0.1.0-alpha.66",
29
+ "@h-ai/audit": "0.1.0-alpha.68",
30
+ "@h-ai/core": "0.1.0-alpha.68",
31
+ "@h-ai/reldb": "0.1.0-alpha.68",
31
32
  "zod": "^4.6.5"
32
33
  },
33
34
  "devDependencies": {
34
35
  "@types/node": "^26.6.5",
36
+ "jsdom": "^30.1.2",
35
37
  "rimraf": "^6.1.3",
36
38
  "tsup": "^8.5.1",
37
39
  "typescript": "^6.0.3",
@@ -1,67 +0,0 @@
1
- import { core } from '@h-ai/core';
2
-
3
- // src/payment-types.ts
4
- var PaymentErrorInfo = {
5
- CREATE_ORDER_FAILED: "001:500",
6
- QUERY_ORDER_FAILED: "002:500",
7
- REFUND_FAILED: "003:500",
8
- CLOSE_ORDER_FAILED: "004:500",
9
- NOT_INITIALIZED: "010:500",
10
- SIGN_FAILED: "020:500",
11
- PROVIDER_NOT_FOUND: "030:404",
12
- INVALID_AMOUNT: "040:400",
13
- NOTIFY_VERIFY_FAILED: "050:400",
14
- NOTIFY_PARSE_FAILED: "051:400",
15
- INVOKE_WEB_FAILED: "060:500",
16
- INVOKE_APP_FAILED: "061:500",
17
- CONFIG_ERROR: "070:500"
18
- };
19
- var HaiPaymentError = core.error.buildHaiErrorsDef("payment", PaymentErrorInfo);
20
-
21
- // messages/en-US.json
22
- var en_US_default = {
23
- payment_createOrderFailed: "Failed to create payment order",
24
- payment_queryOrderFailed: "Failed to query order status",
25
- payment_refundFailed: "Refund request failed",
26
- payment_closeOrderFailed: "Failed to close order",
27
- payment_notifyVerifyFailed: "Payment notification verification failed",
28
- payment_notifyParseFailed: "Failed to parse payment notification",
29
- payment_signFailed: "Payment signature generation failed",
30
- payment_providerNotFound: "Payment provider not found",
31
- payment_invalidAmount: "Invalid payment amount",
32
- payment_notInitialized: "Payment module not initialized, call payment.init() first",
33
- payment_invokeWebFailed: "Failed to invoke web payment",
34
- payment_invokeAppFailed: "Failed to invoke app payment",
35
- payment_configError: "Payment config validation failed: {error}",
36
- payment_initFailed: "Payment module initialization failed: {error}",
37
- payment_configFieldRequired: "This config field is required"
38
- };
39
-
40
- // messages/zh-CN.json
41
- var zh_CN_default = {
42
- payment_createOrderFailed: "\u521B\u5EFA\u652F\u4ED8\u8BA2\u5355\u5931\u8D25",
43
- payment_queryOrderFailed: "\u67E5\u8BE2\u8BA2\u5355\u72B6\u6001\u5931\u8D25",
44
- payment_refundFailed: "\u9000\u6B3E\u8BF7\u6C42\u5931\u8D25",
45
- payment_closeOrderFailed: "\u5173\u95ED\u8BA2\u5355\u5931\u8D25",
46
- payment_notifyVerifyFailed: "\u652F\u4ED8\u56DE\u8C03\u9A8C\u7B7E\u5931\u8D25",
47
- payment_notifyParseFailed: "\u89E3\u6790\u652F\u4ED8\u56DE\u8C03\u5931\u8D25",
48
- payment_signFailed: "\u652F\u4ED8\u7B7E\u540D\u751F\u6210\u5931\u8D25",
49
- payment_providerNotFound: "\u672A\u627E\u5230\u652F\u4ED8 Provider",
50
- payment_invalidAmount: "\u652F\u4ED8\u91D1\u989D\u65E0\u6548",
51
- payment_notInitialized: "\u652F\u4ED8\u6A21\u5757\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148\u8C03\u7528 payment.init()",
52
- payment_invokeWebFailed: "\u8C03\u8D77\u7F51\u9875\u652F\u4ED8\u5931\u8D25",
53
- payment_invokeAppFailed: "\u8C03\u8D77 App \u652F\u4ED8\u5931\u8D25",
54
- payment_configError: "\u652F\u4ED8\u6A21\u5757\u914D\u7F6E\u6821\u9A8C\u5931\u8D25\uFF1A{error}",
55
- payment_initFailed: "\u652F\u4ED8\u6A21\u5757\u521D\u59CB\u5316\u5931\u8D25\uFF1A{error}",
56
- payment_configFieldRequired: "\u6B64\u914D\u7F6E\u5B57\u6BB5\u5FC5\u586B"
57
- };
58
-
59
- // src/payment-i18n.ts
60
- var paymentM = core.i18n.createMessageGetter({
61
- "zh-CN": zh_CN_default,
62
- "en-US": en_US_default
63
- });
64
-
65
- export { HaiPaymentError, paymentM };
66
- //# sourceMappingURL=chunk-4IUARFWH.js.map
67
- //# sourceMappingURL=chunk-4IUARFWH.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/payment-types.ts","../messages/en-US.json","../messages/zh-CN.json","../src/payment-i18n.ts"],"names":["core"],"mappings":";;;AAaA,IAAM,gBAAA,GAAmB;AAAA,EACvB,mBAAA,EAAqB,SAAA;AAAA,EACrB,kBAAA,EAAoB,SAAA;AAAA,EACpB,aAAA,EAAe,SAAA;AAAA,EACf,kBAAA,EAAoB,SAAA;AAAA,EACpB,eAAA,EAAiB,SAAA;AAAA,EACjB,WAAA,EAAa,SAAA;AAAA,EACb,kBAAA,EAAoB,SAAA;AAAA,EACpB,cAAA,EAAgB,SAAA;AAAA,EAChB,oBAAA,EAAsB,SAAA;AAAA,EACtB,mBAAA,EAAqB,SAAA;AAAA,EACrB,iBAAA,EAAmB,SAAA;AAAA,EACnB,iBAAA,EAAmB,SAAA;AAAA,EACnB,YAAA,EAAc;AAChB,CAAA;AAEO,IAAM,eAAA,GAAkB,IAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,WAAW,gBAAgB;;;AC7BvF,IAAA,aAAA,GAAA;AAAA,EACE,yBAAA,EAA6B,gCAAA;AAAA,EAC7B,wBAAA,EAA4B,8BAAA;AAAA,EAC5B,oBAAA,EAAwB,uBAAA;AAAA,EACxB,wBAAA,EAA4B,uBAAA;AAAA,EAC5B,0BAAA,EAA8B,0CAAA;AAAA,EAC9B,yBAAA,EAA6B,sCAAA;AAAA,EAC7B,kBAAA,EAAsB,qCAAA;AAAA,EACtB,wBAAA,EAA4B,4BAAA;AAAA,EAC5B,qBAAA,EAAyB,wBAAA;AAAA,EACzB,sBAAA,EAA0B,2DAAA;AAAA,EAC1B,uBAAA,EAA2B,8BAAA;AAAA,EAC3B,uBAAA,EAA2B,8BAAA;AAAA,EAC3B,mBAAA,EAAuB,2CAAA;AAAA,EACvB,kBAAA,EAAsB,+CAAA;AAAA,EACtB,2BAAA,EAA+B;AACjC,CAAA;;;AChBA,IAAA,aAAA,GAAA;AAAA,EACE,yBAAA,EAA6B,kDAAA;AAAA,EAC7B,wBAAA,EAA4B,kDAAA;AAAA,EAC5B,oBAAA,EAAwB,sCAAA;AAAA,EACxB,wBAAA,EAA4B,sCAAA;AAAA,EAC5B,0BAAA,EAA8B,kDAAA;AAAA,EAC9B,yBAAA,EAA6B,kDAAA;AAAA,EAC7B,kBAAA,EAAsB,kDAAA;AAAA,EACtB,wBAAA,EAA4B,yCAAA;AAAA,EAC5B,qBAAA,EAAyB,sCAAA;AAAA,EACzB,sBAAA,EAA0B,+FAAA;AAAA,EAC1B,uBAAA,EAA2B,kDAAA;AAAA,EAC3B,uBAAA,EAA2B,2CAAA;AAAA,EAC3B,mBAAA,EAAuB,2EAAA;AAAA,EACvB,kBAAA,EAAsB,qEAAA;AAAA,EACtB,2BAAA,EAA+B;AACjC,CAAA;;;ACGO,IAAM,QAAA,GAAWA,IAAAA,CAAK,IAAA,CAAK,mBAAA,CAAuC;AAAA,EACvE,OAAA,EAAS,aAAA;AAAA,EACT,OAAA,EAAS;AACX,CAAC","file":"chunk-4IUARFWH.js","sourcesContent":["/**\n * @h-ai/payment — 类型定义\n *\n * 统一支付模块的公共类型、Provider 接口与错误类型。\n * @module payment-types\n */\n\nimport type { ErrorInfo, HaiResult } from '@h-ai/core'\nimport type { PaymentConfig, PaymentConfigInput } from './payment-config.js'\nimport { core } from '@h-ai/core'\n\n// ─── 错误定义(照 @h-ai/core 范式) ───\n\nconst PaymentErrorInfo = {\n CREATE_ORDER_FAILED: '001:500',\n QUERY_ORDER_FAILED: '002:500',\n REFUND_FAILED: '003:500',\n CLOSE_ORDER_FAILED: '004:500',\n NOT_INITIALIZED: '010:500',\n SIGN_FAILED: '020:500',\n PROVIDER_NOT_FOUND: '030:404',\n INVALID_AMOUNT: '040:400',\n NOTIFY_VERIFY_FAILED: '050:400',\n NOTIFY_PARSE_FAILED: '051:400',\n INVOKE_WEB_FAILED: '060:500',\n INVOKE_APP_FAILED: '061:500',\n CONFIG_ERROR: '070:500',\n} as const satisfies ErrorInfo\n\nexport const HaiPaymentError = core.error.buildHaiErrorsDef('payment', PaymentErrorInfo)\n\n// ─── 支付场景 ───\n\n/** 支付场景(tradeType) */\nexport type TradeType = 'jsapi' | 'h5' | 'app' | 'native' | 'mini_program'\n\n/** 订单状态 */\nexport type OrderStatusValue = 'pending' | 'paid' | 'closed' | 'refunded' | 'failed'\n\n// ─── 创建订单 ───\n\n/** 创建订单入参 */\nexport interface CreateOrderInput {\n /** 商户订单号 */\n orderNo: string\n /** 金额(分) */\n amount: number\n /** 币种(默认 CNY) */\n currency?: string\n /** 商品描述 */\n description: string\n /** 支付场景 */\n tradeType: TradeType\n /** 用户标识(微信 openid 等,jsapi 必填) */\n userId?: string\n /** 异步通知 URL */\n notifyUrl: string\n /** 附加数据(透传) */\n metadata?: Record<string, string>\n}\n\n/** 支付订单(返回给客户端调起支付) */\nexport interface PaymentOrder {\n /** Provider 名称 */\n provider: string\n /** 支付场景 */\n tradeType: TradeType\n /** 客户端调起参数(各平台差异) */\n clientParams: Record<string, unknown>\n /** 预支付 ID(如微信 prepay_id) */\n prepayId?: string\n}\n\n// ─── 回调通知 ───\n\n/** 回调请求(原始 HTTP 数据) */\nexport interface PaymentNotifyRequest {\n /** 请求 body */\n body: string\n /** 请求 headers */\n headers: Record<string, string>\n}\n\n/** 回调解析结果 */\nexport interface PaymentNotifyResult {\n /** 商户订单号 */\n orderNo: string\n /** 支付渠道交易号 */\n transactionId: string\n /** 支付金额(分) */\n amount: number\n /** 支付状态 */\n status: OrderStatusValue\n /** 支付时间 */\n paidAt?: Date\n /** 原始数据 */\n raw?: Record<string, unknown>\n}\n\n// ─── 查询订单 ───\n\n/** 订单状态查询结果 */\nexport interface OrderStatus {\n /** 商户订单号 */\n orderNo: string\n /** 渠道交易号 */\n transactionId?: string\n /** 订单状态 */\n status: OrderStatusValue\n /** 金额(分) */\n amount: number\n /** 支付时间 */\n paidAt?: Date\n}\n\n// ─── 退款 ───\n\n/** 退款入参 */\nexport interface RefundInput {\n /** 商户订单号 */\n orderNo: string\n /** 退款单号 */\n refundNo: string\n /** 退款金额(分) */\n amount: number\n /** 订单总金额(分),微信退款必填 */\n totalAmount?: number\n /** 退款原因 */\n reason?: string\n}\n\n/** 退款结果 */\nexport interface RefundResult {\n /** 退款单号 */\n refundNo: string\n /** 渠道退款 ID */\n refundId: string\n /** 退款状态 */\n status: 'processing' | 'success' | 'failed'\n}\n\n// ─── Provider 接口 ───\n\n/**\n * 支付 Provider 接口\n *\n * 各支付渠道(微信/支付宝/Stripe)实现此接口,\n * 由 payment 模块统一调度。\n */\nexport interface PaymentProvider {\n /** Provider 名称(如 'wechat', 'alipay', 'stripe') */\n readonly name: string\n\n /** 创建支付订单 */\n createOrder: (input: CreateOrderInput) => Promise<HaiResult<PaymentOrder>>\n\n /** 处理异步通知回调(验签 + 解析) */\n handleNotify: (request: PaymentNotifyRequest) => Promise<HaiResult<PaymentNotifyResult>>\n\n /** 查询订单状态 */\n queryOrder: (orderNo: string) => Promise<HaiResult<OrderStatus>>\n\n /** 申请退款 */\n refund: (input: RefundInput) => Promise<HaiResult<RefundResult>>\n\n /** 关闭订单 */\n closeOrder: (orderNo: string) => Promise<HaiResult<void>>\n}\n\n// ─── 函数接口 ───\n\n/** 支付模块函数接口 */\nexport interface PaymentFunctions {\n init: (config: PaymentConfigInput) => Promise<HaiResult<void>>\n close: () => Promise<void>\n readonly config: PaymentConfig | null\n readonly isInitialized: boolean\n createOrder: (providerName: string, input: CreateOrderInput) => Promise<HaiResult<PaymentOrder>>\n handleNotify: (providerName: string, request: PaymentNotifyRequest) => Promise<HaiResult<PaymentNotifyResult>>\n queryOrder: (providerName: string, orderNo: string) => Promise<HaiResult<OrderStatus>>\n refund: (providerName: string, input: RefundInput) => Promise<HaiResult<RefundResult>>\n closeOrder: (providerName: string, orderNo: string) => Promise<HaiResult<void>>\n getProvider: (name: string) => PaymentProvider | undefined\n registerProvider: (provider: PaymentProvider) => void\n}\n","{\n \"payment_createOrderFailed\": \"Failed to create payment order\",\n \"payment_queryOrderFailed\": \"Failed to query order status\",\n \"payment_refundFailed\": \"Refund request failed\",\n \"payment_closeOrderFailed\": \"Failed to close order\",\n \"payment_notifyVerifyFailed\": \"Payment notification verification failed\",\n \"payment_notifyParseFailed\": \"Failed to parse payment notification\",\n \"payment_signFailed\": \"Payment signature generation failed\",\n \"payment_providerNotFound\": \"Payment provider not found\",\n \"payment_invalidAmount\": \"Invalid payment amount\",\n \"payment_notInitialized\": \"Payment module not initialized, call payment.init() first\",\n \"payment_invokeWebFailed\": \"Failed to invoke web payment\",\n \"payment_invokeAppFailed\": \"Failed to invoke app payment\",\n \"payment_configError\": \"Payment config validation failed: {error}\",\n \"payment_initFailed\": \"Payment module initialization failed: {error}\",\n \"payment_configFieldRequired\": \"This config field is required\"\n}\n","{\n \"payment_createOrderFailed\": \"创建支付订单失败\",\n \"payment_queryOrderFailed\": \"查询订单状态失败\",\n \"payment_refundFailed\": \"退款请求失败\",\n \"payment_closeOrderFailed\": \"关闭订单失败\",\n \"payment_notifyVerifyFailed\": \"支付回调验签失败\",\n \"payment_notifyParseFailed\": \"解析支付回调失败\",\n \"payment_signFailed\": \"支付签名生成失败\",\n \"payment_providerNotFound\": \"未找到支付 Provider\",\n \"payment_invalidAmount\": \"支付金额无效\",\n \"payment_notInitialized\": \"支付模块未初始化,请先调用 payment.init()\",\n \"payment_invokeWebFailed\": \"调起网页支付失败\",\n \"payment_invokeAppFailed\": \"调起 App 支付失败\",\n \"payment_configError\": \"支付模块配置校验失败:{error}\",\n \"payment_initFailed\": \"支付模块初始化失败:{error}\",\n \"payment_configFieldRequired\": \"此配置字段必填\"\n}\n","/**\n * @h-ai/payment — i18n\n *\n * 模块 i18n 文案访问入口。\n * @module payment-i18n\n */\n\nimport { core } from '@h-ai/core'\nimport messagesEnUS from '../messages/en-US.json'\nimport messagesZhCN from '../messages/zh-CN.json'\n\ntype PaymentMessageKey = keyof typeof messagesZhCN\n\n/**\n * payment 模块 i18n 文案获取器\n *\n * @param key - 文案 Key\n * @returns 本地化后的文案字符串\n */\nexport const paymentM = core.i18n.createMessageGetter<PaymentMessageKey>({\n 'zh-CN': messagesZhCN,\n 'en-US': messagesEnUS,\n})\n"]}