oak-pay-business/es/triggers/refund.js

283 lines
9.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { generateNewId, generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { getPayClazz } from '../utils/payClazz';
import assert from 'assert';
/**
* 当refund完成或失败时如果关联有提现去更新提现的状态
* @param context
* @param refunds
*/
async function changeWithdrawStateByRefunds(context, refunds) {
const withdraws = await context.select('withdraw', {
data: {
id: 1,
iState: 1,
price: 1,
refund$entity: {
$entity: 'refund',
data: {
id: 1,
price: 1,
iState: 1,
loss: 1,
},
},
},
filter: {
id: {
$in: refunds.filter(ele => ele.entity === 'withdraw').map(ele => ele.entityId),
}
}
}, {});
let count = 0;
for (const withdraw of withdraws) {
const { price, iState, refund$entity: relatedRefunds } = withdraw;
assert(iState === 'withdrawing');
let dealPrice = 0;
let dealLoss = 0;
let allRefundsOver = true;
for (const refund2 of relatedRefunds) {
if (refund2.iState === 'refunding') {
allRefundsOver = false;
break;
}
if (refund2.iState === 'refunded') {
dealPrice += refund2.price;
dealLoss += refund2.loss;
}
}
if (!allRefundsOver) {
continue;
}
const action = dealPrice === 0 ? 'fail' : (dealPrice === price ? 'succeed' : 'succeedPartially');
await context.operate('withdraw', {
id: await generateNewIdAsync(),
action,
data: {
dealPrice,
dealLoss,
},
filter: {
id: withdraw.id,
}
}, {});
count++;
}
return count;
}
const triggers = [
{
entity: 'refund',
action: 'create',
when: 'commit',
name: '当refund建立时触发外部退款操作',
strict: 'makeSure',
fn: async ({ ids }, context) => {
const refunds = await context.select('refund', {
data: {
id: 1,
price: 1,
loss: 1,
pay: {
id: 1,
price: 1,
iState: 1,
refunded: 1,
entity: 1,
entityId: 1,
applicationId: 1,
},
},
filter: {
id: {
$in: ids,
},
},
}, {});
for (const refund of refunds) {
const { id, price, pay } = refund;
const { price: payPrice, refunded, entity, entityId, applicationId } = pay;
const payClazz = await getPayClazz(applicationId, entity, entityId, context);
const data = await payClazz.refund(refund);
if (data) {
const closeFn = context.openRootMode();
await context.operate('refund', {
id: await generateNewIdAsync(),
data,
action: 'update',
filter: {
id,
}
}, { dontCollect: true });
closeFn();
}
}
},
},
{
entity: 'refund',
action: 'succeedRefunding',
when: 'after',
asRoot: true,
name: '退款成功时更新对应的pay状态以及对应的withdraw状态',
fn: async ({ operation }, context) => {
const { filter } = operation;
const payProj = {
id: 1,
price: 1,
refunded: 1,
entity: 1,
entityId: 1,
iState: 1,
applicationId: 1,
application: {
systemId: 1,
}
};
const refunds = await context.select('refund', {
data: {
id: 1,
price: 1,
iState: 1,
entity: 1,
entityId: 1,
pay: payProj,
loss: 1,
},
filter,
}, {});
for (const refund of refunds) {
const { price, iState, pay, loss } = refund;
assert(iState === 'refunded' && pay.iState === 'refunding');
const { price: payPrice, refunded, entity, entityId, applicationId, application } = pay;
const refunded2 = refunded + price;
assert(refunded2 <= payPrice, '退款金额不应高于pay的总金额');
const action = refunded2 === payPrice ? 'refundAll' : 'refundPartially';
await context.operate('pay', {
id: await generateNewIdAsync(),
action,
data: {
refunded: refunded2,
},
filter: {
id: pay.id,
}
}, {});
const payClazz = await getPayClazz(applicationId, entity, entityId, context);
// 实际执行的退款的额度应该是price - loss
const [delta, sysAccountEntity, sysAccountEntityId] = payClazz.calcRefundTax(price - loss);
// sysAccount上减掉实际退款的额度
await context.operate('sysAccountOper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
delta: loss - price,
entity: sysAccountEntity,
entityId: sysAccountEntityId,
refundId: refund.id,
type: 'refund',
}
}, {});
if (delta !== 0) {
// 如果有退回或者要缴纳的税费进入system相关联的account账户
const [account] = await context.select('account', {
data: {
id: 1,
},
filter: {
entity: 'system',
entityId: application.systemId,
},
}, { dontCollect: true });
await context.operate('accountOper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
accountId: account.id,
type: delta < 0 ? 'taxRefund' : 'tax',
totalPlus: -delta,
availPlus: -delta,
entity: 'refund',
entityId: refund.id,
},
}, {});
}
}
return refunds.length + await changeWithdrawStateByRefunds(context, refunds);
}
},
{
entity: 'refund',
action: 'failRefunding',
when: 'after',
asRoot: true,
name: '退款失败时更新对应的pay状态以及对应的withdraw状态',
fn: async ({ operation }, context) => {
const { filter } = operation;
const refunds = await context.select('refund', {
data: {
id: 1,
price: 1,
iState: 1,
entity: 1,
entityId: 1,
pay: {
id: 1,
price: 1,
iState: 1,
refunded: 1,
applicationId: 1,
orderId: 1,
},
},
filter,
}, {});
for (const refund of refunds) {
const { id, iState, pay } = refund;
assert(iState === 'failed' && pay.iState === 'refunding');
const { refunded } = pay;
const action = refunded === 0 ? 'stopRefunding' : 'refundPartially';
await context.operate('pay', {
id: await generateNewIdAsync(),
action,
data: {},
filter: {
id: pay.id,
}
}, {});
}
return refunds.length + await changeWithdrawStateByRefunds(context, refunds);
}
},
{
entity: 'refund',
name: '当发起退款时将对应的pay置退款中状态',
action: 'create',
asRoot: true,
when: 'before',
fn: async ({ operation }, context) => {
const { data } = operation;
if (data instanceof Array) {
data.forEach((refund) => {
assert(!refund.pay && refund.payId);
refund.pay = {
id: generateNewId(),
action: 'startRefunding',
data: {},
};
});
}
else {
data.pay = {
id: generateNewId(),
action: 'startRefunding',
data: {},
};
}
return 1;
}
},
];
export default triggers;