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.
Files changed (131) hide show
  1. package/PROVIDERS-DETAILS.md +1544 -0
  2. package/examples/example-2checkout.js +78 -0
  3. package/examples/example-bitpay.js +83 -0
  4. package/examples/example-cardcom.js +80 -0
  5. package/examples/example-cashfree.js +109 -0
  6. package/examples/example-checkout.js +85 -0
  7. package/examples/example-coingate.js +101 -0
  8. package/examples/example-coinpayments.js +89 -0
  9. package/examples/example-doku.js +27 -0
  10. package/examples/example-epay.js +64 -0
  11. package/examples/example-epoint.js +91 -0
  12. package/examples/example-freekassa.js +26 -0
  13. package/examples/example-heleket.js +139 -0
  14. package/examples/example-konnect.js +227 -0
  15. package/examples/example-midtrans.js +80 -0
  16. package/examples/example-noonpayments.js +297 -0
  17. package/examples/example-nowpayments.js +289 -0
  18. package/examples/example-omise.js +27 -0
  19. package/examples/example-paycom.js +82 -0
  20. package/{example-paydisini.js → examples/example-paydisini.js} +1 -1
  21. package/examples/example-payid19.js +87 -0
  22. package/examples/example-paykun.js +29 -0
  23. package/examples/example-payme.js +202 -0
  24. package/examples/example-paymentwall.js +201 -0
  25. package/examples/example-paynet.js +104 -0
  26. package/examples/example-paynettr.js +18 -0
  27. package/examples/example-payoneer.js +74 -0
  28. package/examples/example-payop.js +351 -0
  29. package/examples/example-paypal.js +200 -0
  30. package/examples/example-payriff.js +89 -0
  31. package/examples/example-paysend.js +81 -0
  32. package/examples/example-payspace.js +103 -0
  33. package/examples/example-payssion.js +27 -0
  34. package/examples/example-paytabs.js +28 -0
  35. package/examples/example-paytm.js +78 -0
  36. package/examples/example-payuindia.js +108 -0
  37. package/examples/example-payulatam.js +75 -0
  38. package/examples/example-phonepe.js +27 -0
  39. package/examples/example-picpay.js +27 -0
  40. package/examples/example-plisio.js +84 -0
  41. package/examples/example-portwallet.js +90 -0
  42. package/examples/example-primepayments.js +250 -0
  43. package/examples/example-razorpay.js +30 -0
  44. package/examples/example-senangpay.js +28 -0
  45. package/examples/example-shurjopay.js +94 -0
  46. package/examples/example-toyyibpay.js +80 -0
  47. package/examples/example-tripay.js +89 -0
  48. package/examples/example-unitpay.js +26 -0
  49. package/examples/example-urway.js +28 -0
  50. package/examples/example-volet.js +80 -0
  51. package/examples/example-xendit.js +28 -0
  52. package/examples/example-yallapay.js +253 -0
  53. package/examples/example-yookassa.js +27 -0
  54. package/examples/example-youcanpay.js +28 -0
  55. package/examples/example-zarinpal.js +98 -0
  56. package/{example.js → examples/example.js} +1 -1
  57. package/lib/2checkout.js +165 -0
  58. package/lib/amazonpay.js +161 -0
  59. package/lib/bitpay.js +122 -0
  60. package/lib/cardcom.js +193 -0
  61. package/lib/cashfree.js +184 -0
  62. package/lib/checkout.js +248 -0
  63. package/lib/coinbase.js +150 -0
  64. package/lib/coingate.js +137 -0
  65. package/lib/coinpayments.js +245 -0
  66. package/lib/doku.js +173 -0
  67. package/lib/epay.js +175 -0
  68. package/lib/epoint.js +162 -0
  69. package/lib/freekassa.js +128 -0
  70. package/lib/heleket.js +67 -1
  71. package/lib/instamojo.js +158 -0
  72. package/lib/konnect.js +211 -0
  73. package/lib/midtrans.js +227 -0
  74. package/lib/noonpayments.js +650 -0
  75. package/lib/nowpayments.js +311 -0
  76. package/lib/omise.js +150 -0
  77. package/lib/paddle.js +180 -0
  78. package/lib/paycom.js +216 -0
  79. package/lib/payid19.js +211 -0
  80. package/lib/paykun.js +144 -0
  81. package/lib/payme.js +302 -0
  82. package/lib/paymentwall.js +205 -0
  83. package/lib/paynet.js +186 -0
  84. package/lib/paynettr.js +165 -0
  85. package/lib/payoneer.js +128 -0
  86. package/lib/payop.js +256 -0
  87. package/lib/paypal.js +542 -0
  88. package/lib/payriff.js +148 -0
  89. package/lib/paysend.js +189 -0
  90. package/lib/payspace.js +168 -0
  91. package/lib/payssion.js +177 -0
  92. package/lib/paytabs.js +145 -0
  93. package/lib/paytm.js +253 -0
  94. package/lib/payuindia.js +162 -0
  95. package/lib/payulatam.js +179 -0
  96. package/lib/perfectmoney.js +143 -0
  97. package/lib/phonepe.js +174 -0
  98. package/lib/picpay.js +119 -0
  99. package/lib/plisio.js +234 -0
  100. package/lib/portwallet.js +152 -0
  101. package/lib/primepayments.js +256 -0
  102. package/lib/razorpay.js +205 -0
  103. package/lib/senangpay.js +130 -0
  104. package/lib/shurjopay.js +159 -0
  105. package/lib/toyyibpay.js +151 -0
  106. package/lib/tripay.js +220 -0
  107. package/lib/unitpay.js +223 -0
  108. package/lib/urway.js +182 -0
  109. package/lib/volet.js +147 -0
  110. package/lib/xendit.js +206 -0
  111. package/lib/yallapay.js +279 -0
  112. package/lib/yookassa.js +193 -0
  113. package/lib/youcanpay.js +124 -0
  114. package/lib/zarinpal.js +157 -0
  115. package/package.json +138 -64
  116. package/readme.md +348 -105
  117. package/test.js +492 -0
  118. package/example-heleket.js +0 -83
  119. package/lib/vallet.js +0 -22
  120. /package/{example-anypay.js → examples/example-anypay.js} +0 -0
  121. /package/{example-bufpay.js → examples/example-bufpay.js} +0 -0
  122. /package/{example-cryptomus.js → examples/example-cryptomus.js} +0 -0
  123. /package/{example-esnekpos.js → examples/example-esnekpos.js} +0 -0
  124. /package/{example-fedapay.js → examples/example-fedapay.js} +0 -0
  125. /package/{example-iyzico.js → examples/example-iyzico.js} +0 -0
  126. /package/{example-papara.js → examples/example-papara.js} +0 -0
  127. /package/{example-payeer.js → examples/example-payeer.js} +0 -0
  128. /package/{example-paymaya.js → examples/example-paymaya.js} +0 -0
  129. /package/{example-shopier.js → examples/example-shopier.js} +0 -0
  130. /package/{ipaymu.js → examples/ipaymu.js} +0 -0
  131. /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;
@@ -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;
@@ -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;