Skip to content

Conversation

@tiffany-tran07
Copy link
Contributor

@tiffany-tran07 tiffany-tran07 commented Jan 6, 2026

for issue #1965

Co-authored-by: adarsh <110150037+adarshm11@users.noreply.github.com>
Copy link
Contributor

@charred-70 charred-70 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm tuffany is also my hero

* initial commit

* fixed spacing

* fixed spacing 2

* fixed spacing 2

* updated logic and merged find and Verify functions

* fix spacing

* refactor: improve payment verification and rejection logic

* fix

* refactor: improve error handling in membership payment verification and rejection

* fix

* final fixes

* final fixes

* refactored updateMembershipExpiration

* fix spacing
@adarshm11 adarshm11 changed the title added membership payment schema added membership payment validation Jan 7, 2026
* i pray i fixed my origin

* added instructions

* fixed import error

* fixed template formatting

* made function to call email route

---------

Co-authored-by: Charlynn Nguyen <charred@Charlynns-MacBook-Pro.local>
@adarshm11 adarshm11 changed the title added membership payment validation add payment project integration Jan 15, 2026
Copy link
Collaborator

@evanugarte evanugarte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we split this into 3 prs

  1. the changes to the email code
  2. backend changes
  3. react changes

return resolve({
from: user,
to: recipient,
subject: 'SCE Membership Confirmation',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we put the code in the subject too

Your SCE Membership Code is ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 7 to 10
createdAt: {
type: Date,
default: Date.now
},
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does mongodb already store this field? this might work

const mySchema = new mongoose.Schema({
  name: String,
  // other fields...
}, {
  timestamps: true
});

Comment on lines 49 to 61
if (amount < 20){
const rejected = await rejectPayment(paymentId);
if (rejected === null){
logger.error('Error rejecting payment with ID:', paymentId);
return res.status(SERVER_ERROR).send('Error rejecting payment.');
}
if (rejected === false){
logger.error('No payment found to reject with ID:', paymentId);
return res.status(NOT_FOUND).send('No payment found to reject.');
}
logger.info('Payment rejected due to insufficient amount. Payment ID:', paymentId);
return res.status(BAD_REQUEST).send('Payment amount insufficient.');
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont need this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


const confirmationCode = crypto.randomBytes(4).toString('hex').toUpperCase();
const newPayment = {
userId: null,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
userId: null,

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 43 to 49
async function membershipConfirmationCode(confirmCode, email) {
return new Promise((resolve) => {
axios
.post(`${MAILER_API_URL}/Mailer/sendMembershipConfirmationCode`, {
recipientEmail: email,
confirmationCode: confirmCode
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
async function membershipConfirmationCode(confirmCode, email) {
return new Promise((resolve) => {
axios
.post(`${MAILER_API_URL}/Mailer/sendMembershipConfirmationCode`, {
recipientEmail: email,
confirmationCode: confirmCode
})
async function membershipConfirmationCode(confirmationCode, recipientEmail) {
return new Promise((resolve) => {
axios
.post(`${MAILER_API_URL}/Mailer/sendMembershipConfirmationCode`, {
recipientEmail,
confirmationCode,
})

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines +22 to +29
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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we rate limit this by user email, we dont have to solve that rn, just open an issue like using the decoded email from the token, if theres more than 5 requests in the past 30 min, send 429 or something

const [confirmationCode, setConfirmationCode] = useState('');
const { user } = useSCE();

const INPUT_CLASS_NAME = 'indent-2 block w-full rounded-md border-0 py-1.5 bg-white text-black shadow-sm ring-1 ring-inset ring-gray-300 placeholder-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this above the function

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

confirmationCode,
);
if (apiResponse.error) {
bannerCallback('Unable to verify membership. Please try again later.');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include the http code in the title? in case they take a screenshot and post in the discord?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

bannerCallback('Unable to verify membership. Please try again later.');
return;
}
bannerCallback('Congrats, you confirmed your membership!');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add the date like

you are now a member (expiration DATE_GOES_HERE)

like could the api return the date that the expiration is, if the code worked

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expiration date already shows on their profile when membership is confirmed vro

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as soon as the modal closes, is the profile page updated or do they need to refresh

Comment on lines 28 to 50
<dialog id="verify-membership-modal" className="modal modal-bottom sm:modal-middle">
<div className="modal-box">
<h3 className="font-bold text-lg">Verify Membership</h3>
{accessLevel > 0 ? (
<>
<p className="text-sm text-gray-500 mt-2">
You are already verified!!!
</p>
<div className="modal-action">
<form method="dialog">
<div className="px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
<button
className="btn inline-flex w-full justify-center rounded-md px-3 py-2 text-sm font-semibold shadow-sm ring-1 ring-inset sm:w-auto"
>
Close
</button>
</div>
</form>
</div>
</>
) : (
<>
<p className="text-sm text-gray-500 mt-2">
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (something) {

return dialog_goes_here
}

return p_class_etc_goes_here

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

holy nitpick ✌️

const { findVerifyPayment, storePayment } = require('../util/membershipPaymentQueries.js');
const { decodeToken } = require('../util/token-functions.js');
const { membershipPayment = {} } = require('../../config/config.json');
const { API_KEY = 'TUFFANYCHAR' } = membershipPayment;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

be sure to update config example json to include this field

Comment on lines 82 to 83
if (amount < 20) {
return res.status(BAD_REQUEST).send('Payment amount must be at least $20.');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont need this? like the validation should happen before we call this api

we should assume that an incoming request with a valid api key means we are trying to store a payment of the correct amount

return true;
} catch (error) {
logger.error('Error updating membership:', error);
return null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets return false instead of null

true if worked, false if didnt work for any reason

return resolve({
from: user,
to: recipient,
subject: 'SCE Membership Confirmation: Your SCE Membership Code Is...',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i meant like put the code in the subject

sorry that wasnt clear

Suggested change
subject: 'SCE Membership Confirmation: Your SCE Membership Code Is...',
subject: `Your SCE Membership Code Is ${confirmCode}`,

bannerCallback('Unable to verify membership. Please try again later.');
return;
}
bannerCallback('Congrats, you confirmed your membership!');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as soon as the modal closes, is the profile page updated or do they need to refresh

}

function modalContent() {
if (accessLevel > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the membership state enum in

const membershipState = {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also how about we dont even show the modal button if the user is a valid member?

what do you think

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea mr evan ugarte ❤️‍🩹

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants