From 0c7263e544bfce72738f4a1a24208364d5169573 Mon Sep 17 00:00:00 2001 From: Srishty Mangutte <68679980+Srish-ty@users.noreply.github.com> Date: Sun, 3 Nov 2024 12:11:00 +0530 Subject: [PATCH 1/3] chore: payment gateway integration (#242) * fix:remove playground and unnecessary files * feat: added payment * fix: env vars * fix: env vars --- .env.sample | 5 +++ package.json | 3 +- src/app/callback/page.jsx | 31 +++++++++++++++++++ src/app/payment/page.jsx | 44 +++++++++++++++++++++++++++ src/app/payment/payButton.jsx | 29 ++++++++++++++++++ src/app/payment/payment.styles.jsx | 7 +++++ src/app/playground/page.js | 21 ------------- src/config/content/NavData.js | 10 ------ src/pages/api/phonepePayment.js | 42 +++++++++++++++++++++++++ src/pages/api/phonepePaymentStatus.js | 31 +++++++++++++++++++ 10 files changed, 191 insertions(+), 32 deletions(-) create mode 100644 src/app/callback/page.jsx create mode 100644 src/app/payment/page.jsx create mode 100644 src/app/payment/payButton.jsx create mode 100644 src/app/payment/payment.styles.jsx delete mode 100644 src/app/playground/page.js delete mode 100644 src/config/content/NavData.js create mode 100644 src/pages/api/phonepePayment.js create mode 100644 src/pages/api/phonepePaymentStatus.js diff --git a/.env.sample b/.env.sample index f2817e41..ceda341a 100644 --- a/.env.sample +++ b/.env.sample @@ -11,3 +11,8 @@ NEXT_PUBLIC_FIREBASE_APP_ID= "" NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID= "" NEXT_PUBLIC_APOLLO_URI="" + +NEXT_PUBLIC_PHONEPE_MERCHANT_ID=your_merchant_id +NEXT_PUBLIC_PHONEPE_API_KEY="" +NEXT_PUBLIC_PHONEPE_API_KEY_INDEX="" +NEXT_PUBLIC_PHONEPE_API_URL="" diff --git a/package.json b/package.json index 128dfb53..354aae69 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@react-three/fiber": "^8.17.7", "ajv": "^8.17.1", "axios": "^1.7.7", + "crypto": "^1.0.1", "firebase": "^10.14.1", "flowbite-react": "^0.10.2", "framer-motion": "^11.5.6", @@ -55,8 +56,8 @@ "swiper": "^11.1.14", "three": "^0.168.0", "twin.macro": "^3.4.1", - "yarn": "^1.22.22", "uuidv4": "^6.2.13", + "yarn": "^1.22.22", "zod": "^3.23.8" }, "devDependencies": { diff --git a/src/app/callback/page.jsx b/src/app/callback/page.jsx new file mode 100644 index 00000000..1bbd7d94 --- /dev/null +++ b/src/app/callback/page.jsx @@ -0,0 +1,31 @@ +'use client'; +import { useEffect } from 'react'; +import { useRouter } from 'next/router'; + +const CallbackPage = () => { + const router = useRouter(); + const { transactionId } = router.query; + + useEffect(() => { + const checkPaymentStatus = async () => { + if (transactionId) { + const response = await fetch('/api/phonepePaymentStatus', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ transactionId }), + }); + const data = await response.json(); + if (data.status === 'SUCCESS') { + alert('Payment successful!'); + } else { + alert('Payment failed. Please try again.'); + } + } + }; + checkPaymentStatus(); + }, [transactionId]); + + return
Processing your payment...
; +}; + +export default CallbackPage; diff --git a/src/app/payment/page.jsx b/src/app/payment/page.jsx new file mode 100644 index 00000000..2c67a6a0 --- /dev/null +++ b/src/app/payment/page.jsx @@ -0,0 +1,44 @@ +'use client'; +import React, { useEffect, useState } from 'react'; +import PayButton from './payButton'; +import { PayContainer } from './payment.styles'; +import Cookies from 'js-cookie'; + +const PaymentPage = () => { + const [isClient, setIsClient] = useState(false); + const [userEmail, setUserEmail] = useState(''); + + useEffect(() => { + setIsClient(true); + const userData = Cookies.get('userData'); + const email = (userData && JSON.parse(userData).email) || ''; + setUserEmail(email); + }, []); + + const validEmails = [ + 'innovision2024.nitr@gmail.com', + 'srishtymangutte@gmail.com', + 'jaiswal2nikhil@gmail.com', + ]; + + if (!isClient) { + return null; + } + + return ( +
+ + {validEmails.includes(userEmail) ? ( + <> +

Payment Page

+ + + ) : ( +

404 page not found

+ )} +
+
+ ); +}; + +export default PaymentPage; diff --git a/src/app/payment/payButton.jsx b/src/app/payment/payButton.jsx new file mode 100644 index 00000000..b5ef1f89 --- /dev/null +++ b/src/app/payment/payButton.jsx @@ -0,0 +1,29 @@ +'use client'; +import { useState } from 'react'; +import { SecondaryButton } from '@/components/shared/Typography/Buttons'; + +const PayButton = () => { + const handlePayment = async () => { + const response = await fetch('/api/phonepePayment', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + amount: 100, + orderId: 'unique_order_id_12345', + callbackUrl: `${window.location.origin}/callback`, + }), + }); + + const data = await response.json(); + + if (data.success) { + window.location.href = data.paymentUrl; + } else { + alert('Payment failed. Please try again.'); + } + }; + + return ; +}; + +export default PayButton; diff --git a/src/app/payment/payment.styles.jsx b/src/app/payment/payment.styles.jsx new file mode 100644 index 00000000..f4a4225a --- /dev/null +++ b/src/app/payment/payment.styles.jsx @@ -0,0 +1,7 @@ +import tw from 'twin.macro'; +import styled from 'styled-components'; + +export const PayContainer = styled.div` + background-image: url('https://res.cloudinary.com/dhv234qct/image/upload/v1728888341/Inno2k24/yupqoznoucyhxwchhbv7.png'); + ${tw`w-full flex flex-col items-center justify-center bg-cover pt-36 `} +`; diff --git a/src/app/playground/page.js b/src/app/playground/page.js deleted file mode 100644 index 7a5517df..00000000 --- a/src/app/playground/page.js +++ /dev/null @@ -1,21 +0,0 @@ -'use client'; - -import { Hero } from '@/components/HeroSection/Hero'; -import { AboutUsMain } from '@/components/AboutUs/Main'; -import { EventSectionMain } from '@/components/EventsSection/wrapperComponents/Main'; -import { StatisticsMain } from '@/components/Statistics/wrapper/Main'; -import { SponsorSection } from '@/components/Sponsors/sponsors'; - -const Page = () => { - return ( - <> - - - - - - - ); -}; - -export default Page; diff --git a/src/config/content/NavData.js b/src/config/content/NavData.js deleted file mode 100644 index 27c8f755..00000000 --- a/src/config/content/NavData.js +++ /dev/null @@ -1,10 +0,0 @@ -export const NavData = [ - { - text: 'Home', - path: '/', - }, - { - text: 'Playground', - path: '/playground', - }, -]; diff --git a/src/pages/api/phonepePayment.js b/src/pages/api/phonepePayment.js new file mode 100644 index 00000000..284996ee --- /dev/null +++ b/src/pages/api/phonepePayment.js @@ -0,0 +1,42 @@ +import crypto from 'crypto'; + +export default async function handler(req, res) { + if (req.method === 'POST') { + const { amount, orderId, callbackUrl } = req.body; + + const payload = { + merchantId: process.env.NEXT_PUBLIC_PHONEPE_MERCHANT_ID, + transactionId: orderId, + amount: amount, + merchantTransactionId: orderId, + redirectUrl: callbackUrl, + }; + + const payloadString = JSON.stringify(payload); + const base64Payload = Buffer.from(payloadString).toString('base64'); + + const checksum = crypto + .createHmac('sha256', process.env.NEXT_PUBLIC_PHONEPE_API_KEY) + .update(base64Payload + '/pg/v1/pay' + process.env.NEXT_PUBLIC_PHONEPE_API_KEY) + .digest('base64'); + + try { + const response = await fetch(`${process.env.NEXT_PUBLIC_PHONEPE_API_URL}/pg/v1/pay`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-VERIFY': `${checksum}###${process.env.NEXT_PUBLIC_PHONEPE_API_KEY_INDEX}`, + }, + body: payloadString, + }); + + const data = await response.json(); + res.status(200).json(data); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Payment initiation failed' }); + } + } else { + res.status(405).json({ error: 'Method not allowed' }); + } +} diff --git a/src/pages/api/phonepePaymentStatus.js b/src/pages/api/phonepePaymentStatus.js new file mode 100644 index 00000000..119042d2 --- /dev/null +++ b/src/pages/api/phonepePaymentStatus.js @@ -0,0 +1,31 @@ +import crypto from 'crypto'; + +export default async function handler(req, res) { + if (req.method === 'POST') { + const { transactionId } = req.body; + + const url = `${process.env.NEXT_PUBLIC_PHONEPE_API_URL}/status/${process.env.NEXT_PUBLIC_PHONEPE_MERCHANT_ID}/${transactionId}`; + const checksum = crypto + .createHmac('sha256', process.env.NEXT_PUBLIC_PHONEPE_API_KEY) + .update(`${url}${process.env.NEXT_PUBLIC_PHONEPE_API_KEY_INDEX}`) + .digest('base64'); + + try { + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'X-VERIFY': `${checksum}###${process.env.NEXT_PUBLIC_PHONEPE_API_KEY_INDEX}`, + }, + }); + + const data = await response.json(); + res.status(200).json(data); + } catch (error) { + console.error(error); + res.status(500).json({ error: 'Failed to verify payment status' }); + } + } else { + res.status(405).json({ error: 'Method not allowed' }); + } +} From 7a58807a57e35a6db7ea94c4ba9f6d196110ac92 Mon Sep 17 00:00:00 2001 From: Ayan Date: Sun, 3 Nov 2024 12:12:32 +0530 Subject: [PATCH 2/3] fix:Registration of nitr student (#241) * Feat: improved Upper Slider,lower Slider,fix Responsiveness of EventPage * Feat:fix minor changes * feat:add payment Section in Register Form * feat:update payment section in register page * feat:update payment section in registration page * feat: fix prettier issue * fix: prettier fix * feat: update Payment Section * fix:prettier issue * faet: fix event section rendering, update eventdata. * fix:registration nitr student * fix:minor changes --- src/components/EventsPage/Event/Event.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/EventsPage/Event/Event.jsx b/src/components/EventsPage/Event/Event.jsx index 101927d4..44078d28 100644 --- a/src/components/EventsPage/Event/Event.jsx +++ b/src/components/EventsPage/Event/Event.jsx @@ -19,6 +19,7 @@ const initialState = { uid: null, registered: false, hasPaid: false, + isNitR: false, isCurrentSlideId: 0, }; @@ -49,9 +50,9 @@ export const Events = ({ EventItem }) => { const userData = Cookies.get('userDataDB'); if (userData) { - const { id, hasPaid } = JSON.parse(userData); + const { id, hasPaid, isNitR } = JSON.parse(userData); - setState((prev) => ({ ...prev, uid: id, hasPaid })); + setState((prev) => ({ ...prev, uid: id, hasPaid, isNitR })); } } catch (error) { console.error('Error parsing user data cookie:', error); @@ -115,7 +116,7 @@ export const Events = ({ EventItem }) => { })); try { - if (state.hasPaid) { + if (state.hasPaid || state.isNitR) { const response = await handleLoadingAndToast( registerForEvent({ variables: { From c55adf3cdd9505a99585cb69e6c557d13bf64787 Mon Sep 17 00:00:00 2001 From: Ayush <135319056+ayussh-2@users.noreply.github.com> Date: Sun, 3 Nov 2024 12:13:16 +0530 Subject: [PATCH 3/3] fix: add prefilled details from firebase data (#240) fix: add prefilled details from firebase --- src/app/register/page.jsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app/register/page.jsx b/src/app/register/page.jsx index 7df0a557..b1b254ad 100644 --- a/src/app/register/page.jsx +++ b/src/app/register/page.jsx @@ -25,12 +25,12 @@ import { userSchema } from '@/config/zodd/userDetailsSchema'; import { AuthContext } from '@/context/auth-context'; import { REGISTER_ORG } from '@/graphql/mutations/organizationMutations'; import { REGISTER_USER } from '@/graphql/mutations/userMutations'; +import { GET_USER_BY_UID } from '@/graphql/queries/userQueries'; import { useIsLoggedIn } from '@/hooks/useIsLoggedIn'; import { useUserDetails } from '@/hooks/useUserDetails'; import handleLoadingAndToast from '@/utils/handleLoadingToast'; import { uploadToCloudinary } from '@/utils/uploadToCloudinary'; import { useMutation, useSuspenseQuery } from '@apollo/client'; -import { GET_USER_BY_UID } from '@/graphql/queries/userQueries'; import { DisclaimerPara, @@ -301,6 +301,16 @@ function Page() { } const userCookie = Cookies.get('userDataDB'); + const userGoogleData = Cookies.get('userData'); + + if (userGoogleData) { + const googleData = JSON.parse(userGoogleData); + setUserDetails((prev) => ({ + ...prev, + name: googleData.name.toUpperCase(), + email: googleData.email, + })); + } const hasUserData = userDataDB?.user?.data?.length > 0; const userData = hasUserData ? userDataDB.user.data[0] : null; const isNitR = userData?.college === nitrID;