wechat_miniprogram_refund.js
6.08 KB
1
2
3
4
5
6
7
8
9
10
11
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
const Router = require('koa-router');
const Promise = require('bluebird');
const tenpay = require('tenpay');
const config = require('config');
const mathjs = require('mathjs');
const route = new Router();
const payApi = new tenpay(
Object.assign({}, config.get('wechatPay'), {
pfx: require('fs').readFileSync(__dirname + '/../../cert/apiclient_cert.p12')
}),
true
);
const utils = require('../../utils/utils');
const { logger } = utils;
async function callRefundApi(redis, tradeNo, payPrice, refundFee, refundNo) {
const logTitle = `【退款接口】【订单编号】:${tradeNo}`;
// 获取退款单号序列
const refundNoSuffix = await redis.incr(`wechat:refund:suffix:${tradeNo}`);
const demoRefundFee = config.get('demo.refundFee');
logger.info(
`${logTitle},开始请求退款,【参数】:${JSON.stringify({
out_trade_no: tradeNo,
out_refund_no: refundNo ? refundNo : tradeNo + refundNoSuffix,
total_fee: demoRefundFee || mathjs.evaluate(`${payPrice}*100`),
refund_fee: demoRefundFee || mathjs.evaluate(`${refundFee}*100`)
})}`
);
const resultRefund = await payApi.refund({
out_trade_no: tradeNo,
out_refund_no: refundNo ? refundNo : tradeNo + refundNoSuffix,
total_fee: demoRefundFee || mathjs.evaluate(`${payPrice}*100`),
refund_fee: demoRefundFee || mathjs.evaluate(`${refundFee}*100`)
});
const result =
resultRefund.result_code.toUpperCase() === 'SUCCESS' &&
resultRefund.return_code.toUpperCase() === 'SUCCESS';
if (!result) {
logger.info(
`${logTitle},调用微信退款接口失败。【resultRefund】:${JSON.stringify(resultRefund)}`
);
} else {
logger.info(`${logTitle},调用微信退款接口成功。`);
}
return result;
}
async function updateRefundStatus(connection, redis, order, wechatRefundProcessKey, refundFee) {
const transitFee = (await redis.get(`wechat:refund:transit:${order.order_id}`)) || '0.00';
// 如果所有金额都已退款,把订单置位申请退款中。否则订单为原状态
const refundStatus = mathjs.evaluate(`${transitFee}/100+${refundFee}==${order.pay_price}`)
? 1
: order.refund_status;
const result = await connection.queryAsync(
'UPDATE ct_store_order SET refund_status=? WHERE order_id=?',
[refundStatus, order.order_id]
);
if (result && result.affectedRows) {
// 设置时间间隔
await redis.setex(wechatRefundProcessKey, 300, 1);
// 设置在途资金
await redis.incrby(
`wechat:refund:transit:${order.order_id}`,
mathjs.evaluate(`${refundFee}*100`)
);
logger.info(`【退款接口】【订单编号】:${order.order_id},退款申请已发出!`);
return '退款申请已发出';
} else {
logger.info(
`【退款接口】【订单编号】:${order.order_id},更新退款信息失败,【result】:${JSON.stringify(
result
)}`
);
return '退款申请已发出,更新退款信息失败';
}
}
route.post('/wxpay/js/refund', async ctx => {
const refundInfo = ctx.request.body;
const tradeNo = refundInfo.tradeNo;
const refundNo = refundInfo.refundNo;
const logTitle = `【退款接口】【订单编号】:${tradeNo}`;
const wechatRefundProcessKey = `wechat:refund:${tradeNo}`;
// 记录客户端IP
logger.info(`${logTitle},【客户端ip】:${ctx.ip}`);
// 同一个订单退款间隔不能少于5分钟
const processing = await ctx.redis.get(wechatRefundProcessKey);
if (processing) {
ctx.status = 400;
return (ctx.body = '同一订单两次退款申请需要间隔5分钟');
}
if (refundInfo && tradeNo && refundInfo.refundFee) {
logger.info(`${logTitle},【请求参数】:${JSON.stringify(refundInfo)}`);
const connection = Promise.promisifyAll(await ctx.pool.getConnectionAsync());
// 当前在途金额
const transitFee = (await ctx.redis.get(`wechat:refund:transit:${tradeNo}`)) || '0.00';
// 此次退款金额
const refundFee = parseFloat(refundInfo.refundFee);
// 查询订单真实性
const result = await connection.queryAsync(
'SELECT id,pay_price,refund_price,refund_status,order_id FROM ct_store_order WHERE order_id=?',
[tradeNo]
);
if (result && result.length) {
const order = result[0];
// 退款金额大于实际支付金额,为避开小数,所有金额以分结算
if (
!refundNo &&
mathjs.evaluate(
`${transitFee}+${refundFee}*100+${order.refund_price}*100>${order.pay_price}*100`
)
) {
logger.info(`${logTitle},退款总额大于实付金额`);
ctx.status = 400;
ctx.body = '退款总额大于实付金额';
} else {
// 调用微信退款接口
const callApiSuccess = await callRefundApi(
ctx.redis,
tradeNo,
order.pay_price,
refundFee,
refundNo
);
if (callApiSuccess) {
// 退款申请已发出,状态可置为200;
ctx.status = 200;
// 重新发起的退款
if (refundNo) {
ctx.body = '已发起重新退款';
} else {
try {
ctx.body = await updateRefundStatus(
connection,
ctx.redis,
order,
wechatRefundProcessKey,
refundFee
);
} catch (e) {
logger.info(
`${logTitle},更新退款信息失败,【错误】:${e.message},【wechatRefundProcessKey】:${wechatRefundProcessKey},【refundFee】:${refundFee}`
);
ctx.body = '退款申请已发出,更新退款信息失败';
}
}
} else {
ctx.status = 500;
ctx.body = '退款失败';
}
}
} else {
logger.info(`${logTitle},订单编号有误`);
ctx.status = 400;
ctx.body = '订单编号有误';
}
// 释放数据库链接
connection.release();
} else {
logger.error(`${logTitle},未收到任何参数`);
ctx.status = 400;
ctx.body = '参数缺失';
}
});
module.exports = route;