There are a lot of tutorials on how to integrate Razorpay with Next.js, but most of them are outdated. Let’s integrate Razorpay with Next.js 13 (app router).
Install the Razorpay NPM package:
npm install razorpay
Create an API route in app/api/order/create/route.ts
:
import { NextResponse } from "next/server";
import Razorpay from "razorpay";
import { v4 as uuid } from "uuid";
const instance = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID,
key_secret: process.env.RAZORPAY_KEY_SECRET,
});
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const totalAmount = Number(searchParams.get("amount")); // in paisa
const amount = totalAmount * 100;
const options = {
amount: amount.toString(),
currency: "INR",
receipt: uuid(),
};
const order = await instance.orders.create(options);
return NextResponse.json({ message: "success", order });
}
Create one more route in app/api/order/verify/route.ts
:
import { NextResponse } from "next/server";
import Razorpay from "razorpay";
import crypto from "crypto";
import Order from "@/models/OrderModel";
import { v4 as uuid } from "uuid";
import { connectDB } from "@/lib/mongodb";
const instance = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID,
key_secret: process.env.RAZORPAY_KEY_SECRET,
});
export async function POST(req, res) {
const {
razorpayOrderId,
razorpaySignature,
razorpayPaymentId,
email,
} = await req.json();
const body = razorpayOrderId + "|" + razorpayPaymentId;
const expectedSignature = crypto
.createHmac("sha256", process.env.RAZORPAY_KEY_SECRET)
.update(body.toString())
.digest("hex");
const isAuthentic = expectedSignature === razorpaySignature;
if (!isAuthentic) {
return NextResponse.json({ message: "invalid payment signature", error: true }, { status: 400 });
}
// connect db and update data
await connectDB();
await Order.findOneAndUpdate({ email: email },{ hasPaid: true });
return NextResponse.json({ message: "payment success", error: false }, { status: 200 });
}
Add the Razorpay script to the root layout in app/layout.tsx
:
import Script from 'next/script'
import "./globals.css";
export default function RootLayout({ children }) {
return (
<>
<html lang="en">
<body>
{children}
</body>
</html>
<Script src="https://checkout.razorpay.com/v1/checkout.js" />
</>
);
}
Create a payment button component in app/components/PaymentButton.tsx
:
"use client";
import React, { Suspense, useState } from "react";
import { useRouter } from "next/navigation";
import Loading from "@/app/loading";
import { useSession } from "next-auth/react";
import { Button, buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
const PaymentButton = ({ amount }) => {
const { userData } = useSession();
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const makePayment = async () => {
setIsLoading(true);
// make an endpoint to get this key
const key = "rzp_test_M******Pw5***n";
const data = await fetch("/api/order/create?amount=" + amount);
const { order } = await data?.json();
const options = {
key: key,
name: userData.user?.email,
currency: order.currency,
amount: order.amount,
order_id: order.id,
modal: {
ondismiss: function () {
setIsLoading(false);
},
},
handler: async function (response) {
const data = await fetch("/api/order/verify", {
method: "POST",
body: JSON.stringify({
razorpayPaymentId: response.razorpay_payment_id,
razorpayOrderId: response.razorpay_order_id,
razorpaySignature: response.razorpay_signature,
email: userData.user?.email,
}),
});
const res = await data.json();
if (res?.error === false) {
// redirect to success page
router.push("/success");
}
},
prefill: {
email: userData.user?.email,
},
};
const paymentObject = new window.Razorpay(options);
paymentObject.open();
paymentObject.on("payment.failed", function (response) {
alert("Payment failed. Please try again.");
setIsLoading(false);
});
};
return (
<>
<Suspense fallback={<Loading />}>
<div className="">
<Button
className={cn(buttonVariants({ size: "lg" }))}
disabled={isLoading}
onClick={makePayment()}
>
Pay Now
</Button>
</div>
</Suspense>
</>
);
};
export default PaymentButton;
That’s it! You have successfully integrated Razorpay with Next.js.