"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', 'oak-pay-business'); } 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 OakInputIllegalException('withdraw', ['price'], 'error::withdraw.overflow'); // } if (totalPrice > refundable && !withdrawAccountId) { throw new types_1.OakInputIllegalException('withdraw', ['price'], 'error::withdraw.needWithdrawAccountId', 'oak-pay-business'); } 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)(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$withdraw = await Promise.all(refundData.map(async (ele) => ({ id: await (0, uuid_1.generateNewIdAsync)(), action: 'create', data: ele, }))); } else { data.refund$withdraw = []; } 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)(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, creatorId: context.getCurrentUserId(), meta: { lossExplanation, } } } ]; } else { // 保持结构完整,让上层可以和withdraw$detail同构渲染 data.withdrawTransfer$withdraw = []; } data.loss = totalLoss; return data; } exports.getWithdrawCreateData = getWithdrawCreateData;