@jayfong/x-utils 3.3.0 → 3.4.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/lib/ai.d.mts +103 -0
- package/lib/ai.mjs +75 -0
- package/lib/cache.d.mts +464 -0
- package/lib/cache.mjs +531 -0
- package/lib/captcha.d.mts +259 -0
- package/lib/captcha.mjs +174 -0
- package/lib/chinese.d.mts +19 -1
- package/lib/chinese.mjs +28 -1
- package/lib/cli.d.mts +25 -37
- package/lib/cli.mjs +25 -22
- package/lib/crawler.d.mts +1 -1
- package/lib/crawler.mjs +1 -1
- package/lib/crypto.d.mts +158 -0
- package/lib/crypto.mjs +213 -0
- package/lib/data_set.mjs +3 -3
- package/lib/db_value.d.mts +213 -0
- package/lib/db_value.mjs +208 -0
- package/lib/dispose.d.mts +98 -0
- package/lib/dispose.mjs +107 -0
- package/lib/file_store.d.mts +167 -0
- package/lib/file_store.mjs +120 -0
- package/lib/fs.d.mts +64 -2
- package/lib/fs.mjs +65 -3
- package/lib/html.d.mts +103 -0
- package/lib/html.mjs +112 -0
- package/lib/jwt.d.mts +128 -0
- package/lib/jwt.mjs +104 -0
- package/lib/mail.d.mts +125 -0
- package/lib/mail.mjs +108 -0
- package/lib/pay.d.mts +394 -0
- package/lib/pay.mjs +343 -0
- package/lib/puppeteer.mjs +3 -3
- package/lib/random.d.mts +71 -0
- package/lib/random.mjs +92 -0
- package/lib/redis.d.mts +32 -0
- package/lib/redis.mjs +24 -0
- package/lib/runner.d.mts +52 -24
- package/lib/runner.mjs +31 -15
- package/lib/sensitive_words.d.mts +82 -0
- package/lib/sensitive_words.mjs +73 -0
- package/lib/sms.d.mts +133 -0
- package/lib/sms.mjs +100 -0
- package/lib/task.d.mts +1 -1
- package/lib/task.mjs +4 -4
- package/package.json +35 -3
package/lib/mail.d.mts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { SendMailOptions, SentMessageInfo } from "nodemailer";
|
|
2
|
+
/**
|
|
3
|
+
* `XMail` 的构造配置。
|
|
4
|
+
*/
|
|
5
|
+
export interface XMailOptions {
|
|
6
|
+
/** SMTP 服务器地址,例如 `smtp.qq.com`、`smtp.163.com`。 */
|
|
7
|
+
host: string;
|
|
8
|
+
/**
|
|
9
|
+
* SMTP 服务器端口。
|
|
10
|
+
*
|
|
11
|
+
* 常见取值:
|
|
12
|
+
* - `465`:TLS 加密连接(配合 `secure: true`);
|
|
13
|
+
* - `587`:STARTTLS 升级加密(配合 `secure: false`);
|
|
14
|
+
* - `25`:明文连接,不推荐。
|
|
15
|
+
*/
|
|
16
|
+
port: number;
|
|
17
|
+
/**
|
|
18
|
+
* 是否使用 TLS 加密连接。
|
|
19
|
+
*
|
|
20
|
+
* - `true`:连接即建立 TLS 通道,通常配合端口 `465`;
|
|
21
|
+
* - `false`:先建立明文连接,再视服务器能力通过 STARTTLS 升级,
|
|
22
|
+
* 通常配合端口 `587`。
|
|
23
|
+
*/
|
|
24
|
+
secure: boolean;
|
|
25
|
+
/** SMTP 认证用户名,通常是邮箱地址。 */
|
|
26
|
+
user: string;
|
|
27
|
+
/** SMTP 认证密码,通常是邮箱登录密码或服务商签发的授权码。 */
|
|
28
|
+
pass: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 发送邮件时的邮件内容选项。
|
|
32
|
+
*
|
|
33
|
+
* 直接复用 nodemailer 的 {@link SendMailOptions},字段包括
|
|
34
|
+
* `from`、`to`、`subject`、`text`、`html`、`attachments` 等。
|
|
35
|
+
*/
|
|
36
|
+
export interface XMailSendOptions extends SendMailOptions {}
|
|
37
|
+
/**
|
|
38
|
+
* 发送邮件成功后的返回信息。
|
|
39
|
+
*
|
|
40
|
+
* 直接复用 nodemailer 的 {@link SentMessageInfo},不同 transport
|
|
41
|
+
* 返回的字段略有差异:SMTP 下通常包含 `messageId`、`accepted`、
|
|
42
|
+
* `rejected`、`response` 等。
|
|
43
|
+
*/
|
|
44
|
+
export interface XMailSendResult extends SentMessageInfo {}
|
|
45
|
+
/**
|
|
46
|
+
* 邮件发送工具。
|
|
47
|
+
*
|
|
48
|
+
* 基于 nodemailer 封装,创建实例即建立 SMTP 连接配置;首次发送前
|
|
49
|
+
* 会自动校验连接是否可用(`verify`),校验结果会被缓存,校验失败时
|
|
50
|
+
* 自动清除缓存,以便下一次发送重新校验。
|
|
51
|
+
*
|
|
52
|
+
* 实例使用完毕后建议通过 `using` 释放底层连接(也可手动调用
|
|
53
|
+
* `dispose`),避免连接泄漏。
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* using mail = new XMail({
|
|
57
|
+
* host: 'smtp.qq.com',
|
|
58
|
+
* port: 465,
|
|
59
|
+
* secure: true,
|
|
60
|
+
* user: 'you@qq.com',
|
|
61
|
+
* pass: '授权码',
|
|
62
|
+
* });
|
|
63
|
+
* await mail.send({
|
|
64
|
+
* from: 'you@qq.com',
|
|
65
|
+
* to: 'friend@example.com',
|
|
66
|
+
* subject: 'Hello',
|
|
67
|
+
* text: 'Hi!',
|
|
68
|
+
* });
|
|
69
|
+
* // 邮件已发送,作用域结束后连接自动释放
|
|
70
|
+
*/
|
|
71
|
+
export declare class XMail implements Disposable {
|
|
72
|
+
private options;
|
|
73
|
+
private transporter;
|
|
74
|
+
private disposed;
|
|
75
|
+
constructor(options: XMailOptions);
|
|
76
|
+
/**
|
|
77
|
+
* 校验 SMTP 连接是否可用。
|
|
78
|
+
*
|
|
79
|
+
* 通过 `asyncMemoize` 缓存校验结果:
|
|
80
|
+
* - 校验成功后永久缓存(直到实例被释放),后续发送不再重复校验;
|
|
81
|
+
* - 校验失败时自动清除缓存,因此失败后下一次 `send` 会重新校验,
|
|
82
|
+
* 而不是一直使用失败的缓存。
|
|
83
|
+
*/
|
|
84
|
+
private verify;
|
|
85
|
+
/**
|
|
86
|
+
* 发送邮件。
|
|
87
|
+
*
|
|
88
|
+
* 发送前会先执行连接校验(首次发送或校验缓存失效时),校验通过后
|
|
89
|
+
* 再调用 nodemailer 的 `sendMail` 完成实际发送;若实例已通过
|
|
90
|
+
* `dispose` 或 `using` 释放,则直接抛出错误。
|
|
91
|
+
*
|
|
92
|
+
* @param options 邮件内容选项,见 {@link XMailSendOptions}。
|
|
93
|
+
* @returns 发送结果,见 {@link XMailSendResult}。
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* await mail.send({
|
|
97
|
+
* from: 'you@qq.com',
|
|
98
|
+
* to: ['friend@example.com'],
|
|
99
|
+
* subject: 'Hello',
|
|
100
|
+
* html: '<p>Hi!</p>',
|
|
101
|
+
* });
|
|
102
|
+
*/
|
|
103
|
+
send(options: XMailSendOptions): Promise<XMailSendResult>;
|
|
104
|
+
/**
|
|
105
|
+
* 手动释放 SMTP 连接。
|
|
106
|
+
*
|
|
107
|
+
* 关闭底层 transporter 的连接(nodemailer 的 `close` 为同步操作);
|
|
108
|
+
* 该方法幂等,重复调用只会执行一次关闭。关闭后该实例不再可用,
|
|
109
|
+
* 如需再次发送请重新创建实例。
|
|
110
|
+
*
|
|
111
|
+
* 通常在 `using` 语法之外需要提前释放连接时手动调用。
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* const mail = new XMail(options);
|
|
115
|
+
* await mail.send({ from: 'a', to: 'b', text: 'x' });
|
|
116
|
+
* mail.dispose();
|
|
117
|
+
*/
|
|
118
|
+
dispose(): void;
|
|
119
|
+
/**
|
|
120
|
+
* 实现 `Disposable`,使实例支持 `using` 语法。
|
|
121
|
+
*
|
|
122
|
+
* 直接委托 {@link dispose} 释放连接,在作用域结束时自动调用。
|
|
123
|
+
*/
|
|
124
|
+
[Symbol.dispose](): void;
|
|
125
|
+
}
|
package/lib/mail.mjs
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { asyncMemoize } from "vtils";
|
|
2
|
+
import { createTransport } from "nodemailer";
|
|
3
|
+
// src/mail.ts
|
|
4
|
+
/**
|
|
5
|
+
* 邮件发送工具。
|
|
6
|
+
*
|
|
7
|
+
* 基于 nodemailer 封装,创建实例即建立 SMTP 连接配置;首次发送前
|
|
8
|
+
* 会自动校验连接是否可用(`verify`),校验结果会被缓存,校验失败时
|
|
9
|
+
* 自动清除缓存,以便下一次发送重新校验。
|
|
10
|
+
*
|
|
11
|
+
* 实例使用完毕后建议通过 `using` 释放底层连接(也可手动调用
|
|
12
|
+
* `dispose`),避免连接泄漏。
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* using mail = new XMail({
|
|
16
|
+
* host: 'smtp.qq.com',
|
|
17
|
+
* port: 465,
|
|
18
|
+
* secure: true,
|
|
19
|
+
* user: 'you@qq.com',
|
|
20
|
+
* pass: '授权码',
|
|
21
|
+
* });
|
|
22
|
+
* await mail.send({
|
|
23
|
+
* from: 'you@qq.com',
|
|
24
|
+
* to: 'friend@example.com',
|
|
25
|
+
* subject: 'Hello',
|
|
26
|
+
* text: 'Hi!',
|
|
27
|
+
* });
|
|
28
|
+
* // 邮件已发送,作用域结束后连接自动释放
|
|
29
|
+
*/
|
|
30
|
+
var XMail = class {
|
|
31
|
+
options;
|
|
32
|
+
transporter;
|
|
33
|
+
disposed = false;
|
|
34
|
+
constructor(options) {
|
|
35
|
+
this.options = options;
|
|
36
|
+
this.transporter = createTransport({
|
|
37
|
+
host: this.options.host,
|
|
38
|
+
port: this.options.port,
|
|
39
|
+
secure: this.options.secure,
|
|
40
|
+
auth: {
|
|
41
|
+
user: this.options.user,
|
|
42
|
+
pass: this.options.pass
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 校验 SMTP 连接是否可用。
|
|
48
|
+
*
|
|
49
|
+
* 通过 `asyncMemoize` 缓存校验结果:
|
|
50
|
+
* - 校验成功后永久缓存(直到实例被释放),后续发送不再重复校验;
|
|
51
|
+
* - 校验失败时自动清除缓存,因此失败后下一次 `send` 会重新校验,
|
|
52
|
+
* 而不是一直使用失败的缓存。
|
|
53
|
+
*/
|
|
54
|
+
verify = asyncMemoize(async () => {
|
|
55
|
+
await this.transporter.verify();
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* 发送邮件。
|
|
59
|
+
*
|
|
60
|
+
* 发送前会先执行连接校验(首次发送或校验缓存失效时),校验通过后
|
|
61
|
+
* 再调用 nodemailer 的 `sendMail` 完成实际发送;若实例已通过
|
|
62
|
+
* `dispose` 或 `using` 释放,则直接抛出错误。
|
|
63
|
+
*
|
|
64
|
+
* @param options 邮件内容选项,见 {@link XMailSendOptions}。
|
|
65
|
+
* @returns 发送结果,见 {@link XMailSendResult}。
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* await mail.send({
|
|
69
|
+
* from: 'you@qq.com',
|
|
70
|
+
* to: ['friend@example.com'],
|
|
71
|
+
* subject: 'Hello',
|
|
72
|
+
* html: '<p>Hi!</p>',
|
|
73
|
+
* });
|
|
74
|
+
*/
|
|
75
|
+
async send(options) {
|
|
76
|
+
if (this.disposed) throw new Error("邮件连接已释放,无法继续发送,请重新创建 XMail 实例");
|
|
77
|
+
await this.verify();
|
|
78
|
+
return this.transporter.sendMail(options);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 手动释放 SMTP 连接。
|
|
82
|
+
*
|
|
83
|
+
* 关闭底层 transporter 的连接(nodemailer 的 `close` 为同步操作);
|
|
84
|
+
* 该方法幂等,重复调用只会执行一次关闭。关闭后该实例不再可用,
|
|
85
|
+
* 如需再次发送请重新创建实例。
|
|
86
|
+
*
|
|
87
|
+
* 通常在 `using` 语法之外需要提前释放连接时手动调用。
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* const mail = new XMail(options);
|
|
91
|
+
* await mail.send({ from: 'a', to: 'b', text: 'x' });
|
|
92
|
+
* mail.dispose();
|
|
93
|
+
*/
|
|
94
|
+
dispose() {
|
|
95
|
+
if (this.disposed) return;
|
|
96
|
+
this.disposed = true;
|
|
97
|
+
this.transporter.close();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 实现 `Disposable`,使实例支持 `using` 语法。
|
|
101
|
+
*
|
|
102
|
+
* 直接委托 {@link dispose} 释放连接,在作用域结束时自动调用。
|
|
103
|
+
*/
|
|
104
|
+
[Symbol.dispose]() {
|
|
105
|
+
this.dispose();
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
export { XMail };
|
package/lib/pay.d.mts
ADDED
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `XPay` 的构造配置。
|
|
3
|
+
*/
|
|
4
|
+
export interface XPayOptions {
|
|
5
|
+
/**
|
|
6
|
+
* 支付宝配置。
|
|
7
|
+
*
|
|
8
|
+
* 缺省时,支付宝相关方法(`prepareAlipay`、`getOrderInfo` 等)会抛出
|
|
9
|
+
* “未配置支付宝”的错误。
|
|
10
|
+
*/
|
|
11
|
+
alipay?: {
|
|
12
|
+
/** 应用 ID(支付宝开放平台分配)。 */
|
|
13
|
+
appId: string;
|
|
14
|
+
/** 应用私钥,用于对请求进行签名。 */
|
|
15
|
+
privateKey: string;
|
|
16
|
+
/** 支付宝公钥,用于对回调通知与查询结果进行验签。 */
|
|
17
|
+
publicKey: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* 微信支付(APIv3)配置。
|
|
21
|
+
*
|
|
22
|
+
* 缺省时,微信支付相关方法(`prepareWepay`、`getOrderInfo` 等)会抛出
|
|
23
|
+
* “未配置微信支付”的错误。
|
|
24
|
+
*
|
|
25
|
+
* 说明:当前未实现微信支付回调验签,回调数据仅做 AES-GCM 解密,
|
|
26
|
+
* 验证微信支付身份时需业务方自行补充签名校验。
|
|
27
|
+
*/
|
|
28
|
+
wepay?: {
|
|
29
|
+
/** 公众号 APPID。 */
|
|
30
|
+
appId: string;
|
|
31
|
+
/** 商户 ID。 */
|
|
32
|
+
merchantId: string;
|
|
33
|
+
/** 商户 API 证书私钥,用于对请求与支付参数签名。 */
|
|
34
|
+
privateKey: string;
|
|
35
|
+
/** 商户 API 证书序列号,用于构造请求头 `serial_no`。 */
|
|
36
|
+
certificateSerialNumber: string;
|
|
37
|
+
/** APIv3 密钥(32 字节),用于解密回调数据。 */
|
|
38
|
+
secretKey: string;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* `XPay.prepareAlipay` 的入参。
|
|
43
|
+
*/
|
|
44
|
+
export interface XPayPrepareAlipayOptions {
|
|
45
|
+
/** 商品名称(订单标题)。 */
|
|
46
|
+
goodsName: string;
|
|
47
|
+
/** 商家订单号(`out_trade_no`),需保证在商户侧唯一。 */
|
|
48
|
+
tradeNumber: string;
|
|
49
|
+
/** 订单总金额,单位:元,如 `0.01` 表示 1 分钱。 */
|
|
50
|
+
totalMoney: number;
|
|
51
|
+
/** 支付宝异步通知地址(POST)。 */
|
|
52
|
+
notifyUrl: string;
|
|
53
|
+
/** 支付完成后同步跳转地址,缺省时不传入支付宝。 */
|
|
54
|
+
returnUrl?: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `XPay.prepareAlipay` 的返回结果。
|
|
58
|
+
*/
|
|
59
|
+
export interface XPayPrepareAlipayResult {
|
|
60
|
+
/** 支付地址,需跳转到该地址完成支付。 */
|
|
61
|
+
payUrl: string;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* `XPay.prepareWepay` 的入参。
|
|
65
|
+
*/
|
|
66
|
+
export interface XPayPrepareWepayOptions {
|
|
67
|
+
/** 商品名称(订单描述)。 */
|
|
68
|
+
goodsName: string;
|
|
69
|
+
/** 商家订单号(`out_trade_no`)。 */
|
|
70
|
+
tradeNumber: string;
|
|
71
|
+
/** 订单总金额,单位:元,内部会转换为分(乘以 100 后四舍五入)。 */
|
|
72
|
+
totalMoney: number;
|
|
73
|
+
/** 微信支付异步通知地址(POST)。 */
|
|
74
|
+
notifyUrl: string;
|
|
75
|
+
/** 公众号用户的 openid。 */
|
|
76
|
+
openid: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* 微信支付 JSSDK 支付参数。
|
|
80
|
+
*
|
|
81
|
+
* 对应公众号 JSAPI 支付 `wx.requestPayment` 所需的参数对象。
|
|
82
|
+
*/
|
|
83
|
+
export interface XPayWepayPayParams {
|
|
84
|
+
/** 公众号 APPID。 */
|
|
85
|
+
appId: string;
|
|
86
|
+
/** 生成签名时的时间戳,单位:秒。 */
|
|
87
|
+
timestamp: string;
|
|
88
|
+
/** 随机字符串。 */
|
|
89
|
+
nonceStr: string;
|
|
90
|
+
/** 预支付标识,格式为 `prepay_id=...`。 */
|
|
91
|
+
package: string;
|
|
92
|
+
/** 签名类型,固定为 `RSA`。 */
|
|
93
|
+
signType: string;
|
|
94
|
+
/** 支付签名。 */
|
|
95
|
+
paySign: string;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* `XPay.prepareWepay` 的返回结果。
|
|
99
|
+
*/
|
|
100
|
+
export interface XPayPrepareWepayResult {
|
|
101
|
+
/** 支付参数,可直接传给 JSSDK 的 `wx.requestPayment` 调用。 */
|
|
102
|
+
payParams: XPayWepayPayParams;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* `XPay.verifyNotifyData` 的返回结果。
|
|
106
|
+
*/
|
|
107
|
+
export interface XPayVerifyNotifyDataResult {
|
|
108
|
+
/**
|
|
109
|
+
* 渠道订单号。
|
|
110
|
+
*
|
|
111
|
+
* - 支付宝最长 64 位;
|
|
112
|
+
* - 微信支付最长 32 位。
|
|
113
|
+
*/
|
|
114
|
+
channelOrderNo: string;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* `XPay.getOrderInfo` 的入参。
|
|
118
|
+
*/
|
|
119
|
+
export interface XPayGetOrderInfoPayload {
|
|
120
|
+
/** 渠道:`alipay` 支付宝、`wepay` 微信支付。 */
|
|
121
|
+
channel: 'alipay' | 'wepay';
|
|
122
|
+
/** 商家订单号;与 `channelOrderNo` 二选一,两者都传时优先使用它。 */
|
|
123
|
+
orderNo?: string;
|
|
124
|
+
/** 渠道订单号;未传 `orderNo` 时使用。 */
|
|
125
|
+
channelOrderNo?: string;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* `XPay.getOrderInfo` 的返回结果。
|
|
129
|
+
*/
|
|
130
|
+
export interface XPayGetOrderInfoResult {
|
|
131
|
+
/** 商家订单号。 */
|
|
132
|
+
orderNo: string;
|
|
133
|
+
/**
|
|
134
|
+
* 渠道订单号。
|
|
135
|
+
*
|
|
136
|
+
* - 支付宝最长 64 位;
|
|
137
|
+
* - 微信支付最长 32 位。
|
|
138
|
+
*/
|
|
139
|
+
channelOrderNo: string;
|
|
140
|
+
/** 订单状态:`success` 支付成功、`fail` 未支付或支付失败。 */
|
|
141
|
+
status: 'success' | 'fail';
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 微信支付回调通知中的 `resource` 字段(AES-GCM 加密数据)。
|
|
145
|
+
*
|
|
146
|
+
* @see https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_5.shtml
|
|
147
|
+
*/
|
|
148
|
+
export interface XPayWepayResource {
|
|
149
|
+
/** 原始回调类型,如 `transaction`。 */
|
|
150
|
+
original_type?: string;
|
|
151
|
+
/** 加密算法类型,如 `AEAD_AES_256_GCM`。 */
|
|
152
|
+
algorithm: string;
|
|
153
|
+
/** 附加数据,用于校验密文完整性,可能为空字符串。 */
|
|
154
|
+
associated_data?: string;
|
|
155
|
+
/** 密文内容(base64)。 */
|
|
156
|
+
ciphertext: string;
|
|
157
|
+
/** 随机串,作为 AES-GCM 解密的 IV。 */
|
|
158
|
+
nonce: string;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 微信支付回调数据解密后的内容(本工具用到的字段)。
|
|
162
|
+
*/
|
|
163
|
+
export interface XPayWepayNotifyData {
|
|
164
|
+
/** 附加数据,用于标记支付渠道,本工具使用 `wepay`。 */
|
|
165
|
+
attach?: string;
|
|
166
|
+
/** 微信支付订单号。 */
|
|
167
|
+
transaction_id?: string;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* 微信支付 JSAPI 下单的响应。
|
|
171
|
+
*/
|
|
172
|
+
export interface XPayWepayPrepayResult {
|
|
173
|
+
/** 预支付交易会话标识。 */
|
|
174
|
+
prepay_id?: string;
|
|
175
|
+
/** 错误描述,下单失败时返回。 */
|
|
176
|
+
message?: string;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* 微信支付订单查询的响应(本工具用到的字段)。
|
|
180
|
+
*/
|
|
181
|
+
export interface XPayWepayOrderInfoData {
|
|
182
|
+
/** 交易状态,如 `SUCCESS`、`NOTPAY`、`CLOSED`、`REVOKED`、`PAYERROR` 等。 */
|
|
183
|
+
trade_state: string;
|
|
184
|
+
/** 商家订单号。 */
|
|
185
|
+
out_trade_no: string;
|
|
186
|
+
/** 微信支付订单号。 */
|
|
187
|
+
transaction_id: string;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* 支付宝订单查询的响应(本工具用到的字段)。
|
|
191
|
+
*
|
|
192
|
+
* 支付宝 SDK 默认开启驼峰转换,因此字段为驼峰命名。
|
|
193
|
+
*/
|
|
194
|
+
export interface XPayAlipayOrderInfoData {
|
|
195
|
+
/** 支付宝交易号。 */
|
|
196
|
+
tradeNo: string;
|
|
197
|
+
/** 商家订单号。 */
|
|
198
|
+
outTradeNo: string;
|
|
199
|
+
/** 交易状态,如 `TRADE_SUCCESS`、`TRADE_FINISHED`、`WAIT_BUYER_PAY` 等。 */
|
|
200
|
+
tradeStatus: string;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 支付工具。
|
|
204
|
+
*
|
|
205
|
+
* 封装支付宝(网页支付)与微信支付(JSAPI 支付)的下单、回调验签与
|
|
206
|
+
* 订单查询能力:
|
|
207
|
+
* - 支付宝:`prepareAlipay` 生成支付链接、`getOrderInfo` 查询订单;
|
|
208
|
+
* - 微信支付:`prepareWepay` 生成 JSSDK 支付参数、`getOrderInfo` 查询订单;
|
|
209
|
+
* - 回调:`verifyNotifyData` 统一校验并解析两个渠道的回调数据。
|
|
210
|
+
*
|
|
211
|
+
* 支付宝 SDK 按需惰性创建,只有首次使用支付宝能力时才初始化。
|
|
212
|
+
* 注意:微信支付回调目前只做 AES-GCM 解密,未实现签名验签,
|
|
213
|
+
* 业务方如需严格校验身份,请自行补充。
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* const pay = new XPay({
|
|
217
|
+
* alipay: {
|
|
218
|
+
* appId: '2021000000000000',
|
|
219
|
+
* privateKey: '应用私钥',
|
|
220
|
+
* publicKey: '支付宝公钥',
|
|
221
|
+
* },
|
|
222
|
+
* wepay: {
|
|
223
|
+
* appId: 'wx1234567890',
|
|
224
|
+
* merchantId: '1234567890',
|
|
225
|
+
* privateKey: '商户 API 证书私钥',
|
|
226
|
+
* certificateSerialNumber: '证书序列号',
|
|
227
|
+
* secretKey: '32 字节 APIv3 密钥',
|
|
228
|
+
* },
|
|
229
|
+
* });
|
|
230
|
+
*
|
|
231
|
+
* const { payUrl } = await pay.prepareAlipay({
|
|
232
|
+
* goodsName: '测试商品',
|
|
233
|
+
* tradeNumber: 'T20260101001',
|
|
234
|
+
* totalMoney: 0.01,
|
|
235
|
+
* notifyUrl: 'https://example.com/notify/alipay',
|
|
236
|
+
* });
|
|
237
|
+
* // 跳转到 payUrl 完成支付
|
|
238
|
+
*/
|
|
239
|
+
export declare class XPay {
|
|
240
|
+
private options;
|
|
241
|
+
private alipaySdk;
|
|
242
|
+
constructor(options: XPayOptions);
|
|
243
|
+
/**
|
|
244
|
+
* 支付宝网页支付下单,生成支付链接。
|
|
245
|
+
*
|
|
246
|
+
* 调用支付宝 `alipay.trade.page.pay` 接口生成 GET 形式的支付地址,
|
|
247
|
+
* 用户跳转到该地址即可在支付宝收银台完成支付。订单金额会格式化为
|
|
248
|
+
* 两位小数字符串,并通过 `passback_params` 标记渠道为 `alipay`,
|
|
249
|
+
* 以便回调时识别。
|
|
250
|
+
*
|
|
251
|
+
* @param options 下单参数,见 {@link XPayPrepareAlipayOptions}。
|
|
252
|
+
* @returns 支付地址,见 {@link XPayPrepareAlipayResult}。
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* const { payUrl } = await pay.prepareAlipay({
|
|
256
|
+
* goodsName: '测试商品',
|
|
257
|
+
* tradeNumber: 'T20260101001',
|
|
258
|
+
* totalMoney: 0.01,
|
|
259
|
+
* notifyUrl: 'https://example.com/notify/alipay',
|
|
260
|
+
* });
|
|
261
|
+
*/
|
|
262
|
+
prepareAlipay(options: XPayPrepareAlipayOptions): Promise<XPayPrepareAlipayResult>;
|
|
263
|
+
/**
|
|
264
|
+
* 微信支付 JSAPI 下单,生成 JSSDK 支付参数。
|
|
265
|
+
*
|
|
266
|
+
* 调用微信支付 `v3/pay/transactions/jsapi` 接口获取 `prepay_id`,
|
|
267
|
+
* 再按 JSSDK 规则签名得到支付参数,可直接传给前端
|
|
268
|
+
* `wx.requestPayment`。金额由元转换为分(乘以 100 后四舍五入),
|
|
269
|
+
* 避免浮点误差;下单请求会通过 `attach` 字段标记渠道为 `wepay`。
|
|
270
|
+
*
|
|
271
|
+
* @param options 下单参数,见 {@link XPayPrepareWepayOptions}。
|
|
272
|
+
* @returns 支付参数,见 {@link XPayPrepareWepayResult}。
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* const { payParams } = await pay.prepareWepay({
|
|
276
|
+
* goodsName: '测试商品',
|
|
277
|
+
* tradeNumber: 'T20260101002',
|
|
278
|
+
* totalMoney: 0.01,
|
|
279
|
+
* notifyUrl: 'https://example.com/notify/wepay',
|
|
280
|
+
* openid: 'oABC123',
|
|
281
|
+
* });
|
|
282
|
+
*/
|
|
283
|
+
prepareWepay(options: XPayPrepareWepayOptions): Promise<XPayPrepareWepayResult>;
|
|
284
|
+
/**
|
|
285
|
+
* 校验并解析支付回调通知数据。
|
|
286
|
+
*
|
|
287
|
+
* 先判断数据是否为对象,再按渠道标记识别:
|
|
288
|
+
* - 支付宝:当 `passback_params` 为 `alipay` 时,调用支付宝 SDK 验签,
|
|
289
|
+
* 验签通过后返回支付宝交易号;
|
|
290
|
+
* - 微信支付:当存在 `resource` 字段时,先 AES-GCM 解密,再校验
|
|
291
|
+
* `attach` 是否为 `wepay`,通过后返回微信支付订单号。
|
|
292
|
+
*
|
|
293
|
+
* 任何一步失败(数据非法、未配置渠道、验签失败、解密失败)都会
|
|
294
|
+
* 返回 `false`,不会抛出异常。
|
|
295
|
+
*
|
|
296
|
+
* @param data 支付平台 POST 过来的回调数据(支付宝为表单 JSON,
|
|
297
|
+
* 微信支付为通知报文 JSON)。
|
|
298
|
+
* @returns 校验通过时返回渠道订单号,否则返回 `false`。
|
|
299
|
+
*
|
|
300
|
+
* @example
|
|
301
|
+
* const res = pay.verifyNotifyData(req.body);
|
|
302
|
+
* if (res) {
|
|
303
|
+
* // 校验通过,res.channelOrderNo 为渠道订单号
|
|
304
|
+
* }
|
|
305
|
+
*/
|
|
306
|
+
verifyNotifyData(data: any): false | XPayVerifyNotifyDataResult;
|
|
307
|
+
/**
|
|
308
|
+
* 校验并解析支付回调通知数据,失败时抛出错误。
|
|
309
|
+
*
|
|
310
|
+
* 行为与 {@link verifyNotifyData} 一致,区别在于校验失败时抛出
|
|
311
|
+
* `Error`,便于在需要强制校验的流程中使用。
|
|
312
|
+
*
|
|
313
|
+
* @param data 支付平台 POST 过来的回调数据。
|
|
314
|
+
* @param message 校验失败时抛出的错误信息。
|
|
315
|
+
* @returns 校验通过时的渠道订单号。
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* const res = pay.verifyNotifyDataOrFail(req.body, '回调验签失败');
|
|
319
|
+
*/
|
|
320
|
+
verifyNotifyDataOrFail(data: any, message?: string): XPayVerifyNotifyDataResult;
|
|
321
|
+
/**
|
|
322
|
+
* 查询订单状态。
|
|
323
|
+
*
|
|
324
|
+
* 按渠道分别调用微信支付订单查询接口或支付宝 `alipay.trade.query`:
|
|
325
|
+
* - 优先使用 `orderNo`(商家订单号)查询,未提供时使用
|
|
326
|
+
* `channelOrderNo`(渠道订单号);
|
|
327
|
+
* - 微信支付 `trade_state` 为 `SUCCESS` 时视为成功;
|
|
328
|
+
* - 支付宝 `trade_status` 为 `TRADE_SUCCESS` 或 `TRADE_FINISHED` 时
|
|
329
|
+
* 视为成功,其余状态视为失败。
|
|
330
|
+
*
|
|
331
|
+
* @param payload 查询条件,见 {@link XPayGetOrderInfoPayload}。
|
|
332
|
+
* @returns 订单信息,见 {@link XPayGetOrderInfoResult}。
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* await pay.getOrderInfo({ channel: 'alipay', orderNo: 'T20260101001' });
|
|
336
|
+
* // { channelOrderNo: '2026...', orderNo: 'T20260101001', status: 'success' }
|
|
337
|
+
*/
|
|
338
|
+
getOrderInfo(payload: XPayGetOrderInfoPayload): Promise<XPayGetOrderInfoResult>;
|
|
339
|
+
/**
|
|
340
|
+
* 获取(并按需创建)支付宝 SDK 实例。
|
|
341
|
+
*
|
|
342
|
+
* 首次调用时才初始化 SDK,后续复用同一实例;未配置支付宝时抛出
|
|
343
|
+
* 明确错误。
|
|
344
|
+
*/
|
|
345
|
+
private getAlipaySdk;
|
|
346
|
+
/**
|
|
347
|
+
* 获取支付宝配置,未配置时抛出错误。
|
|
348
|
+
*/
|
|
349
|
+
private getAlipayConfig;
|
|
350
|
+
/**
|
|
351
|
+
* 获取微信支付配置,未配置时抛出错误。
|
|
352
|
+
*/
|
|
353
|
+
private getWepayConfig;
|
|
354
|
+
/**
|
|
355
|
+
* 生成随机字符串,用于微信支付请求的 `nonce_str` 与支付参数。
|
|
356
|
+
*
|
|
357
|
+
* 使用密码学安全的随机字节生成 12 位十六进制字符串。
|
|
358
|
+
*/
|
|
359
|
+
private generateNonceStr;
|
|
360
|
+
/**
|
|
361
|
+
* 微信支付 RSA 签名。
|
|
362
|
+
*
|
|
363
|
+
* 将每个字段拼接换行符(`\n`)后使用 `RSA-SHA256` 私钥签名,返回
|
|
364
|
+
* base64 编码的签名;同一方法同时用于请求头签名与 JSSDK 支付参数
|
|
365
|
+
* 签名。
|
|
366
|
+
*
|
|
367
|
+
* @param data 待签名字段,按顺序依次拼接,每个字段后追加换行符。
|
|
368
|
+
* @returns base64 编码的签名串。
|
|
369
|
+
*/
|
|
370
|
+
private wepaySign;
|
|
371
|
+
/**
|
|
372
|
+
* 微信支付回调数据解密。
|
|
373
|
+
*
|
|
374
|
+
* 使用 APIv3 密钥对 `resource` 做 AES-256-GCM 解密:密文末尾 16
|
|
375
|
+
* 字节为认证标签,前面为加密数据,`nonce` 作为 IV,
|
|
376
|
+
* `associated_data` 作为附加认证数据。
|
|
377
|
+
*
|
|
378
|
+
* @param resource 回调数据中的 `resource` 字段。
|
|
379
|
+
* @returns 解密后的 JSON 字符串。
|
|
380
|
+
*/
|
|
381
|
+
private wepayDecrypt;
|
|
382
|
+
/**
|
|
383
|
+
* 发起微信支付 APIv3 请求。
|
|
384
|
+
*
|
|
385
|
+
* 根据 URL 计算请求路径,按 `方法\nURL\n时间戳\n随机串\n请求体\n`
|
|
386
|
+
* 构造签名并生成 `Authorization` 请求头;GET 请求体为空字符串,
|
|
387
|
+
* POST 请求体为 `data` 的 JSON 字符串。
|
|
388
|
+
*
|
|
389
|
+
* @param url 完整请求地址。
|
|
390
|
+
* @param data 请求体,缺省表示 GET 请求。
|
|
391
|
+
* @returns 响应数据。
|
|
392
|
+
*/
|
|
393
|
+
private wepayRequest;
|
|
394
|
+
}
|