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/xendit.js
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class XenditClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['apiKey'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.apiKey = config.apiKey;
|
|
12
|
+
this.webhookToken = config.webhookToken;
|
|
13
|
+
this.baseURL = 'https://api.xendit.co';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.baseURL,
|
|
17
|
+
auth: {
|
|
18
|
+
username: this.apiKey,
|
|
19
|
+
password: ''
|
|
20
|
+
},
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json'
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async createPayment(options) {
|
|
28
|
+
try {
|
|
29
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
30
|
+
|
|
31
|
+
// Xendit supports multiple payment methods
|
|
32
|
+
const paymentMethod = options.paymentMethod || 'invoice'; // invoice, ewallet, va, qr, card
|
|
33
|
+
|
|
34
|
+
let response;
|
|
35
|
+
|
|
36
|
+
if (paymentMethod === 'invoice') {
|
|
37
|
+
response = await this.createInvoice(options, orderId);
|
|
38
|
+
} else if (paymentMethod === 'ewallet') {
|
|
39
|
+
response = await this.createEWallet(options, orderId);
|
|
40
|
+
} else if (paymentMethod === 'va') {
|
|
41
|
+
response = await this.createVirtualAccount(options, orderId);
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error(`Unsupported payment method: ${paymentMethod}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return response;
|
|
47
|
+
} catch (error) {
|
|
48
|
+
throw new Error(`Payment creation error: ${error.response?.data?.message || error.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async createInvoice(options, orderId) {
|
|
53
|
+
const invoiceData = {
|
|
54
|
+
external_id: orderId,
|
|
55
|
+
amount: parseFloat(options.amount),
|
|
56
|
+
payer_email: options.email || '',
|
|
57
|
+
description: options.description || options.name || 'Payment',
|
|
58
|
+
currency: options.currency || 'IDR',
|
|
59
|
+
success_redirect_url: options.successUrl || options.callback_link,
|
|
60
|
+
failure_redirect_url: options.failureUrl || options.callback_link,
|
|
61
|
+
customer: {
|
|
62
|
+
given_names: options.name || '',
|
|
63
|
+
email: options.email || '',
|
|
64
|
+
mobile_number: options.phone || ''
|
|
65
|
+
},
|
|
66
|
+
items: options.items || [{
|
|
67
|
+
name: options.name || 'Payment',
|
|
68
|
+
quantity: 1,
|
|
69
|
+
price: parseFloat(options.amount)
|
|
70
|
+
}]
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const response = await this.client.post('/v2/invoices', invoiceData);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
status: 'success',
|
|
77
|
+
data: {
|
|
78
|
+
id: response.data.id,
|
|
79
|
+
url: response.data.invoice_url,
|
|
80
|
+
orderId: orderId,
|
|
81
|
+
amount: response.data.amount,
|
|
82
|
+
currency: response.data.currency,
|
|
83
|
+
status: response.data.status
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async createEWallet(options, orderId) {
|
|
89
|
+
const ewalletData = {
|
|
90
|
+
reference_id: orderId,
|
|
91
|
+
currency: options.currency || 'IDR',
|
|
92
|
+
amount: parseFloat(options.amount),
|
|
93
|
+
checkout_method: 'ONE_TIME_PAYMENT',
|
|
94
|
+
channel_code: options.ewalletType || 'ID_OVO', // ID_OVO, ID_DANA, ID_LINKAJA, PH_GCASH, etc.
|
|
95
|
+
channel_properties: {
|
|
96
|
+
success_redirect_url: options.successUrl || options.callback_link,
|
|
97
|
+
failure_redirect_url: options.failureUrl || options.callback_link,
|
|
98
|
+
mobile_number: options.phone || ''
|
|
99
|
+
},
|
|
100
|
+
metadata: {
|
|
101
|
+
order_id: orderId
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const response = await this.client.post('/ewallets/charges', ewalletData);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
status: 'success',
|
|
109
|
+
data: {
|
|
110
|
+
id: response.data.id,
|
|
111
|
+
url: response.data.actions?.desktop_web_checkout_url || response.data.actions?.mobile_web_checkout_url,
|
|
112
|
+
orderId: orderId,
|
|
113
|
+
amount: response.data.charge_amount,
|
|
114
|
+
currency: response.data.currency,
|
|
115
|
+
status: response.data.status
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async createVirtualAccount(options, orderId) {
|
|
121
|
+
const vaData = {
|
|
122
|
+
external_id: orderId,
|
|
123
|
+
bank_code: options.bankCode || 'BNI', // BNI, BRI, MANDIRI, PERMATA, etc.
|
|
124
|
+
name: options.name || 'Customer',
|
|
125
|
+
expected_amount: parseFloat(options.amount),
|
|
126
|
+
is_closed: true,
|
|
127
|
+
expiration_date: options.expirationDate || new Date(Date.now() + 24*60*60*1000).toISOString()
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const response = await this.client.post('/callback_virtual_accounts', vaData);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
status: 'success',
|
|
134
|
+
data: {
|
|
135
|
+
id: response.data.id,
|
|
136
|
+
accountNumber: response.data.account_number,
|
|
137
|
+
bankCode: response.data.bank_code,
|
|
138
|
+
orderId: orderId,
|
|
139
|
+
amount: response.data.expected_amount,
|
|
140
|
+
currency: 'IDR'
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async handleCallback(callbackData) {
|
|
146
|
+
try {
|
|
147
|
+
// Verify webhook token if provided
|
|
148
|
+
if (this.webhookToken && callbackData.callback_token !== this.webhookToken) {
|
|
149
|
+
throw new Error('Invalid webhook token');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Different callback structures for different payment methods
|
|
153
|
+
let orderId, transactionId, amount, currency, paymentStatus;
|
|
154
|
+
|
|
155
|
+
if (callbackData.external_id) {
|
|
156
|
+
// Invoice or VA callback
|
|
157
|
+
orderId = callbackData.external_id;
|
|
158
|
+
transactionId = callbackData.id;
|
|
159
|
+
amount = parseFloat(callbackData.amount || callbackData.paid_amount);
|
|
160
|
+
currency = callbackData.currency || 'IDR';
|
|
161
|
+
paymentStatus = callbackData.status;
|
|
162
|
+
} else if (callbackData.reference_id) {
|
|
163
|
+
// E-wallet callback
|
|
164
|
+
orderId = callbackData.data?.reference_id || callbackData.reference_id;
|
|
165
|
+
transactionId = callbackData.id || callbackData.data?.id;
|
|
166
|
+
amount = parseFloat(callbackData.data?.charge_amount || callbackData.charge_amount);
|
|
167
|
+
currency = callbackData.data?.currency || callbackData.currency || 'IDR';
|
|
168
|
+
paymentStatus = callbackData.data?.status || callbackData.status;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Status mapping
|
|
172
|
+
const statusMapping = {
|
|
173
|
+
'PAID': 'success',
|
|
174
|
+
'SETTLED': 'success',
|
|
175
|
+
'SUCCEEDED': 'success',
|
|
176
|
+
'PENDING': 'pending',
|
|
177
|
+
'ACTIVE': 'pending',
|
|
178
|
+
'EXPIRED': 'failed',
|
|
179
|
+
'FAILED': 'failed',
|
|
180
|
+
'VOIDED': 'failed'
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
status: statusMapping[paymentStatus] || 'unknown',
|
|
185
|
+
orderId: orderId,
|
|
186
|
+
transactionId: transactionId,
|
|
187
|
+
amount: amount,
|
|
188
|
+
currency: currency,
|
|
189
|
+
paymentStatus: paymentStatus
|
|
190
|
+
};
|
|
191
|
+
} catch (error) {
|
|
192
|
+
throw new Error(`Error in Xendit callback handling: ${error.message}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async getInvoice(invoiceId) {
|
|
197
|
+
try {
|
|
198
|
+
const response = await this.client.get(`/v2/invoices/${invoiceId}`);
|
|
199
|
+
return response.data;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
throw new Error(`Error getting invoice: ${error.response?.data?.message || error.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
module.exports = XenditClient;
|
package/lib/yallapay.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* YallaPay Ödeme Entegrasyonu
|
|
5
|
+
*
|
|
6
|
+
* Başlamadan önce:
|
|
7
|
+
* 1. YallaPay hesabı oluşturun
|
|
8
|
+
* 2. Merchant Dashboard'dan private key alın
|
|
9
|
+
* 3. Webhook URL'inizi ayarlayın
|
|
10
|
+
*/
|
|
11
|
+
class YallaPayService {
|
|
12
|
+
constructor(config) {
|
|
13
|
+
this.config = config || {};
|
|
14
|
+
const requiredFields = ['privateKey', 'currency'];
|
|
15
|
+
for (let field of requiredFields) {
|
|
16
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.privateKey = config.privateKey;
|
|
20
|
+
this.currency = config.currency;
|
|
21
|
+
this.baseUrl = config.baseUrl || 'https://yallapay.net/api';
|
|
22
|
+
this.webhookSecret = config.webhookSecret || '';
|
|
23
|
+
this.debug = config.debug || false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Ödeme bağlantısı oluşturur
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} paymentDetails - Ödeme detayları
|
|
30
|
+
* @returns {Promise<Object>} Ödeme sonucu
|
|
31
|
+
*/
|
|
32
|
+
async createPayment(paymentDetails) {
|
|
33
|
+
try {
|
|
34
|
+
// Zorunlu alanları kontrol et
|
|
35
|
+
let requiredData = ['amount', 'purpose', 'external_id'];
|
|
36
|
+
for (let data of requiredData) {
|
|
37
|
+
if (!paymentDetails[data]) throw new Error(`Missing required data: ${data}`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Form verilerini hazırla
|
|
41
|
+
const formData = new FormData();
|
|
42
|
+
formData.append('private_key', this.privateKey);
|
|
43
|
+
formData.append('currency', paymentDetails.currency || this.currency);
|
|
44
|
+
formData.append('amount', paymentDetails.amount);
|
|
45
|
+
formData.append('purpose', paymentDetails.purpose);
|
|
46
|
+
formData.append('external_id', paymentDetails.external_id);
|
|
47
|
+
|
|
48
|
+
// İsteğe bağlı parametreler
|
|
49
|
+
if (paymentDetails.is_fallback) {
|
|
50
|
+
formData.append('is_fallback', paymentDetails.is_fallback);
|
|
51
|
+
if (paymentDetails.is_fallback === '1' && paymentDetails.fallback_url) {
|
|
52
|
+
formData.append('fallback_url', paymentDetails.fallback_url);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (paymentDetails.store_id) {
|
|
57
|
+
formData.append('store_id', paymentDetails.store_id);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Debug modunda istek detaylarını göster
|
|
61
|
+
if (this.debug) {
|
|
62
|
+
console.log('YallaPay ödeme isteği hazırlanıyor:', Object.fromEntries(formData));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// API isteği gönder
|
|
66
|
+
const response = await axios({
|
|
67
|
+
method: 'POST',
|
|
68
|
+
url: `${this.baseUrl}/request`,
|
|
69
|
+
headers: {
|
|
70
|
+
'Accept': 'application/json',
|
|
71
|
+
'Content-Type': 'application/json'
|
|
72
|
+
},
|
|
73
|
+
data: Object.fromEntries(formData)
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const responseData = response.data;
|
|
77
|
+
|
|
78
|
+
// Debug modunda yanıt detaylarını göster
|
|
79
|
+
if (this.debug) {
|
|
80
|
+
console.log('YallaPay API yanıtı:', responseData);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Hata kontrolü
|
|
84
|
+
if (responseData.status !== "success") {
|
|
85
|
+
throw new Error(`YallaPay API error: ${responseData.message || 'Unknown error'}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// QR kod oluştur (isteğe bağlı)
|
|
89
|
+
let qrCode = null;
|
|
90
|
+
if (paymentDetails.generateQr && responseData.data.checkout_url) {
|
|
91
|
+
qrCode = await this.generateQrCode(responseData.data.checkout_url);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Başarılı yanıt
|
|
95
|
+
return {
|
|
96
|
+
status: 'success',
|
|
97
|
+
data: {
|
|
98
|
+
transactionId: responseData.data.transaction_id,
|
|
99
|
+
url: responseData.data.checkout_url,
|
|
100
|
+
fallbackUrl: responseData.data.fallback_url || null,
|
|
101
|
+
id: responseData.data.transaction_id,
|
|
102
|
+
qr: qrCode
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (this.debug) {
|
|
107
|
+
console.error('YallaPay API hatası:', error);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (error.response) {
|
|
111
|
+
console.error('Hata detayları:', {
|
|
112
|
+
statusCode: error.response.status,
|
|
113
|
+
statusText: error.response.statusText,
|
|
114
|
+
data: error.response.data
|
|
115
|
+
});
|
|
116
|
+
throw new Error(`YallaPay API error (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
117
|
+
} else if (error.request) {
|
|
118
|
+
console.error('Yanıt alınamadı:', error.request);
|
|
119
|
+
throw new Error('No response received from YallaPay API');
|
|
120
|
+
} else {
|
|
121
|
+
throw new Error(`Error in YallaPay payment creation: ${error.message}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* İşlem durumunu doğrular
|
|
128
|
+
*
|
|
129
|
+
* @param {string} transactionId - YallaPay işlem ID'si
|
|
130
|
+
* @returns {Promise<Object>} İşlem durumu
|
|
131
|
+
*/
|
|
132
|
+
async verifyTransaction(transactionId) {
|
|
133
|
+
try {
|
|
134
|
+
if (!transactionId) {
|
|
135
|
+
throw new Error("Transaction ID is required");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Form verilerini hazırla
|
|
139
|
+
const formData = new FormData();
|
|
140
|
+
formData.append('private_key', this.privateKey);
|
|
141
|
+
formData.append('trx_id', transactionId);
|
|
142
|
+
|
|
143
|
+
// API isteği gönder
|
|
144
|
+
const response = await axios({
|
|
145
|
+
method: 'POST',
|
|
146
|
+
url: `${this.baseUrl}/transaction/verify`,
|
|
147
|
+
headers: {
|
|
148
|
+
'Accept': 'application/json',
|
|
149
|
+
'Content-Type': 'application/json'
|
|
150
|
+
},
|
|
151
|
+
data: Object.fromEntries(formData)
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
const responseData = response.data;
|
|
155
|
+
|
|
156
|
+
// Debug modunda yanıt detaylarını göster
|
|
157
|
+
if (this.debug) {
|
|
158
|
+
console.log('YallaPay doğrulama yanıtı:', responseData);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Hata kontrolü
|
|
162
|
+
if (responseData.status !== "success") {
|
|
163
|
+
throw new Error(`YallaPay verification error: ${responseData.message || 'Unknown error'}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// İşlem durumunu belirle
|
|
167
|
+
let status = this.mapTransactionStatus(responseData.data.status);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
status: status,
|
|
171
|
+
transaction: {
|
|
172
|
+
id: responseData.data.transaction_id,
|
|
173
|
+
external_id: responseData.data.external_id,
|
|
174
|
+
amount: responseData.data.amount,
|
|
175
|
+
net_amount: responseData.data.net_amount,
|
|
176
|
+
currency: responseData.data.currency,
|
|
177
|
+
payment_method: responseData.data.payment_method,
|
|
178
|
+
payment_date: responseData.data.payment_date,
|
|
179
|
+
status_code: responseData.data.status,
|
|
180
|
+
status: status
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (this.debug) {
|
|
185
|
+
console.error('YallaPay doğrulama hatası:', error);
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`Error in YallaPay transaction verification: ${error.message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Webhook callback'ini işler
|
|
193
|
+
*
|
|
194
|
+
* @param {Object} webhookData - Webhook verileri
|
|
195
|
+
* @returns {Promise<Object>} İşlem sonucu
|
|
196
|
+
*/
|
|
197
|
+
async handleWebhook(webhookData) {
|
|
198
|
+
try {
|
|
199
|
+
// Debug modunda webhook verilerini göster
|
|
200
|
+
if (this.debug) {
|
|
201
|
+
console.log('YallaPay webhook verileri:', webhookData);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Webhook verilerini doğrula
|
|
205
|
+
if (!webhookData.transaction_id || !webhookData.webhook_secret) {
|
|
206
|
+
throw new Error("Missing required fields in webhook data");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Webhook secret'ı doğrula
|
|
210
|
+
if (this.webhookSecret && webhookData.webhook_secret !== this.webhookSecret) {
|
|
211
|
+
throw new Error("Invalid webhook secret");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// İşlem durumunu belirle
|
|
215
|
+
let status = this.mapTransactionStatus(webhookData.status);
|
|
216
|
+
|
|
217
|
+
// İşlem durumuna göre yanıt döndür
|
|
218
|
+
return {
|
|
219
|
+
status: status,
|
|
220
|
+
orderId: webhookData.external_id,
|
|
221
|
+
merchant_oid: webhookData.transaction_id,
|
|
222
|
+
amount: webhookData.amount,
|
|
223
|
+
net_amount: webhookData.net_amount,
|
|
224
|
+
currency: webhookData.currency,
|
|
225
|
+
paymentType: webhookData.payment_method,
|
|
226
|
+
payment_date: webhookData.payment_date
|
|
227
|
+
};
|
|
228
|
+
} catch (error) {
|
|
229
|
+
if (this.debug) {
|
|
230
|
+
console.error('YallaPay webhook işleme hatası:', error);
|
|
231
|
+
}
|
|
232
|
+
throw new Error(`Error in YallaPay webhook handling: ${error.message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* YallaPay durum kodunu anlaşılır ifadelere dönüştürür
|
|
238
|
+
*
|
|
239
|
+
* @param {number} statusCode - YallaPay durum kodu
|
|
240
|
+
* @returns {string} Durum açıklaması
|
|
241
|
+
*/
|
|
242
|
+
mapTransactionStatus(statusCode) {
|
|
243
|
+
const statusMap = {
|
|
244
|
+
0: 'failed',
|
|
245
|
+
1: 'success',
|
|
246
|
+
3: 'refunded',
|
|
247
|
+
4: 'disputed',
|
|
248
|
+
5: 'initiated'
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
return statusMap[statusCode] || 'unknown';
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Ödeme bağlantısı için QR kod oluşturur
|
|
256
|
+
*
|
|
257
|
+
* @param {string} paymentUrl - Ödeme URL'i
|
|
258
|
+
* @returns {Promise<string|null>} Base64 formatında QR kod
|
|
259
|
+
*/
|
|
260
|
+
async generateQrCode(paymentUrl) {
|
|
261
|
+
try {
|
|
262
|
+
const response = await axios.get('https://api.qrserver.com/v1/create-qr-code/', {
|
|
263
|
+
params: {
|
|
264
|
+
size: '300x300',
|
|
265
|
+
data: paymentUrl
|
|
266
|
+
},
|
|
267
|
+
responseType: 'arraybuffer'
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
const base64Image = Buffer.from(response.data, 'binary').toString('base64');
|
|
271
|
+
return `data:image/png;base64,${base64Image}`;
|
|
272
|
+
} catch (error) {
|
|
273
|
+
console.error('QR kod oluşturma hatası:', error);
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = YallaPayService;
|
package/lib/yookassa.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
class YooKassaClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
const requiredFields = ['shopId', 'secretKey'];
|
|
7
|
+
for (let field of requiredFields) {
|
|
8
|
+
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
this.shopId = config.shopId;
|
|
12
|
+
this.secretKey = config.secretKey;
|
|
13
|
+
this.baseURL = 'https://api.yookassa.ru/v3';
|
|
14
|
+
|
|
15
|
+
this.client = axios.create({
|
|
16
|
+
baseURL: this.baseURL,
|
|
17
|
+
auth: {
|
|
18
|
+
username: this.shopId,
|
|
19
|
+
password: this.secretKey
|
|
20
|
+
},
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
'Idempotence-Key': this.generateIdempotenceKey()
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
generateIdempotenceKey() {
|
|
29
|
+
return crypto.randomBytes(16).toString('hex');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async createPayment(options) {
|
|
33
|
+
try {
|
|
34
|
+
const orderId = options.orderId || `ORDER-${Date.now()}`;
|
|
35
|
+
|
|
36
|
+
const paymentData = {
|
|
37
|
+
amount: {
|
|
38
|
+
value: parseFloat(options.amount).toFixed(2),
|
|
39
|
+
currency: options.currency || 'RUB'
|
|
40
|
+
},
|
|
41
|
+
confirmation: {
|
|
42
|
+
type: 'redirect',
|
|
43
|
+
return_url: options.successUrl || options.callback_link
|
|
44
|
+
},
|
|
45
|
+
capture: options.autoCapture !== false,
|
|
46
|
+
description: options.description || options.name || 'Payment',
|
|
47
|
+
metadata: {
|
|
48
|
+
order_id: orderId
|
|
49
|
+
},
|
|
50
|
+
receipt: options.receipt || undefined
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// Customer info
|
|
54
|
+
if (options.email || options.phone) {
|
|
55
|
+
paymentData.receipt = {
|
|
56
|
+
customer: {
|
|
57
|
+
email: options.email,
|
|
58
|
+
phone: options.phone
|
|
59
|
+
},
|
|
60
|
+
items: options.items || [{
|
|
61
|
+
description: paymentData.description,
|
|
62
|
+
quantity: '1.00',
|
|
63
|
+
amount: paymentData.amount,
|
|
64
|
+
vat_code: options.vatCode || 1
|
|
65
|
+
}]
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const response = await this.client.post('/payments', paymentData, {
|
|
70
|
+
headers: {
|
|
71
|
+
'Idempotence-Key': this.generateIdempotenceKey()
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
status: 'success',
|
|
77
|
+
data: {
|
|
78
|
+
id: response.data.id,
|
|
79
|
+
url: response.data.confirmation.confirmation_url,
|
|
80
|
+
orderId: orderId,
|
|
81
|
+
amount: response.data.amount.value,
|
|
82
|
+
currency: response.data.amount.currency,
|
|
83
|
+
status: response.data.status
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw new Error(`Payment creation error: ${error.response?.data?.description || error.message}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async handleCallback(callbackData) {
|
|
92
|
+
try {
|
|
93
|
+
const payment = callbackData.object;
|
|
94
|
+
|
|
95
|
+
if (!payment || !payment.id) {
|
|
96
|
+
throw new Error('Invalid callback data');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Verify payment
|
|
100
|
+
const verifiedPayment = await this.getPaymentDetails(payment.id);
|
|
101
|
+
|
|
102
|
+
// Status mapping
|
|
103
|
+
const statusMapping = {
|
|
104
|
+
'succeeded': 'success',
|
|
105
|
+
'pending': 'pending',
|
|
106
|
+
'waiting_for_capture': 'pending',
|
|
107
|
+
'canceled': 'failed'
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
status: statusMapping[verifiedPayment.status] || 'unknown',
|
|
112
|
+
orderId: verifiedPayment.metadata?.order_id || '',
|
|
113
|
+
transactionId: verifiedPayment.id,
|
|
114
|
+
amount: parseFloat(verifiedPayment.amount.value),
|
|
115
|
+
currency: verifiedPayment.amount.currency,
|
|
116
|
+
paymentStatus: verifiedPayment.status,
|
|
117
|
+
paymentMethod: verifiedPayment.payment_method?.type,
|
|
118
|
+
paid: verifiedPayment.paid,
|
|
119
|
+
refundable: verifiedPayment.refundable
|
|
120
|
+
};
|
|
121
|
+
} catch (error) {
|
|
122
|
+
throw new Error(`Error in YooKassa callback handling: ${error.message}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async getPaymentDetails(paymentId) {
|
|
127
|
+
try {
|
|
128
|
+
const response = await this.client.get(`/payments/${paymentId}`);
|
|
129
|
+
return response.data;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
throw new Error(`Error getting payment details: ${error.response?.data?.description || error.message}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async capturePayment(paymentId, options = {}) {
|
|
136
|
+
try {
|
|
137
|
+
const captureData = {
|
|
138
|
+
amount: options.amount ? {
|
|
139
|
+
value: parseFloat(options.amount).toFixed(2),
|
|
140
|
+
currency: options.currency || 'RUB'
|
|
141
|
+
} : undefined
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const response = await this.client.post(`/payments/${paymentId}/capture`, captureData, {
|
|
145
|
+
headers: {
|
|
146
|
+
'Idempotence-Key': this.generateIdempotenceKey()
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return response.data;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
throw new Error(`Error capturing payment: ${error.response?.data?.description || error.message}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async cancelPayment(paymentId) {
|
|
157
|
+
try {
|
|
158
|
+
const response = await this.client.post(`/payments/${paymentId}/cancel`, {}, {
|
|
159
|
+
headers: {
|
|
160
|
+
'Idempotence-Key': this.generateIdempotenceKey()
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return response.data;
|
|
165
|
+
} catch (error) {
|
|
166
|
+
throw new Error(`Error canceling payment: ${error.response?.data?.description || error.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async createRefund(paymentId, options = {}) {
|
|
171
|
+
try {
|
|
172
|
+
const refundData = {
|
|
173
|
+
payment_id: paymentId,
|
|
174
|
+
amount: {
|
|
175
|
+
value: parseFloat(options.amount).toFixed(2),
|
|
176
|
+
currency: options.currency || 'RUB'
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const response = await this.client.post('/refunds', refundData, {
|
|
181
|
+
headers: {
|
|
182
|
+
'Idempotence-Key': this.generateIdempotenceKey()
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
return response.data;
|
|
187
|
+
} catch (error) {
|
|
188
|
+
throw new Error(`Error creating refund: ${error.response?.data?.description || error.message}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = YooKassaClient;
|