q-koa 13.6.0 → 13.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,389 @@
1
+ const axios = require('axios')
2
+ const crypto = require('crypto')
3
+
4
+ const is_dev = process.env.NODE_ENV !== 'production'
5
+
6
+ const config = {
7
+ allinpay: {
8
+ orgid: '',
9
+ cusid: '56458105193URP4',
10
+ appid: '00412889',
11
+ subAppid: '',
12
+ frontUrl: 'https://your-domain.com/pay/result',
13
+ isTest: true,
14
+ signType: 'RSA',
15
+ md5Key: '123456',
16
+ privateKey:
17
+ 'MIIEowIBAAKCAQEAnNkSdA2SelrReNOdNgto5OnkLKvntzzX47pvO1byIj7ewfNlEZtKpzn9b8khyKrAPdQe5O0jTzx3W7wuUnExeCgfB5FjpZP5oSrvaEP0jJq/rYtF84k6zpc/aL+9dXaqN+k5atv1OliJRMovOCsWjWpG2+TuNXeB649mp297hLJfdBp8Fm0SZE6O8SOxLAhOkB4fk+kNWQW0puquxydSqS9gJQ/cFlHYaTa4HJ53TgISj+OEycbyOXBwMR9iz7MvLiueDI1fNEA1LcyXP4hvsBxciJe4wMLrlUlY0OvHbRoF/EmJmb0yTPwUREjcPVFrQwBX0J2Xflm6A/tFAhZQvwIDAQABAoIBAA3xdiR++GCMJkb5eOZPCVogOLqH5adybza9YhoCVrAYqrDPNFLmXTKUgyUm2iv+kextKPns4tCubWXoSq3lhiJKXsfLcvJLP0vGh1XbhKfjMdr5oJRPxLyM0YQ8V1lT3TnvL72CJEC1ki5dUulJW03i/r2ZkluKbjBEpTmJPY/EL/jSQ6eG6JJfYIf8Ew4SfW1Tr+1JVunvhH6hymR4mdcepKKpd9HkC/I/7HkyTftlJsZl3iyLBNMO9w5xoKGEhwIQShJfDZNe6cev0FyOtVwsoXjki13sGNM6xBCDf8rSZo0IksIIHj8k/uNkI/I0MfPpaUY+5pxTR5Mv4UmVOAECgYEAzWPQhhiioHUWAFcN+avxHvW1S6/QR9U7HlZ+B4QjuNYGJneKfj/LsBd/MRUbbNiPm9GJvxeHTvIaPtrGvHzDl8KkJzKxcYh9cko6AsCcvYaf0tVDi5Dyir+syk5qR7qbw1jQyaiuo55+PARDj7vvFTV4aqcyafufx6HT3DducgECgYEAw38vfM+3SE9dsP3kXo6y70gaYccKQyFBvHLGEndZGKaVay+U5oXENZOL0YEEs+iFylbMK1uLa3hiULyy23E0SsLTpuS0/8GItWqqN9oYV/TYeleUUaMje1EEBlMRnJilvzEtATYX8s++hj1AS/aGReC9N/bg/WKnfvlVs8dLQr8CgYBrsfbZGXnMoRhUPVF6pQndrgDOGvYfdFN3aOrUjhkdBjxbVzsrT9BCGU/8MJEc1cEXdxu/M1sjrhbM9nJWyyTUIp9oXlBks6jzkLAyWUTWrAyJxIGSYQKMdkzoF43q5pJeL+fqlaRc7vJKDgJPD8AhYtQtOFZa9DGnv9cTFY24AQKBgQCZFysfySWU+Zm79UZ1ibezy7nlIdH2eb+ZjCNx6IYb2K+frTYkQ1JARleyax88p3TF26yss1Z9hVswMGS7lW3GscS+p9DZizu8HVhIvoBbuIAhZh3exCsP8YR+U2SyRZNeImtEzJB6c5aWVrbYBahmZjSm86yCoFPcCfLEAuf7zwKBgFktK+pUMv+UTLNXvM7j6EcdYKwno5Emg1aU7s9YYh4Hi6exmYVoqnZMhXDvvX2zjUDl2MiLTbs2B4wKSycVOOQ18bN+aqlxcM9UPyUxx93gAKCOeddabjgarxlu7koyDS1MjvjNq8YQakOv3201z85kkf+eIVVgC3C/8aNBgrhx',
18
+ apiUrl: {
19
+ test: 'https://syb-test.allinpay.com/apiweb/unitorder',
20
+ production: 'https://vsp.allinpay.com/apiweb/unitorder',
21
+ },
22
+ publicKey: {
23
+ test: 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDYXfu4b7xgDSmEGQpQ8Sn3RzFgl5CE4gL4TbYrND4FtCYOrvbgLijkdFgIrVVWi2hUW4K0PwBsmlYhXcbR+JSmqv9zviVXZiym0lK3glJGVCN86r9EPvNTusZZPm40TOEKMVENSYaUjCxZ7JzeZDfQ4WCeQQr2xirqn6LdJjpZ5wIDAQAB',
24
+ production:
25
+ 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCm9OV6zH5DYH/ZnAVYHscEELdCNfNTHGuBv1nYYEY9FrOzE0/4kLl9f7Y9dkWHlc2ocDwbrFSm0Vqz0q2rJPxXUYBCQl5yW3jzuKSXif7q1yOwkFVtJXvuhf5WRy+1X5FOFoMvS7538No0RpnLzmNi3ktmiqmhpcY/1pmt20FHQQIDAQAB',
26
+ },
27
+ },
28
+ }
29
+
30
+ class AllinPay {
31
+ constructor(options = {}) {
32
+ this.orgid = options.orgid || config.allinpay.orgid
33
+ this.cusid = options.cusid || config.allinpay.cusid
34
+ this.appid = options.appid || config.allinpay.appid
35
+ this.subAppid = options.subAppid || config.allinpay.subAppid
36
+ this.notifyUrl = options.notifyUrl || config.allinpay.notifyUrl
37
+ this.frontUrl = options.frontUrl || config.allinpay.frontUrl
38
+ this.isTest =
39
+ options.isTest !== undefined ? options.isTest : config.allinpay.isTest
40
+ this.signType = options.signType || config.allinpay.signType
41
+ this.privateKey = options.privateKey || config.allinpay.privateKey
42
+ this.publicKey = this.isTest
43
+ ? config.allinpay.publicKey.test
44
+ : config.allinpay.publicKey.production
45
+ this.baseUrl = this.isTest
46
+ ? config.allinpay.apiUrl.test
47
+ : config.allinpay.apiUrl.production
48
+ }
49
+
50
+ buildSignStr(params) {
51
+ const sortedKeys = Object.keys(params).sort()
52
+ let signStr = ''
53
+ for (const key of sortedKeys) {
54
+ if (
55
+ params[key] !== undefined &&
56
+ params[key] !== null &&
57
+ params[key] !== ''
58
+ ) {
59
+ if (signStr) signStr += '&'
60
+ signStr += `${key}=${params[key]}`
61
+ }
62
+ }
63
+ return signStr
64
+ }
65
+
66
+ sign(params) {
67
+ const signStr = this.buildSignStr(params)
68
+
69
+ if (this.signType === 'RSA') {
70
+ const privateKey = `-----BEGIN RSA PRIVATE KEY-----\n${this.privateKey}\n-----END RSA PRIVATE KEY-----`
71
+ const sign = crypto.createSign('RSA-SHA1')
72
+ sign.update(signStr, 'utf8')
73
+ const signature = sign.sign(privateKey, 'base64')
74
+
75
+ // crypto.createHash('md5').update(originStr).digest('hex')
76
+ return signature
77
+ } else if (this.signType === 'SM2') {
78
+ throw new Error('SM2 signing not implemented')
79
+ }
80
+
81
+ throw new Error(`Unsupported signType: ${this.signType}`)
82
+ }
83
+
84
+ verifySign(params) {
85
+ const sign = params.sign
86
+ if (!sign) {
87
+ console.log('No sign found in response')
88
+ return false
89
+ }
90
+
91
+ const paramsCopy = { ...params }
92
+ delete paramsCopy.sign
93
+
94
+ const signStr = this.buildSignStr(paramsCopy)
95
+
96
+ const publicKey = `-----BEGIN PUBLIC KEY-----\n${this.publicKey}\n-----END PUBLIC KEY-----`
97
+ const verify = crypto.createVerify('RSA-SHA1')
98
+ verify.update(signStr, 'utf8')
99
+ const result = verify.verify(publicKey, sign, 'base64')
100
+
101
+ return result
102
+ }
103
+
104
+ async request(url, params) {
105
+ const _params = new URLSearchParams()
106
+ Object.keys(params).forEach((key, value) => {
107
+ _params.append(key, params[key])
108
+ })
109
+ try {
110
+ if (is_dev) {
111
+ console.log('Request URL:', url)
112
+ console.log('Request params:', _params)
113
+ }
114
+
115
+ const response = await axios.post(url, _params, {
116
+ headers: {
117
+ 'Content-Type': 'application/x-www-form-urlencoded',
118
+ 'User-Agent': 'AllinPay Node.js SDK',
119
+ },
120
+ })
121
+ if (response.data.errmsg) {
122
+ throw new Error(response.data.errmsg)
123
+ }
124
+ if (is_dev) {
125
+ console.log('Response data:', JSON.stringify(response.data))
126
+ }
127
+ return response.data
128
+ } catch (error) {
129
+ if (is_dev) {
130
+ console.error('Request error:', error.message)
131
+ }
132
+ if (is_dev && error.response) {
133
+ console.error('Response status:', error.response.status)
134
+ console.error('Response data:', JSON.stringify(error.response.data))
135
+ }
136
+ throw new Error(`AllinPay request error: ${error.message}`)
137
+ }
138
+ }
139
+
140
+ async createOrder(orderData) {
141
+ const {
142
+ reqsn,
143
+ trxamt,
144
+ body,
145
+ acct,
146
+ remark = '',
147
+ validtime = '',
148
+ limitPay = '',
149
+ goodsTag = '',
150
+ benefitdetail = '',
151
+ chnlstoreid = '',
152
+ subbranch = '',
153
+ extendparams = '',
154
+ cusip = '',
155
+ idno = '',
156
+ truename = '',
157
+ asinfo = '',
158
+ fqnum = '',
159
+ } = orderData
160
+
161
+ const params = {}
162
+
163
+ if (this.orgid) params.orgid = this.orgid
164
+ params.cusid = this.cusid
165
+ params.appid = this.appid
166
+ params.version = '11'
167
+ params.trxamt = trxamt
168
+ params.reqsn = reqsn
169
+ params.paytype = 'W06'
170
+ params.randomstr = this.generateRandomStr()
171
+
172
+ if (body) params.body = body.substring(0, 100)
173
+ if (remark) params.remark = remark
174
+ if (validtime) params.validtime = validtime
175
+ if (acct) params.acct = acct
176
+ if (this.notifyUrl) params.notify_url = this.notifyUrl
177
+ if (limitPay) params.limit_pay = limitPay
178
+ if (this.subAppid) params.sub_appid = this.subAppid
179
+ if (goodsTag) params.goods_tag = goodsTag
180
+ if (benefitdetail) params.benefitdetail = benefitdetail
181
+ if (chnlstoreid) params.chnlstoreid = chnlstoreid
182
+ if (subbranch) params.subbranch = subbranch
183
+ if (extendparams) params.extendparams = extendparams
184
+ if (cusip) params.cusip = cusip
185
+ if (idno) params.idno = idno
186
+ if (truename) params.truename = truename
187
+ if (asinfo) params.asinfo = asinfo
188
+ if (fqnum) params.fqnum = fqnum
189
+
190
+ params.signtype = this.signType
191
+ params.sign = this.sign(params)
192
+
193
+ const url = `${this.baseUrl}/pay`
194
+ const result = await this.request(url, params)
195
+
196
+ if (result && result.retcode === 'SUCCESS') {
197
+ if (result.sign && this.verifySign(result)) {
198
+ return result
199
+ } else {
200
+ console.warn(
201
+ 'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
202
+ )
203
+ return result
204
+ }
205
+ }
206
+ throw new Error(`AllinPay error: ${result.retmsg || 'Unknown error'}`)
207
+ }
208
+
209
+ async queryOrder(reqsn) {
210
+ const params = {
211
+ cusid: this.cusid,
212
+ appid: this.appid,
213
+ version: '11',
214
+ reqsn,
215
+ signtype: this.signType,
216
+ }
217
+
218
+ params.sign = this.sign(params)
219
+
220
+ const url = `${this.baseUrl}/query`
221
+ const result = await this.request(url, params)
222
+
223
+ if (result && result.retcode === 'SUCCESS') {
224
+ if (result.sign && this.verifySign(result)) {
225
+ return result
226
+ } else {
227
+ console.warn(
228
+ 'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
229
+ )
230
+ return result
231
+ }
232
+ }
233
+ throw new Error(`AllinPay error: ${result.retmsg || 'Unknown error'}`)
234
+ }
235
+
236
+ async checkOrder(reqsn) {
237
+ const params = {
238
+ cusid: this.cusid,
239
+ appid: this.appid,
240
+ version: '11',
241
+ reqsn,
242
+ signtype: this.signType,
243
+ }
244
+ if (this.notifyUrl) params.notify_url = this.notifyUrl
245
+
246
+ params.sign = this.sign(params)
247
+
248
+ const url = this.isTest
249
+ ? 'https://syb-test.allinpay.com/apiweb/tranx/query'
250
+ : 'https://vsp.allinpay.com/apiweb/tranx/query'
251
+ const result = await this.request(url, params)
252
+
253
+ if (result && result.retcode === 'SUCCESS') {
254
+ if (result.sign && this.verifySign(result)) {
255
+ return result
256
+ } else {
257
+ console.warn(
258
+ 'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
259
+ )
260
+ return result
261
+ }
262
+ }
263
+ throw new Error(`AllinPay error: ${result.retmsg || 'Unknown error'}`)
264
+ }
265
+
266
+ async refundOrder(orderData) {
267
+ const { reqsn, trxamt, oldreqsn, remark = '', acct = '' } = orderData
268
+
269
+ const params = {
270
+ cusid: this.cusid,
271
+ appid: this.appid,
272
+ version: '11',
273
+ trxamt,
274
+ reqsn,
275
+ oldreqsn,
276
+ signtype: this.signType,
277
+ }
278
+ if (remark) params.remark = remark
279
+ if (acct) params.acct = acct
280
+ params.signtype = this.signType
281
+ if (this.notifyUrl) params.notify_url = this.notifyUrl
282
+
283
+ params.sign = this.sign(params)
284
+
285
+ const url = this.isTest
286
+ ? 'https://syb-test.allinpay.com/apiweb/tranx/refund'
287
+ : 'https://vsp.allinpay.com/apiweb/tranx/refund'
288
+ const result = await this.request(url, params)
289
+
290
+ if (result && result.retcode === 'SUCCESS') {
291
+ if (result.sign && this.verifySign(result)) {
292
+ return result
293
+ } else {
294
+ console.warn(
295
+ 'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
296
+ )
297
+ return result
298
+ }
299
+ }
300
+ throw new Error(`AllinPay error: ${result.retmsg || 'Unknown error'}`)
301
+ }
302
+
303
+ async cancelOrder(reqsn) {
304
+ const params = {
305
+ cusid: this.cusid,
306
+ appid: this.appid,
307
+ version: '11',
308
+ reqsn,
309
+ signtype: this.signType,
310
+ }
311
+
312
+ params.sign = this.sign(params)
313
+
314
+ const url = `${this.baseUrl}/cancel`
315
+ const result = await this.request(url, params)
316
+
317
+ if (result && result.retcode === 'SUCCESS') {
318
+ if (result.sign && this.verifySign(result)) {
319
+ return result
320
+ } else {
321
+ console.warn(
322
+ 'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
323
+ )
324
+ return result
325
+ }
326
+ }
327
+ throw new Error(`AllinPay error: ${result.retmsg || 'Unknown error'}`)
328
+ }
329
+
330
+ async downloadBill({ date }) {
331
+ const params = {
332
+ cusid: this.cusid,
333
+ appid: this.appid,
334
+ date,
335
+ randomstr: this.generateRandomStr(),
336
+ filetype: '1',
337
+ signtype: this.signType,
338
+ }
339
+ params.signtype = this.signType
340
+
341
+ params.sign = this.sign(params)
342
+
343
+ const url = this.isTest
344
+ ? 'https://syb-test.allinpay.com/vsppcusapi/trxfile/get'
345
+ : 'https://cus.allinpay.com/cusapi/trxfile/get'
346
+ const result = await this.request(url, params)
347
+
348
+ if (result && result.retcode === 'SUCCESS') {
349
+ if (result.sign && this.verifySign(result)) {
350
+ return result
351
+ } else {
352
+ console.warn(
353
+ 'Sign verification failed, but retcode is SUCCESS. Returning result anyway.'
354
+ )
355
+ return result
356
+ }
357
+ }
358
+ throw new Error(`AllinPay error: ${result.retmsg || 'Unknown error'}`)
359
+ }
360
+
361
+ generateRandomStr(length = 32) {
362
+ const chars =
363
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
364
+ let result = ''
365
+ for (let i = 0; i < length; i++) {
366
+ result += chars.charAt(Math.floor(Math.random() * chars.length))
367
+ }
368
+ return result
369
+ }
370
+
371
+ generateOrderNum() {
372
+ const timestamp = Date.now().toString()
373
+ const random = Math.random().toString(36).substr(2, 6).toUpperCase()
374
+ return `ORD${timestamp}${random}`
375
+ }
376
+
377
+ formatDateTime() {
378
+ const now = new Date()
379
+ const year = now.getFullYear()
380
+ const month = String(now.getMonth() + 1).padStart(2, '0')
381
+ const day = String(now.getDate()).padStart(2, '0')
382
+ const hour = String(now.getHours()).padStart(2, '0')
383
+ const minute = String(now.getMinutes()).padStart(2, '0')
384
+ const second = String(now.getSeconds()).padStart(2, '0')
385
+ return `${year}${month}${day}${hour}${minute}${second}`
386
+ }
387
+ }
388
+
389
+ module.exports = AllinPay
@@ -96,6 +96,13 @@ const b2bCheckRefundUrl =
96
96
 
97
97
  const b2bCheckOrderUrl =
98
98
  'https://api.weixin.qq.com/retail/B2b/getorder?access_token=%s&pay_sig=%s'
99
+
100
+ const b2bPreStoreUrl =
101
+ 'https://api.weixin.qq.com/wxa/business/batchcreateretail?access_token=%s'
102
+
103
+ const b2bFinishUrl =
104
+ 'https://api.weixin.qq.com/retail/B2b/finishprofitsharingorder?access_token=%s&pay_sig=%s'
105
+
99
106
  const fsPromise = require('fs/promises')
100
107
  const LRU = require('lru-cache')
101
108
  const request = require('request')
@@ -1123,7 +1130,7 @@ module.exports = class Singleton {
1123
1130
  return result
1124
1131
  }
1125
1132
 
1126
- async b2bCheckRefund({ mchid, app_key, data }) {
1133
+ async b2bCheckRefund({ mchid, app_key, out_refund_no }) {
1127
1134
  const requestUrl = b2bCheckRefundUrl
1128
1135
 
1129
1136
  const access_token = await this.getAccessToken()
@@ -1132,7 +1139,7 @@ module.exports = class Singleton {
1132
1139
 
1133
1140
  const signData = {
1134
1141
  mchid,
1135
- ...data,
1142
+ out_refund_no,
1136
1143
  }
1137
1144
 
1138
1145
  const pay_sig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
@@ -1142,14 +1149,14 @@ module.exports = class Singleton {
1142
1149
  if (result.errcode) {
1143
1150
  if (result.errcode === 40001) {
1144
1151
  cache.reset()
1145
- return await this.b2bCheckRefund({ mchid, app_key, data })
1152
+ return await this.b2bCheckRefund({ mchid, app_key, out_refund_no })
1146
1153
  }
1147
1154
  throw new Error(`${result.errcode};${result.errmsg}`)
1148
1155
  }
1149
1156
  return result
1150
1157
  }
1151
1158
 
1152
- async b2bCheckOrder({ mchid, app_key, data }) {
1159
+ async b2bCheckOrder({ mchid, app_key, out_trade_no }) {
1153
1160
  const requestUrl = b2bCheckOrderUrl
1154
1161
 
1155
1162
  const access_token = await this.getAccessToken()
@@ -1158,7 +1165,69 @@ module.exports = class Singleton {
1158
1165
 
1159
1166
  const signData = {
1160
1167
  mchid,
1161
- ...data,
1168
+ out_trade_no,
1169
+ }
1170
+
1171
+ const pay_sig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
1172
+
1173
+ const url = util.format(requestUrl, access_token, pay_sig)
1174
+ const result = await axios.post(url, signData).then((res) => res.data)
1175
+ if (result.errcode) {
1176
+ if (result.errcode === 40001) {
1177
+ cache.reset()
1178
+ return await this.b2bCheckOrder({ mchid, app_key, out_trade_no })
1179
+ }
1180
+ throw new Error(`${result.errcode};${result.errmsg}`)
1181
+ }
1182
+ return result
1183
+ }
1184
+
1185
+ async b2bPreStore(list) {
1186
+ const access_token = await this.getAccessToken()
1187
+ const url = util.format(b2bPreStoreUrl, access_token)
1188
+
1189
+ const result = await axios
1190
+ .post(url, {
1191
+ retail_info_list: list,
1192
+ })
1193
+ .then((res) => res.data)
1194
+ if (result.errcode) {
1195
+ if (result.errcode === 40001) {
1196
+ cache.reset()
1197
+ return await this.b2bPreStore({
1198
+ retail_info_list: list,
1199
+ })
1200
+ }
1201
+ throw new Error(`${result.errcode};${result.errmsg}`)
1202
+ }
1203
+ return result
1204
+ }
1205
+
1206
+ // async b2bFinish(payLoad) {
1207
+ // const access_token = await this.getAccessToken()
1208
+ // const url = util.format(b2bFinishUrl, access_token)
1209
+
1210
+ // const result = await axios.post(url, payLoad).then((res) => res.data)
1211
+ // if (result.errcode) {
1212
+ // if (result.errcode === 40001) {
1213
+ // cache.reset()
1214
+ // return await this.b2bFinish(payLoad)
1215
+ // }
1216
+ // throw new Error(`${result.errcode};${result.errmsg}`)
1217
+ // }
1218
+ // return result
1219
+ // }
1220
+
1221
+ async b2bFinish({ mchid, app_key, out_trade_no }) {
1222
+ const requestUrl = b2bFinishUrl
1223
+
1224
+ const access_token = await this.getAccessToken()
1225
+
1226
+ const uri = requestUrl.split('api.weixin.qq.com')[1].split('?')[0]
1227
+
1228
+ const signData = {
1229
+ mchid,
1230
+ out_trade_no,
1162
1231
  }
1163
1232
 
1164
1233
  const pay_sig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
@@ -1168,7 +1237,7 @@ module.exports = class Singleton {
1168
1237
  if (result.errcode) {
1169
1238
  if (result.errcode === 40001) {
1170
1239
  cache.reset()
1171
- return await this.b2bCheckOrder({ mchid, app_key, data })
1240
+ return await this.b2bFinish({ mchid, app_key, out_trade_no })
1172
1241
  }
1173
1242
  throw new Error(`${result.errcode};${result.errmsg}`)
1174
1243
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "q-koa",
3
- "version": "13.6.0",
3
+ "version": "13.6.2",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -61,7 +61,9 @@
61
61
  "yly-nodejs-sdk": "^1.0.2",
62
62
  "vm2": "^3.9.19",
63
63
  "xlsx": "^0.18.5",
64
- "pm2": "^7.0.1"
64
+ "pm2": "^7.0.1",
65
+ "dayjs": "^1.11.13",
66
+ "request": "^2.88.2"
65
67
  },
66
68
  "devDependencies": {
67
69
  "eslint": "^4.19.1",