oak-pay-business/lib/triggers/pay.js

297 lines
11 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.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const uuid_1 = require("oak-domain/lib/utils/uuid");
const assert_1 = tslib_1.__importDefault(require("assert"));
const payClazz_1 = require("../utils/payClazz");
const pay_1 = require("../utils/pay");
const PayConfig_1 = require("../types/PayConfig");
async function changeOrderStateByPay(filter, context, option) {
const orders = await context.select('order', {
data: {
id: 1,
iState: 1,
price: 1,
paid: 1,
refunded: 1,
pay$order: {
$entity: 'pay',
data: {
id: 1,
price: 1,
paid: 1,
refunded: 1,
iState: 1,
},
},
},
filter,
}, { dontCollect: true });
(0, assert_1.default)(orders.length === 1);
const [{ id: orderId, pay$order: pays, price: orderPrice, paid: orderPaid, refunded: orderRefunded, iState: orderIState }] = orders;
let hasPaying = false, hasRefunding = false;
let payPaid = 0, payRefunded = 0;
for (const pay of pays) {
const { price, iState, paid, refunded } = pay;
switch (iState) {
case 'paying': {
hasPaying = true;
break;
}
case 'refunding': {
hasRefunding = true;
break;
}
default: {
break;
}
}
if (paid) {
payPaid += paid;
}
if (refunded) {
payRefunded += refunded;
}
}
const closeFn = context.openRootMode();
try {
if (hasPaying) {
// 一定是在支付状态
(0, assert_1.default)(!hasRefunding && payRefunded === 0 && payPaid < orderPrice);
if (orderIState !== 'paying' || orderPaid !== payPaid) {
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'startPaying',
data: {
paid: payPaid,
},
filter: {
id: orderId,
},
}, option);
}
}
else if (hasRefunding) {
(0, assert_1.default)(!hasPaying && payPaid === orderPrice && payPaid === orderPaid && payRefunded < orderPrice);
if (orderIState !== 'refunding' || orderRefunded !== payRefunded) {
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'startRefunding',
data: {
refunded: payRefunded,
},
filter: {
id: orderId,
},
}, option);
}
}
else if (payRefunded) {
// 已经在退款流程当中
(0, assert_1.default)(payPaid === orderPrice && payPaid === orderPaid);
const iState = payPaid === orderPrice ? 'refunded' : 'partiallyRefunded';
if (orderIState !== iState || orderRefunded !== payRefunded) {
const action = payPaid === orderPrice ? 'refundAll' : 'refundPartially';
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action,
data: {
refunded: payRefunded,
},
filter: {
id: orderId,
},
}, option);
}
}
else if (payPaid) {
// 在支付流程当中
(0, assert_1.default)(orderRefunded === 0);
const iState = payPaid === orderPrice ? 'paid' : 'partiallyPaid';
if (orderIState !== iState || orderPaid !== payPaid) {
const action = payPaid === orderPrice ? 'payAll' : 'payPartially';
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action,
data: {
paid: payPaid,
},
filter: {
id: orderId,
},
}, option);
}
}
else {
const iState = 'unpaid';
(0, assert_1.default)(orderRefunded === 0 && orderPaid === 0);
if (orderIState !== iState) {
await context.operate('order', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'payNone',
data: {},
filter: {
id: orderId,
},
}, option);
}
}
closeFn();
return 1;
}
catch (err) {
closeFn();
throw err;
}
}
const triggers = [
{
name: '当生成pay时自动开始支付流程',
entity: 'pay',
action: 'create',
when: 'after',
fn: async ({ operation }, context, option) => {
const { data } = operation;
(0, assert_1.default)(!(data instanceof Array));
const { accountId, price, orderId, id } = data;
/* if (orderId) {
if (accountId) {
// 使用帐户支付,直接成功并扣款
const close = context.openRootMode();
try {
await context.operate('pay', {
id: await generateNewIdAsync(),
action: 'succeedPaying',
data: {
accountOper$entity: [
{
id: await generateNewIdAsync(),
action: 'create',
data: {
id: await generateNewIdAsync(),
totalPlus: -price!,
availPlus: -price!,
type: 'consume',
accountId,
},
}
]
},
filter: {
id,
}
}, option);
close();
}
catch (err: any) {
close();
throw err;
}
return 1;
}
} */
// 其余情况都是进入paying流程
await context.operate('pay', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'startPaying',
data: {},
filter: {
id,
}
}, option);
return 1;
},
},
{
name: '当pay的状态发生变化时修改相应的order的状态或者account的状态',
entity: 'pay',
action: ['startPaying', 'succeedPaying', 'startClosing', 'succeedClosing', 'startRefunding',
'refundAll', 'refundPartially'],
when: 'after',
fn: async ({ operation }, context, option) => {
const { filter, action } = operation;
(0, assert_1.default)(typeof filter.id === 'string');
const [pay] = await context.select('pay', {
data: {
id: 1,
orderId: 1,
accountId: 1,
price: 1,
},
filter,
}, { dontCollect: true });
const { orderId, accountId } = pay;
if (orderId) {
return await changeOrderStateByPay({ id: orderId }, context, option);
}
else {
(0, assert_1.default)(accountId);
// 如果是支付成功,则增加帐户余额,其它暂时不支持(提现还未设计)
if (action === 'succeedPaying') {
const payPrice = pay.price;
await context.operate('accountOper', {
id: await (0, uuid_1.generateNewIdAsync)(),
action: 'create',
data: {
id: await (0, uuid_1.generateNewIdAsync)(),
totalPlus: payPrice,
availPlus: payPrice,
accountId,
type: 'deposit',
entity: 'pay',
entityId: pay.id,
}
}, option);
return 1;
}
return 0;
}
},
},
{
name: '当pay变为paying状态时调用外部下单接口',
entity: 'pay',
action: ['startPaying'],
when: 'before',
priority: 99,
fn: async ({ operation }, context, option) => {
const { data, filter } = operation;
const pays = await context.select('pay', {
data: pay_1.fullPayProjection,
filter,
}, {});
(0, assert_1.default)(pays.length === 1);
const [pay] = pays;
const { applicationId, channel, iState } = pay;
(0, assert_1.default)(iState === 'unpaid');
const payClazz = await (0, payClazz_1.getPayClazz)(applicationId, channel, context);
await payClazz.prepay(pay, data, context);
return 1;
},
},
{
name: '当pay开始close时调用外部关闭订单接口',
entity: 'pay',
action: ['close'],
when: 'before',
fn: async ({ operation }, context, option) => {
const { data, filter } = operation;
const pays = await context.select('pay', {
data: pay_1.fullPayProjection,
filter,
}, {});
(0, assert_1.default)(pays.length === 1);
const [pay] = pays;
const { applicationId, channel, iState, externalId } = pay;
(0, assert_1.default)(iState === 'unpaid' || iState === 'paying');
if (iState === 'paying' && externalId && ![PayConfig_1.PAY_CHANNEL_ACCOUNT_NAME, PayConfig_1.PAY_CHANNEL_OFFLINE_NAME].includes(channel)) {
const payClazz = await (0, payClazz_1.getPayClazz)(applicationId, channel, context);
await payClazz.close(pay);
return 1;
}
return 0;
},
},
];
exports.default = triggers;