quickpos
Version:
<div align="center"> <h1>💳 QuickPos 🚀</h1> <p><strong>A powerful, multi-gateway payment integration module for Node.js</strong></p> <p>Seamlessly integrate with 50+ payment providers worldwide</p>
254 lines (217 loc) • 8.5 kB
JavaScript
const axios = require('axios');
const crypto = require('crypto');
class PaytmClient {
constructor(config) {
const requiredFields = ['merchantId', 'merchantKey', 'websiteName'];
for (let field of requiredFields) {
if (!config[field]) throw new Error(`Missing required field: ${field}`);
}
this.merchantId = config.merchantId;
this.merchantKey = config.merchantKey;
this.websiteName = config.websiteName;
this.industryType = config.industryType || 'Retail';
this.channelId = config.channelId || 'WEB';
this.isSandbox = config.sandbox || false;
this.URL = this.isSandbox
? 'https://securegw-stage.paytm.in'
: 'https://securegw.paytm.in';
this.client = axios.create({
baseURL: this.URL,
headers: {
'Content-Type': 'application/json'
}
});
this.client.interceptors.response.use(response => {
return response;
}, error => {
if (error.response) {
throw new Error(`Paytm API error: ${error.response.data.message || error.message}`);
}
throw new Error(`Paytm API error: ${error.message}`);
});
}
generateChecksum(params, key) {
const data = JSON.stringify(params);
const salt = crypto.randomBytes(4).toString('hex');
const checksum = crypto
.createHash('sha256')
.update(data + salt)
.digest('hex');
return checksum + salt;
}
verifyChecksum(params, key, checksumReceived) {
const data = JSON.stringify(params);
const salt = checksumReceived.substr(checksumReceived.length - 8);
const checksum = crypto
.createHash('sha256')
.update(data + salt)
.digest('hex');
return (checksum + salt) === checksumReceived;
}
async createPayment(options) {
try {
const orderId = options.orderId || `ORDER-${Date.now()}`;
const customerId = options.customerId || `CUST-${Date.now()}`;
const paytmParams = {
body: {
requestType: 'Payment',
mid: this.merchantId,
websiteName: this.websiteName,
orderId: orderId,
txnAmount: {
value: parseFloat(options.amount).toFixed(2),
currency: options.currency || 'INR'
},
userInfo: {
custId: customerId,
email: options.email || '',
mobile: options.mobile || options.phone || ''
},
callbackUrl: options.callbackUrl || options.callback_link
}
};
// Generate checksum
const checksum = this.generateChecksum(paytmParams.body, this.merchantKey);
const requestData = {
...paytmParams,
head: {
signature: checksum
}
};
const response = await this.client.post('/theia/api/v1/initiateTransaction?mid=' + this.merchantId + '&orderId=' + orderId, requestData);
if (response.data.body.resultInfo.resultStatus === 'S') {
const txnToken = response.data.body.txnToken;
return {
status: 'success',
data: {
txnToken: txnToken,
orderId: orderId,
url: `${this.URL}/theia/api/v1/showPaymentPage?mid=${this.merchantId}&orderId=${orderId}`,
amount: options.amount
}
};
} else {
throw new Error(response.data.body.resultInfo.resultMsg || 'Payment creation failed');
}
} catch (error) {
throw new Error(`Payment creation error: ${error.message}`);
}
}
async getTransactionStatus(orderId) {
try {
const paytmParams = {
body: {
mid: this.merchantId,
orderId: orderId
}
};
const checksum = this.generateChecksum(paytmParams.body, this.merchantKey);
const requestData = {
...paytmParams,
head: {
signature: checksum
}
};
const response = await this.client.post('/v3/order/status', requestData);
return response.data;
} catch (error) {
throw new Error(`Transaction status error: ${error.message}`);
}
}
async handleCallback(callbackData) {
try {
const verification = await this.verifyCallback(callbackData);
if (!verification.status) {
throw new Error(verification.error.message);
}
const data = verification.data;
// Status mapping
const statusMapping = {
'TXN_SUCCESS': 'success',
'TXN_FAILURE': 'failed',
'PENDING': 'pending'
};
return {
status: statusMapping[data.STATUS] || 'unknown',
orderId: data.ORDERID,
txnId: data.TXNID,
txnAmount: parseFloat(data.TXNAMOUNT),
currency: data.CURRENCY || 'INR',
bankTxnId: data.BANKTXNID,
paymentMode: data.PAYMENTMODE,
bankName: data.BANKNAME,
txnDate: data.TXNDATE,
gatewayName: data.GATEWAYNAME,
respCode: data.RESPCODE,
respMsg: data.RESPMSG,
paymentStatus: data.STATUS
};
} catch (error) {
throw new Error(`Error in Paytm callback handling: ${error.message}`);
}
}
async verifyCallback(data) {
try {
const checksumReceived = data.CHECKSUMHASH;
delete data.CHECKSUMHASH;
const isValidChecksum = this.verifyChecksum(data, this.merchantKey, checksumReceived);
if (!isValidChecksum) {
return {
status: false,
error: {
code: 401,
message: 'Invalid checksum'
}
};
}
if (data.STATUS === 'TXN_FAILURE') {
return {
status: false,
error: {
code: 400,
message: data.RESPMSG || 'Transaction failed'
}
};
}
return {
status: true,
data: data
};
} catch (error) {
return {
status: false,
error: {
code: 500,
message: error.message
}
};
}
}
async refundTransaction(orderId, refundAmount, txnId) {
try {
const refId = `REFUND-${Date.now()}`;
const paytmParams = {
body: {
mid: this.merchantId,
orderId: orderId,
txnId: txnId,
refId: refId,
refundAmount: parseFloat(refundAmount).toFixed(2),
txnType: 'REFUND'
}
};
const checksum = this.generateChecksum(paytmParams.body, this.merchantKey);
const requestData = {
...paytmParams,
head: {
signature: checksum
}
};
const response = await this.client.post('/refund/apply', requestData);
return response.data;
} catch (error) {
throw new Error(`Refund transaction error: ${error.message}`);
}
}
}
module.exports = PaytmClient;