Framework guide

Integrate UPI payments with a Next.js backend

Build a Next.js UPI checkout with server-only order creation, Route Handler raw-body webhook verification, idempotency, and pending-state UX.

In Next.js, payment code belongs in server-only modules and Route Handlers. A client component may request checkout from your own /api route and display the returned QR or payment URL, but it must never receive X-API-Key or the webhook secret. The server reloads the cart, calculates the amount, stores a stable payment reference, and calls VyaparGateway.

The webhook Route Handler reads request.arrayBuffer() before JSON parsing, verifies the timestamped HMAC, and applies an idempotent database transition. The customer return page reads your local order state; it does not trust query-string success parameters or a browser redirect as proof of payment.

Design principles

The boundaries that keep this flow reliable

01

Use server-only boundaries

Keep gateway configuration in a module imported only by Route Handlers, Server Actions, or backend workers. Never prefix secrets with NEXT_PUBLIC_.

02

Reprice inside the route

Accept cart identity from the client, then load products and calculate the total from trusted database values.

03

Keep webhook runtime compatible

Use a runtime with the cryptography and database features your verifier needs, and test exact raw-body behavior in deployment.

Implementation workflow

From request to a durable result

  1. 1

    Create POST /api/checkout

    Authenticate the customer, load the cart, persist client_txn_id, and call create_order with a server-only API key.

  2. 2

    Render a pending order page

    Return customer-safe fields and navigate to an order route that displays paid, pending, expired, or review state from your database.

  3. 3

    Create POST /api/webhooks/vyapargateway

    Read raw bytes, verify timestamp and HMAC, parse JSON, deduplicate event_id, and update the mapped order transactionally.

  4. 4

    Add recovery

    When a webhook is delayed, let a server job call check_order_status for unresolved orders with bounded backoff.

Next.js App Router webhook skeleton

ts
// app/api/webhooks/vyapargateway/route.ts
import crypto from "node:crypto";

export const runtime = "nodejs";

export async function POST(request: Request) {
  const raw = Buffer.from(await request.arrayBuffer());
  const timestamp = request.headers.get("X-VyaparGateway-Timestamp") ?? "";
  const signature = request.headers.get("X-VyaparGateway-Signature") ?? "";

  const expected = crypto
    .createHmac("sha256", process.env.VG_WEBHOOK_SECRET!)
    .update(timestamp + ".")
    .update(raw)
    .digest("hex");

  if (!safeEqual(expected, signature)) {
    return new Response("invalid signature", { status: 401 });
  }

  const event = JSON.parse(raw.toString("utf8"));
  await acceptPaymentEventOnce(event); // transaction + unique event_id
  return Response.json({ received: true });
}

Production checklist

Verify before going live

  • No payment secret uses the NEXT_PUBLIC_ prefix
  • Checkout route authenticates user and reprices cart
  • Webhook reads arrayBuffer before JSON.parse
  • event_id and client_txn_id have database constraints
  • Return page reads local server state
  • Deployment logs redact secrets and customer payment data

Failure recovery

Next.js integration mistakes

API key in a client component

Anything shipped to the browser can be extracted. Move gateway calls to a Route Handler or protected backend service immediately.

Webhook works in dev only

Deployment runtime, proxy, or middleware may alter headers/body. Test the public HTTPS route with the Webhook Workbench.

Success page unlocks the order

A customer can open or modify that URL. Render state from your database after webhook or authenticated status verification.

FAQ

Questions developers ask

Can I call VyaparGateway from a Next.js Server Action?

A server-only action can call backend services, but a Route Handler is often clearer for checkout APIs and required for the public webhook endpoint.

Should the webhook use Edge runtime?

Choose the runtime supported by your cryptography, raw-body, database, and deployment stack. The example declares Node.js for the built-in crypto API.

How does the client know payment succeeded?

Render or refresh a server-owned order status that your webhook or reconciliation worker updates. Do not trust redirect query parameters.

Build the real flow

Move from guide to a verified ₹1 payment

The dashboard checklist takes you through merchant connection, payment verification, webhook testing, and go-live readiness.

Start setup