Unified Pay Docs

Getting Started

  • Introduction
  • Getting Started

Payment Providers

  • Stripe
  • PayPal
  • Razorpay

Resources

  • API Reference
  • Best Practices
  • FAQ
DocumentationRazorpay Integration

Razorpay Integration

Complete guide for integrating Razorpay payments with unified-pay-core

Documentation Team

This guide explains how to integrate the Razorpay payment gateway using the unified-pay-core package. It covers setup, configuration, and key operations like creating payments, capturing payments, processing refunds, and handling webhooks.

Prerequisites

  • Node.js and npm installed.
  • A Razorpay account with API keys (Key ID and Key Secret).
  • A webhook secret for verifying webhook events.
  • Install the unified-pay-core package:
npm install unified-pay-core

Setup

Obtain Razorpay Credentials

  1. Log in to your Razorpay Dashboard.
  2. Get your Key ID (rzp_test_...) and Key Secret for test or live mode.
  3. Set up a webhook in the Razorpay Dashboard and obtain the Webhook Secret.

Install Dependencies

Ensure you have express and cors for the example server:

npm install express cors

Configure the Gateway

Initialize the Razorpay gateway using the createGateways function.

const express = require("express");
const cors = require("cors");
const { createGateways } = require("unified-pay-core");

const app = express();
app.use(cors());
app.use("/webhook/razorpay", express.raw({ type: "application/json" }));
app.use(express.json());

const { razorpay } = createGateways([
  {
    gateway: "razorpay",
    config: {
      apiKey:process.env.API_KEY,
      apiSecret:  process.env.API_SECRET,
      webhookSecret: "your_webhook_secret",
    },
  },
]);

Key Operations

1. Create a Payment

Create a payment order to initiate a transaction.

app.post("/create-payment", async (req, res) => {
  try {
    const { amount, currency = "INR", metadata } = req.body;
    if (!amount) {
      return res.status(400).json({ error: "Amount is required" });
    }
    const payment = await razorpay.processPayment(amount, currency, metadata || {});
    res.json({
      success: true,
      orderId: payment.transactionId,
      transactionId: payment.transactionId,
      metadata: payment.metadata,
    });
  } catch (error) {
    console.error("Razorpay payment creation error:", error);
    res.status(500).json({ success: false, error: "Payment creation failed" });
  }
});

Input:

  • amount: Payment amount (e.g., 20 for ₹20.00).
  • currency: Currency code (e.g., "INR").
  • metadata: Optional key-value pairs for additional data.

Output:

  • orderId: Razorpay Order ID (order_...) for client-side payment processing.

💡 Developer Note: After calling processPayment, use the returned orderId (which is a Razorpay Order ID starting with order_) to initiate payment on the frontend using Razorpay's client SDK. Pass this orderId as the order_id parameter in the Razorpay checkout options. The customer will then complete the payment, and you'll receive a Payment ID (pay_...) in the success handler for subsequent operations like capture, refund, and verification.

2. Capture a Payment

Capture a payment after the customer completes the payment process.

app.post("/capture-payment", async (req, res) => {
  try {
    const { paymentId } = req.body;
    if (!paymentId || !paymentId.startsWith("pay_")) {
      return res.status(400).json({ error: "Valid paymentId is required" });
    }
    const capture = await razorpay.capturePayment(paymentId);
    const verification = await razorpay.verifyPayment(paymentId);
    res.status(capture.success ? 200 : 400).json({ capture, verification });
  } catch (error) {
    console.error("Payment capture error:", error);
    res.status(500).json({ error: "Payment capture failed" });
  }
});

Input:

  • paymentId: The Payment ID (pay_...) from the Razorpay payment process.

Output:

  • capture: Payment response with status, amount, and currency.
  • verification: Verification details confirming the payment status.

3. Process a Refund

Refund a captured payment (full or partial).

app.post("/refund-payment", async (req, res) => {
  try {
    const { paymentId, amount } = req.body;
    if (!paymentId || !paymentId.startsWith("pay_")) {
      return res.status(400).json({ error: "Valid paymentId is required" });
    }
    const refund = await razorpay.processRefund(
      paymentId,
      amount ? parseFloat(amount) : undefined
    );
    res.json({
      success: refund.success,
      refundId: refund.refundId,
      transactionId: paymentId,
      status: refund.status,
      amount: refund.amount,
      currency: refund.currency,
    });
  } catch (error) {
    console.error("Refund Error:", error);
    res.status(500).json({ success: false, error: "Refund failed" });
  }
});

Input:

  • paymentId: The Payment ID (pay_...) to refund.
  • amount: Optional partial refund amount (e.g., 10 for ₹10.00).

Output:

  • refundId: Razorpay Refund ID (rfnd_...).
  • status: Refund status (succeeded, pending, failed).

4. Verify a Payment

Verify the status of a payment.

const verification = await razorpay.verifyPayment(paymentId);

Input:

  • paymentId: The Payment ID to verify.

Output:

  • verified: Boolean indicating if the verification was successful.
  • status: Payment status (succeeded, pending, failed).
  • isValid: Boolean indicating if the payment is valid (e.g., captured).

5. Handle Webhooks

Set up a webhook endpoint to receive and process Razorpay events.

app.post("/webhook/razorpay", async (req, res) => {
  try {
    const rawBodyString = req.body.toString();
    const result = await razorpay.handleWebhookEvent(rawBodyString, req.headers);
    if (result === "INVALID") {
      return res.status(400).send("Invalid webhook signature");
    }
    const parsedBody = JSON.parse(rawBodyString);
    const eventType = parsedBody.event;
    switch (eventType) {
      case "payment.captured":
        console.log("Payment captured:", parsedBody.payload.payment.entity.id);
        break;
      case "payment.failed":
        console.log("Payment failed:", parsedBody.payload.payment.entity.id);
        break;
      case "order.paid":
        console.log("Order paid:", parsedBody.payload.order.entity.id);
        break;
      case "refund.processed":
        console.log("Refund processed:", parsedBody.payload.refund.entity.id);
        break;
      default:
        console.log("Unhandled event:", eventType);
    }
    res.status(200).send("OK");
  } catch (error) {
    console.error("Webhook processing error:", error);
    res.status(500).send("Webhook processing failed");
  }
});

Setup:

  • Use express.raw({ type: "application/json" }) for the webhook route.
  • Configure the webhook URL in the Razorpay Dashboard (e.g., https://yourdomain.com/webhook/razorpay).

Supported Events:

  • payment.captured
  • payment.failed
  • order.paid
  • refund.processed

Webhook Testing

  1. Use ngrok to expose your local server: ngrok http 3000.
  2. Update the webhook URL in the Razorpay Dashboard.
  3. Test events like payment.captured or order.paid using Razorpay's webhook testing tools.

Payment Flow Summary

Here's the complete payment flow for better understanding:

  1. Backend: Call processPayment() → Returns Order ID (order_...)
  2. Frontend: Use Order ID with Razorpay checkout SDK → Customer completes payment → Returns Payment ID (pay_...)
  3. Backend: Use Payment ID for capturePayment(), processRefund(), verifyPayment()

Notes

  • Order vs. Payment ID: processPayment returns an Order ID (order_...). After customer payment, use the Payment ID (pay_...) for capture, refund, and verification.
  • Amount Handling: Razorpay expects amounts in paise (e.g., ₹20.00 = 2000).
  • Webhook Secret: Ensure webhookSecret is set for signature verification.
  • Error Handling: Check success and error fields in responses to handle failures.
  • Client-Side Integration: Use the Order ID with Razorpay's checkout script to collect payment details.
Previous
PayPal Integration
Next
API Reference

Was this helpful?

Help us improve our documentation by providing feedback.