Skip to content
Engineering· 7 min read· August 11, 2026

How to Integrate Razorpay in Next.js: A 2026 Guide for Indian Businesses

Learn how to integrate Razorpay in Next.js the production way — order creation via API routes, UPI and card checkout, and webhook verification. This 2026 guide covers the exact App Router code Indian businesses need for a secure payment flow.

Aditya Kumar
Aditya Kumar

Lead Systems Architect

How to Integrate Razorpay in Next.js: A 2026 Guide for Indian Businesses

Most payment integrations fail for the same three reasons: tutorials mix the App Router with the Pages Router, the key_secret leaks into client code, and webhook signatures are never verified. This guide shows you how to integrate Razorpay in Next.js the way we do it at VoiceAct Solutions for production e-commerce builds — a server-side order route, a checkout page that never touches your secret, and signature-verified callbacks. When you are done, you will accept UPI, cards and net banking in under an hour.

How to Integrate Razorpay in Next.js: The Architecture

The one rule that keeps payments safe: the browser may know your key_id, but only the server may know key_secret. Razorpay's flow has two halves — your backend creates an order, and Checkout.js handles the customer-facing payment. In code, that looks like:

  1. The client calls your /api/razorpay/create-order route with the amount and a receipt id.
  2. The route creates an order via the Razorpay REST API and returns the order_id.
  3. Checkout.js opens, collects UPI, card or net-banking details, and hands back a payment_id.
  4. Your server verifies the signature before marking the order paid.

Step 1: Install and Set Up Credentials

npm install razorpay

Then in .env.local:

RAZORPAY_KEY_ID=rzp_live_xxxxxxxx
RAZORPAY_KEY_SECRET=xxxxxxxxxxxxxxxx
NEXT_PUBLIC_RAZORPAY_KEY_ID=rzp_live_xxxxxxxx

Anything prefixed with NEXT_PUBLIC_ ships to the browser, so never put RAZORPAY_KEY_SECRET in one. The NEXT_PUBLIC_RAZORPAY_KEY_ID is safe to expose — it is public by design.

Step 2: Create the Order API Route

App Router route handler, app/api/razorpay/create-order/route.ts:

ts
[object Object], { ,[object Object],, ,[object Object], } ,[object Object], ,[object Object],;
,[object Object], ,[object Object], ,[object Object], ,[object Object],;

,[object Object], razorpay = ,[object Object], ,[object Object],({
  ,[object Object],: process.,[object Object],.,[object Object],!,
  ,[object Object],: process.,[object Object],.,[object Object],!,
});

,[object Object], ,[object Object], ,[object Object], ,[object Object],(,[object Object],) {
  ,[object Object], { amount, currency = ,[object Object],, receipt } = ,[object Object], req.,[object Object],();
  ,[object Object], order = ,[object Object], razorpay.,[object Object],.,[object Object],({
    ,[object Object],: ,[object Object],.,[object Object],(amount * ,[object Object],), ,[object Object],
    currency,
    receipt,
  });
  ,[object Object], ,[object Object],.,[object Object],(order);
}

Razorpay bills in paise, so multiply rupees by 100 before sending. This single mistake causes more failed checkouts than any other.

Step 3: Build the Checkout Component

tsx
[object Object],;
,[object Object], ,[object Object], ,[object Object], ,[object Object],;

,[object Object], ,[object Object], {
  ,[object Object], ,[object Object], { ,[object Object],: ,[object Object], }
}

,[object Object], ,[object Object], ,[object Object], ,[object Object],(,[object Object],) {
  ,[object Object], ,[object Object], = ,[object Object], (,[object Object],) => {
    ,[object Object], res = ,[object Object], ,[object Object],(,[object Object],, {
      ,[object Object],: ,[object Object],,
      ,[object Object],: { ,[object Object],: ,[object Object], },
      ,[object Object],: ,[object Object],.,[object Object],({ amount, receipt }),
    });
    ,[object Object], order = ,[object Object], res.,[object Object],();
    ,[object Object], rzp = ,[object Object], ,[object Object],.,[object Object],({
      ,[object Object],: process.,[object Object],.,[object Object],,
      ,[object Object],: order.,[object Object],,
      ,[object Object],: order.,[object Object],,
      ,[object Object],: order.,[object Object],,
      ,[object Object],: ,[object Object],
        ,[object Object],(,[object Object],, {
          ,[object Object],: ,[object Object],,
          ,[object Object],: { ,[object Object],: ,[object Object], },
          ,[object Object],: ,[object Object],.,[object Object],(resp),
        }),
    });
    rzp.,[object Object],();
  };

  ,[object Object], (
    ,[object Object],
  );
}

Step 4: Verify the Signature Server-Side

ts
[object Object], crypto ,[object Object], ,[object Object],;
,[object Object], { ,[object Object],, ,[object Object], } ,[object Object], ,[object Object],;

,[object Object], ,[object Object], ,[object Object], ,[object Object],(,[object Object],) {
  ,[object Object], { razorpay_order_id, razorpay_payment_id, razorpay_signature } = ,[object Object], req.,[object Object],();
  ,[object Object], expected = crypto
    .,[object Object],(,[object Object],, process.,[object Object],.,[object Object],!)
    .,[object Object],(,[object Object],)
    .,[object Object],(,[object Object],);

  ,[object Object], (expected !== razorpay_signature) {
    ,[object Object], ,[object Object],.,[object Object],({ ,[object Object],: ,[object Object], }, { ,[object Object],: ,[object Object], });
  }
  ,[object Object],
  ,[object Object], ,[object Object],.,[object Object],({ ,[object Object],: ,[object Object], });
}

Handling the Captured Webhook

The verify route covers the redirect-time handshake, but the source of truth for fulfilment should be Razorpay's payment.captured webhook. Register the webhook URL in the Razorpay dashboard, and in the handler:

  • Confirm the payload signature with the x-razorpay-signature header (the same HMAC, computed over the raw body).
  • Update the order status only if the amount matches the stored order amount.
  • Make the update idempotent — Razorpay retries delivery, and double-processing an order is a fast way to anger customers.

Production Rules That Prevent Chargebacks

  1. Verify the HMAC signature before touching order status — always.
  2. Create the order in your database before opening checkout, then reconcile with the payment.captured webhook.
  3. Enforce amount and currency server-side; never trust the client.
  4. Keep the amount in paise consistently across order, checkout and webhook.

For the surrounding stack, our guide on how to improve website conversion rate covers checkout drop-off, and the how to optimize website speed for SEO playbook keeps payment pages fast on slow networks. If you are upgrading an older codebase, our Next.js 16 upgrade guide shows what changed in the App Router.

Ship the flow above, test with Razorpay's test keys first, and you have a payment stack that survives a real launch day.

Related Articles