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/payme.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PayMe Ödeme Entegrasyonu
|
|
5
|
+
*
|
|
6
|
+
* Başlamadan önce:
|
|
7
|
+
* 1. PayMe hesabı oluşturun
|
|
8
|
+
* 2. API anahtarınızı alın (seller_payme_id)
|
|
9
|
+
* 3. Gerekli webhook URL'lerini ayarlayın
|
|
10
|
+
*/
|
|
11
|
+
class PayMeService {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config || {};
|
|
14
|
+
const requiredFields = ['sellerPaymeId'];
|
|
15
|
+
for (let field of requiredFields) {
|
|
16
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.sellerPaymeId = config.sellerPaymeId;
|
|
20
|
+
|
|
21
|
+
// Sandbox veya Production modu seçimi
|
|
22
|
+
this.sandbox = config.sandbox || false;
|
|
23
|
+
|
|
24
|
+
// API URL'ini ortama göre ayarla
|
|
25
|
+
if (this.sandbox) {
|
|
26
|
+
this.baseUrl = 'https://sandbox.payme.io/api';
|
|
27
|
+
} else {
|
|
28
|
+
this.baseUrl = 'https://live.payme.io/api';
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Debug modu
|
|
32
|
+
this.debug = config.debug || false;
|
|
33
|
+
console.log(`PayMe API URL: ${this.baseUrl}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Yeni bir ödeme linki oluşturur
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} paymentDetails - Ödeme detayları
|
|
40
|
+
* @returns {Promise<Object>} Ödeme sonucu
|
|
41
|
+
*/
|
|
42
|
+
async createPayment(paymentDetails) {
|
|
43
|
+
try {
|
|
44
|
+
// Zorunlu alanları kontrol et
|
|
45
|
+
let requiredData = ['name', 'amount', 'currency'];
|
|
46
|
+
for (let data of requiredData) {
|
|
47
|
+
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// PayMe için fiyat formatını ayarla (kuruş/cents cinsinden)
|
|
51
|
+
const salePrice = Math.round(paymentDetails.amount * 100);
|
|
52
|
+
|
|
53
|
+
// Temel ödeme verilerini hazırla
|
|
54
|
+
const paymentData = {
|
|
55
|
+
seller_payme_id: this.sellerPaymeId,
|
|
56
|
+
sale_price: salePrice,
|
|
57
|
+
currency: paymentDetails.currency || 'ILS',
|
|
58
|
+
product_name: paymentDetails.name,
|
|
59
|
+
transaction_id: paymentDetails.orderId || `order-${Date.now()}`,
|
|
60
|
+
installments: paymentDetails.installments || '1',
|
|
61
|
+
market_fee: paymentDetails.marketFee || 0,
|
|
62
|
+
sale_send_notification: paymentDetails.sendNotification || false,
|
|
63
|
+
sale_type: paymentDetails.saleType || 'sale',
|
|
64
|
+
sale_payment_method: paymentDetails.paymentMethod || 'credit-card',
|
|
65
|
+
language: paymentDetails.language || 'he' // Varsayılan dil: İbranice (he)
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// İsteğe bağlı alanları ekle
|
|
69
|
+
if (paymentDetails.callbackUrl) paymentData.sale_callback_url = paymentDetails.callbackUrl;
|
|
70
|
+
if (paymentDetails.returnUrl) paymentData.sale_return_url = paymentDetails.returnUrl;
|
|
71
|
+
if (paymentDetails.email) paymentData.sale_email = paymentDetails.email;
|
|
72
|
+
if (paymentDetails.phone) paymentData.sale_mobile = paymentDetails.phone;
|
|
73
|
+
if (paymentDetails.buyerName) paymentData.sale_name = paymentDetails.buyerName;
|
|
74
|
+
if (paymentDetails.layout) paymentData.layout = paymentDetails.layout;
|
|
75
|
+
|
|
76
|
+
// Buyer token kaydetme
|
|
77
|
+
if (paymentDetails.captureBuyer === true) {
|
|
78
|
+
paymentData.capture_buyer = '1';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Debug modunda istek detaylarını göster
|
|
82
|
+
if (this.debug) {
|
|
83
|
+
console.log('PayMe ödeme isteği hazırlanıyor:', JSON.stringify(paymentData, null, 2));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// API isteği gönder
|
|
87
|
+
const response = await axios({
|
|
88
|
+
method: 'POST',
|
|
89
|
+
url: `${this.baseUrl}/generate-sale`,
|
|
90
|
+
headers: {
|
|
91
|
+
'Content-Type': 'application/json',
|
|
92
|
+
'Accept': 'application/json'
|
|
93
|
+
},
|
|
94
|
+
data: paymentData
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const responseData = response.data;
|
|
98
|
+
|
|
99
|
+
// Debug modunda yanıt detaylarını göster
|
|
100
|
+
if (this.debug) {
|
|
101
|
+
console.log('PayMe API yanıtı:', JSON.stringify(responseData, null, 2));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Hata kontrolü
|
|
105
|
+
if (responseData.status_code !== 0) {
|
|
106
|
+
throw new Error(`PayMe API error: ${responseData.status_error_details || 'Unknown error'}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// QR kod oluştur (isteğe bağlı)
|
|
110
|
+
let qrCode = null;
|
|
111
|
+
if (paymentDetails.generateQr) {
|
|
112
|
+
qrCode = await this.generateQrCode(responseData.sale_url);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
status: 'success',
|
|
117
|
+
data: {
|
|
118
|
+
transactionId: responseData.transaction_id,
|
|
119
|
+
paymeId: responseData.payme_sale_id,
|
|
120
|
+
paymeCode: responseData.payme_sale_code,
|
|
121
|
+
url: responseData.sale_url,
|
|
122
|
+
id: responseData.payme_sale_id,
|
|
123
|
+
qr: qrCode
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
} catch (error) {
|
|
127
|
+
if (this.debug) {
|
|
128
|
+
console.error('PayMe API hatası:', error);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (error.response) {
|
|
132
|
+
console.error('Hata detayları:', {
|
|
133
|
+
statusCode: error.response.status,
|
|
134
|
+
statusText: error.response.statusText,
|
|
135
|
+
data: error.response.data
|
|
136
|
+
});
|
|
137
|
+
throw new Error(`PayMe API error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
138
|
+
} else if (error.request) {
|
|
139
|
+
console.error('Yanıt alınamadı:', error.request);
|
|
140
|
+
throw new Error('No response received from PayMe API');
|
|
141
|
+
} else {
|
|
142
|
+
throw new Error(`Error in PayMe payment creation: ${error.message}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Webhook callback'ini işler
|
|
149
|
+
*
|
|
150
|
+
* @param {Object} callbackData - Callback verileri
|
|
151
|
+
* @returns {Promise<Object>} İşlem sonucu
|
|
152
|
+
*/
|
|
153
|
+
async handleCallback(callbackData) {
|
|
154
|
+
try {
|
|
155
|
+
// Debug modunda callback verilerini göster
|
|
156
|
+
if (this.debug) {
|
|
157
|
+
console.log('PayMe callback verileri:', JSON.stringify(callbackData, null, 2));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// PayMe'den gelen önemli alanları kontrol et
|
|
161
|
+
if (!callbackData.payme_sale_id || !callbackData.status_code) {
|
|
162
|
+
throw new Error("Missing required fields in callback data");
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Signature doğrulaması (tam implementasyon için PayMe ile kontrol gerekebilir)
|
|
166
|
+
if (callbackData.payme_signature) {
|
|
167
|
+
// İleri düzey implementasyon: Signature doğrulaması
|
|
168
|
+
console.log('PayMe signature:', callbackData.payme_signature);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Bildirim türü
|
|
172
|
+
const notifyType = callbackData.notify_type || '';
|
|
173
|
+
const saleStatus = callbackData.sale_status || '';
|
|
174
|
+
|
|
175
|
+
// Ödeme durumunu notifyType ve saleStatus'a göre belirle
|
|
176
|
+
if (notifyType === 'sale-complete' || saleStatus === 'completed') {
|
|
177
|
+
// Başarılı ödeme
|
|
178
|
+
return {
|
|
179
|
+
status: 'success',
|
|
180
|
+
orderId: callbackData.transaction_id || '',
|
|
181
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
182
|
+
amount: parseFloat(callbackData.price) / 100, // Kuruş/cents'ten ana birime çevirme
|
|
183
|
+
currency: callbackData.currency || '',
|
|
184
|
+
paymentType: 'payme',
|
|
185
|
+
card: {
|
|
186
|
+
cardMask: callbackData.buyer_card_mask || '',
|
|
187
|
+
cardExp: callbackData.buyer_card_exp || '',
|
|
188
|
+
cardBrand: callbackData.payme_transaction_card_brand || ''
|
|
189
|
+
},
|
|
190
|
+
buyer: {
|
|
191
|
+
name: callbackData.buyer_name || '',
|
|
192
|
+
email: callbackData.buyer_email || '',
|
|
193
|
+
phone: callbackData.buyer_phone || '',
|
|
194
|
+
socialId: callbackData.buyer_social_id || '',
|
|
195
|
+
buyerKey: callbackData.buyer_key || '' // Token için kullanılabilir
|
|
196
|
+
},
|
|
197
|
+
sale: {
|
|
198
|
+
id: callbackData.payme_sale_id,
|
|
199
|
+
code: callbackData.payme_sale_code || '',
|
|
200
|
+
transactionId: callbackData.payme_transaction_id || '',
|
|
201
|
+
authNumber: callbackData.payme_transaction_auth_number || '',
|
|
202
|
+
installments: callbackData.installments || '1',
|
|
203
|
+
createDate: callbackData.sale_created || '',
|
|
204
|
+
paidDate: callbackData.sale_paid_date || '',
|
|
205
|
+
releaseDate: callbackData.sale_release_date || '',
|
|
206
|
+
isTokenSale: callbackData.is_token_sale === '1',
|
|
207
|
+
invoiceUrl: callbackData.sale_invoice_url || ''
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
} else if (notifyType === 'sale-authorized') {
|
|
211
|
+
// Yetkilendirilmiş ödeme (para henüz çekilmemiş)
|
|
212
|
+
return {
|
|
213
|
+
status: 'authorized',
|
|
214
|
+
orderId: callbackData.transaction_id || '',
|
|
215
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
216
|
+
amount: parseFloat(callbackData.price) / 100,
|
|
217
|
+
currency: callbackData.currency || '',
|
|
218
|
+
paymentType: 'payme',
|
|
219
|
+
authNumber: callbackData.payme_transaction_auth_number || ''
|
|
220
|
+
};
|
|
221
|
+
} else if (notifyType === 'refund' || saleStatus === 'refunded' || saleStatus === 'partial-refund') {
|
|
222
|
+
// İade edilen ödeme
|
|
223
|
+
return {
|
|
224
|
+
status: 'refunded',
|
|
225
|
+
orderId: callbackData.transaction_id || '',
|
|
226
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
227
|
+
amount: parseFloat(callbackData.price) / 100,
|
|
228
|
+
currency: callbackData.currency || '',
|
|
229
|
+
isPartial: saleStatus === 'partial-refund'
|
|
230
|
+
};
|
|
231
|
+
} else if (notifyType === 'sale-chargeback' || saleStatus === 'chargeback' || saleStatus === 'partial-chargeback') {
|
|
232
|
+
// Chargeback - müşteri bankasından geri ödeme talebi
|
|
233
|
+
return {
|
|
234
|
+
status: 'chargeback',
|
|
235
|
+
orderId: callbackData.transaction_id || '',
|
|
236
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
237
|
+
amount: parseFloat(callbackData.price) / 100,
|
|
238
|
+
currency: callbackData.currency || '',
|
|
239
|
+
isPartial: saleStatus === 'partial-chargeback'
|
|
240
|
+
};
|
|
241
|
+
} else if (notifyType === 'sale-chargeback-refund') {
|
|
242
|
+
// Chargeback geri alındı
|
|
243
|
+
return {
|
|
244
|
+
status: 'chargeback-refund',
|
|
245
|
+
orderId: callbackData.transaction_id || '',
|
|
246
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
247
|
+
amount: parseFloat(callbackData.price) / 100,
|
|
248
|
+
currency: callbackData.currency || ''
|
|
249
|
+
};
|
|
250
|
+
} else if (notifyType === 'sale-failure' || saleStatus === 'failed' || saleStatus === 'canceled') {
|
|
251
|
+
// Başarısız ödeme
|
|
252
|
+
return {
|
|
253
|
+
status: 'failed',
|
|
254
|
+
orderId: callbackData.transaction_id || '',
|
|
255
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
256
|
+
reason: callbackData.status_error_details || 'Payment failed or canceled'
|
|
257
|
+
};
|
|
258
|
+
} else {
|
|
259
|
+
// Diğer durumlar
|
|
260
|
+
return {
|
|
261
|
+
status: 'other',
|
|
262
|
+
type: notifyType,
|
|
263
|
+
saleStatus: saleStatus,
|
|
264
|
+
orderId: callbackData.transaction_id || '',
|
|
265
|
+
merchant_oid: callbackData.payme_sale_id,
|
|
266
|
+
rawData: callbackData
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (this.debug) {
|
|
271
|
+
console.error('PayMe callback işleme hatası:', error);
|
|
272
|
+
}
|
|
273
|
+
throw new Error(`Error in PayMe callback handling: ${error.message}`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Ödeme bağlantısı için QR kod oluşturur
|
|
279
|
+
*
|
|
280
|
+
* @param {string} paymentUrl - Ödeme URL'i
|
|
281
|
+
* @returns {Promise<string|null>} Base64 formatında QR kod
|
|
282
|
+
*/
|
|
283
|
+
async generateQrCode(paymentUrl) {
|
|
284
|
+
try {
|
|
285
|
+
const response = await axios.get('https://api.qrserver.com/v1/create-qr-code/', {
|
|
286
|
+
params: {
|
|
287
|
+
size: '300x300',
|
|
288
|
+
data: paymentUrl
|
|
289
|
+
},
|
|
290
|
+
responseType: 'arraybuffer'
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
const base64Image = Buffer.from(response.data, 'binary').toString('base64');
|
|
294
|
+
return `data:image/png;base64,${base64Image}`;
|
|
295
|
+
} catch (error) {
|
|
296
|
+
console.error('QR kod oluşturma hatası:', error);
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
module.exports = PayMeService;
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
const Paymentwall = require('paymentwall');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
|
|
4
|
+
class PaymentwallService {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config || {};
|
|
7
|
+
const requiredFields = ['appKey', 'secretKey'];
|
|
8
|
+
for (let field of requiredFields) {
|
|
9
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Paymentwall SDK'sını yapılandır
|
|
13
|
+
Paymentwall.Configure(
|
|
14
|
+
Paymentwall.Base.API_GOODS,
|
|
15
|
+
config.appKey,
|
|
16
|
+
config.secretKey
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
this.testMode = config.testMode || false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async createPayment(paymentDetails) {
|
|
23
|
+
try {
|
|
24
|
+
let requiredData = ['name', 'amount', 'currency', 'email'];
|
|
25
|
+
for (let data of requiredData) {
|
|
26
|
+
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Benzersiz bir ürün ve işlem ID'si oluştur
|
|
30
|
+
const productId = paymentDetails.productId || 'product_' + Date.now();
|
|
31
|
+
const orderId = paymentDetails.orderId || 'order_' + Date.now();
|
|
32
|
+
|
|
33
|
+
// Ürünü tek seferlik (one-time) ödeme olarak yapılandır
|
|
34
|
+
const product = new Paymentwall.Product(
|
|
35
|
+
productId,
|
|
36
|
+
paymentDetails.amount,
|
|
37
|
+
paymentDetails.currency,
|
|
38
|
+
paymentDetails.name,
|
|
39
|
+
Paymentwall.Product.TYPE_FIXED // Tek seferlik ödeme tipi
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// Kullanıcı bilgilerini oluştur
|
|
43
|
+
const userId = paymentDetails.userId || 'user_' + Date.now();
|
|
44
|
+
const userInfo = {
|
|
45
|
+
'email': paymentDetails.email,
|
|
46
|
+
'order_id': orderId
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
if (paymentDetails.userName) userInfo.name = paymentDetails.userName;
|
|
50
|
+
|
|
51
|
+
// Tüm extra parametreleri toplayalım
|
|
52
|
+
const extraParams = {};
|
|
53
|
+
|
|
54
|
+
// Custom parametreleri ekle (varsa)
|
|
55
|
+
if (paymentDetails.customParams) {
|
|
56
|
+
Object.entries(paymentDetails.customParams).forEach(([key, value]) => {
|
|
57
|
+
extraParams[key] = value;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Webhook URL'ini ayarla (varsa)
|
|
62
|
+
if (paymentDetails.pingback_url) {
|
|
63
|
+
extraParams['pingback_url'] = paymentDetails.pingback_url;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Dönüş URL'ini ayarla (varsa)
|
|
67
|
+
if (paymentDetails.successUrl) {
|
|
68
|
+
extraParams['success_url'] = paymentDetails.successUrl;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Ödeme Widget'ını oluştur
|
|
72
|
+
const widgetCode = paymentDetails.widgetCode || 'p1_1';
|
|
73
|
+
const widget = new Paymentwall.Widget(
|
|
74
|
+
userId,
|
|
75
|
+
widgetCode,
|
|
76
|
+
[product],
|
|
77
|
+
{...userInfo, ...extraParams}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
// Widget URL'sini al
|
|
81
|
+
const widgetUrl = widget.getUrl();
|
|
82
|
+
|
|
83
|
+
// QR kod oluştur (isteğe bağlı)
|
|
84
|
+
let qrCode = null;
|
|
85
|
+
if (paymentDetails.generateQr) {
|
|
86
|
+
qrCode = await this.generateQrCode(widgetUrl);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
status: 'success',
|
|
91
|
+
data: {
|
|
92
|
+
transactionId: orderId,
|
|
93
|
+
url: widgetUrl,
|
|
94
|
+
id: orderId,
|
|
95
|
+
qr: qrCode
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
throw new Error(`Error in Paymentwall payment creation: ${error.message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async handleCallback(callbackData, ipAddress) {
|
|
104
|
+
try {
|
|
105
|
+
console.log('🔍 Paymentwall callback işleniyor:', callbackData);
|
|
106
|
+
|
|
107
|
+
// Test modu kontrolü
|
|
108
|
+
if (callbackData.is_test === 1 || callbackData.is_test === '1' || callbackData.test_mode === 1) {
|
|
109
|
+
console.log('🧪 Test modu callback algılandı');
|
|
110
|
+
|
|
111
|
+
// Test callback işleme - gerçek data doğrulama yapmadan test için
|
|
112
|
+
return {
|
|
113
|
+
status: 'success',
|
|
114
|
+
orderId: callbackData.order_id || callbackData.uid || 'test-order',
|
|
115
|
+
merchant_oid: callbackData.ref || 'test-ref',
|
|
116
|
+
amount: callbackData.amount || '199.99',
|
|
117
|
+
currency: callbackData.currency || 'TRY',
|
|
118
|
+
paymentType: 'paymentwall-test'
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Paymentwall pingback nesnesi oluştur
|
|
123
|
+
const pingback = new Paymentwall.Pingback(
|
|
124
|
+
callbackData,
|
|
125
|
+
ipAddress,
|
|
126
|
+
this.testMode
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
console.log('🔍 Pingback oluşturuldu, doğrulanıyor...');
|
|
130
|
+
|
|
131
|
+
// Pingback'in geçerli olup olmadığını kontrol et
|
|
132
|
+
if (pingback.validate()) {
|
|
133
|
+
console.log('✅ Pingback doğrulandı. Tip:', pingback.getType());
|
|
134
|
+
|
|
135
|
+
// Callback verileri direkt olarak kullanılacak
|
|
136
|
+
let amount = callbackData.amount || '0';
|
|
137
|
+
let currency = callbackData.currency || 'TRY';
|
|
138
|
+
|
|
139
|
+
// Callback veri yapısına göre doğru alan isimlerini kullan
|
|
140
|
+
if (callbackData.goodsid) {
|
|
141
|
+
console.log('✅ Ürün ID tespit edildi:', callbackData.goodsid);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Ödeme başarılı mı kontrolü
|
|
145
|
+
if (pingback.isDeliverable()) {
|
|
146
|
+
console.log('✅ Ödeme başarılı (deliverable)');
|
|
147
|
+
// Tek seferlik ödeme başarılı
|
|
148
|
+
return {
|
|
149
|
+
status: 'success',
|
|
150
|
+
orderId: callbackData.order_id || callbackData.uid || '',
|
|
151
|
+
merchant_oid: callbackData.ref || '',
|
|
152
|
+
amount: amount,
|
|
153
|
+
currency: currency,
|
|
154
|
+
paymentType: 'paymentwall',
|
|
155
|
+
raw: callbackData // Tüm veriyi de ekleyelim
|
|
156
|
+
};
|
|
157
|
+
} else if (pingback.isCancelable()) {
|
|
158
|
+
console.log('❌ Ödeme iptal edildi (cancelable)');
|
|
159
|
+
return {
|
|
160
|
+
status: 'failed',
|
|
161
|
+
orderId: callbackData.order_id || callbackData.uid || '',
|
|
162
|
+
reason: 'Payment was cancelled',
|
|
163
|
+
raw: callbackData
|
|
164
|
+
};
|
|
165
|
+
} else {
|
|
166
|
+
console.log('ℹ️ Diğer pingback tipi:', pingback.getType());
|
|
167
|
+
return {
|
|
168
|
+
status: 'other',
|
|
169
|
+
type: pingback.getType(),
|
|
170
|
+
orderId: callbackData.order_id || callbackData.uid || '',
|
|
171
|
+
raw: callbackData
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
const errorCode = pingback.getErrorCode();
|
|
176
|
+
console.error('❌ Geçersiz pingback:', errorCode);
|
|
177
|
+
throw new Error('Invalid pingback: ' + errorCode);
|
|
178
|
+
}
|
|
179
|
+
} catch (error) {
|
|
180
|
+
console.error('❌ Callback işleme hatası:', error);
|
|
181
|
+
throw new Error(`Error in Paymentwall callback handling: ${error.message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// QR code oluşturmak için yardımcı fonksiyon
|
|
186
|
+
async generateQrCode(paymentUrl) {
|
|
187
|
+
try {
|
|
188
|
+
const response = await axios.get('https://api.qrserver.com/v1/create-qr-code/', {
|
|
189
|
+
params: {
|
|
190
|
+
size: '300x300',
|
|
191
|
+
data: paymentUrl
|
|
192
|
+
},
|
|
193
|
+
responseType: 'arraybuffer'
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const base64Image = Buffer.from(response.data, 'binary').toString('base64');
|
|
197
|
+
return `data:image/png;base64,${base64Image}`;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
console.error('QR code generation failed:', error);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = PaymentwallService;
|
package/lib/paynet.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PayNetClient {
|
|
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 = 'https://paynet.md/acquiring';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.baseURL,
|
|
17
|
+
headers: {
|
|
18
|
+
'Content-Type': 'application/json'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
generateSignature(data) {
|
|
24
|
+
const signatureString = Object.keys(data)
|
|
25
|
+
.filter(key => key !== 'signature')
|
|
26
|
+
.sort()
|
|
27
|
+
.map(key => `${key}=${data[key]}`)
|
|
28
|
+
.join('&');
|
|
29
|
+
|
|
30
|
+
return crypto
|
|
31
|
+
.createHmac('sha256', this.secretKey)
|
|
32
|
+
.update(signatureString)
|
|
33
|
+
.digest('hex')
|
|
34
|
+
.toUpperCase();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async createPayment(options) {
|
|
38
|
+
try {
|
|
39
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
40
|
+
const amount = parseFloat(options.amount) * 100; // Convert to cents
|
|
41
|
+
|
|
42
|
+
const paymentData = {
|
|
43
|
+
merchant_id: this.merchantId,
|
|
44
|
+
order_id: orderId,
|
|
45
|
+
amount: amount,
|
|
46
|
+
currency: options.currency || 'MDL',
|
|
47
|
+
description: options.description || options.name || 'Payment',
|
|
48
|
+
success_url: options.successUrl || options.callback_link,
|
|
49
|
+
fail_url: options.failUrl || options.callback_link,
|
|
50
|
+
callback_url: options.callbackUrl || options.callback_link,
|
|
51
|
+
language: options.language || 'ro',
|
|
52
|
+
customer_email: options.email || '',
|
|
53
|
+
customer_name: options.name || '',
|
|
54
|
+
customer_phone: options.phone || ''
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
paymentData.signature = this.generateSignature(paymentData);
|
|
58
|
+
|
|
59
|
+
const response = await this.client.post('/api/v1/payment', paymentData);
|
|
60
|
+
|
|
61
|
+
if (response.data.success) {
|
|
62
|
+
return {
|
|
63
|
+
status: 'success',
|
|
64
|
+
data: {
|
|
65
|
+
url: response.data.payment_url,
|
|
66
|
+
orderId: orderId,
|
|
67
|
+
paymentId: response.data.payment_id,
|
|
68
|
+
amount: amount / 100,
|
|
69
|
+
currency: paymentData.currency
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error(response.data.message || 'Payment creation failed');
|
|
74
|
+
}
|
|
75
|
+
} catch (error) {
|
|
76
|
+
throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async handleCallback(callbackData) {
|
|
81
|
+
try {
|
|
82
|
+
const verification = await this.verifyCallback(callbackData);
|
|
83
|
+
|
|
84
|
+
if (!verification.status) {
|
|
85
|
+
throw new Error(verification.error.message);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const data = verification.data;
|
|
89
|
+
|
|
90
|
+
// Status mapping
|
|
91
|
+
const statusMapping = {
|
|
92
|
+
'success': 'success',
|
|
93
|
+
'completed': 'success',
|
|
94
|
+
'approved': 'success',
|
|
95
|
+
'failed': 'failed',
|
|
96
|
+
'declined': 'failed',
|
|
97
|
+
'cancelled': 'failed',
|
|
98
|
+
'pending': 'pending',
|
|
99
|
+
'processing': 'pending'
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
status: statusMapping[data.status] || 'unknown',
|
|
104
|
+
orderId: data.order_id,
|
|
105
|
+
transactionId: data.payment_id || data.transaction_id,
|
|
106
|
+
amount: parseFloat(data.amount) / 100,
|
|
107
|
+
currency: data.currency,
|
|
108
|
+
paymentStatus: data.status,
|
|
109
|
+
paymentMethod: data.payment_method,
|
|
110
|
+
rrn: data.rrn,
|
|
111
|
+
approvalCode: data.approval_code
|
|
112
|
+
};
|
|
113
|
+
} catch (error) {
|
|
114
|
+
throw new Error(`Error in PayNet callback handling: ${error.message}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async verifyCallback(data) {
|
|
119
|
+
try {
|
|
120
|
+
const receivedSign = data.signature;
|
|
121
|
+
const dataToVerify = { ...data };
|
|
122
|
+
delete dataToVerify.signature;
|
|
123
|
+
|
|
124
|
+
const expectedSign = this.generateSignature(dataToVerify);
|
|
125
|
+
|
|
126
|
+
if (receivedSign !== expectedSign) {
|
|
127
|
+
return {
|
|
128
|
+
status: false,
|
|
129
|
+
error: {
|
|
130
|
+
code: 401,
|
|
131
|
+
message: 'Invalid signature'
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
status: true,
|
|
138
|
+
data: data
|
|
139
|
+
};
|
|
140
|
+
} catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
status: false,
|
|
143
|
+
error: {
|
|
144
|
+
code: 500,
|
|
145
|
+
message: error.message
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async getPaymentStatus(paymentId) {
|
|
152
|
+
try {
|
|
153
|
+
const data = {
|
|
154
|
+
merchant_id: this.merchantId,
|
|
155
|
+
payment_id: paymentId
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
data.signature = this.generateSignature(data);
|
|
159
|
+
|
|
160
|
+
const response = await this.client.post('/api/v1/payment/status', data);
|
|
161
|
+
return response.data;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
throw new Error(`Error getting payment status: ${error.response?.data?.message || error.message}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async refundPayment(paymentId, options = {}) {
|
|
168
|
+
try {
|
|
169
|
+
const data = {
|
|
170
|
+
merchant_id: this.merchantId,
|
|
171
|
+
payment_id: paymentId,
|
|
172
|
+
amount: options.amount ? parseFloat(options.amount) * 100 : undefined,
|
|
173
|
+
reason: options.reason || 'Customer request'
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
data.signature = this.generateSignature(data);
|
|
177
|
+
|
|
178
|
+
const response = await this.client.post('/api/v1/payment/refund', data);
|
|
179
|
+
return response.data;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw new Error(`Error creating refund: ${error.response?.data?.message || error.message}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = PayNetClient;
|