quickpos 1.0.905 → 1.0.907
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/app.js +2 -1
- package/example-anypay.js +334 -0
- package/example-cryptomus.js +40 -0
- package/example-esnekpos.js +254 -0
- package/example-fedapay.js +217 -0
- package/example-iyzico.js +103 -0
- package/example-paymaya.js +112 -0
- package/example-shopier.js +1 -1
- package/lib/anypay.js +246 -0
- package/lib/cryptomus.js +2 -2
- package/lib/esnekpos.js +352 -0
- package/lib/fedapay.js +194 -0
- package/lib/iyzico.js +180 -0
- package/lib/paymaya.js +126 -0
- package/package.json +17 -4
- package/readme.md +10 -12
package/lib/esnekpos.js
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
const esnekpos = require('esnekpos');
|
|
4
|
+
|
|
5
|
+
class EsnekPos {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config || {};
|
|
8
|
+
const requiredFields = ['merchant', 'merchantKey'];
|
|
9
|
+
for (let field of requiredFields) {
|
|
10
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this.client = esnekpos.createClient({
|
|
14
|
+
merchant: config.merchant,
|
|
15
|
+
merchantKey: config.merchantKey,
|
|
16
|
+
testMode: config.testMode || false
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
this.debug = config.debug || false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async createPayment(paymentDetails) {
|
|
23
|
+
try {
|
|
24
|
+
// Zorunlu alanları kontrol et
|
|
25
|
+
const requiredData = ['amount', 'currency', 'orderId', 'callbackUrl'];
|
|
26
|
+
for (let data of requiredData) {
|
|
27
|
+
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Temel yapılandırma objesi
|
|
31
|
+
const baseConfig = {
|
|
32
|
+
Config: {
|
|
33
|
+
ORDER_REF_NUMBER: paymentDetails.orderId,
|
|
34
|
+
ORDER_AMOUNT: paymentDetails.amount,
|
|
35
|
+
PRICES_CURRENCY: paymentDetails.currency || 'TRY',
|
|
36
|
+
BACK_URL: paymentDetails.callbackUrl,
|
|
37
|
+
LOCALE: paymentDetails.locale || 'tr'
|
|
38
|
+
},
|
|
39
|
+
Customer: {
|
|
40
|
+
FIRST_NAME: paymentDetails.name || 'Müşteri',
|
|
41
|
+
LAST_NAME: paymentDetails.surname || 'Adı',
|
|
42
|
+
MAIL: paymentDetails.email || 'musteri@example.com',
|
|
43
|
+
PHONE: paymentDetails.phone || '',
|
|
44
|
+
CITY: paymentDetails.city || '',
|
|
45
|
+
STATE: paymentDetails.state || '',
|
|
46
|
+
ADDRESS: paymentDetails.address || ''
|
|
47
|
+
},
|
|
48
|
+
Product: [
|
|
49
|
+
{
|
|
50
|
+
PRODUCT_ID: paymentDetails.productId || '1',
|
|
51
|
+
PRODUCT_NAME: paymentDetails.description || 'Ürün Adı',
|
|
52
|
+
PRODUCT_CATEGORY: paymentDetails.category || 'Diğer',
|
|
53
|
+
PRODUCT_DESCRIPTION: paymentDetails.description || 'Ürün Açıklaması',
|
|
54
|
+
PRODUCT_AMOUNT: paymentDetails.amount
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Ödeme türüne göre işlem
|
|
60
|
+
let response;
|
|
61
|
+
|
|
62
|
+
if (paymentDetails.paymentMethod === 'bkm') {
|
|
63
|
+
// BKM Express ödemesi
|
|
64
|
+
if (this.debug) console.log('Creating BKM Express payment');
|
|
65
|
+
response = await this.client.payment.createBkmPayment(baseConfig);
|
|
66
|
+
} else if (paymentDetails.creditCard) {
|
|
67
|
+
// 3D ödeme
|
|
68
|
+
if (this.debug) console.log('Creating 3D payment with card info');
|
|
69
|
+
|
|
70
|
+
baseConfig.CreditCard = {
|
|
71
|
+
CC_NUMBER: paymentDetails.creditCard.number,
|
|
72
|
+
EXP_MONTH: paymentDetails.creditCard.expireMonth,
|
|
73
|
+
EXP_YEAR: paymentDetails.creditCard.expireYear,
|
|
74
|
+
CC_CVV: paymentDetails.creditCard.cvv,
|
|
75
|
+
CC_OWNER: paymentDetails.creditCard.owner,
|
|
76
|
+
INSTALLMENT_NUMBER: paymentDetails.creditCard.installment || '1'
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
response = await this.client.payment.create3DPayment(baseConfig);
|
|
80
|
+
} else if (paymentDetails.recurring) {
|
|
81
|
+
// Tekrarlı ödeme
|
|
82
|
+
if (this.debug) console.log('Creating recurring payment');
|
|
83
|
+
|
|
84
|
+
// Recurring özellikleri ekle
|
|
85
|
+
baseConfig.Config.REPEAT = paymentDetails.recurring.repeat || '1';
|
|
86
|
+
baseConfig.Config.TRIES_COUNT = paymentDetails.recurring.triesCount || '3';
|
|
87
|
+
baseConfig.Config.START_DATE = paymentDetails.recurring.startDate ||
|
|
88
|
+
new Date(Date.now() + 86400000).toISOString().split('T')[0];
|
|
89
|
+
|
|
90
|
+
// Kart bilgileri array olarak eklenmeli
|
|
91
|
+
baseConfig.Cards = [{
|
|
92
|
+
CC_NUMBER: paymentDetails.creditCard.number,
|
|
93
|
+
EXP_MONTH: paymentDetails.creditCard.expireMonth,
|
|
94
|
+
EXP_YEAR: paymentDetails.creditCard.expireYear,
|
|
95
|
+
CC_CVV: paymentDetails.creditCard.cvv,
|
|
96
|
+
CC_OWNER: paymentDetails.creditCard.owner
|
|
97
|
+
}];
|
|
98
|
+
|
|
99
|
+
response = await this.client.recurring.createRecurringPayment(baseConfig);
|
|
100
|
+
} else {
|
|
101
|
+
// Ortak ödeme sayfası
|
|
102
|
+
if (this.debug) console.log('Creating common payment page');
|
|
103
|
+
response = await this.client.payment.createCommonPayment(baseConfig);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (this.debug) console.log('EsnekPOS response:', response);
|
|
107
|
+
|
|
108
|
+
if (response && (response.STATUS == 'SUCCESS')) {
|
|
109
|
+
return {
|
|
110
|
+
status: 'success',
|
|
111
|
+
data: {
|
|
112
|
+
transactionId: response.ORDER_REF_NUMBER || response.orderRefNumber || response.data?.orderRefNumber,
|
|
113
|
+
url: response.URL_3DS || response.data?.URL_3DS,
|
|
114
|
+
id: response.REFNO || response.orderRefNumber || response.data?.orderRefNumber,
|
|
115
|
+
html: response.HTML_3DS || response.data?.HTML_3DS || null
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
} else {
|
|
119
|
+
return {
|
|
120
|
+
status: 'fail',
|
|
121
|
+
message: response.message || 'Ödeme oluşturulamadı',
|
|
122
|
+
error: response.errorMessage || response.error || 'Bilinmeyen hata'
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (this.debug) console.error('EsnekPOS payment creation error:', error);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
status: 'fail',
|
|
130
|
+
message: error.message || 'Bilinmeyen hata'
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async handleCallback(callbackData) {
|
|
136
|
+
try {
|
|
137
|
+
if (this.debug) console.log('Processing callback data:', callbackData);
|
|
138
|
+
|
|
139
|
+
// İşlem başarılı mı?
|
|
140
|
+
const isSuccess = callbackData.STATUS === 'SUCCESS' ||
|
|
141
|
+
callbackData.result === 'success' ||
|
|
142
|
+
callbackData.success === true;
|
|
143
|
+
|
|
144
|
+
if (isSuccess) {
|
|
145
|
+
// İşlem sorgulama ile durum teyidi
|
|
146
|
+
const orderRefNumber = callbackData.ORDER_REF_NUMBER || callbackData.orderRefNumber;
|
|
147
|
+
|
|
148
|
+
if (!orderRefNumber) {
|
|
149
|
+
throw new Error('Missing order reference number');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const transactionDetails = await this.getPaymentStatus(orderRefNumber);
|
|
153
|
+
|
|
154
|
+
if (transactionDetails.status === 'success') {
|
|
155
|
+
return {
|
|
156
|
+
status: 'success',
|
|
157
|
+
orderId: orderRefNumber,
|
|
158
|
+
transactionId: callbackData.transactionId || orderRefNumber,
|
|
159
|
+
amount: parseFloat(callbackData.AMOUNT || callbackData.amount || callbackData.ORDER_AMOUNT),
|
|
160
|
+
currency: callbackData.currency || callbackData.PRICES_CURRENCY || 'TRY',
|
|
161
|
+
paymentType: callbackData.paymentType || 'creditcard',
|
|
162
|
+
date: callbackData.date || new Date().toISOString()
|
|
163
|
+
};
|
|
164
|
+
} else {
|
|
165
|
+
throw new Error('Transaction verification failed');
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
throw new Error(`Payment failed with status: ${callbackData.status || 'unknown'}`);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (this.debug) console.error('Callback handling error:', error);
|
|
172
|
+
throw new Error(`Error in EsnekPOS callback handling: ${error.message}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async getPaymentStatus(orderRefNumber) {
|
|
177
|
+
try {
|
|
178
|
+
const result = await this.client.query.queryTransactionDetail(orderRefNumber);
|
|
179
|
+
|
|
180
|
+
if (this.debug) console.log('Payment status result:', result);
|
|
181
|
+
|
|
182
|
+
if (result && result.STATUS === 'SUCCESS') {
|
|
183
|
+
return {
|
|
184
|
+
status: 'success',
|
|
185
|
+
data: result.paymentList || result
|
|
186
|
+
};
|
|
187
|
+
} else {
|
|
188
|
+
return {
|
|
189
|
+
status: 'fail',
|
|
190
|
+
message: result.message || 'İşlem bulunamadı'
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (this.debug) console.error('Get payment status error:', error);
|
|
195
|
+
throw new Error(`Error getting payment status: ${error.message}`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async refundPayment(orderRefNumber, amount = null, syncWithPos = true) {
|
|
200
|
+
try {
|
|
201
|
+
const params = {
|
|
202
|
+
orderRefNumber: orderRefNumber,
|
|
203
|
+
amount: amount, // Null ise tam iade yapılır
|
|
204
|
+
syncWithPos: syncWithPos
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const result = await this.client.refund.refundTransaction(params);
|
|
208
|
+
|
|
209
|
+
if (result && result.success) {
|
|
210
|
+
return {
|
|
211
|
+
status: 'success',
|
|
212
|
+
data: result.data || result
|
|
213
|
+
};
|
|
214
|
+
} else {
|
|
215
|
+
return {
|
|
216
|
+
status: 'fail',
|
|
217
|
+
message: result.message || 'İade işlemi başarısız oldu'
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
} catch (error) {
|
|
221
|
+
if (this.debug) console.error('Refund error:', error);
|
|
222
|
+
throw new Error(`Error refunding payment: ${error.message}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Taksit seçenekleri sorgulama
|
|
227
|
+
async getInstallmentOptions(amount, bin = null, commissionForCustomer = 1) {
|
|
228
|
+
try {
|
|
229
|
+
const params = {
|
|
230
|
+
amount: amount,
|
|
231
|
+
commissionForCustomer: commissionForCustomer
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
if (bin && bin.length === 6) {
|
|
235
|
+
params.bin = bin;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const result = await this.client.query.getInstallmentOptions(params);
|
|
239
|
+
|
|
240
|
+
if (result && result.success) {
|
|
241
|
+
return {
|
|
242
|
+
status: 'success',
|
|
243
|
+
data: result.data || result
|
|
244
|
+
};
|
|
245
|
+
} else {
|
|
246
|
+
return {
|
|
247
|
+
status: 'fail',
|
|
248
|
+
message: result.message || 'Taksit seçenekleri alınamadı'
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
if (this.debug) console.error('Get installment options error:', error);
|
|
253
|
+
throw new Error(`Error getting installment options: ${error.message}`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Üye işyeri bakiyesi sorgulama
|
|
258
|
+
async getDealerBalance(currency = 'TRY') {
|
|
259
|
+
try {
|
|
260
|
+
const result = await this.client.query.getDealerBalance({
|
|
261
|
+
currency: currency
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
if (result && result.success) {
|
|
265
|
+
return {
|
|
266
|
+
status: 'success',
|
|
267
|
+
data: result.data || result
|
|
268
|
+
};
|
|
269
|
+
} else {
|
|
270
|
+
return {
|
|
271
|
+
status: 'fail',
|
|
272
|
+
message: result.message || 'Bakiye bilgisi alınamadı'
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
} catch (error) {
|
|
276
|
+
if (this.debug) console.error('Get dealer balance error:', error);
|
|
277
|
+
throw new Error(`Error getting dealer balance: ${error.message}`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// BIN sorgulama
|
|
282
|
+
async getBinInfo(bin) {
|
|
283
|
+
try {
|
|
284
|
+
if (!bin || bin.length !== 6) {
|
|
285
|
+
throw new Error('Geçerli bir BIN numarası (ilk 6 hane) gerekli');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const result = await this.client.query.getBinInfo(bin);
|
|
289
|
+
|
|
290
|
+
if (result && result.success) {
|
|
291
|
+
return {
|
|
292
|
+
status: 'success',
|
|
293
|
+
data: result.data || result
|
|
294
|
+
};
|
|
295
|
+
} else {
|
|
296
|
+
return {
|
|
297
|
+
status: 'fail',
|
|
298
|
+
message: result.message || 'BIN bilgisi alınamadı'
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
if (this.debug) console.error('Get BIN info error:', error);
|
|
303
|
+
throw new Error(`Error getting BIN info: ${error.message}`);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Alt üye işyeri tanımlama (Pazaryeri için)
|
|
308
|
+
async setSubMerchant(merchantData) {
|
|
309
|
+
try {
|
|
310
|
+
const result = await this.client.marketplace.setSubMerchant(merchantData);
|
|
311
|
+
|
|
312
|
+
if (result && result.success) {
|
|
313
|
+
return {
|
|
314
|
+
status: 'success',
|
|
315
|
+
data: result.data || result
|
|
316
|
+
};
|
|
317
|
+
} else {
|
|
318
|
+
return {
|
|
319
|
+
status: 'fail',
|
|
320
|
+
message: result.message || 'Alt üye işyeri tanımlanamadı'
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (this.debug) console.error('Set sub-merchant error:', error);
|
|
325
|
+
throw new Error(`Error setting sub-merchant: ${error.message}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Fiziksel POS listesi
|
|
330
|
+
async listPhysicalPos() {
|
|
331
|
+
try {
|
|
332
|
+
const result = await this.client.physicalPos.listPhysicalPos();
|
|
333
|
+
|
|
334
|
+
if (result && result.success) {
|
|
335
|
+
return {
|
|
336
|
+
status: 'success',
|
|
337
|
+
data: result.data || result
|
|
338
|
+
};
|
|
339
|
+
} else {
|
|
340
|
+
return {
|
|
341
|
+
status: 'fail',
|
|
342
|
+
message: result.message || 'Fiziksel POS listesi alınamadı'
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
} catch (error) {
|
|
346
|
+
if (this.debug) console.error('List physical POS error:', error);
|
|
347
|
+
throw new Error(`Error listing physical POS: ${error.message}`);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
module.exports = EsnekPos;
|
package/lib/fedapay.js
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const { FedaPay, Transaction, Customer } = require('fedapay');
|
|
2
|
+
|
|
3
|
+
class FedaPayClient {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
this.config = config || {};
|
|
6
|
+
const requiredFields = ['apiKey', 'environment'];
|
|
7
|
+
|
|
8
|
+
for (let field of requiredFields) {
|
|
9
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Initialize FedaPay client
|
|
13
|
+
FedaPay.setApiKey(config.apiKey);
|
|
14
|
+
FedaPay.setEnvironment(config.environment || 'sandbox'); // sandbox or live
|
|
15
|
+
|
|
16
|
+
// Optional configuration
|
|
17
|
+
if (config.accountId) FedaPay.setAccountId(config.accountId);
|
|
18
|
+
|
|
19
|
+
this.debug = config.debug || false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async createPayment(paymentDetails) {
|
|
23
|
+
try {
|
|
24
|
+
// Validate required fields
|
|
25
|
+
const requiredData = ['amount', 'currency', 'description'];
|
|
26
|
+
for (let data of requiredData) {
|
|
27
|
+
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this.debug) console.log('Creating payment with details:', paymentDetails);
|
|
31
|
+
|
|
32
|
+
// Prepare customer data if available
|
|
33
|
+
let customer = null;
|
|
34
|
+
if (paymentDetails.email || paymentDetails.firstName || paymentDetails.lastName || paymentDetails.phone) {
|
|
35
|
+
customer = {
|
|
36
|
+
email: paymentDetails.email,
|
|
37
|
+
firstname: paymentDetails.firstName || paymentDetails.name,
|
|
38
|
+
lastname: paymentDetails.lastName || paymentDetails.surname
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// Format phone number correctly according to FedaPay API
|
|
42
|
+
if (paymentDetails.phone) {
|
|
43
|
+
customer.phone_number = {
|
|
44
|
+
number: paymentDetails.phone,
|
|
45
|
+
country: paymentDetails.phoneCountry || 'BJ' // Default to Benin if not specified
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Create transaction data
|
|
51
|
+
const transactionData = {
|
|
52
|
+
description: paymentDetails.description,
|
|
53
|
+
amount: paymentDetails.amount,
|
|
54
|
+
currency: {
|
|
55
|
+
iso: paymentDetails.currency
|
|
56
|
+
},
|
|
57
|
+
callback_url: paymentDetails.callbackUrl || paymentDetails.notificationUrl,
|
|
58
|
+
reference: paymentDetails.orderId || `order-${Date.now()}`
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Add payment mode if specified (mtn, moov, etc.)
|
|
62
|
+
if (paymentDetails.mode) {
|
|
63
|
+
transactionData.mode = paymentDetails.mode;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Add customer if exists
|
|
67
|
+
if (customer) {
|
|
68
|
+
transactionData.customer = customer;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Create transaction
|
|
72
|
+
const transaction = await Transaction.create(transactionData);
|
|
73
|
+
|
|
74
|
+
if (this.debug) console.log('Transaction created:', transaction);
|
|
75
|
+
|
|
76
|
+
// Correct way to generate payment URL using the Transaction class directly
|
|
77
|
+
// We need to retrieve the transaction first to use generateToken method
|
|
78
|
+
const retrievedTransaction = await Transaction.retrieve(transaction.id);
|
|
79
|
+
const tokenData = await retrievedTransaction.generateToken({
|
|
80
|
+
return_url: paymentDetails.returnUrl || paymentDetails.successUrl,
|
|
81
|
+
cancel_url: paymentDetails.cancelUrl || paymentDetails.failUrl
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (this.debug) console.log('Payment token generated:', tokenData);
|
|
85
|
+
|
|
86
|
+
// Extract the token and URL from the response
|
|
87
|
+
const paymentUrl = tokenData.url;
|
|
88
|
+
const token = tokenData.token;
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
status: 'success',
|
|
92
|
+
data: {
|
|
93
|
+
id: transaction.id,
|
|
94
|
+
transactionId: transaction.id,
|
|
95
|
+
reference: transaction.reference,
|
|
96
|
+
url: paymentUrl,
|
|
97
|
+
token: token
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (this.debug) console.error('FedaPay payment creation error:', error);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
status: 'fail',
|
|
105
|
+
message: error.message || 'Unknown error'
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async handleCallback(callbackData) {
|
|
111
|
+
try {
|
|
112
|
+
if (this.debug) console.log('Processing callback data:', callbackData);
|
|
113
|
+
|
|
114
|
+
// FedaPay webhook data structure may vary, adjust as needed
|
|
115
|
+
const transactionId = callbackData.id || callbackData.transaction_id;
|
|
116
|
+
|
|
117
|
+
if (!transactionId) {
|
|
118
|
+
throw new Error('Invalid callback data: missing transaction ID');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Retrieve the transaction to verify its status
|
|
122
|
+
const transaction = await Transaction.retrieve(transactionId);
|
|
123
|
+
|
|
124
|
+
if (this.debug) console.log('Retrieved transaction:', transaction);
|
|
125
|
+
|
|
126
|
+
if (transaction && transaction.status === 'approved') {
|
|
127
|
+
return {
|
|
128
|
+
status: 'success',
|
|
129
|
+
transactionId: transaction.id,
|
|
130
|
+
orderId: transaction.reference,
|
|
131
|
+
amount: transaction.amount,
|
|
132
|
+
currency: transaction.currency.iso,
|
|
133
|
+
paymentDate: transaction.approved_at || transaction.updated_at,
|
|
134
|
+
paymentMethod: transaction.mode || 'unknown'
|
|
135
|
+
};
|
|
136
|
+
} else {
|
|
137
|
+
throw new Error(`Payment failed with status: ${transaction?.status || 'unknown'}`);
|
|
138
|
+
}
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (this.debug) console.error('Callback handling error:', error);
|
|
141
|
+
throw new Error(`Error in FedaPay callback handling: ${error.message}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Additional utility methods
|
|
146
|
+
|
|
147
|
+
async getTransaction(transactionId) {
|
|
148
|
+
try {
|
|
149
|
+
const transaction = await Transaction.retrieve(transactionId);
|
|
150
|
+
return {
|
|
151
|
+
status: 'success',
|
|
152
|
+
data: transaction
|
|
153
|
+
};
|
|
154
|
+
} catch (error) {
|
|
155
|
+
if (this.debug) console.error('Get transaction error:', error);
|
|
156
|
+
return {
|
|
157
|
+
status: 'fail',
|
|
158
|
+
message: error.message
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async listTransactions(params = {}) {
|
|
164
|
+
try {
|
|
165
|
+
const transactions = await Transaction.all(params);
|
|
166
|
+
return {
|
|
167
|
+
status: 'success',
|
|
168
|
+
data: transactions
|
|
169
|
+
};
|
|
170
|
+
} catch (error) {
|
|
171
|
+
if (this.debug) console.error('List transactions error:', error);
|
|
172
|
+
return {
|
|
173
|
+
status: 'fail',
|
|
174
|
+
message: error.message
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async verifySignature(payload, signature, key = null) {
|
|
180
|
+
try {
|
|
181
|
+
// Use provided key or default to API key
|
|
182
|
+
const secretKey = key || this.config.apiKey;
|
|
183
|
+
|
|
184
|
+
// Implementation depends on FedaPay's webhook signature verification method
|
|
185
|
+
// This is a placeholder - adjust according to FedaPay's actual verification method
|
|
186
|
+
return FedaPay.Webhook.verifySignature(payload, signature, secretKey);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (this.debug) console.error('Signature verification error:', error);
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
module.exports = FedaPayClient;
|