quickpos 1.0.910 → 1.0.912
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/examples/example-heleket.js +139 -0
- 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-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/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/heleket.js +67 -1
- 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 +311 -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 +256 -0
- package/lib/paypal.js +542 -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 +256 -0
- package/lib/razorpay.js +205 -0
- package/lib/senangpay.js +130 -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 +279 -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/test.js +492 -0
- package/example-heleket.js +0 -83
- 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/unitpay.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class UnitpayClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['publicKey', 'secretKey'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.publicKey = config.publicKey;
|
|
12
|
+
this.secretKey = config.secretKey;
|
|
13
|
+
this.domain = config.domain || 'unitpay.ru';
|
|
14
|
+
this.baseURL = `https://${this.domain}/api`;
|
|
15
|
+
|
|
16
|
+
this.client = axios.create({
|
|
17
|
+
baseURL: this.baseURL,
|
|
18
|
+
headers: {
|
|
19
|
+
'Content-Type': 'application/json'
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
generateSignature(params) {
|
|
25
|
+
const sorted = Object.keys(params).sort().reduce((acc, key) => {
|
|
26
|
+
if (params[key] !== undefined && params[key] !== null) {
|
|
27
|
+
acc[key] = params[key];
|
|
28
|
+
}
|
|
29
|
+
return acc;
|
|
30
|
+
}, {});
|
|
31
|
+
|
|
32
|
+
const signatureString = Object.entries(sorted)
|
|
33
|
+
.map(([key, value]) => value)
|
|
34
|
+
.join('{up}');
|
|
35
|
+
|
|
36
|
+
return crypto
|
|
37
|
+
.createHash('sha256')
|
|
38
|
+
.update(signatureString + '{up}' + this.secretKey)
|
|
39
|
+
.digest('hex');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async createPayment(options) {
|
|
43
|
+
try {
|
|
44
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
45
|
+
|
|
46
|
+
const params = {
|
|
47
|
+
account: options.account || orderId,
|
|
48
|
+
sum: parseFloat(options.amount).toFixed(2),
|
|
49
|
+
desc: options.description || options.name || 'Payment',
|
|
50
|
+
currency: options.currency || 'RUB',
|
|
51
|
+
resultUrl: options.callbackUrl || options.callback_link,
|
|
52
|
+
successUrl: options.successUrl || options.callback_link,
|
|
53
|
+
errorUrl: options.failureUrl || options.callback_link,
|
|
54
|
+
customerEmail: options.email || '',
|
|
55
|
+
customerPhone: options.phone || '',
|
|
56
|
+
locale: options.locale || 'ru'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
params.signature = this.generateSignature({
|
|
60
|
+
account: params.account,
|
|
61
|
+
currency: params.currency,
|
|
62
|
+
desc: params.desc,
|
|
63
|
+
sum: params.sum
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Create payment URL
|
|
67
|
+
const paymentUrl = `https://${this.domain}/pay/${this.publicKey}?` +
|
|
68
|
+
new URLSearchParams(params).toString();
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
status: 'success',
|
|
72
|
+
data: {
|
|
73
|
+
url: paymentUrl,
|
|
74
|
+
orderId: orderId,
|
|
75
|
+
account: params.account,
|
|
76
|
+
amount: params.sum,
|
|
77
|
+
currency: params.currency
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async handleCallback(callbackData) {
|
|
86
|
+
try {
|
|
87
|
+
const method = callbackData.method;
|
|
88
|
+
const params = callbackData.params || callbackData;
|
|
89
|
+
|
|
90
|
+
// Verify signature
|
|
91
|
+
const verification = await this.verifyCallback(params, method);
|
|
92
|
+
|
|
93
|
+
if (!verification.status) {
|
|
94
|
+
throw new Error(verification.error.message);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Handle different methods
|
|
98
|
+
if (method === 'check') {
|
|
99
|
+
// Payment check - validate order
|
|
100
|
+
return {
|
|
101
|
+
status: 'pending',
|
|
102
|
+
orderId: params.account,
|
|
103
|
+
message: 'Order validation'
|
|
104
|
+
};
|
|
105
|
+
} else if (method === 'pay') {
|
|
106
|
+
// Payment successful
|
|
107
|
+
return {
|
|
108
|
+
status: 'success',
|
|
109
|
+
orderId: params.account,
|
|
110
|
+
transactionId: params.unitpayId,
|
|
111
|
+
amount: parseFloat(params.orderSum),
|
|
112
|
+
currency: params.orderCurrency || 'RUB',
|
|
113
|
+
paymentStatus: 'completed',
|
|
114
|
+
paymentType: params.paymentType,
|
|
115
|
+
profit: parseFloat(params.profit || 0)
|
|
116
|
+
};
|
|
117
|
+
} else if (method === 'error') {
|
|
118
|
+
// Payment error
|
|
119
|
+
return {
|
|
120
|
+
status: 'failed',
|
|
121
|
+
orderId: params.account,
|
|
122
|
+
message: params.message || 'Payment failed'
|
|
123
|
+
};
|
|
124
|
+
} else if (method === 'refund') {
|
|
125
|
+
// Refund notification
|
|
126
|
+
return {
|
|
127
|
+
status: 'refunded',
|
|
128
|
+
orderId: params.account,
|
|
129
|
+
transactionId: params.unitpayId,
|
|
130
|
+
amount: parseFloat(params.orderSum)
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
throw new Error(`Unknown method: ${method}`);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
throw new Error(`Error in Unitpay callback handling: ${error.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async verifyCallback(params, method) {
|
|
141
|
+
try {
|
|
142
|
+
const receivedSignature = params.signature;
|
|
143
|
+
|
|
144
|
+
let signatureParams;
|
|
145
|
+
if (method === 'check') {
|
|
146
|
+
signatureParams = {
|
|
147
|
+
account: params.account,
|
|
148
|
+
orderSum: params.orderSum,
|
|
149
|
+
orderCurrency: params.orderCurrency,
|
|
150
|
+
method: method
|
|
151
|
+
};
|
|
152
|
+
} else if (method === 'pay') {
|
|
153
|
+
signatureParams = {
|
|
154
|
+
account: params.account,
|
|
155
|
+
orderSum: params.orderSum,
|
|
156
|
+
orderCurrency: params.orderCurrency,
|
|
157
|
+
profit: params.profit,
|
|
158
|
+
method: method
|
|
159
|
+
};
|
|
160
|
+
} else if (method === 'refund') {
|
|
161
|
+
signatureParams = {
|
|
162
|
+
account: params.account,
|
|
163
|
+
orderSum: params.orderSum,
|
|
164
|
+
method: method
|
|
165
|
+
};
|
|
166
|
+
} else {
|
|
167
|
+
signatureParams = params;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const expectedSignature = this.generateSignature(signatureParams);
|
|
171
|
+
|
|
172
|
+
if (receivedSignature !== expectedSignature) {
|
|
173
|
+
return {
|
|
174
|
+
status: false,
|
|
175
|
+
error: {
|
|
176
|
+
code: 401,
|
|
177
|
+
message: 'Invalid signature'
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
status: true,
|
|
184
|
+
data: params
|
|
185
|
+
};
|
|
186
|
+
} catch (error) {
|
|
187
|
+
return {
|
|
188
|
+
status: false,
|
|
189
|
+
error: {
|
|
190
|
+
code: 500,
|
|
191
|
+
message: error.message
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
async initPayment(options) {
|
|
198
|
+
try {
|
|
199
|
+
// API method to init payment and get payment ID
|
|
200
|
+
const params = {
|
|
201
|
+
account: options.account || options.orderId || `ORDER-${Date.now()}`,
|
|
202
|
+
sum: parseFloat(options.amount).toFixed(2),
|
|
203
|
+
projectId: this.publicKey,
|
|
204
|
+
desc: options.description || 'Payment',
|
|
205
|
+
currency: options.currency || 'RUB',
|
|
206
|
+
resultUrl: options.callbackUrl || options.callback_link,
|
|
207
|
+
secretKey: this.secretKey
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const response = await this.client.get('/initPayment', { params });
|
|
211
|
+
|
|
212
|
+
if (response.data.result) {
|
|
213
|
+
return response.data.result;
|
|
214
|
+
} else {
|
|
215
|
+
throw new Error(response.data.error?.message || 'Payment initialization failed');
|
|
216
|
+
}
|
|
217
|
+
} catch (error) {
|
|
218
|
+
throw new Error(`Error initializing payment: ${error.response?.data?.error?.message || error.message}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = UnitpayClient;
|
package/lib/urway.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class URWayClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['terminalId', 'password', 'merchantKey'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.terminalId = config.terminalId;
|
|
12
|
+
this.password = config.password;
|
|
13
|
+
this.merchantKey = config.merchantKey;
|
|
14
|
+
this.baseURL = config.testMode
|
|
15
|
+
? 'https://payments-dev.urway-tech.com/URWAYPGService/transaction/jsonProcess/JSONrequest'
|
|
16
|
+
: 'https://payments.urway-tech.com/URWAYPGService/transaction/jsonProcess/JSONrequest';
|
|
17
|
+
|
|
18
|
+
this.client = axios.create({
|
|
19
|
+
headers: {
|
|
20
|
+
'Content-Type': 'application/json'
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
generateHash(trackId, amount, currency) {
|
|
26
|
+
const hashString = `${trackId}|${this.terminalId}|${this.password}|${this.merchantKey}|${amount}|${currency}`;
|
|
27
|
+
return crypto.createHash('sha256').update(hashString).digest('hex');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async createPayment(options) {
|
|
31
|
+
try {
|
|
32
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
33
|
+
const trackId = options.trackId || orderId;
|
|
34
|
+
const amount = parseFloat(options.amount).toFixed(2);
|
|
35
|
+
const currency = options.currency || 'SAR';
|
|
36
|
+
|
|
37
|
+
const requestHash = this.generateHash(trackId, amount, currency);
|
|
38
|
+
|
|
39
|
+
const paymentData = {
|
|
40
|
+
trackid: trackId,
|
|
41
|
+
terminalId: this.terminalId,
|
|
42
|
+
customerEmail: options.email || '',
|
|
43
|
+
action: '1', // Purchase action
|
|
44
|
+
merchantIp: options.merchantIp || '',
|
|
45
|
+
password: this.password,
|
|
46
|
+
currency: currency,
|
|
47
|
+
country: options.country || 'SA',
|
|
48
|
+
amount: amount,
|
|
49
|
+
requestHash: requestHash,
|
|
50
|
+
udf1: options.udf1 || '',
|
|
51
|
+
udf2: options.udf2 || orderId,
|
|
52
|
+
udf3: options.udf3 || '',
|
|
53
|
+
udf4: options.udf4 || '',
|
|
54
|
+
udf5: options.udf5 || '',
|
|
55
|
+
tokenizationType: options.tokenization || '0'
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const response = await this.client.post(this.baseURL, paymentData);
|
|
59
|
+
|
|
60
|
+
if (response.data.responseCode === '000') {
|
|
61
|
+
return {
|
|
62
|
+
status: 'success',
|
|
63
|
+
data: {
|
|
64
|
+
url: response.data.paymentUrl || response.data.targetUrl,
|
|
65
|
+
trackId: trackId,
|
|
66
|
+
orderId: orderId,
|
|
67
|
+
amount: amount,
|
|
68
|
+
currency: currency,
|
|
69
|
+
payid: response.data.payid
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(response.data.responseMessage || 'Payment creation failed');
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new Error(`Payment creation error: ${error.response?.data?.responseMessage || error.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async handleCallback(callbackData) {
|
|
81
|
+
try {
|
|
82
|
+
const trackId = callbackData.trackid || callbackData.TrackId;
|
|
83
|
+
|
|
84
|
+
if (!trackId) {
|
|
85
|
+
throw new Error('Track ID not found in callback data');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Verify transaction hash
|
|
89
|
+
const verification = await this.verifyTransaction(callbackData);
|
|
90
|
+
|
|
91
|
+
if (!verification.status) {
|
|
92
|
+
throw new Error(verification.error.message);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Status mapping
|
|
96
|
+
const statusMapping = {
|
|
97
|
+
'000': 'success', // Approved
|
|
98
|
+
'001': 'pending', // In progress
|
|
99
|
+
'100': 'failed', // Declined
|
|
100
|
+
'400': 'failed', // Error
|
|
101
|
+
'401': 'failed' // Invalid
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
status: statusMapping[callbackData.responseCode] || 'unknown',
|
|
106
|
+
orderId: callbackData.udf2 || trackId,
|
|
107
|
+
trackId: trackId,
|
|
108
|
+
transactionId: callbackData.TranId || callbackData.tranid,
|
|
109
|
+
amount: parseFloat(callbackData.amount),
|
|
110
|
+
currency: callbackData.currency || 'SAR',
|
|
111
|
+
paymentStatus: callbackData.result,
|
|
112
|
+
responseCode: callbackData.responseCode,
|
|
113
|
+
responseMessage: callbackData.responseMessage,
|
|
114
|
+
authCode: callbackData.auth,
|
|
115
|
+
paymentId: callbackData.payid
|
|
116
|
+
};
|
|
117
|
+
} catch (error) {
|
|
118
|
+
throw new Error(`Error in URWay callback handling: ${error.message}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async verifyTransaction(data) {
|
|
123
|
+
try {
|
|
124
|
+
const receivedHash = data.responseHash;
|
|
125
|
+
const trackId = data.trackid || data.TrackId;
|
|
126
|
+
const amount = data.amount;
|
|
127
|
+
const currency = data.currency || 'SAR';
|
|
128
|
+
|
|
129
|
+
const expectedHash = this.generateHash(trackId, amount, currency);
|
|
130
|
+
|
|
131
|
+
if (receivedHash && receivedHash !== expectedHash) {
|
|
132
|
+
return {
|
|
133
|
+
status: false,
|
|
134
|
+
error: {
|
|
135
|
+
code: 401,
|
|
136
|
+
message: 'Invalid transaction hash'
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
status: true,
|
|
143
|
+
data: data
|
|
144
|
+
};
|
|
145
|
+
} catch (error) {
|
|
146
|
+
return {
|
|
147
|
+
status: false,
|
|
148
|
+
error: {
|
|
149
|
+
code: 500,
|
|
150
|
+
message: error.message
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async refund(trackId, options = {}) {
|
|
157
|
+
try {
|
|
158
|
+
const amount = parseFloat(options.amount).toFixed(2);
|
|
159
|
+
const currency = options.currency || 'SAR';
|
|
160
|
+
|
|
161
|
+
const requestHash = this.generateHash(trackId, amount, currency);
|
|
162
|
+
|
|
163
|
+
const refundData = {
|
|
164
|
+
trackid: trackId,
|
|
165
|
+
terminalId: this.terminalId,
|
|
166
|
+
password: this.password,
|
|
167
|
+
action: '2', // Refund action
|
|
168
|
+
amount: amount,
|
|
169
|
+
currency: currency,
|
|
170
|
+
requestHash: requestHash
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const response = await this.client.post(this.baseURL, refundData);
|
|
174
|
+
|
|
175
|
+
return response.data;
|
|
176
|
+
} catch (error) {
|
|
177
|
+
throw new Error(`Error processing refund: ${error.response?.data?.responseMessage || error.message}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = URWayClient;
|
package/lib/volet.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class VoletClient {
|
|
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.URL = 'https://volet.com/sci';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.URL,
|
|
17
|
+
headers: {
|
|
18
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
generateSignature(data) {
|
|
24
|
+
const signatureString = Object.keys(data)
|
|
25
|
+
.sort()
|
|
26
|
+
.map(key => `${key}=${data[key]}`)
|
|
27
|
+
.join(':');
|
|
28
|
+
|
|
29
|
+
return crypto
|
|
30
|
+
.createHash('md5')
|
|
31
|
+
.update(signatureString + ':' + this.secretKey)
|
|
32
|
+
.digest('hex');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async createPayment(options) {
|
|
36
|
+
try {
|
|
37
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
38
|
+
|
|
39
|
+
const paymentData = {
|
|
40
|
+
v_merchant_id: this.merchantId,
|
|
41
|
+
v_amount: parseFloat(options.amount),
|
|
42
|
+
v_currency: options.currency || 'USD',
|
|
43
|
+
v_description: options.description || options.name || 'Payment',
|
|
44
|
+
v_order_id: orderId,
|
|
45
|
+
v_success_url: options.successUrl || options.callback_link,
|
|
46
|
+
v_fail_url: options.failUrl || options.callback_link,
|
|
47
|
+
v_status_url: options.callbackUrl || options.callback_link,
|
|
48
|
+
v_email: options.email || '',
|
|
49
|
+
v_phone: options.phone || ''
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Generate signature
|
|
53
|
+
paymentData.v_sign = this.generateSignature(paymentData);
|
|
54
|
+
|
|
55
|
+
// Create payment URL
|
|
56
|
+
const params = new URLSearchParams(paymentData);
|
|
57
|
+
const paymentUrl = `${this.URL}?${params.toString()}`;
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
status: 'success',
|
|
61
|
+
data: {
|
|
62
|
+
url: paymentUrl,
|
|
63
|
+
orderId: orderId,
|
|
64
|
+
merchantId: this.merchantId
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Payment creation error: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async handleCallback(callbackData) {
|
|
73
|
+
try {
|
|
74
|
+
const verification = await this.verifyCallback(callbackData);
|
|
75
|
+
|
|
76
|
+
if (!verification.status) {
|
|
77
|
+
throw new Error(verification.error.message);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = verification.data;
|
|
81
|
+
|
|
82
|
+
// Status mapping
|
|
83
|
+
const statusMapping = {
|
|
84
|
+
'SUCCESS': 'success',
|
|
85
|
+
'PENDING': 'pending',
|
|
86
|
+
'FAIL': 'failed'
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
status: statusMapping[data.v_status] || 'unknown',
|
|
91
|
+
orderId: data.v_order_id,
|
|
92
|
+
transactionId: data.v_transaction_id,
|
|
93
|
+
amount: parseFloat(data.v_amount),
|
|
94
|
+
currency: data.v_currency,
|
|
95
|
+
paymentStatus: data.v_status,
|
|
96
|
+
paymentMethod: data.v_payment_method
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(`Error in Volet callback handling: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async verifyCallback(data) {
|
|
104
|
+
try {
|
|
105
|
+
const receivedSign = data.v_sign;
|
|
106
|
+
const dataToVerify = { ...data };
|
|
107
|
+
delete dataToVerify.v_sign;
|
|
108
|
+
|
|
109
|
+
const expectedSign = this.generateSignature(dataToVerify);
|
|
110
|
+
|
|
111
|
+
if (receivedSign !== expectedSign) {
|
|
112
|
+
return {
|
|
113
|
+
status: false,
|
|
114
|
+
error: {
|
|
115
|
+
code: 401,
|
|
116
|
+
message: 'Invalid signature'
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (data.v_status === 'FAIL') {
|
|
122
|
+
return {
|
|
123
|
+
status: false,
|
|
124
|
+
error: {
|
|
125
|
+
code: 400,
|
|
126
|
+
message: 'Payment failed'
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
status: true,
|
|
133
|
+
data: data
|
|
134
|
+
};
|
|
135
|
+
} catch (error) {
|
|
136
|
+
return {
|
|
137
|
+
status: false,
|
|
138
|
+
error: {
|
|
139
|
+
code: 500,
|
|
140
|
+
message: error.message
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = VoletClient;
|