nets-service-sdk 1.1.8 → 1.1.10
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/dist/index.js +253 -694
- package/package.json +12 -3
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ var require_utils_helper = __commonJS({
|
|
|
22
22
|
response = xor(response, value2);
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
|
-
return response;
|
|
25
|
+
return response.toUpperCase();
|
|
26
26
|
};
|
|
27
27
|
String.prototype.toHexString = function() {
|
|
28
28
|
var res = [];
|
|
@@ -374,29 +374,32 @@ var require_httpRequest = __commonJS({
|
|
|
374
374
|
// src/payment/parser.js
|
|
375
375
|
var require_parser = __commonJS({
|
|
376
376
|
"src/payment/parser.js"(exports2, module2) {
|
|
377
|
+
var logger = require_winston()(module2);
|
|
377
378
|
module2.exports.statusParser = (hexCode, ecn) => {
|
|
378
|
-
const splitted = hexCode.split("
|
|
379
|
+
const splitted = hexCode.split("1C");
|
|
379
380
|
for (let i = 0; i < splitted.length; i++) {
|
|
380
|
-
if (splitted[i]
|
|
381
|
+
if (splitted[i]?.toAsciiString()?.indexOf(ecn) > -1) {
|
|
382
|
+
logger.log({ level: "info", message: `statusParser: ECN ${ecn} found in response` });
|
|
381
383
|
return true;
|
|
382
384
|
}
|
|
383
385
|
}
|
|
386
|
+
logger.log({ level: "info", message: `statusParser: ECN ${ecn} NOT found in response` });
|
|
384
387
|
return false;
|
|
385
388
|
};
|
|
386
389
|
module2.exports.logonParser = (hexCode, ecn) => {
|
|
387
390
|
try {
|
|
388
|
-
const splitted = hexCode.split("
|
|
391
|
+
const splitted = hexCode.split("1C");
|
|
389
392
|
let status = false;
|
|
390
393
|
const json = {};
|
|
391
394
|
for (let i = 0; i < splitted.length; i++) {
|
|
392
395
|
const ascii = splitted[i].toAsciiString();
|
|
393
|
-
if (ascii
|
|
396
|
+
if (ascii?.indexOf(ecn) > -1) {
|
|
394
397
|
status = true;
|
|
395
398
|
json["ECN"] = ascii.cleanUp();
|
|
396
399
|
} else {
|
|
397
400
|
const subStringArray = ascii.splitAtIndex(2);
|
|
398
|
-
if (subStringArray.length >
|
|
399
|
-
if (subStringArray[0] == "02" && subStringArray[0]
|
|
401
|
+
if (subStringArray.length > 0) {
|
|
402
|
+
if (subStringArray.length > 2 && subStringArray[0] == "02" && subStringArray[0]?.indexOf("APPROVED") > -1) {
|
|
400
403
|
status = true;
|
|
401
404
|
}
|
|
402
405
|
const identifier = subStringArray[0].cleanUp();
|
|
@@ -407,20 +410,25 @@ var require_parser = __commonJS({
|
|
|
407
410
|
}
|
|
408
411
|
}
|
|
409
412
|
}
|
|
413
|
+
logger.log({ level: "info", message: `logonParser completed for ECN ${ecn}: status ${status}` });
|
|
410
414
|
return { status, json };
|
|
411
415
|
} catch (e) {
|
|
416
|
+
logger.log({ level: "error", message: `Error in logonParser: ${e.message}`, stack: e.stack });
|
|
412
417
|
throw e;
|
|
413
418
|
}
|
|
414
419
|
};
|
|
415
420
|
module2.exports.netsPaymentParser = (hexCode, ecn) => {
|
|
416
421
|
try {
|
|
417
|
-
const splitted = hexCode.split("
|
|
422
|
+
const splitted = hexCode.split("1C");
|
|
418
423
|
let status = false;
|
|
419
424
|
const json = {};
|
|
420
425
|
for (let i = 0; i < splitted.length; i++) {
|
|
421
426
|
const ascii = splitted[i].toAsciiString();
|
|
422
|
-
if (ascii
|
|
427
|
+
if (ascii?.indexOf(ecn) > -1 || i === 0 && ascii.length >= 12) {
|
|
423
428
|
json["ECN"] = ascii.cleanUp();
|
|
429
|
+
if (ascii?.indexOf(ecn) === -1) {
|
|
430
|
+
logger.warn({ level: "info", message: `ECN match mismatch but segment identified: expected ${ecn} in ${ascii}` });
|
|
431
|
+
}
|
|
424
432
|
} else {
|
|
425
433
|
const subStringArray = ascii.splitAtIndex(2);
|
|
426
434
|
if (subStringArray.length > 1) {
|
|
@@ -432,19 +440,21 @@ var require_parser = __commonJS({
|
|
|
432
440
|
}
|
|
433
441
|
}
|
|
434
442
|
}
|
|
443
|
+
logger.log({ level: "info", message: `netsPaymentParser completed for ECN ${ecn}` });
|
|
435
444
|
return json;
|
|
436
445
|
} catch (e) {
|
|
446
|
+
logger.log({ level: "error", message: `Error in netsPaymentParser: ${e.message}`, stack: e.stack });
|
|
437
447
|
throw e;
|
|
438
448
|
}
|
|
439
449
|
};
|
|
440
450
|
module2.exports.creditPaymentParser = (hexCode, ecn) => {
|
|
441
451
|
try {
|
|
442
|
-
const splitted = hexCode.split("
|
|
452
|
+
const splitted = hexCode.split("1C");
|
|
443
453
|
let status = false;
|
|
444
454
|
const json = {};
|
|
445
455
|
for (let i = 0; i < splitted.length; i++) {
|
|
446
456
|
const ascii = splitted[i].toAsciiString();
|
|
447
|
-
if (ascii
|
|
457
|
+
if (ascii?.indexOf(ecn) > -1 || i === 0 && ascii.length >= 12) {
|
|
448
458
|
json["ECN"] = ascii.cleanUp();
|
|
449
459
|
} else {
|
|
450
460
|
const subStringArray = ascii.splitAtIndex(2);
|
|
@@ -457,8 +467,10 @@ var require_parser = __commonJS({
|
|
|
457
467
|
}
|
|
458
468
|
}
|
|
459
469
|
}
|
|
470
|
+
logger.log({ level: "info", message: `creditPaymentParser completed for ECN ${ecn}` });
|
|
460
471
|
return json;
|
|
461
472
|
} catch (e) {
|
|
473
|
+
logger.log({ level: "error", message: `Error in creditPaymentParser: ${e.message}`, stack: e.stack });
|
|
462
474
|
throw e;
|
|
463
475
|
}
|
|
464
476
|
};
|
|
@@ -604,39 +616,40 @@ var require_parser = __commonJS({
|
|
|
604
616
|
translatedJson["receiptTextFormat"] = obj[key];
|
|
605
617
|
}
|
|
606
618
|
});
|
|
619
|
+
logger.log({ level: "info", message: "jsonProcessor translated terminal response into JSON format." });
|
|
607
620
|
return { translated: translatedJson, raw: obj };
|
|
608
621
|
};
|
|
609
622
|
function statusCheck(data) {
|
|
610
|
-
if (data
|
|
623
|
+
if (data?.indexOf("APPROVED") > -1) {
|
|
611
624
|
const response = { status: "APPROVED", detail: "Payment Succeed" };
|
|
612
|
-
if (data
|
|
625
|
+
if (data?.indexOf("BAL:") > -1) {
|
|
613
626
|
const balance = data.split("BAL:");
|
|
614
627
|
response.balance = balance[1].trim();
|
|
615
628
|
}
|
|
616
629
|
return response;
|
|
617
630
|
}
|
|
618
|
-
if (data
|
|
631
|
+
if (data?.indexOf("INVALID CARD") > -1) {
|
|
619
632
|
return { status: "INVALID_CARD", detail: "Invalid Card" };
|
|
620
633
|
}
|
|
621
|
-
if (data
|
|
634
|
+
if (data?.indexOf("F3905-Parameter") > -1) {
|
|
622
635
|
return { status: "INVALID_CARD", detail: "Invalid Card" };
|
|
623
636
|
}
|
|
624
|
-
if (data
|
|
637
|
+
if (data?.indexOf("DECLINED") > -1) {
|
|
625
638
|
return { status: "DECLINED", detail: "Transaction Declined" };
|
|
626
639
|
}
|
|
627
|
-
if (data
|
|
640
|
+
if (data?.indexOf("CARD NOT SUPPORTED") > -1) {
|
|
628
641
|
return { status: "CARD_NOT_SUPPORTED", detail: "Card not supported" };
|
|
629
642
|
}
|
|
630
643
|
return null;
|
|
631
644
|
}
|
|
632
645
|
function CheckECNERROR(data) {
|
|
633
|
-
if (data
|
|
646
|
+
if (data?.indexOf("US") > -1) {
|
|
634
647
|
return { status: "USER_CANCELLED", detail: "User Cancelled" };
|
|
635
648
|
}
|
|
636
|
-
if (data
|
|
649
|
+
if (data?.indexOf("GX") > -1) {
|
|
637
650
|
return { status: "OUT_OF_PAPER", detail: "Out of Paper" };
|
|
638
651
|
}
|
|
639
|
-
if (data
|
|
652
|
+
if (data?.indexOf("TO") > -1) {
|
|
640
653
|
return { status: "TIME_OUT", detail: "Time Out" };
|
|
641
654
|
}
|
|
642
655
|
return null;
|
|
@@ -644,303 +657,6 @@ var require_parser = __commonJS({
|
|
|
644
657
|
}
|
|
645
658
|
});
|
|
646
659
|
|
|
647
|
-
// src/db/autoPurge.js
|
|
648
|
-
var require_autoPurge = __commonJS({
|
|
649
|
-
"src/db/autoPurge.js"(exports2, module2) {
|
|
650
|
-
var cron = require("node-cron");
|
|
651
|
-
var logger = require_winston()(module2);
|
|
652
|
-
var { cleanupOldTransactions } = require_transactionStore();
|
|
653
|
-
var scheduledTask = null;
|
|
654
|
-
function startAutoPurge(daysToKeep = 7, cronSchedule = "0 2 * * 0") {
|
|
655
|
-
if (scheduledTask) {
|
|
656
|
-
logger.log({
|
|
657
|
-
level: "warn",
|
|
658
|
-
message: "Auto-purge already running. Stopping existing task first."
|
|
659
|
-
});
|
|
660
|
-
stopAutoPurge();
|
|
661
|
-
}
|
|
662
|
-
scheduledTask = cron.schedule(cronSchedule, () => {
|
|
663
|
-
logger.log({
|
|
664
|
-
level: "info",
|
|
665
|
-
message: `Running scheduled transaction cleanup (keeping last ${daysToKeep} days)`
|
|
666
|
-
});
|
|
667
|
-
try {
|
|
668
|
-
const deletedCount = cleanupOldTransactions(daysToKeep);
|
|
669
|
-
logger.log({
|
|
670
|
-
level: "info",
|
|
671
|
-
message: `Auto-purge completed: Deleted ${deletedCount} old transactions`
|
|
672
|
-
});
|
|
673
|
-
} catch (error) {
|
|
674
|
-
logger.log({
|
|
675
|
-
level: "error",
|
|
676
|
-
message: `Auto-purge failed: ${error.message}`
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
|
-
});
|
|
680
|
-
logger.log({
|
|
681
|
-
level: "info",
|
|
682
|
-
message: `Auto-purge scheduled: ${cronSchedule} (keeping last ${daysToKeep} days)`
|
|
683
|
-
});
|
|
684
|
-
return scheduledTask;
|
|
685
|
-
}
|
|
686
|
-
function stopAutoPurge() {
|
|
687
|
-
if (scheduledTask) {
|
|
688
|
-
scheduledTask.stop();
|
|
689
|
-
scheduledTask = null;
|
|
690
|
-
logger.log({
|
|
691
|
-
level: "info",
|
|
692
|
-
message: "Auto-purge stopped"
|
|
693
|
-
});
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
function isAutoPurgeRunning() {
|
|
697
|
-
return scheduledTask !== null;
|
|
698
|
-
}
|
|
699
|
-
module2.exports = {
|
|
700
|
-
startAutoPurge,
|
|
701
|
-
stopAutoPurge,
|
|
702
|
-
isAutoPurgeRunning
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
});
|
|
706
|
-
|
|
707
|
-
// src/db/transactionStore.js
|
|
708
|
-
var require_transactionStore = __commonJS({
|
|
709
|
-
"src/db/transactionStore.js"(exports2, module2) {
|
|
710
|
-
var Database = require("better-sqlite3");
|
|
711
|
-
var path = require("path");
|
|
712
|
-
var fs = require("fs");
|
|
713
|
-
var logger = require_winston()(module2);
|
|
714
|
-
var db = null;
|
|
715
|
-
function initDatabase(dbPath, autoPurgeConfig = {}) {
|
|
716
|
-
try {
|
|
717
|
-
const defaultPath = path.join(__dirname, "../../transactions.db");
|
|
718
|
-
const finalPath = dbPath || defaultPath;
|
|
719
|
-
const dir = path.dirname(finalPath);
|
|
720
|
-
if (!fs.existsSync(dir)) {
|
|
721
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
722
|
-
}
|
|
723
|
-
db = new Database(finalPath);
|
|
724
|
-
db.pragma("journal_mode = WAL");
|
|
725
|
-
const createTableSQL = `
|
|
726
|
-
CREATE TABLE IF NOT EXISTS transactions (
|
|
727
|
-
ecn TEXT PRIMARY KEY,
|
|
728
|
-
request_type TEXT NOT NULL,
|
|
729
|
-
request_payload TEXT,
|
|
730
|
-
request_hex TEXT,
|
|
731
|
-
request_timestamp INTEGER NOT NULL,
|
|
732
|
-
response_hex TEXT,
|
|
733
|
-
response_data TEXT,
|
|
734
|
-
response_timestamp INTEGER,
|
|
735
|
-
status TEXT DEFAULT 'PENDING',
|
|
736
|
-
created_at INTEGER NOT NULL,
|
|
737
|
-
updated_at INTEGER NOT NULL
|
|
738
|
-
)
|
|
739
|
-
`;
|
|
740
|
-
db.exec(createTableSQL);
|
|
741
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_request_timestamp ON transactions(request_timestamp DESC)");
|
|
742
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_status ON transactions(status)");
|
|
743
|
-
logger.log({
|
|
744
|
-
level: "info",
|
|
745
|
-
message: `Transaction database initialized at ${finalPath}`
|
|
746
|
-
});
|
|
747
|
-
const { enabled = true, daysToKeep = 7, schedule = "0 2 * * 0" } = autoPurgeConfig;
|
|
748
|
-
if (enabled) {
|
|
749
|
-
const autoPurge = require_autoPurge();
|
|
750
|
-
autoPurge.startAutoPurge(daysToKeep, schedule);
|
|
751
|
-
}
|
|
752
|
-
return db;
|
|
753
|
-
} catch (error) {
|
|
754
|
-
logger.log({
|
|
755
|
-
level: "error",
|
|
756
|
-
message: `Failed to initialize database: ${error.message}`
|
|
757
|
-
});
|
|
758
|
-
throw error;
|
|
759
|
-
}
|
|
760
|
-
}
|
|
761
|
-
function getDatabase() {
|
|
762
|
-
if (!db) {
|
|
763
|
-
initDatabase();
|
|
764
|
-
}
|
|
765
|
-
return db;
|
|
766
|
-
}
|
|
767
|
-
function createTransaction(data) {
|
|
768
|
-
try {
|
|
769
|
-
const database = getDatabase();
|
|
770
|
-
const now = Date.now();
|
|
771
|
-
const stmt = database.prepare(`
|
|
772
|
-
INSERT INTO transactions (
|
|
773
|
-
ecn, request_type, request_payload, request_hex,
|
|
774
|
-
request_timestamp, status, created_at, updated_at
|
|
775
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
776
|
-
`);
|
|
777
|
-
const result = stmt.run(
|
|
778
|
-
data.ecn,
|
|
779
|
-
data.requestType,
|
|
780
|
-
JSON.stringify(data.requestPayload || {}),
|
|
781
|
-
data.requestHex,
|
|
782
|
-
now,
|
|
783
|
-
"PENDING",
|
|
784
|
-
now,
|
|
785
|
-
now
|
|
786
|
-
);
|
|
787
|
-
logger.log({
|
|
788
|
-
level: "info",
|
|
789
|
-
message: `Transaction created: ECN=${data.ecn}, Type=${data.requestType}`
|
|
790
|
-
});
|
|
791
|
-
return result;
|
|
792
|
-
} catch (error) {
|
|
793
|
-
logger.log({
|
|
794
|
-
level: "error",
|
|
795
|
-
message: `Failed to create transaction: ${error.message}`
|
|
796
|
-
});
|
|
797
|
-
throw error;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
function updateTransactionResponse(ecn, data) {
|
|
801
|
-
try {
|
|
802
|
-
const database = getDatabase();
|
|
803
|
-
const now = Date.now();
|
|
804
|
-
const stmt = database.prepare(`
|
|
805
|
-
UPDATE transactions
|
|
806
|
-
SET response_hex = ?,
|
|
807
|
-
response_data = ?,
|
|
808
|
-
response_timestamp = ?,
|
|
809
|
-
status = ?,
|
|
810
|
-
updated_at = ?
|
|
811
|
-
WHERE ecn = ?
|
|
812
|
-
`);
|
|
813
|
-
const result = stmt.run(
|
|
814
|
-
data.responseHex,
|
|
815
|
-
JSON.stringify(data.responseData || {}),
|
|
816
|
-
now,
|
|
817
|
-
data.status || "COMPLETED",
|
|
818
|
-
now,
|
|
819
|
-
ecn
|
|
820
|
-
);
|
|
821
|
-
logger.log({
|
|
822
|
-
level: "info",
|
|
823
|
-
message: `Transaction updated: ECN=${ecn}, Status=${data.status || "COMPLETED"}`
|
|
824
|
-
});
|
|
825
|
-
return result;
|
|
826
|
-
} catch (error) {
|
|
827
|
-
logger.log({
|
|
828
|
-
level: "error",
|
|
829
|
-
message: `Failed to update transaction: ${error.message}`
|
|
830
|
-
});
|
|
831
|
-
throw error;
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
function getTransactionByEcn(ecn) {
|
|
835
|
-
try {
|
|
836
|
-
const database = getDatabase();
|
|
837
|
-
const stmt = database.prepare("SELECT * FROM transactions WHERE ecn = ?");
|
|
838
|
-
const row = stmt.get(ecn);
|
|
839
|
-
if (row) {
|
|
840
|
-
row.request_payload = JSON.parse(row.request_payload || "{}");
|
|
841
|
-
row.response_data = JSON.parse(row.response_data || "{}");
|
|
842
|
-
}
|
|
843
|
-
return row;
|
|
844
|
-
} catch (error) {
|
|
845
|
-
logger.log({
|
|
846
|
-
level: "error",
|
|
847
|
-
message: `Failed to get transaction: ${error.message}`
|
|
848
|
-
});
|
|
849
|
-
return null;
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
function getRecentTransactions(limit = 100) {
|
|
853
|
-
try {
|
|
854
|
-
const database = getDatabase();
|
|
855
|
-
const stmt = database.prepare(`
|
|
856
|
-
SELECT * FROM transactions
|
|
857
|
-
ORDER BY request_timestamp DESC
|
|
858
|
-
LIMIT ?
|
|
859
|
-
`);
|
|
860
|
-
const rows = stmt.all(limit);
|
|
861
|
-
return rows.map((row) => ({
|
|
862
|
-
...row,
|
|
863
|
-
request_payload: JSON.parse(row.request_payload || "{}"),
|
|
864
|
-
response_data: JSON.parse(row.response_data || "{}")
|
|
865
|
-
}));
|
|
866
|
-
} catch (error) {
|
|
867
|
-
logger.log({
|
|
868
|
-
level: "error",
|
|
869
|
-
message: `Failed to get recent transactions: ${error.message}`
|
|
870
|
-
});
|
|
871
|
-
return [];
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
function getTransactionsByStatus(status, limit = 100) {
|
|
875
|
-
try {
|
|
876
|
-
const database = getDatabase();
|
|
877
|
-
const stmt = database.prepare(`
|
|
878
|
-
SELECT * FROM transactions
|
|
879
|
-
WHERE status = ?
|
|
880
|
-
ORDER BY request_timestamp DESC
|
|
881
|
-
LIMIT ?
|
|
882
|
-
`);
|
|
883
|
-
const rows = stmt.all(status, limit);
|
|
884
|
-
return rows.map((row) => ({
|
|
885
|
-
...row,
|
|
886
|
-
request_payload: JSON.parse(row.request_payload || "{}"),
|
|
887
|
-
response_data: JSON.parse(row.response_data || "{}")
|
|
888
|
-
}));
|
|
889
|
-
} catch (error) {
|
|
890
|
-
logger.log({
|
|
891
|
-
level: "error",
|
|
892
|
-
message: `Failed to get transactions by status: ${error.message}`
|
|
893
|
-
});
|
|
894
|
-
return [];
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
function cleanupOldTransactions(daysToKeep = 90) {
|
|
898
|
-
try {
|
|
899
|
-
const database = getDatabase();
|
|
900
|
-
const cutoffTime = Date.now() - daysToKeep * 24 * 60 * 60 * 1e3;
|
|
901
|
-
const stmt = database.prepare("DELETE FROM transactions WHERE request_timestamp < ?");
|
|
902
|
-
const result = stmt.run(cutoffTime);
|
|
903
|
-
logger.log({
|
|
904
|
-
level: "info",
|
|
905
|
-
message: `Cleaned up ${result.changes} old transactions (older than ${daysToKeep} days)`
|
|
906
|
-
});
|
|
907
|
-
return result.changes;
|
|
908
|
-
} catch (error) {
|
|
909
|
-
logger.log({
|
|
910
|
-
level: "error",
|
|
911
|
-
message: `Failed to cleanup old transactions: ${error.message}`
|
|
912
|
-
});
|
|
913
|
-
return 0;
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
function closeDatabase() {
|
|
917
|
-
if (db) {
|
|
918
|
-
db.close();
|
|
919
|
-
db = null;
|
|
920
|
-
logger.log({
|
|
921
|
-
level: "info",
|
|
922
|
-
message: "Database connection closed"
|
|
923
|
-
});
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
module2.exports = {
|
|
927
|
-
initDatabase,
|
|
928
|
-
getDatabase,
|
|
929
|
-
createTransaction,
|
|
930
|
-
updateTransactionResponse,
|
|
931
|
-
getTransactionByEcn,
|
|
932
|
-
getRecentTransactions,
|
|
933
|
-
getTransactionsByStatus,
|
|
934
|
-
cleanupOldTransactions,
|
|
935
|
-
closeDatabase,
|
|
936
|
-
// Auto-purge control functions
|
|
937
|
-
startAutoPurge: require_autoPurge().startAutoPurge,
|
|
938
|
-
stopAutoPurge: require_autoPurge().stopAutoPurge,
|
|
939
|
-
isAutoPurgeRunning: require_autoPurge().isAutoPurgeRunning
|
|
940
|
-
};
|
|
941
|
-
}
|
|
942
|
-
});
|
|
943
|
-
|
|
944
660
|
// src/payment/responseHandler.js
|
|
945
661
|
var require_responseHandler = __commonJS({
|
|
946
662
|
"src/payment/responseHandler.js"(exports2, module2) {
|
|
@@ -959,47 +675,49 @@ var require_responseHandler = __commonJS({
|
|
|
959
675
|
} = require_parser();
|
|
960
676
|
var fw = require_manageConfig();
|
|
961
677
|
var { xorCalculation } = require_utils_helper();
|
|
962
|
-
var { updateTransactionResponse } = require_transactionStore();
|
|
963
678
|
var queue2 = new Queue();
|
|
964
679
|
var paymentCloudUrl = null;
|
|
965
680
|
var paymentOrderId = null;
|
|
681
|
+
var activePaymentEcn = null;
|
|
966
682
|
module2.exports.setPaymentCloudUrl = (url) => {
|
|
967
683
|
paymentCloudUrl = url;
|
|
968
684
|
};
|
|
969
685
|
module2.exports.setPaymentOrderId = (orderId) => {
|
|
970
686
|
paymentOrderId = orderId;
|
|
971
687
|
};
|
|
688
|
+
module2.exports.setPaymentEcn = (ecn) => {
|
|
689
|
+
activePaymentEcn = ecn;
|
|
690
|
+
};
|
|
972
691
|
function sendPaymentResponseToCloud(translatedResponse) {
|
|
973
692
|
const cloudUrl = paymentCloudUrl || fw.getConfigByKey("cloud_url");
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
translatedResponse.retrievalReferenceNumber = paymentOrderId;
|
|
693
|
+
if (paymentOrderId && translatedResponse) {
|
|
694
|
+
translatedResponse.enhancedECRReferenceNumber = paymentOrderId;
|
|
977
695
|
}
|
|
978
696
|
paymentCloudUrl = null;
|
|
979
697
|
paymentOrderId = null;
|
|
980
698
|
if (!cloudUrl) {
|
|
981
|
-
logger.log({ level: "warn", message: "
|
|
699
|
+
logger.log({ level: "warn", message: "\u26A0\uFE0F cloud_url not configured, skipping payment response POST" });
|
|
982
700
|
return;
|
|
983
701
|
}
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
}));
|
|
988
|
-
console.log(URl);
|
|
989
|
-
axios.post(URl, {
|
|
702
|
+
const targetUrl = `${cloudUrl}/payresponse/api/v1/nets/response`;
|
|
703
|
+
logger.log({ level: "info", message: `\u{1F4E1} POSTing payment response to: ${targetUrl}` });
|
|
704
|
+
axios.post(targetUrl, {
|
|
990
705
|
netsRes: translatedResponse
|
|
991
706
|
}).then((res) => {
|
|
992
|
-
logger.log({ level: "info", message:
|
|
707
|
+
logger.log({ level: "info", message: `\u2705 Payment response sent to cloud: ${res.status}` });
|
|
993
708
|
}).catch((err) => {
|
|
994
|
-
logger.log({ level: "error", message:
|
|
709
|
+
logger.log({ level: "error", message: `\u274C Failed to send payment response to cloud: ${err.message}` });
|
|
995
710
|
});
|
|
996
711
|
}
|
|
997
|
-
module2.exports.terminalStatus = (ecn) => {
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
712
|
+
module2.exports.terminalStatus = (ecn, hexValue = null) => {
|
|
713
|
+
let fullHex = hexValue;
|
|
714
|
+
if (!fullHex) {
|
|
715
|
+
const length = queue2.size();
|
|
716
|
+
fullHex = "";
|
|
717
|
+
for (let i = 0; i < length; i++) {
|
|
718
|
+
const node = queue2.dequeue();
|
|
719
|
+
fullHex += node.data.hex;
|
|
720
|
+
}
|
|
1003
721
|
}
|
|
1004
722
|
const status = statusParser(fullHex, ecn);
|
|
1005
723
|
console.log(fullHex);
|
|
@@ -1007,18 +725,6 @@ var require_responseHandler = __commonJS({
|
|
|
1007
725
|
level: "info",
|
|
1008
726
|
message: status
|
|
1009
727
|
});
|
|
1010
|
-
try {
|
|
1011
|
-
updateTransactionResponse(ecn, {
|
|
1012
|
-
responseHex: fullHex,
|
|
1013
|
-
responseData: { status },
|
|
1014
|
-
status: status ? "COMPLETED" : "FAILED"
|
|
1015
|
-
});
|
|
1016
|
-
} catch (error) {
|
|
1017
|
-
logger.log({
|
|
1018
|
-
level: "error",
|
|
1019
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1020
|
-
});
|
|
1021
|
-
}
|
|
1022
728
|
if (status) {
|
|
1023
729
|
fw.updateConfig({ terminal_status: true });
|
|
1024
730
|
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
@@ -1035,26 +741,17 @@ var require_responseHandler = __commonJS({
|
|
|
1035
741
|
});
|
|
1036
742
|
}
|
|
1037
743
|
};
|
|
1038
|
-
module2.exports.terminalLogon = (ecn) => {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
744
|
+
module2.exports.terminalLogon = (ecn, hexValue = null) => {
|
|
745
|
+
let fullHex = hexValue;
|
|
746
|
+
if (!fullHex) {
|
|
747
|
+
const length = queue2.size();
|
|
748
|
+
fullHex = "";
|
|
749
|
+
for (let i = 0; i < length; i++) {
|
|
750
|
+
const node = queue2.dequeue();
|
|
751
|
+
fullHex += node.data.hex;
|
|
752
|
+
}
|
|
1044
753
|
}
|
|
1045
754
|
const response = logonParser(fullHex, ecn);
|
|
1046
|
-
try {
|
|
1047
|
-
updateTransactionResponse(ecn, {
|
|
1048
|
-
responseHex: fullHex,
|
|
1049
|
-
responseData: response,
|
|
1050
|
-
status: response.status ? "COMPLETED" : "FAILED"
|
|
1051
|
-
});
|
|
1052
|
-
} catch (error) {
|
|
1053
|
-
logger.log({
|
|
1054
|
-
level: "error",
|
|
1055
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1056
|
-
});
|
|
1057
|
-
}
|
|
1058
755
|
if (response.status) {
|
|
1059
756
|
fw.updateConfig({ last_logon: /* @__PURE__ */ new Date() });
|
|
1060
757
|
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
@@ -1079,25 +776,18 @@ var require_responseHandler = __commonJS({
|
|
|
1079
776
|
}
|
|
1080
777
|
return fullHex;
|
|
1081
778
|
};
|
|
1082
|
-
module2.exports.terminalPayment = (hexValue,
|
|
779
|
+
module2.exports.terminalPayment = (hexValue, ecnArg) => {
|
|
780
|
+
const ecn = activePaymentEcn || ecnArg;
|
|
1083
781
|
const parsedValue = netsPaymentParser(hexValue, ecn);
|
|
1084
782
|
const response = jsonProcessor(parsedValue);
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
responseHex: hexValue,
|
|
1088
|
-
responseData: response,
|
|
1089
|
-
status: response.translated?.status || "UNKNOWN"
|
|
1090
|
-
});
|
|
1091
|
-
} catch (error) {
|
|
1092
|
-
logger.log({
|
|
1093
|
-
level: "error",
|
|
1094
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1095
|
-
});
|
|
783
|
+
if (response.translated) {
|
|
784
|
+
activePaymentEcn = null;
|
|
1096
785
|
}
|
|
1097
786
|
if (response.translated) {
|
|
1098
787
|
if (response.translated.status == "APPROVED") {
|
|
1099
788
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1100
789
|
success: true,
|
|
790
|
+
status: "SUCCESS",
|
|
1101
791
|
response,
|
|
1102
792
|
action: "COMPLETED"
|
|
1103
793
|
});
|
|
@@ -1119,24 +809,17 @@ var require_responseHandler = __commonJS({
|
|
|
1119
809
|
}
|
|
1120
810
|
}
|
|
1121
811
|
};
|
|
1122
|
-
module2.exports.terminalCreditPayment = (hexValue,
|
|
812
|
+
module2.exports.terminalCreditPayment = (hexValue, ecnArg) => {
|
|
813
|
+
const ecn = activePaymentEcn || ecnArg;
|
|
1123
814
|
const parsedValue = creditPaymentParser(hexValue, ecn);
|
|
1124
815
|
const response = jsonProcessor(parsedValue);
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
responseHex: hexValue,
|
|
1128
|
-
responseData: response,
|
|
1129
|
-
status: response.translated?.status || "UNKNOWN"
|
|
1130
|
-
});
|
|
1131
|
-
} catch (error) {
|
|
1132
|
-
logger.log({
|
|
1133
|
-
level: "error",
|
|
1134
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1135
|
-
});
|
|
816
|
+
if (response.translated) {
|
|
817
|
+
activePaymentEcn = null;
|
|
1136
818
|
}
|
|
1137
819
|
if (response.translated) {
|
|
1138
820
|
if (response.translated.status == "APPROVED") {
|
|
1139
821
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
822
|
+
success: true,
|
|
1140
823
|
status: "SUCCESS",
|
|
1141
824
|
response,
|
|
1142
825
|
action: "COMPLETED"
|
|
@@ -1180,11 +863,12 @@ var require_responseHandler = __commonJS({
|
|
|
1180
863
|
}
|
|
1181
864
|
};
|
|
1182
865
|
module2.exports.checkLrc = (hexValue) => {
|
|
866
|
+
if (!hexValue || hexValue.length < 4) return false;
|
|
1183
867
|
const value = hexValue.substring(2, hexValue.length - 2);
|
|
1184
|
-
const xor = xorCalculation(value);
|
|
1185
|
-
const lrcCheck = hexValue.substring(hexValue.length - 2, hexValue.length);
|
|
1186
|
-
|
|
1187
|
-
return xor
|
|
868
|
+
const xor = xorCalculation(value).toUpperCase();
|
|
869
|
+
const lrcCheck = hexValue.substring(hexValue.length - 2, hexValue.length).toUpperCase();
|
|
870
|
+
logger.info({ level: "info", message: `LRC Check: Calculated ${xor} vs Received ${lrcCheck}` });
|
|
871
|
+
return xor === lrcCheck;
|
|
1188
872
|
};
|
|
1189
873
|
}
|
|
1190
874
|
});
|
|
@@ -1207,93 +891,47 @@ var require_communication = __commonJS({
|
|
|
1207
891
|
terminalStatus,
|
|
1208
892
|
fetchFullDetails,
|
|
1209
893
|
checkLrc,
|
|
1210
|
-
verifyParser
|
|
894
|
+
verifyParser,
|
|
895
|
+
setPaymentEcn
|
|
1211
896
|
} = require_responseHandler();
|
|
1212
897
|
var { sendMessage: sendMessage2 } = require_sendMessage();
|
|
1213
898
|
var fw = require_manageConfig();
|
|
1214
|
-
var { createTransaction } = require_transactionStore();
|
|
1215
899
|
var { initialiseRequest } = require_httpRequest();
|
|
1216
900
|
var requestType;
|
|
1217
901
|
var calculated;
|
|
1218
|
-
var isTimeOutset = false;
|
|
1219
902
|
var MAX_COUNT = 3;
|
|
1220
903
|
var WRONG_LRC_MAX_COUNT = 3;
|
|
1221
904
|
var count;
|
|
1222
905
|
var lrcCount;
|
|
1223
906
|
var ackOrNack;
|
|
1224
907
|
var requestPayload;
|
|
908
|
+
var ackTimeout = null;
|
|
1225
909
|
var ACK = Buffer.alloc(1, 6, "hex");
|
|
1226
910
|
var NACK = Buffer.alloc(1, 21, "hex");
|
|
911
|
+
var dataBuffer = Buffer.alloc(0);
|
|
1227
912
|
var config = fw.getConfig();
|
|
1228
|
-
var
|
|
1229
|
-
|
|
1230
|
-
setTimeout(() => {
|
|
1231
|
-
isTimeOutset = false;
|
|
1232
|
-
terminalStatus(calculated.ecn);
|
|
1233
|
-
}, 3e3);
|
|
1234
|
-
}
|
|
1235
|
-
if (requestType == "LOGON") {
|
|
1236
|
-
setTimeout(() => {
|
|
1237
|
-
isTimeOutset = false;
|
|
1238
|
-
terminalLogon(calculated.ecn);
|
|
1239
|
-
}, 5e3);
|
|
1240
|
-
}
|
|
1241
|
-
if (requestType == "PAYMENT") {
|
|
1242
|
-
setTimeout(() => {
|
|
1243
|
-
isTimeOutset = false;
|
|
1244
|
-
lrcCount--;
|
|
1245
|
-
if (lrcCount >= 0) {
|
|
1246
|
-
const hexValue = fetchFullDetails();
|
|
1247
|
-
const lrcResponse = checkLrc(hexValue);
|
|
1248
|
-
if (!lrcResponse) {
|
|
1249
|
-
logger.log({
|
|
1250
|
-
level: "info",
|
|
1251
|
-
message: `Terminal send response data with wrong LRC, POS expected to send NACK`
|
|
1252
|
-
});
|
|
1253
|
-
global.port.write(NACK);
|
|
1254
|
-
} else {
|
|
1255
|
-
logger.log({
|
|
1256
|
-
level: "info",
|
|
1257
|
-
message: `Terminal resend response data with correct LRC, POS expected to send ACK`
|
|
1258
|
-
});
|
|
1259
|
-
global.port.write(ACK);
|
|
1260
|
-
terminalPayment(hexValue, calculated.ecn);
|
|
1261
|
-
}
|
|
1262
|
-
} else {
|
|
1263
|
-
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1264
|
-
success: true,
|
|
1265
|
-
action: "APPROVED_FLAGGED"
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
}, 8e3);
|
|
1269
|
-
}
|
|
1270
|
-
if (requestType === "CREDIT_PAYMENT") {
|
|
1271
|
-
setTimeout(() => {
|
|
1272
|
-
isTimeOutset = false;
|
|
1273
|
-
const hexValue = fetchFullDetails();
|
|
1274
|
-
const lrcResponse = checkLrc(hexValue);
|
|
1275
|
-
if (!lrcResponse) {
|
|
1276
|
-
global.port.write(NACK);
|
|
1277
|
-
} else {
|
|
1278
|
-
global.port.write(ACK);
|
|
1279
|
-
terminalCreditPayment(hexValue, calculated.ecn);
|
|
1280
|
-
}
|
|
1281
|
-
}, 8e3);
|
|
1282
|
-
}
|
|
1283
|
-
};
|
|
913
|
+
var lastPaymentReference = null;
|
|
914
|
+
var lastPaymentTime = 0;
|
|
1284
915
|
module2.exports.reset = () => {
|
|
1285
916
|
count = MAX_COUNT;
|
|
1286
917
|
lrcCount = WRONG_LRC_MAX_COUNT;
|
|
1287
918
|
ackOrNack = null;
|
|
919
|
+
dataBuffer = Buffer.alloc(0);
|
|
920
|
+
if (ackTimeout) {
|
|
921
|
+
clearTimeout(ackTimeout);
|
|
922
|
+
ackTimeout = null;
|
|
923
|
+
}
|
|
1288
924
|
};
|
|
1289
925
|
module2.exports.checkACKorNACK = () => {
|
|
1290
|
-
|
|
926
|
+
if (ackTimeout) clearTimeout(ackTimeout);
|
|
927
|
+
ackTimeout = setTimeout(() => {
|
|
928
|
+
ackTimeout = null;
|
|
1291
929
|
if (ackOrNack == null) {
|
|
1292
930
|
count--;
|
|
1293
931
|
if (count >= 0) {
|
|
1294
932
|
logger.log({
|
|
1295
933
|
level: "info",
|
|
1296
|
-
message: `POS send Purchase command, Terminal doesn't send any reply
|
|
934
|
+
message: `POS send Purchase command, Terminal doesn't send any reply. Retrying...`
|
|
1297
935
|
});
|
|
1298
936
|
resendData();
|
|
1299
937
|
} else {
|
|
@@ -1303,7 +941,7 @@ var require_communication = __commonJS({
|
|
|
1303
941
|
});
|
|
1304
942
|
}
|
|
1305
943
|
}
|
|
1306
|
-
},
|
|
944
|
+
}, 4e3);
|
|
1307
945
|
};
|
|
1308
946
|
var resendData = () => {
|
|
1309
947
|
if (requestType == "STATUS_CHECK") {
|
|
@@ -1319,55 +957,149 @@ var require_communication = __commonJS({
|
|
|
1319
957
|
exports2.checkACKorNACK();
|
|
1320
958
|
};
|
|
1321
959
|
module2.exports.initialize = async (socketConnection = null) => {
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
960
|
+
try {
|
|
961
|
+
if (socketConnection) {
|
|
962
|
+
global.io = socketConnection;
|
|
963
|
+
}
|
|
964
|
+
if (global.io) {
|
|
965
|
+
logger.log({
|
|
966
|
+
level: "info",
|
|
967
|
+
message: "Socket connection available for terminal communication"
|
|
968
|
+
});
|
|
969
|
+
} else {
|
|
970
|
+
logger.log({
|
|
971
|
+
level: "warn",
|
|
972
|
+
message: "No socket connection (global.io) found. Messages will be logged only."
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
if (!config.terminal || config.terminal === "enable") {
|
|
976
|
+
if (global.port) {
|
|
977
|
+
try {
|
|
978
|
+
if (global.port.isOpen) {
|
|
979
|
+
logger.log({ level: "info", message: "Closing existing serial port..." });
|
|
980
|
+
global.port.close();
|
|
981
|
+
}
|
|
982
|
+
global.port.removeAllListeners();
|
|
983
|
+
if (global.port.destroy) global.port.destroy();
|
|
984
|
+
} catch (e) {
|
|
985
|
+
logger.log({ level: "debug", message: `Error during port cleanup: ${e.message}` });
|
|
986
|
+
}
|
|
987
|
+
global.port = null;
|
|
988
|
+
}
|
|
989
|
+
const portPath = config.simulation ? config.simulationPort : config.com;
|
|
990
|
+
const openPort = () => {
|
|
991
|
+
logger.log({ level: "info", message: `Initializing Serial Port on ${portPath}...` });
|
|
992
|
+
global.port = new SerialPort({
|
|
993
|
+
path: portPath,
|
|
994
|
+
baudRate: 9600,
|
|
995
|
+
dataBits: 8,
|
|
996
|
+
stopBits: 1,
|
|
997
|
+
parity: "none",
|
|
998
|
+
autoOpen: false,
|
|
999
|
+
lock: false,
|
|
1000
|
+
// Disable locking to prevent "Access Denied" or "Error 31" stuck files
|
|
1001
|
+
rtscts: false
|
|
1002
|
+
// Explicitly disable flow control
|
|
1003
|
+
});
|
|
1004
|
+
global.port.on("data", (data) => {
|
|
1005
|
+
logger.log({ level: "info", message: `Raw data: ${data.toString("hex")}` });
|
|
1006
|
+
dataBuffer = Buffer.concat([dataBuffer, data]);
|
|
1007
|
+
exports2.processIncomingData();
|
|
1008
|
+
});
|
|
1009
|
+
global.port.on("error", (err) => {
|
|
1010
|
+
logger.error({ level: "error", message: `SerialPort Error Event: ${err.message}` });
|
|
1011
|
+
});
|
|
1012
|
+
setTimeout(() => {
|
|
1013
|
+
global.port.open((err) => {
|
|
1014
|
+
if (err) {
|
|
1015
|
+
logger.error({ level: "error", message: `Error opening serial port ${portPath}: ${err.message}` });
|
|
1016
|
+
if (err.message.includes("31")) {
|
|
1017
|
+
logger.log({ level: "warn", message: "Error 31 detected. Attempting hardware reset retry in 2s..." });
|
|
1018
|
+
setTimeout(openPort, 2e3);
|
|
1019
|
+
}
|
|
1020
|
+
} else {
|
|
1021
|
+
logger.log({ level: "info", message: `Serial port ${portPath} opened successfully` });
|
|
1022
|
+
setTimeout(() => exports2.sentToTerminal("STATUS_CHECK"), 1e3);
|
|
1023
|
+
}
|
|
1024
|
+
});
|
|
1025
|
+
}, 1500);
|
|
1026
|
+
};
|
|
1027
|
+
openPort();
|
|
1028
|
+
}
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
console.error("Serial initialization error:", err.message);
|
|
1333
1031
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
console.log(data);
|
|
1344
|
-
if (data.toString("hex").length == 2 && data.toString("hex") == "15") {
|
|
1032
|
+
};
|
|
1033
|
+
module2.exports.processIncomingData = () => {
|
|
1034
|
+
while (dataBuffer.length >= 3) {
|
|
1035
|
+
if (dataBuffer[0] === 6 || dataBuffer[0] === 21) {
|
|
1036
|
+
const byte = dataBuffer[0];
|
|
1037
|
+
ackOrNack = Buffer.from([byte]);
|
|
1038
|
+
dataBuffer = dataBuffer.slice(1);
|
|
1039
|
+
logger.log({ level: "info", message: `Terminal sent ${byte === 6 ? "ACK" : "NACK"} (0x${byte.toString(16)})` });
|
|
1040
|
+
if (byte === 21) {
|
|
1345
1041
|
count--;
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
logger.log({
|
|
1349
|
-
level: "info",
|
|
1350
|
-
message: `POS send Purchase command, Terminal reply NACK`
|
|
1351
|
-
});
|
|
1352
|
-
resendData();
|
|
1353
|
-
} else {
|
|
1354
|
-
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1355
|
-
success: true,
|
|
1356
|
-
action: "TERMINAL_ERROR"
|
|
1357
|
-
});
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
} else if (data.toString("hex").length == 2 && data.toString("hex") == "06") {
|
|
1361
|
-
ackOrNack = data;
|
|
1362
|
-
} else if (data.length > 2) {
|
|
1363
|
-
global.port.write(ACK);
|
|
1364
|
-
if (!isTimeOutset) {
|
|
1365
|
-
isTimeOutset = true;
|
|
1366
|
-
triggerTimer();
|
|
1367
|
-
}
|
|
1368
|
-
requestParser(data, requestType, calculated?.ecn);
|
|
1042
|
+
if (count >= 0) resendData();
|
|
1043
|
+
else sendMessage2("clientRoom", "PAYMENT_MESSAGE", { success: true, action: "TERMINAL_ERROR" });
|
|
1369
1044
|
}
|
|
1370
|
-
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
const stxIndex = dataBuffer.indexOf(2);
|
|
1048
|
+
if (stxIndex === -1) {
|
|
1049
|
+
dataBuffer = Buffer.alloc(0);
|
|
1050
|
+
break;
|
|
1051
|
+
}
|
|
1052
|
+
if (stxIndex > 0) {
|
|
1053
|
+
dataBuffer = dataBuffer.slice(stxIndex);
|
|
1054
|
+
}
|
|
1055
|
+
if (dataBuffer.length < 3) break;
|
|
1056
|
+
if (dataBuffer.length < 3) break;
|
|
1057
|
+
const lenBytes = dataBuffer.slice(1, 3).toString("hex");
|
|
1058
|
+
const bodyLen = parseInt(lenBytes, 10);
|
|
1059
|
+
const expectedFrameSize = 1 + 2 + bodyLen + 2;
|
|
1060
|
+
if (dataBuffer.length < expectedFrameSize) break;
|
|
1061
|
+
if (dataBuffer[3 + bodyLen] === 3) {
|
|
1062
|
+
const frame = dataBuffer.slice(0, expectedFrameSize);
|
|
1063
|
+
dataBuffer = dataBuffer.slice(expectedFrameSize);
|
|
1064
|
+
exports2.handleFullMessage(frame);
|
|
1065
|
+
} else {
|
|
1066
|
+
const etxIndex = dataBuffer.indexOf(3, 1);
|
|
1067
|
+
if (etxIndex !== -1 && dataBuffer.length > etxIndex + 1) {
|
|
1068
|
+
const frame = dataBuffer.slice(0, etxIndex + 2);
|
|
1069
|
+
dataBuffer = dataBuffer.slice(etxIndex + 2);
|
|
1070
|
+
exports2.handleFullMessage(frame);
|
|
1071
|
+
} else {
|
|
1072
|
+
if (dataBuffer.length > 1024) dataBuffer = Buffer.alloc(0);
|
|
1073
|
+
break;
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
};
|
|
1078
|
+
module2.exports.handleFullMessage = (fullFrame) => {
|
|
1079
|
+
const { checkLrc: checkLrc2, terminalStatus: terminalStatus2, terminalLogon: terminalLogon2, terminalPayment: terminalPayment2, terminalCreditPayment: terminalCreditPayment2 } = require_responseHandler();
|
|
1080
|
+
if (ackTimeout) {
|
|
1081
|
+
clearTimeout(ackTimeout);
|
|
1082
|
+
ackTimeout = null;
|
|
1083
|
+
}
|
|
1084
|
+
ackOrNack = Buffer.from([6]);
|
|
1085
|
+
const hexFrame = fullFrame.toString("hex").toUpperCase();
|
|
1086
|
+
logger.log({ level: "info", message: `Full frame processed: ${hexFrame}` });
|
|
1087
|
+
if (checkLrc2(hexFrame)) {
|
|
1088
|
+
logger.log({ level: "info", message: `LRC Correct. Sending ACK (0x06).` });
|
|
1089
|
+
global.port.write(ACK);
|
|
1090
|
+
logger.log({ level: "info", message: `requestType: ${requestType}` });
|
|
1091
|
+
if (requestType === "STATUS_CHECK") {
|
|
1092
|
+
terminalStatus2(calculated?.ecn, hexFrame);
|
|
1093
|
+
} else if (requestType === "LOGON") {
|
|
1094
|
+
terminalLogon2(calculated?.ecn, hexFrame);
|
|
1095
|
+
} else if (requestType === "PAYMENT") {
|
|
1096
|
+
terminalPayment2(hexFrame, calculated?.ecn);
|
|
1097
|
+
} else if (requestType === "CREDIT_PAYMENT") {
|
|
1098
|
+
terminalCreditPayment2(hexFrame, calculated?.ecn);
|
|
1099
|
+
}
|
|
1100
|
+
} else {
|
|
1101
|
+
logger.log({ level: "error", message: `LRC Incorrect. Sending NACK (0x15).` });
|
|
1102
|
+
global.port.write(NACK);
|
|
1371
1103
|
}
|
|
1372
1104
|
};
|
|
1373
1105
|
module2.exports.sentToTerminal = (type, body) => {
|
|
@@ -1375,19 +1107,6 @@ var require_communication = __commonJS({
|
|
|
1375
1107
|
requestPayload = body;
|
|
1376
1108
|
if (requestType == "STATUS_CHECK") {
|
|
1377
1109
|
calculated = generateStatusReq();
|
|
1378
|
-
try {
|
|
1379
|
-
createTransaction({
|
|
1380
|
-
ecn: calculated.ecn,
|
|
1381
|
-
requestType,
|
|
1382
|
-
requestPayload: null,
|
|
1383
|
-
requestHex: calculated.buffer.toString("hex")
|
|
1384
|
-
});
|
|
1385
|
-
} catch (error) {
|
|
1386
|
-
logger.log({
|
|
1387
|
-
level: "error",
|
|
1388
|
-
message: `Failed to store transaction: ${error.message}`
|
|
1389
|
-
});
|
|
1390
|
-
}
|
|
1391
1110
|
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
1392
1111
|
status: "INITIATED",
|
|
1393
1112
|
action: "STATUS_CHECK_INITIATED"
|
|
@@ -1396,19 +1115,6 @@ var require_communication = __commonJS({
|
|
|
1396
1115
|
}
|
|
1397
1116
|
if (requestType == "LOGON") {
|
|
1398
1117
|
calculated = generateLogonRequest();
|
|
1399
|
-
try {
|
|
1400
|
-
createTransaction({
|
|
1401
|
-
ecn: calculated.ecn,
|
|
1402
|
-
requestType,
|
|
1403
|
-
requestPayload: null,
|
|
1404
|
-
requestHex: calculated.buffer.toString("hex")
|
|
1405
|
-
});
|
|
1406
|
-
} catch (error) {
|
|
1407
|
-
logger.log({
|
|
1408
|
-
level: "error",
|
|
1409
|
-
message: `Failed to store transaction: ${error.message}`
|
|
1410
|
-
});
|
|
1411
|
-
}
|
|
1412
1118
|
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
1413
1119
|
status: "INITIATED",
|
|
1414
1120
|
action: "LOGON_INITIATED"
|
|
@@ -1416,26 +1122,30 @@ var require_communication = __commonJS({
|
|
|
1416
1122
|
global.port.write(calculated.buffer);
|
|
1417
1123
|
}
|
|
1418
1124
|
if (requestType == "PAYMENT" || requestType == "CREDIT_PAYMENT") {
|
|
1125
|
+
if (body && body.reference) {
|
|
1126
|
+
const now = Date.now();
|
|
1127
|
+
if (body.reference === lastPaymentReference && now - lastPaymentTime < 1e4) {
|
|
1128
|
+
logger.log({
|
|
1129
|
+
level: "warn",
|
|
1130
|
+
message: `Duplicate payment request received for reference ${body.reference} within 10s. Ignored.`
|
|
1131
|
+
});
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
lastPaymentReference = body.reference;
|
|
1135
|
+
lastPaymentTime = now;
|
|
1136
|
+
}
|
|
1419
1137
|
exports2.reset();
|
|
1138
|
+
const { setPaymentCloudUrl, setPaymentOrderId } = require_responseHandler();
|
|
1139
|
+
if (body && body.cloud_url) setPaymentCloudUrl(body.cloud_url);
|
|
1140
|
+
if (body && (body.reference || body.orderId)) setPaymentOrderId(body.reference || body.orderId);
|
|
1420
1141
|
calculated = generatePaymentRequest(body);
|
|
1421
1142
|
console.log(calculated);
|
|
1422
|
-
try {
|
|
1423
|
-
createTransaction({
|
|
1424
|
-
ecn: calculated.ecn,
|
|
1425
|
-
requestType,
|
|
1426
|
-
requestPayload: body,
|
|
1427
|
-
requestHex: calculated.hexString
|
|
1428
|
-
});
|
|
1429
|
-
} catch (error) {
|
|
1430
|
-
logger.log({
|
|
1431
|
-
level: "error",
|
|
1432
|
-
message: `Failed to store transaction: ${error.message}`
|
|
1433
|
-
});
|
|
1434
|
-
}
|
|
1435
1143
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1436
1144
|
status: "INITIATED",
|
|
1437
|
-
action: "PAYMENT_INITIATED"
|
|
1145
|
+
action: "PAYMENT_INITIATED",
|
|
1146
|
+
orderId: body.reference || body.orderId
|
|
1438
1147
|
});
|
|
1148
|
+
if (calculated && calculated.ecn) setPaymentEcn(calculated.ecn);
|
|
1439
1149
|
global.port.write(calculated.buffer);
|
|
1440
1150
|
exports2.checkACKorNACK();
|
|
1441
1151
|
}
|
|
@@ -1443,142 +1153,6 @@ var require_communication = __commonJS({
|
|
|
1443
1153
|
}
|
|
1444
1154
|
});
|
|
1445
1155
|
|
|
1446
|
-
// src/db/listTransactions.js
|
|
1447
|
-
var require_listTransactions = __commonJS({
|
|
1448
|
-
"src/db/listTransactions.js"(exports2, module2) {
|
|
1449
|
-
var { getRecentTransactions, getTransactionByEcn, getTransactionsByStatus } = require_transactionStore();
|
|
1450
|
-
var logger = require_winston()(module2);
|
|
1451
|
-
function listRecentTransactions(limit = 10) {
|
|
1452
|
-
try {
|
|
1453
|
-
const transactions = getRecentTransactions(limit);
|
|
1454
|
-
if (transactions.length === 0) {
|
|
1455
|
-
console.log("No transactions found in database.");
|
|
1456
|
-
return [];
|
|
1457
|
-
}
|
|
1458
|
-
console.log(`
|
|
1459
|
-
\u{1F4CA} Recent ${transactions.length} Transaction(s)
|
|
1460
|
-
`);
|
|
1461
|
-
console.log("=".repeat(80));
|
|
1462
|
-
transactions.forEach((txn, index) => {
|
|
1463
|
-
const time = new Date(txn.request_timestamp).toLocaleString();
|
|
1464
|
-
const duration = txn.response_timestamp ? `${txn.response_timestamp - txn.request_timestamp}ms` : "Pending";
|
|
1465
|
-
console.log(`
|
|
1466
|
-
${index + 1}. ECN: ${txn.ecn}`);
|
|
1467
|
-
console.log(` Type: ${txn.request_type}`);
|
|
1468
|
-
console.log(` Status: ${txn.status}`);
|
|
1469
|
-
console.log(` Time: ${time}`);
|
|
1470
|
-
console.log(` Duration: ${duration}`);
|
|
1471
|
-
if (txn.request_type === "PAYMENT" || txn.request_type === "CREDIT_PAYMENT") {
|
|
1472
|
-
const amount = txn.request_payload.amount ? `$${(txn.request_payload.amount / 100).toFixed(2)}` : "N/A";
|
|
1473
|
-
console.log(` Amount: ${amount}`);
|
|
1474
|
-
console.log(` Reference: ${txn.request_payload.reference || "N/A"}`);
|
|
1475
|
-
}
|
|
1476
|
-
});
|
|
1477
|
-
console.log("\n" + "=".repeat(80) + "\n");
|
|
1478
|
-
return transactions;
|
|
1479
|
-
} catch (error) {
|
|
1480
|
-
if (error.code === "SQLITE_CANTOPEN") {
|
|
1481
|
-
console.log("\u2139\uFE0F Database not found. No transactions have been created yet.");
|
|
1482
|
-
console.log(" Transactions will be automatically stored when you send requests to the terminal.\n");
|
|
1483
|
-
return [];
|
|
1484
|
-
}
|
|
1485
|
-
logger.log({
|
|
1486
|
-
level: "error",
|
|
1487
|
-
message: `Failed to list transactions: ${error.message}`
|
|
1488
|
-
});
|
|
1489
|
-
throw error;
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
function listTransactionsByStatus(status, limit = 50) {
|
|
1493
|
-
try {
|
|
1494
|
-
const transactions = getTransactionsByStatus(status, limit);
|
|
1495
|
-
if (transactions.length === 0) {
|
|
1496
|
-
console.log(`No ${status} transactions found.`);
|
|
1497
|
-
return [];
|
|
1498
|
-
}
|
|
1499
|
-
console.log(`
|
|
1500
|
-
\u{1F4CA} ${status} Transactions (${transactions.length})
|
|
1501
|
-
`);
|
|
1502
|
-
console.log("=".repeat(80));
|
|
1503
|
-
transactions.forEach((txn, index) => {
|
|
1504
|
-
const time = new Date(txn.request_timestamp).toLocaleString();
|
|
1505
|
-
console.log(`
|
|
1506
|
-
${index + 1}. ECN: ${txn.ecn} | ${txn.request_type} | ${time}`);
|
|
1507
|
-
});
|
|
1508
|
-
console.log("\n" + "=".repeat(80) + "\n");
|
|
1509
|
-
return transactions;
|
|
1510
|
-
} catch (error) {
|
|
1511
|
-
if (error.code === "SQLITE_CANTOPEN") {
|
|
1512
|
-
console.log("\u2139\uFE0F Database not found. No transactions have been created yet.\n");
|
|
1513
|
-
return [];
|
|
1514
|
-
}
|
|
1515
|
-
logger.log({
|
|
1516
|
-
level: "error",
|
|
1517
|
-
message: `Failed to list transactions: ${error.message}`
|
|
1518
|
-
});
|
|
1519
|
-
throw error;
|
|
1520
|
-
}
|
|
1521
|
-
}
|
|
1522
|
-
function getTransactionDetails(ecn) {
|
|
1523
|
-
try {
|
|
1524
|
-
const transaction = getTransactionByEcn(ecn);
|
|
1525
|
-
if (!transaction) {
|
|
1526
|
-
console.log(`Transaction with ECN ${ecn} not found.`);
|
|
1527
|
-
return null;
|
|
1528
|
-
}
|
|
1529
|
-
console.log(`
|
|
1530
|
-
\u{1F4C4} Transaction Details: ${ecn}
|
|
1531
|
-
`);
|
|
1532
|
-
console.log("=".repeat(80));
|
|
1533
|
-
console.log(`Type: ${transaction.request_type}`);
|
|
1534
|
-
console.log(`Status: ${transaction.status}`);
|
|
1535
|
-
console.log(`Request Time: ${new Date(transaction.request_timestamp).toLocaleString()}`);
|
|
1536
|
-
if (transaction.response_timestamp) {
|
|
1537
|
-
console.log(`Response Time: ${new Date(transaction.response_timestamp).toLocaleString()}`);
|
|
1538
|
-
console.log(`Duration: ${transaction.response_timestamp - transaction.request_timestamp}ms`);
|
|
1539
|
-
}
|
|
1540
|
-
console.log(`
|
|
1541
|
-
Request Payload:`);
|
|
1542
|
-
console.log(JSON.stringify(transaction.request_payload, null, 2));
|
|
1543
|
-
if (transaction.response_data && Object.keys(transaction.response_data).length > 0) {
|
|
1544
|
-
console.log(`
|
|
1545
|
-
Response Data:`);
|
|
1546
|
-
console.log(JSON.stringify(transaction.response_data, null, 2));
|
|
1547
|
-
}
|
|
1548
|
-
console.log("\n" + "=".repeat(80) + "\n");
|
|
1549
|
-
return transaction;
|
|
1550
|
-
} catch (error) {
|
|
1551
|
-
if (error.code === "SQLITE_CANTOPEN") {
|
|
1552
|
-
console.log("\u2139\uFE0F Database not found. No transactions have been created yet.\n");
|
|
1553
|
-
return null;
|
|
1554
|
-
}
|
|
1555
|
-
logger.log({
|
|
1556
|
-
level: "error",
|
|
1557
|
-
message: `Failed to get transaction: ${error.message}`
|
|
1558
|
-
});
|
|
1559
|
-
throw error;
|
|
1560
|
-
}
|
|
1561
|
-
}
|
|
1562
|
-
module2.exports = {
|
|
1563
|
-
listRecentTransactions,
|
|
1564
|
-
listTransactionsByStatus,
|
|
1565
|
-
getTransactionDetails
|
|
1566
|
-
};
|
|
1567
|
-
if (require.main === module2) {
|
|
1568
|
-
const args = process.argv.slice(2);
|
|
1569
|
-
if (args[0] === "--status" && args[1]) {
|
|
1570
|
-
const limit = args[2] ? parseInt(args[2]) : 50;
|
|
1571
|
-
listTransactionsByStatus(args[1], limit);
|
|
1572
|
-
} else if (args[0] === "--ecn" && args[1]) {
|
|
1573
|
-
getTransactionDetails(args[1]);
|
|
1574
|
-
} else {
|
|
1575
|
-
const limit = args[0] ? parseInt(args[0]) : 10;
|
|
1576
|
-
listRecentTransactions(limit);
|
|
1577
|
-
}
|
|
1578
|
-
}
|
|
1579
|
-
}
|
|
1580
|
-
});
|
|
1581
|
-
|
|
1582
1156
|
// src/index.js
|
|
1583
1157
|
var utilsHelper = require_utils_helper();
|
|
1584
1158
|
var winston = require_winston();
|
|
@@ -1590,8 +1164,6 @@ var hexRequest = require_hexRequest();
|
|
|
1590
1164
|
var httpRequest = require_httpRequest();
|
|
1591
1165
|
var parser = require_parser();
|
|
1592
1166
|
var responseHandler = require_responseHandler();
|
|
1593
|
-
var transactionStore = require_transactionStore();
|
|
1594
|
-
var listTransactions = require_listTransactions();
|
|
1595
1167
|
module.exports = {
|
|
1596
1168
|
...utilsHelper,
|
|
1597
1169
|
logger: winston,
|
|
@@ -1602,18 +1174,5 @@ module.exports = {
|
|
|
1602
1174
|
hexRequest,
|
|
1603
1175
|
httpRequest,
|
|
1604
1176
|
parser,
|
|
1605
|
-
responseHandler
|
|
1606
|
-
// Transaction store utilities
|
|
1607
|
-
getTransactionByEcn: transactionStore.getTransactionByEcn,
|
|
1608
|
-
getRecentTransactions: transactionStore.getRecentTransactions,
|
|
1609
|
-
getTransactionsByStatus: transactionStore.getTransactionsByStatus,
|
|
1610
|
-
cleanupOldTransactions: transactionStore.cleanupOldTransactions,
|
|
1611
|
-
// Auto-purge control
|
|
1612
|
-
startAutoPurge: transactionStore.startAutoPurge,
|
|
1613
|
-
stopAutoPurge: transactionStore.stopAutoPurge,
|
|
1614
|
-
isAutoPurgeRunning: transactionStore.isAutoPurgeRunning,
|
|
1615
|
-
// Transaction listing functions
|
|
1616
|
-
listRecentTransactions: listTransactions.listRecentTransactions,
|
|
1617
|
-
listTransactionsByStatus: listTransactions.listTransactionsByStatus,
|
|
1618
|
-
getTransactionDetails: listTransactions.getTransactionDetails
|
|
1177
|
+
responseHandler
|
|
1619
1178
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nets-service-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.10",
|
|
4
4
|
"description": "Utility functions for Nets Service",
|
|
5
5
|
"source": "src/index.js",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,16 @@
|
|
|
11
11
|
"watch": "microbundle watch",
|
|
12
12
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
13
13
|
},
|
|
14
|
-
"keywords": [
|
|
14
|
+
"keywords": [
|
|
15
|
+
"nets",
|
|
16
|
+
"payment",
|
|
17
|
+
"terminal",
|
|
18
|
+
"pos",
|
|
19
|
+
"sqlite",
|
|
20
|
+
"transaction",
|
|
21
|
+
"serial",
|
|
22
|
+
"payment-gateway"
|
|
23
|
+
],
|
|
15
24
|
"author": "dineshhv",
|
|
16
25
|
"license": "ISC",
|
|
17
26
|
"files": [
|
|
@@ -32,4 +41,4 @@
|
|
|
32
41
|
"esbuild": "^0.27.0",
|
|
33
42
|
"microbundle": "^0.15.1"
|
|
34
43
|
}
|
|
35
|
-
}
|
|
44
|
+
}
|