diff --git a/api/main_endpoints/routes/MembershipPayment.js b/api/main_endpoints/routes/MembershipPayment.js new file mode 100644 index 000000000..093dd8817 --- /dev/null +++ b/api/main_endpoints/routes/MembershipPayment.js @@ -0,0 +1,71 @@ +const express = require('express'); +const router = express.Router(); +const bodyParser = require('body-parser'); +router.use(bodyParser.json()); +const { + BAD_REQUEST, + SERVER_ERROR, + NOT_FOUND, + OK, +} = require('../../util/constants').STATUS_CODES; +const membershipState = require('../../util/constants').MEMBERSHIP_STATE; +const User = require('../models/User'); +const { getMemberExpirationDate, updateMembershipExpiration } = require('../util/userHelpers'); +const { findVerifyPayment, rejectPayment } = require('../util/membershipPaymentQueries'); +const { decodeToken } = require('../util/token-functions.js'); + +router.post('/verifyMembership', async (req, res) => { + const decoded = await decodeToken(req, membershipState.PENDING); + if (decoded.status !== OK) { + return res.sendStatus(decoded.status); + } + + const { confirmationCode } = req.body; + const userId = decoded.token._id; + + if (!confirmationCode) { + return res.sendStatus(BAD_REQUEST); + } + + const paymentDocument = await findVerifyPayment(confirmationCode, userId); + if (paymentDocument === null){ + return res.sendStatus(SERVER_ERROR); + } + if (paymentDocument === false){ + return res.sendStatus(NOT_FOUND); + } + + const paymentId = paymentDocument._id; + const { amount } = paymentDocument; + + if (amount < 20){ + const rejected = await rejectPayment(paymentId); + if (rejected === null){ + return res.sendStatus(SERVER_ERROR); + } + if (rejected === false){ + return res.sendStatus(NOT_FOUND); + } + return res.sendStatus(BAD_REQUEST); + } + + let semestersToAdd = 0; + if (amount >= 30) { + semestersToAdd = 2; + } else { + semestersToAdd = 1; + } + + const membershipUpdateResult = await updateMembershipExpiration( + decoded.token._id, + semestersToAdd + ); + + if (membershipUpdateResult === null) { + return res.sendStatus(SERVER_ERROR); + } + if (membershipUpdateResult === false) { + return res.status(NOT_FOUND).send('User not found.'); + } + return res.sendStatus(OK); +}); diff --git a/api/main_endpoints/util/membershipPaymentQueries.js b/api/main_endpoints/util/membershipPaymentQueries.js new file mode 100644 index 000000000..27361c2ba --- /dev/null +++ b/api/main_endpoints/util/membershipPaymentQueries.js @@ -0,0 +1,62 @@ +import MembershipPayment from '../main_endpoints/models/MembershipPayment.js'; + +const status = { + PENDING: 'pending', + COMPLETED: 'completed', + REJECTED: 'rejected', +}; + +function findVerifyPayment(confirmationCode, userId) { + return new Promise((resolve) => { + try { + MembershipPayment.findOneAndUpdate( + { + confirmationCode, + status: status.PENDING, + }, + { + $set: { userId, status: status.COMPLETED }, + }, + { + new: true, + runValidators: true, + }, + (error, result) => { + if (error) { + return resolve(null); + } + if (!result) { + return resolve(false); + } + return resolve(result); + } + ); + } catch (error) { + return resolve(null); + } + }); +} + +function rejectPayment(paymentId) { + return new Promise((resolve) => { + try { + MembershipPayment.findByIdAndUpdate( + paymentId, + { $set: { status: status.REJECTED } }, + (error, result) => { + if (error) { + return resolve(null); + } + if (!result) { + return resolve(false); + } + return resolve(true); + } + ); + } catch (error) { + return resolve(null); + } + }); +} + +module.exports = { findVerifyPayment, rejectPayment }; diff --git a/api/main_endpoints/util/userHelpers.js b/api/main_endpoints/util/userHelpers.js index 8ac327872..022aa9abc 100644 --- a/api/main_endpoints/util/userHelpers.js +++ b/api/main_endpoints/util/userHelpers.js @@ -203,6 +203,30 @@ function checkIfPageCountResets(lastLogin) { return lastLoginWasOverOneWeekAgo || aSundayHasPassedSinceLastLogin; } +/** + * Update a user's membershipValidUntil date + * @param {String} userId - The user's ID + * @param {Number} numberOfSemestersToSignUpFor - Number of semesters to extend + * @returns {Object} result - Contains success status and message + */ +async function updateMembershipExpiration(userId, numberOfSemestersToSignUpFor) { + try { + const newExpiration = getMemberExpirationDate(numberOfSemestersToSignUpFor); + const user = await User.findByIdAndUpdate( + userId, + { membershipValidUntil: newExpiration }, + { new: true }, + ); + if (!user) { + return false; + } + return true; + } catch (error) { + logger.error('Error updating membership:', error); + return null; + } +} + module.exports = { registerUser, getMemberExpirationDate, @@ -211,4 +235,5 @@ module.exports = { userWithEmailExists, checkIfPageCountResets, findPasswordReset, + updateMembershipExpiration };