Why Razorpay Is the Default Payment Gateway for Indian React Apps in 2026
If you are building a React or Next.js app for the Indian market in 2026, Razorpay is almost certainly the right payment gateway. It supports UPI, cards, netbanking, wallets, EMI, and international payments — and its developer experience is the best of any Indian payment provider. This guide walks through a complete, production-ready integration from zero to live, with code you can copy into your project today.
What You Will Build
By the end of this tutorial, you will have a working checkout that:
- Creates an order on your Next.js backend using the Razorpay Orders API
- Opens the Razorpay checkout modal on the frontend
- Verifies the payment signature on the backend for security
- Handles success, failure, and webhook events
Step 1: Create a Razorpay Account and Get Your Keys
- Go to dashboard.razorpay.com and sign up with your business PAN and bank details
- Complete the KYC — it usually takes 24–48 hours
- Navigate to Settings → API Keys and generate a test Key ID and Key Secret
- Keep the Key Secret private — it only belongs on your server, never in your React code
Step 2: Install the SDK
In your Next.js project, install the server SDK:
npm install razorpay
The frontend loads the Razorpay checkout script dynamically — no npm package needed.
Step 3: Add Environment Variables
Create or update .env.local:
RAZORPAY_KEY_ID=rzp_test_xxxxxxxxxxxx
RAZORPAY_KEY_SECRET=xxxxxxxxxxxx
NEXT_PUBLIC_RAZORPAY_KEY_ID=rzp_test_xxxxxxxxxxxx
The NEXT_PUBLIC_ prefix exposes the Key ID to the browser — which is safe. The Secret stays server-only.
Step 4: Create the Order API Route
Create app/api/razorpay/order/route.ts:
import { NextResponse } from 'next/server';
import Razorpay from 'razorpay';
const razorpay = new Razorpay({
key_id: process.env.RAZORPAY_KEY_ID!,
key_secret: process.env.RAZORPAY_KEY_SECRET!,
});
export async function POST(req: Request) {
const { amount, currency = 'INR', receipt } = await req.json();
const order = await razorpay.orders.create({
amount: amount * 100, // paise
currency,
receipt,
});
return NextResponse.json(order);
}
Step 5: Build the Checkout Button
On your frontend (for example components/CheckoutButton.tsx):
"use client";
import { useState } from "react";
declare global {
interface Window { Razorpay: any; }
}
export default function CheckoutButton({ amount }: { amount: number }) {
const [loading, setLoading] = useState(false);
const loadScript = (src: string) =>
new Promise((resolve) => {
const s = document.createElement("script");
s.src = src;
s.onload = () => resolve(true);
s.onerror = () => resolve(false);
document.body.appendChild(s);
});
const handlePayment = async () => {
setLoading(true);
const ok = await loadScript("https://checkout.razorpay.com/v1/checkout.js");
if (!ok) { alert("Razorpay SDK failed to load"); setLoading(false); return; }
const res = await fetch("/api/razorpay/order", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ amount, receipt: `rcpt_${Date.now()}` }),
});
const order = await res.json();
const options = {
key: process.env.NEXT_PUBLIC_RAZORPAY_KEY_ID,
amount: order.amount,
currency: order.currency,
name: "Your Business Name",
description: "Order Payment",
order_id: order.id,
handler: async (response: any) => {
const verify = await fetch("/api/razorpay/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(response),
});
const data = await verify.json();
if (data.ok) alert("Payment successful!");
else alert("Payment verification failed.");
},
theme: { color: "#6A27FF" },
};
const rzp = new window.Razorpay(options);
rzp.open();
setLoading(false);
};
return (
<button onClick={handlePayment} disabled={loading}>
{loading ? "Processing..." : `Pay ₹${amount}`}
</button>
);
}
Step 6: Verify the Payment Signature on the Server
This is the most important security step. Never trust the frontend response alone. Create app/api/razorpay/verify/route.ts:
import { NextResponse } from 'next/server';
import crypto from 'crypto';
export async function POST(req: Request) {
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } = await req.json();
const body = `${razorpay_order_id}|${razorpay_payment_id}`;
const expected = crypto
.createHmac('sha256', process.env.RAZORPAY_KEY_SECRET!)
.update(body)
.digest('hex');
const ok = expected === razorpay_signature;
return NextResponse.json({ ok });
}
Step 7: Add Webhook Support (Production)
Webhooks let you catch payment events even if the user closes their browser mid-transaction. Set up a webhook URL in Razorpay Dashboard pointing to /api/razorpay/webhook, and verify the signature the same way as Step 6 using the webhook secret.
Common Mistakes to Avoid
- Exposing your Key Secret in the frontend — it must only live on the server
- Skipping signature verification — opens you to refund fraud
- Using test keys in production — transactions will silently fail
- Forgetting to handle payment failure in the
handlercallback
Going Live
Once everything works in test mode:
- Switch to live mode in the Razorpay dashboard
- Generate live Key ID and Secret
- Update your environment variables on Vercel / your host
- Complete the settlement bank account verification
- Run one ₹1 test transaction end to end
Why This Matters
A solid payment integration is often the difference between a shipped product and a stalled one. Razorpay's React and Next.js integration is one of the cleanest in the Indian fintech space, and following this checklist gives you a production-ready checkout in under a day. Tech Assistant has shipped 50+ Razorpay integrations for Indian clients. If you would rather hand this off to a team that does it every day, explore our website development service or talk to us.