Skip to content

Commit

Permalink
added barter settlement option
Browse files Browse the repository at this point in the history
abhiraj-ku committed Oct 3, 2024
1 parent b5db8cb commit dd024b9
Showing 9 changed files with 142 additions and 61 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -10,4 +10,8 @@ src/utils/constants.js

build
dist
.npm
.npm


#misc
prettierrcconf.txt
14 changes: 14 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "always",
"endOfLine": "lf",
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"proseWrap": "preserve"
}
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
<div align="center">
<img src="https://img.shields.io/badge/Version-1.0.0-green" alt="Version Badge" />
<img src="https://img.shields.io/badge/License-MIT-orange" alt="License Badge" />
<img src="https://img.shields.io/badge/Type-Web%20Application-purple" alt="Type Badge" />
<img src="https://img.shields.io/badge/Type-Mobile%20Application-purple" alt="Type Badge" />
<img src="https://img.shields.io/badge/For-Group%20Expense%20Management-color" alt="Target Audience Badge" />
</div>

20 changes: 17 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
"rate-limit-redis": "^4.2.0",
"redis": "^4.7.0",
"speakeasy": "^2.0.0",
"uuid": "^10.0.0",
"validator": "^13.12.0"
},
"devDependencies": {
124 changes: 87 additions & 37 deletions src/controllers/barterSettlement.js
Original file line number Diff line number Diff line change
@@ -15,40 +15,41 @@ How this Works?
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 { promisify } = require('util');
const { v4: uuidv4 } = require('uuid');

// User & Group Models
const User = require('../models/userModel');
const groupModel = require('../models/groupModel');

// Helpers and emails services
const validateBarterPayInput = require('../helpers/validateBarterPay');
const queueBarterNotification = require('../services/emailQueueProducer');

// Redis functional imports
const redisClient = require('./redisServer');
const BarterPayment = require('../models/barterModel');
const setAsync = promisify(redisClient.set).bind(redisClient);
const expireAsync = promisify(redisClient.expire).bind(redisClient);
const getAsync = promisify(redisClient.get).bind(redisClient);

async function tempUserDataRedis(
debtorId,
creditorId,
groupId,
amount,
barterType
) {
async function tempUserDataRedis(barterId, debtorId, creditorId, groupId, amount, barterType) {
try {
const tempUserData = json.Stringify({
const tempUserData = JSON.stringify({
debtorId,
creditorId,
groupId,
amount,
barterType,
status: "pending",
});

await setAsync("tempData", tempUserData);
// set the barter data in redis with barterId as key
await setAsync(barterId, tempUserData);

// Set the expiration time
const deleteAfter = process.env.TEMP_DATA_STORE || 300;
await expireAsync(debtorId, 300);
await expireAsync(debtorId, deleteAfter);

console.log(`Barter details saved to redis for ${deleteAfter} seconds `);
} catch (error) {
console.error(`Error while saving barter data to redis..`, error);
@@ -61,6 +62,7 @@ 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

// Custom validation function to valid input data -> helper/validateBarterPay.js
const validationErrors = validateBarterPayInput(
debtorId,
creditorId,
@@ -70,50 +72,98 @@ module.exports.initiateBarterPayment = async (req, res) => {
);

if (validateInput.size > 0) {
return res
.status(400)
.json({ message: "Valiation error in bPay", error: validationErrors });
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" });
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." });
return res.status(404).json({ message: 'Group not found.' });
}

if (!creditor) {
return res.status(404).json({ message: "Creditor not found." });
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.",
});
}

// Unique id for each barter payment
const barterId = uuidv4();

await tempUserDataRedis(barterId, debtorId, creditorId, groupId, amount, barterType);

// Mail the creditor to approve the payment
const mailOptions = {
from: `"SplitBhai Team" <backend.team@splitbhai.com>`,
to: creditor.email,
subject: "New Barter Request",
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." });
res.status(201).json({ message: 'Barter request initiated successfully.' });
} catch (error) {
res.status(500).json({ message: "Server error.", error: err.message });
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) => {};
// Creditor "reject","approve" the barter payment initiated
module.exports.respBarter = async (req, res) => {
const { barterId, status } = req.body;
const creditorId = req.user._id;

try {
const barterPaymentInfo = await getAsync(barterId);

if (!barterPaymentInfo) {
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);

// 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' });
}

// Check for status "approve" & "reject" one by one
if (status == 'approve') {
const barterPayment = await BarterPayment.create({
debtorId,
debtorId,
groupId,
amount,
barterType,
status: 'approved',
});

await barterPayment.save();

return res
.status(200)
.json({ message: 'Barter request approved successfully', barterPayment });
} else if (status == 'reject') {
return res.status(200).json({ message: 'Barter request declined' });
} else {
return res.status(400).json({
message: "Invalid status. Please provide 'approve' or 'reject' as status.",
});
}
} catch (error) {
console.log(`Error responsding to the barter request`, error);
res.status(500).json({ message: 'Server error while responding to barter request.' });
}
};
12 changes: 6 additions & 6 deletions src/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -259,12 +259,6 @@ exports.resendVerificationEmail = async (req, res) => {
}
};

// TODO: Implement the forgot password route

// TODO: Implement the update password route

// TODO: Implement the reset token route

// Take user's choice to create group or join group

module.exports.handleUserChoice = async (req, res) => {
@@ -302,3 +296,9 @@ module.exports.handleUserChoice = async (req, res) => {
});
}
};

// TODO: Implement the forgot password route

// TODO: Implement the update password route

// TODO: Implement the reset token route
22 changes: 10 additions & 12 deletions src/models/barterModel.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
const mongoose = require("mongoose");
const mongoose = require('mongoose');
const { Schema } = mongoose;

const barterPaymentsSchema = new Schema(
{
debtor: {
type: Schema.Types.ObjectId,
ref: "User",
ref: 'User',
required: true,
},
creditor: {
type: Schema.Types.ObjectId,
ref: "User",
ref: 'User',
required: true,
},
groupId: {
type: Schema.Types.ObjectId,
ref: "Group",
ref: 'Group',
required: true,
},
amount: {
@@ -29,24 +29,22 @@ const barterPaymentsSchema = new Schema(
},
status: {
type: String,
enum: ["pending", "approved", "rejected"],
default: "pending",
},
settlementDate: {
type: Date,
enum: ['pending', 'approved', 'rejected'],
default: 'pending',
},
settlementdate: { type: Date, default: Date.now },
},
{ timestamps: true }
);

// setBarter date
barterPaymentsSchema.pre("save", function (next) {
if (this.status == "approved" && !this.settlementDate) {
barterPaymentsSchema.pre('save', function (next) {
if (this.status == 'approved' && !this.settlementDate) {
this.settlementDate = Date.now();
}
next();
});

const BarterPayment = mongoose.model("BarterPayment", barterPaymentsSchema);
const BarterPayment = mongoose.model('BarterPayment', barterPaymentsSchema);

module.exports = BarterPayment;
2 changes: 1 addition & 1 deletion src/services/templates/verification-email-template.html
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ <h3>Verification Code</h3>
<p>If you did not request this verification code, please ignore this email.</p>
<p>Thank you!</p>
<div class="footer">
<p>&copy; {{year}} Your Company. All rights reserved.</p>
<p>&copy; {{year}} splitBhai. All rights reserved.</p>
</div>
</div>
</body>

0 comments on commit dd024b9

Please sign in to comment.