feat: added beneficiary wise transaction limit
This commit is contained in:
@@ -50,11 +50,19 @@ async function validateOutsideBank(req, res) {
|
||||
|
||||
async function addBeneficiary(req, res) {
|
||||
try {
|
||||
const { accountNo, ifscCode, accountType, name } = req.body;
|
||||
const { accountNo, ifscCode, accountType, name, transactionLimit } =
|
||||
req.body;
|
||||
const customerNo = req.user;
|
||||
const query =
|
||||
'INSERT INTO beneficiaries (customer_no, account_no, account_type, ifsc_code, name) VALUES ($1, $2, $3, $4, $5)';
|
||||
await db.query(query, [customerNo, accountNo, accountType, ifscCode, name]);
|
||||
'INSERT INTO beneficiaries (customer_no, account_no, account_type, ifsc_code, name, transaction_limit) VALUES ($1, $2, $3, $4, $5, $6)';
|
||||
await db.query(query, [
|
||||
customerNo,
|
||||
accountNo,
|
||||
accountType,
|
||||
ifscCode,
|
||||
name,
|
||||
transactionLimit,
|
||||
]);
|
||||
res.json({ message: 'SUCCESS' });
|
||||
} catch (error) {
|
||||
logger.error(error, 'Error adding beneficiary');
|
||||
@@ -132,6 +140,45 @@ async function getIfscDetails(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
async function updateBeneficiaryLimit(req, res) {
|
||||
const { beneficiaryAccountNo, newLimit } = req.body;
|
||||
|
||||
if (newLimit === undefined) {
|
||||
return res.status(400).json({ error: 'newLimit is required' });
|
||||
}
|
||||
|
||||
if (typeof newLimit !== 'number') {
|
||||
return res.status(400).json({ error: 'Limit must be a numeric value' });
|
||||
}
|
||||
|
||||
if (newLimit < 0 || newLimit > 1000000) {
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: 'Limit must be between 10 and 1000000' });
|
||||
}
|
||||
|
||||
try {
|
||||
const updatedBeneficiary = await beneficiaryService.updateBeneficiaryLimit(
|
||||
req.user,
|
||||
beneficiaryAccountNo,
|
||||
newLimit
|
||||
);
|
||||
return res.status(200).json({
|
||||
message: `limit for beneficiary ${updatedBeneficiary.account_no} updated successfully to ${updatedBeneficiary.transaction_limit}`,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.message === 'ACCOUNT_NOT_FOUND') {
|
||||
logger.warn(
|
||||
`beneficiary ${beneficiaryAccountNo} does not exist for the customer ${req.user}`
|
||||
);
|
||||
return res.status(400).json({ error: 'INVALID_BENEFICIARY_ACCOUNT_NO' });
|
||||
} else {
|
||||
logger.error(error, 'error deleting beneficiary');
|
||||
return res.status(500).json({ error: 'INTERNAL_SERVER_ERROR' });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
@@ -171,4 +218,5 @@ module.exports = {
|
||||
getIfscDetails,
|
||||
getBeneficiary,
|
||||
deleteBeneficiary,
|
||||
updateBeneficiaryLimit,
|
||||
};
|
||||
|
||||
33
src/middlewares/beneficiaryLimit.middleware.js
Normal file
33
src/middlewares/beneficiaryLimit.middleware.js
Normal file
@@ -0,0 +1,33 @@
|
||||
const { getSingleBeneficiary } = require('../services/beneficiary.service');
|
||||
const { getBeneficiaryUsedLimit } = require('../services/paymentLimit.service');
|
||||
const { logger } = require('../util/logger');
|
||||
|
||||
async function checkBeneficiaryLimit(req, res, next) {
|
||||
const { amount, toAccount } = req.body;
|
||||
const { user } = req;
|
||||
const beneficiary = await getSingleBeneficiary(user, toAccount);
|
||||
if (!beneficiary) {
|
||||
return res
|
||||
.status(403)
|
||||
.json({ error: 'No beneficiary added for this account' });
|
||||
}
|
||||
const beneficiaryLimit = beneficiary.transaction_limit;
|
||||
|
||||
if (!beneficiary.transactionLimit) {
|
||||
logger.info('NO LIMIT SET FOR BENEFICIARY. ALLOWING TRANSACTIONS');
|
||||
next();
|
||||
}
|
||||
const usedLimit = await getBeneficiaryUsedLimit(user, toAccount);
|
||||
|
||||
const remainingLimit = beneficiaryLimit - usedLimit;
|
||||
|
||||
if (amount > remainingLimit) {
|
||||
const midnight = new Date();
|
||||
midnight.setHours(24, 0, 0, 0);
|
||||
res.set('Retry-After', midnight.toUTCString());
|
||||
return res.status(403).json({ error: 'Beneficairy limit exhausted' });
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
module.exports = { checkBeneficiaryLimit };
|
||||
@@ -13,5 +13,6 @@ router.delete(
|
||||
'/:beneficiaryAccountNo',
|
||||
beneficiaryController.deleteBeneficiary
|
||||
);
|
||||
router.patch('/update-limit', beneficiaryController.updateBeneficiaryLimit);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -4,6 +4,9 @@ const { logger } = require('../util/logger');
|
||||
const impsValidator = require('../validators/imps.validator');
|
||||
const paymentSecretValidator = require('../validators/payment.secret.validator');
|
||||
const { checkLimit } = require('../middlewares/limitCheck.middleware');
|
||||
const {
|
||||
checkBeneficiaryLimit,
|
||||
} = require('../middlewares/beneficiaryLimit.middleware');
|
||||
const {
|
||||
checkBeneficiaryCooldown,
|
||||
} = require('../middlewares/cooldown.middleware');
|
||||
@@ -13,7 +16,8 @@ router.use(
|
||||
impsValidator,
|
||||
paymentSecretValidator,
|
||||
checkLimit,
|
||||
checkBeneficiaryCooldown
|
||||
checkBeneficiaryCooldown,
|
||||
checkBeneficiaryLimit
|
||||
);
|
||||
|
||||
const impsRoute = async (req, res) => {
|
||||
|
||||
@@ -4,6 +4,9 @@ const { logger } = require('../util/logger');
|
||||
const neftValidator = require('../validators/neft.validator.js');
|
||||
const paymentSecretValidator = require('../validators/payment.secret.validator');
|
||||
const { checkLimit } = require('../middlewares/limitCheck.middleware');
|
||||
const {
|
||||
checkBeneficiaryLimit,
|
||||
} = require('../middlewares/beneficiaryLimit.middleware');
|
||||
const {
|
||||
checkBeneficiaryCooldown,
|
||||
} = require('../middlewares/cooldown.middleware');
|
||||
@@ -13,7 +16,8 @@ router.use(
|
||||
neftValidator,
|
||||
paymentSecretValidator,
|
||||
checkLimit,
|
||||
checkBeneficiaryCooldown
|
||||
checkBeneficiaryCooldown,
|
||||
checkBeneficiaryLimit
|
||||
);
|
||||
|
||||
const neftRoute = async (req, res) => {
|
||||
|
||||
@@ -4,6 +4,9 @@ const { logger } = require('../util/logger');
|
||||
const rtgsValidator = require('../validators/rtgs.validator.js');
|
||||
const paymentSecretValidator = require('../validators/payment.secret.validator');
|
||||
const { checkLimit } = require('../middlewares/limitCheck.middleware');
|
||||
const {
|
||||
checkBeneficiaryLimit,
|
||||
} = require('../middlewares/beneficiaryLimit.middleware');
|
||||
const {
|
||||
checkBeneficiaryCooldown,
|
||||
} = require('../middlewares/cooldown.middleware');
|
||||
@@ -13,7 +16,8 @@ router.use(
|
||||
rtgsValidator,
|
||||
paymentSecretValidator,
|
||||
checkLimit,
|
||||
checkBeneficiaryCooldown
|
||||
checkBeneficiaryCooldown,
|
||||
checkBeneficiaryLimit
|
||||
);
|
||||
|
||||
const rtgsRoute = async (req, res) => {
|
||||
|
||||
@@ -36,7 +36,7 @@ async function validateOutsideBank(accountNo, ifscCode, name) {
|
||||
|
||||
async function getSingleBeneficiary(customerNo, accountNo) {
|
||||
const queryStr =
|
||||
'SELECT b.account_no, b.name, b.account_type, b.ifsc_code, b.created_at, i.bank_name, i.branch_name FROM beneficiaries b JOIN ifsc_details i ON b.ifsc_code = i.ifsc_code WHERE customer_no = $1 AND account_no = $2';
|
||||
'SELECT b.account_no, b.name, b.account_type, b.ifsc_code, b.created_at, b.transaction_limit, i.bank_name, i.branch_name FROM beneficiaries b JOIN ifsc_details i ON b.ifsc_code = i.ifsc_code WHERE customer_no = $1 AND account_no = $2';
|
||||
const result = await db.query(queryStr, [customerNo, accountNo]);
|
||||
return result.rows[0];
|
||||
}
|
||||
@@ -53,7 +53,7 @@ async function deleteBeneficiary(customerNo, beneficiaryAccountNo) {
|
||||
|
||||
async function getAllBeneficiaries(customerNo) {
|
||||
const queryStr =
|
||||
'SELECT b.account_no, b.name, b.account_type, b.ifsc_code, b.created_at, i.bank_name, i.branch_name FROM beneficiaries b JOIN LATERAL( SELECT * FROM ifsc_details i WHERE i.ifsc_code = b.ifsc_code LIMIT 1 ) i ON true WHERE customer_no = $1';
|
||||
'SELECT b.account_no, b.name, b.account_type, b.ifsc_code, b.created_at, b.transaction_limit, i.bank_name, i.branch_name FROM beneficiaries b JOIN LATERAL( SELECT * FROM ifsc_details i WHERE i.ifsc_code = b.ifsc_code LIMIT 1 ) i ON true WHERE customer_no = $1';
|
||||
const result = await db.query(queryStr, [customerNo]);
|
||||
const list = result.rows.map((row) => {
|
||||
const details = {
|
||||
@@ -61,6 +61,7 @@ async function getAllBeneficiaries(customerNo) {
|
||||
name: row['name'],
|
||||
accountType: row['account_type'],
|
||||
createdAt: row['created_at'],
|
||||
transactionLimit: row['transaction_limit'],
|
||||
};
|
||||
if (row['ifsc_code'] === '_') {
|
||||
details['bankName'] = 'THE KANGRA CENTRAL COOPERATIVE BANK LIMITED';
|
||||
@@ -74,10 +75,29 @@ async function getAllBeneficiaries(customerNo) {
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
async function updateBeneficiaryLimit(
|
||||
customerNo,
|
||||
beneficiaryAccountNo,
|
||||
newLimit
|
||||
) {
|
||||
const queryStr =
|
||||
'UPDATE beneficiaries set transaction_limit = $1 WHERE customer_no = $2 AND account_no = $3 RETURNING *';
|
||||
const result = await db.query(queryStr, [
|
||||
newLimit,
|
||||
customerNo,
|
||||
beneficiaryAccountNo,
|
||||
]);
|
||||
if (result.rowCount == 0) {
|
||||
throw new Error('ACCOUNT_NOT_FOUND');
|
||||
}
|
||||
return result.rows[0];
|
||||
}
|
||||
module.exports = {
|
||||
validateWithinBank,
|
||||
validateOutsideBank,
|
||||
getAllBeneficiaries,
|
||||
getSingleBeneficiary,
|
||||
deleteBeneficiary,
|
||||
updateBeneficiaryLimit,
|
||||
};
|
||||
|
||||
@@ -20,6 +20,14 @@ async function getUsedLimit(customerNo, clientType) {
|
||||
return Number(usedLimit);
|
||||
}
|
||||
|
||||
async function getBeneficiaryUsedLimit(customerNo, beneficiaryAccountNo) {
|
||||
const query =
|
||||
"SELECT SUM(amount::numeric) AS used_limit FROM transactions WHERE created_at BETWEEN CURRENT_DATE AND (CURRENT_DATE + INTERVAL '1 day') AND customer_no = $1 AND to_account = $2";
|
||||
const result = await db.query(query, [customerNo, beneficiaryAccountNo]);
|
||||
const usedLimit = result.rows[0].used_limit;
|
||||
return Number(usedLimit);
|
||||
}
|
||||
|
||||
async function setDailyLimit(customerNo, clientType, amount) {
|
||||
let query = '';
|
||||
if (clientType === 'IB') {
|
||||
@@ -35,4 +43,9 @@ async function setDailyLimit(customerNo, clientType, amount) {
|
||||
logger.info(`set new limit: ${result.rowCount} rows affected`);
|
||||
}
|
||||
|
||||
module.exports = { getDailyLimit, getUsedLimit, setDailyLimit };
|
||||
module.exports = {
|
||||
getDailyLimit,
|
||||
getUsedLimit,
|
||||
setDailyLimit,
|
||||
getBeneficiaryUsedLimit,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user