q-koa 13.5.1 → 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.
- package/core/app.js +45 -10
- package/core/file/plugins/log/service.js +1 -1
- package/core/file/plugins/model/service.js +26 -25
- package/core/file/plugins/weixin/controller.js +427 -22
- package/core/file/plugins/weixin/service.js +445 -28
- package/core/file/services/allinpay.js +389 -0
- package/core/file/services/weixinMP.js +75 -6
- package/package.json +4 -2
|
@@ -13,6 +13,15 @@ const fsPromise = require('fs/promises')
|
|
|
13
13
|
const WeixinMp = require('../../services/weixinMP')
|
|
14
14
|
const OSS = require('ali-oss')
|
|
15
15
|
|
|
16
|
+
const axios = require('axios')
|
|
17
|
+
const AdmZip = require('adm-zip')
|
|
18
|
+
const fs = require('fs')
|
|
19
|
+
|
|
20
|
+
const XLSX = require('xlsx')
|
|
21
|
+
const _ = require('lodash')
|
|
22
|
+
|
|
23
|
+
const AllinPay = require('../../services/allinpay')
|
|
24
|
+
|
|
16
25
|
exports.b2b_refund = async ({
|
|
17
26
|
ctx,
|
|
18
27
|
id,
|
|
@@ -20,20 +29,25 @@ exports.b2b_refund = async ({
|
|
|
20
29
|
total_fee,
|
|
21
30
|
refund_fee,
|
|
22
31
|
price,
|
|
23
|
-
pay_type = '
|
|
32
|
+
pay_type = 'MP-WEIXIN',
|
|
24
33
|
type = '',
|
|
25
34
|
config = 'weixin_mp',
|
|
26
35
|
pay_config = 'weixin_pay',
|
|
27
36
|
out_trade_no: _out_trade_no,
|
|
28
37
|
out_refund_no: _out_refund_no,
|
|
29
38
|
refund_from = 1,
|
|
39
|
+
mch_id = '',
|
|
30
40
|
...rest
|
|
31
41
|
}) => {
|
|
32
42
|
if (!ctx) throw new Error('?ctx')
|
|
33
43
|
const { app, appName } = getAppByCtx(ctx)
|
|
34
44
|
const appConfig = getConfig(app)
|
|
35
45
|
const { app_id, app_secrect } = await appConfig.getObject(config)
|
|
36
|
-
const { mchId
|
|
46
|
+
const { mchId, b2b_mchId, key, app_key } = await appConfig.getObject(
|
|
47
|
+
pay_config
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const mchid = mch_id || b2b_mchId || mchId
|
|
37
51
|
|
|
38
52
|
const weixinMp = new WeixinMp({
|
|
39
53
|
appid: app_id,
|
|
@@ -54,6 +68,17 @@ exports.b2b_refund = async ({
|
|
|
54
68
|
|
|
55
69
|
if (!orderDetail) throw new Error('不存在该订单')
|
|
56
70
|
|
|
71
|
+
const payResult = await weixinMp.b2bCheckOrder({
|
|
72
|
+
out_trade_no,
|
|
73
|
+
mchid,
|
|
74
|
+
app_key,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (payResult.settle_status !== 2) {
|
|
78
|
+
console.log(payResult)
|
|
79
|
+
throw new Error('系统正在结算,请等待若干分钟后才能退款')
|
|
80
|
+
}
|
|
81
|
+
|
|
57
82
|
const refundRecords = lodash.get(orderDetail, 'order_refund_records', [])
|
|
58
83
|
const refundNumber = refundRecords.length + 1
|
|
59
84
|
const out_refund_no =
|
|
@@ -97,6 +122,7 @@ exports.refund = async ({
|
|
|
97
122
|
out_trade_no: _out_trade_no,
|
|
98
123
|
out_refund_no: _out_refund_no,
|
|
99
124
|
is_pem = false,
|
|
125
|
+
mch_id = '',
|
|
100
126
|
...rest
|
|
101
127
|
}) => {
|
|
102
128
|
if (!ctx) throw new Error('?ctx')
|
|
@@ -110,12 +136,14 @@ exports.refund = async ({
|
|
|
110
136
|
|
|
111
137
|
const payObj = new Pay({
|
|
112
138
|
appId: appId,
|
|
113
|
-
mchId: mchId,
|
|
139
|
+
mchId: mch_id || mchId,
|
|
114
140
|
key: key.length > 10 ? key : partner_key, // 微信商户平台API密钥,
|
|
115
141
|
pfx: await fsPromise.readFile(
|
|
116
142
|
path.resolve(
|
|
117
143
|
__dirname,
|
|
118
|
-
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
144
|
+
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
145
|
+
mch_id || key
|
|
146
|
+
}/apiclient_cert.p12`
|
|
119
147
|
)
|
|
120
148
|
),
|
|
121
149
|
})
|
|
@@ -134,20 +162,24 @@ exports.refund = async ({
|
|
|
134
162
|
if (!orderDetail) throw new Error('不存在该订单')
|
|
135
163
|
const wxpay = WXPay({
|
|
136
164
|
appid: appId,
|
|
137
|
-
mch_id: mchId,
|
|
165
|
+
mch_id: mch_id || mchId,
|
|
138
166
|
partner_key: key.length > 10 ? key : partner_key, // 微信商户平台API密钥
|
|
139
167
|
...(isPem
|
|
140
168
|
? {
|
|
141
169
|
cert_pem: await fsPromise.readFile(
|
|
142
170
|
path.resolve(
|
|
143
171
|
__dirname,
|
|
144
|
-
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
172
|
+
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
173
|
+
mch_id || key
|
|
174
|
+
}/apiclient_cert.pem`
|
|
145
175
|
)
|
|
146
176
|
),
|
|
147
177
|
key_pem: await fsPromise.readFile(
|
|
148
178
|
path.resolve(
|
|
149
179
|
__dirname,
|
|
150
|
-
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
180
|
+
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
181
|
+
mch_id || key
|
|
182
|
+
}/apiclient_key.pem`
|
|
151
183
|
)
|
|
152
184
|
),
|
|
153
185
|
}
|
|
@@ -210,6 +242,7 @@ exports.refundOnly = async ({
|
|
|
210
242
|
out_trade_no: _out_trade_no,
|
|
211
243
|
out_refund_no: _out_refund_no,
|
|
212
244
|
is_pem = false,
|
|
245
|
+
mch_id = '',
|
|
213
246
|
...rest
|
|
214
247
|
}) => {
|
|
215
248
|
if (!ctx) throw new Error('?ctx')
|
|
@@ -223,12 +256,14 @@ exports.refundOnly = async ({
|
|
|
223
256
|
|
|
224
257
|
const payObj = new Pay({
|
|
225
258
|
appId: appId,
|
|
226
|
-
mchId: mchId,
|
|
259
|
+
mchId: mch_id || mchId,
|
|
227
260
|
key: key.length > 10 ? key : partner_key, // 微信商户平台API密钥,
|
|
228
261
|
pfx: await fsPromise.readFile(
|
|
229
262
|
path.resolve(
|
|
230
263
|
__dirname,
|
|
231
|
-
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
264
|
+
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
265
|
+
mch_id || key
|
|
266
|
+
}/apiclient_cert.p12`
|
|
232
267
|
)
|
|
233
268
|
), // 微信商户平台证书
|
|
234
269
|
})
|
|
@@ -238,7 +273,7 @@ exports.refundOnly = async ({
|
|
|
238
273
|
|
|
239
274
|
const wxpay = WXPay({
|
|
240
275
|
appid: appId,
|
|
241
|
-
mch_id: mchId,
|
|
276
|
+
mch_id: mch_id || mchId,
|
|
242
277
|
partner_key: key.length > 10 ? key : partner_key, // 微信商户平台API密钥
|
|
243
278
|
...(isPem
|
|
244
279
|
? {
|
|
@@ -364,6 +399,7 @@ exports.cash = async ({
|
|
|
364
399
|
user_id,
|
|
365
400
|
number,
|
|
366
401
|
pay_config = 'weixin_pay',
|
|
402
|
+
mch_id = '',
|
|
367
403
|
}) => {
|
|
368
404
|
if (!ctx) throw new Error('?ctx')
|
|
369
405
|
const { app, appName } = getAppByCtx(ctx)
|
|
@@ -388,12 +424,14 @@ exports.cash = async ({
|
|
|
388
424
|
|
|
389
425
|
const payObj = new Pay({
|
|
390
426
|
appId: appId,
|
|
391
|
-
mchId: mchId,
|
|
427
|
+
mchId: mch_id || mchId,
|
|
392
428
|
key: key.length > 10 ? key : partner_key, // 微信商户平台API密钥,
|
|
393
429
|
pfx: await fsPromise.readFile(
|
|
394
430
|
path.resolve(
|
|
395
431
|
__dirname,
|
|
396
|
-
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
432
|
+
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
433
|
+
mch_id || key
|
|
434
|
+
}/apiclient_cert.p12`
|
|
397
435
|
)
|
|
398
436
|
), // 微信商户平台证书
|
|
399
437
|
})
|
|
@@ -697,17 +735,20 @@ exports.retail_refund_notify = async ({ ctx, app, result }) => {
|
|
|
697
735
|
const type = trade_no.includes('_') ? trade_no.split('_')[1] : ''
|
|
698
736
|
|
|
699
737
|
if (app.service[model] && app.service[model].refund_notify) {
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
738
|
+
await app.service[model].refund_notify({
|
|
739
|
+
ctx,
|
|
740
|
+
app,
|
|
741
|
+
order_id,
|
|
742
|
+
order: model,
|
|
743
|
+
refund_price: result.refund_amount / 100,
|
|
744
|
+
type,
|
|
745
|
+
mch_id: result.mchid,
|
|
746
|
+
refundid: result.refundid,
|
|
747
|
+
out_trade_no: result.out_trade_no,
|
|
748
|
+
is_success: result.refund_status === 'REFUND_SUCC',
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
if (result.refund_status !== 'REFUND_SUCC') {
|
|
711
752
|
app.service.log.push({
|
|
712
753
|
app,
|
|
713
754
|
message: JSON.stringify(result),
|
|
@@ -746,6 +787,7 @@ exports.retail_pay_notify = async ({ ctx, app, result }) => {
|
|
|
746
787
|
order_price: result.amount.order_amount / 100,
|
|
747
788
|
transactionid,
|
|
748
789
|
mch_id: result.mchid,
|
|
790
|
+
is_b2b: true,
|
|
749
791
|
})
|
|
750
792
|
} else {
|
|
751
793
|
app.service.log.push({
|
|
@@ -767,7 +809,7 @@ exports.pc_pay = async ({
|
|
|
767
809
|
pay_config = 'weixin_pay',
|
|
768
810
|
out_trade_no: _out_trade_no,
|
|
769
811
|
is_admin = false,
|
|
770
|
-
type = '
|
|
812
|
+
type = 'SCAN-WEIXIN',
|
|
771
813
|
prefix = '',
|
|
772
814
|
}) => {
|
|
773
815
|
if (!ctx) throw new Error('?ctx')
|
|
@@ -952,8 +994,10 @@ const handleBillData = (result) => {
|
|
|
952
994
|
return {
|
|
953
995
|
header: arr[0],
|
|
954
996
|
list,
|
|
955
|
-
result: list.map((item) => {
|
|
997
|
+
result: list.map((item, index) => {
|
|
956
998
|
const array = item.split(',')
|
|
999
|
+
|
|
1000
|
+
const mchid = array[2].substring(1)
|
|
957
1001
|
const orderdate = array[20].substring(1).slice(2, 10)
|
|
958
1002
|
const orderPrefix = array[6].substring(1).split('_')[1]
|
|
959
1003
|
let order_id = 0
|
|
@@ -976,6 +1020,7 @@ const handleBillData = (result) => {
|
|
|
976
1020
|
).format('YYYY-MM-DD')
|
|
977
1021
|
}
|
|
978
1022
|
return {
|
|
1023
|
+
mchid,
|
|
979
1024
|
created_at: array[0].substring(1),
|
|
980
1025
|
transactionid: array[5].substring(1),
|
|
981
1026
|
[`${[prefix, 'order_id'].filter(Boolean).join('_')}`]: order_id,
|
|
@@ -1000,7 +1045,285 @@ const handleBillData = (result) => {
|
|
|
1000
1045
|
}
|
|
1001
1046
|
}
|
|
1002
1047
|
|
|
1003
|
-
|
|
1048
|
+
async function downloadZip(url) {
|
|
1049
|
+
const response = await axios.get(url, {
|
|
1050
|
+
responseType: 'arraybuffer',
|
|
1051
|
+
timeout: 30000,
|
|
1052
|
+
headers: {
|
|
1053
|
+
'User-Agent':
|
|
1054
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
1055
|
+
Accept: 'application/zip,application/octet-stream,*/*;q=0.8',
|
|
1056
|
+
Referer: 'https://cus.allinpay.com/',
|
|
1057
|
+
},
|
|
1058
|
+
maxRedirects: 5,
|
|
1059
|
+
})
|
|
1060
|
+
if (response.status !== 200) {
|
|
1061
|
+
throw new Error(`下载失败: HTTP ${response.status}`)
|
|
1062
|
+
}
|
|
1063
|
+
return Buffer.from(response.data)
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
function extractXlsx(zipBuffer, targetDir) {
|
|
1067
|
+
const zip = new AdmZip(zipBuffer)
|
|
1068
|
+
zip.extractAllTo(targetDir, true)
|
|
1069
|
+
|
|
1070
|
+
const xlsxFiles = []
|
|
1071
|
+
const walk = (dir) => {
|
|
1072
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
1073
|
+
const fullPath = path.join(dir, entry.name)
|
|
1074
|
+
if (entry.isDirectory()) {
|
|
1075
|
+
walk(fullPath)
|
|
1076
|
+
} else if (entry.name.toLowerCase().endsWith('.xlsx')) {
|
|
1077
|
+
xlsxFiles.push(fullPath)
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
walk(targetDir)
|
|
1082
|
+
|
|
1083
|
+
if (xlsxFiles.length === 0) {
|
|
1084
|
+
throw new Error('ZIP 包中未找到 xlsx 文件')
|
|
1085
|
+
}
|
|
1086
|
+
return xlsxFiles
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
function parseTransactions(xlsxPath) {
|
|
1090
|
+
const workbook = XLSX.readFile(xlsxPath)
|
|
1091
|
+
const sheet = workbook.Sheets[workbook.SheetNames[0]]
|
|
1092
|
+
const rows = XLSX.utils.sheet_to_json(sheet, {
|
|
1093
|
+
header: 1,
|
|
1094
|
+
defval: '',
|
|
1095
|
+
raw: false,
|
|
1096
|
+
})
|
|
1097
|
+
|
|
1098
|
+
// 1. 定位表头行(包含 "终端号" 或 "交易时间")
|
|
1099
|
+
const headerKeywords = ['终端号', '交易时间', '交易类型']
|
|
1100
|
+
let headerIndex = rows.findIndex((row) =>
|
|
1101
|
+
headerKeywords.every((kw) => row.some((cell) => String(cell).trim() === kw))
|
|
1102
|
+
)
|
|
1103
|
+
if (headerIndex === -1) {
|
|
1104
|
+
throw new Error('未找到表头行')
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const headers = rows[headerIndex].map((h) => String(h || '').trim())
|
|
1108
|
+
|
|
1109
|
+
// 2. 从表头下一行开始取数据,遇到小计/合计/汇总行停止
|
|
1110
|
+
const stopKeywords = ['小计', '合计', '汇总']
|
|
1111
|
+
const records = []
|
|
1112
|
+
|
|
1113
|
+
for (let i = headerIndex + 1; i < rows.length; i++) {
|
|
1114
|
+
const row = rows[i]
|
|
1115
|
+
if (!row || row.every((cell) => String(cell || '').trim() === '')) continue
|
|
1116
|
+
|
|
1117
|
+
const firstNonEmpty = String(
|
|
1118
|
+
row.find((cell) => String(cell || '').trim() !== '') || ''
|
|
1119
|
+
).trim()
|
|
1120
|
+
if (stopKeywords.some((kw) => firstNonEmpty.includes(kw))) break
|
|
1121
|
+
|
|
1122
|
+
const record = {}
|
|
1123
|
+
headers.forEach((header, idx) => {
|
|
1124
|
+
if (header)
|
|
1125
|
+
record[header] = row[idx] !== undefined ? String(row[idx]).trim() : ''
|
|
1126
|
+
})
|
|
1127
|
+
records.push(record)
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
return records
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
exports.union_downloadBill = async ({
|
|
1134
|
+
ctx,
|
|
1135
|
+
date,
|
|
1136
|
+
pay_config = 'weixin_pay',
|
|
1137
|
+
}) => {
|
|
1138
|
+
const { app, appName } = getAppByCtx(ctx)
|
|
1139
|
+
const appConfig = getConfig(app)
|
|
1140
|
+
const { appId, union_mchId, union_appid, union_privateKey, union_publicKey } =
|
|
1141
|
+
await appConfig.getObject(pay_config)
|
|
1142
|
+
|
|
1143
|
+
const allinpay = new AllinPay({
|
|
1144
|
+
cusid: union_mchId,
|
|
1145
|
+
appid: union_appid,
|
|
1146
|
+
isTest: false,
|
|
1147
|
+
subAppid: appId,
|
|
1148
|
+
privateKey: union_privateKey,
|
|
1149
|
+
publicKey: union_publicKey,
|
|
1150
|
+
})
|
|
1151
|
+
|
|
1152
|
+
const result = await allinpay.downloadBill({
|
|
1153
|
+
date: date || moment().add(-1, 'days').format('YYYYMMDD'),
|
|
1154
|
+
})
|
|
1155
|
+
|
|
1156
|
+
if (result && result.url) {
|
|
1157
|
+
console.log('\n[1/2] 正在下载 ZIP 文件...')
|
|
1158
|
+
const zipBuffer = await downloadZip(result.url)
|
|
1159
|
+
console.log(`下载完成,大小: ${zipBuffer.length} bytes`)
|
|
1160
|
+
|
|
1161
|
+
console.log('\n[2/2] 正在解压...')
|
|
1162
|
+
const xlsxFiles = extractXlsx(
|
|
1163
|
+
zipBuffer,
|
|
1164
|
+
path.resolve(__dirname, `${process.cwd()}/public/upload`)
|
|
1165
|
+
)
|
|
1166
|
+
|
|
1167
|
+
console.log(`\n========== 完成 ==========`)
|
|
1168
|
+
console.log(`解压出 ${xlsxFiles.length} 个 xlsx 文件:`)
|
|
1169
|
+
xlsxFiles.forEach((f) => console.log(' -', f))
|
|
1170
|
+
const res = parseTransactions(
|
|
1171
|
+
path.resolve(
|
|
1172
|
+
__dirname,
|
|
1173
|
+
`${process.cwd()}/public/upload/${union_mchId}.xlsx`
|
|
1174
|
+
)
|
|
1175
|
+
)
|
|
1176
|
+
let formatRes = []
|
|
1177
|
+
let order_list = []
|
|
1178
|
+
let member_order_list = []
|
|
1179
|
+
let invest_order_list = []
|
|
1180
|
+
let returnRes = []
|
|
1181
|
+
for (const item of res) {
|
|
1182
|
+
const is_refund = !(item['交易类型'] === '微信支付')
|
|
1183
|
+
let order_id = 0
|
|
1184
|
+
let member_order_id = 0
|
|
1185
|
+
let invest_order_id = 0
|
|
1186
|
+
let prefix = ''
|
|
1187
|
+
if (!is_refund) {
|
|
1188
|
+
const orderPrefix = item['订单号'].split('_')[1]
|
|
1189
|
+
|
|
1190
|
+
if (orderPrefix.includes('-')) {
|
|
1191
|
+
order_id = Number(orderPrefix.split('-')[1])
|
|
1192
|
+
prefix = orderPrefix.split('-')[0]
|
|
1193
|
+
if (prefix === 'member') {
|
|
1194
|
+
member_order_id = order_id
|
|
1195
|
+
order_id = 0
|
|
1196
|
+
} else if (prefix === 'invest') {
|
|
1197
|
+
invest_order_id = order_id
|
|
1198
|
+
order_id = 0
|
|
1199
|
+
}
|
|
1200
|
+
} else {
|
|
1201
|
+
order_id = Number(orderPrefix)
|
|
1202
|
+
}
|
|
1203
|
+
} else {
|
|
1204
|
+
console.log(item['订单号'])
|
|
1205
|
+
const orderPrefix = item['订单号'].split('_')[0]
|
|
1206
|
+
prefix = orderPrefix.split('-')[0]
|
|
1207
|
+
if (prefix === 'member') {
|
|
1208
|
+
member_order_id = Number(orderPrefix.split('-')[1])
|
|
1209
|
+
order_id = 0
|
|
1210
|
+
invest_order_id = 0
|
|
1211
|
+
} else if (prefix === 'invest') {
|
|
1212
|
+
invest_order_id = Number(orderPrefix.split('-')[1])
|
|
1213
|
+
order_id = 0
|
|
1214
|
+
member_order_id = 0
|
|
1215
|
+
} else if (prefix === '') {
|
|
1216
|
+
order_id = Number(orderPrefix.split('-')[1])
|
|
1217
|
+
invest_order_id = 0
|
|
1218
|
+
member_order_id = 0
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
if (order_id) {
|
|
1222
|
+
order_list.push(order_id)
|
|
1223
|
+
}
|
|
1224
|
+
if (member_order_id) {
|
|
1225
|
+
member_order_list.push(member_order_id)
|
|
1226
|
+
}
|
|
1227
|
+
if (invest_order_id) {
|
|
1228
|
+
invest_order_list.push(invest_order_id)
|
|
1229
|
+
}
|
|
1230
|
+
formatRes.push({
|
|
1231
|
+
...item,
|
|
1232
|
+
mchid: union_mchId,
|
|
1233
|
+
date: item['交易日期'],
|
|
1234
|
+
order_id,
|
|
1235
|
+
invest_order_id,
|
|
1236
|
+
member_order_id,
|
|
1237
|
+
created_at: `${item['交易日期']} ${item['交易时间']}`,
|
|
1238
|
+
transactionid: item['参考号'],
|
|
1239
|
+
...(is_refund
|
|
1240
|
+
? {
|
|
1241
|
+
price: 0,
|
|
1242
|
+
refund_price: Math.abs(Number(item['交易金额'])),
|
|
1243
|
+
|
|
1244
|
+
type: 'REFUND',
|
|
1245
|
+
rate_price: Number(item['手续费']),
|
|
1246
|
+
}
|
|
1247
|
+
: {
|
|
1248
|
+
price: Math.abs(Number(item['交易金额'])),
|
|
1249
|
+
refund_price: 0,
|
|
1250
|
+
type: 'SUCCESS',
|
|
1251
|
+
rate_price: Number(item['手续费']),
|
|
1252
|
+
}),
|
|
1253
|
+
})
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
const orderList = await app.model.order.findAll({
|
|
1257
|
+
attributes: ['application_id', 'created_at', 'id', 'orderid'],
|
|
1258
|
+
where: {
|
|
1259
|
+
id: order_list,
|
|
1260
|
+
},
|
|
1261
|
+
include: [
|
|
1262
|
+
{
|
|
1263
|
+
model: app.model.user,
|
|
1264
|
+
attributes: ['mp_openid'],
|
|
1265
|
+
},
|
|
1266
|
+
],
|
|
1267
|
+
})
|
|
1268
|
+
|
|
1269
|
+
member_order_list = await app.model.member_order.findAll({
|
|
1270
|
+
attributes: ['application_id', 'created_at', 'id'],
|
|
1271
|
+
where: {
|
|
1272
|
+
id: member_order_list,
|
|
1273
|
+
},
|
|
1274
|
+
include: [
|
|
1275
|
+
{
|
|
1276
|
+
model: app.model.user,
|
|
1277
|
+
attributes: ['mp_openid'],
|
|
1278
|
+
},
|
|
1279
|
+
{
|
|
1280
|
+
model: app.model.application,
|
|
1281
|
+
attributes: ['name'],
|
|
1282
|
+
},
|
|
1283
|
+
],
|
|
1284
|
+
})
|
|
1285
|
+
|
|
1286
|
+
for (const item of formatRes) {
|
|
1287
|
+
let openid
|
|
1288
|
+
let remark
|
|
1289
|
+
let order_date
|
|
1290
|
+
let application_id
|
|
1291
|
+
if (item.order_id) {
|
|
1292
|
+
const orderTarget = orderList.find((o) => o.id === item.order_id)
|
|
1293
|
+
openid = orderTarget.user && orderTarget.user.mp_openid
|
|
1294
|
+
remark = `订单${orderTarget.orderid}`
|
|
1295
|
+
order_date = moment(orderTarget.created_at).format('YYYY-MM-DD')
|
|
1296
|
+
application_id = orderTarget.application_id
|
|
1297
|
+
} else if (item.member_order_id) {
|
|
1298
|
+
const orderTarget = member_order_list.find(
|
|
1299
|
+
(o) => o.id === item.member_order_id
|
|
1300
|
+
)
|
|
1301
|
+
openid = orderTarget.user && orderTarget.user.mp_openid
|
|
1302
|
+
remark = `购买${orderTarget.application.name}会员${item.member_order_id}`
|
|
1303
|
+
order_date = moment(orderTarget.created_at).format('YYYY-MM-DD')
|
|
1304
|
+
application_id = orderTarget.application_id
|
|
1305
|
+
}
|
|
1306
|
+
returnRes.push({
|
|
1307
|
+
...item,
|
|
1308
|
+
openid,
|
|
1309
|
+
remark,
|
|
1310
|
+
order_date,
|
|
1311
|
+
application_id,
|
|
1312
|
+
})
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
return returnRes
|
|
1316
|
+
} else {
|
|
1317
|
+
throw new Error('没有url')
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
exports.downloadBill = async ({
|
|
1322
|
+
ctx,
|
|
1323
|
+
pay_config = 'weixin_pay',
|
|
1324
|
+
mch_id = '',
|
|
1325
|
+
...rest
|
|
1326
|
+
}) => {
|
|
1004
1327
|
if (!ctx) throw new Error('?ctx')
|
|
1005
1328
|
const { app, appName } = getAppByCtx(ctx)
|
|
1006
1329
|
const appConfig = getConfig(app)
|
|
@@ -1009,12 +1332,14 @@ exports.downloadBill = async ({ ctx, pay_config = 'weixin_pay', ...rest }) => {
|
|
|
1009
1332
|
)
|
|
1010
1333
|
const payObj = new Pay({
|
|
1011
1334
|
appId: appId,
|
|
1012
|
-
mchId: mchId,
|
|
1335
|
+
mchId: mch_id || mchId,
|
|
1013
1336
|
key: key.length > 10 ? key : partner_key, // 微信商户平台API密钥,
|
|
1014
1337
|
pfx: await fsPromise.readFile(
|
|
1015
1338
|
path.resolve(
|
|
1016
1339
|
__dirname,
|
|
1017
|
-
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
1340
|
+
`${process.cwd()}/app/${appName}/plugins/weixin/${
|
|
1341
|
+
mch_id || key
|
|
1342
|
+
}/apiclient_cert.p12`
|
|
1018
1343
|
)
|
|
1019
1344
|
), // 微信商户平台证书
|
|
1020
1345
|
})
|
|
@@ -1036,3 +1361,95 @@ exports.uploadBill = async ({ ctx, content }) => {
|
|
|
1036
1361
|
return []
|
|
1037
1362
|
}
|
|
1038
1363
|
}
|
|
1364
|
+
|
|
1365
|
+
exports.union_refund = async ({
|
|
1366
|
+
ctx,
|
|
1367
|
+
id,
|
|
1368
|
+
prefix = '',
|
|
1369
|
+
total_fee,
|
|
1370
|
+
refund_fee,
|
|
1371
|
+
price,
|
|
1372
|
+
pay_type = 'MP-WEIXIN',
|
|
1373
|
+
type = '',
|
|
1374
|
+
config = 'weixin_mp',
|
|
1375
|
+
pay_config = 'weixin_pay',
|
|
1376
|
+
out_trade_no: _out_trade_no,
|
|
1377
|
+
out_refund_no: _out_refund_no,
|
|
1378
|
+
is_pem = false,
|
|
1379
|
+
mch_id = '',
|
|
1380
|
+
...rest
|
|
1381
|
+
}) => {
|
|
1382
|
+
if (!ctx) throw new Error('?ctx')
|
|
1383
|
+
const { app, appName } = getAppByCtx(ctx)
|
|
1384
|
+
const isPem = is_pem || lodash.get(app, 'appConfig.is_pem', false)
|
|
1385
|
+
const appConfig = getConfig(app)
|
|
1386
|
+
const {
|
|
1387
|
+
mchId,
|
|
1388
|
+
key,
|
|
1389
|
+
appId,
|
|
1390
|
+
partner_key,
|
|
1391
|
+
union_mchId,
|
|
1392
|
+
union_appid,
|
|
1393
|
+
union_privateKey,
|
|
1394
|
+
union_publicKey,
|
|
1395
|
+
} = await appConfig.getObject(pay_config)
|
|
1396
|
+
const { site_host } = await appConfig.getObject('base')
|
|
1397
|
+
const notify_url = `https://${site_host}/${appName}/weixin/union_notify`
|
|
1398
|
+
|
|
1399
|
+
const allinpay = new AllinPay({
|
|
1400
|
+
cusid: union_mchId,
|
|
1401
|
+
appid: union_appid,
|
|
1402
|
+
isTest: false,
|
|
1403
|
+
subAppid: appId,
|
|
1404
|
+
privateKey: union_privateKey,
|
|
1405
|
+
publicKey: union_publicKey,
|
|
1406
|
+
notifyUrl: notify_url,
|
|
1407
|
+
})
|
|
1408
|
+
const orderModel = prefix ? `${prefix}_order` : 'order'
|
|
1409
|
+
const out_trade_no = _out_trade_no
|
|
1410
|
+
? _out_trade_no
|
|
1411
|
+
: prefix
|
|
1412
|
+
? `${key}_${prefix}-${id}_${pay_type}`
|
|
1413
|
+
: `${key}_${id}_${pay_type}`
|
|
1414
|
+
const orderDetail = await app.model[orderModel].findOne({
|
|
1415
|
+
where: {
|
|
1416
|
+
id,
|
|
1417
|
+
},
|
|
1418
|
+
include: app.model.order_refund_record,
|
|
1419
|
+
})
|
|
1420
|
+
if (!orderDetail) throw new Error('不存在该订单')
|
|
1421
|
+
|
|
1422
|
+
// const payResult = await wxpay.queryOrderSync({
|
|
1423
|
+
// out_trade_no,
|
|
1424
|
+
// })
|
|
1425
|
+
|
|
1426
|
+
try {
|
|
1427
|
+
const refundRecords = lodash.get(orderDetail, 'order_refund_records', [])
|
|
1428
|
+
const refundNumber = refundRecords.length + 1
|
|
1429
|
+
const out_refund_no =
|
|
1430
|
+
_out_refund_no || [`${prefix}-${id}`, type, refundNumber].join('_')
|
|
1431
|
+
|
|
1432
|
+
// const data = {
|
|
1433
|
+
// ...rest,
|
|
1434
|
+
// out_trade_no,
|
|
1435
|
+
// out_refund_no,
|
|
1436
|
+
// total_fee: Number(payResult.total_fee),
|
|
1437
|
+
// refund_fee: Math.round((refund_fee || price) * 100),
|
|
1438
|
+
// notify_url: `https://${
|
|
1439
|
+
// site_host || 'api.kuashou.com'
|
|
1440
|
+
// }/${appName}/weixin/refund_notify/${pay_config}`,
|
|
1441
|
+
// }
|
|
1442
|
+
|
|
1443
|
+
const result = await allinpay.refundOrder({
|
|
1444
|
+
oldreqsn: out_trade_no,
|
|
1445
|
+
trxamt: Math.round((refund_fee || price) * 100) + '',
|
|
1446
|
+
reqsn: out_refund_no,
|
|
1447
|
+
remark: rest.refund_desc,
|
|
1448
|
+
})
|
|
1449
|
+
|
|
1450
|
+
console.log(result)
|
|
1451
|
+
return result
|
|
1452
|
+
} catch (e) {
|
|
1453
|
+
throw new Error(e.message)
|
|
1454
|
+
}
|
|
1455
|
+
}
|