Razorpay Payment Gateway Integration In Node JS & React JS

amit kumar
7 min readMay 19, 2023

--

Razorpay Payment Gateway Integration In Node JS & React JS

Node.js Implementation of Razorpay Payment API

The payment gateway, of which there are many on the market, is where every online transaction is processed. You could look here. Here, I’ll explain how to use Node.js to implement Razorpay Payment API on an online store.

we will be discussing how to integrate Razorpay payment gateway in reactjs with APIs written in Node.js

By the way, logic will remain the same so it doesn’t matter what languages are. So, let’s start-

Step 1:

Create an account on Razorpay here — Razorpay Dashboard and get your Key Id and Key Secret.
You will get them in Settings -> API keys.

Note- Just check and confirm that you are in Test Mode.

Step 2:

In this step we will understand how Razorpay payment gateway works. What is the flow of the Razorpay payment gateway?

These steps are the main blocks of payment flow in Razorpay-

  1. The customer creates an Order.
  2. Razorpay creates an order id for it, which we use in our integration.
  3. Using the order id a checkout UI is opened, in which the customer enters the details and selects payment methods, and pays the amount.
  4. Then, this single payment gets a payment id which gets processed and we get razorpay_signature, razorpay_order_id, and razorpay_payment_id in response.
  5. Then, we have to authorize and capture this payment in order to settle and complete the whole transaction.

Step 3:

Let’s code now.

Backend

Initializing Razorpay

npm i razorpay

Employing Secret Keys

To access all Razorpay APIs, we must first install the Razorpay package and then utilize those secret keys.

const Razorpay = require('razorpay')
const razorpay = new Razorpay({key_id: 'rzp_test_uGoq5ADrFTgYRAhk',   key_secret: 'FySe2f58UYtg6Hjkj1a5s6clk9B'})

API route for creating an order

We must use the UI to place an order and call the API listed below. To keep the fundamental information about the order and return the order. I constructed the request body for Razorpay in this API and then called the create order function. I then needed to send this order Id to the frontend team so they could utilize it and the secret keys for the Razorpay checkout page. The status will change from “Failed” to “Authorized” if the payment is successful. In the Razorpay dashboard, you may check.

app.post('/order', async (req, res) => {
// initializing razorpay
const razorpay = new Razorpay({
key_id: req.body.keyId,
key_secret: req.body.keySecret,
});

// setting up options for razorpay order.
const options = {
amount: req.body.amount,
currency: req.body.currency,
receipt: "any unique id for every order",
payment_capture: 1
};
try {
const response = await razorpay.orders.create(options)
res.json({
order_id: response.id,
currency: response.currency,
amount: response.amount,
})
} catch (err) {
res.status(400).send('Not able to create order. Please try again!');
}
});

API route for Payment Capture

In the Razorpay settings, we must enter the following URL in a webhook with a special secret key and choose the “payment.capture” column so that it will be activated whenever a payment is successful.

The secret key you supplied in the webhook must be used to verify the signature in this URL. The status of the payment will change to “Captured” after verification

const crypto = require('crypto')

const secret_key = '1234567890'

app.post('/paymentCapture', (req, res) => {

// do a validation

const data = crypto.createHmac('sha256', secret_key)

data.update(JSON.stringify(req.body))

const digest = data.digest('hex')

if (digest === req.headers['x-razorpay-signature']) {

console.log('request is legit')

//We can send the response and store information in a database.

res.json({

status: 'ok'

})

} else {

res.status(400).send('Invalid signature');

}

})

Refund

Once the payment has been received, we can return it. To do that, all we have to do is make the following API call along with the payment Id and amount, and internally, Razorpay’s refund function is called to return the money to the same account.

app.post('/refund', async (req, res) => {

try {

//Verify the payment Id first, then access the Razorpay API.

const options = {

payment_id: req.body.paymentId,

amount: req.body.amount,

};

const razorpayResponse = await razorpay.refund(options);

//We can send the response and store information in a database

res.send('Successfully refunded')

} catch (error) {

console.log(error);

res.status(400).send('unable to issue a refund');

}

})

Advantages

The benefits of integrating with Razorpay Payment Gateway are listed below.

Onboarding

Utilizing Standard Checkout, connect the Razorpay Payment Gateway to your natively developed website. Explore our plugins for a number of different platforms, including WooCommerce, WordPress, Magento, Shopify, and more.

Success Rate

By employing numerous connections to route a transaction, also known as direct net banking pipelines, we increase the success rate.

Refunds

Based on the error code it receives from the bank, Razorpay intelligently retries failed API reimbursements. You have the choice to offer your clients the finest possible refund experience thanks to our Instant refunds tool.

Scalability and Availability

800 transaction requests per second can be processed by our system without it degrading. Aside from the status page and Dashboard, we also send emails with updates on outages.

Settlements Reconciliation

To keep track of all the transactions, including payments, refunds, transfers, and adjustments paid to you for a specific day or month, use settlement reconciliation.

Coverage

Numerous domestic and foreign cards, different net banking alternatives, UPI collect and intent, EMI, Cardless EMI, and wallets like Paytm and PhonePe are all supported by our company.

Step 4: Frontend (React js)

Basic code for a button with hard-coded values

const [displayRazorpay, setDisplayRazorpay] = useState(false);
const [orderDetails, setOrderDetails] = useState({
orderId: null,
currency: null,
amount: null,
});

const handleCreateOrder = async (amount, currency) => {
const data = await axios.post(serverBaseUrl + '/order',
{
amount: amount*100, //convert amount into lowest unit. here, Dollar->Cents
currency,
keyId: process.env.REACT_APP_RAZORPAY_KEY_ID,
KeySecret: process.env.REACT_APP_RAZORPAY_KEY_SECRET,
}
);

if(data && data.order_id){
setOrderDetails({
orderId: data.order_id,
currency: data.currency,
amount: data.amount,
});
setDisplayRazorpay(true);
};

return (
<div>
<button
onClick={() => handleCreateOrder(100, 'USD')}
>Place Order
</button>

{displayRazorpay && (
<RenderRazorpay
amount={orderDetails.amount}
currency={orderDetails.currency}
orderId={orderDetails.orderId}
keyId={process.env.REACT_APP_RAZORPAY_KEY_ID}
keySecret={process.env.REACT_APP_RAZORPAY_KEY_SECRET}
/>
}
</div>
);

Now, the code for rendering Razorpay

import { useEffect, useRef } from 'react';
import crypto from 'crypto-js';
import PropTypes from 'prop-types';
import Axios from 'axios';

// Function to load script and append in DOM tree.
const loadScript = src => new Promise((resolve) => {
const script = document.createElement('script');
script.src = src;
script.onload = () => {
console.log('razorpay loaded successfully');
resolve(true);
};
script.onerror = () => {
console.log('error in loading razorpay');
resolve(false);
};
document.body.appendChild(script);
});


const RenderRazorpay = ({
orderId,
keyId,
keySecret,
currency,
amount,
}) => {
const paymentId = useRef(null);
const paymentMethod = useRef(null);

// To load razorpay checkout modal script.
const displayRazorpay = async (options) => {
const res = await loadScript(
'https://checkout.razorpay.com/v1/checkout.js',
);

if (!res) {
console.log('Razorpay SDK failed to load. Are you online?');
return;
}
// All information is loaded in options which we will discuss later.
const rzp1 = new window.Razorpay(options);

// If you want to retreive the chosen payment method.
rzp1.on('payment.submit', (response) => {
paymentMethod.current = response.method;
});

// To get payment id in case of failed transaction.
rzp1.on('payment.failed', (response) => {
paymentId.current = response.error.metadata.payment_id;
});

// to open razorpay checkout modal.
rzp1.open();
};


// informing server about payment
const handlePayment = async (status, orderDetails = {}) => {
await Axios.post(`${serverBaseUrl}/payment`,
{
status,
orderDetails,
});
};


// we will be filling this object in next step.
const options = {},

useEffect(() => {
console.log('in razorpay');
displayRazorpay(options);
}, []);

return null;
};

export default RenderRazorpay;

Now, the most important part of the integration

const options = {
key: keyId, // key id from props
amount, // Amount in lowest denomination from props
currency, // Currency from props.
name: 'amit', // Title for your organization to display in checkout modal
// image, // custom logo url
order_id: orderId, // order id from props
// This handler menthod is always executed in case of succeeded payment
handler: (response) => {
console.log('succeeded');
console.log(response);
paymentId.current = response.razorpay_payment_id;

// Most important step to capture and authorize the payment. This can be done of Backend server.
const succeeded = crypto.HmacSHA256(`${orderId}|${response.razorpay_payment_id}`, keySecret).toString() === response.razorpay_signature;

// If successfully authorized. Then we can consider the payment as successful.
if (succeeded) {
handlePayment('succeeded', {
orderId,
paymentId,
signature: response.razorpay_signature,
});
} else {
handlePayment('failed', {
orderId,
paymentId: response.razorpay_payment_id,
});
}
},
modal: {
confirm_close: true, // this is set to true, if we want confirmation when clicked on cross button.
// This function is executed when checkout modal is closed
// There can be 3 reasons when this modal is closed.
ondismiss: async (reason) => {
const {
reason: paymentReason, field, step, code,
} = reason && reason.error ? reason.error : {};
// Reason 1 - when payment is cancelled. It can happend when we click cross icon or cancel any payment explicitly.
if (reason === undefined) {
console.log('cancelled');
handlePayment('Cancelled');
}
// Reason 2 - When modal is auto closed because of time out
else if (reason === 'timeout') {
console.log('timedout');
handlePayment('timedout');
}
// Reason 3 - When payment gets failed.
else {
console.log('failed');
handlePayment('failed', {
paymentReason, field, step, code,
});
}
},
},
// This property allows to enble/disable retries.
// This is enabled true by default.
retry: {
enabled: false,
},
timeout: 900, // Time limit in Seconds
theme: {
color: '', // Custom color for your checkout modal.
},
};

Step 5: Now let’s pay.

Here’s how it looks

--

--

amit kumar

M.Tech CSE | Data Science & Generative AI from IIT Kanpur | Senior Software Engineer at Pulse Labs | Expertise: PERN stack, Linux admin, cloud computing