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
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PrimePayments - JavaScript client for PrimePayments payment service
|
|
3
|
+
*/
|
|
4
|
+
class PrimePayments {
|
|
5
|
+
/**
|
|
6
|
+
* Create a new PrimePayments client
|
|
7
|
+
* @param {Object} config - Configuration options
|
|
8
|
+
* @param {string} config.projectId - Project ID
|
|
9
|
+
* @param {string} config.secretWord1 - Secret word 1 for signing requests
|
|
10
|
+
* @param {string} config.secretWord2 - Secret word 2 for verifying notifications
|
|
11
|
+
* @param {string} config.payoutKey - Key for payout operations
|
|
12
|
+
*/
|
|
13
|
+
constructor(config) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.apiUrl = 'https://pay.primepayments.io/API/v2/';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Calculate MD5 hash
|
|
20
|
+
* @param {string} str - Input string
|
|
21
|
+
* @returns {string} - MD5 hash
|
|
22
|
+
*/
|
|
23
|
+
md5(str) {
|
|
24
|
+
return require('crypto').createHash('md5').update(str).digest('hex');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Make an API request to PrimePayments
|
|
29
|
+
* @param {Object} data - Request data
|
|
30
|
+
* @returns {Promise<Object>} - Response data
|
|
31
|
+
*/
|
|
32
|
+
async makeRequest(data) {
|
|
33
|
+
const queryString = Object.entries(data)
|
|
34
|
+
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
|
|
35
|
+
.join('&');
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const response = await fetch(this.apiUrl, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
42
|
+
},
|
|
43
|
+
body: queryString
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const result = await response.json();
|
|
47
|
+
return result;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
throw new Error(`PrimePayments API request failed: ${error.message}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Initialize a payment and get payment form URL
|
|
55
|
+
* @param {Object} options - Payment options
|
|
56
|
+
* @param {number} options.sum - Payment amount
|
|
57
|
+
* @param {string} options.currency - Payment currency (RUB, UAH, USD, EUR)
|
|
58
|
+
* @param {string} options.innerID - Internal ID in your system
|
|
59
|
+
* @param {string} options.email - Customer email
|
|
60
|
+
* @param {string} [options.payWay] - Preferred payment method (1-cards, 2-Yandex, etc.)
|
|
61
|
+
* @param {string} [options.comment] - Payment description
|
|
62
|
+
* @param {boolean} [options.needFailNotice] - Send notification on payment failure
|
|
63
|
+
* @param {string} [options.lang] - Form language
|
|
64
|
+
* @param {boolean} [options.strict_payWay] - Force specified payment method
|
|
65
|
+
* @param {boolean} [options.block_payWay] - Block other payment methods
|
|
66
|
+
* @param {boolean} [options.directPay] - Direct payment without intermediate page
|
|
67
|
+
* @returns {Promise<Object>} - Response with payment URL
|
|
68
|
+
*/
|
|
69
|
+
async initPayment(options) {
|
|
70
|
+
const data = {
|
|
71
|
+
action: 'initPayment',
|
|
72
|
+
project: this.config.projectId,
|
|
73
|
+
sum: options.sum,
|
|
74
|
+
currency: options.currency,
|
|
75
|
+
innerID: options.innerID,
|
|
76
|
+
email: options.email,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
if (options.payWay) data.payWay = options.payWay;
|
|
80
|
+
if (options.comment) data.comment = options.comment;
|
|
81
|
+
if (options.needFailNotice) data.needFailNotice = options.needFailNotice;
|
|
82
|
+
if (options.lang) data.lang = options.lang;
|
|
83
|
+
if (options.strict_payWay) data.strict_payWay = options.strict_payWay;
|
|
84
|
+
if (options.block_payWay) data.block_payWay = options.block_payWay;
|
|
85
|
+
if (options.directPay) data.directPay = options.directPay;
|
|
86
|
+
|
|
87
|
+
// Calculate signature
|
|
88
|
+
let signString = this.config.secretWord1 + data.action + data.project +
|
|
89
|
+
data.sum + data.currency + data.innerID + data.email;
|
|
90
|
+
|
|
91
|
+
if (data.payWay) signString += data.payWay;
|
|
92
|
+
|
|
93
|
+
data.sign = this.md5(signString);
|
|
94
|
+
|
|
95
|
+
return this.makeRequest(data);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Initialize a payout
|
|
100
|
+
* @param {Object} options - Payout options
|
|
101
|
+
* @param {number} options.sum - Payout amount
|
|
102
|
+
* @param {string} options.currency - Payout currency
|
|
103
|
+
* @param {string} options.payWay - Payout method
|
|
104
|
+
* @param {string} options.email - Recipient email
|
|
105
|
+
* @param {string} options.purse - Card number or wallet
|
|
106
|
+
* @param {string} [options.comment] - Payout description
|
|
107
|
+
* @param {string} [options.cardholder] - Card holder name
|
|
108
|
+
* @param {string} [options.SBP_id] - SBP bank identifier
|
|
109
|
+
* @param {string} [options.needUnique] - Unique identifier for the payout
|
|
110
|
+
* @returns {Promise<Object>} - Response with payout ID
|
|
111
|
+
*/
|
|
112
|
+
async initPayout(options) {
|
|
113
|
+
const data = {
|
|
114
|
+
action: 'initPayout',
|
|
115
|
+
project: this.config.projectId,
|
|
116
|
+
sum: options.sum,
|
|
117
|
+
currency: options.currency,
|
|
118
|
+
payWay: options.payWay,
|
|
119
|
+
email: options.email,
|
|
120
|
+
purse: options.purse
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (options.comment) data.comment = options.comment;
|
|
124
|
+
if (options.cardholder) data.cardholder = options.cardholder;
|
|
125
|
+
if (options.SBP_id) data.SBP_id = options.SBP_id;
|
|
126
|
+
if (options.needUnique) data.needUnique = options.needUnique;
|
|
127
|
+
|
|
128
|
+
// Calculate signature
|
|
129
|
+
const signString = this.config.payoutKey + data.action + data.project +
|
|
130
|
+
data.sum + data.currency + data.payWay + data.email + data.purse;
|
|
131
|
+
|
|
132
|
+
data.sign = this.md5(signString);
|
|
133
|
+
|
|
134
|
+
return this.makeRequest(data);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get order information
|
|
139
|
+
* @param {string} orderID - Order ID
|
|
140
|
+
* @returns {Promise<Object>} - Order information
|
|
141
|
+
*/
|
|
142
|
+
async getOrderInfo(orderID) {
|
|
143
|
+
const data = {
|
|
144
|
+
action: 'getOrderInfo',
|
|
145
|
+
project: this.config.projectId,
|
|
146
|
+
orderID: orderID
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Calculate signature
|
|
150
|
+
data.sign = this.md5(this.config.secretWord1 + data.action + data.project + data.orderID);
|
|
151
|
+
|
|
152
|
+
return this.makeRequest(data);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Refund a payment
|
|
157
|
+
* @param {string} orderID - Order ID to refund
|
|
158
|
+
* @returns {Promise<Object>} - Refund result
|
|
159
|
+
*/
|
|
160
|
+
async refund(orderID) {
|
|
161
|
+
const data = {
|
|
162
|
+
action: 'refund',
|
|
163
|
+
orderID: orderID
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// Calculate signature
|
|
167
|
+
data.sign = this.md5(this.config.secretWord1 + data.action + data.orderID);
|
|
168
|
+
|
|
169
|
+
return this.makeRequest(data);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Get project balance
|
|
174
|
+
* @returns {Promise<Object>} - Project balance information
|
|
175
|
+
*/
|
|
176
|
+
async getProjectBalance() {
|
|
177
|
+
const data = {
|
|
178
|
+
action: 'getProjectBalance',
|
|
179
|
+
project: this.config.projectId
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// Calculate signature
|
|
183
|
+
data.sign = this.md5(this.config.secretWord1 + data.action + data.project);
|
|
184
|
+
|
|
185
|
+
return this.makeRequest(data);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get payout information
|
|
190
|
+
* @param {string} payoutID - Payout ID
|
|
191
|
+
* @returns {Promise<Object>} - Payout information
|
|
192
|
+
*/
|
|
193
|
+
async getPayoutInfo(payoutID) {
|
|
194
|
+
const data = {
|
|
195
|
+
action: 'getPayoutInfo',
|
|
196
|
+
project: this.config.projectId,
|
|
197
|
+
payoutID: payoutID
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Calculate signature
|
|
201
|
+
data.sign = this.md5(this.config.secretWord1 + data.action + data.project + data.payoutID);
|
|
202
|
+
|
|
203
|
+
return this.makeRequest(data);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get project information
|
|
208
|
+
* @returns {Promise<Object>} - Project information and available payment methods
|
|
209
|
+
*/
|
|
210
|
+
async getProjectInfo() {
|
|
211
|
+
const data = {
|
|
212
|
+
action: 'getProjectInfo',
|
|
213
|
+
project: this.config.projectId
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Calculate signature
|
|
217
|
+
data.sign = this.md5(this.config.secretWord1 + data.action + data.project);
|
|
218
|
+
|
|
219
|
+
return this.makeRequest(data);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Get exchange rates
|
|
224
|
+
* @returns {Promise<Object>} - Exchange rates information
|
|
225
|
+
*/
|
|
226
|
+
async getExchangeRates() {
|
|
227
|
+
const data = {
|
|
228
|
+
action: 'getExchangeRates'
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
return this.makeRequest(data);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Verify payment notification
|
|
236
|
+
* @param {Object} postData - POST data from notification
|
|
237
|
+
* @returns {boolean} - True if signature is valid
|
|
238
|
+
*/
|
|
239
|
+
verifyPaymentNotification(postData) {
|
|
240
|
+
if (postData.action === 'order_payed') {
|
|
241
|
+
const hash = this.md5(this.config.secretWord2 + postData.orderID +
|
|
242
|
+
postData.payWay + postData.innerID +
|
|
243
|
+
postData.sum + postData.webmaster_profit);
|
|
244
|
+
|
|
245
|
+
return hash === postData.sign;
|
|
246
|
+
} else if (postData.action === 'order_cancel') {
|
|
247
|
+
const hash = this.md5(this.config.secretWord2 + postData.orderID + postData.innerID);
|
|
248
|
+
|
|
249
|
+
return hash === postData.sign;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Create payment (wrapper for initPayment)
|
|
257
|
+
* @param {Object} paymentData - Payment data
|
|
258
|
+
* @returns {Promise<Object>} - Payment result
|
|
259
|
+
*/
|
|
260
|
+
async createPayment(paymentData) {
|
|
261
|
+
try {
|
|
262
|
+
const result = await this.initPayment({
|
|
263
|
+
amount: paymentData.amount,
|
|
264
|
+
currency: paymentData.currency || 'USD',
|
|
265
|
+
description: paymentData.description || 'Payment',
|
|
266
|
+
order_id: paymentData.orderId,
|
|
267
|
+
success_url: paymentData.callback_link,
|
|
268
|
+
fail_url: paymentData.fail_link
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (result.result === 'ok') {
|
|
272
|
+
return {
|
|
273
|
+
success: true,
|
|
274
|
+
paymentUrl: result.payment_url,
|
|
275
|
+
transactionId: result.order_id,
|
|
276
|
+
amount: paymentData.amount,
|
|
277
|
+
currency: paymentData.currency
|
|
278
|
+
};
|
|
279
|
+
} else {
|
|
280
|
+
throw new Error(result.message || 'Payment creation failed');
|
|
281
|
+
}
|
|
282
|
+
} catch (error) {
|
|
283
|
+
throw new Error(`PrimePayments payment creation failed: ${error.message}`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Handle callback/notification
|
|
289
|
+
* @param {Object} callbackData - Callback data
|
|
290
|
+
* @returns {Promise<Object>} - Callback result
|
|
291
|
+
*/
|
|
292
|
+
async handleCallback(callbackData) {
|
|
293
|
+
try {
|
|
294
|
+
// Verify notification with secret word 2
|
|
295
|
+
const isValid = this.verifyNotification(callbackData);
|
|
296
|
+
|
|
297
|
+
if (!isValid) {
|
|
298
|
+
return {
|
|
299
|
+
success: false,
|
|
300
|
+
status: 'invalid',
|
|
301
|
+
rawData: callbackData
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const orderStatus = callbackData.status;
|
|
306
|
+
const orderId = callbackData.order_id;
|
|
307
|
+
|
|
308
|
+
if (orderStatus === 'success') {
|
|
309
|
+
return {
|
|
310
|
+
success: true,
|
|
311
|
+
transactionId: orderId,
|
|
312
|
+
status: 'completed',
|
|
313
|
+
amount: parseFloat(callbackData.amount),
|
|
314
|
+
currency: callbackData.currency,
|
|
315
|
+
rawData: callbackData
|
|
316
|
+
};
|
|
317
|
+
} else if (orderStatus === 'fail') {
|
|
318
|
+
return {
|
|
319
|
+
success: false,
|
|
320
|
+
status: 'failed',
|
|
321
|
+
transactionId: orderId,
|
|
322
|
+
rawData: callbackData
|
|
323
|
+
};
|
|
324
|
+
} else {
|
|
325
|
+
return {
|
|
326
|
+
success: false,
|
|
327
|
+
status: 'pending',
|
|
328
|
+
transactionId: orderId,
|
|
329
|
+
rawData: callbackData
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
} catch (error) {
|
|
333
|
+
throw new Error(`PrimePayments callback handling failed: ${error.message}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = PrimePayments;
|
package/lib/razorpay.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class RazorpayClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['keyId', 'keySecret'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.keyId = config.keyId;
|
|
12
|
+
this.keySecret = config.keySecret;
|
|
13
|
+
this.baseURL = 'https://api.razorpay.com/v1';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.baseURL,
|
|
17
|
+
auth: {
|
|
18
|
+
username: this.keyId,
|
|
19
|
+
password: this.keySecret
|
|
20
|
+
},
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async createPayment(options) {
|
|
28
|
+
try {
|
|
29
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
30
|
+
|
|
31
|
+
// Create Razorpay order
|
|
32
|
+
const orderData = {
|
|
33
|
+
amount: Math.round(parseFloat(options.amount) * 100), // Amount in paise
|
|
34
|
+
currency: options.currency || 'INR',
|
|
35
|
+
receipt: orderId,
|
|
36
|
+
notes: {
|
|
37
|
+
description: options.description || options.name || 'Payment',
|
|
38
|
+
customer_name: options.name || '',
|
|
39
|
+
customer_email: options.email || '',
|
|
40
|
+
customer_phone: options.phone || ''
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const response = await this.client.post('/orders', orderData);
|
|
45
|
+
|
|
46
|
+
// For Razorpay, you need to use Checkout.js on frontend
|
|
47
|
+
// This returns the order details needed for checkout
|
|
48
|
+
return {
|
|
49
|
+
status: 'success',
|
|
50
|
+
data: {
|
|
51
|
+
orderId: response.data.id,
|
|
52
|
+
orderIdCustom: orderId,
|
|
53
|
+
amount: response.data.amount / 100,
|
|
54
|
+
currency: response.data.currency,
|
|
55
|
+
status: response.data.status,
|
|
56
|
+
// These are needed for Razorpay Checkout.js
|
|
57
|
+
keyId: this.keyId,
|
|
58
|
+
name: options.merchantName || 'Your Business',
|
|
59
|
+
description: options.description || 'Payment',
|
|
60
|
+
prefill: {
|
|
61
|
+
name: options.name || '',
|
|
62
|
+
email: options.email || '',
|
|
63
|
+
contact: options.phone || ''
|
|
64
|
+
},
|
|
65
|
+
callback_url: options.callbackUrl || options.callback_link,
|
|
66
|
+
redirect: options.redirect !== false
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
throw new Error(`Payment creation error: ${error.response?.data?.error?.description || error.message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async handleCallback(callbackData) {
|
|
75
|
+
try {
|
|
76
|
+
// Verify payment signature
|
|
77
|
+
const verification = await this.verifyPaymentSignature(callbackData);
|
|
78
|
+
|
|
79
|
+
if (!verification.status) {
|
|
80
|
+
throw new Error(verification.error.message);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Get payment details
|
|
84
|
+
const payment = await this.getPaymentDetails(callbackData.razorpay_payment_id);
|
|
85
|
+
|
|
86
|
+
// Status mapping
|
|
87
|
+
const statusMapping = {
|
|
88
|
+
'captured': 'success',
|
|
89
|
+
'authorized': 'success',
|
|
90
|
+
'refunded': 'refunded',
|
|
91
|
+
'failed': 'failed'
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
status: statusMapping[payment.status] || 'unknown',
|
|
96
|
+
orderId: payment.notes?.order_id || payment.order_id,
|
|
97
|
+
paymentId: payment.id,
|
|
98
|
+
amount: payment.amount / 100,
|
|
99
|
+
currency: payment.currency,
|
|
100
|
+
paymentStatus: payment.status,
|
|
101
|
+
method: payment.method,
|
|
102
|
+
email: payment.email,
|
|
103
|
+
contact: payment.contact,
|
|
104
|
+
fee: payment.fee / 100,
|
|
105
|
+
tax: payment.tax / 100
|
|
106
|
+
};
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw new Error(`Error in Razorpay callback handling: ${error.message}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async verifyPaymentSignature(data) {
|
|
113
|
+
try {
|
|
114
|
+
const expectedSignature = crypto
|
|
115
|
+
.createHmac('sha256', this.keySecret)
|
|
116
|
+
.update(data.razorpay_order_id + '|' + data.razorpay_payment_id)
|
|
117
|
+
.digest('hex');
|
|
118
|
+
|
|
119
|
+
if (expectedSignature !== data.razorpay_signature) {
|
|
120
|
+
return {
|
|
121
|
+
status: false,
|
|
122
|
+
error: {
|
|
123
|
+
code: 401,
|
|
124
|
+
message: 'Invalid payment signature'
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
status: true,
|
|
131
|
+
data: data
|
|
132
|
+
};
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return {
|
|
135
|
+
status: false,
|
|
136
|
+
error: {
|
|
137
|
+
code: 500,
|
|
138
|
+
message: error.message
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async getPaymentDetails(paymentId) {
|
|
145
|
+
try {
|
|
146
|
+
const response = await this.client.get(`/payments/${paymentId}`);
|
|
147
|
+
return response.data;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
throw new Error(`Error getting payment details: ${error.response?.data?.error?.description || error.message}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async getOrderDetails(orderId) {
|
|
154
|
+
try {
|
|
155
|
+
const response = await this.client.get(`/orders/${orderId}`);
|
|
156
|
+
return response.data;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
throw new Error(`Error getting order details: ${error.response?.data?.error?.description || error.message}`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async capturePayment(paymentId, amount) {
|
|
163
|
+
try {
|
|
164
|
+
const response = await this.client.post(`/payments/${paymentId}/capture`, {
|
|
165
|
+
amount: Math.round(parseFloat(amount) * 100),
|
|
166
|
+
currency: 'INR'
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return response.data;
|
|
170
|
+
} catch (error) {
|
|
171
|
+
throw new Error(`Error capturing payment: ${error.response?.data?.error?.description || error.message}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async createRefund(paymentId, options = {}) {
|
|
176
|
+
try {
|
|
177
|
+
const refundData = {
|
|
178
|
+
amount: options.amount ? Math.round(parseFloat(options.amount) * 100) : undefined,
|
|
179
|
+
speed: options.speed || 'normal', // normal or optimum
|
|
180
|
+
notes: options.notes || {},
|
|
181
|
+
receipt: options.receipt
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const response = await this.client.post(`/payments/${paymentId}/refund`, refundData);
|
|
185
|
+
return response.data;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
throw new Error(`Error creating refund: ${error.response?.data?.error?.description || error.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async verifyWebhookSignature(webhookBody, webhookSignature, webhookSecret) {
|
|
192
|
+
try {
|
|
193
|
+
const expectedSignature = crypto
|
|
194
|
+
.createHmac('sha256', webhookSecret)
|
|
195
|
+
.update(webhookBody)
|
|
196
|
+
.digest('hex');
|
|
197
|
+
|
|
198
|
+
return expectedSignature === webhookSignature;
|
|
199
|
+
} catch (error) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = RazorpayClient;
|
package/lib/senangpay.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class SenangPayClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['merchantId', 'secretKey'];
|
|
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.secretKey = config.secretKey;
|
|
13
|
+
this.baseURL = config.sandbox
|
|
14
|
+
? 'https://sandbox.senangpay.my/payment'
|
|
15
|
+
: 'https://app.senangpay.my/payment';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
generateHash(detail, amount, orderId) {
|
|
19
|
+
const hashString = this.secretKey + detail + amount + orderId;
|
|
20
|
+
return crypto.createHash('md5').update(hashString).digest('hex');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async createPayment(options) {
|
|
24
|
+
try {
|
|
25
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
26
|
+
const amount = parseFloat(options.amount).toFixed(2);
|
|
27
|
+
const detail = options.description || options.name || 'Payment';
|
|
28
|
+
|
|
29
|
+
const hash = this.generateHash(detail, amount, orderId);
|
|
30
|
+
|
|
31
|
+
const paymentData = {
|
|
32
|
+
detail: detail,
|
|
33
|
+
amount: amount,
|
|
34
|
+
order_id: orderId,
|
|
35
|
+
name: options.name || options.customerName || '',
|
|
36
|
+
email: options.email || '',
|
|
37
|
+
phone: options.phone || '',
|
|
38
|
+
hash: hash
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Create payment URL
|
|
42
|
+
const params = new URLSearchParams(paymentData);
|
|
43
|
+
const paymentUrl = `${this.baseURL}/${this.merchantId}?${params.toString()}`;
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
status: 'success',
|
|
47
|
+
data: {
|
|
48
|
+
url: paymentUrl,
|
|
49
|
+
orderId: orderId,
|
|
50
|
+
amount: amount,
|
|
51
|
+
currency: 'MYR',
|
|
52
|
+
hash: hash
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async handleCallback(callbackData) {
|
|
61
|
+
try {
|
|
62
|
+
const verification = await this.verifyCallback(callbackData);
|
|
63
|
+
|
|
64
|
+
if (!verification.status) {
|
|
65
|
+
throw new Error(verification.error.message);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Status mapping
|
|
69
|
+
const statusMapping = {
|
|
70
|
+
'1': 'success', // Success
|
|
71
|
+
'0': 'failed' // Failed
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
status: statusMapping[callbackData.status_id] || 'unknown',
|
|
76
|
+
orderId: callbackData.order_id,
|
|
77
|
+
transactionId: callbackData.transaction_id,
|
|
78
|
+
amount: parseFloat(callbackData.amount),
|
|
79
|
+
currency: 'MYR',
|
|
80
|
+
paymentStatus: callbackData.status_id === '1' ? 'completed' : 'failed',
|
|
81
|
+
message: callbackData.msg
|
|
82
|
+
};
|
|
83
|
+
} catch (error) {
|
|
84
|
+
throw new Error(`Error in SenangPay callback handling: ${error.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async verifyCallback(data) {
|
|
89
|
+
try {
|
|
90
|
+
const receivedHash = data.hash;
|
|
91
|
+
const hashString = this.secretKey + data.status_id + data.order_id + data.transaction_id + data.msg;
|
|
92
|
+
const expectedHash = crypto.createHash('md5').update(hashString).digest('hex');
|
|
93
|
+
|
|
94
|
+
if (receivedHash !== expectedHash) {
|
|
95
|
+
return {
|
|
96
|
+
status: false,
|
|
97
|
+
error: {
|
|
98
|
+
code: 401,
|
|
99
|
+
message: 'Invalid hash'
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
status: true,
|
|
106
|
+
data: data
|
|
107
|
+
};
|
|
108
|
+
} catch (error) {
|
|
109
|
+
return {
|
|
110
|
+
status: false,
|
|
111
|
+
error: {
|
|
112
|
+
code: 500,
|
|
113
|
+
message: error.message
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async getTransactionStatus(orderId) {
|
|
120
|
+
try {
|
|
121
|
+
// SenangPay doesn't have a standard API for checking transaction status
|
|
122
|
+
// This would need to be implemented based on their specific API if available
|
|
123
|
+
throw new Error('Transaction status check not available in standard SenangPay integration');
|
|
124
|
+
} catch (error) {
|
|
125
|
+
throw new Error(`Error getting transaction status: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = SenangPayClient;
|