-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added security checks and enhanced barter init and resp logic
- Loading branch information
1 parent
99147c9
commit fa48eac
Showing
3 changed files
with
105 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,11 +27,14 @@ const validateBarterPayInput = require('../helpers/validateBarterPay'); | |
const queueBarterNotification = require('../services/emailQueueProducer'); | ||
|
||
// Redis functional imports | ||
const redisClient = require('./redisServer'); | ||
const BarterPayment = require('../models/barterModel'); | ||
const mailOptions = require('../utils/mailOptions'); | ||
|
||
const redisClient = require('./redisServer'); | ||
const setAsync = promisify(redisClient.set).bind(redisClient); | ||
const expireAsync = promisify(redisClient.expire).bind(redisClient); | ||
const getAsync = promisify(redisClient.get).bind(redisClient); | ||
const delAsync = promisify(redisClient.del).bind(redisClient); | ||
|
||
async function tempUserDataRedis(barterId, debtorId, creditorId, groupId, amount, barterType) { | ||
try { | ||
|
@@ -59,10 +62,10 @@ async function tempUserDataRedis(barterId, debtorId, creditorId, groupId, amount | |
// Debtor route | ||
// Debtor initiates the barter option | ||
module.exports.initiateBarterPayment = async (req, res) => { | ||
const { debtorId, groupId, amount, barterType } = req.body; | ||
const creditorId = req.user._id; // the person to whom payment is made | ||
const { creditorId, groupId, amount, barterType } = req.body; | ||
const debtorId = req.user._id; // the person initiating the payment (debtor) | ||
|
||
// Custom validation function to valid input data -> helper/validateBarterPay.js | ||
// Custom validation function to validate input data -> helper/validateBarterPay.js | ||
const validationErrors = validateBarterPayInput( | ||
debtorId, | ||
creditorId, | ||
|
@@ -71,12 +74,12 @@ module.exports.initiateBarterPayment = async (req, res) => { | |
barterType | ||
); | ||
|
||
if (validateInput.size > 0) { | ||
return res.status(400).json({ message: 'Valiation error in bPay', error: validationErrors }); | ||
if (validationErrors.size > 0) { | ||
return res.status(400).json({ message: 'Validation error in bPay', error: validationErrors }); | ||
} | ||
try { | ||
if (amount > req.user.riskApetite) { | ||
return res.status(400).json({ message: 'Barter amount exceeds your risk apetite' }); | ||
return res.status(400).json({ message: 'Barter amount exceeds your risk appetite' }); | ||
} | ||
|
||
// Fetch group and creditor from the database | ||
|
@@ -96,24 +99,38 @@ module.exports.initiateBarterPayment = async (req, res) => { | |
message: "Amount exceeds group's barter cap.", | ||
}); | ||
} | ||
|
||
// Unique id for each barter payment | ||
const barterId = uuidv4(); | ||
|
||
// save the initial barter request | ||
const barterRequest = await BarterPayment.create({ | ||
barterId, // Store the UUID | ||
debtorId, | ||
creditorId, | ||
groupId, | ||
amount, | ||
barterType, | ||
agreementStatus: 'none', | ||
status: 'pending', | ||
}); | ||
await barterRequest.save(); | ||
|
||
// Store temp data to redis also | ||
await tempUserDataRedis(barterId, debtorId, creditorId, groupId, amount, barterType); | ||
|
||
// Mail the creditor to approve the payment | ||
const mailOptions = { | ||
const mailOptionsData = mailOptions({ | ||
from: `"SplitBhai Team" <[email protected]>`, | ||
to: creditor.email, | ||
to: creditor, | ||
subject: 'New Barter Request', | ||
text: `You have a new barter request from ${req.user.name} for ${amount}. Barter Type: ${barterType}`, | ||
}; | ||
await queueBarterNotification(mailOptions); | ||
}); | ||
await queueBarterNotification(mailOptionsData); | ||
|
||
res.status(201).json({ message: 'Barter request initiated successfully.' }); | ||
res.status(201).json({ message: 'Barter request initiated successfully.', barterId }); | ||
} catch (error) { | ||
res.status(500).json({ message: 'Server error.', error: err.message }); | ||
console.error('Error initiating barter payment:', error); | ||
res.status(500).json({ message: 'Server error.', error: error.message }); | ||
} | ||
}; | ||
|
||
|
@@ -126,36 +143,56 @@ module.exports.respBarter = async (req, res) => { | |
const barterPaymentInfo = await getAsync(barterId); | ||
|
||
if (!barterPaymentInfo) { | ||
res.status(400).json({ | ||
return res.status(400).json({ | ||
message: 'The barter request has expired, Please ask the debtor to initiate payment again', | ||
}); | ||
} | ||
|
||
// Parse the data from redis ttl | ||
const { debtorId, groupId, amount, barterType } = JSON.parse(barterPaymentInfo); | ||
const { | ||
debtorId, | ||
groupId, | ||
amount, | ||
barterType, | ||
creditorId: storedCreditor, | ||
} = JSON.parse(barterPaymentInfo); | ||
|
||
// check if the reposding creditor is the one whose id is stored in data | ||
if (creditorId.toString() != creditorId) { | ||
res.status(403).json({ message: 'You are not authorized creditor for this barter payment' }); | ||
if (creditorId.toString() != storedCreditor.toString()) { | ||
return res | ||
.status(403) | ||
.json({ message: 'You are not authorized creditor for this barter payment' }); | ||
} | ||
|
||
// Check for status "approve" & "reject" one by one | ||
if (status == 'approve') { | ||
const barterPayment = await BarterPayment.create({ | ||
debtorId, | ||
debtorId, | ||
groupId, | ||
amount, | ||
barterType, | ||
status: 'approved', | ||
}); | ||
// delete barter request from redis | ||
await delAsync(barterId); | ||
|
||
await barterPayment.save(); | ||
// Check for status "creditor_approved" & "reject" one by one | ||
if (status == 'approve') { | ||
let barterRequest = await BarterPayment.findOne({ barterId }); | ||
if (!barterRequest) { | ||
await BarterPayment.create({ | ||
debtorId, | ||
debtorId, | ||
groupId, | ||
amount, | ||
barterType, | ||
agreementStatus: 'creditor_approved', | ||
status: 'approved', | ||
}); | ||
} else { | ||
barterRequest.agreementStatus = 'creditor_approved'; | ||
await barterRequest.save(); | ||
} | ||
|
||
// Delete from redis cache since it's updated into mongodb | ||
await delAsync(barterId); | ||
|
||
return res | ||
.status(200) | ||
.json({ message: 'Barter request approved successfully', barterPayment }); | ||
.json({ message: 'Barter request approved successfully', barterRequest }); | ||
} else if (status == 'reject') { | ||
await delAsync(barterRequest); | ||
return res.status(200).json({ message: 'Barter request declined' }); | ||
} else { | ||
return res.status(400).json({ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
// custom mail options based on the params passed for different use cases | ||
// separation of concerns | ||
|
||
function mailOptions({ from, to, subject, text }) { | ||
const defaultFrom = '[email protected]'; | ||
if (!from || typeof from !== 'string') { | ||
from = defaultFrom; | ||
} | ||
if (!to || !to.email || typeof to.email !== 'string') { | ||
throw new Error('Invalid recipient email address.'); | ||
} | ||
|
||
if (!subject || typeof subject !== 'string') { | ||
throw new Error('Invalid subject.'); | ||
} | ||
if (!text || typeof text !== 'string') { | ||
throw new Error('Invalid email body.'); | ||
} | ||
|
||
// Safe email data to be returned | ||
return { | ||
from: from, | ||
to: to.email, | ||
subject: subject, | ||
text: text, | ||
}; | ||
} | ||
|
||
module.exports = mailOptions; |