From 0a55c37a7e15b28fb91bf1ae3cd6abec64404eec Mon Sep 17 00:00:00 2001 From: Srishty Mangutte Date: Sun, 3 Nov 2024 08:14:40 +0530 Subject: [PATCH 1/4] fix:remove playground and unnecessary files --- src/app/playground/page.js | 21 --------------------- src/config/content/NavData.js | 10 ---------- 2 files changed, 31 deletions(-) delete mode 100644 src/app/playground/page.js delete mode 100644 src/config/content/NavData.js 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', - }, -]; From 4e287e0cd334ad3c1cb2a05b42ef0755a1b116c1 Mon Sep 17 00:00:00 2001 From: Srishty Mangutte Date: Sun, 3 Nov 2024 11:59:36 +0530 Subject: [PATCH 2/4] feat: added payment --- .env.sample | 4 +++ 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/pages/api/phonepePayment.js | 42 +++++++++++++++++++++++++ src/pages/api/phonepePaymentStatus.js | 31 +++++++++++++++++++ 8 files changed, 190 insertions(+), 1 deletion(-) 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 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..5f4a762b 100644 --- a/.env.sample +++ b/.env.sample @@ -11,3 +11,7 @@ NEXT_PUBLIC_FIREBASE_APP_ID= "" NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID= "" NEXT_PUBLIC_APOLLO_URI="" + +PHONEPE_MERCHANT_ID=your_merchant_id +PHONEPE_SALT_KEY=your_salt_key +PHONEPE_SECRET_KEY=your_secret_key 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/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 a6ba5a04e1d0c7719d7cff7a0ab5cbe928ddc9c7 Mon Sep 17 00:00:00 2001 From: Srishty Mangutte Date: Sun, 3 Nov 2024 12:06:50 +0530 Subject: [PATCH 3/4] fix: env vars --- .env.sample | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index 5f4a762b..1de3bfba 100644 --- a/.env.sample +++ b/.env.sample @@ -12,6 +12,6 @@ NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID= "" NEXT_PUBLIC_APOLLO_URI="" -PHONEPE_MERCHANT_ID=your_merchant_id -PHONEPE_SALT_KEY=your_salt_key -PHONEPE_SECRET_KEY=your_secret_key +NEXT_PUBLIC_PHONEPE_MERCHANT_ID=your_merchant_id +NEXT_PUBLIC_PHONEPE_SALT_KEY=your_salt_key +NEXT_PUBLIC_PHONEPE_SECRET_KEY=your_secret_key From f32ede622ae9ff35f094898addaee55bffeba83a Mon Sep 17 00:00:00 2001 From: Srishty Mangutte Date: Sun, 3 Nov 2024 12:07:55 +0530 Subject: [PATCH 4/4] fix: env vars --- .env.sample | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.env.sample b/.env.sample index 1de3bfba..ceda341a 100644 --- a/.env.sample +++ b/.env.sample @@ -13,5 +13,6 @@ NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID= "" NEXT_PUBLIC_APOLLO_URI="" NEXT_PUBLIC_PHONEPE_MERCHANT_ID=your_merchant_id -NEXT_PUBLIC_PHONEPE_SALT_KEY=your_salt_key -NEXT_PUBLIC_PHONEPE_SECRET_KEY=your_secret_key +NEXT_PUBLIC_PHONEPE_API_KEY="" +NEXT_PUBLIC_PHONEPE_API_KEY_INDEX="" +NEXT_PUBLIC_PHONEPE_API_URL=""