quickpos 1.0.914 → 1.0.915
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/paytr.js +169 -47
- package/package.json +1 -1
- package/lib/paytr_eft.js +0 -156
package/lib/paytr.js
CHANGED
|
@@ -1,26 +1,39 @@
|
|
|
1
1
|
const crypto = require('crypto');
|
|
2
|
-
const jsSHA = require('jssha');
|
|
3
2
|
const axios = require('axios');
|
|
4
3
|
|
|
5
4
|
class PayTR {
|
|
6
5
|
constructor(config) {
|
|
7
6
|
this.config = config || {};
|
|
7
|
+
|
|
8
8
|
const requiredFields = ['merchantId', 'merchantKey', 'merchantSalt'];
|
|
9
9
|
for (let field of requiredFields) {
|
|
10
|
-
if (!config[field]) throw new Error(`Missing required field: ${field}`);
|
|
10
|
+
if (!this.config[field]) throw new Error(`Missing required field: ${field}`);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
this.merchantId = config.merchantId;
|
|
14
|
-
this.merchantKey = config.merchantKey;
|
|
15
|
-
this.merchantSalt = config.merchantSalt;
|
|
16
|
-
this.
|
|
13
|
+
this.merchantId = this.config.merchantId;
|
|
14
|
+
this.merchantKey = this.config.merchantKey;
|
|
15
|
+
this.merchantSalt = this.config.merchantSalt;
|
|
16
|
+
this.testMode = this.config.testMode ? '1' : '0';
|
|
17
|
+
this.debugOn = this.config.debugOn ? '1' : '0';
|
|
18
|
+
|
|
19
|
+
this.iframe = {
|
|
20
|
+
createToken: this.createIframeToken.bind(this)
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
this.direct = {
|
|
24
|
+
createPayment: this.createDirectPayment.bind(this)
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
this.link = {
|
|
28
|
+
createPayment: this.createLinkPayment.bind(this)
|
|
29
|
+
};
|
|
17
30
|
}
|
|
18
31
|
|
|
19
|
-
async
|
|
32
|
+
async createLinkPayment(paymentDetails) {
|
|
20
33
|
try {
|
|
21
|
-
|
|
22
|
-
for (let
|
|
23
|
-
if (!paymentDetails[
|
|
34
|
+
const requiredFields = ['name', 'amount', 'currency', 'maxInstallment', 'expiry_date'];
|
|
35
|
+
for (let field of requiredFields) {
|
|
36
|
+
if (!paymentDetails[field]) throw new Error(`Missing required data: ${field}`);
|
|
24
37
|
}
|
|
25
38
|
|
|
26
39
|
const price = Math.round(paymentDetails.amount * 100).toString();
|
|
@@ -42,14 +55,16 @@ class PayTR {
|
|
|
42
55
|
lang: lang,
|
|
43
56
|
min_count: minCount,
|
|
44
57
|
paytr_token: paytrToken,
|
|
45
|
-
expiry_date: paymentDetails
|
|
58
|
+
expiry_date: paymentDetails.expiry_date,
|
|
46
59
|
get_qr: '1',
|
|
47
|
-
max_count: Number(paymentDetails
|
|
48
|
-
debug_on: this.
|
|
60
|
+
max_count: Number(paymentDetails.max_count) || '1',
|
|
61
|
+
debug_on: this.debugOn
|
|
49
62
|
};
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
for (let data of optionalData) {
|
|
64
|
+
const optionalData = ['email', 'callback_link', 'callback_id'];
|
|
65
|
+
for (let data of optionalData) {
|
|
66
|
+
if(paymentDetails[data]) formData[data] = paymentDetails[data];
|
|
67
|
+
}
|
|
53
68
|
|
|
54
69
|
const response = await axios({
|
|
55
70
|
method: 'POST',
|
|
@@ -77,42 +92,157 @@ class PayTR {
|
|
|
77
92
|
}
|
|
78
93
|
} catch (error) {
|
|
79
94
|
if (error.response) {
|
|
80
|
-
// The request was made and the server responded with a status code
|
|
81
|
-
// that falls out of the range of 2xx
|
|
82
95
|
throw new Error(`PayTR API error: ${error.response.data.reason || error.response.statusText}`);
|
|
83
|
-
} else if (error.request) {
|
|
84
|
-
// The request was made but no response was received
|
|
85
|
-
throw new Error('No response received from PayTR API');
|
|
86
|
-
} else {
|
|
87
|
-
// Something happened in setting up the request that triggered an Error
|
|
88
|
-
throw new Error(`Error in PayTR payment creation: ${error.message}`);
|
|
89
96
|
}
|
|
97
|
+
throw error;
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
async
|
|
101
|
+
async createDirectPayment(params) {
|
|
94
102
|
try {
|
|
95
|
-
const
|
|
103
|
+
const userBasket = JSON.stringify(params.userBasket);
|
|
104
|
+
const userIp = params.userIp;
|
|
105
|
+
const merchantOid = params.merchantOid;
|
|
106
|
+
const email = params.email;
|
|
107
|
+
const paymentAmount = params.paymentAmount.toString();
|
|
108
|
+
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);
|
|
115
|
+
|
|
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);
|
|
140
|
+
|
|
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
|
+
});
|
|
147
|
+
|
|
148
|
+
const result = response.data;
|
|
96
149
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
150
|
+
if (result.status === 'success') {
|
|
151
|
+
return result;
|
|
152
|
+
} else {
|
|
153
|
+
throw new Error(result.reason || 'PayTR Direct API Hatası');
|
|
100
154
|
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
if (error.response) throw new Error(`PayTR API Error: ${error.response.data.reason || error.response.statusText}`);
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
101
160
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
161
|
+
async createIframeToken(params) {
|
|
162
|
+
try {
|
|
163
|
+
const userBasket = JSON.stringify(params.userBasket);
|
|
164
|
+
const userIp = params.userIp;
|
|
165
|
+
const merchantOid = params.merchantOid;
|
|
166
|
+
const email = params.email;
|
|
167
|
+
const paymentAmount = params.paymentAmount.toString();
|
|
168
|
+
const currency = params.currency || 'TL';
|
|
169
|
+
const noInstallment = params.noInstallment ? '1' : '0';
|
|
170
|
+
const maxInstallment = params.maxInstallment || '0';
|
|
171
|
+
const paymentType = params.paymentType || 'card';
|
|
172
|
+
|
|
173
|
+
const rawString = `${this.merchantId}${userIp}${merchantOid}${email}${paymentAmount}${userBasket}${noInstallment}${maxInstallment}${currency}${this.testMode}`;
|
|
174
|
+
const paytrToken = this.generateToken(rawString);
|
|
175
|
+
|
|
176
|
+
const formData = new URLSearchParams();
|
|
177
|
+
formData.append('merchant_id', this.merchantId);
|
|
178
|
+
formData.append('user_ip', userIp);
|
|
179
|
+
formData.append('merchant_oid', merchantOid);
|
|
180
|
+
formData.append('email', email);
|
|
181
|
+
formData.append('payment_amount', paymentAmount);
|
|
182
|
+
formData.append('paytr_token', paytrToken);
|
|
183
|
+
formData.append('user_basket', userBasket);
|
|
184
|
+
formData.append('debug_on', this.debugOn);
|
|
185
|
+
formData.append('no_installment', noInstallment);
|
|
186
|
+
formData.append('max_installment', maxInstallment);
|
|
187
|
+
formData.append('user_name', params.userName);
|
|
188
|
+
formData.append('user_address', params.userAddress);
|
|
189
|
+
formData.append('user_phone', params.userPhone);
|
|
190
|
+
formData.append('merchant_ok_url', params.merchantOkUrl);
|
|
191
|
+
formData.append('merchant_fail_url', params.merchantFailUrl);
|
|
192
|
+
formData.append('timeout_limit', params.timeoutLimit || '30');
|
|
193
|
+
formData.append('currency', currency);
|
|
194
|
+
formData.append('test_mode', this.testMode);
|
|
195
|
+
formData.append('payment_type', paymentType);
|
|
196
|
+
|
|
197
|
+
if (params.bank) formData.append('bank', params.bank);
|
|
198
|
+
if (params.lang) formData.append('lang', params.lang);
|
|
199
|
+
|
|
200
|
+
const response = await axios({
|
|
201
|
+
method: 'POST',
|
|
202
|
+
url: 'https://www.paytr.com/odeme/api/get-token',
|
|
203
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
204
|
+
data: formData
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const result = response.data;
|
|
208
|
+
if (result.status === 'success') {
|
|
209
|
+
return { status: 'success', token: result.token };
|
|
111
210
|
} else {
|
|
112
|
-
throw new Error(
|
|
211
|
+
throw new Error(result.reason || 'PayTR Token alma hatası');
|
|
113
212
|
}
|
|
114
213
|
} catch (error) {
|
|
115
|
-
throw new Error(`
|
|
214
|
+
if (error.response) throw new Error(`PayTR API Error: ${error.response.data.reason || error.response.statusText}`);
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
handleCallback(callbackData) {
|
|
220
|
+
const { merchant_oid, status, total_amount, hash } = callbackData;
|
|
221
|
+
|
|
222
|
+
if (!hash) throw new Error('Hash param missing');
|
|
223
|
+
|
|
224
|
+
const token = `${merchant_oid}${this.merchantSalt}${status}${total_amount}`;
|
|
225
|
+
|
|
226
|
+
const generatedHash = crypto
|
|
227
|
+
.createHmac('sha256', this.merchantKey)
|
|
228
|
+
.update(token)
|
|
229
|
+
.digest('base64');
|
|
230
|
+
|
|
231
|
+
if (generatedHash !== hash) {
|
|
232
|
+
throw new Error("PAYTR notification failed: bad hash");
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (status === 'success') {
|
|
236
|
+
return {
|
|
237
|
+
status: 'success',
|
|
238
|
+
orderId: callbackData.callback_id || merchant_oid,
|
|
239
|
+
merchant_oid: merchant_oid,
|
|
240
|
+
amount: parseFloat(total_amount) / 100,
|
|
241
|
+
currency: callbackData.currency,
|
|
242
|
+
paymentType: callbackData.payment_type
|
|
243
|
+
};
|
|
244
|
+
} else {
|
|
245
|
+
throw new Error("Payment failed");
|
|
116
246
|
}
|
|
117
247
|
}
|
|
118
248
|
|
|
@@ -121,14 +251,6 @@ class PayTR {
|
|
|
121
251
|
.update(data + this.merchantSalt)
|
|
122
252
|
.digest('base64');
|
|
123
253
|
}
|
|
124
|
-
|
|
125
|
-
hashCheck(data, key) {
|
|
126
|
-
let shaObj = new jsSHA("SHA-256", "TEXT");
|
|
127
|
-
shaObj.setHMACKey(this.merchantKey, "TEXT");
|
|
128
|
-
shaObj.update(data);
|
|
129
|
-
if (shaObj.getHMAC("B64") === key) return true;
|
|
130
|
-
else return false;
|
|
131
|
-
}
|
|
132
254
|
}
|
|
133
255
|
|
|
134
256
|
module.exports = PayTR;
|
package/package.json
CHANGED
package/lib/paytr_eft.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
const axios = require('axios');
|
|
3
|
-
|
|
4
|
-
class PayTR {
|
|
5
|
-
/**
|
|
6
|
-
* PayTR Kurucu Metodu
|
|
7
|
-
* @param {Object} config - Yapılandırma ayarları
|
|
8
|
-
* @param {string} config.merchantId - Mağaza No
|
|
9
|
-
* @param {string} config.merchantKey - Mağaza Parola
|
|
10
|
-
* @param {string} config.merchantSalt - Mağaza Gizli Anahtar
|
|
11
|
-
* @param {boolean} [config.testMode=false] - Test modu (true/false)
|
|
12
|
-
* @param {boolean} [config.debugOn=false] - Hata ayıklama modu
|
|
13
|
-
*/
|
|
14
|
-
constructor(config) {
|
|
15
|
-
this.config = config || {};
|
|
16
|
-
|
|
17
|
-
// Zorunlu alan kontrolü
|
|
18
|
-
const requiredFields = ['merchantId', 'merchantKey', 'merchantSalt'];
|
|
19
|
-
for (let field of requiredFields) {
|
|
20
|
-
if (!this.config[field]) throw new Error(`Missing required field: ${field}`);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
this.merchantId = this.config.merchantId;
|
|
24
|
-
this.merchantKey = this.config.merchantKey;
|
|
25
|
-
this.merchantSalt = this.config.merchantSalt;
|
|
26
|
-
this.testMode = this.config.testMode ? '1' : '0';
|
|
27
|
-
this.debugOn = this.config.debugOn ? '1' : '0';
|
|
28
|
-
|
|
29
|
-
// iframe namespace'i tanımlama (örnekteki kullanıma uygun olması için)
|
|
30
|
-
this.iframe = {
|
|
31
|
-
createToken: this.createIframeToken.bind(this)
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* iFrame Token Oluşturma (EFT ve Kredi Kartı için)
|
|
37
|
-
* @param {Object} params - Ödeme parametreleri
|
|
38
|
-
*/
|
|
39
|
-
async createIframeToken(params) {
|
|
40
|
-
try {
|
|
41
|
-
const userBasket = JSON.stringify(params.userBasket);
|
|
42
|
-
const userIp = params.userIp;
|
|
43
|
-
const merchantOid = params.merchantOid;
|
|
44
|
-
const email = params.email;
|
|
45
|
-
const paymentAmount = params.paymentAmount.toString(); // Kuruş cinsinden (Örn: 10.00 TL -> 1000)
|
|
46
|
-
const currency = params.currency || 'TL';
|
|
47
|
-
const noInstallment = params.noInstallment ? '1' : '0';
|
|
48
|
-
const maxInstallment = params.maxInstallment || '0';
|
|
49
|
-
const paymentType = params.paymentType || 'card'; // 'eft' veya 'card'
|
|
50
|
-
|
|
51
|
-
// PayTR Token Oluşturma Sırası (Dökümantasyona Göre)
|
|
52
|
-
// merchant_id + user_ip + merchant_oid + email + payment_amount + user_basket + no_installment + max_installment + currency + test_mode
|
|
53
|
-
const rawString = `${this.merchantId}${userIp}${merchantOid}${email}${paymentAmount}${userBasket}${noInstallment}${maxInstallment}${currency}${this.testMode}`;
|
|
54
|
-
|
|
55
|
-
const paytrToken = this.generateToken(rawString);
|
|
56
|
-
|
|
57
|
-
// API'ye gönderilecek veri
|
|
58
|
-
const formData = new URLSearchParams();
|
|
59
|
-
formData.append('merchant_id', this.merchantId);
|
|
60
|
-
formData.append('user_ip', userIp);
|
|
61
|
-
formData.append('merchant_oid', merchantOid);
|
|
62
|
-
formData.append('email', email);
|
|
63
|
-
formData.append('payment_amount', paymentAmount);
|
|
64
|
-
formData.append('paytr_token', paytrToken);
|
|
65
|
-
formData.append('user_basket', userBasket);
|
|
66
|
-
formData.append('debug_on', this.debugOn);
|
|
67
|
-
formData.append('no_installment', noInstallment);
|
|
68
|
-
formData.append('max_installment', maxInstallment);
|
|
69
|
-
formData.append('user_name', params.userName);
|
|
70
|
-
formData.append('user_address', params.userAddress);
|
|
71
|
-
formData.append('user_phone', params.userPhone);
|
|
72
|
-
formData.append('merchant_ok_url', params.merchantOkUrl);
|
|
73
|
-
formData.append('merchant_fail_url', params.merchantFailUrl);
|
|
74
|
-
formData.append('timeout_limit', params.timeoutLimit || '30');
|
|
75
|
-
formData.append('currency', currency);
|
|
76
|
-
formData.append('test_mode', this.testMode);
|
|
77
|
-
formData.append('payment_type', paymentType); // 'eft' gönderimi için kritik
|
|
78
|
-
|
|
79
|
-
// Opsiyonel banka seçimi (Sadece payment_type='eft' ise işe yarar)
|
|
80
|
-
if (params.bank) {
|
|
81
|
-
formData.append('bank', params.bank);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Opsiyonel: Lang
|
|
85
|
-
if (params.lang) {
|
|
86
|
-
formData.append('lang', params.lang);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const response = await axios({
|
|
90
|
-
method: 'POST',
|
|
91
|
-
url: 'https://www.paytr.com/odeme/api/get-token',
|
|
92
|
-
headers: {
|
|
93
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
94
|
-
},
|
|
95
|
-
data: formData
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
const result = response.data;
|
|
99
|
-
|
|
100
|
-
if (result.status === 'success') {
|
|
101
|
-
return {
|
|
102
|
-
status: 'success',
|
|
103
|
-
token: result.token
|
|
104
|
-
};
|
|
105
|
-
} else {
|
|
106
|
-
throw new Error(result.reason || 'PayTR Token alma hatası');
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
} catch (error) {
|
|
110
|
-
if (error.response) {
|
|
111
|
-
throw new Error(`PayTR API Error: ${error.response.data.reason || error.response.statusText}`);
|
|
112
|
-
}
|
|
113
|
-
throw error;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Callback (Bildirim) Doğrulama
|
|
119
|
-
* PayTR'den gelen POST isteğinin hash'ini doğrular.
|
|
120
|
-
* @param {Object} reqBody - Express/Http request body
|
|
121
|
-
*/
|
|
122
|
-
validateCallback(reqBody) {
|
|
123
|
-
const { merchant_oid, status, total_amount, hash } = reqBody;
|
|
124
|
-
|
|
125
|
-
// Hash Oluşturma Sırası:
|
|
126
|
-
// merchant_oid + merchant_salt + status + total_amount
|
|
127
|
-
const rawString = `${merchant_oid}${this.merchantSalt}${status}${total_amount}`;
|
|
128
|
-
const generatedHash = this.generateToken(rawString);
|
|
129
|
-
|
|
130
|
-
if (hash === generatedHash) {
|
|
131
|
-
return {
|
|
132
|
-
success: true,
|
|
133
|
-
merchantOid: merchant_oid,
|
|
134
|
-
status: status,
|
|
135
|
-
totalAmount: total_amount
|
|
136
|
-
};
|
|
137
|
-
} else {
|
|
138
|
-
return {
|
|
139
|
-
success: false,
|
|
140
|
-
error: 'Hash mismatch'
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Helper: HMAC-SHA256 Token Üretici
|
|
147
|
-
*/
|
|
148
|
-
generateToken(data) {
|
|
149
|
-
return crypto
|
|
150
|
-
.createHmac('sha256', this.merchantKey)
|
|
151
|
-
.update(data + this.merchantSalt)
|
|
152
|
-
.digest('base64');
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
module.exports = PayTR;
|