q-koa 13.4.0 → 13.4.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.
- package/core/app.js +115 -16
- package/core/config.js +1 -0
- package/core/file/plugins/administrator/controller.js +2 -2
- package/core/file/plugins/common/controller.js +3 -2
- package/core/file/plugins/model/model.js +7 -0
- package/core/file/plugins/model/service.js +1 -0
- package/core/file/plugins/system/controller.js +30 -13
- package/core/file/plugins/system/service.js +2 -4
- package/core/file/plugins/weixin/controller.js +46 -0
- package/core/file/plugins/weixin/service.js +70 -0
- package/core/file/services/weixinMP.js +121 -2
- package/package.json +1 -1
package/core/app.js
CHANGED
|
@@ -23,7 +23,23 @@ const jwt = require('jsonwebtoken')
|
|
|
23
23
|
const verify = util.promisify(jwt.verify)
|
|
24
24
|
const fsExtra = require('fs-extra')
|
|
25
25
|
const { getObject, getAppConfig, formatLiteralObj } = require('./file/utils')
|
|
26
|
-
|
|
26
|
+
const redisVirtual = {
|
|
27
|
+
hSet: () => {
|
|
28
|
+
console.log('redisVirtual hSet')
|
|
29
|
+
},
|
|
30
|
+
hGet: () => {
|
|
31
|
+
console.log('redisVirtual hGet')
|
|
32
|
+
},
|
|
33
|
+
set: () => {
|
|
34
|
+
console.log('redisVirtual set')
|
|
35
|
+
},
|
|
36
|
+
get: () => {
|
|
37
|
+
console.log('redisVirtual get')
|
|
38
|
+
},
|
|
39
|
+
publish: () => {
|
|
40
|
+
console.log('redisVirtual publish')
|
|
41
|
+
},
|
|
42
|
+
}
|
|
27
43
|
const restc = require('./restc')
|
|
28
44
|
const APP_DIR = 'app'
|
|
29
45
|
const PLUGINS_DIR = 'plugins'
|
|
@@ -159,6 +175,14 @@ class APP {
|
|
|
159
175
|
) {
|
|
160
176
|
return ctx.ERROR('error')
|
|
161
177
|
}
|
|
178
|
+
|
|
179
|
+
if (
|
|
180
|
+
ctx.request.header['user-agent'] &&
|
|
181
|
+
ctx.request.header['user-agent'].includes('Apache-HttpClient')
|
|
182
|
+
) {
|
|
183
|
+
throw new Error(`error`)
|
|
184
|
+
}
|
|
185
|
+
|
|
162
186
|
if (this.config.blackList.includes(ip)) {
|
|
163
187
|
return ctx.ERROR('error')
|
|
164
188
|
}
|
|
@@ -459,6 +483,7 @@ class APP {
|
|
|
459
483
|
} else {
|
|
460
484
|
this.app[appName] = {
|
|
461
485
|
sequelize: db,
|
|
486
|
+
redisClient: redisVirtual,
|
|
462
487
|
appConfig: _.defaultsDeep(
|
|
463
488
|
configExist ? require(path.resolve(__dirname, configPath)) : {},
|
|
464
489
|
this.config.app,
|
|
@@ -483,9 +508,12 @@ class APP {
|
|
|
483
508
|
if (initFileConfig) {
|
|
484
509
|
this.initFile(appName)
|
|
485
510
|
}
|
|
486
|
-
|
|
487
511
|
await this.initPlugin(appName)
|
|
488
|
-
|
|
512
|
+
|
|
513
|
+
const initEvent = _.get(this.app[appName], 'appConfig.initEvent', false)
|
|
514
|
+
if (initEvent) {
|
|
515
|
+
this.app[appName].event = new EventEmitter()
|
|
516
|
+
}
|
|
489
517
|
|
|
490
518
|
const cacheConfig = _.get(this.app[appName], 'appConfig.cache', {
|
|
491
519
|
max: 100,
|
|
@@ -592,7 +620,7 @@ class APP {
|
|
|
592
620
|
const folderResult = await fsPromise.readdir(
|
|
593
621
|
path.resolve(pluginDir, folder)
|
|
594
622
|
)
|
|
595
|
-
|
|
623
|
+
for (const filename of folderResult) {
|
|
596
624
|
const extname = path.extname(filename)
|
|
597
625
|
if (extname === '.js') {
|
|
598
626
|
const n = path.basename(filename, extname)
|
|
@@ -675,7 +703,53 @@ class APP {
|
|
|
675
703
|
const indexes = _.cloneDeep(
|
|
676
704
|
_.get(this.app[appName].config[folder], 'indexList', [])
|
|
677
705
|
)
|
|
706
|
+
let hookList = _.cloneDeep(
|
|
707
|
+
_.get(this.app[appName].config[folder], 'hookList', [])
|
|
708
|
+
)
|
|
709
|
+
if (
|
|
710
|
+
hookList.some((h) =>
|
|
711
|
+
['afterDestroy', 'beforeDestroy'].includes(h)
|
|
712
|
+
) &&
|
|
713
|
+
!hookList.includes('beforeBulkDestroy')
|
|
714
|
+
) {
|
|
715
|
+
hookList = [...hookList, 'beforeBulkDestroy']
|
|
716
|
+
}
|
|
717
|
+
let reduceHook = {}
|
|
718
|
+
for (let i = 0; i < hookList.length; i++) {
|
|
719
|
+
const hook = hookList[i]
|
|
720
|
+
reduceHook = {
|
|
721
|
+
...reduceHook,
|
|
722
|
+
[hook]: (target, t) => {
|
|
723
|
+
if (['beforeBulkDestroy'].includes(hook)) {
|
|
724
|
+
target.individualHooks = true
|
|
725
|
+
}
|
|
726
|
+
try {
|
|
727
|
+
this.app[appName].service &&
|
|
728
|
+
this.app[appName].service[folder] &&
|
|
729
|
+
this.app[appName].service[folder][hook] &&
|
|
730
|
+
this.app[appName].service[folder][hook]({
|
|
731
|
+
app: this.app[appName],
|
|
732
|
+
model: folder,
|
|
733
|
+
hook,
|
|
734
|
+
...(target.toJSON
|
|
735
|
+
? {
|
|
736
|
+
target: target.toJSON(),
|
|
737
|
+
}
|
|
738
|
+
: {
|
|
739
|
+
target,
|
|
740
|
+
}),
|
|
741
|
+
...t,
|
|
742
|
+
})
|
|
743
|
+
} catch (e) {
|
|
744
|
+
console.log(e)
|
|
745
|
+
}
|
|
678
746
|
|
|
747
|
+
if (['beforeBulkDestroy'].includes(hook)) {
|
|
748
|
+
return target
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
}
|
|
752
|
+
}
|
|
679
753
|
const instance = this.app[appName].sequelize.define(
|
|
680
754
|
folder,
|
|
681
755
|
attributes,
|
|
@@ -690,6 +764,7 @@ class APP {
|
|
|
690
764
|
plural: `${folder}s`,
|
|
691
765
|
},
|
|
692
766
|
hooks: {
|
|
767
|
+
...reduceHook,
|
|
693
768
|
beforeCreate: (res, t) => {
|
|
694
769
|
try {
|
|
695
770
|
if (folder === 'log') {
|
|
@@ -807,7 +882,7 @@ class APP {
|
|
|
807
882
|
}
|
|
808
883
|
}
|
|
809
884
|
}
|
|
810
|
-
}
|
|
885
|
+
}
|
|
811
886
|
}
|
|
812
887
|
}
|
|
813
888
|
const start = moment().valueOf()
|
|
@@ -836,19 +911,33 @@ class APP {
|
|
|
836
911
|
}
|
|
837
912
|
|
|
838
913
|
initModel(appName) {
|
|
914
|
+
const modelList = Object.keys(this.app[appName].model)
|
|
915
|
+
|
|
916
|
+
const include = modelList.reduce((a, b) => {
|
|
917
|
+
return {
|
|
918
|
+
...a,
|
|
919
|
+
[b]: [],
|
|
920
|
+
}
|
|
921
|
+
}, {})
|
|
922
|
+
this.app[appName] = _.defaultsDeep(
|
|
923
|
+
{
|
|
924
|
+
include,
|
|
925
|
+
},
|
|
926
|
+
this.app[appName]
|
|
927
|
+
)
|
|
839
928
|
for (let i = 0; i < Object.keys(this.app[appName].model).length; i++) {
|
|
840
929
|
const model = Object.keys(this.app[appName].model)[i]
|
|
841
930
|
|
|
842
|
-
if (!this.app[appName].include || !this.app[appName].include[model]) {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
}
|
|
931
|
+
// if (!this.app[appName].include || !this.app[appName].include[model]) {
|
|
932
|
+
// this.app[appName] = _.defaultsDeep(
|
|
933
|
+
// {
|
|
934
|
+
// include: {
|
|
935
|
+
// [model]: [],
|
|
936
|
+
// },
|
|
937
|
+
// },
|
|
938
|
+
// this.app[appName]
|
|
939
|
+
// )
|
|
940
|
+
// }
|
|
852
941
|
const db = this.app[appName].attributes[model]
|
|
853
942
|
|
|
854
943
|
const list = Object.keys(db)
|
|
@@ -1095,7 +1184,7 @@ class APP {
|
|
|
1095
1184
|
} = ctx.request.body
|
|
1096
1185
|
if (['findOne', 'findAll', 'findAndCountAll'].includes(fn)) {
|
|
1097
1186
|
if (!_.isEmpty(other)) {
|
|
1098
|
-
throw new Error('
|
|
1187
|
+
throw new Error('参数不合理')
|
|
1099
1188
|
}
|
|
1100
1189
|
}
|
|
1101
1190
|
|
|
@@ -1374,6 +1463,11 @@ class APP {
|
|
|
1374
1463
|
app[appName].cache.get(modelName)
|
|
1375
1464
|
? app[appName].cache.get(modelName)
|
|
1376
1465
|
: await model.findAll({
|
|
1466
|
+
...(myInclude[j].attributes
|
|
1467
|
+
? {
|
|
1468
|
+
attributes: myInclude[j].attributes,
|
|
1469
|
+
}
|
|
1470
|
+
: {}),
|
|
1377
1471
|
where: {
|
|
1378
1472
|
id: idList,
|
|
1379
1473
|
...myInclude[j].where,
|
|
@@ -1394,6 +1488,11 @@ class APP {
|
|
|
1394
1488
|
const idList = rows.map((r) => r.id).filter((item) => item)
|
|
1395
1489
|
if (idList.length) {
|
|
1396
1490
|
const list = await model.findAll({
|
|
1491
|
+
...(myInclude[j].attributes
|
|
1492
|
+
? {
|
|
1493
|
+
attributes: myInclude[j].attributes,
|
|
1494
|
+
}
|
|
1495
|
+
: {}),
|
|
1397
1496
|
where: {
|
|
1398
1497
|
[controller + '_id']: idList,
|
|
1399
1498
|
...myInclude[j].where,
|
package/core/config.js
CHANGED
|
@@ -214,8 +214,8 @@ exports.scan = async (ctx) => {
|
|
|
214
214
|
})
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
app.event.removeAllListeners()
|
|
218
|
-
app.event.on('scan', sendMessage)
|
|
217
|
+
app.event && app.event.removeAllListeners()
|
|
218
|
+
app.event && app.event.on('scan', sendMessage)
|
|
219
219
|
} else {
|
|
220
220
|
throw new Error('这是一个websocket url')
|
|
221
221
|
}
|
|
@@ -224,8 +224,8 @@ exports.channel = async (ctx) => {
|
|
|
224
224
|
})
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
app.event.removeAllListeners()
|
|
228
|
-
app.event.on(channelId, sendMessage)
|
|
227
|
+
app.event && app.event.removeAllListeners()
|
|
228
|
+
app.event && app.event.on(channelId, sendMessage)
|
|
229
229
|
} else {
|
|
230
230
|
throw new Error('这是一个websocket url')
|
|
231
231
|
}
|
|
@@ -442,6 +442,7 @@ exports.verify = async (ctx) => {
|
|
|
442
442
|
const expressFn = async ({ app, express_number: _express_number }) => {
|
|
443
443
|
const appConfig = getConfig(app)
|
|
444
444
|
const { app_code, express_mobile } = await appConfig.getObject('express')
|
|
445
|
+
|
|
445
446
|
const express_number = _express_number.replace(/\t/, '').trim()
|
|
446
447
|
|
|
447
448
|
const lastFour = express_mobile
|
|
@@ -111,6 +111,7 @@ exports.loadModel = async ({ app, appName }) => {
|
|
|
111
111
|
deleteCheckList: config.deleteCheckList,
|
|
112
112
|
bulkCreateList: config.bulkCreateList,
|
|
113
113
|
indexList: config.indexList,
|
|
114
|
+
hookList: config.hookList,
|
|
114
115
|
list: attributes.map((attr, index) => {
|
|
115
116
|
const name = attr.match(/exports.(.*)=/)[1].trim()
|
|
116
117
|
let defaultValue = ''
|
|
@@ -3,7 +3,6 @@ const path = require('path')
|
|
|
3
3
|
const fsPromise = require('fs/promises')
|
|
4
4
|
const { VM } = require('vm2')
|
|
5
5
|
const axios = require('axios')
|
|
6
|
-
const cheerio = require('cheerio')
|
|
7
6
|
const { lodash, getAppByCtx, getConfig, Sequelize, moment } = require('q-koa')
|
|
8
7
|
const LRU = require('lru-cache')
|
|
9
8
|
const cache = new LRU({
|
|
@@ -231,12 +230,19 @@ exports.customRouter = async (ctx) => {
|
|
|
231
230
|
return list
|
|
232
231
|
}
|
|
233
232
|
|
|
234
|
-
exports.showTables = async (ctx) => {
|
|
233
|
+
exports.showTables = async (ctx, _data) => {
|
|
235
234
|
const { app, appName } = getAppByCtx(ctx)
|
|
235
|
+
const { omit = [] } = _data || ctx.request.body
|
|
236
236
|
|
|
237
|
+
const mapFn = (item) => {
|
|
238
|
+
if (omit.length === 0) return item
|
|
239
|
+
return lodash.omit(item.toJSON ? item.toJSON() : item, omit)
|
|
240
|
+
}
|
|
237
241
|
if (app.cache && app.cache.get('showTables')) {
|
|
238
|
-
|
|
239
|
-
|
|
242
|
+
const cacheResult = app.cache.get('showTables')
|
|
243
|
+
const payLoad = cacheResult.map(mapFn)
|
|
244
|
+
ctx.SUCCESS(payLoad)
|
|
245
|
+
return payLoad
|
|
240
246
|
}
|
|
241
247
|
|
|
242
248
|
const pluginDir = path.resolve(
|
|
@@ -316,7 +322,13 @@ exports.showTables = async (ctx) => {
|
|
|
316
322
|
// 可排序字段
|
|
317
323
|
order: lodash.get(target, 'order', []),
|
|
318
324
|
// 默认排序
|
|
319
|
-
defaultOrder:
|
|
325
|
+
defaultOrder: getValueByModelName(
|
|
326
|
+
target,
|
|
327
|
+
'defaultOrder',
|
|
328
|
+
[],
|
|
329
|
+
model,
|
|
330
|
+
_model
|
|
331
|
+
),
|
|
320
332
|
// 下拉筛选
|
|
321
333
|
select: getValueByModelName(target, 'select', [], model, _model),
|
|
322
334
|
// 是否可以选择批量删除
|
|
@@ -357,19 +369,25 @@ exports.showTables = async (ctx) => {
|
|
|
357
369
|
// can be mock boolean
|
|
358
370
|
mock,
|
|
359
371
|
// reference model query
|
|
360
|
-
referenceSelect:
|
|
372
|
+
referenceSelect: getValueByModelName(
|
|
373
|
+
target,
|
|
374
|
+
'referenceSelect',
|
|
375
|
+
[],
|
|
376
|
+
model,
|
|
377
|
+
_model
|
|
378
|
+
),
|
|
361
379
|
// 是否可以选择批量删除
|
|
362
380
|
availableSort: lodash.get(target, 'availableSort', false),
|
|
363
381
|
// 用于后台管理不要查询太多数据,常用于表多的model
|
|
364
382
|
autoData: lodash.get(target, 'autoData', null),
|
|
365
383
|
// 有些model需要前置查询其它model数据
|
|
366
|
-
initList:
|
|
384
|
+
initList: getValueByModelName(target, 'initList', [], model, _model),
|
|
367
385
|
// 有些table需要前置查询数据
|
|
368
386
|
initTableList: lodash.get(target, 'initTableList', []),
|
|
369
387
|
// 局部更新列表
|
|
370
388
|
editInline: lodash.get(target, 'editInline', {}),
|
|
371
389
|
// 有些model需要前置查询其它model数据
|
|
372
|
-
comment:
|
|
390
|
+
comment: getValueByModelName(target, 'comment', {}, model, _model),
|
|
373
391
|
// 默认查询,覆盖之前
|
|
374
392
|
modelQuery: lodash.get(target, 'modelQuery', {}),
|
|
375
393
|
// 默认更新,覆盖之前
|
|
@@ -387,9 +405,10 @@ exports.showTables = async (ctx) => {
|
|
|
387
405
|
}
|
|
388
406
|
})
|
|
389
407
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
408
|
+
const payLoad = models.map(mapFn)
|
|
409
|
+
ctx.SUCCESS(payLoad)
|
|
410
|
+
app.cache && app.cache.set('showTables', models.map(mapFn))
|
|
411
|
+
return payLoad
|
|
393
412
|
}
|
|
394
413
|
|
|
395
414
|
exports.getTable = async (ctx) => {
|
|
@@ -584,7 +603,6 @@ exports.sandbox = async (ctx) => {
|
|
|
584
603
|
const vm = new VM({
|
|
585
604
|
sandbox: {
|
|
586
605
|
axios,
|
|
587
|
-
cheerio,
|
|
588
606
|
module,
|
|
589
607
|
setTimeout,
|
|
590
608
|
sleep: (time) =>
|
|
@@ -603,7 +621,6 @@ exports.onlineChat = async (ctx) => {
|
|
|
603
621
|
sandbox: {
|
|
604
622
|
send: (data) => ws.send(JSON.stringify(data)),
|
|
605
623
|
axios,
|
|
606
|
-
cheerio,
|
|
607
624
|
},
|
|
608
625
|
})
|
|
609
626
|
vm.run(code)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { getConfig, lodash, Sequelize } = require('q-koa')
|
|
2
|
-
const { formatLiteralObj } = require('../../utils')
|
|
2
|
+
const { formatLiteralObj, filterFunction } = require('../../utils')
|
|
3
3
|
const formatPostFunction = (str) => `eval(${str})`
|
|
4
4
|
const nodeVm = require('vm')
|
|
5
5
|
const axios = require('axios')
|
|
@@ -84,9 +84,7 @@ exports.initData = async ({ includes, excludes, app, ctx }) => {
|
|
|
84
84
|
.map((i) => (i.toJSON ? i.toJSON() : i))
|
|
85
85
|
.filter((c) => {
|
|
86
86
|
const target = data && data.where ? data.where : {}
|
|
87
|
-
return
|
|
88
|
-
return c[key] === target[key]
|
|
89
|
-
})
|
|
87
|
+
return filterFunction(target, c)
|
|
90
88
|
})
|
|
91
89
|
.map((c) => {
|
|
92
90
|
return pick.length > 0
|
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
getConfig,
|
|
5
5
|
moment,
|
|
6
6
|
ServiceError,
|
|
7
|
+
getUserByCtx,
|
|
7
8
|
} = require('q-koa')
|
|
8
9
|
|
|
9
10
|
const axios = require('axios')
|
|
@@ -93,6 +94,7 @@ exports.mp_getPhone = async (ctx) => {
|
|
|
93
94
|
(!exist.mp_user.appid || exist.mp_user.appid === app_id)
|
|
94
95
|
|
|
95
96
|
if (mobileExist) {
|
|
97
|
+
app.service.log.push({ app, message: `系统已存在该手机号${phoneNumber}` })
|
|
96
98
|
return ctx.ERROR('系统已存在该手机号')
|
|
97
99
|
}
|
|
98
100
|
|
|
@@ -1144,6 +1146,50 @@ exports.mp_pay = async (ctx) => {
|
|
|
1144
1146
|
})
|
|
1145
1147
|
}
|
|
1146
1148
|
|
|
1149
|
+
exports.b2b_pay = async (ctx) => {
|
|
1150
|
+
const { app, appName } = getAppByCtx(ctx)
|
|
1151
|
+
const appConfig = getConfig(app)
|
|
1152
|
+
const { is_dev } = await appConfig.getObject('base')
|
|
1153
|
+
const { name, _out_trade_no, order_id, price, prefix, code, type, is_admin } =
|
|
1154
|
+
ctx.request.body
|
|
1155
|
+
|
|
1156
|
+
const { app_id, app_secrect } = await appConfig.getObject('weixin_mp')
|
|
1157
|
+
const {
|
|
1158
|
+
mchId: mchid,
|
|
1159
|
+
app_key,
|
|
1160
|
+
key,
|
|
1161
|
+
pay_key: _pay_key,
|
|
1162
|
+
} = await appConfig.getObject('weixin_pay')
|
|
1163
|
+
|
|
1164
|
+
const pay_key = _pay_key || key || appName
|
|
1165
|
+
|
|
1166
|
+
const weixinMp = new WeixinMp({
|
|
1167
|
+
appid: app_id,
|
|
1168
|
+
secrect: app_secrect,
|
|
1169
|
+
})
|
|
1170
|
+
weixinMp.init()
|
|
1171
|
+
const out_trade_no = _out_trade_no
|
|
1172
|
+
? _out_trade_no
|
|
1173
|
+
: prefix
|
|
1174
|
+
? `${pay_key}_${prefix}-${order_id}_${type}`
|
|
1175
|
+
: `${pay_key}_${order_id}_${type}`
|
|
1176
|
+
|
|
1177
|
+
const res = await weixinMp.b2bPay({
|
|
1178
|
+
mchid,
|
|
1179
|
+
app_key,
|
|
1180
|
+
code,
|
|
1181
|
+
data: {
|
|
1182
|
+
out_trade_no,
|
|
1183
|
+
description: name,
|
|
1184
|
+
amount: {
|
|
1185
|
+
order_amount: is_admin || is_dev ? 1 : Math.round(price * 100),
|
|
1186
|
+
},
|
|
1187
|
+
},
|
|
1188
|
+
})
|
|
1189
|
+
|
|
1190
|
+
ctx.SUCCESS(res)
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1147
1193
|
exports.mp_pay_new = async (ctx) => {
|
|
1148
1194
|
const { app, appName } = getAppByCtx(ctx)
|
|
1149
1195
|
const {
|
|
@@ -13,6 +13,76 @@ const fsPromise = require('fs/promises')
|
|
|
13
13
|
const WeixinMp = require('../../services/weixinMP')
|
|
14
14
|
const OSS = require('ali-oss')
|
|
15
15
|
|
|
16
|
+
exports.b2b_refund = async ({
|
|
17
|
+
ctx,
|
|
18
|
+
id,
|
|
19
|
+
prefix = '',
|
|
20
|
+
total_fee,
|
|
21
|
+
refund_fee,
|
|
22
|
+
price,
|
|
23
|
+
pay_type = 'B2B-WEIXIN',
|
|
24
|
+
type = '',
|
|
25
|
+
config = 'weixin_mp',
|
|
26
|
+
pay_config = 'weixin_pay',
|
|
27
|
+
out_trade_no: _out_trade_no,
|
|
28
|
+
out_refund_no: _out_refund_no,
|
|
29
|
+
refund_from = 1,
|
|
30
|
+
...rest
|
|
31
|
+
}) => {
|
|
32
|
+
if (!ctx) throw new Error('?ctx')
|
|
33
|
+
const { app, appName } = getAppByCtx(ctx)
|
|
34
|
+
const appConfig = getConfig(app)
|
|
35
|
+
const { app_id, app_secrect } = await appConfig.getObject(config)
|
|
36
|
+
const { mchId: mchid, key, app_key } = await appConfig.getObject(pay_config)
|
|
37
|
+
|
|
38
|
+
const weixinMp = new WeixinMp({
|
|
39
|
+
appid: app_id,
|
|
40
|
+
secrect: app_secrect,
|
|
41
|
+
})
|
|
42
|
+
weixinMp.init()
|
|
43
|
+
|
|
44
|
+
const orderModel = prefix ? `${prefix}_order` : 'order'
|
|
45
|
+
const out_trade_no =
|
|
46
|
+
_out_trade_no ||
|
|
47
|
+
(prefix ? `${key}_${prefix}-${id}_${pay_type}` : `${key}_${id}_${pay_type}`)
|
|
48
|
+
const orderDetail = await app.model[orderModel].findOne({
|
|
49
|
+
where: {
|
|
50
|
+
id,
|
|
51
|
+
},
|
|
52
|
+
include: app.model.order_refund_record,
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
if (!orderDetail) throw new Error('不存在该订单')
|
|
56
|
+
|
|
57
|
+
const refundRecords = lodash.get(orderDetail, 'order_refund_records', [])
|
|
58
|
+
const refundNumber = refundRecords.length + 1
|
|
59
|
+
const out_refund_no =
|
|
60
|
+
_out_refund_no || [`${prefix}-${id}`, type, refundNumber].join('_')
|
|
61
|
+
|
|
62
|
+
console.log({
|
|
63
|
+
mchid,
|
|
64
|
+
app_key,
|
|
65
|
+
data: {
|
|
66
|
+
out_trade_no: out_trade_no,
|
|
67
|
+
out_refund_no,
|
|
68
|
+
refund_amount: Math.round((refund_fee || price) * 100),
|
|
69
|
+
refund_from,
|
|
70
|
+
},
|
|
71
|
+
})
|
|
72
|
+
const res = await weixinMp.b2bRefund({
|
|
73
|
+
mchid,
|
|
74
|
+
app_key,
|
|
75
|
+
data: {
|
|
76
|
+
out_trade_no: out_trade_no,
|
|
77
|
+
out_refund_no,
|
|
78
|
+
refund_amount: Math.round((refund_fee || price) * 100),
|
|
79
|
+
refund_from,
|
|
80
|
+
},
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
return res
|
|
84
|
+
}
|
|
85
|
+
|
|
16
86
|
exports.refund = async ({
|
|
17
87
|
ctx,
|
|
18
88
|
id,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const util = require('util')
|
|
2
2
|
const axios = require('axios')
|
|
3
|
-
|
|
3
|
+
const crypto = require('crypto')
|
|
4
4
|
const { lodash, ServiceError } = require('q-koa')
|
|
5
5
|
const getAccessTokenUrl =
|
|
6
6
|
'https://api.weixin.qq.com/cgi-bin/token?grant_type=%s&appid=%s&secret=%s'
|
|
@@ -82,6 +82,14 @@ const createActivityUrl =
|
|
|
82
82
|
const setUpdatableMsgUrl =
|
|
83
83
|
'https://api.weixin.qq.com/cgi-bin/message/wxopen/updatablemsg/send?access_token=%s'
|
|
84
84
|
|
|
85
|
+
const b2bRefundUrl =
|
|
86
|
+
'https://api.weixin.qq.com/retail/B2b/refund?access_token=%s&pay_sig=%s'
|
|
87
|
+
|
|
88
|
+
const b2bCheckRefundUrl =
|
|
89
|
+
'https://api.weixin.qq.com/retail/B2b/getrefund?access_token=%s&pay_sig=%s'
|
|
90
|
+
|
|
91
|
+
const b2bCheckOrderUrl =
|
|
92
|
+
'https://api.weixin.qq.com/retail/B2b/getorder?access_token=%s&pay_sig=%s'
|
|
85
93
|
const fsPromise = require('fs/promises')
|
|
86
94
|
const LRU = require('lru-cache')
|
|
87
95
|
const request = require('request')
|
|
@@ -90,7 +98,9 @@ const cache = new LRU({
|
|
|
90
98
|
maxAge: 1000 * 60 * 60,
|
|
91
99
|
})
|
|
92
100
|
const uuid = require('node-uuid')
|
|
93
|
-
|
|
101
|
+
const hmacSha256 = (data, secret) => {
|
|
102
|
+
return crypto.createHmac('sha256', secret).update(data).digest('hex')
|
|
103
|
+
}
|
|
94
104
|
const uploadPromise = (options) => {
|
|
95
105
|
return new Promise((resolve, reject) => {
|
|
96
106
|
request.post(
|
|
@@ -998,6 +1008,115 @@ module.exports = class Singleton {
|
|
|
998
1008
|
return result
|
|
999
1009
|
}
|
|
1000
1010
|
|
|
1011
|
+
async b2bPay({
|
|
1012
|
+
uri = 'requestCommonPayment',
|
|
1013
|
+
data,
|
|
1014
|
+
app_key,
|
|
1015
|
+
code,
|
|
1016
|
+
env = 0,
|
|
1017
|
+
mchid,
|
|
1018
|
+
}) {
|
|
1019
|
+
const weixinLoginUrl = `https://api.weixin.qq.com/sns/jscode2session?appid=${this.config.appid}&secret=${this.config.secrect}&js_code=${code}&grant_type=authorization_code`
|
|
1020
|
+
|
|
1021
|
+
const weixinResult = await axios.get(weixinLoginUrl).then((res) => res.data)
|
|
1022
|
+
|
|
1023
|
+
const { session_key } = weixinResult
|
|
1024
|
+
|
|
1025
|
+
const signData = {
|
|
1026
|
+
mchid,
|
|
1027
|
+
env,
|
|
1028
|
+
...data,
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
const signature = hmacSha256(JSON.stringify(signData), session_key)
|
|
1032
|
+
const paySig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
|
|
1033
|
+
|
|
1034
|
+
return {
|
|
1035
|
+
signData: JSON.stringify(signData),
|
|
1036
|
+
mode: 'retail_pay_goods',
|
|
1037
|
+
signature,
|
|
1038
|
+
paySig,
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
async b2bRefund({ mchid, app_key, data }) {
|
|
1043
|
+
const requestUrl = b2bRefundUrl
|
|
1044
|
+
|
|
1045
|
+
const access_token = await this.getAccessToken()
|
|
1046
|
+
|
|
1047
|
+
const uri = requestUrl.split('api.weixin.qq.com')[1].split('?')[0]
|
|
1048
|
+
|
|
1049
|
+
const signData = {
|
|
1050
|
+
mchid,
|
|
1051
|
+
...data,
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
const pay_sig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
|
|
1055
|
+
|
|
1056
|
+
const url = util.format(requestUrl, access_token, pay_sig)
|
|
1057
|
+
const result = await axios.post(url, signData).then((res) => res.data)
|
|
1058
|
+
if (result.errcode) {
|
|
1059
|
+
if (result.errcode === 40001) {
|
|
1060
|
+
cache.reset()
|
|
1061
|
+
return await this.b2bRefund({ mchid, app_key, data })
|
|
1062
|
+
}
|
|
1063
|
+
throw new Error(`${result.errcode};${result.errmsg}`)
|
|
1064
|
+
}
|
|
1065
|
+
return result
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
async b2bCheckRefund({ mchid, app_key, data }) {
|
|
1069
|
+
const requestUrl = b2bCheckRefundUrl
|
|
1070
|
+
|
|
1071
|
+
const access_token = await this.getAccessToken()
|
|
1072
|
+
|
|
1073
|
+
const uri = requestUrl.split('api.weixin.qq.com')[1].split('?')[0]
|
|
1074
|
+
|
|
1075
|
+
const signData = {
|
|
1076
|
+
mchid,
|
|
1077
|
+
...data,
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
const pay_sig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
|
|
1081
|
+
|
|
1082
|
+
const url = util.format(requestUrl, access_token, pay_sig)
|
|
1083
|
+
const result = await axios.post(url, signData).then((res) => res.data)
|
|
1084
|
+
if (result.errcode) {
|
|
1085
|
+
if (result.errcode === 40001) {
|
|
1086
|
+
cache.reset()
|
|
1087
|
+
return await this.b2bCheckRefund({ mchid, app_key, data })
|
|
1088
|
+
}
|
|
1089
|
+
throw new Error(`${result.errcode};${result.errmsg}`)
|
|
1090
|
+
}
|
|
1091
|
+
return result
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
async b2bCheckOrder({ mchid, app_key, data }) {
|
|
1095
|
+
const requestUrl = b2bCheckOrderUrl
|
|
1096
|
+
|
|
1097
|
+
const access_token = await this.getAccessToken()
|
|
1098
|
+
|
|
1099
|
+
const uri = requestUrl.split('api.weixin.qq.com')[1].split('?')[0]
|
|
1100
|
+
|
|
1101
|
+
const signData = {
|
|
1102
|
+
mchid,
|
|
1103
|
+
...data,
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
const pay_sig = hmacSha256(`${uri}&${JSON.stringify(signData)}`, app_key)
|
|
1107
|
+
|
|
1108
|
+
const url = util.format(requestUrl, access_token, pay_sig)
|
|
1109
|
+
const result = await axios.post(url, signData).then((res) => res.data)
|
|
1110
|
+
if (result.errcode) {
|
|
1111
|
+
if (result.errcode === 40001) {
|
|
1112
|
+
cache.reset()
|
|
1113
|
+
return await this.b2bCheckOrder({ mchid, app_key, data })
|
|
1114
|
+
}
|
|
1115
|
+
throw new Error(`${result.errcode};${result.errmsg}`)
|
|
1116
|
+
}
|
|
1117
|
+
return result
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1001
1120
|
getConfig() {
|
|
1002
1121
|
return this.config
|
|
1003
1122
|
}
|