quickpos 1.0.911 → 1.0.913
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PROVIDERS-DETAILS.md +1544 -0
- package/examples/example-2checkout.js +78 -0
- package/examples/example-bitpay.js +83 -0
- package/examples/example-cardcom.js +80 -0
- package/examples/example-cashfree.js +109 -0
- package/examples/example-checkout.js +85 -0
- package/examples/example-coingate.js +101 -0
- package/examples/example-coinpayments.js +89 -0
- package/examples/example-doku.js +27 -0
- package/examples/example-epay.js +64 -0
- package/examples/example-epoint.js +91 -0
- package/examples/example-freekassa.js +26 -0
- package/{example-heleket.js → examples/example-heleket.js} +3 -3
- package/examples/example-konnect.js +227 -0
- package/examples/example-midtrans.js +80 -0
- package/examples/example-noonpayments.js +297 -0
- package/examples/example-nowpayments.js +289 -0
- package/examples/example-omise.js +27 -0
- package/examples/example-paycom.js +82 -0
- package/{example-paydisini.js → examples/example-paydisini.js} +1 -1
- package/examples/example-payid19.js +87 -0
- package/examples/example-paykun.js +29 -0
- package/examples/example-payme.js +202 -0
- package/examples/example-paymentwall.js +201 -0
- package/examples/example-paynet.js +104 -0
- package/examples/example-paynettr.js +18 -0
- package/examples/example-payoneer.js +74 -0
- package/examples/example-payop.js +351 -0
- package/examples/example-paypal.js +200 -0
- package/examples/example-payriff.js +89 -0
- package/examples/example-paysend.js +81 -0
- package/examples/example-payspace.js +103 -0
- package/examples/example-payssion.js +27 -0
- package/examples/example-paytabs.js +28 -0
- package/examples/example-paytm.js +78 -0
- package/examples/example-payuindia.js +108 -0
- package/examples/example-payulatam.js +75 -0
- package/examples/example-phonepe.js +27 -0
- package/examples/example-picpay.js +27 -0
- package/examples/example-plisio.js +84 -0
- package/examples/example-portwallet.js +90 -0
- package/examples/example-primepayments.js +250 -0
- package/examples/example-razorpay.js +30 -0
- package/examples/example-senangpay.js +28 -0
- package/examples/example-shopier-card.js +67 -0
- package/examples/example-shurjopay.js +94 -0
- package/examples/example-toyyibpay.js +80 -0
- package/examples/example-tripay.js +89 -0
- package/examples/example-unitpay.js +26 -0
- package/examples/example-urway.js +28 -0
- package/examples/example-volet.js +80 -0
- package/examples/example-xendit.js +28 -0
- package/examples/example-yallapay.js +253 -0
- package/examples/example-yookassa.js +27 -0
- package/examples/example-youcanpay.js +28 -0
- package/examples/example-zarinpal.js +98 -0
- package/{example.js → examples/example.js} +1 -1
- package/lib/2checkout.js +165 -0
- package/lib/amazonpay.js +161 -0
- package/lib/billplz.js +79 -0
- package/lib/bitpay.js +122 -0
- package/lib/cardcom.js +193 -0
- package/lib/cashfree.js +184 -0
- package/lib/checkout.js +248 -0
- package/lib/coinbase.js +150 -0
- package/lib/coingate.js +137 -0
- package/lib/coinpayments.js +245 -0
- package/lib/doku.js +173 -0
- package/lib/epay.js +175 -0
- package/lib/epoint.js +162 -0
- package/lib/freekassa.js +128 -0
- package/lib/instamojo.js +158 -0
- package/lib/konnect.js +211 -0
- package/lib/midtrans.js +227 -0
- package/lib/noonpayments.js +650 -0
- package/lib/nowpayments.js +361 -0
- package/lib/omise.js +150 -0
- package/lib/paddle.js +180 -0
- package/lib/paycom.js +216 -0
- package/lib/payid19.js +211 -0
- package/lib/paykun.js +144 -0
- package/lib/payme.js +302 -0
- package/lib/paymentwall.js +205 -0
- package/lib/paynet.js +186 -0
- package/lib/paynettr.js +165 -0
- package/lib/payoneer.js +128 -0
- package/lib/payop.js +345 -0
- package/lib/paypal.js +596 -0
- package/lib/payriff.js +148 -0
- package/lib/paysend.js +189 -0
- package/lib/payspace.js +168 -0
- package/lib/payssion.js +177 -0
- package/lib/paytabs.js +145 -0
- package/lib/paytm.js +253 -0
- package/lib/payuindia.js +162 -0
- package/lib/payulatam.js +179 -0
- package/lib/perfectmoney.js +143 -0
- package/lib/phonepe.js +174 -0
- package/lib/picpay.js +119 -0
- package/lib/plisio.js +234 -0
- package/lib/portwallet.js +152 -0
- package/lib/primepayments.js +338 -0
- package/lib/razorpay.js +205 -0
- package/lib/senangpay.js +130 -0
- package/lib/shopier_card.js +188 -0
- package/lib/shurjopay.js +159 -0
- package/lib/toyyibpay.js +151 -0
- package/lib/tripay.js +220 -0
- package/lib/unitpay.js +223 -0
- package/lib/urway.js +182 -0
- package/lib/volet.js +147 -0
- package/lib/xendit.js +206 -0
- package/lib/yallapay.js +319 -0
- package/lib/yookassa.js +193 -0
- package/lib/youcanpay.js +124 -0
- package/lib/zarinpal.js +157 -0
- package/package.json +138 -64
- package/readme.md +348 -105
- package/reported.md +347 -0
- package/test.js +492 -0
- package/lib/vallet.js +0 -22
- /package/{example-anypay.js → examples/example-anypay.js} +0 -0
- /package/{example-bufpay.js → examples/example-bufpay.js} +0 -0
- /package/{example-cryptomus.js → examples/example-cryptomus.js} +0 -0
- /package/{example-esnekpos.js → examples/example-esnekpos.js} +0 -0
- /package/{example-fedapay.js → examples/example-fedapay.js} +0 -0
- /package/{example-iyzico.js → examples/example-iyzico.js} +0 -0
- /package/{example-papara.js → examples/example-papara.js} +0 -0
- /package/{example-payeer.js → examples/example-payeer.js} +0 -0
- /package/{example-paymaya.js → examples/example-paymaya.js} +0 -0
- /package/{example-shopier.js → examples/example-shopier.js} +0 -0
- /package/{ipaymu.js → examples/ipaymu.js} +0 -0
- /package/{oderopay.js → examples/oderopay.js} +0 -0
package/lib/paytabs.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PayTabsClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['profileId', 'serverKey'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.profileId = config.profileId;
|
|
12
|
+
this.serverKey = config.serverKey;
|
|
13
|
+
this.region = config.region || 'ARE'; // ARE, SAU, OMN, JOR, EGY, etc.
|
|
14
|
+
|
|
15
|
+
const regionUrls = {
|
|
16
|
+
'ARE': 'https://secure.paytabs.com',
|
|
17
|
+
'SAU': 'https://secure.paytabs.sa',
|
|
18
|
+
'OMN': 'https://secure-oman.paytabs.com',
|
|
19
|
+
'JOR': 'https://secure-jordan.paytabs.com',
|
|
20
|
+
'EGY': 'https://secure-egypt.paytabs.com',
|
|
21
|
+
'global': 'https://secure-global.paytabs.com'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
this.baseURL = regionUrls[this.region] || regionUrls['global'];
|
|
25
|
+
|
|
26
|
+
this.client = axios.create({
|
|
27
|
+
baseURL: this.baseURL,
|
|
28
|
+
headers: {
|
|
29
|
+
'Content-Type': 'application/json',
|
|
30
|
+
'Authorization': this.serverKey
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async createPayment(options) {
|
|
36
|
+
try {
|
|
37
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
38
|
+
|
|
39
|
+
const paymentData = {
|
|
40
|
+
profile_id: this.profileId,
|
|
41
|
+
tran_type: options.tranType || 'sale',
|
|
42
|
+
tran_class: options.tranClass || 'ecom',
|
|
43
|
+
cart_id: orderId,
|
|
44
|
+
cart_description: options.description || options.name || 'Payment',
|
|
45
|
+
cart_currency: options.currency || 'AED',
|
|
46
|
+
cart_amount: parseFloat(options.amount).toFixed(2),
|
|
47
|
+
callback: options.callbackUrl || options.callback_link,
|
|
48
|
+
return: options.successUrl || options.callback_link,
|
|
49
|
+
customer_details: {
|
|
50
|
+
name: options.name || options.customerName || '',
|
|
51
|
+
email: options.email || '',
|
|
52
|
+
phone: options.phone || '',
|
|
53
|
+
street1: options.address || '',
|
|
54
|
+
city: options.city || '',
|
|
55
|
+
state: options.state || '',
|
|
56
|
+
country: options.country || 'AE',
|
|
57
|
+
zip: options.zip || ''
|
|
58
|
+
},
|
|
59
|
+
hide_shipping: options.hideShipping !== false
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const response = await this.client.post('/payment/request', paymentData);
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
status: 'success',
|
|
66
|
+
data: {
|
|
67
|
+
transactionRef: response.data.tran_ref,
|
|
68
|
+
url: response.data.redirect_url,
|
|
69
|
+
orderId: orderId,
|
|
70
|
+
amount: paymentData.cart_amount,
|
|
71
|
+
currency: paymentData.cart_currency
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async handleCallback(callbackData) {
|
|
80
|
+
try {
|
|
81
|
+
const transactionRef = callbackData.tran_ref || callbackData.tranRef;
|
|
82
|
+
|
|
83
|
+
if (!transactionRef) {
|
|
84
|
+
throw new Error('Transaction reference not found in callback data');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Verify payment
|
|
88
|
+
const payment = await this.verifyPayment(transactionRef);
|
|
89
|
+
|
|
90
|
+
// Status mapping
|
|
91
|
+
const statusMapping = {
|
|
92
|
+
'A': 'success', // Authorized/Approved
|
|
93
|
+
'H': 'pending', // On Hold
|
|
94
|
+
'P': 'pending', // Pending
|
|
95
|
+
'V': 'pending', // Voided
|
|
96
|
+
'E': 'failed', // Error
|
|
97
|
+
'D': 'failed' // Declined
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
status: statusMapping[payment.payment_result?.response_status] || 'unknown',
|
|
102
|
+
orderId: payment.cart_id,
|
|
103
|
+
transactionRef: payment.tran_ref,
|
|
104
|
+
amount: parseFloat(payment.cart_amount),
|
|
105
|
+
currency: payment.cart_currency,
|
|
106
|
+
paymentStatus: payment.payment_result?.response_status,
|
|
107
|
+
responseCode: payment.payment_result?.response_code,
|
|
108
|
+
responseMessage: payment.payment_result?.response_message
|
|
109
|
+
};
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw new Error(`Error in PayTabs callback handling: ${error.message}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async verifyPayment(transactionRef) {
|
|
116
|
+
try {
|
|
117
|
+
const response = await this.client.post('/payment/query', {
|
|
118
|
+
profile_id: this.profileId,
|
|
119
|
+
tran_ref: transactionRef
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return response.data;
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throw new Error(`Error verifying payment: ${error.response?.data?.message || error.message}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async refundPayment(transactionRef, options = {}) {
|
|
129
|
+
try {
|
|
130
|
+
const response = await this.client.post('/payment/refund', {
|
|
131
|
+
profile_id: this.profileId,
|
|
132
|
+
tran_ref: transactionRef,
|
|
133
|
+
cart_amount: options.amount,
|
|
134
|
+
cart_description: options.description || 'Refund',
|
|
135
|
+
cart_id: options.orderId
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return response.data;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
throw new Error(`Error processing refund: ${error.response?.data?.message || error.message}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = PayTabsClient;
|
package/lib/paytm.js
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PaytmClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['merchantId', 'merchantKey', 'websiteName'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.merchantId = config.merchantId;
|
|
12
|
+
this.merchantKey = config.merchantKey;
|
|
13
|
+
this.websiteName = config.websiteName;
|
|
14
|
+
this.industryType = config.industryType || 'Retail';
|
|
15
|
+
this.channelId = config.channelId || 'WEB';
|
|
16
|
+
this.isSandbox = config.sandbox || false;
|
|
17
|
+
|
|
18
|
+
this.URL = this.isSandbox
|
|
19
|
+
? 'https://securegw-stage.paytm.in'
|
|
20
|
+
: 'https://securegw.paytm.in';
|
|
21
|
+
|
|
22
|
+
this.client = axios.create({
|
|
23
|
+
baseURL: this.URL,
|
|
24
|
+
headers: {
|
|
25
|
+
'Content-Type': 'application/json'
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
this.client.interceptors.response.use(response => {
|
|
30
|
+
return response;
|
|
31
|
+
}, error => {
|
|
32
|
+
if (error.response) {
|
|
33
|
+
throw new Error(`Paytm API error: ${error.response.data.message || error.message}`);
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Paytm API error: ${error.message}`);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
generateChecksum(params, key) {
|
|
40
|
+
const data = JSON.stringify(params);
|
|
41
|
+
const salt = crypto.randomBytes(4).toString('hex');
|
|
42
|
+
const checksum = crypto
|
|
43
|
+
.createHash('sha256')
|
|
44
|
+
.update(data + salt)
|
|
45
|
+
.digest('hex');
|
|
46
|
+
|
|
47
|
+
return checksum + salt;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
verifyChecksum(params, key, checksumReceived) {
|
|
51
|
+
const data = JSON.stringify(params);
|
|
52
|
+
const salt = checksumReceived.substr(checksumReceived.length - 8);
|
|
53
|
+
const checksum = crypto
|
|
54
|
+
.createHash('sha256')
|
|
55
|
+
.update(data + salt)
|
|
56
|
+
.digest('hex');
|
|
57
|
+
|
|
58
|
+
return (checksum + salt) === checksumReceived;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async createPayment(options) {
|
|
62
|
+
try {
|
|
63
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
64
|
+
const customerId = options.customerId || `CUST-${Date.now()}`;
|
|
65
|
+
|
|
66
|
+
const paytmParams = {
|
|
67
|
+
body: {
|
|
68
|
+
requestType: 'Payment',
|
|
69
|
+
mid: this.merchantId,
|
|
70
|
+
websiteName: this.websiteName,
|
|
71
|
+
orderId: orderId,
|
|
72
|
+
txnAmount: {
|
|
73
|
+
value: parseFloat(options.amount).toFixed(2),
|
|
74
|
+
currency: options.currency || 'INR'
|
|
75
|
+
},
|
|
76
|
+
userInfo: {
|
|
77
|
+
custId: customerId,
|
|
78
|
+
email: options.email || '',
|
|
79
|
+
mobile: options.mobile || options.phone || ''
|
|
80
|
+
},
|
|
81
|
+
callbackUrl: options.callbackUrl || options.callback_link
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Generate checksum
|
|
86
|
+
const checksum = this.generateChecksum(paytmParams.body, this.merchantKey);
|
|
87
|
+
|
|
88
|
+
const requestData = {
|
|
89
|
+
...paytmParams,
|
|
90
|
+
head: {
|
|
91
|
+
signature: checksum
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const response = await this.client.post('/theia/api/v1/initiateTransaction?mid=' + this.merchantId + '&orderId=' + orderId, requestData);
|
|
96
|
+
|
|
97
|
+
if (response.data.body.resultInfo.resultStatus === 'S') {
|
|
98
|
+
const txnToken = response.data.body.txnToken;
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
status: 'success',
|
|
102
|
+
data: {
|
|
103
|
+
txnToken: txnToken,
|
|
104
|
+
orderId: orderId,
|
|
105
|
+
url: `${this.URL}/theia/api/v1/showPaymentPage?mid=${this.merchantId}&orderId=${orderId}`,
|
|
106
|
+
amount: options.amount
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
} else {
|
|
110
|
+
throw new Error(response.data.body.resultInfo.resultMsg || 'Payment creation failed');
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async getTransactionStatus(orderId) {
|
|
118
|
+
try {
|
|
119
|
+
const paytmParams = {
|
|
120
|
+
body: {
|
|
121
|
+
mid: this.merchantId,
|
|
122
|
+
orderId: orderId
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const checksum = this.generateChecksum(paytmParams.body, this.merchantKey);
|
|
127
|
+
|
|
128
|
+
const requestData = {
|
|
129
|
+
...paytmParams,
|
|
130
|
+
head: {
|
|
131
|
+
signature: checksum
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const response = await this.client.post('/v3/order/status', requestData);
|
|
136
|
+
return response.data;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
throw new Error(`Transaction status error: ${error.message}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async handleCallback(callbackData) {
|
|
143
|
+
try {
|
|
144
|
+
const verification = await this.verifyCallback(callbackData);
|
|
145
|
+
|
|
146
|
+
if (!verification.status) {
|
|
147
|
+
throw new Error(verification.error.message);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const data = verification.data;
|
|
151
|
+
|
|
152
|
+
// Status mapping
|
|
153
|
+
const statusMapping = {
|
|
154
|
+
'TXN_SUCCESS': 'success',
|
|
155
|
+
'TXN_FAILURE': 'failed',
|
|
156
|
+
'PENDING': 'pending'
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
status: statusMapping[data.STATUS] || 'unknown',
|
|
161
|
+
orderId: data.ORDERID,
|
|
162
|
+
txnId: data.TXNID,
|
|
163
|
+
txnAmount: parseFloat(data.TXNAMOUNT),
|
|
164
|
+
currency: data.CURRENCY || 'INR',
|
|
165
|
+
bankTxnId: data.BANKTXNID,
|
|
166
|
+
paymentMode: data.PAYMENTMODE,
|
|
167
|
+
bankName: data.BANKNAME,
|
|
168
|
+
txnDate: data.TXNDATE,
|
|
169
|
+
gatewayName: data.GATEWAYNAME,
|
|
170
|
+
respCode: data.RESPCODE,
|
|
171
|
+
respMsg: data.RESPMSG,
|
|
172
|
+
paymentStatus: data.STATUS
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new Error(`Error in Paytm callback handling: ${error.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async verifyCallback(data) {
|
|
180
|
+
try {
|
|
181
|
+
const checksumReceived = data.CHECKSUMHASH;
|
|
182
|
+
delete data.CHECKSUMHASH;
|
|
183
|
+
|
|
184
|
+
const isValidChecksum = this.verifyChecksum(data, this.merchantKey, checksumReceived);
|
|
185
|
+
|
|
186
|
+
if (!isValidChecksum) {
|
|
187
|
+
return {
|
|
188
|
+
status: false,
|
|
189
|
+
error: {
|
|
190
|
+
code: 401,
|
|
191
|
+
message: 'Invalid checksum'
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (data.STATUS === 'TXN_FAILURE') {
|
|
197
|
+
return {
|
|
198
|
+
status: false,
|
|
199
|
+
error: {
|
|
200
|
+
code: 400,
|
|
201
|
+
message: data.RESPMSG || 'Transaction failed'
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
status: true,
|
|
208
|
+
data: data
|
|
209
|
+
};
|
|
210
|
+
} catch (error) {
|
|
211
|
+
return {
|
|
212
|
+
status: false,
|
|
213
|
+
error: {
|
|
214
|
+
code: 500,
|
|
215
|
+
message: error.message
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async refundTransaction(orderId, refundAmount, txnId) {
|
|
222
|
+
try {
|
|
223
|
+
const refId = `REFUND-${Date.now()}`;
|
|
224
|
+
|
|
225
|
+
const paytmParams = {
|
|
226
|
+
body: {
|
|
227
|
+
mid: this.merchantId,
|
|
228
|
+
orderId: orderId,
|
|
229
|
+
txnId: txnId,
|
|
230
|
+
refId: refId,
|
|
231
|
+
refundAmount: parseFloat(refundAmount).toFixed(2),
|
|
232
|
+
txnType: 'REFUND'
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const checksum = this.generateChecksum(paytmParams.body, this.merchantKey);
|
|
237
|
+
|
|
238
|
+
const requestData = {
|
|
239
|
+
...paytmParams,
|
|
240
|
+
head: {
|
|
241
|
+
signature: checksum
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const response = await this.client.post('/refund/apply', requestData);
|
|
246
|
+
return response.data;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
throw new Error(`Refund transaction error: ${error.message}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
module.exports = PaytmClient;
|
package/lib/payuindia.js
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PayUIndiaClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['merchantKey', 'salt'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.merchantKey = config.merchantKey;
|
|
12
|
+
this.salt = config.salt;
|
|
13
|
+
this.baseURL = config.sandbox
|
|
14
|
+
? 'https://test.payu.in'
|
|
15
|
+
: 'https://secure.payu.in';
|
|
16
|
+
|
|
17
|
+
this.client = axios.create({
|
|
18
|
+
baseURL: this.baseURL,
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
generateHash(params) {
|
|
26
|
+
const hashString = `${this.merchantKey}|${params.txnid}|${params.amount}|${params.productinfo}|${params.firstname}|${params.email}|||||||||||${this.salt}`;
|
|
27
|
+
return crypto.createHash('sha512').update(hashString).digest('hex');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
generateVerificationHash(params) {
|
|
31
|
+
const hashString = `${this.salt}|${params.status}|||||||||||${params.email}|${params.firstname}|${params.productinfo}|${params.amount}|${params.txnid}|${this.merchantKey}`;
|
|
32
|
+
return crypto.createHash('sha512').update(hashString).digest('hex');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async createPayment(options) {
|
|
36
|
+
try {
|
|
37
|
+
const txnid = options.orderId || `TXN${Date.now()}`;
|
|
38
|
+
const amount = parseFloat(options.amount).toFixed(2);
|
|
39
|
+
|
|
40
|
+
const params = {
|
|
41
|
+
key: this.merchantKey,
|
|
42
|
+
txnid: txnid,
|
|
43
|
+
amount: amount,
|
|
44
|
+
productinfo: options.productInfo || options.name || 'Payment',
|
|
45
|
+
firstname: options.firstname || options.name || '',
|
|
46
|
+
email: options.email || '',
|
|
47
|
+
phone: options.phone || '',
|
|
48
|
+
surl: options.successUrl || options.callback_link,
|
|
49
|
+
furl: options.failUrl || options.callback_link,
|
|
50
|
+
udf1: options.udf1 || '',
|
|
51
|
+
udf2: options.udf2 || '',
|
|
52
|
+
udf3: options.udf3 || '',
|
|
53
|
+
udf4: options.udf4 || '',
|
|
54
|
+
udf5: options.udf5 || ''
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
params.hash = this.generateHash(params);
|
|
58
|
+
|
|
59
|
+
// Create payment URL
|
|
60
|
+
const formParams = new URLSearchParams(params);
|
|
61
|
+
const paymentUrl = `${this.baseURL}/_payment`;
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
status: 'success',
|
|
65
|
+
data: {
|
|
66
|
+
url: paymentUrl,
|
|
67
|
+
orderId: txnid,
|
|
68
|
+
amount: amount,
|
|
69
|
+
params: params,
|
|
70
|
+
method: 'POST'
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async handleCallback(callbackData) {
|
|
79
|
+
try {
|
|
80
|
+
const verification = await this.verifyCallback(callbackData);
|
|
81
|
+
|
|
82
|
+
if (!verification.status) {
|
|
83
|
+
throw new Error(verification.error.message);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Status mapping
|
|
87
|
+
const statusMapping = {
|
|
88
|
+
'success': 'success',
|
|
89
|
+
'pending': 'pending',
|
|
90
|
+
'failure': 'failed',
|
|
91
|
+
'bounced': 'failed',
|
|
92
|
+
'cancelled': 'failed'
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
status: statusMapping[callbackData.status] || 'unknown',
|
|
97
|
+
orderId: callbackData.txnid,
|
|
98
|
+
transactionId: callbackData.mihpayid,
|
|
99
|
+
amount: parseFloat(callbackData.amount),
|
|
100
|
+
currency: 'INR',
|
|
101
|
+
paymentStatus: callbackData.status,
|
|
102
|
+
paymentMode: callbackData.mode,
|
|
103
|
+
bankRefNum: callbackData.bank_ref_num,
|
|
104
|
+
cardNumber: callbackData.cardnum
|
|
105
|
+
};
|
|
106
|
+
} catch (error) {
|
|
107
|
+
throw new Error(`Error in PayU India callback handling: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async verifyCallback(data) {
|
|
112
|
+
try {
|
|
113
|
+
const receivedHash = data.hash;
|
|
114
|
+
const expectedHash = this.generateVerificationHash(data);
|
|
115
|
+
|
|
116
|
+
if (receivedHash !== expectedHash) {
|
|
117
|
+
return {
|
|
118
|
+
status: false,
|
|
119
|
+
error: {
|
|
120
|
+
code: 401,
|
|
121
|
+
message: 'Invalid hash'
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
status: true,
|
|
128
|
+
data: data
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
status: false,
|
|
133
|
+
error: {
|
|
134
|
+
code: 500,
|
|
135
|
+
message: error.message
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async verifyPayment(txnid) {
|
|
142
|
+
try {
|
|
143
|
+
const command = 'verify_payment';
|
|
144
|
+
const hashString = `${this.merchantKey}|${command}|${txnid}|${this.salt}`;
|
|
145
|
+
const hash = crypto.createHash('sha512').update(hashString).digest('hex');
|
|
146
|
+
|
|
147
|
+
const params = new URLSearchParams({
|
|
148
|
+
key: this.merchantKey,
|
|
149
|
+
command: command,
|
|
150
|
+
var1: txnid,
|
|
151
|
+
hash: hash
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const response = await this.client.post('/merchant/postservice.php?form=2', params);
|
|
155
|
+
return response.data;
|
|
156
|
+
} catch (error) {
|
|
157
|
+
throw new Error(`Error verifying payment: ${error.response?.data || error.message}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = PayUIndiaClient;
|