diff --git a/app/api/razorpay/order/route.js b/app/api/razorpay/order/route.js new file mode 100644 index 0000000..0b7670f --- /dev/null +++ b/app/api/razorpay/order/route.js @@ -0,0 +1,23 @@ +import Razorpay from "razorpay"; + +const razorpay = new Razorpay({ + key_id: "rzp_test_1SbLmNX2nCKRZA", + key_secret: "8CTcXrodJqKQ3cfmic84Ffdl", +}); + +export async function POST(req) { + try { + const body = await req.json(); // Parse the request body + const { amount, currency } = body; + + const order = await razorpay.orders.create({ + amount, + currency, + }); + + return new Response(JSON.stringify(order), { status: 200 }); + } catch (error) { + console.error("Error creating Razorpay order:", error); + return new Response(JSON.stringify({ error: "Failed to create Razorpay order" }), { status: 500 }); + } +} \ No newline at end of file diff --git a/app/api/razorpay/verify/route.js b/app/api/razorpay/verify/route.js new file mode 100644 index 0000000..823547b --- /dev/null +++ b/app/api/razorpay/verify/route.js @@ -0,0 +1,23 @@ +const crypto = require("crypto"); + +export default async function handler(req, res) { + if (req.method === "POST") { + const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = + req.body; + + const body = razorpay_order_id + "|" + razorpay_payment_id; + + const expectedSignature = crypto + .createHmac("sha256", "8CTcXrodJqKQ3cfmic84Ffdl") + .update(body.toString()) + .digest("hex"); + + if (expectedSignature === razorpay_signature) { + res.status(200).json({ success: true }); + } else { + res.status(400).json({ success: false }); + } + } else { + res.status(405).json({ error: "Method not allowed" }); + } +} \ No newline at end of file diff --git a/components/payment/paymentComponent.jsx b/components/payment/paymentComponent.jsx index f3937e9..6a9c075 100644 --- a/components/payment/paymentComponent.jsx +++ b/components/payment/paymentComponent.jsx @@ -1,15 +1,18 @@ import React, { useState, useEffect } from "react"; import { PayPalButtons, PayPalScriptProvider } from "@paypal/react-paypal-js"; import axios from "axios"; -import authAxios from "@/utils/axios"; +import { useRazorpay } from "react-razorpay"; const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours in milliseconds const EXCHANGE_RATE_KEY = "exchange_rate_cache"; const PaymentComponent = ({ amount, onSuccess }) => { + const { error: razorpayError, isLoading, Razorpay } = useRazorpay(); const [error, setError] = useState(null); const [isProcessing, setIsProcessing] = useState(false); const [usdAmount, setUsdAmount] = useState(null); + const [userData, setUserData] = useState({ name: "", email: "", contact: "" }); + const [showPopup, setShowPopup] = useState(false); useEffect(() => { const fetchExchangeRate = async () => { @@ -25,7 +28,7 @@ const PaymentComponent = ({ amount, onSuccess }) => { const response = await axios.get("https://apilayer.net/api/live", { params: { - access_key: "9bcb30907dee1cda9866f7b49f0f8def", + access_key: "", currencies: "USD", source: "INR", format: 1, @@ -54,19 +57,57 @@ const PaymentComponent = ({ amount, onSuccess }) => { fetchExchangeRate(); }, [amount]); - const handleApprove = async (data, actions) => { + const createOrder = async () => { try { - setIsProcessing(true); - const order = await actions.order.capture(); - onSuccess?.(order); - } catch (err) { - setError("Payment failed. Please try again."); - console.error("Payment error:", err); - } finally { - setIsProcessing(false); + const response = await axios.post("/api/razorpay/order", { + amount: amount * 100, // Amount in paise + currency: "INR", + }); + return response.data; + } catch (error) { + console.error("Error creating order:", error); + throw error; } }; + const handlePaymentSuccess = (response) => { + onSuccess?.(response); + }; + + const handleRazorpayPayment = async () => { + try { + const order = await createOrder(); + + const options = { + key: "rzp_test_1SbLmNX2nCKRZA", + amount: order.amount, + currency: order.currency, + name: "Rudraksha", + description: "", + order_id: order.id, + prefill: { + name: userData.name, + email: userData.email, + contact: userData.contact, + }, + handler: (response) => { + handlePaymentSuccess(response); // Pass the response to the success handler + }, + }; + + const razorpayInstance = new Razorpay(options); + razorpayInstance.open(); + } catch (error) { + setError("Payment failed. Please try again."); + console.error("Razorpay error:", error); + } + }; + + const handlePopupSubmit = () => { + setShowPopup(false); + handleRazorpayPayment(); + }; + if (!usdAmount) { return
Loading exchange rates...
; } @@ -78,6 +119,11 @@ const PaymentComponent = ({ amount, onSuccess }) => { {error} )} + {razorpayError && ( +
+ Error loading Razorpay: {razorpayError} +
+ )} { { }, }); }} - onShippingChange={(data, actions) => { - const allowedCountries = ["IN", "MY", "NP"]; - const shippingCountry = data.shipping_address.country_code; - - if (!allowedCountries.includes(shippingCountry)) { - return actions.reject().then(() => { - setError( - "Shipping is only available for India, Malaysia, and Nepal. Please update your address." - ); - }); + onApprove={async (data, actions) => { + try { + setIsProcessing(true); + const order = await actions.order.capture(); + onSuccess?.(order); + } catch (err) { + setError("Payment failed. Please try again."); + console.error("Payment error:", err); + } finally { + setIsProcessing(false); } - return actions.resolve(); }} - onApprove={handleApprove} onError={(err) => { setError("Payment failed. Please try again."); }} /> - -

- Note: We only ship to addresses in India, Malaysia, and Nepal. -

+ + + + {showPopup && ( +
+
+

Enter Your Details

+ setUserData({ ...userData, name: e.target.value })} + className="w-full p-2 mb-4 border border-gray-300 rounded" + /> + + setUserData({ ...userData, email: e.target.value }) + } + className="w-full p-2 mb-4 border border-gray-300 rounded" + /> + + setUserData({ ...userData, contact: e.target.value }) + } + className="w-full p-2 mb-4 border border-gray-300 rounded" + /> + +
+
+ )} + +

+ Note: We only ship to addresses in India, Malaysia, and Nepal. +

); }; diff --git a/components/shopping-cart/shoppingCart.jsx b/components/shopping-cart/shoppingCart.jsx index 09645e7..316e7bf 100644 --- a/components/shopping-cart/shoppingCart.jsx +++ b/components/shopping-cart/shoppingCart.jsx @@ -80,19 +80,29 @@ const ShoppingCart = () => { return ; } - const handlePaymentSuccess = async (order) => { - console.log("Payment SuccessFul", order); - const response = await authAxios.post("/orders/payment/", { - orderId: order.id, - payer_mail: order.payer.email_address, - address: order.purchase_units[0].shipping.address, - name: order.purchase_units[0].shipping.name.full_name, - cartId: cartItems[0].id, - }); - router.push('/accounts/profile/orders') - toast.success('You Order Is Successfull!') - console.log(response); + const handlePaymentSuccess = async (response) => { + console.log("Payment Successful", response); + + const { razorpay_payment_id, razorpay_order_id, razorpay_signature } = response; + + try { + const paymentData = { + paymentId: razorpay_payment_id, + orderId: razorpay_order_id, + signature: razorpay_signature, + cartId: cartItems[0].id, + }; + + const apiResponse = await authAxios.post("/orders/payment/", paymentData); + router.push("/accounts/profile/orders"); + toast.success("Your Order Is Successful!"); + console.log(apiResponse); + } catch (error) { + console.error("Error processing payment:", error); + toast.error("Payment processing failed. Please try again."); + } }; + return (
diff --git a/package-lock.json b/package-lock.json index a3a09b1..c332234 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,10 +25,12 @@ "framer-motion": "^12.0.6", "lucide-react": "^0.445.0", "next": "14.2.13", + "razorpay": "^2.9.6", "react": "^18", "react-dom": "^18", "react-hot-toast": "^2.4.1", "react-icons": "^5.3.0", + "react-razorpay": "^3.0.1", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7" }, @@ -5585,6 +5587,15 @@ ], "license": "MIT" }, + "node_modules/razorpay": { + "version": "2.9.6", + "resolved": "https://registry.npmjs.org/razorpay/-/razorpay-2.9.6.tgz", + "integrity": "sha512-zsHAQzd6e1Cc6BNoCNZQaf65ElL6O6yw0wulxmoG5VQDr363fZC90Mp1V5EktVzG45yPyNomNXWlf4cQ3622gQ==", + "license": "MIT", + "dependencies": { + "axios": "^1.6.8" + } + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -5641,6 +5652,15 @@ "dev": true, "license": "MIT" }, + "node_modules/react-razorpay": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/react-razorpay/-/react-razorpay-3.0.1.tgz", + "integrity": "sha512-43P6VB20quinlUDN3dUdg0XlMtoClzfvhgpPWnGrLwLE6Pr570fXdA4kC+92Wqof48A/x96TXRpLnrQ/J1eXyw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-remove-scroll": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", diff --git a/package.json b/package.json index 5878af7..c58b338 100644 --- a/package.json +++ b/package.json @@ -27,10 +27,12 @@ "framer-motion": "^12.0.6", "lucide-react": "^0.445.0", "next": "14.2.13", + "razorpay": "^2.9.6", "react": "^18", "react-dom": "^18", "react-hot-toast": "^2.4.1", "react-icons": "^5.3.0", + "react-razorpay": "^3.0.1", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7" },