nets-service-sdk 1.0.1 → 1.1.0
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/README.md +78 -2
- package/dist/index.js +1406 -2
- package/dist/index.js.map +1 -1
- package/dist/index.modern.mjs +1 -1
- package/dist/index.modern.mjs.map +1 -1
- package/dist/index.module.js +1 -1
- package/dist/index.module.js.map +1 -1
- package/dist/index.umd.js +1 -1
- package/dist/index.umd.js.map +1 -1
- package/package.json +7 -4
package/dist/index.js
CHANGED
|
@@ -1,2 +1,1406 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
2
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
3
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
// src/utils.helper.js
|
|
7
|
+
var require_utils_helper = __commonJS({
|
|
8
|
+
"src/utils.helper.js"(exports2, module2) {
|
|
9
|
+
var { SerialPort } = require("serialport");
|
|
10
|
+
var crypto = require("crypto");
|
|
11
|
+
var _ = require("lodash");
|
|
12
|
+
module2.exports.padWithLeadingZeros = (num, totalLength) => {
|
|
13
|
+
return String(num).padStart(totalLength, "0");
|
|
14
|
+
};
|
|
15
|
+
module2.exports.xorCalculation = (value) => {
|
|
16
|
+
const splitted_arr = value.match(/[\w]{2}/g);
|
|
17
|
+
let response = "";
|
|
18
|
+
_.map(splitted_arr, (value2, i) => {
|
|
19
|
+
if (i == 0) {
|
|
20
|
+
response = value2;
|
|
21
|
+
} else {
|
|
22
|
+
response = xor(response, value2);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return response;
|
|
26
|
+
};
|
|
27
|
+
String.prototype.toHexString = function() {
|
|
28
|
+
var res = [];
|
|
29
|
+
for (var i = 0, j = this.length; i < j; ++i) {
|
|
30
|
+
var chr = this.charCodeAt(i).toString(16);
|
|
31
|
+
chr = (chr.length == 1 ? "0" : "") + chr;
|
|
32
|
+
res.push(chr);
|
|
33
|
+
}
|
|
34
|
+
return res.join("").toUpperCase();
|
|
35
|
+
};
|
|
36
|
+
function xor(hex1, hex2) {
|
|
37
|
+
const buf1 = Buffer.from(hex1, "hex");
|
|
38
|
+
const buf2 = Buffer.from(hex2, "hex");
|
|
39
|
+
const bufResult = buf1.map((b, i) => b ^ buf2[i]);
|
|
40
|
+
return bufResult.toString("hex");
|
|
41
|
+
}
|
|
42
|
+
String.prototype.toAsciiString = function() {
|
|
43
|
+
return this.match(/.{1,2}/g).map(function(v) {
|
|
44
|
+
return String.fromCharCode(parseInt(v, 16));
|
|
45
|
+
}).join("");
|
|
46
|
+
};
|
|
47
|
+
String.prototype.splitAtIndex = function(index) {
|
|
48
|
+
return [this.substring(0, index), this.substring(index)];
|
|
49
|
+
};
|
|
50
|
+
String.prototype.cleanUp = function() {
|
|
51
|
+
return this.replace(
|
|
52
|
+
/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g,
|
|
53
|
+
""
|
|
54
|
+
).trim();
|
|
55
|
+
};
|
|
56
|
+
SerialPort.prototype._disconnected = function(err) {
|
|
57
|
+
this.paused = true;
|
|
58
|
+
this.emit("disconnect", err);
|
|
59
|
+
if (this.closing) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (this.fd === null) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.closing = true;
|
|
66
|
+
if (process.platform !== "win32") {
|
|
67
|
+
this.readable = false;
|
|
68
|
+
if (this.serialPoller) {
|
|
69
|
+
this.serialPoller.close();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
SerialPortBinding.close(
|
|
73
|
+
this.fd,
|
|
74
|
+
function(err2) {
|
|
75
|
+
this.closing = false;
|
|
76
|
+
if (err2) {
|
|
77
|
+
debug("Disconnect close completed with error: ", err2);
|
|
78
|
+
}
|
|
79
|
+
this.fd = null;
|
|
80
|
+
this.emit("close");
|
|
81
|
+
}.bind(this)
|
|
82
|
+
);
|
|
83
|
+
};
|
|
84
|
+
module2.exports.generateServiceKey = (payload) => {
|
|
85
|
+
const key = `${process.env.user}:${process.env.password}`;
|
|
86
|
+
const algorithm = "aes256";
|
|
87
|
+
var cipher = crypto.createCipher(algorithm, key);
|
|
88
|
+
return cipher.update(JSON.stringify(payload), "utf8", "hex") + cipher.final("hex");
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// src/winston.js
|
|
94
|
+
var require_winston = __commonJS({
|
|
95
|
+
"src/winston.js"(exports2, module2) {
|
|
96
|
+
var { createLogger, format, transports } = require("winston");
|
|
97
|
+
var { combine, timestamp, label, printf } = format;
|
|
98
|
+
var path = require("path");
|
|
99
|
+
var getLabel = (callingModule) => {
|
|
100
|
+
if (!callingModule || !callingModule.filename) {
|
|
101
|
+
return "unknown";
|
|
102
|
+
}
|
|
103
|
+
const parts = callingModule.filename.split(path.sep);
|
|
104
|
+
return path.join(parts[parts.length - 1], parts.pop());
|
|
105
|
+
};
|
|
106
|
+
var myFormat = printf(
|
|
107
|
+
({ level, message, label: label2, timestamp: timestamp2 }) => `${timestamp2} [${label2}] ${level}: ${message}`
|
|
108
|
+
);
|
|
109
|
+
var logger = (module3) => createLogger({
|
|
110
|
+
format: combine(label({ label: getLabel(module3) }), timestamp(), myFormat),
|
|
111
|
+
transports: [
|
|
112
|
+
new transports.Console(),
|
|
113
|
+
new transports.File({
|
|
114
|
+
filename: "log/platform-access.log",
|
|
115
|
+
level: "info",
|
|
116
|
+
timestamp: true
|
|
117
|
+
}),
|
|
118
|
+
new transports.File({
|
|
119
|
+
filename: "log/platform-error.log",
|
|
120
|
+
level: "error",
|
|
121
|
+
timestamp: true
|
|
122
|
+
})
|
|
123
|
+
]
|
|
124
|
+
});
|
|
125
|
+
module2.exports = logger;
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// src/manageConfig.js
|
|
130
|
+
var require_manageConfig = __commonJS({
|
|
131
|
+
"src/manageConfig.js"(exports2, module2) {
|
|
132
|
+
var fs = require("fs");
|
|
133
|
+
var _ = require("lodash");
|
|
134
|
+
module2.exports.updateConfig = (data) => {
|
|
135
|
+
try {
|
|
136
|
+
const config = fs.readFileSync("./config.json");
|
|
137
|
+
const configJson = JSON.parse(config);
|
|
138
|
+
_.assignIn(configJson, data);
|
|
139
|
+
fs.writeFileSync("config.json", JSON.stringify(configJson, null, " "));
|
|
140
|
+
} catch (e) {
|
|
141
|
+
throw e;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
module2.exports.getConfigByKey = (key) => {
|
|
145
|
+
try {
|
|
146
|
+
const config = fs.readFileSync("./config.json");
|
|
147
|
+
const configJson = JSON.parse(config);
|
|
148
|
+
return configJson[key];
|
|
149
|
+
} catch (e) {
|
|
150
|
+
throw e;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
module2.exports.getConfig = () => {
|
|
154
|
+
try {
|
|
155
|
+
const config = fs.readFileSync("./config.json");
|
|
156
|
+
return JSON.parse(config);
|
|
157
|
+
} catch (e) {
|
|
158
|
+
throw e;
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// src/queue.js
|
|
165
|
+
var require_queue = __commonJS({
|
|
166
|
+
"src/queue.js"(exports2, module2) {
|
|
167
|
+
"use strict";
|
|
168
|
+
var Node = class {
|
|
169
|
+
constructor(data) {
|
|
170
|
+
this.data = data;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
var Queue = class {
|
|
174
|
+
constructor() {
|
|
175
|
+
this.elements = [];
|
|
176
|
+
}
|
|
177
|
+
enqueue(node) {
|
|
178
|
+
this.elements.push(node);
|
|
179
|
+
}
|
|
180
|
+
dequeue() {
|
|
181
|
+
if (this.elements.length > 0) {
|
|
182
|
+
return this.elements.shift();
|
|
183
|
+
} else {
|
|
184
|
+
return "Underflow situation";
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
isEmpty() {
|
|
188
|
+
return this.elements.length == 0;
|
|
189
|
+
}
|
|
190
|
+
front() {
|
|
191
|
+
if (this.elements.length > 0) {
|
|
192
|
+
return this.elements[0];
|
|
193
|
+
} else {
|
|
194
|
+
return "The Queue is empty!";
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
size() {
|
|
198
|
+
return this.elements.length;
|
|
199
|
+
}
|
|
200
|
+
print() {
|
|
201
|
+
return this.elements;
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
module2.exports = {
|
|
205
|
+
Node,
|
|
206
|
+
Queue
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// src/sendMessage.js
|
|
212
|
+
var require_sendMessage = __commonJS({
|
|
213
|
+
"src/sendMessage.js"(exports2, module2) {
|
|
214
|
+
var logger = require_winston()(module2);
|
|
215
|
+
module2.exports.sendMessage = (room = "clientRoom", tag, msg) => {
|
|
216
|
+
logger.log({
|
|
217
|
+
level: "info",
|
|
218
|
+
message: JSON.stringify(msg)
|
|
219
|
+
});
|
|
220
|
+
global.io.emit(tag, msg);
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// src/payment/hexRequest.js
|
|
226
|
+
var require_hexRequest = __commonJS({
|
|
227
|
+
"src/payment/hexRequest.js"(exports2, module2) {
|
|
228
|
+
var { padWithLeadingZeros, xorCalculation } = require_utils_helper();
|
|
229
|
+
var dollarsToCents = require("dollars-to-cents");
|
|
230
|
+
var _ = require("lodash");
|
|
231
|
+
var moment = require("moment");
|
|
232
|
+
module2.exports.generateStatusReq = () => {
|
|
233
|
+
const date = moment().format("DDMMYYHHmmss");
|
|
234
|
+
const paddedValue = padWithLeadingZeros(date, "12");
|
|
235
|
+
const hexValue = paddedValue.toHexString();
|
|
236
|
+
const calculated = `0018${hexValue}35353031301C03`;
|
|
237
|
+
const calculatedXor = xorCalculation(calculated);
|
|
238
|
+
const hexString = `02${calculated}` + calculatedXor;
|
|
239
|
+
const buffer = Buffer.from(hexString, "hex");
|
|
240
|
+
return { buffer, ecn: date };
|
|
241
|
+
};
|
|
242
|
+
module2.exports.generateLogonRequest = () => {
|
|
243
|
+
const date = moment().format("DDMMYYHHmmss");
|
|
244
|
+
const paddedValue = padWithLeadingZeros(date, "12");
|
|
245
|
+
const hexValue = paddedValue.toHexString();
|
|
246
|
+
const calculated = `0018${hexValue}38303031301C03`;
|
|
247
|
+
const calculatedXor = xorCalculation(calculated);
|
|
248
|
+
const hexString = `02${calculated}` + calculatedXor;
|
|
249
|
+
console.log(hexString);
|
|
250
|
+
const buffer = Buffer.from(hexString, "hex");
|
|
251
|
+
return { buffer, ecn: date };
|
|
252
|
+
};
|
|
253
|
+
module2.exports.generatePaymentRequest = (body) => {
|
|
254
|
+
const ecnPadded = padWithLeadingZeros(body.ecn, "12");
|
|
255
|
+
console.log("ecnPadded", ecnPadded);
|
|
256
|
+
const cents = dollarsToCents(body.amount);
|
|
257
|
+
console.log("cents", cents);
|
|
258
|
+
const amountPadded = padWithLeadingZeros(cents, "12").toHexString();
|
|
259
|
+
console.log("amountPadded", amountPadded);
|
|
260
|
+
const orderReference = padWithLeadingZeros(body.reference, "13").toHexString();
|
|
261
|
+
console.log("orderReference", orderReference);
|
|
262
|
+
let calculated;
|
|
263
|
+
if (body.isCreditTxn) {
|
|
264
|
+
const calculatedEcn = `${ecnPadded}I0010`.toHexString();
|
|
265
|
+
calculated = `0053${calculatedEcn}1C34300012${amountPadded}1C48440013${orderReference}1C03`;
|
|
266
|
+
} else {
|
|
267
|
+
const calculatedEcn = `${ecnPadded}30010`.toHexString();
|
|
268
|
+
calculated = `0077${calculatedEcn}1C5432000230311C34300012${amountPadded}1C343200123030303030303030303030301C48440013${orderReference}1C03`;
|
|
269
|
+
}
|
|
270
|
+
console.log("calculated", calculated);
|
|
271
|
+
const calculatedXor = xorCalculation(calculated);
|
|
272
|
+
console.log("calculatedXor", calculatedXor);
|
|
273
|
+
const hexString = `02${calculated}` + calculatedXor;
|
|
274
|
+
console.log("hexString", hexString);
|
|
275
|
+
const buffer = Buffer.from(hexString, "hex");
|
|
276
|
+
console.log("hexString", hexString);
|
|
277
|
+
console.log("buffer", buffer);
|
|
278
|
+
return { buffer, ecn: body.ecn, hexString };
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// src/payment/httpRequest.js
|
|
284
|
+
var require_httpRequest = __commonJS({
|
|
285
|
+
"src/payment/httpRequest.js"(exports2, module2) {
|
|
286
|
+
var fw = require_manageConfig();
|
|
287
|
+
var request = require("request");
|
|
288
|
+
var logger = require_winston()(module2);
|
|
289
|
+
var { generateServiceKey } = require_utils_helper();
|
|
290
|
+
module2.exports.requestCompletion = (entryId, fullfillmentDetails, paymentStatus, paymentType) => new Promise(async (resolve, reject) => {
|
|
291
|
+
try {
|
|
292
|
+
const URL = `${process.env.URL}api/v1/hooks`;
|
|
293
|
+
const payload = {
|
|
294
|
+
...fullfillmentDetails,
|
|
295
|
+
paymentType,
|
|
296
|
+
paymentStatus,
|
|
297
|
+
entryId
|
|
298
|
+
};
|
|
299
|
+
return request(
|
|
300
|
+
{
|
|
301
|
+
method: "POST",
|
|
302
|
+
uri: URL,
|
|
303
|
+
body: payload,
|
|
304
|
+
json: true,
|
|
305
|
+
headers: { "x-service-key": generateServiceKey(payload) }
|
|
306
|
+
},
|
|
307
|
+
async (err, res, body) => {
|
|
308
|
+
if (err) {
|
|
309
|
+
return reject(err);
|
|
310
|
+
}
|
|
311
|
+
if (res) {
|
|
312
|
+
return resolve(body);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
);
|
|
316
|
+
} catch (e) {
|
|
317
|
+
console.log(`getToken Service Error: ${JSON.stringify(e)}`);
|
|
318
|
+
return reject(e);
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
module2.exports.initialiseRequest = (entryId, fullfillmentDetails, paymentType) => new Promise(async (resolve, reject) => {
|
|
322
|
+
try {
|
|
323
|
+
const config = fw.getConfig();
|
|
324
|
+
const URL = `${config.cloud_url}api/v1/hooks`;
|
|
325
|
+
const payload = {
|
|
326
|
+
...fullfillmentDetails,
|
|
327
|
+
paymentType,
|
|
328
|
+
entryId,
|
|
329
|
+
update: "INIT"
|
|
330
|
+
};
|
|
331
|
+
logger.log({
|
|
332
|
+
level: "info",
|
|
333
|
+
message: `update Request: ${JSON.stringify(payload)}`
|
|
334
|
+
});
|
|
335
|
+
return request(
|
|
336
|
+
{
|
|
337
|
+
method: "POST",
|
|
338
|
+
uri: URL,
|
|
339
|
+
body: payload,
|
|
340
|
+
json: true,
|
|
341
|
+
headers: { "x-service-key": generateServiceKey(payload) }
|
|
342
|
+
},
|
|
343
|
+
async (err, res, body) => {
|
|
344
|
+
if (err) {
|
|
345
|
+
console.log(err);
|
|
346
|
+
return reject(err);
|
|
347
|
+
}
|
|
348
|
+
if (res) {
|
|
349
|
+
return resolve(body);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
);
|
|
353
|
+
} catch (e) {
|
|
354
|
+
console.log(`getToken Service Error: ${JSON.stringify(e)}`);
|
|
355
|
+
return reject(e);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// src/payment/parser.js
|
|
362
|
+
var require_parser = __commonJS({
|
|
363
|
+
"src/payment/parser.js"(exports2, module2) {
|
|
364
|
+
module2.exports.statusParser = (hexCode, ecn) => {
|
|
365
|
+
const splitted = hexCode.split("1c");
|
|
366
|
+
for (let i = 0; i < splitted.length; i++) {
|
|
367
|
+
if (splitted[i].toAsciiString().indexOf(ecn) > -1) {
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
};
|
|
373
|
+
module2.exports.logonParser = (hexCode, ecn) => {
|
|
374
|
+
try {
|
|
375
|
+
const splitted = hexCode.split("1c");
|
|
376
|
+
let status = false;
|
|
377
|
+
const json = {};
|
|
378
|
+
for (let i = 0; i < splitted.length; i++) {
|
|
379
|
+
const ascii = splitted[i].toAsciiString();
|
|
380
|
+
if (ascii.indexOf(ecn) > -1) {
|
|
381
|
+
status = true;
|
|
382
|
+
json["ECN"] = ascii.cleanUp();
|
|
383
|
+
} else {
|
|
384
|
+
const subStringArray = ascii.splitAtIndex(2);
|
|
385
|
+
if (subStringArray.length > 1) {
|
|
386
|
+
if (subStringArray[0] == "02" && subStringArray[0].indexOf("APPROVED") > -1) {
|
|
387
|
+
status = true;
|
|
388
|
+
}
|
|
389
|
+
const identifier = subStringArray[0].cleanUp();
|
|
390
|
+
const value = subStringArray[1].cleanUp();
|
|
391
|
+
if (identifier && value) {
|
|
392
|
+
json[identifier] = value;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return { status, json };
|
|
398
|
+
} catch (e) {
|
|
399
|
+
throw e;
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
module2.exports.netsPaymentParser = (hexCode, ecn) => {
|
|
403
|
+
try {
|
|
404
|
+
const splitted = hexCode.split("1c");
|
|
405
|
+
let status = false;
|
|
406
|
+
const json = {};
|
|
407
|
+
for (let i = 0; i < splitted.length; i++) {
|
|
408
|
+
const ascii = splitted[i].toAsciiString();
|
|
409
|
+
if (ascii.indexOf(ecn) > -1) {
|
|
410
|
+
json["ECN"] = ascii.cleanUp();
|
|
411
|
+
} else {
|
|
412
|
+
const subStringArray = ascii.splitAtIndex(2);
|
|
413
|
+
if (subStringArray.length > 1) {
|
|
414
|
+
const identifier = subStringArray[0].cleanUp();
|
|
415
|
+
const value = subStringArray[1].cleanUp();
|
|
416
|
+
if (identifier && value) {
|
|
417
|
+
json[identifier] = value;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return json;
|
|
423
|
+
} catch (e) {
|
|
424
|
+
throw e;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
module2.exports.creditPaymentParser = (hexCode, ecn) => {
|
|
428
|
+
try {
|
|
429
|
+
const splitted = hexCode.split("1c");
|
|
430
|
+
let status = false;
|
|
431
|
+
const json = {};
|
|
432
|
+
for (let i = 0; i < splitted.length; i++) {
|
|
433
|
+
const ascii = splitted[i].toAsciiString();
|
|
434
|
+
if (ascii.indexOf(ecn) > -1) {
|
|
435
|
+
json["ECN"] = ascii.cleanUp();
|
|
436
|
+
} else {
|
|
437
|
+
const subStringArray = ascii.splitAtIndex(2);
|
|
438
|
+
if (subStringArray.length > 1) {
|
|
439
|
+
const identifier = subStringArray[0].cleanUp();
|
|
440
|
+
const value = subStringArray[1].cleanUp();
|
|
441
|
+
if (identifier && value) {
|
|
442
|
+
json[identifier] = value;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
return json;
|
|
448
|
+
} catch (e) {
|
|
449
|
+
throw e;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
module2.exports.jsonProcessor = (obj) => {
|
|
453
|
+
const data = { ...obj };
|
|
454
|
+
const keys = Object.keys(data);
|
|
455
|
+
const translatedJson = {};
|
|
456
|
+
keys.forEach((key) => {
|
|
457
|
+
if (key == "01") {
|
|
458
|
+
translatedJson["approvalCode"] = obj[key];
|
|
459
|
+
}
|
|
460
|
+
if (key == "02") {
|
|
461
|
+
const statusResponse = statusCheck(obj[key]);
|
|
462
|
+
if (statusResponse) {
|
|
463
|
+
translatedJson.status = statusResponse.status;
|
|
464
|
+
translatedJson.description = statusResponse.detail;
|
|
465
|
+
if (statusResponse.balance) {
|
|
466
|
+
translatedJson["balance"] = statusResponse.balance;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
const ecnnError = CheckECNERROR(obj["ECN"]);
|
|
470
|
+
if (ecnnError) {
|
|
471
|
+
translatedJson.status = ecnnError.status;
|
|
472
|
+
translatedJson.description = ecnnError.detail;
|
|
473
|
+
}
|
|
474
|
+
translatedJson["responsetext"] = obj[key];
|
|
475
|
+
}
|
|
476
|
+
if (key == "03") {
|
|
477
|
+
translatedJson["date"] = obj[key].match(/.{1,2}/g) ?? [];
|
|
478
|
+
}
|
|
479
|
+
if (key == "04") {
|
|
480
|
+
translatedJson["time"] = obj[key].match(/.{1,2}/g) ?? [];
|
|
481
|
+
}
|
|
482
|
+
if (key == "16") {
|
|
483
|
+
translatedJson["terminalId"] = obj[key];
|
|
484
|
+
}
|
|
485
|
+
if (key == "30") {
|
|
486
|
+
translatedJson["cardNumber"] = obj[key];
|
|
487
|
+
}
|
|
488
|
+
if (key == "31") {
|
|
489
|
+
translatedJson["expiryDate"] = obj[key];
|
|
490
|
+
}
|
|
491
|
+
if (key == "40") {
|
|
492
|
+
translatedJson["transactionAmount"] = obj[key];
|
|
493
|
+
}
|
|
494
|
+
if (key == "41") {
|
|
495
|
+
translatedJson["serviceFee"] = obj[key];
|
|
496
|
+
}
|
|
497
|
+
if (key == "50") {
|
|
498
|
+
translatedJson["batchNumber"] = obj[key];
|
|
499
|
+
}
|
|
500
|
+
if (key == "65") {
|
|
501
|
+
translatedJson["stan"] = obj[key];
|
|
502
|
+
}
|
|
503
|
+
if (key == "A1") {
|
|
504
|
+
translatedJson["transactionId"] = obj[key];
|
|
505
|
+
}
|
|
506
|
+
if (key == "D0") {
|
|
507
|
+
translatedJson["merchantNameAndAddress"] = obj[key];
|
|
508
|
+
}
|
|
509
|
+
if (key == "D1") {
|
|
510
|
+
translatedJson["merchantId"] = obj[key];
|
|
511
|
+
}
|
|
512
|
+
if (key == "D3") {
|
|
513
|
+
translatedJson["retrievalReferenceNumber"] = obj[key];
|
|
514
|
+
}
|
|
515
|
+
if (key == "D6") {
|
|
516
|
+
translatedJson["cardHolderName"] = obj[key];
|
|
517
|
+
}
|
|
518
|
+
if (key == "D7") {
|
|
519
|
+
translatedJson["processingGateway"] = obj[key];
|
|
520
|
+
}
|
|
521
|
+
if (key == "D8") {
|
|
522
|
+
translatedJson["cardDescription"] = obj[key];
|
|
523
|
+
}
|
|
524
|
+
if (key == "CN") {
|
|
525
|
+
translatedJson["cardEntryMode"] = obj[key];
|
|
526
|
+
}
|
|
527
|
+
if (key == "L1") {
|
|
528
|
+
translatedJson["loyaltyProgramName"] = obj[key];
|
|
529
|
+
}
|
|
530
|
+
if (key == "L2") {
|
|
531
|
+
translatedJson["loyaltyType"] = obj[key];
|
|
532
|
+
}
|
|
533
|
+
if (key == "L3") {
|
|
534
|
+
translatedJson["redemptionValue"] = obj[key];
|
|
535
|
+
}
|
|
536
|
+
if (key == "L4") {
|
|
537
|
+
translatedJson["currentLoyaltyBalance"] = obj[key];
|
|
538
|
+
}
|
|
539
|
+
if (key == "L5") {
|
|
540
|
+
translatedJson["posMessages"] = obj[key];
|
|
541
|
+
}
|
|
542
|
+
if (key == "L7") {
|
|
543
|
+
translatedJson["cardName"] = obj[key];
|
|
544
|
+
}
|
|
545
|
+
if (key == "L8") {
|
|
546
|
+
translatedJson["loyaltyProgramExpDate"] = obj[key];
|
|
547
|
+
}
|
|
548
|
+
if (key == "L9") {
|
|
549
|
+
translatedJson["loyaltyMarketingMessage"] = obj[key];
|
|
550
|
+
}
|
|
551
|
+
if (key == "HC") {
|
|
552
|
+
translatedJson["hostResponseCode"] = obj[key];
|
|
553
|
+
}
|
|
554
|
+
if (key == "HD") {
|
|
555
|
+
translatedJson["enhancedECRReferenceNumber"] = obj[key];
|
|
556
|
+
}
|
|
557
|
+
if (key == "9A") {
|
|
558
|
+
translatedJson["aid-EMV"] = obj[key];
|
|
559
|
+
}
|
|
560
|
+
if (key == "9B") {
|
|
561
|
+
translatedJson["applicationProfile-EMV"] = obj[key];
|
|
562
|
+
}
|
|
563
|
+
if (key == "9C") {
|
|
564
|
+
translatedJson["cid-EMV"] = obj[key];
|
|
565
|
+
}
|
|
566
|
+
if (key == "9D") {
|
|
567
|
+
translatedJson["transactionCertificate-EMV"] = obj[key];
|
|
568
|
+
}
|
|
569
|
+
if (key == "9E") {
|
|
570
|
+
translatedJson["tsi-EMV"] = obj[key];
|
|
571
|
+
}
|
|
572
|
+
if (key == "9F") {
|
|
573
|
+
translatedJson["tvr-EMV"] = obj[key];
|
|
574
|
+
}
|
|
575
|
+
if (key == "9H") {
|
|
576
|
+
translatedJson["invoiceNumber"] = obj[key];
|
|
577
|
+
}
|
|
578
|
+
if (key == "9I") {
|
|
579
|
+
translatedJson["posId"] = obj[key];
|
|
580
|
+
}
|
|
581
|
+
if (key == "9M") {
|
|
582
|
+
translatedJson["cardType"] = obj[key];
|
|
583
|
+
}
|
|
584
|
+
if (key == "9Q") {
|
|
585
|
+
translatedJson["schemeCategory"] = obj[key];
|
|
586
|
+
}
|
|
587
|
+
if (key == "O1") {
|
|
588
|
+
translatedJson["offlineTxnType"] = obj[key];
|
|
589
|
+
}
|
|
590
|
+
if (key == "RP") {
|
|
591
|
+
translatedJson["receiptTextFormat"] = obj[key];
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
return { translated: translatedJson, raw: obj };
|
|
595
|
+
};
|
|
596
|
+
function statusCheck(data) {
|
|
597
|
+
if (data.indexOf("APPROVED") > -1) {
|
|
598
|
+
const response = { status: "APPROVED", detail: "Payment Succeed" };
|
|
599
|
+
if (data.indexOf("BAL:") > -1) {
|
|
600
|
+
const balance = data.split("BAL:");
|
|
601
|
+
response.balance = balance[1].trim();
|
|
602
|
+
}
|
|
603
|
+
return response;
|
|
604
|
+
}
|
|
605
|
+
if (data.indexOf("INVALID CARD") > -1) {
|
|
606
|
+
return { status: "INVALID_CARD", detail: "Invalid Card" };
|
|
607
|
+
}
|
|
608
|
+
if (data.indexOf("F3905-Parameter") > -1) {
|
|
609
|
+
return { status: "INVALID_CARD", detail: "Invalid Card" };
|
|
610
|
+
}
|
|
611
|
+
if (data.indexOf("DECLINED") > -1) {
|
|
612
|
+
return { status: "DECLINED", detail: "Transaction Declined" };
|
|
613
|
+
}
|
|
614
|
+
if (data.indexOf("CARD NOT SUPPORTED") > -1) {
|
|
615
|
+
return { status: "CARD_NOT_SUPPORTED", detail: "Card not supported" };
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
function CheckECNERROR(data) {
|
|
620
|
+
if (data.indexOf("US") > -1) {
|
|
621
|
+
return { status: "USER_CANCELLED", detail: "User Cancelled" };
|
|
622
|
+
}
|
|
623
|
+
if (data.indexOf("GX") > -1) {
|
|
624
|
+
return { status: "OUT_OF_PAPER", detail: "Out of Paper" };
|
|
625
|
+
}
|
|
626
|
+
if (data.indexOf("TO") > -1) {
|
|
627
|
+
return { status: "TIME_OUT", detail: "Time Out" };
|
|
628
|
+
}
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
// src/db/autoPurge.js
|
|
635
|
+
var require_autoPurge = __commonJS({
|
|
636
|
+
"src/db/autoPurge.js"(exports2, module2) {
|
|
637
|
+
var cron = require("node-cron");
|
|
638
|
+
var logger = require_winston()(module2);
|
|
639
|
+
var { cleanupOldTransactions } = require_transactionStore();
|
|
640
|
+
var scheduledTask = null;
|
|
641
|
+
function startAutoPurge(daysToKeep = 7, cronSchedule = "0 2 * * 0") {
|
|
642
|
+
if (scheduledTask) {
|
|
643
|
+
logger.log({
|
|
644
|
+
level: "warn",
|
|
645
|
+
message: "Auto-purge already running. Stopping existing task first."
|
|
646
|
+
});
|
|
647
|
+
stopAutoPurge();
|
|
648
|
+
}
|
|
649
|
+
scheduledTask = cron.schedule(cronSchedule, () => {
|
|
650
|
+
logger.log({
|
|
651
|
+
level: "info",
|
|
652
|
+
message: `Running scheduled transaction cleanup (keeping last ${daysToKeep} days)`
|
|
653
|
+
});
|
|
654
|
+
try {
|
|
655
|
+
const deletedCount = cleanupOldTransactions(daysToKeep);
|
|
656
|
+
logger.log({
|
|
657
|
+
level: "info",
|
|
658
|
+
message: `Auto-purge completed: Deleted ${deletedCount} old transactions`
|
|
659
|
+
});
|
|
660
|
+
} catch (error) {
|
|
661
|
+
logger.log({
|
|
662
|
+
level: "error",
|
|
663
|
+
message: `Auto-purge failed: ${error.message}`
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
});
|
|
667
|
+
logger.log({
|
|
668
|
+
level: "info",
|
|
669
|
+
message: `Auto-purge scheduled: ${cronSchedule} (keeping last ${daysToKeep} days)`
|
|
670
|
+
});
|
|
671
|
+
return scheduledTask;
|
|
672
|
+
}
|
|
673
|
+
function stopAutoPurge() {
|
|
674
|
+
if (scheduledTask) {
|
|
675
|
+
scheduledTask.stop();
|
|
676
|
+
scheduledTask = null;
|
|
677
|
+
logger.log({
|
|
678
|
+
level: "info",
|
|
679
|
+
message: "Auto-purge stopped"
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
function isAutoPurgeRunning() {
|
|
684
|
+
return scheduledTask !== null;
|
|
685
|
+
}
|
|
686
|
+
module2.exports = {
|
|
687
|
+
startAutoPurge,
|
|
688
|
+
stopAutoPurge,
|
|
689
|
+
isAutoPurgeRunning
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// src/db/transactionStore.js
|
|
695
|
+
var require_transactionStore = __commonJS({
|
|
696
|
+
"src/db/transactionStore.js"(exports2, module2) {
|
|
697
|
+
var Database = require("better-sqlite3");
|
|
698
|
+
var path = require("path");
|
|
699
|
+
var fs = require("fs");
|
|
700
|
+
var logger = require_winston()(module2);
|
|
701
|
+
var db = null;
|
|
702
|
+
function initDatabase(dbPath, autoPurgeConfig = {}) {
|
|
703
|
+
try {
|
|
704
|
+
const defaultPath = path.join(__dirname, "../../transactions.db");
|
|
705
|
+
const finalPath = dbPath || defaultPath;
|
|
706
|
+
const dir = path.dirname(finalPath);
|
|
707
|
+
if (!fs.existsSync(dir)) {
|
|
708
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
709
|
+
}
|
|
710
|
+
db = new Database(finalPath);
|
|
711
|
+
db.pragma("journal_mode = WAL");
|
|
712
|
+
const createTableSQL = `
|
|
713
|
+
CREATE TABLE IF NOT EXISTS transactions (
|
|
714
|
+
ecn TEXT PRIMARY KEY,
|
|
715
|
+
request_type TEXT NOT NULL,
|
|
716
|
+
request_payload TEXT,
|
|
717
|
+
request_hex TEXT,
|
|
718
|
+
request_timestamp INTEGER NOT NULL,
|
|
719
|
+
response_hex TEXT,
|
|
720
|
+
response_data TEXT,
|
|
721
|
+
response_timestamp INTEGER,
|
|
722
|
+
status TEXT DEFAULT 'PENDING',
|
|
723
|
+
created_at INTEGER NOT NULL,
|
|
724
|
+
updated_at INTEGER NOT NULL
|
|
725
|
+
)
|
|
726
|
+
`;
|
|
727
|
+
db.exec(createTableSQL);
|
|
728
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_request_timestamp ON transactions(request_timestamp DESC)");
|
|
729
|
+
db.exec("CREATE INDEX IF NOT EXISTS idx_status ON transactions(status)");
|
|
730
|
+
logger.log({
|
|
731
|
+
level: "info",
|
|
732
|
+
message: `Transaction database initialized at ${finalPath}`
|
|
733
|
+
});
|
|
734
|
+
const { enabled = true, daysToKeep = 7, schedule = "0 2 * * 0" } = autoPurgeConfig;
|
|
735
|
+
if (enabled) {
|
|
736
|
+
const autoPurge = require_autoPurge();
|
|
737
|
+
autoPurge.startAutoPurge(daysToKeep, schedule);
|
|
738
|
+
}
|
|
739
|
+
return db;
|
|
740
|
+
} catch (error) {
|
|
741
|
+
logger.log({
|
|
742
|
+
level: "error",
|
|
743
|
+
message: `Failed to initialize database: ${error.message}`
|
|
744
|
+
});
|
|
745
|
+
throw error;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
function getDatabase() {
|
|
749
|
+
if (!db) {
|
|
750
|
+
initDatabase();
|
|
751
|
+
}
|
|
752
|
+
return db;
|
|
753
|
+
}
|
|
754
|
+
function createTransaction(data) {
|
|
755
|
+
try {
|
|
756
|
+
const database = getDatabase();
|
|
757
|
+
const now = Date.now();
|
|
758
|
+
const stmt = database.prepare(`
|
|
759
|
+
INSERT INTO transactions (
|
|
760
|
+
ecn, request_type, request_payload, request_hex,
|
|
761
|
+
request_timestamp, status, created_at, updated_at
|
|
762
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
763
|
+
`);
|
|
764
|
+
const result = stmt.run(
|
|
765
|
+
data.ecn,
|
|
766
|
+
data.requestType,
|
|
767
|
+
JSON.stringify(data.requestPayload || {}),
|
|
768
|
+
data.requestHex,
|
|
769
|
+
now,
|
|
770
|
+
"PENDING",
|
|
771
|
+
now,
|
|
772
|
+
now
|
|
773
|
+
);
|
|
774
|
+
logger.log({
|
|
775
|
+
level: "info",
|
|
776
|
+
message: `Transaction created: ECN=${data.ecn}, Type=${data.requestType}`
|
|
777
|
+
});
|
|
778
|
+
return result;
|
|
779
|
+
} catch (error) {
|
|
780
|
+
logger.log({
|
|
781
|
+
level: "error",
|
|
782
|
+
message: `Failed to create transaction: ${error.message}`
|
|
783
|
+
});
|
|
784
|
+
throw error;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
function updateTransactionResponse(ecn, data) {
|
|
788
|
+
try {
|
|
789
|
+
const database = getDatabase();
|
|
790
|
+
const now = Date.now();
|
|
791
|
+
const stmt = database.prepare(`
|
|
792
|
+
UPDATE transactions
|
|
793
|
+
SET response_hex = ?,
|
|
794
|
+
response_data = ?,
|
|
795
|
+
response_timestamp = ?,
|
|
796
|
+
status = ?,
|
|
797
|
+
updated_at = ?
|
|
798
|
+
WHERE ecn = ?
|
|
799
|
+
`);
|
|
800
|
+
const result = stmt.run(
|
|
801
|
+
data.responseHex,
|
|
802
|
+
JSON.stringify(data.responseData || {}),
|
|
803
|
+
now,
|
|
804
|
+
data.status || "COMPLETED",
|
|
805
|
+
now,
|
|
806
|
+
ecn
|
|
807
|
+
);
|
|
808
|
+
logger.log({
|
|
809
|
+
level: "info",
|
|
810
|
+
message: `Transaction updated: ECN=${ecn}, Status=${data.status || "COMPLETED"}`
|
|
811
|
+
});
|
|
812
|
+
return result;
|
|
813
|
+
} catch (error) {
|
|
814
|
+
logger.log({
|
|
815
|
+
level: "error",
|
|
816
|
+
message: `Failed to update transaction: ${error.message}`
|
|
817
|
+
});
|
|
818
|
+
throw error;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
function getTransactionByEcn(ecn) {
|
|
822
|
+
try {
|
|
823
|
+
const database = getDatabase();
|
|
824
|
+
const stmt = database.prepare("SELECT * FROM transactions WHERE ecn = ?");
|
|
825
|
+
const row = stmt.get(ecn);
|
|
826
|
+
if (row) {
|
|
827
|
+
row.request_payload = JSON.parse(row.request_payload || "{}");
|
|
828
|
+
row.response_data = JSON.parse(row.response_data || "{}");
|
|
829
|
+
}
|
|
830
|
+
return row;
|
|
831
|
+
} catch (error) {
|
|
832
|
+
logger.log({
|
|
833
|
+
level: "error",
|
|
834
|
+
message: `Failed to get transaction: ${error.message}`
|
|
835
|
+
});
|
|
836
|
+
return null;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
function getRecentTransactions(limit = 100) {
|
|
840
|
+
try {
|
|
841
|
+
const database = getDatabase();
|
|
842
|
+
const stmt = database.prepare(`
|
|
843
|
+
SELECT * FROM transactions
|
|
844
|
+
ORDER BY request_timestamp DESC
|
|
845
|
+
LIMIT ?
|
|
846
|
+
`);
|
|
847
|
+
const rows = stmt.all(limit);
|
|
848
|
+
return rows.map((row) => ({
|
|
849
|
+
...row,
|
|
850
|
+
request_payload: JSON.parse(row.request_payload || "{}"),
|
|
851
|
+
response_data: JSON.parse(row.response_data || "{}")
|
|
852
|
+
}));
|
|
853
|
+
} catch (error) {
|
|
854
|
+
logger.log({
|
|
855
|
+
level: "error",
|
|
856
|
+
message: `Failed to get recent transactions: ${error.message}`
|
|
857
|
+
});
|
|
858
|
+
return [];
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
function getTransactionsByStatus(status, limit = 100) {
|
|
862
|
+
try {
|
|
863
|
+
const database = getDatabase();
|
|
864
|
+
const stmt = database.prepare(`
|
|
865
|
+
SELECT * FROM transactions
|
|
866
|
+
WHERE status = ?
|
|
867
|
+
ORDER BY request_timestamp DESC
|
|
868
|
+
LIMIT ?
|
|
869
|
+
`);
|
|
870
|
+
const rows = stmt.all(status, limit);
|
|
871
|
+
return rows.map((row) => ({
|
|
872
|
+
...row,
|
|
873
|
+
request_payload: JSON.parse(row.request_payload || "{}"),
|
|
874
|
+
response_data: JSON.parse(row.response_data || "{}")
|
|
875
|
+
}));
|
|
876
|
+
} catch (error) {
|
|
877
|
+
logger.log({
|
|
878
|
+
level: "error",
|
|
879
|
+
message: `Failed to get transactions by status: ${error.message}`
|
|
880
|
+
});
|
|
881
|
+
return [];
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
function cleanupOldTransactions(daysToKeep = 90) {
|
|
885
|
+
try {
|
|
886
|
+
const database = getDatabase();
|
|
887
|
+
const cutoffTime = Date.now() - daysToKeep * 24 * 60 * 60 * 1e3;
|
|
888
|
+
const stmt = database.prepare("DELETE FROM transactions WHERE request_timestamp < ?");
|
|
889
|
+
const result = stmt.run(cutoffTime);
|
|
890
|
+
logger.log({
|
|
891
|
+
level: "info",
|
|
892
|
+
message: `Cleaned up ${result.changes} old transactions (older than ${daysToKeep} days)`
|
|
893
|
+
});
|
|
894
|
+
return result.changes;
|
|
895
|
+
} catch (error) {
|
|
896
|
+
logger.log({
|
|
897
|
+
level: "error",
|
|
898
|
+
message: `Failed to cleanup old transactions: ${error.message}`
|
|
899
|
+
});
|
|
900
|
+
return 0;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
function closeDatabase() {
|
|
904
|
+
if (db) {
|
|
905
|
+
db.close();
|
|
906
|
+
db = null;
|
|
907
|
+
logger.log({
|
|
908
|
+
level: "info",
|
|
909
|
+
message: "Database connection closed"
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
module2.exports = {
|
|
914
|
+
initDatabase,
|
|
915
|
+
getDatabase,
|
|
916
|
+
createTransaction,
|
|
917
|
+
updateTransactionResponse,
|
|
918
|
+
getTransactionByEcn,
|
|
919
|
+
getRecentTransactions,
|
|
920
|
+
getTransactionsByStatus,
|
|
921
|
+
cleanupOldTransactions,
|
|
922
|
+
closeDatabase,
|
|
923
|
+
// Auto-purge control functions
|
|
924
|
+
startAutoPurge: require_autoPurge().startAutoPurge,
|
|
925
|
+
stopAutoPurge: require_autoPurge().stopAutoPurge,
|
|
926
|
+
isAutoPurgeRunning: require_autoPurge().isAutoPurgeRunning
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
|
|
931
|
+
// src/payment/responseHandler.js
|
|
932
|
+
var require_responseHandler = __commonJS({
|
|
933
|
+
"src/payment/responseHandler.js"(exports2, module2) {
|
|
934
|
+
var fs = require("fs");
|
|
935
|
+
var { Queue, Node } = require_queue();
|
|
936
|
+
var { sendMessage: sendMessage2 } = require_sendMessage();
|
|
937
|
+
var logger = require_winston()(module2);
|
|
938
|
+
var { requestCompletion } = require_httpRequest();
|
|
939
|
+
var {
|
|
940
|
+
statusParser,
|
|
941
|
+
logonParser,
|
|
942
|
+
netsPaymentParser,
|
|
943
|
+
creditPaymentParser,
|
|
944
|
+
jsonProcessor
|
|
945
|
+
} = require_parser();
|
|
946
|
+
var fw = require_manageConfig();
|
|
947
|
+
var { xorCalculation } = require_utils_helper();
|
|
948
|
+
var { updateTransactionResponse } = require_transactionStore();
|
|
949
|
+
var queue2 = new Queue();
|
|
950
|
+
module2.exports.terminalStatus = (ecn) => {
|
|
951
|
+
const length = queue2.size();
|
|
952
|
+
let fullHex = "";
|
|
953
|
+
for (let i = 0; i < length; i++) {
|
|
954
|
+
const node = queue2.dequeue();
|
|
955
|
+
fullHex += node.data.hex;
|
|
956
|
+
}
|
|
957
|
+
const status = statusParser(fullHex, ecn);
|
|
958
|
+
console.log(fullHex);
|
|
959
|
+
logger.log({
|
|
960
|
+
level: "info",
|
|
961
|
+
message: status
|
|
962
|
+
});
|
|
963
|
+
try {
|
|
964
|
+
updateTransactionResponse(ecn, {
|
|
965
|
+
responseHex: fullHex,
|
|
966
|
+
responseData: { status },
|
|
967
|
+
status: status ? "COMPLETED" : "FAILED"
|
|
968
|
+
});
|
|
969
|
+
} catch (error) {
|
|
970
|
+
logger.log({
|
|
971
|
+
level: "error",
|
|
972
|
+
message: `Failed to update transaction: ${error.message}`
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
if (status) {
|
|
976
|
+
fw.updateConfig({ terminal_status: true });
|
|
977
|
+
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
978
|
+
success: true,
|
|
979
|
+
request: "status_scheck",
|
|
980
|
+
action: "COMPLETED"
|
|
981
|
+
});
|
|
982
|
+
} else {
|
|
983
|
+
fw.updateConfig({ terminal_status: false });
|
|
984
|
+
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
985
|
+
success: false,
|
|
986
|
+
request: "status_scheck",
|
|
987
|
+
action: "FAILED_TO_GET_THE_STATUS"
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
module2.exports.terminalLogon = (ecn) => {
|
|
992
|
+
const length = queue2.size();
|
|
993
|
+
let fullHex = "";
|
|
994
|
+
for (let i = 0; i < length; i++) {
|
|
995
|
+
const node = queue2.dequeue();
|
|
996
|
+
fullHex += node.data.hex;
|
|
997
|
+
}
|
|
998
|
+
console.log(fullHex);
|
|
999
|
+
const response = logonParser(fullHex, ecn);
|
|
1000
|
+
try {
|
|
1001
|
+
updateTransactionResponse(ecn, {
|
|
1002
|
+
responseHex: fullHex,
|
|
1003
|
+
responseData: response,
|
|
1004
|
+
status: response.status ? "COMPLETED" : "FAILED"
|
|
1005
|
+
});
|
|
1006
|
+
} catch (error) {
|
|
1007
|
+
logger.log({
|
|
1008
|
+
level: "error",
|
|
1009
|
+
message: `Failed to update transaction: ${error.message}`
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
if (response.status) {
|
|
1013
|
+
fw.updateConfig({ last_logon: /* @__PURE__ */ new Date() });
|
|
1014
|
+
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
1015
|
+
success: true,
|
|
1016
|
+
request: "logon",
|
|
1017
|
+
action: "COMPLETED"
|
|
1018
|
+
});
|
|
1019
|
+
} else {
|
|
1020
|
+
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
1021
|
+
success: false,
|
|
1022
|
+
request: "logon",
|
|
1023
|
+
action: "FAILED_TO_LOGON"
|
|
1024
|
+
});
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
module2.exports.fetchFullDetails = () => {
|
|
1028
|
+
const length = queue2.size();
|
|
1029
|
+
let fullHex = "";
|
|
1030
|
+
for (let i = 0; i < length; i++) {
|
|
1031
|
+
const node = queue2.dequeue();
|
|
1032
|
+
fullHex += node.data.hex;
|
|
1033
|
+
}
|
|
1034
|
+
return fullHex;
|
|
1035
|
+
};
|
|
1036
|
+
module2.exports.terminalPayment = (hexValue, ecn) => {
|
|
1037
|
+
const parsedValue = netsPaymentParser(hexValue, ecn);
|
|
1038
|
+
const response = jsonProcessor(parsedValue);
|
|
1039
|
+
try {
|
|
1040
|
+
updateTransactionResponse(ecn, {
|
|
1041
|
+
responseHex: hexValue,
|
|
1042
|
+
responseData: response,
|
|
1043
|
+
status: response.translated?.status || "UNKNOWN"
|
|
1044
|
+
});
|
|
1045
|
+
} catch (error) {
|
|
1046
|
+
logger.log({
|
|
1047
|
+
level: "error",
|
|
1048
|
+
message: `Failed to update transaction: ${error.message}`
|
|
1049
|
+
});
|
|
1050
|
+
}
|
|
1051
|
+
if (response.translated) {
|
|
1052
|
+
if (response.translated.status == "APPROVED") {
|
|
1053
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1054
|
+
success: true,
|
|
1055
|
+
response,
|
|
1056
|
+
action: "COMPLETED"
|
|
1057
|
+
});
|
|
1058
|
+
}
|
|
1059
|
+
if (response.translated.status == "TIME_OUT") {
|
|
1060
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1061
|
+
success: true,
|
|
1062
|
+
response,
|
|
1063
|
+
action: "RETRY"
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
if (response.translated.status == "USER_CANCELLED" || response.translated.status == "OUT_OF_PAPER" || response.translated.status == "INVALID_CARD" || response.translated.status == "DECLINED" || response.translated.status == "CARD_NOT_SUPPORTED") {
|
|
1067
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1068
|
+
success: true,
|
|
1069
|
+
response,
|
|
1070
|
+
action: "CANCELLED"
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
module2.exports.terminalCreditPayment = (hexValue, ecn) => {
|
|
1076
|
+
const parsedValue = creditPaymentParser(hexValue, ecn);
|
|
1077
|
+
const response = jsonProcessor(parsedValue);
|
|
1078
|
+
try {
|
|
1079
|
+
updateTransactionResponse(ecn, {
|
|
1080
|
+
responseHex: hexValue,
|
|
1081
|
+
responseData: response,
|
|
1082
|
+
status: response.translated?.status || "UNKNOWN"
|
|
1083
|
+
});
|
|
1084
|
+
} catch (error) {
|
|
1085
|
+
logger.log({
|
|
1086
|
+
level: "error",
|
|
1087
|
+
message: `Failed to update transaction: ${error.message}`
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
if (response.translated) {
|
|
1091
|
+
if (response.translated.status == "APPROVED") {
|
|
1092
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1093
|
+
success: true,
|
|
1094
|
+
response,
|
|
1095
|
+
action: "COMPLETED"
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
if (response.translated.status == "TIME_OUT") {
|
|
1099
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1100
|
+
success: true,
|
|
1101
|
+
response,
|
|
1102
|
+
action: "RETRY"
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
if (response.translated.status == "USER_CANCELLED" || response.translated.status == "OUT_OF_PAPER" || response.translated.status == "INVALID_CARD" || response.translated.status == "DECLINED" || response.translated.status == "CARD_NOT_SUPPORTED") {
|
|
1106
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1107
|
+
success: true,
|
|
1108
|
+
response,
|
|
1109
|
+
action: "CANCELLED"
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
};
|
|
1114
|
+
module2.exports.verifyParser = (hex, ecn, type) => {
|
|
1115
|
+
if (type == "LOGON") {
|
|
1116
|
+
return logonParser(hex, ecn);
|
|
1117
|
+
}
|
|
1118
|
+
if (type == "PAYMENT") {
|
|
1119
|
+
const response = netsPaymentParser(hex, ecn);
|
|
1120
|
+
return jsonProcessor(response);
|
|
1121
|
+
}
|
|
1122
|
+
if (type == "CREDIT_PAYMENT") {
|
|
1123
|
+
const response = creditPaymentParser(hex, ecn);
|
|
1124
|
+
return jsonProcessor(response);
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
module2.exports.requestParser = (data, request, ecn) => {
|
|
1128
|
+
try {
|
|
1129
|
+
const hexValue = data.toString("hex");
|
|
1130
|
+
queue2.enqueue(new Node({ hex: hexValue, alpha: hexValue.toAsciiString() }));
|
|
1131
|
+
} catch (e) {
|
|
1132
|
+
}
|
|
1133
|
+
};
|
|
1134
|
+
module2.exports.checkLrc = (hexValue) => {
|
|
1135
|
+
const value = hexValue.substring(2, hexValue.length - 2);
|
|
1136
|
+
const xor = xorCalculation(value);
|
|
1137
|
+
const lrcCheck = hexValue.substring(hexValue.length - 2, hexValue.length);
|
|
1138
|
+
console.log(`LRC Check: ${xor} === ${lrcCheck}`);
|
|
1139
|
+
return xor == lrcCheck;
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
|
|
1144
|
+
// src/payment/communication.js
|
|
1145
|
+
var require_communication = __commonJS({
|
|
1146
|
+
"src/payment/communication.js"(exports2, module2) {
|
|
1147
|
+
var { SerialPort, ReadlineParser } = require("serialport");
|
|
1148
|
+
var {
|
|
1149
|
+
generateStatusReq,
|
|
1150
|
+
generateLogonRequest,
|
|
1151
|
+
generatePaymentRequest
|
|
1152
|
+
} = require_hexRequest();
|
|
1153
|
+
var logger = require_winston()(module2);
|
|
1154
|
+
var {
|
|
1155
|
+
requestParser,
|
|
1156
|
+
terminalPayment,
|
|
1157
|
+
terminalCreditPayment,
|
|
1158
|
+
terminalLogon,
|
|
1159
|
+
terminalStatus,
|
|
1160
|
+
fetchFullDetails,
|
|
1161
|
+
checkLrc,
|
|
1162
|
+
verifyParser
|
|
1163
|
+
} = require_responseHandler();
|
|
1164
|
+
var { sendMessage: sendMessage2 } = require_sendMessage();
|
|
1165
|
+
var fw = require_manageConfig();
|
|
1166
|
+
var { createTransaction } = require_transactionStore();
|
|
1167
|
+
var { initialiseRequest } = require_httpRequest();
|
|
1168
|
+
var requestType;
|
|
1169
|
+
var calculated;
|
|
1170
|
+
var isTimeOutset = false;
|
|
1171
|
+
var MAX_COUNT = 3;
|
|
1172
|
+
var WRONG_LRC_MAX_COUNT = 3;
|
|
1173
|
+
var count;
|
|
1174
|
+
var lrcCount;
|
|
1175
|
+
var ackOrNack;
|
|
1176
|
+
var requestPayload;
|
|
1177
|
+
var ACK = Buffer.alloc(1, 6, "hex");
|
|
1178
|
+
var NACK = Buffer.alloc(1, 21, "hex");
|
|
1179
|
+
var config = fw.getConfig();
|
|
1180
|
+
var triggerTimer = () => {
|
|
1181
|
+
if (requestType == "STATUS_CHECK") {
|
|
1182
|
+
setTimeout(() => {
|
|
1183
|
+
isTimeOutset = false;
|
|
1184
|
+
terminalStatus(calculated.ecn);
|
|
1185
|
+
}, 3e3);
|
|
1186
|
+
}
|
|
1187
|
+
if (requestType == "LOGON") {
|
|
1188
|
+
setTimeout(() => {
|
|
1189
|
+
isTimeOutset = false;
|
|
1190
|
+
terminalLogon(calculated.ecn);
|
|
1191
|
+
}, 5e3);
|
|
1192
|
+
}
|
|
1193
|
+
if (requestType == "PAYMENT") {
|
|
1194
|
+
setTimeout(() => {
|
|
1195
|
+
isTimeOutset = false;
|
|
1196
|
+
lrcCount--;
|
|
1197
|
+
if (lrcCount >= 0) {
|
|
1198
|
+
const hexValue = fetchFullDetails();
|
|
1199
|
+
const lrcResponse = checkLrc(hexValue);
|
|
1200
|
+
if (!lrcResponse) {
|
|
1201
|
+
logger.log({
|
|
1202
|
+
level: "info",
|
|
1203
|
+
message: `Terminal send response data with wrong LRC, POS expected to send NACK`
|
|
1204
|
+
});
|
|
1205
|
+
global.port.write(NACK);
|
|
1206
|
+
} else {
|
|
1207
|
+
logger.log({
|
|
1208
|
+
level: "info",
|
|
1209
|
+
message: `Terminal resend response data with correct LRC, POS expected to send ACK`
|
|
1210
|
+
});
|
|
1211
|
+
global.port.write(ACK);
|
|
1212
|
+
terminalPayment(hexValue, calculated.ecn);
|
|
1213
|
+
}
|
|
1214
|
+
} else {
|
|
1215
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1216
|
+
success: true,
|
|
1217
|
+
action: "APPROVED_FLAGGED"
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
}, 8e3);
|
|
1221
|
+
}
|
|
1222
|
+
if (requestType === "CREDIT_PAYMENT") {
|
|
1223
|
+
setTimeout(() => {
|
|
1224
|
+
isTimeOutset = false;
|
|
1225
|
+
const hexValue = fetchFullDetails();
|
|
1226
|
+
const lrcResponse = checkLrc(hexValue);
|
|
1227
|
+
if (!lrcResponse) {
|
|
1228
|
+
global.port.write(NACK);
|
|
1229
|
+
} else {
|
|
1230
|
+
global.port.write(ACK);
|
|
1231
|
+
terminalCreditPayment(hexValue, calculated.ecn);
|
|
1232
|
+
}
|
|
1233
|
+
}, 8e3);
|
|
1234
|
+
}
|
|
1235
|
+
};
|
|
1236
|
+
module2.exports.reset = () => {
|
|
1237
|
+
count = MAX_COUNT;
|
|
1238
|
+
lrcCount = WRONG_LRC_MAX_COUNT;
|
|
1239
|
+
ackOrNack = null;
|
|
1240
|
+
};
|
|
1241
|
+
module2.exports.checkACKorNACK = () => {
|
|
1242
|
+
setTimeout(() => {
|
|
1243
|
+
if (ackOrNack == null) {
|
|
1244
|
+
count--;
|
|
1245
|
+
if (count >= 0) {
|
|
1246
|
+
logger.log({
|
|
1247
|
+
level: "info",
|
|
1248
|
+
message: `POS send Purchase command, Terminal doesn't send any reply`
|
|
1249
|
+
});
|
|
1250
|
+
resendData();
|
|
1251
|
+
} else {
|
|
1252
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1253
|
+
success: true,
|
|
1254
|
+
action: "TERMINAL_ERROR"
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}, 2e3);
|
|
1259
|
+
};
|
|
1260
|
+
var resendData = () => {
|
|
1261
|
+
if (requestType == "STATUS_CHECK") {
|
|
1262
|
+
calculated = generateStatusReq();
|
|
1263
|
+
}
|
|
1264
|
+
if (requestType == "LOGON") {
|
|
1265
|
+
calculated = generateLogonRequest();
|
|
1266
|
+
}
|
|
1267
|
+
if (requestType == "PAYMENT" || requestType === "CREDIT_PAYMENT") {
|
|
1268
|
+
calculated = generatePaymentRequest(requestPayload);
|
|
1269
|
+
}
|
|
1270
|
+
global.port.write(calculated.buffer);
|
|
1271
|
+
exports2.checkACKorNACK();
|
|
1272
|
+
};
|
|
1273
|
+
module2.exports.initialize = async () => {
|
|
1274
|
+
if (!config.terminal || config.terminal === "enable") {
|
|
1275
|
+
global.port = new SerialPort({
|
|
1276
|
+
path: config.simulation ? config.simulationPort : config.com,
|
|
1277
|
+
baudRate: 9600,
|
|
1278
|
+
dataBits: 8,
|
|
1279
|
+
stopBits: 1,
|
|
1280
|
+
parity: "none"
|
|
1281
|
+
});
|
|
1282
|
+
global.port.on("data", (data) => {
|
|
1283
|
+
console.log(data);
|
|
1284
|
+
if (data.toString("hex").length == 2 && data.toString("hex") == "15") {
|
|
1285
|
+
count--;
|
|
1286
|
+
ackOrNack = data;
|
|
1287
|
+
if (count >= 0) {
|
|
1288
|
+
logger.log({
|
|
1289
|
+
level: "info",
|
|
1290
|
+
message: `POS send Purchase command, Terminal reply NACK`
|
|
1291
|
+
});
|
|
1292
|
+
resendData();
|
|
1293
|
+
} else {
|
|
1294
|
+
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1295
|
+
success: true,
|
|
1296
|
+
action: "TERMINAL_ERROR"
|
|
1297
|
+
});
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
} else if (data.toString("hex").length == 2 && data.toString("hex") == "06") {
|
|
1301
|
+
ackOrNack = data;
|
|
1302
|
+
} else if (data.length > 2) {
|
|
1303
|
+
global.port.write(ACK);
|
|
1304
|
+
if (!isTimeOutset) {
|
|
1305
|
+
isTimeOutset = true;
|
|
1306
|
+
triggerTimer();
|
|
1307
|
+
}
|
|
1308
|
+
requestParser(data, requestType, calculated?.ecn);
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
};
|
|
1313
|
+
module2.exports.sentToTerminal = (type, body) => {
|
|
1314
|
+
requestType = type;
|
|
1315
|
+
requestPayload = body;
|
|
1316
|
+
if (requestType == "STATUS_CHECK") {
|
|
1317
|
+
calculated = generateStatusReq();
|
|
1318
|
+
try {
|
|
1319
|
+
createTransaction({
|
|
1320
|
+
ecn: calculated.ecn,
|
|
1321
|
+
requestType,
|
|
1322
|
+
requestPayload: null,
|
|
1323
|
+
requestHex: calculated.buffer.toString("hex")
|
|
1324
|
+
});
|
|
1325
|
+
} catch (error) {
|
|
1326
|
+
logger.log({
|
|
1327
|
+
level: "error",
|
|
1328
|
+
message: `Failed to store transaction: ${error.message}`
|
|
1329
|
+
});
|
|
1330
|
+
}
|
|
1331
|
+
global.port.write(calculated.buffer);
|
|
1332
|
+
}
|
|
1333
|
+
if (requestType == "LOGON") {
|
|
1334
|
+
calculated = generateLogonRequest();
|
|
1335
|
+
try {
|
|
1336
|
+
createTransaction({
|
|
1337
|
+
ecn: calculated.ecn,
|
|
1338
|
+
requestType,
|
|
1339
|
+
requestPayload: null,
|
|
1340
|
+
requestHex: calculated.buffer.toString("hex")
|
|
1341
|
+
});
|
|
1342
|
+
} catch (error) {
|
|
1343
|
+
logger.log({
|
|
1344
|
+
level: "error",
|
|
1345
|
+
message: `Failed to store transaction: ${error.message}`
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
global.port.write(calculated.buffer);
|
|
1349
|
+
}
|
|
1350
|
+
if (requestType == "PAYMENT" || requestType == "CREDIT_PAYMENT") {
|
|
1351
|
+
exports2.reset();
|
|
1352
|
+
calculated = generatePaymentRequest(body);
|
|
1353
|
+
console.log(calculated);
|
|
1354
|
+
try {
|
|
1355
|
+
createTransaction({
|
|
1356
|
+
ecn: calculated.ecn,
|
|
1357
|
+
requestType,
|
|
1358
|
+
requestPayload: body,
|
|
1359
|
+
requestHex: calculated.hexString
|
|
1360
|
+
});
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
logger.log({
|
|
1363
|
+
level: "error",
|
|
1364
|
+
message: `Failed to store transaction: ${error.message}`
|
|
1365
|
+
});
|
|
1366
|
+
}
|
|
1367
|
+
global.port.write(calculated.buffer);
|
|
1368
|
+
exports2.checkACKorNACK();
|
|
1369
|
+
}
|
|
1370
|
+
};
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
// src/index.js
|
|
1375
|
+
var utilsHelper = require_utils_helper();
|
|
1376
|
+
var winston = require_winston();
|
|
1377
|
+
var manageConfig = require_manageConfig();
|
|
1378
|
+
var queue = require_queue();
|
|
1379
|
+
var sendMessage = require_sendMessage();
|
|
1380
|
+
var communication = require_communication();
|
|
1381
|
+
var hexRequest = require_hexRequest();
|
|
1382
|
+
var httpRequest = require_httpRequest();
|
|
1383
|
+
var parser = require_parser();
|
|
1384
|
+
var responseHandler = require_responseHandler();
|
|
1385
|
+
var transactionStore = require_transactionStore();
|
|
1386
|
+
module.exports = {
|
|
1387
|
+
...utilsHelper,
|
|
1388
|
+
logger: winston,
|
|
1389
|
+
manageConfig,
|
|
1390
|
+
queue,
|
|
1391
|
+
sendMessage,
|
|
1392
|
+
communication,
|
|
1393
|
+
hexRequest,
|
|
1394
|
+
httpRequest,
|
|
1395
|
+
parser,
|
|
1396
|
+
responseHandler,
|
|
1397
|
+
// Transaction store utilities
|
|
1398
|
+
getTransactionByEcn: transactionStore.getTransactionByEcn,
|
|
1399
|
+
getRecentTransactions: transactionStore.getRecentTransactions,
|
|
1400
|
+
getTransactionsByStatus: transactionStore.getTransactionsByStatus,
|
|
1401
|
+
cleanupOldTransactions: transactionStore.cleanupOldTransactions,
|
|
1402
|
+
// Auto-purge control
|
|
1403
|
+
startAutoPurge: transactionStore.startAutoPurge,
|
|
1404
|
+
stopAutoPurge: transactionStore.stopAutoPurge,
|
|
1405
|
+
isAutoPurgeRunning: transactionStore.isAutoPurgeRunning
|
|
1406
|
+
};
|