214 lines
8.1 KiB
JavaScript
214 lines
8.1 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.getWithdrawCreateData = void 0;
|
||
const tslib_1 = require("tslib");
|
||
const types_1 = require("oak-domain/lib/types");
|
||
const uuid_1 = require("oak-domain/lib/utils/uuid");
|
||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||
const payClazz_1 = require("../utils/payClazz");
|
||
function calcWithdrawLossByConfig(price, withdrawLossConfig) {
|
||
const { ratio, lowest, highest, trim } = withdrawLossConfig;
|
||
(0, assert_1.default)(ratio);
|
||
let value = Math.round(price * ratio / 100);
|
||
if (highest && value > highest) {
|
||
value = highest;
|
||
}
|
||
if (lowest && value < lowest) {
|
||
value = lowest;
|
||
}
|
||
switch (trim) {
|
||
case 'jiao': {
|
||
value = Math.ceil(value / 10) * 10;
|
||
break;
|
||
}
|
||
case 'yuan': {
|
||
value = Math.ceil(value / 100) * 100;
|
||
break;
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
async function getWithdrawCreateData(params, context) {
|
||
const { system } = context.getApplication();
|
||
const withdrawLoss = system?.payConfig?.withdrawLoss;
|
||
if (!withdrawLoss || (!withdrawLoss.conservative && typeof withdrawLoss.ratio !== 'number')) {
|
||
throw new types_1.OakException('error::system.withdrawLossUnSet');
|
||
}
|
||
const { accountId, price: totalPrice, withdrawAccountId } = params;
|
||
const [account] = await context.select('account', {
|
||
data: {
|
||
id: 1,
|
||
avail: 1,
|
||
refundable: 1,
|
||
},
|
||
filter: {
|
||
id: accountId,
|
||
},
|
||
}, { dontCollect: true, forUpdate: true });
|
||
const { avail, refundable } = account;
|
||
if (totalPrice > avail) {
|
||
throw new types_1.OakInputIllegalException('withdraw', ['price'], 'error::withdraw.overflow');
|
||
}
|
||
if (totalPrice > refundable && !withdrawAccountId) {
|
||
throw new types_1.OakInputIllegalException('withdraw', ['price'], 'error::withdraw.needWithdrawAccountId');
|
||
}
|
||
const data = {
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
accountId,
|
||
creatorId: context.getCurrentUserId(),
|
||
dealLoss: 0,
|
||
dealPrice: 0,
|
||
};
|
||
let preComputeLoss = !withdrawLoss.conservative ? calcWithdrawLossByConfig(totalPrice, withdrawLoss) : 0;
|
||
let totalLoss = 0;
|
||
let price2 = 0; // 计算过程中累积已经退款的金额
|
||
data.price = totalPrice;
|
||
if (refundable) {
|
||
const refundData = [];
|
||
const pays = await context.select('pay', {
|
||
data: {
|
||
id: 1,
|
||
price: 1,
|
||
paid: 1,
|
||
refunded: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
offlineAccount: {
|
||
id: 1,
|
||
type: 1,
|
||
channel: 1,
|
||
},
|
||
applicationId: 1,
|
||
},
|
||
filter: {
|
||
refundable: true,
|
||
deposit: {
|
||
accountId,
|
||
},
|
||
orderId: {
|
||
$exists: false,
|
||
},
|
||
},
|
||
sorter: [
|
||
{
|
||
$attr: {
|
||
forbidRefundAt: 1,
|
||
},
|
||
$direction: 'asc',
|
||
}
|
||
],
|
||
}, { forUpdate: true });
|
||
for (const pay of pays) {
|
||
const { price, paid, refunded, refundable, entity, entityId, applicationId, offlineAccount } = pay;
|
||
(0, assert_1.default)(price === paid && refundable);
|
||
(0, assert_1.default)(!['account'].includes(entity));
|
||
const rest = paid - refunded;
|
||
(0, assert_1.default)(rest > 0);
|
||
let refundPrice = rest;
|
||
if (totalPrice && totalPrice - price2 < rest) {
|
||
refundPrice = totalPrice - price2;
|
||
}
|
||
price2 += refundPrice;
|
||
let loss = 0;
|
||
let lossExplanation = 'withdraw::refund.lossReason.preCompute';
|
||
// 如果是保守策略,则按照渠道配置的规则来计算这次提现的损失,并转嫁到整体的loss上
|
||
if (withdrawLoss.conservative) {
|
||
// tax + delta即是这次refund损失的税费,如果配置是conservative,就按这个价格退
|
||
const payClazz = await (0, payClazz_1.getPayClazz)(applicationId, entity, entityId, context);
|
||
const [delta] = payClazz.calcRefundTax(refundPrice);
|
||
const [tax] = payClazz.calcPayTax(refundPrice);
|
||
loss = tax + delta;
|
||
lossExplanation = loss ? 'withdraw::refund.lossReason.noBack' : 'withdraw::refund.lossReason.back';
|
||
}
|
||
else {
|
||
// 如果是预计算策略,这里就按比例折算,这样成功或者失败的时候易于计算
|
||
loss = Math.ceil(preComputeLoss * refundPrice / totalPrice);
|
||
}
|
||
totalLoss += loss;
|
||
refundData.push({
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
price: refundPrice,
|
||
loss,
|
||
creatorId: context.getCurrentUserId(),
|
||
payId: pay.id,
|
||
iState: 'refunding',
|
||
meta: {
|
||
channel: entity === 'offlineAccount' ? `withdraw::channel.offlineAccount.${offlineAccount.type}` : `${entity}:name`,
|
||
lossExplanation,
|
||
}
|
||
});
|
||
if (price2 === totalPrice) {
|
||
break;
|
||
}
|
||
}
|
||
data.refund$entity = await Promise.all(refundData.map(async (ele) => ({
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
action: 'create',
|
||
data: ele,
|
||
})));
|
||
}
|
||
else {
|
||
data.refund$entity = [];
|
||
}
|
||
if (totalPrice > price2) {
|
||
// 如果还有要退的部分,就从withdrawAccount来进行转账
|
||
const rest = totalPrice - price2;
|
||
(0, assert_1.default)(price2 === refundable); // 可退款的额度计算必须匹配
|
||
(0, assert_1.default)(withdrawAccountId);
|
||
const [withdrawAccount] = await context.select('withdrawAccount', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
enabled: 1,
|
||
entityId: 1,
|
||
channel: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id: withdrawAccountId,
|
||
}
|
||
}, { dontCollect: true });
|
||
const { enabled, entity, entityId, channel } = withdrawAccount;
|
||
(0, assert_1.default)(enabled);
|
||
let loss = 0;
|
||
let lossExplanation = 'withdraw::transfer.lossReason.preCompute';
|
||
// 如果是保守策略,则按照渠道配置的规则来计算这次提现的损失,并转嫁到整体的loss上
|
||
if (withdrawLoss.conservative) {
|
||
// 如果配置是conservative,就按计算所选渠道的费用损失值
|
||
const payClazz = await (0, payClazz_1.getPayClazz)(context.getApplicationId(), channel.entity, channel.entityId, context);
|
||
loss = payClazz.calcTransferTax(rest)[0];
|
||
lossExplanation = loss ? 'withdraw::transfer.lossReason.fee' : 'withdraw::transfer.lossReason.noFee';
|
||
}
|
||
else {
|
||
// 如果是预计算策略,这里就按比例折算,这样成功或者失败的时候易于计算
|
||
loss = Math.ceil(preComputeLoss * rest / totalPrice);
|
||
}
|
||
totalLoss += loss;
|
||
data.withdrawTransfer$withdraw = [
|
||
{
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
action: 'create',
|
||
data: {
|
||
id: await (0, uuid_1.generateNewIdAsync)(),
|
||
price: rest,
|
||
loss,
|
||
withdrawAccountId,
|
||
meta: {
|
||
lossExplanation,
|
||
}
|
||
}
|
||
}
|
||
];
|
||
}
|
||
else {
|
||
// 保持结构完整,让上层可以和withdraw$detail同构渲染
|
||
data.withdrawTransfer$withdraw = [];
|
||
}
|
||
data.loss = totalLoss;
|
||
return data;
|
||
}
|
||
exports.getWithdrawCreateData = getWithdrawCreateData;
|