@yunzhanghu/sdk-nodejs 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/example/apiUserSign.js +2 -2
- package/example/h5UserSign.js +1 -1
- package/example/invoice.js +13 -0
- package/example/notify.js +1 -1
- package/package.json +1 -1
- package/src/common/client.ts +10 -10
- package/src/services/apiusersign/index.ts +24 -0
- package/src/services/invoice/index.ts +1 -1
- package/yzh/common/client.d.ts +4 -4
- package/yzh/common/client.js +11 -11
- package/yzh/services/apiusersign/index.d.ts +15 -0
- package/yzh/services/apiusersign/index.js +4 -0
- package/yzh/services/invoice/index.js +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 云账户 SDK for Node.js
|
|
2
2
|
|
|
3
3
|
欢迎使用云账户 SDK for Node.js。
|
|
4
|
-
云账户是一家专注为平台企业和新就业形态劳动者提供高质量灵活就业服务的新时代企业。云账户 SDK 对云账户综合服务平台 API
|
|
4
|
+
云账户是一家专注为平台企业和新就业形态劳动者提供高质量灵活就业服务的新时代企业。云账户 SDK 对云账户综合服务平台 API 接口进行封装,帮助您快速接入到云账户综合服务平台。云账户 SDK for Node.js 为您提供签约、支付、回调、数据查询等功能,帮助您完成与云账户综合服务平台的接口对接及业务开发。
|
|
5
5
|
如果您在使用过程中遇到任何问题,请在当前 GitHub 提交 Issues,或发送邮件至技术支持组 [techsupport@yunzhanghu.com](mailto:techsupport@yunzhanghu.com)。
|
|
6
6
|
|
|
7
7
|
## 环境要求
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
### 1、获取配置
|
|
14
14
|
|
|
15
|
-
使用云账户 SDK for Node.js 前,您需先获取 dealer_id、broker_id、3DES Key、App Key
|
|
15
|
+
使用云账户 SDK for Node.js 前,您需先获取 dealer_id、broker_id、3DES Key、App Key、云账户公钥。
|
|
16
16
|
获取方式:使用开户邮件中的账号登录【[云账户综合服务平台](https://service.yunzhanghu.com)】,选择“业务中心 > 业务管理 > 对接信息”,查看并获取以上配置信息。
|
|
17
17
|

|
|
18
18
|
|
|
@@ -34,37 +34,37 @@ OpenSSL-> rsa -in private_key.pem -pubout -out pubkey.pem
|
|
|
34
34
|
|
|
35
35
|
- 方式二:使用工具生成
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
登录【[云账户开放平台](https://open.yunzhanghu.com)】,选择“开发工具下载 > 开发助手 > 工具下载”,下载安装“云账户开放平台开发助手”。
|
|
38
38
|
|
|
39
39
|
### 3、配置密钥
|
|
40
40
|
|
|
41
41
|
登录【[云账户综合服务平台](https://service.yunzhanghu.com)】,选择“业务中心 > 业务管理 > 对接信息”,单击页面右上角的“编辑”,配置平台企业公钥。
|
|
42
42
|

|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## 安装云账户 SDK for Node.js
|
|
45
45
|
|
|
46
46
|
### 通过 NPM 安装
|
|
47
47
|
|
|
48
|
-
推荐通过 Node.js 的包管理工具 NPM
|
|
48
|
+
推荐通过 Node.js 的包管理工具 NPM 获取并安装云账户 SDK for Node.js。NPM 详细介绍请参考[ NPM 官网](https://www.npmjs.com) 。
|
|
49
49
|
|
|
50
|
-
1. 执行以下安装命令安装云账户 Node.js
|
|
50
|
+
1. 执行以下安装命令安装云账户 SDK for Node.js:
|
|
51
51
|
|
|
52
52
|
> npm install @yunzhanghu/sdk-nodejs --save
|
|
53
53
|
|
|
54
|
-
2.
|
|
54
|
+
2. 在您的代码中引用对应模块代码,具体引用方式可参考下文示例。
|
|
55
55
|
|
|
56
56
|
### 通过源码包安装
|
|
57
57
|
|
|
58
58
|
1. 前往 [Github 仓库](https://github.com/YunzhanghuOpen/sdk-nodejs) 下载源码压缩包;
|
|
59
59
|
2. 解压源码包到您项目的合适位置;
|
|
60
|
-
3.
|
|
60
|
+
3. 在您的代码中引用对应模块代码,具体引用方式可参考下文示例。
|
|
61
61
|
|
|
62
62
|
## 快速使用
|
|
63
63
|
|
|
64
64
|
### 示例功能列表
|
|
65
65
|
|
|
66
|
-
- [用户信息验证](./example/
|
|
67
|
-
- [H5
|
|
66
|
+
- [用户信息验证](./example/authentication.js)
|
|
67
|
+
- [用户签约(H5 签约)](./example/h5UserSign.js) or [用户签约(API 签约)](./example/apiUserSign.js)
|
|
68
68
|
- [个体工商户注册(云账户新经济 H5)](./example/bizlicXjjH5.js) or [个体工商户注册(云账户新经济 H5+API)](./example/bizlicXjjH5Api.js)
|
|
69
69
|
- [实时支付](./example/payment.js)
|
|
70
70
|
- [异步通知](./example/notify.js)
|
|
@@ -104,8 +104,8 @@ client
|
|
|
104
104
|
.GetInvoiceAmount({
|
|
105
105
|
dealer_id: process.env.DEALER_ID,
|
|
106
106
|
broker_id: process.env.BROKER_ID,
|
|
107
|
-
//
|
|
108
|
-
//
|
|
107
|
+
// 可自定义 Header 中的 request-id,建议将 request-id 记录在日志中
|
|
108
|
+
// request_id:'req1678257009001'
|
|
109
109
|
})
|
|
110
110
|
.then((data) => {
|
|
111
111
|
console.log("data", data)
|
package/example/apiUserSign.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
const yzhAPI = require("../yzh/index.js")
|
|
2
2
|
const config = require("./conf/config.js")
|
|
3
3
|
|
|
4
|
-
// API
|
|
4
|
+
// 用户签约(API 签约)
|
|
5
5
|
|
|
6
6
|
const apiusersign = new yzhAPI.ApiUserSignServiceClient(config)
|
|
7
7
|
|
|
8
8
|
// 获取协议预览 URL
|
|
9
9
|
apiusersign
|
|
10
|
-
.
|
|
10
|
+
.ApiUserSignContract({
|
|
11
11
|
dealer_id: config.dealer_id,
|
|
12
12
|
broker_id: config.broker_id,
|
|
13
13
|
})
|
package/example/h5UserSign.js
CHANGED
package/example/invoice.js
CHANGED
|
@@ -89,3 +89,16 @@ invoice
|
|
|
89
89
|
.catch((err) => {
|
|
90
90
|
console.log(err.toString())
|
|
91
91
|
})
|
|
92
|
+
|
|
93
|
+
// 查询发票信息
|
|
94
|
+
invoice
|
|
95
|
+
.GetInvoiceInformation({
|
|
96
|
+
invoice_apply_id: "",
|
|
97
|
+
application_id: "423721",
|
|
98
|
+
})
|
|
99
|
+
.then((data) => {
|
|
100
|
+
console.log('响应内容: %j', data)
|
|
101
|
+
})
|
|
102
|
+
.catch((err) => {
|
|
103
|
+
console.log(err.toString())
|
|
104
|
+
})
|
package/example/notify.js
CHANGED
package/package.json
CHANGED
package/src/common/client.ts
CHANGED
|
@@ -130,7 +130,7 @@ export class YZHClient {
|
|
|
130
130
|
|
|
131
131
|
const data = this.encrypt(plaintext)
|
|
132
132
|
|
|
133
|
-
const signStr = this.
|
|
133
|
+
const signStr = this.sign(data, m, t)
|
|
134
134
|
return {
|
|
135
135
|
data,
|
|
136
136
|
mess: m,
|
|
@@ -150,7 +150,7 @@ export class YZHClient {
|
|
|
150
150
|
* @param {string} timestamp 时间戳,精确到秒
|
|
151
151
|
* @returns {string} 签名内容
|
|
152
152
|
*/
|
|
153
|
-
private
|
|
153
|
+
private signRSASHA256 = (data: string, mess: string, timestamp: string) => {
|
|
154
154
|
try {
|
|
155
155
|
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`
|
|
156
156
|
const sign = crypto.createSign("RSA-SHA256")
|
|
@@ -169,7 +169,7 @@ export class YZHClient {
|
|
|
169
169
|
* @param {string} timestamp 时间戳,精确到秒
|
|
170
170
|
* @returns {string} 签名内容
|
|
171
171
|
*/
|
|
172
|
-
private
|
|
172
|
+
private signHmacSHA256 = (data: string, mess: string, timestamp: string) => {
|
|
173
173
|
try {
|
|
174
174
|
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`
|
|
175
175
|
const hmac = crypto.createHmac("sha256", this.app_key)
|
|
@@ -188,14 +188,14 @@ export class YZHClient {
|
|
|
188
188
|
* @param {string} sign_type 签名方式
|
|
189
189
|
* @returns {string} 签名内容
|
|
190
190
|
*/
|
|
191
|
-
private
|
|
191
|
+
private sign = (data: string, mess: string, timestamp: string) => {
|
|
192
192
|
try {
|
|
193
193
|
switch (this.sign_type) {
|
|
194
194
|
case "rsa": {
|
|
195
|
-
return this.
|
|
195
|
+
return this.signRSASHA256(data, mess, timestamp)
|
|
196
196
|
}
|
|
197
197
|
case "sha256": {
|
|
198
|
-
return this.
|
|
198
|
+
return this.signHmacSHA256(data, mess, timestamp)
|
|
199
199
|
}
|
|
200
200
|
default:
|
|
201
201
|
throw new YZHSDKHttpException(`sign_type类型不存在`)
|
|
@@ -239,12 +239,12 @@ export class YZHClient {
|
|
|
239
239
|
yzhError.httpCode = result.status
|
|
240
240
|
throw yzhError
|
|
241
241
|
} else {
|
|
242
|
-
//
|
|
242
|
+
// HTTP Status Code 200
|
|
243
243
|
const { data: axiosData } = result
|
|
244
244
|
let response = axiosData
|
|
245
245
|
// 需解密
|
|
246
246
|
if (encryption) {
|
|
247
|
-
response = { ...response, data: this.
|
|
247
|
+
response = { ...response, data: this.decrypt(response.data) }
|
|
248
248
|
}
|
|
249
249
|
return response
|
|
250
250
|
}
|
|
@@ -255,7 +255,7 @@ export class YZHClient {
|
|
|
255
255
|
* @param ciphertext
|
|
256
256
|
* @returns 明文数据
|
|
257
257
|
*/
|
|
258
|
-
|
|
258
|
+
decrypt = (ciphertext: string) => {
|
|
259
259
|
try {
|
|
260
260
|
const iv = this.des3_key.slice(0, 8)
|
|
261
261
|
const cipherChunks = []
|
|
@@ -350,7 +350,7 @@ export class YZHClient {
|
|
|
350
350
|
const verifyResult = verifyMap[this.sign_type](data, mess, timestamp, sign)
|
|
351
351
|
let plaintext = {}
|
|
352
352
|
if (verifyResult) {
|
|
353
|
-
plaintext = this.
|
|
353
|
+
plaintext = this.decrypt(data)
|
|
354
354
|
}
|
|
355
355
|
return {
|
|
356
356
|
result: verifyResult,
|
|
@@ -16,6 +16,22 @@ interface ApiUseSignContractResponse {
|
|
|
16
16
|
title: string
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/** ApiUserSignContractRequest 获取协议预览 URL 请求 V2 */
|
|
20
|
+
interface ApiUserSignContractRequest {
|
|
21
|
+
/** 平台企业 ID */
|
|
22
|
+
dealer_id: string
|
|
23
|
+
/** 综合服务主体 ID */
|
|
24
|
+
broker_id: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** ApiUserSignContractResponse 获取协议预览 URL 返回 V2 */
|
|
28
|
+
interface ApiUserSignContractResponse {
|
|
29
|
+
/** 预览跳转 URL */
|
|
30
|
+
url: string
|
|
31
|
+
/** 协议名称 */
|
|
32
|
+
title: string
|
|
33
|
+
}
|
|
34
|
+
|
|
19
35
|
/** ApiUserSignRequest 用户签约请求 */
|
|
20
36
|
interface ApiUserSignRequest {
|
|
21
37
|
/** 综合服务主体 ID */
|
|
@@ -98,6 +114,14 @@ export class ApiUserSignServiceClient extends YZHclient {
|
|
|
98
114
|
return this.request("get", "/api/sign/v1/user/contract", req, { encryption: false }, cb)
|
|
99
115
|
}
|
|
100
116
|
|
|
117
|
+
// ApiUserSignContract 获取协议预览 URL V2
|
|
118
|
+
async ApiUserSignContract(
|
|
119
|
+
req: ApiUserSignContractRequest,
|
|
120
|
+
cb?: (error: null | string, rep: ApiUserSignContractResponse) => void
|
|
121
|
+
): Promise<ApiUserSignContractResponse> {
|
|
122
|
+
return this.request("get", "/api/sign/v1/user/contract", req, { encryption: false }, cb)
|
|
123
|
+
}
|
|
124
|
+
|
|
101
125
|
// ApiUserSign 用户签约
|
|
102
126
|
async ApiUserSign(
|
|
103
127
|
req: ApiUserSignRequest,
|
|
@@ -284,7 +284,7 @@ export class InvoiceClient extends YZHclient {
|
|
|
284
284
|
): Promise<GetInvoiceInformationResponse> {
|
|
285
285
|
return this.request(
|
|
286
286
|
"post",
|
|
287
|
-
"/api/invoice/v2/invoice-information",
|
|
287
|
+
"/api/invoice/v2/invoice-face-information",
|
|
288
288
|
req,
|
|
289
289
|
{ encryption: false },
|
|
290
290
|
cb
|
package/yzh/common/client.d.ts
CHANGED
|
@@ -47,7 +47,7 @@ export declare class YZHClient {
|
|
|
47
47
|
* @param {string} timestamp 时间戳,精确到秒
|
|
48
48
|
* @returns {string} 签名内容
|
|
49
49
|
*/
|
|
50
|
-
private
|
|
50
|
+
private signRSASHA256;
|
|
51
51
|
/**
|
|
52
52
|
* 生成签名(HmacSHA256)
|
|
53
53
|
* @param {string} data 经过加密后的具体数据
|
|
@@ -55,7 +55,7 @@ export declare class YZHClient {
|
|
|
55
55
|
* @param {string} timestamp 时间戳,精确到秒
|
|
56
56
|
* @returns {string} 签名内容
|
|
57
57
|
*/
|
|
58
|
-
private
|
|
58
|
+
private signHmacSHA256;
|
|
59
59
|
/**
|
|
60
60
|
* 生成签名
|
|
61
61
|
* @param {string} data 经过加密后的具体数据
|
|
@@ -64,7 +64,7 @@ export declare class YZHClient {
|
|
|
64
64
|
* @param {string} sign_type 签名方式
|
|
65
65
|
* @returns {string} 签名内容
|
|
66
66
|
*/
|
|
67
|
-
private
|
|
67
|
+
private sign;
|
|
68
68
|
private mess;
|
|
69
69
|
/**
|
|
70
70
|
* 3DES 加密数据
|
|
@@ -78,7 +78,7 @@ export declare class YZHClient {
|
|
|
78
78
|
* @param ciphertext
|
|
79
79
|
* @returns 明文数据
|
|
80
80
|
*/
|
|
81
|
-
|
|
81
|
+
decrypt: (ciphertext: string) => any;
|
|
82
82
|
/**
|
|
83
83
|
* 验签
|
|
84
84
|
* @param {string} data 返回的数据
|
package/yzh/common/client.js
CHANGED
|
@@ -26,7 +26,7 @@ class YZHClient {
|
|
|
26
26
|
* @param {string} timestamp 时间戳,精确到秒
|
|
27
27
|
* @returns {string} 签名内容
|
|
28
28
|
*/
|
|
29
|
-
this.
|
|
29
|
+
this.signRSASHA256 = (data, mess, timestamp) => {
|
|
30
30
|
try {
|
|
31
31
|
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`;
|
|
32
32
|
const sign = crypto.createSign("RSA-SHA256");
|
|
@@ -45,7 +45,7 @@ class YZHClient {
|
|
|
45
45
|
* @param {string} timestamp 时间戳,精确到秒
|
|
46
46
|
* @returns {string} 签名内容
|
|
47
47
|
*/
|
|
48
|
-
this.
|
|
48
|
+
this.signHmacSHA256 = (data, mess, timestamp) => {
|
|
49
49
|
try {
|
|
50
50
|
const plaintext = `data=${data}&mess=${mess}×tamp=${timestamp}&key=${this.app_key}`;
|
|
51
51
|
const hmac = crypto.createHmac("sha256", this.app_key);
|
|
@@ -64,17 +64,17 @@ class YZHClient {
|
|
|
64
64
|
* @param {string} sign_type 签名方式
|
|
65
65
|
* @returns {string} 签名内容
|
|
66
66
|
*/
|
|
67
|
-
this.
|
|
67
|
+
this.sign = (data, mess, timestamp) => {
|
|
68
68
|
try {
|
|
69
69
|
switch (this.sign_type) {
|
|
70
70
|
case "rsa": {
|
|
71
|
-
return this.
|
|
71
|
+
return this.signRSASHA256(data, mess, timestamp);
|
|
72
72
|
}
|
|
73
73
|
case "sha256": {
|
|
74
|
-
return this.
|
|
74
|
+
return this.signHmacSHA256(data, mess, timestamp);
|
|
75
75
|
}
|
|
76
76
|
default:
|
|
77
|
-
throw new yzhSDKHttpException_1.default(`sign_type类型不存在`);
|
|
77
|
+
throw new yzhSDKHttpException_1.default(`sign_type 类型不存在`);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
80
|
catch (err) {
|
|
@@ -111,7 +111,7 @@ class YZHClient {
|
|
|
111
111
|
* @param ciphertext
|
|
112
112
|
* @returns 明文数据
|
|
113
113
|
*/
|
|
114
|
-
this.
|
|
114
|
+
this.decrypt = (ciphertext) => {
|
|
115
115
|
try {
|
|
116
116
|
const iv = this.des3_key.slice(0, 8);
|
|
117
117
|
const cipherChunks = [];
|
|
@@ -188,7 +188,7 @@ class YZHClient {
|
|
|
188
188
|
const verifyResult = verifyMap[this.sign_type](data, mess, timestamp, sign);
|
|
189
189
|
let plaintext = {};
|
|
190
190
|
if (verifyResult) {
|
|
191
|
-
plaintext = this.
|
|
191
|
+
plaintext = this.decrypt(data);
|
|
192
192
|
}
|
|
193
193
|
return {
|
|
194
194
|
result: verifyResult,
|
|
@@ -278,7 +278,7 @@ class YZHClient {
|
|
|
278
278
|
const m = this.mess();
|
|
279
279
|
const plaintext = JSON.stringify(params);
|
|
280
280
|
const data = this.encrypt(plaintext);
|
|
281
|
-
const signStr = this.
|
|
281
|
+
const signStr = this.sign(data, m, t);
|
|
282
282
|
return {
|
|
283
283
|
data,
|
|
284
284
|
mess: m,
|
|
@@ -299,12 +299,12 @@ class YZHClient {
|
|
|
299
299
|
throw yzhError;
|
|
300
300
|
}
|
|
301
301
|
else {
|
|
302
|
-
//
|
|
302
|
+
// HTTP Status Code 200
|
|
303
303
|
const { data: axiosData } = result;
|
|
304
304
|
let response = axiosData;
|
|
305
305
|
// 需解密
|
|
306
306
|
if (encryption) {
|
|
307
|
-
response = { ...response, data: this.
|
|
307
|
+
response = { ...response, data: this.decrypt(response.data) };
|
|
308
308
|
}
|
|
309
309
|
return response;
|
|
310
310
|
}
|
|
@@ -13,6 +13,20 @@ interface ApiUseSignContractResponse {
|
|
|
13
13
|
/** 协议名称 */
|
|
14
14
|
title: string;
|
|
15
15
|
}
|
|
16
|
+
/** ApiUserSignContractRequest 获取协议预览 URL 请求 V2 */
|
|
17
|
+
interface ApiUserSignContractRequest {
|
|
18
|
+
/** 平台企业 ID */
|
|
19
|
+
dealer_id: string;
|
|
20
|
+
/** 综合服务主体 ID */
|
|
21
|
+
broker_id: string;
|
|
22
|
+
}
|
|
23
|
+
/** ApiUserSignContractResponse 获取协议预览 URL 返回 V2 */
|
|
24
|
+
interface ApiUserSignContractResponse {
|
|
25
|
+
/** 预览跳转 URL */
|
|
26
|
+
url: string;
|
|
27
|
+
/** 协议名称 */
|
|
28
|
+
title: string;
|
|
29
|
+
}
|
|
16
30
|
/** ApiUserSignRequest 用户签约请求 */
|
|
17
31
|
interface ApiUserSignRequest {
|
|
18
32
|
/** 综合服务主体 ID */
|
|
@@ -79,6 +93,7 @@ export declare class ApiUserSignServiceClient extends YZHclient {
|
|
|
79
93
|
base_url?: string;
|
|
80
94
|
});
|
|
81
95
|
ApiUseSignContract(req: ApiUseSignContractRequest, cb?: (error: null | string, rep: ApiUseSignContractResponse) => void): Promise<ApiUseSignContractResponse>;
|
|
96
|
+
ApiUserSignContract(req: ApiUserSignContractRequest, cb?: (error: null | string, rep: ApiUserSignContractResponse) => void): Promise<ApiUserSignContractResponse>;
|
|
82
97
|
ApiUserSign(req: ApiUserSignRequest, cb?: (error: null | string, rep: ApiUserSignResponse) => void): Promise<ApiUserSignResponse>;
|
|
83
98
|
GetApiUserSignStatus(req: GetApiUserSignStatusRequest, cb?: (error: null | string, rep: GetApiUserSignStatusResponse) => void): Promise<GetApiUserSignStatusResponse>;
|
|
84
99
|
ApiUserSignRelease(req: ApiUserSignReleaseRequest, cb?: (error: null | string, rep: ApiUserSignReleaseResponse) => void): Promise<ApiUserSignReleaseResponse>;
|
|
@@ -10,6 +10,10 @@ class ApiUserSignServiceClient extends client_1.default {
|
|
|
10
10
|
async ApiUseSignContract(req, cb) {
|
|
11
11
|
return this.request("get", "/api/sign/v1/user/contract", req, { encryption: false }, cb);
|
|
12
12
|
}
|
|
13
|
+
// ApiUserSignContract 获取协议预览 URL V2
|
|
14
|
+
async ApiUserSignContract(req, cb) {
|
|
15
|
+
return this.request("get", "/api/sign/v1/user/contract", req, { encryption: false }, cb);
|
|
16
|
+
}
|
|
13
17
|
// ApiUserSign 用户签约
|
|
14
18
|
async ApiUserSign(req, cb) {
|
|
15
19
|
return this.request("post", "/api/sign/v1/user/sign", req, { encryption: false }, cb);
|
|
@@ -24,7 +24,7 @@ class InvoiceClient extends client_1.default {
|
|
|
24
24
|
}
|
|
25
25
|
// GetInvoiceInformation 查询发票信息
|
|
26
26
|
async GetInvoiceInformation(req, cb) {
|
|
27
|
-
return this.request("post", "/api/invoice/v2/invoice-information", req, { encryption: false }, cb);
|
|
27
|
+
return this.request("post", "/api/invoice/v2/invoice-face-information", req, { encryption: false }, cb);
|
|
28
28
|
}
|
|
29
29
|
// GetInvoiceFile 下载 PDF 版发票
|
|
30
30
|
async GetInvoiceFile(req, cb) {
|