quickpos 1.0.917 → 1.0.918

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/lib/2checkout.js CHANGED
@@ -1,165 +1,121 @@
1
- const axios = require('axios');
2
1
  const crypto = require('crypto');
3
2
 
4
3
  class TwoCheckoutClient {
5
4
  constructor(config) {
6
- const requiredFields = ['merchantCode', 'secretKey'];
5
+ const requiredFields = ['merchantCode', 'secretWord'];
7
6
  for (let field of requiredFields) {
8
7
  if (!config[field]) throw new Error(`Missing required field: ${field}`);
9
8
  }
10
9
 
11
10
  this.merchantCode = config.merchantCode;
12
- this.secretKey = config.secretKey;
13
- this.baseURL = config.sandbox
14
- ? 'https://api.sandbox.2checkout.com'
15
- : 'https://api.2checkout.com';
16
-
17
- this.client = axios.create({
18
- baseURL: this.baseURL,
19
- headers: {
20
- 'Content-Type': 'application/json'
11
+ this.secretWord = config.secretWord;
12
+ this.sandbox = config.sandbox || false;
13
+ }
14
+
15
+ generateHash(params, algo = 'md5') {
16
+ let signatureString = '';
17
+
18
+ Object.keys(params).forEach(key => {
19
+ const value = String(params[key]); // Değer string olmalı
20
+ if (value.length > 0) { // Boş değerler bazen hash'e katılmaz
21
+ signatureString += value.length + value;
21
22
  }
22
23
  });
23
- }
24
24
 
25
- generateSignature(params) {
26
- const signatureString = Object.keys(params)
27
- .sort()
28
- .map(key => params[key].length + params[key])
29
- .join('');
30
-
31
25
  return crypto
32
- .createHmac('sha256', this.secretKey)
26
+ .createHmac(algo, this.secretWord)
33
27
  .update(signatureString)
34
28
  .digest('hex');
35
29
  }
36
30
 
37
- async createPayment(options) {
38
- try {
39
- const orderId = options.orderId || `ORDER-${Date.now()}`;
40
- const timestamp = new Date().toISOString();
41
-
42
- const params = {
43
- 'merchant': this.merchantCode,
44
- 'dynamic': '1',
45
- 'return-url': options.successUrl || options.callback_link,
46
- 'return-type': 'redirect',
47
- 'expiration': timestamp,
48
- 'order-ext-ref': orderId,
49
- 'item-ext-ref': orderId,
50
- 'customer-ext-ref': options.customerId || '',
51
- 'currency': options.currency || 'USD',
52
- 'language': options.language || 'en',
53
- 'test': options.sandbox ? '1' : '0',
54
- 'prod-name[0]': options.name || 'Payment',
55
- 'prod-type[0]': 'PRODUCT',
56
- 'prod-price[0]': parseFloat(options.amount).toString(),
57
- 'prod-qty[0]': '1',
58
- 'prod-description[0]': options.description || '',
59
- 'customer-email': options.email || '',
60
- 'customer-name': options.customerName || ''
61
- };
62
-
63
- const signature = this.generateSignature(params);
64
- params.signature = signature;
65
-
66
- // Create form URL
67
- const formParams = new URLSearchParams(params);
68
- const paymentUrl = `https://secure.2checkout.com/order/checkout.php?${formParams.toString()}`;
69
-
70
- return {
71
- status: 'success',
72
- data: {
73
- url: paymentUrl,
74
- orderId: orderId,
75
- params: params
76
- }
77
- };
78
- } catch (error) {
79
- throw new Error(`Payment creation error: ${error.message}`);
80
- }
81
- }
31
+ createPaymentUrl(options) {
32
+ const orderId = options.orderId || `ORDER-${Date.now()}`;
33
+
34
+ const params = {
35
+ 'merchant': this.merchantCode,
36
+ 'dynamic': '1', // Dinamik ürün modu
37
+ 'prod-name[]': options.name || 'Payment',
38
+ 'prod-type[]': 'GLOBAL_SERVICE', // Ürün tipi
39
+ 'prod-price[]': parseFloat(options.amount).toFixed(2),
40
+ 'prod-qty[]': '1',
41
+ 'currency': options.currency || 'USD',
42
+ 'return-type': 'redirect',
43
+ 'return-url': options.callbackUrl,
44
+ 'expiration': Math.floor(Date.now() / 1000) + 3600, // 1 saat geçerli
45
+ 'order-ext-ref': orderId,
46
+ 'customer-ref': options.customerId || '',
47
+ 'customer-email': options.email || ''
48
+ };
49
+
50
+ let signatureData = '';
51
+
52
+ const keysToSign = [
53
+ 'merchant', 'dynamic', 'prod-name[]', 'prod-type[]', 'prod-price[]',
54
+ 'prod-qty[]', 'currency', 'return-type', 'return-url', 'expiration',
55
+ 'order-ext-ref', 'customer-ref', 'customer-email'
56
+ ];
57
+
58
+ keysToSign.forEach(key => {
59
+ const val = String(params[key] || '');
60
+ if(val) signatureData += val.length + val;
61
+ });
82
62
 
83
- async handleCallback(callbackData) {
84
- try {
85
- const verification = await this.verifyCallback(callbackData);
86
-
87
- if (!verification.status) {
88
- throw new Error(verification.error.message);
89
- }
63
+ const signature = crypto.createHmac('md5', this.secretWord).update(signatureData).digest('hex');
64
+ params['signature'] = signature;
90
65
 
91
- const data = verification.data;
92
-
93
- // Status mapping
94
- const statusMapping = {
95
- 'COMPLETE': 'success',
96
- 'PENDING': 'pending',
97
- 'REFUND': 'refunded',
98
- 'REVERSED': 'failed'
99
- };
100
-
101
- return {
102
- status: statusMapping[data.order_status] || 'unknown',
103
- orderId: data['order-ext-ref'] || data.order_number,
104
- transactionId: data.order_number,
105
- amount: parseFloat(data.invoice_list_amount || data.order_amount),
106
- currency: data.list_currency,
107
- paymentStatus: data.order_status,
108
- invoiceId: data.invoice_id
109
- };
110
- } catch (error) {
111
- throw new Error(`Error in 2Checkout callback handling: ${error.message}`);
112
- }
66
+ // URL Parametrelerini oluştur
67
+ const queryParams = new URLSearchParams();
68
+ Object.keys(params).forEach(key => queryParams.append(key, params[key]));
69
+
70
+ return `https://secure.2checkout.com/order/checkout.php?${queryParams.toString()}`;
113
71
  }
114
72
 
115
- async verifyCallback(data) {
73
+ /**
74
+ * IPN (Callback) Doğrulama
75
+ * Bu kısım, sunucunuza gelen POST isteğini doğrular.
76
+ */
77
+ verifyCallback(postData) {
116
78
  try {
117
- const hash = data.hash || data.HASH;
118
-
119
- if (!hash) {
120
- return {
121
- status: false,
122
- error: {
123
- code: 400,
124
- message: 'Hash not found in callback data'
125
- }
126
- };
79
+ // Gelen veriden HASH'i ayıralım
80
+ const receivedHash = postData['HASH'];
81
+ if (!receivedHash) {
82
+ return { status: false, message: 'No hash found' };
127
83
  }
128
84
 
129
- // Verify IPN hash
130
- const params = { ...data };
131
- delete params.hash;
132
- delete params.HASH;
85
+ let hashString = '';
133
86
 
87
+ // 2Checkout, HASH parametresi hariç tüm parametreleri gönderdiği sırayla kullanır.
88
+ for (let key in postData) {
89
+ if (key !== 'HASH') {
90
+ const val = String(postData[key]);
91
+ hashString += val.length + val;
92
+ }
93
+ }
94
+
134
95
  const calculatedHash = crypto
135
- .createHmac('md5', this.secretKey)
136
- .update(JSON.stringify(params))
96
+ .createHmac('md5', this.secretWord)
97
+ .update(hashString)
137
98
  .digest('hex');
138
99
 
139
- if (hash !== calculatedHash) {
140
- return {
141
- status: false,
142
- error: {
143
- code: 401,
144
- message: 'Invalid hash'
145
- }
100
+ if (calculatedHash === receivedHash) {
101
+ const date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
102
+ const responseHash = crypto.createHmac('md5', this.secretWord).update(postData.IPN_PID[0].length + postData.IPN_PID[0] + postData.IPN_PNAME[0].length + postData.IPN_PNAME[0] + postData.IPN_DATE.length + postData.IPN_DATE + date.length + date).digest('hex');
103
+
104
+ return {
105
+ status: true,
106
+ transactionId: postData.REFNO,
107
+ orderStatus: postData.ORDERSTATUS,
108
+ raw: postData
146
109
  };
110
+ } else {
111
+ console.error("Hash Mismatch. Calculated:", calculatedHash, "Received:", receivedHash);
112
+ return { status: false, message: 'Invalid hash signature' };
147
113
  }
148
114
 
149
- return {
150
- status: true,
151
- data: data
152
- };
153
115
  } catch (error) {
154
- return {
155
- status: false,
156
- error: {
157
- code: 500,
158
- message: error.message
159
- }
160
- };
116
+ return { status: false, message: error.message };
161
117
  }
162
118
  }
163
119
  }
164
120
 
165
- module.exports = TwoCheckoutClient;
121
+ module.exports = TwoCheckoutClient;
package/lib/cryptomus.js CHANGED
@@ -98,7 +98,7 @@ class Cryptomus {
98
98
  throw new Error("Cryptomus notification failed: invalid signature");
99
99
  }
100
100
 
101
- if (callbackData.status === 'paid') {
101
+ if (callbackData.status === 'paid' || callbackData.status === 'paid_over') {
102
102
  return {
103
103
  status: 'success',
104
104
  orderId: callbackData.order_id,
@@ -0,0 +1,121 @@
1
+ const crypto = require('crypto');
2
+
3
+ class TwoCheckoutClient {
4
+ constructor(config) {
5
+ const requiredFields = ['merchantCode', 'secretWord'];
6
+ for (let field of requiredFields) {
7
+ if (!config[field]) throw new Error(`Missing required field: ${field}`);
8
+ }
9
+
10
+ this.merchantCode = config.merchantCode;
11
+ this.secretWord = config.secretWord;
12
+ this.sandbox = config.sandbox || false;
13
+ }
14
+
15
+ generateHash(params, algo = 'md5') {
16
+ let signatureString = '';
17
+
18
+ Object.keys(params).forEach(key => {
19
+ const value = String(params[key]); // Değer string olmalı
20
+ if (value.length > 0) { // Boş değerler bazen hash'e katılmaz
21
+ signatureString += value.length + value;
22
+ }
23
+ });
24
+
25
+ return crypto
26
+ .createHmac(algo, this.secretWord)
27
+ .update(signatureString)
28
+ .digest('hex');
29
+ }
30
+
31
+ createPaymentUrl(options) {
32
+ const orderId = options.orderId || `ORDER-${Date.now()}`;
33
+
34
+ const params = {
35
+ 'merchant': this.merchantCode,
36
+ 'dynamic': '1', // Dinamik ürün modu
37
+ 'prod-name[]': options.name || 'Payment',
38
+ 'prod-type[]': 'GLOBAL_SERVICE', // Ürün tipi
39
+ 'prod-price[]': parseFloat(options.amount).toFixed(2),
40
+ 'prod-qty[]': '1',
41
+ 'currency': options.currency || 'USD',
42
+ 'return-type': 'redirect',
43
+ 'return-url': options.callbackUrl,
44
+ 'expiration': Math.floor(Date.now() / 1000) + 3600, // 1 saat geçerli
45
+ 'order-ext-ref': orderId,
46
+ 'customer-ref': options.customerId || '',
47
+ 'customer-email': options.email || ''
48
+ };
49
+
50
+ let signatureData = '';
51
+
52
+ const keysToSign = [
53
+ 'merchant', 'dynamic', 'prod-name[]', 'prod-type[]', 'prod-price[]',
54
+ 'prod-qty[]', 'currency', 'return-type', 'return-url', 'expiration',
55
+ 'order-ext-ref', 'customer-ref', 'customer-email'
56
+ ];
57
+
58
+ keysToSign.forEach(key => {
59
+ const val = String(params[key] || '');
60
+ if(val) signatureData += val.length + val;
61
+ });
62
+
63
+ const signature = crypto.createHmac('md5', this.secretWord).update(signatureData).digest('hex');
64
+ params['signature'] = signature;
65
+
66
+ // URL Parametrelerini oluştur
67
+ const queryParams = new URLSearchParams();
68
+ Object.keys(params).forEach(key => queryParams.append(key, params[key]));
69
+
70
+ return `https://secure.2checkout.com/order/checkout.php?${queryParams.toString()}`;
71
+ }
72
+
73
+ /**
74
+ * IPN (Callback) Doğrulama
75
+ * Bu kısım, sunucunuza gelen POST isteğini doğrular.
76
+ */
77
+ verifyCallback(postData) {
78
+ try {
79
+ // Gelen veriden HASH'i ayıralım
80
+ const receivedHash = postData['HASH'];
81
+ if (!receivedHash) {
82
+ return { status: false, message: 'No hash found' };
83
+ }
84
+
85
+ let hashString = '';
86
+
87
+ // 2Checkout, HASH parametresi hariç tüm parametreleri gönderdiği sırayla kullanır.
88
+ for (let key in postData) {
89
+ if (key !== 'HASH') {
90
+ const val = String(postData[key]);
91
+ hashString += val.length + val;
92
+ }
93
+ }
94
+
95
+ const calculatedHash = crypto
96
+ .createHmac('md5', this.secretWord)
97
+ .update(hashString)
98
+ .digest('hex');
99
+
100
+ if (calculatedHash === receivedHash) {
101
+ const date = new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '');
102
+ const responseHash = crypto.createHmac('md5', this.secretWord).update(postData.IPN_PID[0].length + postData.IPN_PID[0] + postData.IPN_PNAME[0].length + postData.IPN_PNAME[0] + postData.IPN_DATE.length + postData.IPN_DATE + date.length + date).digest('hex');
103
+
104
+ return {
105
+ status: true,
106
+ transactionId: postData.REFNO,
107
+ orderStatus: postData.ORDERSTATUS,
108
+ raw: postData
109
+ };
110
+ } else {
111
+ console.error("Hash Mismatch. Calculated:", calculatedHash, "Received:", receivedHash);
112
+ return { status: false, message: 'Invalid hash signature' };
113
+ }
114
+
115
+ } catch (error) {
116
+ return { status: false, message: error.message };
117
+ }
118
+ }
119
+ }
120
+
121
+ module.exports = TwoCheckoutClient;
package/lib/paytr.js CHANGED
@@ -98,63 +98,91 @@ class PayTR {
98
98
  }
99
99
  }
100
100
 
101
- async createDirectPayment(params) {
101
+ // Direkt API için Otomatik Submit Eden HTML Formu Üretir
102
+ createDirectPayment(params) {
102
103
  try {
103
- const userBasket = JSON.stringify(params.userBasket);
104
- const userIp = params.userIp;
105
- const merchantOid = params.merchantOid;
104
+ // 1. Gerekli Parametrelerin Hazırlanması
105
+ const merchant_id = this.merchantId;
106
+ const user_ip = params.userIp;
107
+ const merchant_oid = params.merchantOid;
106
108
  const email = params.email;
107
- const paymentAmount = params.paymentAmount.toString();
109
+ const payment_amount = params.paymentAmount.toString();
110
+ const payment_type = 'card';
111
+ const installment_count = params.installmentCount || '0';
108
112
  const currency = params.currency || 'TL';
109
- const installmentCount = params.installmentCount || '0';
110
- const paymentType = 'card';
111
- const non3D = params.non3D ? params.non3D : '0';
112
-
113
- const rawString = `${this.merchantId}${userIp}${merchantOid}${email}${paymentAmount}${paymentType}${installmentCount}${currency}${this.testMode}${non3D}`;
114
- const paytrToken = this.generateToken(rawString);
113
+ const test_mode = this.testMode;
114
+ const non_3d = params.non3D || '0';
115
+
116
+ // Sepet verisi JSON string olmalı
117
+ const user_basket = JSON.stringify(params.userBasket);
118
+
119
+ // 2. Token Oluşturma (Sıralama: merchant_id + user_ip + merchant_oid + email + payment_amount + payment_type + installment_count + currency + test_mode + non_3d)
120
+ const rawString = `${merchant_id}${user_ip}${merchant_oid}${email}${payment_amount}${payment_type}${installment_count}${currency}${test_mode}${non_3d}`;
121
+ const paytr_token = this.generateToken(rawString);
122
+
123
+ // 3. Form Verilerinin Hazırlanması
124
+ const formFields = {
125
+ merchant_id,
126
+ user_ip,
127
+ merchant_oid,
128
+ email,
129
+ payment_type,
130
+ payment_amount: Number(payment_amount),
131
+ currency,
132
+ test_mode,
133
+ non_3d,
134
+ merchant_ok_url: params.merchantOkUrl,
135
+ merchant_fail_url: params.merchantFailUrl,
136
+ user_name: params.userName,
137
+ user_address: params.userAddress,
138
+ user_phone: params.userPhone,
139
+ user_basket,
140
+ debug_on: this.debugOn,
141
+ client_lang: params.clientLang || 'tr',
142
+ paytr_token,
143
+ installment_count: installment_count,
144
+ card_type: params.cardType,
145
+ non3d_test_failed: params.non3dTestFailed || '0',
146
+ no_installment: params.noInstallment || '0',
147
+ max_installment: params.maxInstallment || '0',
148
+ lang: params.lang || 'tr',
149
+ // Kart Bilgileri (Form otomatik post edileceği için buraya eklenmeli)
150
+ cc_owner: params.cardOwner,
151
+ card_number: params.cardNumber,
152
+ expiry_month: params.cardExpireMonth,
153
+ expiry_year: params.cardExpireYear,
154
+ cvv: params.cardCvc
155
+ };
115
156
 
116
- const formData = new URLSearchParams();
117
- formData.append('merchant_id', this.merchantId);
118
- formData.append('user_ip', userIp);
119
- formData.append('merchant_oid', merchantOid);
120
- formData.append('email', email);
121
- formData.append('payment_amount', paymentAmount);
122
- formData.append('paytr_token', paytrToken);
123
- formData.append('user_basket', userBasket);
124
- formData.append('debug_on', this.debugOn);
125
- formData.append('test_mode', this.testMode);
126
- formData.append('payment_type', paymentType);
127
- formData.append('installment_count', installmentCount);
128
- formData.append('currency', currency);
129
- formData.append('non_3d', non3D);
130
- formData.append('cc_owner', params.cardOwner);
131
- formData.append('card_number', params.cardNumber);
132
- formData.append('expiry_month', params.cardExpireMonth);
133
- formData.append('expiry_year', params.cardExpireYear);
134
- formData.append('cvc', params.cardCvc);
135
- formData.append('user_name', params.userName);
136
- formData.append('user_address', params.userAddress);
137
- formData.append('user_phone', params.userPhone);
138
- formData.append('merchant_ok_url', params.merchantOkUrl);
139
- formData.append('merchant_fail_url', params.merchantFailUrl);
157
+ // 4. HTML Formunun Oluşturulması
158
+ // Form oluşturulur, inputlar gizlenir ve script ile otomatik submit edilir.
159
+ let html = `
160
+ <!DOCTYPE html>
161
+ <html>
162
+ <head><title>Redirecting to Payment...</title></head>
163
+ <body>
164
+ <form id="paytr_auto_form" action="https://www.paytr.com/odeme" method="post">
165
+ `;
166
+
167
+ for (const [key, value] of Object.entries(formFields)) {
168
+ // Değer undefined veya null ise boş string gönderelim, güvenlik için escape yapalım
169
+ const safeValue = value ? String(value).replace(/"/g, '&quot;') : '';
170
+ html += `<input type="hidden" name="${key}" value="${safeValue}">\n`;
171
+ }
140
172
 
141
- const response = await axios({
142
- method: 'POST',
143
- url: 'https://www.paytr.com/odeme/api/direct',
144
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
145
- data: formData
146
- });
173
+ html += `
174
+ </form>
175
+ <script>
176
+ document.getElementById("paytr_auto_form").submit();
177
+ </script>
178
+ </body>
179
+ </html>
180
+ `;
147
181
 
148
- const result = response.data;
182
+ return html;
149
183
 
150
- if (result.status === 'success') {
151
- return result;
152
- } else {
153
- throw new Error(result.reason || 'PayTR Direct API Hatası');
154
- }
155
184
  } catch (error) {
156
- if (error.response) throw new Error(`PayTR API Error: ${error.response.data.reason || error.response.statusText}`);
157
- throw error;
185
+ throw new Error(`PayTR HTML oluşturma hatası: ${error.message}`);
158
186
  }
159
187
  }
160
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quickpos",
3
- "version": "1.0.917",
3
+ "version": "1.0.918",
4
4
  "main": "app.js",
5
5
  "scripts": {
6
6
  "test": "node test.js"