Seamless Transactions: A Comprehensive Guide to PhonePe Payment Gateway API Integration with Node.js
In the ever-evolving landscape of online transactions, seamless payment experiences are crucial for the success of any web application. Integrating a reliable payment gateway is a fundamental step towards achieving this, and PhonePe, with its robust API, offers an excellent solution. In this guide, we'll walk you through the process of integrating the PhonePe Payment Gateway API with Node.js, enabling you to enhance your web application's payment capabilities.
Firstly, create a PhonePe merchant account and store the MerchantId, key Index and salt key. We will use this during making requests on PhonePe APIs.
Proceeding by the assumption that we have already created an account (PhonePe merchant account), we need to first understand the working of the PhonePe APIs and a brief overview of it.
Let's create our express server to begin the work with.
const express = require('express')
const app = express()
const cors = require('cors')
const dotenv = require('dotenv')
dotenv.config({ path: "config.env" })
app.use(cors());
app.use(express.json())
const PORT = 5000
app.listen(PORT, () =>
console.log(`Server started in development mode on port ${PORT}`)
)
Here, we have created a server using Express and hosted it on port
5000.
In the config.env
(.env file), specify the SALT KEY
and the MERCHANT ID
. We will use it in our further PhonePe API functions without revealing the actual value every time in the code.
SALT_KEY = "********-****-****-****-************"
MERCHANT_ID = "*******************"
For a successful payment approval, we will use PhonePe's two APIs; one for submitting the payment request and the next API for checking the Payment status with the merchant transaction ID value.
The newPayment function:
The fields we will need to request the payment initiation API are already stated in the JSON variable
data
in the function.Next, we have to encode the API link to base64 and add the
key index
value with the hashed sha256 value to create the checksum value for theX-Verify
field.Make the API request to PhonePe and retrieve the response from it. From the response, we will get the redirection link to the Payment Gateway page from where we can initiate our payment.
API to call:
https://api.phonepe.com/apis/hermes/pg/v1/pay
A sample of the response is provided below. Upon successful initiation, we will redirect to the URL specified in the
data.instrumentResponse.redirectInfo.url.
{ "success": true, "code": "PAYMENT_INITIATED", "message": "Payment Iniiated", "data": { "merchantId": "*********", "merchantTransactionId": "M**********", "instrumentResponse": { "type": "PAY_PAGE", "redirectInfo": { "url": "https://mercury-uat.phonepe.com/transact?token=*********", "method": "GET" } } } }
The checkStatus function:
This API is used for checking the status of an existing transaction.
The payment status can be
Success
,Failed
orPending
. WhenPending
, merchants should retry until the status changes toSuccess
orFailed
.API to call:
https://api.preprod.phonepe.com/apis/hermes/pg/v1/status/{merchantId}/{merchantTransactionId}
const crypto = require('crypto');
const axios = require('axios');
require("dotenv").config();
const newPayment = async (req, res) => {
try {
const merchantTransactionId = 'M' + Date.now();
const {user_id, price, phone, name} = req.body;
const data = {
merchantId: process.env.MERCHANT_ID,
merchantTransactionId: merchantTransactionId,
merchantUserId: 'MUID' + user_id,
name: name,
amount: price * 100,
redirectUrl: `http://localhost:3001/api/v1/status/${merchantTransactionId}`,
redirectMode: 'POST',
mobileNumber: phone,
paymentInstrument: {
type: 'PAY_PAGE'
}
};
const payload = JSON.stringify(data);
const payloadMain = Buffer.from(payload).toString('base64');
const keyIndex = 2;
const string = payloadMain + '/pg/v1/pay' + process.env.SALT_KEY;
const sha256 = crypto.createHash('sha256').update(string).digest('hex');
const checksum = sha256 + '###' + keyIndex;
const prod_URL = "https://api.phonepe.com/apis/hermes/pg/v1/pay"
const options = {
method: 'POST',
url: prod_URL,
headers: {
accept: 'application/json',
'Content-Type': 'application/json',
'X-VERIFY': checksum
},
data: {
request: payloadMain
}
};
axios.request(options).then(function (response) {
return res.redirect(response.data.data.instrumentResponse.redirectInfo.url)
})
.catch(function (error) {
console.error(error);
});
} catch (error) {
res.status(500).send({
message: error.message,
success: false
})
}
}
const checkStatus = async(req, res) => {
const merchantTransactionId = req.params['txnId']
const merchantId = process.env.MERCHANT_ID
const keyIndex = 2;
const string = `/pg/v1/status/${merchantId}/${merchantTransactionId}` + process.env.SALT_KEY;
const sha256 = crypto.createHash('sha256').update(string).digest('hex');
const checksum = sha256 + "###" + keyIndex;
const options = {
method: 'GET',
url: `https://api.phonepe.com/apis/hermes/pg/v1/status/${merchantId}/${merchantTransactionId}`,
headers: {
accept: 'application/json',
'Content-Type': 'application/json',
'X-VERIFY': checksum,
'X-MERCHANT-ID': `${merchantId}`
}
};
// CHECK PAYMENT STATUS
axios.request(options).then(async(response) => {
if (response.data.success === true) {
console.log(response.data)
return res.status(200).send({success: true, message:"Payment Success"});
} else {
return res.status(400).send({success: false, message:"Payment Failure"});
}
})
.catch((err) => {
console.error(err);
res.status(500).send({msg: err.message});
});
};
module.exports = {
newPayment,
checkStatus
}
In the route file, call the two created functions from these two APIs. The first API calls the new payment function which starts the payment initiation. The second API calls the status check function which verifies the checksum value with the appropriate merchant transaction id which is provided as txnId as a parameter.
router.post('/payment', newPayment);
router.post('/status/:txnId', checkStatus);
Subscribe to my newsletter
Read articles from Arijit Das directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Arijit Das
Arijit Das
Exploring Backend Web Development and Database Security GitHub : https://github.com/arijit2002 Email : das10arijit@gmail.com Web Dev Stack: Languages: NodeJS (ExpressJS), Python (Flask), Java (SpringBoot) Version Control: Git (GitHub, GitLab) Database: MongoDB, Firestore, Oracle, MySQL Cloud: AWS Hosting: Heroku, Render, Netlify, Firebase