diff --git a/.dockerignore b/.dockerignore index b512c09..a2d1530 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,8 @@ -node_modules \ No newline at end of file +node_modules +*.git +dist +build +package-lock.json +.npm +*.vscode +*.tmp \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9d7dc69..cc77836 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,10 @@ #ignore specific file -src/utils/constants.js \ No newline at end of file +src/utils/constants.js + +*.vscode + +build +dist +.npm \ No newline at end of file diff --git a/src/controllers/barterSettlement.js b/src/controllers/barterSettlement.js index 4433fa2..d580faf 100644 --- a/src/controllers/barterSettlement.js +++ b/src/controllers/barterSettlement.js @@ -18,3 +18,102 @@ Note: This is completely an Experiment Feature // TODO: Implement the two step barter payment settlemnt // TODO: Step 1 is to generate the barter payment by debtor // TODO: Step 2 is to reject or approve barter settlement by debtor based on their risk appetite + +const validateBarterPayInput = require("../helpers/validateBarterPay"); +const groupModel = require("../models/groupModel"); +const User = require("../models/userModel"); +const redisClient = require("./redisServer"); +const { promisify } = require("util"); +const expireAsync = promisify(redisClient.expire).bind(redisClient); +const queueBarterNotification = require("../services/emailQueueProducer"); + +const setAsync = promisify(redisClient.set).bind(redisClient); + +async function tempUserDataRedis( + debtorId, + creditorId, + groupId, + amount, + barterType +) { + try { + const tempUserData = json.Stringify({ + debtorId, + creditorId, + groupId, + amount, + barterType, + status: "pending", + }); + + await setAsync("tempData", tempUserData); + const deleteAfter = process.env.TEMP_DATA_STORE || 300; + await expireAsync(debtorId, 300); + console.log(`Barter details saved to redis for ${deleteAfter} seconds `); + } catch (error) { + console.error(`Error while saving barter data to redis..`, error); + } +} + +// 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 validationErrors = validateBarterPayInput( + debtorId, + creditorId, + groupId, + amount, + barterType + ); + + if (validateInput.size > 0) { + return res + .status(400) + .json({ message: "Valiation error in bPay", error: validationErrors }); + } + try { + if (amount > req.user.riskApetite) { + return res + .status(400) + .json({ message: "Barter amount exceeds your risk apetite" }); + } + + // Fetch group and creditor from the database + const group = await groupModel.findById(groupId); + const creditor = await User.findById(creditorId); + + if (!group) { + return res.status(404).json({ message: "Group not found." }); + } + + if (!creditor) { + return res.status(404).json({ message: "Creditor not found." }); + } + + if (amount > group.maxBarterAmount) { + return res.status(400).json({ + message: "Amount exceeds group's barter cap.", + }); + } + // Mail the creditor to approve the payment + const mailOptions = { + from: `"SplitBhai Team" `, + to: creditor.email, + subject: "New Barter Request", + text: `You have a new barter request from ${req.user.name} for ${amount}. Barter Type: ${barterType}`, + }; + await queueBarterNotification(mailOptions); + await tempUserDataRedis(debtorId, creditorId, groupId, amount, barterType); + + res.status(201).json({ message: "Barter request initiated successfully." }); + } catch (error) { + res.status(500).json({ message: "Server error.", error: err.message }); + } +}; + +// TODO: Creditor approval route implementation +// Creditor "reject","confirm" the barter payment initiated +module.exports.respBarter = async (req, res) => {}; diff --git a/src/helpers/validateBarterPay.js b/src/helpers/validateBarterPay.js new file mode 100644 index 0000000..c4df8e7 --- /dev/null +++ b/src/helpers/validateBarterPay.js @@ -0,0 +1,34 @@ +const validator = require("validator"); +const { isMongoId, isFloat, isEmpty } = validator; + +function validateBarterPayInput(creditorID, groupID, amount, barterType) { + const errors = new Map(); + + // Validate creditor ID + if (!isMongoId(creditorID)) { + errors.set("creditorID", "Invalid creditor ID"); + } + // Validate Debtor ID + if (!isMongoId(debtorId)) { + errors.set("debtorID", "Invalid debtor ID"); + } + + // Validate group ID + if (!isMongoId(groupId)) { + errors.set("groupId", "Invalid group ID"); + } + + // Validate amount + if (!isFloat(amount.toString(), { min: 0 })) { + errors.set("amount", "Amount must be a positive number."); + } + + // Validate barterType + if (isEmpty(barterType)) { + errors.set("barterType", "Barter type is required."); + } + + return errors; +} + +module.exports = validateBarterPayInput; diff --git a/src/models/userModel.js b/src/models/userModel.js index 31cab43..22a54d0 100644 --- a/src/models/userModel.js +++ b/src/models/userModel.js @@ -43,7 +43,7 @@ const userSchema = new mongoose.Schema({ ref: "Event", }, ], - rislApetite: { + riskApetite: { type: Number, required: true, min: 0, diff --git a/src/services/emailServices.js b/src/services/emailServices.js index f27ce1b..ed99dc2 100644 --- a/src/services/emailServices.js +++ b/src/services/emailServices.js @@ -40,7 +40,7 @@ async function storeuser(name, email) { await setAsync(email, userData); const ttl = process.env.REDIS_TTL || 300; - await expireAsync(email, 120); + await expireAsync(email, 300); console.log(`User details saved to redis for ${ttl} seconds `); } catch (error) {