quickpos 1.0.916 → 1.0.917

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.
@@ -0,0 +1,193 @@
1
+ const crypto = require('crypto');
2
+ const axios = require('axios');
3
+
4
+ class Paynkolay {
5
+ /**
6
+ * @param {Object} config
7
+ * @param {string} config.sx - SX parametresi (Panelden alınır)
8
+ * @param {string} config.merchantSecretKey - Gizli Anahtar
9
+ * @param {string} config.merchantNo - Mağaza Numarası (Callback doğrulama için)
10
+ * @param {boolean} [config.isTest=true] - Test ortamı mı?
11
+ */
12
+ constructor(config) {
13
+ this.sx = config.sx;
14
+ this.merchantSecretKey = config.merchantSecretKey;
15
+ this.merchantNo = config.merchantNo;
16
+
17
+ // Postman'deki host bilgisine göre
18
+ this.baseUrl = config.isTest
19
+ ? 'https://paynkolaytest.nkolayislem.com.tr'
20
+ : 'https://paynkolay.nkolayislem.com.tr';
21
+ }
22
+
23
+ /**
24
+ * Link ile Ödeme Oluşturma (/Vpos/by-link-create)
25
+ * Postman'deki 'Link Oluştur' script'ine göre hazırlanmıştır.
26
+ */
27
+ async createPaymentLink(params) {
28
+ try {
29
+ const endpoint = '/Vpos/by-link-create';
30
+
31
+ // Zorunlu alanlar için varsayılan değerler ve kontroller
32
+ const rnd = params.rnd || Date.now().toString();
33
+ const clientRefCode = params.clientRefCode || ('REF-' + rnd);
34
+ const customerKey = params.customerKey || ""; // Kart saklama yoksa boş
35
+ const amount = parseFloat(params.amount).toFixed(2); // 1000.00 formatı
36
+
37
+ // 1. HASH OLUŞTURMA (Postman Script'ine göre)
38
+ // Sıralama: sx|clientRefCode|amount|successUrl|failUrl|rnd|customerKey|merchantSecretKey
39
+ const hashString = [
40
+ this.sx,
41
+ clientRefCode,
42
+ amount,
43
+ params.successUrl,
44
+ params.failUrl,
45
+ rnd,
46
+ customerKey,
47
+ this.merchantSecretKey
48
+ ].join("|");
49
+
50
+ const hashDatav2 = this.generateHash(hashString);
51
+
52
+ // 2. FORM DATA HAZIRLAMA
53
+ const data = new URLSearchParams();
54
+ data.append('sx', this.sx);
55
+ data.append('clientRefCode', clientRefCode);
56
+ data.append('amount', amount);
57
+ data.append('successUrl', params.successUrl);
58
+ data.append('failUrl', params.failUrl);
59
+ data.append('rnd', rnd);
60
+ data.append('hashDatav2', hashDatav2);
61
+ data.append('use3D', params.use3D || 'true');
62
+ data.append('currencyCode', params.currencyCode || '949'); // 949 = TL
63
+ data.append('transactionType', 'SALES');
64
+
65
+ // Opsiyonel (Link Detayları)
66
+ if (params.instalments) data.append('instalments', params.instalments);
67
+ if (params.second) data.append('second', params.second); // Link süresi (saniye)
68
+ if (params.cardHolderIP) data.append('cardHolderIP', params.cardHolderIP);
69
+
70
+ // Link Sayfası Görünen Bilgiler
71
+ data.append('detail', 'true');
72
+ data.append('inputNamesurname', params.inputNamesurname || '');
73
+ data.append('inputDescription', params.inputDescription || '');
74
+ data.append('inputEmail', params.inputEmail || '');
75
+ data.append('inputAddress', params.inputAddress || '');
76
+ data.append('inputTckn', params.inputTckn || '');
77
+ data.append('inputPhone', params.inputPhone || '');
78
+
79
+ // 3. İSTEK GÖNDERME
80
+ const response = await axios.post(`${this.baseUrl}${endpoint}`, data, {
81
+ headers: {
82
+ 'Content-Type': 'application/x-www-form-urlencoded'
83
+ }
84
+ });
85
+
86
+ return response.data;
87
+
88
+ } catch (error) {
89
+ throw new Error(`Paynkolay Link Oluşturma Hatası: ${error.message}`);
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Link Silme (/Vpos/by-link-url-remove)
95
+ */
96
+ async removePaymentLink(qParam) {
97
+ try {
98
+ const endpoint = '/Vpos/by-link-url-remove';
99
+
100
+ // HASH OLUŞTURMA (Link Sil Script'ine göre)
101
+ // Sıralama: sx|q|merchantSecretKey
102
+ const hashString = [
103
+ this.sx,
104
+ qParam, // Bu değer genelde referans kodudur
105
+ this.merchantSecretKey
106
+ ].join("|");
107
+
108
+ const hashDatav2 = this.generateHash(hashString);
109
+
110
+ const data = new URLSearchParams();
111
+ data.append('sx', this.sx);
112
+ data.append('q', qParam);
113
+ data.append('hashDatav2', hashDatav2);
114
+
115
+ const response = await axios.post(`${this.baseUrl}${endpoint}`, data, {
116
+ headers: {
117
+ 'Content-Type': 'application/x-www-form-urlencoded'
118
+ }
119
+ });
120
+
121
+ return response.data;
122
+
123
+ } catch (error) {
124
+ throw new Error(`Paynkolay Link Silme Hatası: ${error.message}`);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Callback (Bildirim) Doğrulama
130
+ * Ödeme sonrası Paynkolay'ın successUrl'e gönderdiği veriyi doğrular.
131
+ */
132
+ handleCallback(params) {
133
+ const {
134
+ RESPONSE_CODE,
135
+ MERCHANT_NO,
136
+ REFERENCE_CODE,
137
+ AUTH_CODE,
138
+ USE_3D,
139
+ RND,
140
+ INSTALLMENT,
141
+ AUTHORIZATION_AMOUNT,
142
+ CURRENCY_CODE,
143
+ hashDataV2
144
+ } = params;
145
+
146
+ // 1. İşlem Başarılı mı kontrolü
147
+ if (RESPONSE_CODE !== "2" && RESPONSE_CODE !== 2) {
148
+ return { status: false, message: 'İşlem başarısız (RESPONSE_CODE != 2)' };
149
+ }
150
+
151
+ // 2. Hash Hesaplama (Dokümantasyondaki 'Response Hash Testi' sırası)
152
+ // MERCHANT_NO | REFERENCE_CODE | AUTH_CODE | RESPONSE_CODE | USE_3D | RND | INSTALLMENT | AUTHORIZATION_AMOUNT | CURRENCY_CODE | MERCHANT_SECRET_KEY
153
+
154
+ const hashString = [
155
+ MERCHANT_NO,
156
+ REFERENCE_CODE,
157
+ AUTH_CODE,
158
+ RESPONSE_CODE,
159
+ USE_3D, // Gelen değer string "true" veya "false" olabilir, olduğu gibi kullanılmalı
160
+ RND,
161
+ INSTALLMENT,
162
+ AUTHORIZATION_AMOUNT,
163
+ CURRENCY_CODE,
164
+ this.merchantSecretKey
165
+ ].join("|");
166
+
167
+ const calculatedHash = this.generateHash(hashString);
168
+
169
+ if (calculatedHash === hashDataV2) {
170
+ return {
171
+ status: 'success',
172
+ message: 'Doğrulama Başarılı',
173
+ amount: AUTHORIZATION_AMOUNT,
174
+ orderId: params.CLIENT_REFERENCE_CODE || REFERENCE_CODE
175
+ };
176
+ } else {
177
+ console.error("Hash String (Local):", hashString);
178
+ console.error("Calculated:", calculatedHash);
179
+ console.error("Received:", hashDataV2);
180
+ return { status: false, message: 'Hash uyuşmazlığı (Güvenlik Hatası)' };
181
+ }
182
+ }
183
+
184
+ /**
185
+ * SHA-512 Base64 Helper
186
+ */
187
+ generateHash(str) {
188
+ // UTF-8 encoding önemlidir (CryptoJS.SHA512 davranışı)
189
+ return crypto.createHash('sha512').update(str, 'utf8').digest('base64');
190
+ }
191
+ }
192
+
193
+ module.exports = Paynkolay;
package/lib/paywant.js ADDED
@@ -0,0 +1,137 @@
1
+ const crypto = require('crypto');
2
+ const axios = require('axios');
3
+
4
+ class Paywant {
5
+ /**
6
+ * Paywant Kurucu Metodu
7
+ * @param {Object} config
8
+ * @param {string} config.apiKey - Paywant API Anahtarı
9
+ * @param {string} config.apiSecret - Paywant Gizli Anahtar
10
+ */
11
+ constructor(config) {
12
+ if (!config.apiKey || !config.apiSecret) {
13
+ throw new Error('Paywant: apiKey ve apiSecret zorunludur.');
14
+ }
15
+ this.apiKey = config.apiKey;
16
+ this.apiSecret = config.apiSecret;
17
+ this.baseUrl = 'https://secure.paywant.com';
18
+ }
19
+
20
+ /**
21
+ * Ödeme Linki Oluşturma (/payment/token)
22
+ * @param {Object} params
23
+ */
24
+ async createPayment(params) {
25
+ try {
26
+ // Zorunlu alan kontrolü
27
+ const required = ['userID', 'userEmail', 'userAccountName', 'userIp', 'productData'];
28
+ for (const field of required) {
29
+ if (!params[field]) throw new Error(`Paywant: ${field} alanı zorunludur.`);
30
+ }
31
+
32
+ // 1. Hash Oluşturma (Dokümantasyona Göre)
33
+ // Sıralama: userName|userEmail|userID + ApiKey
34
+ const hashString = `${params.userAccountName}|${params.userEmail}|${params.userID}${this.apiKey}`;
35
+ const hash = this.generateHash(hashString);
36
+
37
+ // 2. API'ye Gönderilecek Veri
38
+ const payload = {
39
+ apiKey: this.apiKey,
40
+ userAccountName: params.userAccountName,
41
+ userEmail: params.userEmail,
42
+ userID: parseInt(params.userID), // int64 olmalı
43
+ userIPAddress: params.userIp,
44
+ hash: hash,
45
+ proApi: true, // Ürün verisini biz gönderiyoruz
46
+ productData: {
47
+ name: params.productData.name, // Ürün adı
48
+ amount: parseFloat(params.productData.amount), // Tutar (100.00 gibi)
49
+ extraData: params.productData.extraData || '', // Sipariş No vb.
50
+ paymentChannel: params.productData.paymentChannel || '0', // 0 = Hepsi
51
+ commissionType: params.productData.commissionType || '1' // 1: Müşteri, 2: Mağaza öder
52
+ }
53
+ };
54
+
55
+ // 3. İsteği Gönder
56
+ const response = await axios.post(`${this.baseUrl}/payment/token`, payload, {
57
+ headers: {
58
+ 'Content-Type': 'application/json'
59
+ }
60
+ });
61
+
62
+ const result = response.data;
63
+
64
+ if (result.status === true) {
65
+ // Başarılı ise ödeme linkini döndür
66
+ return {
67
+ status: 'success',
68
+ link: result.message // Örn: https://secure.paywant.com/common/TOKEN
69
+ };
70
+ } else {
71
+ // Paywant'tan dönen hata
72
+ throw new Error(result.message || 'Paywant ödeme oluşturulamadı.');
73
+ }
74
+
75
+ } catch (error) {
76
+ if (error.response) {
77
+ throw new Error(`Paywant API Hatası: ${JSON.stringify(error.response.data)}`);
78
+ }
79
+ throw error;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * IPN (Bildirim) Doğrulama
85
+ * Paywant'tan gelen POST isteğini doğrular.
86
+ * @param {Object} reqBody - Express request body
87
+ */
88
+ handleCallback(reqBody) {
89
+ const {
90
+ transactionID,
91
+ extraData,
92
+ userID,
93
+ userAccountName,
94
+ status,
95
+ paymentChannel,
96
+ paymentTotal,
97
+ netProfit,
98
+ hash
99
+ } = reqBody;
100
+
101
+ if (!hash) throw new Error('Hash parametresi eksik.');
102
+
103
+ // Dokümantasyondaki IPN Hash Formülü:
104
+ // transactionID|extraData|userID|userAccountName|status|paymentChannel|paymentTotal|netProfit + ApiKey
105
+ const hashString = `${transactionID}|${extraData}|${userID}|${userAccountName}|${status}|${paymentChannel}|${paymentTotal}|${netProfit}${this.apiKey}`;
106
+
107
+ const generatedHash = this.generateHash(hashString);
108
+
109
+ if (hash === generatedHash) {
110
+ // Status 100 = Başarılı
111
+ if (parseInt(status) === 100) {
112
+ return {
113
+ status: 'success',
114
+ orderId: extraData, // Genelde sipariş no extraData'ya konur
115
+ transactionId: transactionID,
116
+ amount: parseFloat(paymentTotal),
117
+ netAmount: parseFloat(netProfit)
118
+ };
119
+ } else {
120
+ return { success: false, message: 'Ödeme başarısız veya beklemede.' };
121
+ }
122
+ } else {
123
+ throw new Error('Hash uyuşmazlığı (Bad Hash)');
124
+ }
125
+ }
126
+
127
+ /**
128
+ * HMAC-SHA256 Hash Üretici (Base64)
129
+ */
130
+ generateHash(data) {
131
+ return crypto.createHmac('sha256', this.apiSecret)
132
+ .update(data)
133
+ .digest('base64');
134
+ }
135
+ }
136
+
137
+ module.exports = Paywant;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quickpos",
3
- "version": "1.0.916",
3
+ "version": "1.0.917",
4
4
  "main": "app.js",
5
5
  "scripts": {
6
6
  "test": "node test.js"
package/readme.md CHANGED
@@ -75,7 +75,11 @@
75
75
  | **2Checkout** | 🌐 Global | ✅ Active |
76
76
  | **İyzico** | 🇹🇷 Turkey | ✅ Active |
77
77
  | **PayTR** | 🇹🇷 Turkey | ✅ Active |
78
+ | **PayTR EFT** | 🇹🇷 Turkey | ✅ Active |
79
+ | **PayWant** | 🇹🇷 Turkey | ✅ Active |
80
+ | **PaynKolay** | 🇹🇷 Turkey | ✅ Active |
78
81
  | **Shopier** | 🇹🇷 Turkey | ✅ Active |
82
+ | **Shopier Card** | 🇹🇷 Turkey | ✅ Active |
79
83
  | **Papara** | 🇹🇷 Turkey | ✅ Active |
80
84
  | **EsnekPos** | 🇹🇷 Turkey | ✅ Active |
81
85
  | **Paydisini** | 🇹🇷 Turkey | ✅ Active |