@yunzhanghu/sdk-nodejs 1.0.2 → 1.0.4
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/.editorconfig +8 -0
- package/.eslintrc.js +16 -0
- package/.prettierrc.js +3 -0
- package/README.md +23 -6
- package/example/apiUserSign.js +66 -12
- package/example/authentication.js +136 -25
- package/example/bizlicXjjH5.js +33 -7
- package/example/bizlicXjjH5Api.js +50 -10
- package/example/conf/config.js +2 -0
- package/example/dataService.js +105 -8
- package/example/h5UserSign.js +65 -10
- package/example/invoice.js +111 -14
- package/example/notify.js +0 -1
- package/example/payment.js +208 -26
- package/example/tax.js +31 -3
- package/example/uploadusersign.js +66 -0
- package/package.json +50 -45
- package/src/common/client.ts +364 -355
- package/src/common/exception/yzhSDKHttpException.ts +29 -26
- package/src/common/http/index.ts +62 -53
- package/src/common/utils/index.ts +16 -14
- package/src/index.ts +1 -1
- package/src/services/apiusersign/index.ts +107 -105
- package/src/services/authentication/index.ts +162 -196
- package/src/services/bizlicxjjh5/index.ts +77 -107
- package/src/services/bizlicxjjh5api/index.ts +108 -156
- package/src/services/dataservice/index.ts +246 -268
- package/src/services/h5usersign/index.ts +93 -105
- package/src/services/index.ts +11 -10
- package/src/services/invoice/index.ts +184 -244
- package/src/services/payment/index.ts +501 -435
- package/src/services/tax/index.ts +53 -51
- package/src/services/uploadusersign/index.ts +88 -0
- package/src/typings.d.ts +1 -1
- package/tdsformat.js +22 -0
- package/tsconfig.json +2 -1
- package/yzh/common/client.d.ts +3 -0
- package/yzh/common/client.js +9 -3
- package/yzh/common/http/index.d.ts +1 -0
- package/yzh/common/http/index.js +18 -8
- package/yzh/common/utils/index.d.ts +1 -0
- package/yzh/common/utils/index.js +1 -0
- package/yzh/services/apiusersign/index.d.ts +34 -7
- package/yzh/services/apiusersign/index.js +9 -8
- package/yzh/services/authentication/index.d.ts +57 -19
- package/yzh/services/authentication/index.js +12 -11
- package/yzh/services/bizlicxjjh5/index.d.ts +16 -4
- package/yzh/services/bizlicxjjh5/index.js +6 -5
- package/yzh/services/bizlicxjjh5api/index.d.ts +22 -5
- package/yzh/services/bizlicxjjh5api/index.js +7 -6
- package/yzh/services/dataservice/index.d.ts +52 -11
- package/yzh/services/dataservice/index.js +23 -10
- package/yzh/services/h5usersign/index.d.ts +28 -6
- package/yzh/services/h5usersign/index.js +8 -7
- package/yzh/services/index.d.ts +3 -2
- package/yzh/services/index.js +7 -5
- package/yzh/services/invoice/index.d.ts +51 -12
- package/yzh/services/invoice/index.js +11 -10
- package/yzh/services/payment/index.d.ts +194 -18
- package/yzh/services/payment/index.js +28 -13
- package/yzh/services/tax/index.d.ts +17 -4
- package/yzh/services/tax/index.js +6 -5
- package/yzh/services/uploadusersign/index.d.ts +78 -0
- package/yzh/services/uploadusersign/index.js +19 -0
- package/.eslintrc.json +0 -19
- package/prettier.config.js +0 -32
package/src/common/client.ts
CHANGED
|
@@ -1,368 +1,377 @@
|
|
|
1
|
-
|
|
2
|
-
import * as crypto from
|
|
3
|
-
import YZHSDKHttpException from "./exception/yzhSDKHttpException"
|
|
4
|
-
const clearEncoding = "utf8"
|
|
5
|
-
const cipherEncoding = "base64"
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
import * as crypto from 'crypto';
|
|
6
3
|
|
|
7
|
-
|
|
4
|
+
import getInstance from '../common/http';
|
|
5
|
+
import YZHSDKHttpException from './exception/yzhSDKHttpException';
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
const clearEncoding = 'utf8';
|
|
8
|
+
const cipherEncoding = 'base64';
|
|
9
|
+
|
|
10
|
+
export type ResponseCallback<TReuslt = any> = (error: null | string, rep: TReuslt) => void;
|
|
11
|
+
|
|
12
|
+
type ResponseData = any;
|
|
10
13
|
|
|
11
14
|
export class YZHClient {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// 基础请求:进行请求实例生成 Header,动态设置、请求体包装等偏底层操作
|
|
70
|
-
private doRequest(method: string, action: string, req: any): Promise<ResponseData> {
|
|
71
|
-
const { request_id, ...resReq } = req
|
|
72
|
-
// 请求参数加密
|
|
73
|
-
const encryptParams = this.generatorRequestParams(resReq)
|
|
74
|
-
// 生成请求实例,配置 Header
|
|
75
|
-
const instance = getInstance({
|
|
76
|
-
request_id: request_id ?? this.mess(),
|
|
77
|
-
dealer_id: this.dealer_id,
|
|
78
|
-
base_url: this.base_url,
|
|
79
|
-
})
|
|
80
|
-
// 返回请求实例
|
|
81
|
-
const baseInstanceConf = { method: method, url: action }
|
|
82
|
-
let instanceConf
|
|
83
|
-
if (method === "get") {
|
|
84
|
-
instanceConf = { ...baseInstanceConf, params: encryptParams }
|
|
85
|
-
} else {
|
|
86
|
-
instanceConf = { ...baseInstanceConf, data: encryptParams }
|
|
87
|
-
}
|
|
88
|
-
return instance(instanceConf)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// 公共请求:调用封装好的基础请求方法 doRequest,进行发送请求与响应内容处理
|
|
92
|
-
async request(
|
|
93
|
-
method: string,
|
|
94
|
-
action: string,
|
|
95
|
-
req?: any,
|
|
96
|
-
options?: { encryption: boolean },
|
|
97
|
-
cb?: ResponseCallback
|
|
98
|
-
): Promise<ResponseData> {
|
|
99
|
-
if (typeof options === "function") {
|
|
100
|
-
cb = options
|
|
101
|
-
options = {} as any
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
const result = await this.doRequest(method, action, req ?? {})
|
|
105
|
-
// 错误码处理 > 验签 > 解密
|
|
106
|
-
const responseData = await this.parseResponse(result, options?.encryption)
|
|
107
|
-
cb && cb(null, responseData)
|
|
108
|
-
|
|
109
|
-
return responseData
|
|
110
|
-
} catch (e) {
|
|
111
|
-
if (cb) {
|
|
112
|
-
cb(e as any, null)
|
|
113
|
-
} else {
|
|
114
|
-
throw e
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* 请求参数加密
|
|
121
|
-
* @param {object} params
|
|
122
|
-
* @returns {*} object
|
|
123
|
-
*/
|
|
124
|
-
private generatorRequestParams(params: string) {
|
|
125
|
-
try {
|
|
126
|
-
const t = Date.now().toString()
|
|
127
|
-
const m = this.mess()
|
|
128
|
-
|
|
129
|
-
const plaintext = JSON.stringify(params)
|
|
130
|
-
|
|
131
|
-
const data = this.encrypt(plaintext)
|
|
132
|
-
|
|
133
|
-
const signStr = this.sign(data, m, t)
|
|
134
|
-
return {
|
|
135
|
-
data,
|
|
136
|
-
mess: m,
|
|
137
|
-
timestamp: t,
|
|
138
|
-
sign: signStr,
|
|
139
|
-
sign_type: this.sign_type,
|
|
140
|
-
}
|
|
141
|
-
} catch (err) {
|
|
142
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* 生成签名(RSA 签名算法)
|
|
148
|
-
* @param {string} data 经过加密后的具体数据
|
|
149
|
-
* @param {string} mess 自定义随机字符串,用于签名
|
|
150
|
-
* @param {string} timestamp 时间戳,精确到秒
|
|
151
|
-
* @returns {string} 签名内容
|
|
152
|
-
*/
|
|
153
|
-
private signRSASHA256 = (data: string, mess: string, timestamp: string) => {
|
|
154
|
-
try {
|
|
155
|
-
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`
|
|
156
|
-
const sign = crypto.createSign("RSA-SHA256")
|
|
157
|
-
sign.update(plaintext)
|
|
158
|
-
sign.end()
|
|
159
|
-
return sign.sign(this.private_key, cipherEncoding)
|
|
160
|
-
} catch (err) {
|
|
161
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
15
|
+
public dealer_id: string;
|
|
16
|
+
|
|
17
|
+
public broker_id: string;
|
|
18
|
+
|
|
19
|
+
public app_key: string;
|
|
20
|
+
|
|
21
|
+
public des3_key: string;
|
|
22
|
+
|
|
23
|
+
public private_key: string;
|
|
24
|
+
|
|
25
|
+
public yzh_public_key: string;
|
|
26
|
+
|
|
27
|
+
public sign_type: 'rsa' | 'sha256';
|
|
28
|
+
|
|
29
|
+
public base_url?: string;
|
|
30
|
+
|
|
31
|
+
public timeout?: number;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 构造函数参数
|
|
35
|
+
* @param {string} dealer_id 平台企业 ID
|
|
36
|
+
* @param {string} broker_id 综合服务主体 ID
|
|
37
|
+
* @param {string} app_key App Key
|
|
38
|
+
* @param {string} des3_key 3DES Key
|
|
39
|
+
* @param {string} private_key 平台企业私钥
|
|
40
|
+
* @param {string} yzh_public_key 云账户公钥
|
|
41
|
+
* @param {string} sign_type 签名算法,支持 RSA、HMAC,枚举分别为 rsa、sha256
|
|
42
|
+
* @param {string} base_url 可选,默认为 https://api-service.yunzhanghu.com/
|
|
43
|
+
* @param {number} timeout 请求超时时间。可选,默认30*1000ms。0为永不超时。
|
|
44
|
+
*/
|
|
45
|
+
constructor(conf: {
|
|
46
|
+
dealer_id: string;
|
|
47
|
+
broker_id: string;
|
|
48
|
+
app_key: string;
|
|
49
|
+
des3_key: string;
|
|
50
|
+
private_key: string;
|
|
51
|
+
yzh_public_key: string;
|
|
52
|
+
sign_type: 'rsa' | 'sha256';
|
|
53
|
+
base_url?: string;
|
|
54
|
+
timeout?: number;
|
|
55
|
+
}) {
|
|
56
|
+
const { dealer_id, broker_id, app_key, des3_key, private_key, yzh_public_key, sign_type } = conf || {};
|
|
57
|
+
if (conf && dealer_id && broker_id && app_key && des3_key && private_key && yzh_public_key && sign_type) {
|
|
58
|
+
this.dealer_id = conf.dealer_id;
|
|
59
|
+
this.broker_id = conf.broker_id;
|
|
60
|
+
this.app_key = conf.app_key;
|
|
61
|
+
this.des3_key = conf.des3_key;
|
|
62
|
+
this.private_key = conf.private_key;
|
|
63
|
+
this.yzh_public_key = conf.yzh_public_key;
|
|
64
|
+
this.sign_type = conf.sign_type;
|
|
65
|
+
this.base_url = conf?.base_url;
|
|
66
|
+
this.timeout = conf?.timeout;
|
|
67
|
+
} else {
|
|
68
|
+
throw new YZHSDKHttpException(
|
|
69
|
+
`实例初始化失败,请检查以下配置是否缺失:\ndealer_id、broker_id、app_key、des3_key、private_key、yzh_public_key、sign_type`
|
|
70
|
+
);
|
|
71
|
+
}
|
|
162
72
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
73
|
+
|
|
74
|
+
// 基础请求:进行请求实例生成 Header,动态设置、请求体包装等偏底层操作
|
|
75
|
+
private doRequest(method: string, action: string, req: any): Promise<ResponseData> {
|
|
76
|
+
const { request_id, ...resReq } = req;
|
|
77
|
+
// 请求参数加密
|
|
78
|
+
const encryptParams = this.generatorRequestParams(resReq);
|
|
79
|
+
// 生成请求实例,配置 Header
|
|
80
|
+
const instance = getInstance({
|
|
81
|
+
request_id: request_id ?? this.mess(),
|
|
82
|
+
dealer_id: this.dealer_id,
|
|
83
|
+
base_url: this.base_url,
|
|
84
|
+
timeout: this.timeout,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// 返回请求实例
|
|
88
|
+
const baseInstanceConf = { method, url: action };
|
|
89
|
+
let instanceConf;
|
|
90
|
+
if (method === 'get') {
|
|
91
|
+
instanceConf = { ...baseInstanceConf, params: encryptParams };
|
|
92
|
+
} else {
|
|
93
|
+
instanceConf = { ...baseInstanceConf, data: encryptParams };
|
|
94
|
+
}
|
|
95
|
+
return instance(instanceConf);
|
|
180
96
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
switch (this.sign_type) {
|
|
194
|
-
case "rsa": {
|
|
195
|
-
return this.signRSASHA256(data, mess, timestamp)
|
|
97
|
+
|
|
98
|
+
// 公共请求:调用封装好的基础请求方法 doRequest,进行发送请求与响应内容处理
|
|
99
|
+
async request(
|
|
100
|
+
method: string,
|
|
101
|
+
action: string,
|
|
102
|
+
req?: any,
|
|
103
|
+
options?: { encryption: boolean },
|
|
104
|
+
cb?: ResponseCallback
|
|
105
|
+
): Promise<ResponseData> {
|
|
106
|
+
if (typeof options === 'function') {
|
|
107
|
+
cb = options;
|
|
108
|
+
options = {} as any;
|
|
196
109
|
}
|
|
197
|
-
|
|
198
|
-
|
|
110
|
+
try {
|
|
111
|
+
const result = await this.doRequest(method, action, req ?? {});
|
|
112
|
+
// 错误码处理 > 验签 > 解密
|
|
113
|
+
const responseData = await this.parseResponse(result, options?.encryption);
|
|
114
|
+
cb && cb(null, responseData);
|
|
115
|
+
|
|
116
|
+
return responseData;
|
|
117
|
+
} catch (e) {
|
|
118
|
+
if (cb) {
|
|
119
|
+
cb(e as any, null);
|
|
120
|
+
} else {
|
|
121
|
+
throw e;
|
|
122
|
+
}
|
|
199
123
|
}
|
|
200
|
-
default:
|
|
201
|
-
throw new YZHSDKHttpException(`sign_type类型不存在`)
|
|
202
|
-
}
|
|
203
|
-
} catch (err) {
|
|
204
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// 自定义随机字符串
|
|
209
|
-
private mess = () => {
|
|
210
|
-
const buf = crypto.randomBytes(16)
|
|
211
|
-
const token = buf.toString("hex")
|
|
212
|
-
return token.toString()
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* 3DES 加密数据
|
|
217
|
-
* @param plaintext
|
|
218
|
-
* @returns 字符串加密数据
|
|
219
|
-
*/
|
|
220
|
-
private encrypt = (plaintext: string) => {
|
|
221
|
-
try {
|
|
222
|
-
const iv = this.des3_key.slice(0, 8)
|
|
223
|
-
const cipherChunks = []
|
|
224
|
-
const cipher = crypto.createCipheriv("des-ede3-cbc", this.des3_key, iv)
|
|
225
|
-
cipher.setAutoPadding(true)
|
|
226
|
-
cipherChunks.push(cipher.update(plaintext, clearEncoding, cipherEncoding))
|
|
227
|
-
cipherChunks.push(cipher.final(cipherEncoding))
|
|
228
|
-
|
|
229
|
-
return cipherChunks.join("")
|
|
230
|
-
} catch (err) {
|
|
231
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// 返回处理结果
|
|
236
|
-
private async parseResponse(result: ResponseData, encryption?: boolean) {
|
|
237
|
-
if (result.status !== 200) {
|
|
238
|
-
const yzhError = new YZHSDKHttpException(result.statusText)
|
|
239
|
-
yzhError.httpCode = result.status
|
|
240
|
-
throw yzhError
|
|
241
|
-
} else {
|
|
242
|
-
// HTTP Status Code 200
|
|
243
|
-
const { data: axiosData } = result
|
|
244
|
-
let response = axiosData
|
|
245
|
-
// 需解密
|
|
246
|
-
if (encryption) {
|
|
247
|
-
response = { ...response, data: this.decrypt(response.data) }
|
|
248
|
-
}
|
|
249
|
-
return response
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* 3DES 解密数据
|
|
255
|
-
* @param ciphertext
|
|
256
|
-
* @returns 明文数据
|
|
257
|
-
*/
|
|
258
|
-
decrypt = (ciphertext: string) => {
|
|
259
|
-
try {
|
|
260
|
-
const iv = this.des3_key.slice(0, 8)
|
|
261
|
-
const cipherChunks = []
|
|
262
|
-
const decipher = crypto.createDecipheriv("des-ede3-cbc", this.des3_key, iv)
|
|
263
|
-
decipher.setAutoPadding(true)
|
|
264
|
-
cipherChunks.push(decipher.update(ciphertext, cipherEncoding, clearEncoding))
|
|
265
|
-
cipherChunks.push(decipher.final(clearEncoding))
|
|
266
|
-
return JSON.parse(cipherChunks.join(""))
|
|
267
|
-
} catch (err) {
|
|
268
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* 验签
|
|
274
|
-
* @param {string} data 返回的数据
|
|
275
|
-
* @param {string} mess 返回的随机字符串
|
|
276
|
-
* @param {string} timestamp 返回的时间戳
|
|
277
|
-
* @param {string} sign 返回的签名
|
|
278
|
-
* @returns {boolean} true:验签成功;false:验签失败
|
|
279
|
-
*/
|
|
280
|
-
verifyRSASHA256 = (data: string, mess: string, timestamp: string, sign: string) => {
|
|
281
|
-
try {
|
|
282
|
-
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`
|
|
283
|
-
const verify = crypto.createVerify("RSA-SHA256")
|
|
284
|
-
verify.update(plaintext)
|
|
285
|
-
return verify.verify(this.yzh_public_key, sign, cipherEncoding)
|
|
286
|
-
} catch (err) {
|
|
287
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
verifyHmacSHA256 = (data: string, mess: string, timestamp: string, sign: string) => {
|
|
292
|
-
try {
|
|
293
|
-
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`
|
|
294
|
-
const hmac = crypto.createHmac("sha256", this.app_key)
|
|
295
|
-
hmac.update(plaintext)
|
|
296
|
-
return hmac.digest("hex") === sign
|
|
297
|
-
} catch (err) {
|
|
298
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* 文件密码解密
|
|
304
|
-
* @param ciphertextbase64
|
|
305
|
-
* @returns 解密后的密码
|
|
306
|
-
*/
|
|
307
|
-
filePassWordDecryption = (ciphertextbase64: string) => {
|
|
308
|
-
try {
|
|
309
|
-
const buff = Buffer.from(ciphertextbase64, "base64")
|
|
310
|
-
|
|
311
|
-
const decrypted = crypto.privateDecrypt(
|
|
312
|
-
{
|
|
313
|
-
key: this.private_key,
|
|
314
|
-
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
315
|
-
},
|
|
316
|
-
buff
|
|
317
|
-
)
|
|
318
|
-
return decrypted.toString("utf8")
|
|
319
|
-
} catch (err) {
|
|
320
|
-
throw new YZHSDKHttpException(`${err}`)
|
|
321
124
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
349
|
-
// 验签结果 boolean
|
|
350
|
-
const verifyResult = verifyMap[this.sign_type](data, mess, timestamp, sign)
|
|
351
|
-
let plaintext = {}
|
|
352
|
-
if (verifyResult) {
|
|
353
|
-
plaintext = this.decrypt(data)
|
|
354
|
-
}
|
|
355
|
-
return {
|
|
356
|
-
result: verifyResult,
|
|
357
|
-
plaintext,
|
|
358
|
-
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* 请求参数加密
|
|
128
|
+
* @param {object} params
|
|
129
|
+
* @returns {*} object
|
|
130
|
+
*/
|
|
131
|
+
private generatorRequestParams(params: string) {
|
|
132
|
+
try {
|
|
133
|
+
const t = Date.now().toString();
|
|
134
|
+
const m = this.mess();
|
|
135
|
+
|
|
136
|
+
const plaintext = JSON.stringify(params);
|
|
137
|
+
|
|
138
|
+
const data = this.encrypt(plaintext);
|
|
139
|
+
|
|
140
|
+
const signStr = this.sign(data, m, t);
|
|
141
|
+
return {
|
|
142
|
+
data,
|
|
143
|
+
mess: m,
|
|
144
|
+
timestamp: t,
|
|
145
|
+
sign: signStr,
|
|
146
|
+
sign_type: this.sign_type,
|
|
147
|
+
};
|
|
148
|
+
} catch (err) {
|
|
149
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
150
|
+
}
|
|
359
151
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 生成签名(RSA 签名算法)
|
|
155
|
+
* @param {string} data 经过加密后的具体数据
|
|
156
|
+
* @param {string} mess 自定义随机字符串,用于签名
|
|
157
|
+
* @param {string} timestamp 时间戳,精确到秒
|
|
158
|
+
* @returns {string} 签名内容
|
|
159
|
+
*/
|
|
160
|
+
private signRSASHA256 = (data: string, mess: string, timestamp: string) => {
|
|
161
|
+
try {
|
|
162
|
+
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`;
|
|
163
|
+
const sign = crypto.createSign('RSA-SHA256');
|
|
164
|
+
sign.update(plaintext);
|
|
165
|
+
sign.end();
|
|
166
|
+
return sign.sign(this.private_key, cipherEncoding);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* 生成签名(HMAC 签名算法)
|
|
174
|
+
* @param {string} data 经过加密后的具体数据
|
|
175
|
+
* @param {string} mess 自定义随机字符串,用于签名
|
|
176
|
+
* @param {string} timestamp 时间戳,精确到秒
|
|
177
|
+
* @returns {string} 签名内容
|
|
178
|
+
*/
|
|
179
|
+
private signHmacSHA256 = (data: string, mess: string, timestamp: string) => {
|
|
180
|
+
try {
|
|
181
|
+
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`;
|
|
182
|
+
const hmac = crypto.createHmac('sha256', this.app_key);
|
|
183
|
+
hmac.update(plaintext);
|
|
184
|
+
return hmac.digest('hex');
|
|
185
|
+
} catch (err) {
|
|
186
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* 生成签名
|
|
192
|
+
* @param {string} data 经过加密后的具体数据
|
|
193
|
+
* @param {string} mess 自定义随机字符串,用于签名
|
|
194
|
+
* @param {string} timestamp 时间戳,精确到秒
|
|
195
|
+
* @param {string} sign_type 签名算法,支持 RSA、HMAC,枚举分别为 rsa、sha256
|
|
196
|
+
* @returns {string} 签名内容
|
|
197
|
+
*/
|
|
198
|
+
private sign = (data: string, mess: string, timestamp: string) => {
|
|
199
|
+
try {
|
|
200
|
+
switch (this.sign_type) {
|
|
201
|
+
case 'rsa': {
|
|
202
|
+
return this.signRSASHA256(data, mess, timestamp);
|
|
203
|
+
}
|
|
204
|
+
case 'sha256': {
|
|
205
|
+
return this.signHmacSHA256(data, mess, timestamp);
|
|
206
|
+
}
|
|
207
|
+
default:
|
|
208
|
+
throw new YZHSDKHttpException(`sign_type 类型不存在`);
|
|
209
|
+
}
|
|
210
|
+
} catch (err) {
|
|
211
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// 自定义随机字符串
|
|
216
|
+
// eslint-disable-next-line class-methods-use-this
|
|
217
|
+
private mess = () => {
|
|
218
|
+
const buf = crypto.randomBytes(16);
|
|
219
|
+
const token = buf.toString('hex');
|
|
220
|
+
return token.toString();
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* 3DES 加密数据
|
|
225
|
+
* @param plaintext
|
|
226
|
+
* @returns 字符串加密数据
|
|
227
|
+
*/
|
|
228
|
+
private encrypt = (plaintext: string) => {
|
|
229
|
+
try {
|
|
230
|
+
const iv = this.des3_key.slice(0, 8);
|
|
231
|
+
const cipherChunks = [];
|
|
232
|
+
const cipher = crypto.createCipheriv('des-ede3-cbc', this.des3_key, iv);
|
|
233
|
+
cipher.setAutoPadding(true);
|
|
234
|
+
cipherChunks.push(cipher.update(plaintext, clearEncoding, cipherEncoding));
|
|
235
|
+
cipherChunks.push(cipher.final(cipherEncoding));
|
|
236
|
+
|
|
237
|
+
return cipherChunks.join('');
|
|
238
|
+
} catch (err) {
|
|
239
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// 返回处理结果
|
|
244
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
245
|
+
private async parseResponse(result: ResponseData, encryption?: boolean) {
|
|
246
|
+
if (result.status !== 200) {
|
|
247
|
+
const yzhError = new YZHSDKHttpException(result.statusText);
|
|
248
|
+
yzhError.httpCode = result.status;
|
|
249
|
+
throw yzhError;
|
|
250
|
+
} else {
|
|
251
|
+
// HTTP Status Code 200
|
|
252
|
+
const { data: axiosData } = result;
|
|
253
|
+
let response = axiosData;
|
|
254
|
+
// 需解密
|
|
255
|
+
if (encryption) {
|
|
256
|
+
response = { ...response, data: this.decrypt(response.data) };
|
|
257
|
+
}
|
|
258
|
+
return response;
|
|
259
|
+
}
|
|
363
260
|
}
|
|
364
|
-
|
|
365
|
-
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* 3DES 解密数据
|
|
264
|
+
* @param ciphertext
|
|
265
|
+
* @returns 明文数据
|
|
266
|
+
*/
|
|
267
|
+
decrypt = (ciphertext: string) => {
|
|
268
|
+
try {
|
|
269
|
+
const iv = this.des3_key.slice(0, 8);
|
|
270
|
+
const cipherChunks = [];
|
|
271
|
+
const decipher = crypto.createDecipheriv('des-ede3-cbc', this.des3_key, iv);
|
|
272
|
+
decipher.setAutoPadding(true);
|
|
273
|
+
cipherChunks.push(decipher.update(ciphertext, cipherEncoding, clearEncoding));
|
|
274
|
+
cipherChunks.push(decipher.final(clearEncoding));
|
|
275
|
+
return JSON.parse(cipherChunks.join(''));
|
|
276
|
+
} catch (err) {
|
|
277
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* 验签
|
|
283
|
+
* @param {string} data 返回的数据
|
|
284
|
+
* @param {string} mess 返回的随机字符串
|
|
285
|
+
* @param {string} timestamp 返回的时间戳
|
|
286
|
+
* @param {string} sign 返回的签名
|
|
287
|
+
* @returns {boolean} true:验签成功;false:验签失败
|
|
288
|
+
*/
|
|
289
|
+
verifyRSASHA256 = (data: string, mess: string, timestamp: string, sign: string) => {
|
|
290
|
+
try {
|
|
291
|
+
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`;
|
|
292
|
+
const verify = crypto.createVerify('RSA-SHA256');
|
|
293
|
+
verify.update(plaintext);
|
|
294
|
+
return verify.verify(this.yzh_public_key, sign, cipherEncoding);
|
|
295
|
+
} catch (err) {
|
|
296
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
verifyHmacSHA256 = (data: string, mess: string, timestamp: string, sign: string) => {
|
|
301
|
+
try {
|
|
302
|
+
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`;
|
|
303
|
+
const hmac = crypto.createHmac('sha256', this.app_key);
|
|
304
|
+
hmac.update(plaintext);
|
|
305
|
+
return hmac.digest('hex') === sign;
|
|
306
|
+
} catch (err) {
|
|
307
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* 文件密码解密
|
|
313
|
+
* @param ciphertextbase64
|
|
314
|
+
* @returns 解密后的密码
|
|
315
|
+
*/
|
|
316
|
+
filePassWordDecryption = (ciphertextbase64: string) => {
|
|
317
|
+
try {
|
|
318
|
+
const buff = Buffer.from(ciphertextbase64, 'base64');
|
|
319
|
+
|
|
320
|
+
const decrypted = crypto.privateDecrypt(
|
|
321
|
+
{
|
|
322
|
+
key: this.private_key,
|
|
323
|
+
padding: crypto.constants.RSA_PKCS1_PADDING,
|
|
324
|
+
},
|
|
325
|
+
buff
|
|
326
|
+
);
|
|
327
|
+
return decrypted.toString('utf8');
|
|
328
|
+
} catch (err) {
|
|
329
|
+
throw new YZHSDKHttpException(`${err}`);
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* 验签+解密
|
|
335
|
+
* @param responseData 回调返回对象
|
|
336
|
+
* @returns
|
|
337
|
+
*/
|
|
338
|
+
notifyDecoder: (params: {
|
|
339
|
+
/** 返回的数据 */
|
|
340
|
+
data: string;
|
|
341
|
+
/** 返回的随机字符串 */
|
|
342
|
+
mess: string;
|
|
343
|
+
/** 返回的时间戳 */
|
|
344
|
+
timestamp: string;
|
|
345
|
+
/** 返回的签名 */
|
|
346
|
+
sign: string;
|
|
347
|
+
}) => {
|
|
348
|
+
/** 验签结果 */
|
|
349
|
+
result: boolean;
|
|
350
|
+
/** 解密结果 */
|
|
351
|
+
plaintext: object;
|
|
352
|
+
} = responseData => {
|
|
353
|
+
const notifyDecoderResult = (data: string, mess: string, timestamp: string, sign: string) => {
|
|
354
|
+
const verifyMap = {
|
|
355
|
+
rsa: this.verifyRSASHA256,
|
|
356
|
+
sha256: this.verifyHmacSHA256,
|
|
357
|
+
};
|
|
358
|
+
// 验签结果 boolean
|
|
359
|
+
const verifyResult = verifyMap[this.sign_type](data, mess, timestamp, sign);
|
|
360
|
+
let plaintext = {};
|
|
361
|
+
if (verifyResult) {
|
|
362
|
+
plaintext = this.decrypt(data);
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
result: verifyResult,
|
|
366
|
+
plaintext,
|
|
367
|
+
};
|
|
368
|
+
};
|
|
369
|
+
const { data, mess, timestamp, sign } = responseData ?? {};
|
|
370
|
+
if (data && mess && timestamp && sign) {
|
|
371
|
+
return notifyDecoderResult(data, mess, timestamp, sign);
|
|
372
|
+
}
|
|
373
|
+
return { result: false, plaintext: '' };
|
|
374
|
+
};
|
|
366
375
|
}
|
|
367
376
|
|
|
368
|
-
export default YZHClient
|
|
377
|
+
export default YZHClient;
|