diff --git a/package-lock.json b/package-lock.json index a16becf..3e0137e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "chart.js": "^4.3.0", "concurrently": "^8.0.1", "copy-to-clipboard": "^3.3.3", + "razorpay": "^2.9.6", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-countup": "^6.5.3", @@ -4634,11 +4635,11 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "dependencies": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -7952,9 +7953,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", "funding": [ { "type": "individual", @@ -14554,6 +14555,14 @@ "node": ">=0.10.0" } }, + "node_modules/razorpay": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/razorpay/-/razorpay-2.9.6.tgz", + "integrity": "sha512-zsHAQzd6e1Cc6BNoCNZQaf65ElL6O6yw0wulxmoG5VQDr363fZC90Mp1V5EktVzG45yPyNomNXWlf4cQ3622gQ==", + "dependencies": { + "axios": "^1.6.8" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -21328,11 +21337,11 @@ "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==" }, "axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", "requires": { - "follow-redirects": "^1.15.0", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" }, @@ -23777,9 +23786,9 @@ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==" }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" }, "for-each": { "version": "0.3.3", @@ -28230,6 +28239,14 @@ } } }, + "razorpay": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/razorpay/-/razorpay-2.9.6.tgz", + "integrity": "sha512-zsHAQzd6e1Cc6BNoCNZQaf65ElL6O6yw0wulxmoG5VQDr363fZC90Mp1V5EktVzG45yPyNomNXWlf4cQ3622gQ==", + "requires": { + "axios": "^1.6.8" + } + }, "react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", diff --git a/package.json b/package.json index a9931b9..b2fdc62 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "chart.js": "^4.3.0", "concurrently": "^8.0.1", "copy-to-clipboard": "^3.3.3", + "razorpay": "^2.9.6", "react": "^18.2.0", "react-chartjs-2": "^5.2.0", "react-countup": "^6.5.3", diff --git a/server/config/razorpay.js b/server/config/razorpay.js index 1813874..b93235f 100644 --- a/server/config/razorpay.js +++ b/server/config/razorpay.js @@ -1,8 +1,14 @@ const Razorpay = require("razorpay"); +if (!process.env.RAZORPAY_KEY || !process.env.RAZORPAY_SECRET) { + throw new Error("Razorpay credentials are not properly configured"); +} exports.instance = new Razorpay({ key_id: process.env.RAZORPAY_KEY, key_secret: process.env.RAZORPAY_SECRET, + headers: { + 'Content-Type': 'application/json' + } }); diff --git a/server/controllers/Payments.js b/server/controllers/Payments.js index 48488bc..9bda586 100644 --- a/server/controllers/Payments.js +++ b/server/controllers/Payments.js @@ -10,56 +10,68 @@ const CourseProgress = require("../models/CourseProgress"); //initiate the razorpay order exports.capturePayment = async(req, res) => { + try { + const {courses} = req.body; + const userId = req.user.id; - const {courses} = req.body; - const userId = req.user.id; + if(!courses || courses.length === 0) { + return res.status(400).json({success:false, message:"Please provide Course Id"}); + } - if(courses.length === 0) { - return res.json({success:false, message:"Please provide Course Id"}); - } + let totalAmount = 0; - let totalAmount = 0; + for(const course_id of courses) { + let course; + try { + course = await Course.findById(course_id); + if(!course) { + return res.status(404).json({success:false, message:"Could not find the course"}); + } - for(const course_id of courses) { - let course; - try{ - - course = await Course.findById(course_id); - if(!course) { - return res.status(200).json({success:false, message:"Could not find the course"}); - } + const uid = new mongoose.Types.ObjectId(userId); + if(course.studentsEnrolled.includes(uid)) { + return res.status(400).json({success:false, message:"Student is already Enrolled"}); + } - const uid = new mongoose.Types.ObjectId(userId); - if(course.studentsEnrolled.includes(uid)) { - return res.status(200).json({success:false, message:"Student is already Enrolled"}); + totalAmount += course.price; + } + catch(error) { + console.error("Error finding course:", error); + return res.status(500).json({success:false, message:"Error processing course details"}); } - - totalAmount += course.price; - } - catch(error) { - console.log(error); - return res.status(500).json({success:false, message:error.message}); } - } - const currency = "INR"; - const options = { - amount: totalAmount * 100, - currency, - receipt: Math.random(Date.now()).toString(), - } - try{ + const currency = "INR"; + const options = { + amount: Math.round(totalAmount * 100), // Ensure amount is a whole number + currency, + receipt: `receipt_${Date.now()}`, + notes: { + userId: userId, + courses: courses + } + }; + const paymentResponse = await instance.orders.create(options); - res.json({ - success:true, - message:paymentResponse, - }) + + // Return the response in the expected format + return res.status(200).json({ + success: true, + data: { + orderId: paymentResponse.id, + amount: paymentResponse.amount, + currency: paymentResponse.currency, + key: process.env.RAZORPAY_KEY + } + }); } catch(error) { - console.log(error); - return res.status(500).json({success:false, mesage:"Could not Initiate Order"}); + console.error("Payment initiation error:", error); + return res.status(500).json({ + success: false, + message: error.message || "Could not initiate payment" + }); } - } diff --git a/server/index.js b/server/index.js index 0cf85c1..3ce5b50 100644 --- a/server/index.js +++ b/server/index.js @@ -21,7 +21,12 @@ database.connect(); //middlewares app.use(express.json()); app.use(cookieParser()); -app.use(cors()); +app.use( + cors({ + origin: process.env.CORS_ORIGIN || "http://localhost:3000", + credentials: true, + }) +); app.use( fileUpload({ @@ -50,5 +55,4 @@ app.get("/", (req, res) => { app.listen(PORT, () => { console.log(`App is running at ${PORT}`) -}) - +}) \ No newline at end of file diff --git a/src/App.css b/src/App.css index 7bf521b..c5d9451 100644 --- a/src/App.css +++ b/src/App.css @@ -7,8 +7,7 @@ body { overflow-x: hidden; } -/* ===== Scrollbar CSS ===== */ -/* Firefox */ + * { scrollbar-width: auto; scrollbar-color: #afb2bf; diff --git a/src/services/operations/studentFeaturesAPI.js b/src/services/operations/studentFeaturesAPI.js index 6ba9992..d07eed9 100644 --- a/src/services/operations/studentFeaturesAPI.js +++ b/src/services/operations/studentFeaturesAPI.js @@ -48,10 +48,10 @@ export async function buyCourse(token, courses, userDetails, navigate, dispatch) console.log("PRINTING orderResponse", orderResponse); //options const options = { - key: process.env.RAZORPAY_KEY, - currency: orderResponse.data.message.currency, - amount: `${orderResponse.data.message.amount}`, - order_id:orderResponse.data.message.id, + key: orderResponse.data.data.key, + currency: orderResponse.data.data.currency, + amount: orderResponse.data.data.amount, + order_id: orderResponse.data.data.orderId, name:"StudyNotion", description: "Thank You for Purchasing the Course", image:rzpLogo, @@ -61,7 +61,7 @@ export async function buyCourse(token, courses, userDetails, navigate, dispatch) }, handler: function(response) { //send successful wala mail - sendPaymentSuccessEmail(response, orderResponse.data.message.amount,token ); + sendPaymentSuccessEmail(response, orderResponse.data.data.amount, token); //verifyPayment verifyPayment({...response, courses}, token, navigate, dispatch); }