@vobs/payment 0.3.0 → 1.2.0
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/LICENSE +1 -1
- package/README.md +182 -0
- package/dist/alipay/client.cjs +112 -0
- package/dist/alipay/client.cjs.map +1 -0
- package/dist/alipay/client.d.cts +36 -0
- package/dist/alipay/client.d.ts +36 -0
- package/dist/alipay/client.js +110 -0
- package/dist/alipay/client.js.map +1 -0
- package/dist/alipay/config.cjs +67 -0
- package/dist/alipay/config.cjs.map +1 -0
- package/dist/alipay/config.d.cts +30 -0
- package/dist/alipay/config.d.ts +30 -0
- package/dist/alipay/config.js +65 -0
- package/dist/alipay/config.js.map +1 -0
- package/dist/alipay/index.cjs +592 -0
- package/dist/alipay/index.cjs.map +1 -0
- package/dist/alipay/index.d.cts +12 -0
- package/dist/alipay/index.d.ts +12 -0
- package/dist/alipay/index.js +580 -0
- package/dist/alipay/index.js.map +1 -0
- package/dist/alipay/notify.cjs +70 -0
- package/dist/alipay/notify.cjs.map +1 -0
- package/dist/alipay/notify.d.cts +52 -0
- package/dist/alipay/notify.d.ts +52 -0
- package/dist/alipay/notify.js +68 -0
- package/dist/alipay/notify.js.map +1 -0
- package/dist/alipay/payment.cjs +49 -0
- package/dist/alipay/payment.cjs.map +1 -0
- package/dist/alipay/payment.d.cts +24 -0
- package/dist/alipay/payment.d.ts +24 -0
- package/dist/alipay/payment.js +47 -0
- package/dist/alipay/payment.js.map +1 -0
- package/dist/alipay/plugin.cjs +300 -0
- package/dist/alipay/plugin.cjs.map +1 -0
- package/dist/alipay/plugin.d.cts +43 -0
- package/dist/alipay/plugin.d.ts +43 -0
- package/dist/alipay/plugin.js +296 -0
- package/dist/alipay/plugin.js.map +1 -0
- package/dist/alipay/query.cjs +80 -0
- package/dist/alipay/query.cjs.map +1 -0
- package/dist/alipay/query.d.cts +26 -0
- package/dist/alipay/query.d.ts +26 -0
- package/dist/alipay/query.js +78 -0
- package/dist/alipay/query.js.map +1 -0
- package/dist/alipay/refund.cjs +104 -0
- package/dist/alipay/refund.cjs.map +1 -0
- package/dist/alipay/refund.d.cts +32 -0
- package/dist/alipay/refund.d.ts +32 -0
- package/dist/alipay/refund.js +102 -0
- package/dist/alipay/refund.js.map +1 -0
- package/dist/alipay/response.cjs +38 -0
- package/dist/alipay/response.cjs.map +1 -0
- package/dist/alipay/response.d.cts +19 -0
- package/dist/alipay/response.d.ts +19 -0
- package/dist/alipay/response.js +35 -0
- package/dist/alipay/response.js.map +1 -0
- package/dist/alipay/types.cjs +4 -0
- package/dist/alipay/types.cjs.map +1 -0
- package/dist/alipay/types.d.cts +242 -0
- package/dist/alipay/types.d.ts +242 -0
- package/dist/alipay/types.js +3 -0
- package/dist/alipay/types.js.map +1 -0
- package/dist/alipay/wap.cjs +50 -0
- package/dist/alipay/wap.cjs.map +1 -0
- package/dist/alipay/wap.d.cts +24 -0
- package/dist/alipay/wap.d.ts +24 -0
- package/dist/alipay/wap.js +48 -0
- package/dist/alipay/wap.js.map +1 -0
- package/dist/index-B1aUR7Vt.d.ts +49 -0
- package/dist/index-B8pzZxGq.d.cts +44 -0
- package/dist/index-BWnIrwRi.d.ts +44 -0
- package/dist/index-Dyrkl86r.d.cts +49 -0
- package/dist/index.cjs +1071 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +23 -0
- package/dist/index.d.ts +23 -37
- package/dist/index.js +1052 -37
- package/dist/index.js.map +1 -0
- package/dist/wechat/client.cjs +119 -0
- package/dist/wechat/client.cjs.map +1 -0
- package/dist/wechat/client.d.cts +39 -0
- package/dist/wechat/client.d.ts +39 -0
- package/dist/wechat/client.js +117 -0
- package/dist/wechat/client.js.map +1 -0
- package/dist/wechat/config.cjs +76 -0
- package/dist/wechat/config.cjs.map +1 -0
- package/dist/wechat/config.d.cts +29 -0
- package/dist/wechat/config.d.ts +29 -0
- package/dist/wechat/config.js +74 -0
- package/dist/wechat/config.js.map +1 -0
- package/dist/wechat/index.cjs +612 -0
- package/dist/wechat/index.cjs.map +1 -0
- package/dist/wechat/index.d.cts +10 -0
- package/dist/wechat/index.d.ts +10 -0
- package/dist/wechat/index.js +602 -0
- package/dist/wechat/index.js.map +1 -0
- package/dist/wechat/notify.cjs +73 -0
- package/dist/wechat/notify.cjs.map +1 -0
- package/dist/wechat/notify.d.cts +53 -0
- package/dist/wechat/notify.d.ts +53 -0
- package/dist/wechat/notify.js +71 -0
- package/dist/wechat/notify.js.map +1 -0
- package/dist/wechat/payment.cjs +186 -0
- package/dist/wechat/payment.cjs.map +1 -0
- package/dist/wechat/payment.d.cts +50 -0
- package/dist/wechat/payment.d.ts +50 -0
- package/dist/wechat/payment.js +184 -0
- package/dist/wechat/payment.js.map +1 -0
- package/dist/wechat/plugin.cjs +305 -0
- package/dist/wechat/plugin.cjs.map +1 -0
- package/dist/wechat/plugin.d.cts +40 -0
- package/dist/wechat/plugin.d.ts +40 -0
- package/dist/wechat/plugin.js +301 -0
- package/dist/wechat/plugin.js.map +1 -0
- package/dist/wechat/refund.cjs +112 -0
- package/dist/wechat/refund.cjs.map +1 -0
- package/dist/wechat/refund.d.cts +26 -0
- package/dist/wechat/refund.d.ts +26 -0
- package/dist/wechat/refund.js +110 -0
- package/dist/wechat/refund.js.map +1 -0
- package/dist/wechat/response.cjs +50 -0
- package/dist/wechat/response.cjs.map +1 -0
- package/dist/wechat/response.d.cts +20 -0
- package/dist/wechat/response.d.ts +20 -0
- package/dist/wechat/response.js +46 -0
- package/dist/wechat/response.js.map +1 -0
- package/dist/wechat/types.cjs +4 -0
- package/dist/wechat/types.cjs.map +1 -0
- package/dist/wechat/types.d.cts +200 -0
- package/dist/wechat/types.d.ts +200 -0
- package/dist/wechat/types.js +3 -0
- package/dist/wechat/types.js.map +1 -0
- package/package.json +37 -28
- package/src/alipay/alipay.test.ts +231 -0
- package/src/alipay/client.ts +67 -0
- package/src/alipay/config.ts +72 -0
- package/src/alipay/index.ts +32 -0
- package/src/alipay/notify.ts +92 -0
- package/src/alipay/payment.ts +60 -0
- package/src/alipay/plugin.ts +64 -0
- package/src/alipay/query.ts +52 -0
- package/src/alipay/refund.ts +85 -0
- package/src/alipay/response.ts +40 -0
- package/src/alipay/types.ts +271 -0
- package/src/alipay/wap.ts +55 -0
- package/src/index.ts +78 -0
- package/src/wechat/client.ts +68 -0
- package/src/wechat/config.ts +81 -0
- package/src/wechat/index.ts +30 -0
- package/src/wechat/notify.ts +93 -0
- package/src/wechat/payment.ts +177 -0
- package/src/wechat/plugin.ts +59 -0
- package/src/wechat/refund.ts +80 -0
- package/src/wechat/response.ts +54 -0
- package/src/wechat/types.ts +223 -0
- package/src/wechat/wechat.test.ts +393 -0
- package/dist/alipay-official.d.ts +0 -34
- package/dist/alipay-official.js +0 -206
- package/dist/handlers.d.ts +0 -45
- package/dist/handlers.js +0 -105
- package/dist/jeepay.d.ts +0 -39
- package/dist/jeepay.js +0 -206
- package/dist/lakala.d.ts +0 -41
- package/dist/lakala.js +0 -243
- package/dist/manager.d.ts +0 -67
- package/dist/manager.js +0 -538
- package/dist/mock-driver.d.ts +0 -22
- package/dist/mock-driver.js +0 -59
- package/dist/state-machine.d.ts +0 -41
- package/dist/state-machine.js +0 -60
- package/dist/store.d.ts +0 -33
- package/dist/store.js +0 -76
- package/dist/types.d.ts +0 -168
- package/dist/types.js +0 -11
- package/dist/verify.d.ts +0 -34
- package/dist/verify.js +0 -55
- package/dist/wechat-official.d.ts +0 -40
- package/dist/wechat-official.js +0 -208
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { AlipayClient } from './client'
|
|
2
|
+
import { unwrapResponse } from './response'
|
|
3
|
+
import type {
|
|
4
|
+
AlipayRefundParams,
|
|
5
|
+
AlipayRefundResult,
|
|
6
|
+
AlipayRefundQueryParams,
|
|
7
|
+
AlipayRefundQueryResult,
|
|
8
|
+
AlipayCloseParams,
|
|
9
|
+
AlipayCloseResult
|
|
10
|
+
} from './types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 退款与交易关闭
|
|
14
|
+
*
|
|
15
|
+
* 包含退款、退款查询、交易关闭的完整退款生命周期管理。
|
|
16
|
+
*/
|
|
17
|
+
export function createRefund(client: AlipayClient) {
|
|
18
|
+
return {
|
|
19
|
+
/**
|
|
20
|
+
* 交易退款 (alipay.trade.refund)
|
|
21
|
+
* 交易发生后 12 个月内可退款,退款资金原路返回。
|
|
22
|
+
* @throws {AlipayApiError} 业务失败时抛出
|
|
23
|
+
*/
|
|
24
|
+
async refund(params: AlipayRefundParams): Promise<AlipayRefundResult> {
|
|
25
|
+
const alipayResponse = unwrapResponse(
|
|
26
|
+
await client.sdk.exec('alipay.trade.refund', {
|
|
27
|
+
bizContent: params
|
|
28
|
+
}),
|
|
29
|
+
'alipay.trade.refund'
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
tradeNo: alipayResponse.trade_no,
|
|
34
|
+
outTradeNo: alipayResponse.out_trade_no,
|
|
35
|
+
buyerLogonId: alipayResponse.buyer_logon_id,
|
|
36
|
+
refundAmount: alipayResponse.refund_fee,
|
|
37
|
+
fundChange: alipayResponse.fund_change ?? 'N',
|
|
38
|
+
gmtRefundPay: alipayResponse.gmt_refund_pay ?? '',
|
|
39
|
+
refundDetailItemList: alipayResponse.refund_detail_item_list ?? null
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 退款查询 (alipay.trade.fastpay.refund.query)
|
|
45
|
+
* 查询退款状态,确认退款是否成功。
|
|
46
|
+
* @throws {AlipayApiError} 业务失败时抛出
|
|
47
|
+
*/
|
|
48
|
+
async queryRefund(params: AlipayRefundQueryParams): Promise<AlipayRefundQueryResult> {
|
|
49
|
+
const alipayResponse = unwrapResponse(
|
|
50
|
+
await client.sdk.exec('alipay.trade.fastpay.refund.query', {
|
|
51
|
+
bizContent: params
|
|
52
|
+
}),
|
|
53
|
+
'alipay.trade.fastpay.refund.query'
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
tradeNo: alipayResponse.trade_no,
|
|
58
|
+
outTradeNo: alipayResponse.out_trade_no,
|
|
59
|
+
refundAmount: alipayResponse.refund_amount,
|
|
60
|
+
refundStatus: alipayResponse.refund_status,
|
|
61
|
+
outRequestNo: alipayResponse.out_request_no,
|
|
62
|
+
refundReason: alipayResponse.refund_reason ?? ''
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 关闭交易 (alipay.trade.close)
|
|
68
|
+
* 关闭未支付的订单。
|
|
69
|
+
* @throws {AlipayApiError} 业务失败时抛出
|
|
70
|
+
*/
|
|
71
|
+
async close(params: AlipayCloseParams): Promise<AlipayCloseResult> {
|
|
72
|
+
const alipayResponse = unwrapResponse(
|
|
73
|
+
await client.sdk.exec('alipay.trade.close', {
|
|
74
|
+
bizContent: params
|
|
75
|
+
}),
|
|
76
|
+
'alipay.trade.close'
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
tradeNo: alipayResponse.trade_no,
|
|
81
|
+
outTradeNo: alipayResponse.out_trade_no
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 支付宝开放接口响应解包与业务错误检查。
|
|
3
|
+
*
|
|
4
|
+
* 网关应答(HTTP 200)不代表业务成功:业务失败时响应体内
|
|
5
|
+
* code != '10000' 并携带 sub_code/sub_msg,必须显式检查,
|
|
6
|
+
* 否则会把失败结果当成功映射(字段全部 undefined)。
|
|
7
|
+
*/
|
|
8
|
+
export class AlipayApiError extends Error {
|
|
9
|
+
readonly code: string
|
|
10
|
+
readonly subCode?: string
|
|
11
|
+
readonly subMsg?: string
|
|
12
|
+
|
|
13
|
+
constructor(apiName: string, code: string, subCode: string | undefined, subMsg: string | undefined, msg: string | undefined) {
|
|
14
|
+
super(`支付宝接口 ${apiName} 调用失败: ${subMsg ?? msg ?? '未知错误'} (code=${code}${subCode ? `, sub_code=${subCode}` : ''})`)
|
|
15
|
+
this.name = 'AlipayApiError'
|
|
16
|
+
this.code = code
|
|
17
|
+
this.subCode = subCode
|
|
18
|
+
this.subMsg = subMsg
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** alipay.trade.page.pay -> alipay_trade_page_pay_response */
|
|
23
|
+
function toResponseKey(apiName: string): string {
|
|
24
|
+
return `${apiName.replace(/\./g, '_')}_response`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* 从网关应答中取出业务响应体,code !== '10000' 时抛出 AlipayApiError。
|
|
29
|
+
*/
|
|
30
|
+
export function unwrapResponse(raw: unknown, apiName: string): Record<string, any> {
|
|
31
|
+
const outer = raw as Record<string, any> | null | undefined
|
|
32
|
+
const body = (outer && outer[toResponseKey(apiName)]) ?? outer
|
|
33
|
+
if (!body || typeof body !== 'object') {
|
|
34
|
+
throw new AlipayApiError(apiName, 'INVALID_RESPONSE', undefined, undefined, '响应体格式异常')
|
|
35
|
+
}
|
|
36
|
+
if (body.code !== '10000') {
|
|
37
|
+
throw new AlipayApiError(apiName, body.code, body.sub_code, body.sub_msg, body.msg)
|
|
38
|
+
}
|
|
39
|
+
return body
|
|
40
|
+
}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import type { AlipaySdkConfig, AlipaySdkSignType } from 'alipay-sdk'
|
|
2
|
+
|
|
3
|
+
// ---- 重新导出 alipay-sdk 类型 ----
|
|
4
|
+
export type { AlipaySdkConfig, AlipaySdkSignType }
|
|
5
|
+
|
|
6
|
+
// ---- 电脑网站支付 (alipay.trade.page.pay) ----
|
|
7
|
+
export interface AlipayPagePayParams {
|
|
8
|
+
/** 商户订单号,需保证唯一 */
|
|
9
|
+
outTradeNo: string
|
|
10
|
+
/** 订单总金额,单位元,精确到两位小数 */
|
|
11
|
+
totalAmount: string
|
|
12
|
+
/** 订单标题 */
|
|
13
|
+
subject: string
|
|
14
|
+
/** 产品代码,电脑网站支付固定为 FAST_INSTANT_TRADE_PAY */
|
|
15
|
+
productCode?: string
|
|
16
|
+
/** 订单描述 */
|
|
17
|
+
body?: string
|
|
18
|
+
/** 绝对超时时间,格式 yyyy-MM-dd HH:mm:ss */
|
|
19
|
+
timeExpire?: string
|
|
20
|
+
/** 订单相对超时时间,范围 1m~15d */
|
|
21
|
+
timeoutExpress?: string
|
|
22
|
+
/** 商品主类型:0-虚拟,1-实物 */
|
|
23
|
+
goodsType?: '0' | '1'
|
|
24
|
+
/** 公用回传参数,异步通知时原样返回 */
|
|
25
|
+
passbackParams?: string
|
|
26
|
+
/** 支付完成后同步跳转地址 */
|
|
27
|
+
returnUrl?: string
|
|
28
|
+
/** 异步通知地址 */
|
|
29
|
+
notifyUrl?: string
|
|
30
|
+
/** 扩展信息 */
|
|
31
|
+
extendParams?: Record<string, string>
|
|
32
|
+
/** 商户门店编号 */
|
|
33
|
+
storeId?: string
|
|
34
|
+
/** 是否发起实名校验 */
|
|
35
|
+
qrPayMode?: string
|
|
36
|
+
/** 扫码支付模式 */
|
|
37
|
+
qrcodeWidth?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface AlipayPagePayResult {
|
|
41
|
+
/** 支付宝返回的 HTML 表单字符串 */
|
|
42
|
+
formHtml: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---- 手机网站支付 (alipay.trade.wap.pay) ----
|
|
46
|
+
export interface AlipayWapPayParams {
|
|
47
|
+
/** 商户订单号,需保证唯一 */
|
|
48
|
+
outTradeNo: string
|
|
49
|
+
/** 订单总金额,单位元,精确到两位小数 */
|
|
50
|
+
totalAmount: string
|
|
51
|
+
/** 订单标题 */
|
|
52
|
+
subject: string
|
|
53
|
+
/** 产品代码,手机网站支付固定为 QUICK_WAP_WAY */
|
|
54
|
+
productCode?: string
|
|
55
|
+
/** 订单描述 */
|
|
56
|
+
body?: string
|
|
57
|
+
/** 绝对超时时间,格式 yyyy-MM-dd HH:mm:ss */
|
|
58
|
+
timeExpire?: string
|
|
59
|
+
/** 订单相对超时时间,范围 1m~15d */
|
|
60
|
+
timeoutExpress?: string
|
|
61
|
+
/** 公用回传参数,异步通知时原样返回 */
|
|
62
|
+
passbackParams?: string
|
|
63
|
+
/** 用户付款中途退出后返回的商户页面地址 */
|
|
64
|
+
quitUrl?: string
|
|
65
|
+
/** 支付完成后同步跳转地址 */
|
|
66
|
+
returnUrl?: string
|
|
67
|
+
/** 异步通知地址 */
|
|
68
|
+
notifyUrl?: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface AlipayWapPayResult {
|
|
72
|
+
/** 支付宝返回的 HTML 表单字符串 */
|
|
73
|
+
formHtml: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ---- 交易查询 (alipay.trade.query) ----
|
|
77
|
+
export interface AlipayQueryParams {
|
|
78
|
+
outTradeNo?: string
|
|
79
|
+
tradeNo?: string
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface AlipayQueryResult {
|
|
83
|
+
/** 支付宝交易号 */
|
|
84
|
+
tradeNo: string
|
|
85
|
+
/** 商户订单号 */
|
|
86
|
+
outTradeNo: string
|
|
87
|
+
/** 买家支付宝账号 */
|
|
88
|
+
buyerLogonId: string
|
|
89
|
+
/** 交易状态 */
|
|
90
|
+
tradeStatus: TradeStatus
|
|
91
|
+
/** 交易的订单金额 */
|
|
92
|
+
totalAmount: string
|
|
93
|
+
/** 实收金额 */
|
|
94
|
+
receiptAmount: string
|
|
95
|
+
/** 买家实付金额 */
|
|
96
|
+
buyerPayAmount: string
|
|
97
|
+
/** 交易创建时间 */
|
|
98
|
+
sendPayDate: string
|
|
99
|
+
/** 交易付款时间 */
|
|
100
|
+
gmtPayment: string | null
|
|
101
|
+
/** 交易关闭时间 */
|
|
102
|
+
gmtClose: string | null
|
|
103
|
+
/** 退款金额 */
|
|
104
|
+
refundAmount: string
|
|
105
|
+
/** 商品描述 */
|
|
106
|
+
subject: string
|
|
107
|
+
/** 商户传入的回传参数 */
|
|
108
|
+
passbackParams: string | null
|
|
109
|
+
/** 本次交易支付工具 */
|
|
110
|
+
fundChannel: string | null
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---- 交易退款 (alipay.trade.refund) ----
|
|
114
|
+
export interface AlipayRefundParams {
|
|
115
|
+
outTradeNo?: string
|
|
116
|
+
tradeNo?: string
|
|
117
|
+
refundAmount: string
|
|
118
|
+
refundReason?: string
|
|
119
|
+
outRequestNo?: string
|
|
120
|
+
refundCurrency?: string
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface AlipayRefundResult {
|
|
124
|
+
/** 支付宝交易号 */
|
|
125
|
+
tradeNo: string
|
|
126
|
+
/** 商户订单号 */
|
|
127
|
+
outTradeNo: string
|
|
128
|
+
/** 买家支付宝账号 */
|
|
129
|
+
buyerLogonId: string
|
|
130
|
+
/** 本次退款请求金额 */
|
|
131
|
+
refundAmount: string
|
|
132
|
+
/** 资金变动明细 */
|
|
133
|
+
fundChange: string
|
|
134
|
+
/** 退款时间 */
|
|
135
|
+
gmtRefundPay: string
|
|
136
|
+
/** 退款金额(含各渠道) */
|
|
137
|
+
refundDetailItemList: Array<{
|
|
138
|
+
contributionType: string
|
|
139
|
+
amount: string
|
|
140
|
+
}> | null
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ---- 交易关闭 (alipay.trade.close) ----
|
|
144
|
+
export interface AlipayCloseParams {
|
|
145
|
+
outTradeNo?: string
|
|
146
|
+
tradeNo?: string
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface AlipayCloseResult {
|
|
150
|
+
/** 支付宝交易号 */
|
|
151
|
+
tradeNo: string
|
|
152
|
+
/** 商户订单号 */
|
|
153
|
+
outTradeNo: string
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ---- 退款查询 (alipay.trade.fastpay.refund.query) ----
|
|
157
|
+
export interface AlipayRefundQueryParams {
|
|
158
|
+
outTradeNo?: string
|
|
159
|
+
tradeNo?: string
|
|
160
|
+
outRequestNo: string
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface AlipayRefundQueryResult {
|
|
164
|
+
/** 支付宝交易号 */
|
|
165
|
+
tradeNo: string
|
|
166
|
+
/** 商户订单号 */
|
|
167
|
+
outTradeNo: string
|
|
168
|
+
/** 退款金额 */
|
|
169
|
+
refundAmount: string
|
|
170
|
+
/** 退款状态 */
|
|
171
|
+
refundStatus: string
|
|
172
|
+
/** 退款请求号 */
|
|
173
|
+
outRequestNo: string
|
|
174
|
+
/** 退款原因 */
|
|
175
|
+
refundReason: string
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// ---- 异步通知 ----
|
|
179
|
+
export interface AlipayNotifyParams {
|
|
180
|
+
[key: string]: string | undefined
|
|
181
|
+
notifyTime: string
|
|
182
|
+
notifyType: string
|
|
183
|
+
notifyId: string
|
|
184
|
+
appId: string
|
|
185
|
+
charset: string
|
|
186
|
+
version: string
|
|
187
|
+
signType: string
|
|
188
|
+
sign: string
|
|
189
|
+
tradeNo: string
|
|
190
|
+
outTradeNo: string
|
|
191
|
+
outBizNo?: string
|
|
192
|
+
buyerId: string
|
|
193
|
+
buyerLogonId: string
|
|
194
|
+
sellerId: string
|
|
195
|
+
sellerEmail: string
|
|
196
|
+
tradeStatus: string
|
|
197
|
+
totalAmount: string
|
|
198
|
+
receiptAmount: string
|
|
199
|
+
invoiceAmount: string
|
|
200
|
+
buyerPayAmount: string
|
|
201
|
+
pointAmount: string
|
|
202
|
+
refundFee: string
|
|
203
|
+
subject: string
|
|
204
|
+
body: string
|
|
205
|
+
gmtCreate: string
|
|
206
|
+
gmtPayment: string
|
|
207
|
+
gmtClose: string
|
|
208
|
+
fundBillList: string
|
|
209
|
+
passbackParams: string
|
|
210
|
+
voucherDetailList: string
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---- 通用 ----
|
|
214
|
+
export type TradeStatus =
|
|
215
|
+
| 'WAIT_BUYER_PAY'
|
|
216
|
+
| 'TRADE_CLOSED'
|
|
217
|
+
| 'TRADE_SUCCESS'
|
|
218
|
+
| 'TRADE_FINISHED'
|
|
219
|
+
|
|
220
|
+
export type AlipayKeyType = 'PKCS1' | 'PKCS8'
|
|
221
|
+
|
|
222
|
+
export interface AlipayClientOptions {
|
|
223
|
+
/** 应用 ID */
|
|
224
|
+
appId: string
|
|
225
|
+
/** 应用私钥(Node.js 使用 PKCS#1 格式) */
|
|
226
|
+
privateKey: string
|
|
227
|
+
/** 支付宝公钥 */
|
|
228
|
+
alipayPublicKey: string
|
|
229
|
+
/** 签名类型,默认 RSA2 */
|
|
230
|
+
signType?: AlipaySdkSignType
|
|
231
|
+
/** 网关地址,沙箱使用沙箱网关 */
|
|
232
|
+
gateway?: string
|
|
233
|
+
/** 异步通知地址 */
|
|
234
|
+
notifyUrl?: string
|
|
235
|
+
/** 同步跳转地址 */
|
|
236
|
+
returnUrl?: string
|
|
237
|
+
/** 私钥类型,默认 PKCS1(Node.js 使用 PKCS1) */
|
|
238
|
+
keyType?: AlipayKeyType
|
|
239
|
+
/** 请求超时时间,默认 5000ms */
|
|
240
|
+
timeout?: number
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** 电脑网站支付配置 */
|
|
244
|
+
export interface AlipayPagePayConfig {
|
|
245
|
+
/** 商户订单号 */
|
|
246
|
+
outTradeNo: string
|
|
247
|
+
/** 订单总金额,单位元 */
|
|
248
|
+
totalAmount: string
|
|
249
|
+
/** 订单标题 */
|
|
250
|
+
subject: string
|
|
251
|
+
/** 订单描述 */
|
|
252
|
+
body?: string
|
|
253
|
+
/** 公用回传参数 */
|
|
254
|
+
passbackParams?: string
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** 手机网站支付配置 */
|
|
258
|
+
export interface AlipayWapPayConfig {
|
|
259
|
+
/** 商户订单号 */
|
|
260
|
+
outTradeNo: string
|
|
261
|
+
/** 订单总金额,单位元 */
|
|
262
|
+
totalAmount: string
|
|
263
|
+
/** 订单标题 */
|
|
264
|
+
subject: string
|
|
265
|
+
/** 订单描述 */
|
|
266
|
+
body?: string
|
|
267
|
+
/** 公用回传参数 */
|
|
268
|
+
passbackParams?: string
|
|
269
|
+
/** 用户付款中途退出后返回的商户页面地址(wap 支付必填) */
|
|
270
|
+
quitUrl?: string
|
|
271
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { AlipayClient } from './client'
|
|
2
|
+
import type { AlipayWapPayConfig, AlipayWapPayParams, AlipayWapPayResult } from './types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 手机网站支付 (alipay.trade.wap.pay)
|
|
6
|
+
*
|
|
7
|
+
* 移动端浏览器内拉起支付宝收银台,生成表单 HTML 由前端提交跳转。
|
|
8
|
+
*
|
|
9
|
+
* ⚠️ 必须使用 pageExecute() 方法,禁止使用 exec()。
|
|
10
|
+
* ⚠️ 返回值为 HTML 表单字符串,前端必须渲染 form 并自动提交。
|
|
11
|
+
* ⚠️ 支付结果以异步通知或查询接口为准,不可依赖同步跳转。
|
|
12
|
+
*/
|
|
13
|
+
export function createWapPay(client: AlipayClient) {
|
|
14
|
+
return {
|
|
15
|
+
/**
|
|
16
|
+
* 发起手机网站支付
|
|
17
|
+
* @param config 支付配置
|
|
18
|
+
* @returns 支付表单 HTML
|
|
19
|
+
*/
|
|
20
|
+
async pay(config: AlipayWapPayConfig): Promise<AlipayWapPayResult> {
|
|
21
|
+
const { outTradeNo, totalAmount, subject, body, passbackParams, quitUrl } = config
|
|
22
|
+
|
|
23
|
+
const bizParams: AlipayWapPayParams = {
|
|
24
|
+
outTradeNo,
|
|
25
|
+
totalAmount,
|
|
26
|
+
subject,
|
|
27
|
+
productCode: 'QUICK_WAP_WAY',
|
|
28
|
+
body,
|
|
29
|
+
passbackParams,
|
|
30
|
+
quitUrl,
|
|
31
|
+
returnUrl: client.config.returnUrl,
|
|
32
|
+
notifyUrl: client.config.notifyUrl
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const formHtml = client.sdk.pageExecute(
|
|
36
|
+
'alipay.trade.wap.pay',
|
|
37
|
+
'POST',
|
|
38
|
+
{ bizContent: sanitizeParams(bizParams) }
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
return { formHtml }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** 移除 undefined 字段 */
|
|
47
|
+
function sanitizeParams<T extends Record<string, any>>(params: T): Record<string, any> {
|
|
48
|
+
const result: Record<string, any> = {}
|
|
49
|
+
for (const [key, value] of Object.entries(params)) {
|
|
50
|
+
if (value !== undefined && value !== null) {
|
|
51
|
+
result[key] = value
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result
|
|
55
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @vobs/payment 根入口
|
|
3
|
+
*
|
|
4
|
+
* - 通用工厂中 createRefund / createNotifyHandler 在两个提供商间同名,
|
|
5
|
+
* 统一通过 alipay.* / wechat.* 命名空间访问,避免同名不同客户端的隐式误用。
|
|
6
|
+
* - 顶层平铺导出跨提供商无歧义的符号(客户端、配置、错误类、插件、注入键,
|
|
7
|
+
* 以及名称唯一的工厂 createPagePay / createWapPay / createQuery / createPayment)。
|
|
8
|
+
*/
|
|
9
|
+
export * as alipay from './alipay'
|
|
10
|
+
export * as wechat from './wechat'
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
AlipayClient,
|
|
14
|
+
AlipayConfig,
|
|
15
|
+
AlipayApiError,
|
|
16
|
+
createPagePay,
|
|
17
|
+
createWapPay,
|
|
18
|
+
createQuery,
|
|
19
|
+
alipayPlugin,
|
|
20
|
+
useAlipay,
|
|
21
|
+
ALIPAY_KEY
|
|
22
|
+
} from './alipay'
|
|
23
|
+
export type {
|
|
24
|
+
AlipayClientOptions,
|
|
25
|
+
AlipayKeyType,
|
|
26
|
+
AlipaySdkConfig,
|
|
27
|
+
AlipaySdkSignType,
|
|
28
|
+
AlipayPagePayConfig,
|
|
29
|
+
AlipayPagePayParams,
|
|
30
|
+
AlipayPagePayResult,
|
|
31
|
+
AlipayWapPayConfig,
|
|
32
|
+
AlipayWapPayParams,
|
|
33
|
+
AlipayWapPayResult,
|
|
34
|
+
AlipayQueryParams,
|
|
35
|
+
AlipayQueryResult,
|
|
36
|
+
AlipayRefundParams,
|
|
37
|
+
AlipayRefundResult,
|
|
38
|
+
AlipayRefundQueryParams,
|
|
39
|
+
AlipayRefundQueryResult,
|
|
40
|
+
AlipayCloseParams,
|
|
41
|
+
AlipayCloseResult,
|
|
42
|
+
AlipayNotifyParams,
|
|
43
|
+
TradeStatus,
|
|
44
|
+
AlipayPluginOptions
|
|
45
|
+
} from './alipay'
|
|
46
|
+
|
|
47
|
+
export {
|
|
48
|
+
WechatClient,
|
|
49
|
+
WechatConfig,
|
|
50
|
+
WechatApiError,
|
|
51
|
+
createPayment,
|
|
52
|
+
wechatPlugin,
|
|
53
|
+
useWechat,
|
|
54
|
+
WECHAT_KEY
|
|
55
|
+
} from './wechat'
|
|
56
|
+
export type {
|
|
57
|
+
WechatClientOptions,
|
|
58
|
+
WechatPayParams,
|
|
59
|
+
WechatH5PayParams,
|
|
60
|
+
WechatJsapiPayResult,
|
|
61
|
+
WechatAppPayResult,
|
|
62
|
+
WechatNativePayResult,
|
|
63
|
+
WechatH5PayResult,
|
|
64
|
+
WechatQueryParams,
|
|
65
|
+
WechatQueryResult,
|
|
66
|
+
WechatRefundParams,
|
|
67
|
+
WechatRefundResult,
|
|
68
|
+
WechatRefundQueryParams,
|
|
69
|
+
WechatRefundAmount,
|
|
70
|
+
WechatOrderAmount,
|
|
71
|
+
WechatTradeState,
|
|
72
|
+
WechatRefundStatus,
|
|
73
|
+
WechatNotifyResource,
|
|
74
|
+
WechatNotifyHeaders,
|
|
75
|
+
WechatNotifyTransaction,
|
|
76
|
+
WechatNotifyRefund,
|
|
77
|
+
WechatPluginOptions
|
|
78
|
+
} from './wechat'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Wechatpay } from 'wechatpay-axios-plugin'
|
|
2
|
+
import { WechatConfig } from './config'
|
|
3
|
+
import type { WechatClientOptions } from './types'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 微信支付客户端封装
|
|
7
|
+
*
|
|
8
|
+
* 封装官方 wechatpay-axios-plugin 的 Wechatpay 实例,提供统一的生命周期管理。
|
|
9
|
+
* 可独立使用,也可通过 wechatPlugin 注册到 vobs 框架。
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* const client = new WechatClient({
|
|
14
|
+
* mchid: '1900000000',
|
|
15
|
+
* serial: '商户API证书序列号',
|
|
16
|
+
* privateKey: '-----BEGIN PRIVATE KEY-----...',
|
|
17
|
+
* certs: { 'PUB_KEY_ID_xxx': '-----BEGIN PUBLIC KEY-----...' },
|
|
18
|
+
* appid: 'wx1234567890',
|
|
19
|
+
* apiv3Key: '32字节APIv3密钥',
|
|
20
|
+
* notifyUrl: 'https://your-domain.com/wechat/notify'
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export class WechatClient {
|
|
25
|
+
readonly config: WechatConfig
|
|
26
|
+
private _sdk: Wechatpay
|
|
27
|
+
private _disposed = false
|
|
28
|
+
|
|
29
|
+
constructor(options: WechatClientOptions) {
|
|
30
|
+
this.config = new WechatConfig(options)
|
|
31
|
+
this._sdk = this.createSdk(options)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 获取底层 Wechatpay 实例(链式 API:client.sdk.v3.pay.transactions.jsapi.post(...)) */
|
|
35
|
+
get sdk(): Wechatpay {
|
|
36
|
+
this.ensureActive()
|
|
37
|
+
return this._sdk
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 更新配置并重建 SDK 实例 */
|
|
41
|
+
updateConfig(patch: Partial<WechatClientOptions>): void {
|
|
42
|
+
this.ensureActive()
|
|
43
|
+
this.config.update(patch)
|
|
44
|
+
this._sdk = this.createSdk(this.config.options as WechatClientOptions)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 销毁客户端 */
|
|
48
|
+
dispose(): void {
|
|
49
|
+
this._disposed = true
|
|
50
|
+
;(this._sdk as any) = null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private createSdk(options: WechatClientOptions): Wechatpay {
|
|
54
|
+
return new Wechatpay({
|
|
55
|
+
mchid: options.mchid,
|
|
56
|
+
serial: options.serial,
|
|
57
|
+
privateKey: options.privateKey,
|
|
58
|
+
certs: options.certs,
|
|
59
|
+
timeout: options.timeout ?? 5000
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private ensureActive(): void {
|
|
64
|
+
if (this._disposed) {
|
|
65
|
+
throw new Error('WechatClient: 客户端已销毁')
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { WechatClientOptions } from './types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 微信支付配置管理
|
|
5
|
+
* 遵循 vobs 生命周期管理:可创建、可校验、可更新、可替换
|
|
6
|
+
*/
|
|
7
|
+
export class WechatConfig {
|
|
8
|
+
private _options: WechatClientOptions
|
|
9
|
+
|
|
10
|
+
constructor(options: WechatClientOptions) {
|
|
11
|
+
this.validate(options)
|
|
12
|
+
this._options = { ...options }
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 获取完整配置 */
|
|
16
|
+
get options(): Readonly<WechatClientOptions> {
|
|
17
|
+
return this._options
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** 商户号 */
|
|
21
|
+
get mchid(): string {
|
|
22
|
+
return this._options.mchid
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** 应用 ID(公众号 / 小程序 / APP) */
|
|
26
|
+
get appid(): string {
|
|
27
|
+
return this._options.appid
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** 商户 API 证书序列号 */
|
|
31
|
+
get serial(): string {
|
|
32
|
+
return this._options.serial
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** APIv3 密钥(回调解密用) */
|
|
36
|
+
get apiv3Key(): string {
|
|
37
|
+
return this._options.apiv3Key
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** 异步通知地址 */
|
|
41
|
+
get notifyUrl(): string | undefined {
|
|
42
|
+
return this._options.notifyUrl
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** 更新配置(部分更新) */
|
|
46
|
+
update(patch: Partial<WechatClientOptions>): void {
|
|
47
|
+
const merged = { ...this._options, ...patch }
|
|
48
|
+
this.validate(merged)
|
|
49
|
+
this._options = merged
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** 替换配置 */
|
|
53
|
+
replace(options: WechatClientOptions): void {
|
|
54
|
+
this.validate(options)
|
|
55
|
+
this._options = { ...options }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private validate(options: WechatClientOptions): void {
|
|
59
|
+
if (!options.mchid) {
|
|
60
|
+
throw new Error('WechatConfig: mchid 不能为空')
|
|
61
|
+
}
|
|
62
|
+
if (!options.serial) {
|
|
63
|
+
throw new Error('WechatConfig: serial(商户 API 证书序列号)不能为空')
|
|
64
|
+
}
|
|
65
|
+
if (!options.privateKey) {
|
|
66
|
+
throw new Error('WechatConfig: privateKey(商户 API 私钥 PEM)不能为空')
|
|
67
|
+
}
|
|
68
|
+
if (!options.certs || Object.keys(options.certs).length === 0) {
|
|
69
|
+
throw new Error('WechatConfig: certs(平台证书/微信支付公钥 {序列号: PEM})不能为空')
|
|
70
|
+
}
|
|
71
|
+
if (!options.appid) {
|
|
72
|
+
throw new Error('WechatConfig: appid 不能为空')
|
|
73
|
+
}
|
|
74
|
+
if (!options.apiv3Key) {
|
|
75
|
+
throw new Error('WechatConfig: apiv3Key 不能为空')
|
|
76
|
+
}
|
|
77
|
+
if (options.apiv3Key.length !== 32) {
|
|
78
|
+
throw new Error('WechatConfig: apiv3Key 必须为 32 字节字符串')
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export { WechatClient } from './client'
|
|
2
|
+
export { WechatConfig } from './config'
|
|
3
|
+
export { WechatApiError } from './response'
|
|
4
|
+
export { createPayment } from './payment'
|
|
5
|
+
export { createNotifyHandler } from './notify'
|
|
6
|
+
export { createRefund } from './refund'
|
|
7
|
+
export { wechatPlugin, useWechat, WECHAT_KEY } from './plugin'
|
|
8
|
+
export type { WechatPluginOptions } from './plugin'
|
|
9
|
+
export type {
|
|
10
|
+
WechatClientOptions,
|
|
11
|
+
WechatPayParams,
|
|
12
|
+
WechatH5PayParams,
|
|
13
|
+
WechatJsapiPayResult,
|
|
14
|
+
WechatAppPayResult,
|
|
15
|
+
WechatNativePayResult,
|
|
16
|
+
WechatH5PayResult,
|
|
17
|
+
WechatQueryParams,
|
|
18
|
+
WechatQueryResult,
|
|
19
|
+
WechatRefundParams,
|
|
20
|
+
WechatRefundResult,
|
|
21
|
+
WechatRefundQueryParams,
|
|
22
|
+
WechatRefundAmount,
|
|
23
|
+
WechatOrderAmount,
|
|
24
|
+
WechatTradeState,
|
|
25
|
+
WechatRefundStatus,
|
|
26
|
+
WechatNotifyResource,
|
|
27
|
+
WechatNotifyHeaders,
|
|
28
|
+
WechatNotifyTransaction,
|
|
29
|
+
WechatNotifyRefund
|
|
30
|
+
} from './types'
|