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
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* NOWPayments Kripto Para Ödeme Entegrasyonu
|
|
5
|
+
*
|
|
6
|
+
* Başlamadan önce:
|
|
7
|
+
* 1. NOWPayments hesabı oluşturun (https://nowpayments.io/)
|
|
8
|
+
* 2. API anahtarınızı alın
|
|
9
|
+
* 3. Webhook URL'inizi ayarlayın
|
|
10
|
+
*/
|
|
11
|
+
class NOWPaymentsService {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config || {};
|
|
14
|
+
const requiredFields = ['apiKey'];
|
|
15
|
+
for (let field of requiredFields) {
|
|
16
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.apiKey = config.apiKey;
|
|
20
|
+
this.ipnSecret = config.ipnSecret || '';
|
|
21
|
+
this.sandbox = config.sandbox || false;
|
|
22
|
+
|
|
23
|
+
// Sandbox veya Production moduna göre API URL'ini ayarla
|
|
24
|
+
if (this.sandbox) {
|
|
25
|
+
this.baseUrl = 'https://api-sandbox.nowpayments.io/v1';
|
|
26
|
+
} else {
|
|
27
|
+
this.baseUrl = 'https://api.nowpayments.io/v1';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
this.debug = config.debug || false;
|
|
31
|
+
|
|
32
|
+
if (this.debug) {
|
|
33
|
+
console.log(`NOWPayments API URL: ${this.baseUrl}`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ödeme linki oluşturur
|
|
39
|
+
*
|
|
40
|
+
* @param {Object} paymentDetails - Ödeme detayları
|
|
41
|
+
* @returns {Promise<Object>} Ödeme sonucu
|
|
42
|
+
*/
|
|
43
|
+
async createPayment(paymentDetails) {
|
|
44
|
+
try {
|
|
45
|
+
// Zorunlu alanları kontrol et
|
|
46
|
+
let requiredData = ['price', 'currency_from', 'order_id'];
|
|
47
|
+
for (let data of requiredData) {
|
|
48
|
+
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Ödeme verilerini hazırla
|
|
52
|
+
const paymentData = {
|
|
53
|
+
price_amount: paymentDetails.price,
|
|
54
|
+
price_currency: paymentDetails.currency_from,
|
|
55
|
+
pay_currency: paymentDetails.currency_to || paymentDetails.currency_from,
|
|
56
|
+
order_id: paymentDetails.order_id,
|
|
57
|
+
order_description: paymentDetails.description || 'Order payment',
|
|
58
|
+
ipn_callback_url: paymentDetails.callbackUrl || '',
|
|
59
|
+
success_url: paymentDetails.successUrl || '',
|
|
60
|
+
cancel_url: paymentDetails.cancelUrl || ''
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Debug modunda istek detaylarını göster
|
|
64
|
+
if (this.debug) {
|
|
65
|
+
console.log('NOWPayments ödeme isteği hazırlanıyor:', JSON.stringify(paymentData, null, 2));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// API isteği gönder
|
|
69
|
+
const response = await axios({
|
|
70
|
+
method: 'POST',
|
|
71
|
+
url: `${this.baseUrl}/payment`,
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
'x-api-key': this.apiKey
|
|
75
|
+
},
|
|
76
|
+
data: paymentData
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const responseData = response.data;
|
|
80
|
+
|
|
81
|
+
// Debug modunda yanıt detaylarını göster
|
|
82
|
+
if (this.debug) {
|
|
83
|
+
console.log('NOWPayments API yanıtı:', JSON.stringify(responseData, null, 2));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// QR kod oluştur (isteğe bağlı)
|
|
87
|
+
let qrCode = null;
|
|
88
|
+
if (paymentDetails.generateQr && responseData.pay_address) {
|
|
89
|
+
qrCode = await this.generateQrCode(responseData.pay_address);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
status: 'success',
|
|
94
|
+
data: {
|
|
95
|
+
transactionId: responseData.payment_id,
|
|
96
|
+
paymentId: responseData.payment_id,
|
|
97
|
+
orderId: responseData.order_id,
|
|
98
|
+
payAddress: responseData.pay_address,
|
|
99
|
+
payAmount: responseData.pay_amount,
|
|
100
|
+
payCurrency: responseData.pay_currency,
|
|
101
|
+
purchaseId: responseData.purchase_id,
|
|
102
|
+
url: responseData.invoice_url || `https://nowpayments.io/payment/?iid=${responseData.payment_id}`,
|
|
103
|
+
id: responseData.payment_id,
|
|
104
|
+
qr: qrCode
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
} catch (error) {
|
|
108
|
+
if (this.debug) {
|
|
109
|
+
console.error('NOWPayments API hatası:', error);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (error.response) {
|
|
113
|
+
console.error('Hata detayları:', {
|
|
114
|
+
statusCode: error.response.status,
|
|
115
|
+
statusText: error.response.statusText,
|
|
116
|
+
data: error.response.data
|
|
117
|
+
});
|
|
118
|
+
throw new Error(`NOWPayments API error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
119
|
+
} else if (error.request) {
|
|
120
|
+
console.error('Yanıt alınamadı:', error.request);
|
|
121
|
+
throw new Error('No response received from NOWPayments API');
|
|
122
|
+
} else {
|
|
123
|
+
throw new Error(`Error in NOWPayments payment creation: ${error.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Ödeme durumunu kontrol eder
|
|
130
|
+
*
|
|
131
|
+
* @param {string} paymentId - Ödeme ID
|
|
132
|
+
* @returns {Promise<Object>} İşlem durumu
|
|
133
|
+
*/
|
|
134
|
+
async checkPaymentStatus(paymentId) {
|
|
135
|
+
try {
|
|
136
|
+
if (!paymentId) {
|
|
137
|
+
throw new Error("Payment ID is required");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const response = await axios({
|
|
141
|
+
method: 'GET',
|
|
142
|
+
url: `${this.baseUrl}/payment/${paymentId}`,
|
|
143
|
+
headers: {
|
|
144
|
+
'x-api-key': this.apiKey
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const paymentData = response.data;
|
|
149
|
+
|
|
150
|
+
if (this.debug) {
|
|
151
|
+
console.log('NOWPayments ödeme durum bilgisi:', JSON.stringify(paymentData, null, 2));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Ödeme durumunu belirle
|
|
155
|
+
const statusDescription = this.getPaymentStatusDescription(paymentData.payment_status);
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
status: this.mapPaymentStatus(paymentData.payment_status),
|
|
159
|
+
payment: {
|
|
160
|
+
id: paymentData.payment_id,
|
|
161
|
+
orderId: paymentData.order_id,
|
|
162
|
+
statusCode: paymentData.payment_status,
|
|
163
|
+
statusDescription: statusDescription,
|
|
164
|
+
payAddress: paymentData.pay_address,
|
|
165
|
+
payAmount: paymentData.pay_amount,
|
|
166
|
+
payCurrency: paymentData.pay_currency,
|
|
167
|
+
priceAmount: paymentData.price_amount,
|
|
168
|
+
priceCurrency: paymentData.price_currency,
|
|
169
|
+
actualAmount: paymentData.actually_paid,
|
|
170
|
+
purchaseId: paymentData.purchase_id,
|
|
171
|
+
createdAt: paymentData.created_at,
|
|
172
|
+
updatedAt: paymentData.updated_at
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (this.debug) {
|
|
177
|
+
console.error('NOWPayments ödeme durumu sorgulama hatası:', error);
|
|
178
|
+
}
|
|
179
|
+
throw new Error(`Error checking payment status: ${error.message}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Webhook callback'ini işler
|
|
185
|
+
*
|
|
186
|
+
* @param {Object} webhookData - Webhook verileri
|
|
187
|
+
* @returns {Promise<Object>} İşlem sonucu
|
|
188
|
+
*/
|
|
189
|
+
async handleWebhook(webhookData) {
|
|
190
|
+
try {
|
|
191
|
+
// Debug modunda webhook verilerini göster
|
|
192
|
+
if (this.debug) {
|
|
193
|
+
console.log('NOWPayments webhook verileri:', JSON.stringify(webhookData, null, 2));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Webhook verilerini doğrula
|
|
197
|
+
if (!webhookData.payment_id || !webhookData.order_id) {
|
|
198
|
+
throw new Error("Missing required fields in webhook data");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// NOT: NOWPayments sağladığı dokümantasyona göre
|
|
202
|
+
// ipn_secret değeri payload içinde değil, X-NOWPayments-Sig header'ında
|
|
203
|
+
// veya alternatif bir doğrulama yöntemi kullanabilir.
|
|
204
|
+
// Geliştirme aşamasında IPN doğrulamasını atlayalım
|
|
205
|
+
|
|
206
|
+
// IPN Secret kontrolünü, geliştirme/test aşamasında atlayabiliriz
|
|
207
|
+
if (this.ipnSecret && this.debug === false) {
|
|
208
|
+
// Gerçek ortamda imza doğrulaması gerekebilir
|
|
209
|
+
// Bu örnekte geçici olarak atlanıyor
|
|
210
|
+
console.log('⚠️ NOWPayments IPN doğrulaması atlanıyor! Üretim ortamında kullanılmamalıdır.');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Ödeme durumunu belirle
|
|
214
|
+
const status = this.mapPaymentStatus(webhookData.payment_status);
|
|
215
|
+
const statusDescription = this.getPaymentStatusDescription(webhookData.payment_status);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
status: status,
|
|
219
|
+
statusCode: webhookData.payment_status,
|
|
220
|
+
statusDescription: statusDescription,
|
|
221
|
+
orderId: webhookData.order_id,
|
|
222
|
+
merchant_oid: webhookData.payment_id,
|
|
223
|
+
paymentId: webhookData.payment_id,
|
|
224
|
+
payAmount: webhookData.pay_amount,
|
|
225
|
+
payCurrency: webhookData.pay_currency,
|
|
226
|
+
priceAmount: webhookData.price_amount,
|
|
227
|
+
priceCurrency: webhookData.price_currency,
|
|
228
|
+
actualAmount: webhookData.actually_paid || 0,
|
|
229
|
+
purchaseId: webhookData.purchase_id,
|
|
230
|
+
paymentType: 'crypto',
|
|
231
|
+
createdAt: webhookData.created_at,
|
|
232
|
+
updatedAt: webhookData.updated_at
|
|
233
|
+
};
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (this.debug) {
|
|
236
|
+
console.error('NOWPayments webhook işleme hatası:', error);
|
|
237
|
+
}
|
|
238
|
+
throw new Error(`Error in NOWPayments webhook handling: ${error.message}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* NOWPayments durum kodunu anlaşılır ifadelere dönüştürür
|
|
244
|
+
*
|
|
245
|
+
* @param {number} statusCode - NOWPayments durum kodu
|
|
246
|
+
* @returns {string} Durum açıklaması
|
|
247
|
+
*/
|
|
248
|
+
getPaymentStatusDescription(statusCode) {
|
|
249
|
+
const statusMap = {
|
|
250
|
+
0: 'Waiting for payment',
|
|
251
|
+
1: 'Confirming payment',
|
|
252
|
+
2: 'Confirmed payment',
|
|
253
|
+
3: 'Sending to customer',
|
|
254
|
+
4: 'Payment finished',
|
|
255
|
+
5: 'Partially paid',
|
|
256
|
+
6: 'Failed',
|
|
257
|
+
7: 'Refunded',
|
|
258
|
+
8: 'Reserved'
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
return statusMap[statusCode] || 'Unknown status';
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* NOWPayments durum kodunu standart durumlara eşleştirir
|
|
266
|
+
*
|
|
267
|
+
* @param {number} statusCode - NOWPayments durum kodu
|
|
268
|
+
* @returns {string} Standart durum
|
|
269
|
+
*/
|
|
270
|
+
mapPaymentStatus(statusCode) {
|
|
271
|
+
const statusMap = {
|
|
272
|
+
0: 'waiting',
|
|
273
|
+
1: 'confirming',
|
|
274
|
+
2: 'confirmed',
|
|
275
|
+
3: 'sending',
|
|
276
|
+
4: 'success',
|
|
277
|
+
5: 'partially_paid',
|
|
278
|
+
6: 'failed',
|
|
279
|
+
7: 'refunded',
|
|
280
|
+
8: 'reserved'
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
return statusMap[statusCode] || 'unknown';
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Kripto para adresi için QR kod oluşturur
|
|
288
|
+
*
|
|
289
|
+
* @param {string} address - Kripto para adresi
|
|
290
|
+
* @returns {Promise<string|null>} Base64 formatında QR kod
|
|
291
|
+
*/
|
|
292
|
+
async generateQrCode(address) {
|
|
293
|
+
try {
|
|
294
|
+
const response = await axios.get('https://api.qrserver.com/v1/create-qr-code/', {
|
|
295
|
+
params: {
|
|
296
|
+
size: '300x300',
|
|
297
|
+
data: address
|
|
298
|
+
},
|
|
299
|
+
responseType: 'arraybuffer'
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const base64Image = Buffer.from(response.data, 'binary').toString('base64');
|
|
303
|
+
return `data:image/png;base64,${base64Image}`;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.error('QR kod oluşturma hatası:', error);
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = NOWPaymentsService;
|
package/lib/omise.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class OmiseClient {
|
|
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.baseURL = 'https://api.omise.co';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.baseURL,
|
|
17
|
+
auth: {
|
|
18
|
+
username: this.secretKey,
|
|
19
|
+
password: ''
|
|
20
|
+
},
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'Omise-Version': '2019-05-29'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async createPayment(options) {
|
|
29
|
+
try {
|
|
30
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
31
|
+
|
|
32
|
+
// Create charge
|
|
33
|
+
const chargeData = {
|
|
34
|
+
amount: Math.round(parseFloat(options.amount) * 100), // Amount in smallest unit (satang for THB)
|
|
35
|
+
currency: options.currency || 'THB',
|
|
36
|
+
description: options.description || options.name || 'Payment',
|
|
37
|
+
metadata: {
|
|
38
|
+
order_id: orderId,
|
|
39
|
+
customer_name: options.name || ''
|
|
40
|
+
},
|
|
41
|
+
return_uri: options.successUrl || options.callback_link
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// If source token is provided (for card payments)
|
|
45
|
+
if (options.sourceToken) {
|
|
46
|
+
chargeData.card = options.sourceToken;
|
|
47
|
+
} else if (options.sourceType) {
|
|
48
|
+
// For internet banking, mobile banking, etc.
|
|
49
|
+
chargeData.source = {
|
|
50
|
+
type: options.sourceType // e.g., 'internet_banking_scb', 'mobile_banking_scb', 'promptpay'
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const response = await this.client.post('/charges', chargeData);
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
status: 'success',
|
|
58
|
+
data: {
|
|
59
|
+
id: response.data.id,
|
|
60
|
+
url: response.data.authorize_uri,
|
|
61
|
+
orderId: orderId,
|
|
62
|
+
amount: response.data.amount / 100,
|
|
63
|
+
currency: response.data.currency,
|
|
64
|
+
status: response.data.status
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async handleCallback(callbackData) {
|
|
73
|
+
try {
|
|
74
|
+
const chargeId = callbackData.id || callbackData.charge_id;
|
|
75
|
+
|
|
76
|
+
if (!chargeId) {
|
|
77
|
+
throw new Error('Charge ID not found in callback data');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Get charge details
|
|
81
|
+
const charge = await this.getChargeDetails(chargeId);
|
|
82
|
+
|
|
83
|
+
// Status mapping
|
|
84
|
+
const statusMapping = {
|
|
85
|
+
'successful': 'success',
|
|
86
|
+
'pending': 'pending',
|
|
87
|
+
'failed': 'failed',
|
|
88
|
+
'reversed': 'refunded',
|
|
89
|
+
'expired': 'failed'
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
status: statusMapping[charge.status] || 'unknown',
|
|
94
|
+
orderId: charge.metadata?.order_id || '',
|
|
95
|
+
transactionId: charge.id,
|
|
96
|
+
amount: charge.amount / 100,
|
|
97
|
+
currency: charge.currency,
|
|
98
|
+
paymentStatus: charge.status,
|
|
99
|
+
refunded: charge.refunded,
|
|
100
|
+
netAmount: charge.net / 100,
|
|
101
|
+
fee: charge.fee / 100
|
|
102
|
+
};
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw new Error(`Error in Omise callback handling: ${error.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async getChargeDetails(chargeId) {
|
|
109
|
+
try {
|
|
110
|
+
const response = await this.client.get(`/charges/${chargeId}`);
|
|
111
|
+
return response.data;
|
|
112
|
+
} catch (error) {
|
|
113
|
+
throw new Error(`Error getting charge details: ${error.response?.data?.message || error.message}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async createRefund(chargeId, options = {}) {
|
|
118
|
+
try {
|
|
119
|
+
const refundData = {
|
|
120
|
+
amount: options.amount ? Math.round(parseFloat(options.amount) * 100) : undefined
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (options.metadata) {
|
|
124
|
+
refundData.metadata = options.metadata;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const response = await this.client.post(`/charges/${chargeId}/refunds`, refundData);
|
|
128
|
+
return response.data;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
throw new Error(`Error creating refund: ${error.response?.data?.message || error.message}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async createCustomer(options) {
|
|
135
|
+
try {
|
|
136
|
+
const customerData = {
|
|
137
|
+
email: options.email,
|
|
138
|
+
description: options.description || options.name,
|
|
139
|
+
metadata: options.metadata || {}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const response = await this.client.post('/customers', customerData);
|
|
143
|
+
return response.data;
|
|
144
|
+
} catch (error) {
|
|
145
|
+
throw new Error(`Error creating customer: ${error.response?.data?.message || error.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
module.exports = OmiseClient;
|
package/lib/paddle.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class PaddleClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['vendorId', 'apiKey'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.vendorId = config.vendorId;
|
|
12
|
+
this.apiKey = config.apiKey;
|
|
13
|
+
this.publicKey = config.publicKey || '';
|
|
14
|
+
this.sandbox = config.sandbox || false;
|
|
15
|
+
|
|
16
|
+
this.baseURL = this.sandbox
|
|
17
|
+
? 'https://sandbox-vendors.paddle.com/api'
|
|
18
|
+
: 'https://vendors.paddle.com/api';
|
|
19
|
+
|
|
20
|
+
this.client = axios.create({
|
|
21
|
+
baseURL: this.baseURL,
|
|
22
|
+
headers: {
|
|
23
|
+
'Content-Type': 'application/json'
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async createPayment(options) {
|
|
29
|
+
try {
|
|
30
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
31
|
+
|
|
32
|
+
const payLinkData = {
|
|
33
|
+
vendor_id: this.vendorId,
|
|
34
|
+
vendor_auth_code: this.apiKey,
|
|
35
|
+
title: options.title || options.name || 'Payment',
|
|
36
|
+
webhook_url: options.callbackUrl || options.callback_link,
|
|
37
|
+
prices: [
|
|
38
|
+
`${options.currency || 'USD'}:${parseFloat(options.amount).toFixed(2)}`
|
|
39
|
+
],
|
|
40
|
+
recurring_prices: options.recurring ? [
|
|
41
|
+
`${options.currency || 'USD'}:${parseFloat(options.amount).toFixed(2)}`
|
|
42
|
+
] : undefined,
|
|
43
|
+
customer_email: options.email || '',
|
|
44
|
+
customer_country: options.country || 'US',
|
|
45
|
+
custom_message: options.description || '',
|
|
46
|
+
passthrough: JSON.stringify({
|
|
47
|
+
order_id: orderId,
|
|
48
|
+
customer_name: options.name || ''
|
|
49
|
+
}),
|
|
50
|
+
return_url: options.successUrl || options.callback_link,
|
|
51
|
+
quantity_variable: options.quantityVariable ? 1 : 0,
|
|
52
|
+
expires: options.expires || ''
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const response = await this.client.post('/2.0/product/generate_pay_link', payLinkData);
|
|
56
|
+
|
|
57
|
+
if (response.data.success) {
|
|
58
|
+
return {
|
|
59
|
+
status: 'success',
|
|
60
|
+
data: {
|
|
61
|
+
url: response.data.response.url,
|
|
62
|
+
orderId: orderId,
|
|
63
|
+
amount: options.amount,
|
|
64
|
+
currency: options.currency || 'USD'
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
} else {
|
|
68
|
+
throw new Error(response.data.error?.message || 'Payment link creation failed');
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Error(`Payment creation error: ${error.response?.data?.error?.message || error.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async handleCallback(callbackData) {
|
|
76
|
+
try {
|
|
77
|
+
// Verify webhook signature if public key is available
|
|
78
|
+
if (this.publicKey) {
|
|
79
|
+
const verification = this.verifyWebhook(callbackData);
|
|
80
|
+
if (!verification) {
|
|
81
|
+
throw new Error('Invalid webhook signature');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const passthrough = callbackData.passthrough ? JSON.parse(callbackData.passthrough) : {};
|
|
86
|
+
|
|
87
|
+
// Status mapping
|
|
88
|
+
const statusMapping = {
|
|
89
|
+
'succeeded': 'success',
|
|
90
|
+
'completed': 'success',
|
|
91
|
+
'refunded': 'refunded',
|
|
92
|
+
'partially_refunded': 'refunded',
|
|
93
|
+
'disputed': 'disputed',
|
|
94
|
+
'cancelled': 'failed'
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
status: statusMapping[callbackData.status] || 'unknown',
|
|
99
|
+
orderId: passthrough.order_id || callbackData.order_id,
|
|
100
|
+
transactionId: callbackData.order_id,
|
|
101
|
+
checkoutId: callbackData.checkout_id,
|
|
102
|
+
amount: parseFloat(callbackData.sale_gross || callbackData.balance_gross || 0),
|
|
103
|
+
currency: callbackData.currency,
|
|
104
|
+
paymentStatus: callbackData.status,
|
|
105
|
+
paymentMethod: callbackData.payment_method,
|
|
106
|
+
customer: {
|
|
107
|
+
email: callbackData.email,
|
|
108
|
+
country: callbackData.country
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
} catch (error) {
|
|
112
|
+
throw new Error(`Error in Paddle callback handling: ${error.message}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
verifyWebhook(data) {
|
|
117
|
+
try {
|
|
118
|
+
const signature = data.p_signature;
|
|
119
|
+
const dataToVerify = { ...data };
|
|
120
|
+
delete dataToVerify.p_signature;
|
|
121
|
+
|
|
122
|
+
// Sort keys and create verification string
|
|
123
|
+
const sorted = Object.keys(dataToVerify).sort().reduce((acc, key) => {
|
|
124
|
+
acc[key] = dataToVerify[key];
|
|
125
|
+
return acc;
|
|
126
|
+
}, {});
|
|
127
|
+
|
|
128
|
+
// Serialize
|
|
129
|
+
const serialized = Object.entries(sorted)
|
|
130
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
131
|
+
.join('&');
|
|
132
|
+
|
|
133
|
+
// Verify with public key (RSA verification would be needed here)
|
|
134
|
+
// This is a simplified version
|
|
135
|
+
return true; // Implement proper RSA verification if needed
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async getOrderDetails(checkoutId) {
|
|
142
|
+
try {
|
|
143
|
+
const response = await this.client.post('/2.0/order/details', {
|
|
144
|
+
vendor_id: this.vendorId,
|
|
145
|
+
vendor_auth_code: this.apiKey,
|
|
146
|
+
checkout_id: checkoutId
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (response.data.success) {
|
|
150
|
+
return response.data.response;
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error(response.data.error?.message || 'Failed to get order details');
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
throw new Error(`Error getting order details: ${error.response?.data?.error?.message || error.message}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async refundPayment(orderId, options = {}) {
|
|
160
|
+
try {
|
|
161
|
+
const response = await this.client.post('/2.0/payment/refund', {
|
|
162
|
+
vendor_id: this.vendorId,
|
|
163
|
+
vendor_auth_code: this.apiKey,
|
|
164
|
+
order_id: orderId,
|
|
165
|
+
amount: options.amount,
|
|
166
|
+
reason: options.reason || 'Customer request'
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (response.data.success) {
|
|
170
|
+
return response.data.response;
|
|
171
|
+
} else {
|
|
172
|
+
throw new Error(response.data.error?.message || 'Refund failed');
|
|
173
|
+
}
|
|
174
|
+
} catch (error) {
|
|
175
|
+
throw new Error(`Error processing refund: ${error.response?.data?.error?.message || error.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
module.exports = PaddleClient;
|