nets-service-sdk 1.1.9 → 1.1.11
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 +250 -706
- 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 = [];
|
|
@@ -94,6 +94,7 @@ var require_utils_helper = __commonJS({
|
|
|
94
94
|
var require_winston = __commonJS({
|
|
95
95
|
"src/winston.js"(exports2, module2) {
|
|
96
96
|
var { createLogger, format, transports } = require("winston");
|
|
97
|
+
var DailyRotateFile = require("winston-daily-rotate-file");
|
|
97
98
|
var { combine, timestamp, label, printf } = format;
|
|
98
99
|
var path = require("path");
|
|
99
100
|
var getLabel = (callingModule) => {
|
|
@@ -110,15 +111,21 @@ var require_winston = __commonJS({
|
|
|
110
111
|
format: combine(label({ label: getLabel(module3) }), timestamp(), myFormat),
|
|
111
112
|
transports: [
|
|
112
113
|
new transports.Console(),
|
|
113
|
-
new
|
|
114
|
-
filename: "log/platform-access
|
|
115
|
-
|
|
116
|
-
|
|
114
|
+
new DailyRotateFile({
|
|
115
|
+
filename: "log/platform-access-%DATE%.log",
|
|
116
|
+
datePattern: "YYYY-MM-DD",
|
|
117
|
+
zippedArchive: true,
|
|
118
|
+
maxSize: "20m",
|
|
119
|
+
maxFiles: "14d",
|
|
120
|
+
level: "info"
|
|
117
121
|
}),
|
|
118
|
-
new
|
|
119
|
-
filename: "log/platform-error
|
|
120
|
-
|
|
121
|
-
|
|
122
|
+
new DailyRotateFile({
|
|
123
|
+
filename: "log/platform-error-%DATE%.log",
|
|
124
|
+
datePattern: "YYYY-MM-DD",
|
|
125
|
+
zippedArchive: true,
|
|
126
|
+
maxSize: "20m",
|
|
127
|
+
maxFiles: "14d",
|
|
128
|
+
level: "error"
|
|
122
129
|
})
|
|
123
130
|
]
|
|
124
131
|
});
|
|
@@ -376,7 +383,7 @@ var require_parser = __commonJS({
|
|
|
376
383
|
"src/payment/parser.js"(exports2, module2) {
|
|
377
384
|
var logger = require_winston()(module2);
|
|
378
385
|
module2.exports.statusParser = (hexCode, ecn) => {
|
|
379
|
-
const splitted = hexCode.split("
|
|
386
|
+
const splitted = hexCode.split("1C");
|
|
380
387
|
for (let i = 0; i < splitted.length; i++) {
|
|
381
388
|
if (splitted[i]?.toAsciiString()?.indexOf(ecn) > -1) {
|
|
382
389
|
logger.log({ level: "info", message: `statusParser: ECN ${ecn} found in response` });
|
|
@@ -388,7 +395,7 @@ var require_parser = __commonJS({
|
|
|
388
395
|
};
|
|
389
396
|
module2.exports.logonParser = (hexCode, ecn) => {
|
|
390
397
|
try {
|
|
391
|
-
const splitted = hexCode.split("
|
|
398
|
+
const splitted = hexCode.split("1C");
|
|
392
399
|
let status = false;
|
|
393
400
|
const json = {};
|
|
394
401
|
for (let i = 0; i < splitted.length; i++) {
|
|
@@ -419,13 +426,16 @@ var require_parser = __commonJS({
|
|
|
419
426
|
};
|
|
420
427
|
module2.exports.netsPaymentParser = (hexCode, ecn) => {
|
|
421
428
|
try {
|
|
422
|
-
const splitted = hexCode.split("
|
|
429
|
+
const splitted = hexCode.split("1C");
|
|
423
430
|
let status = false;
|
|
424
431
|
const json = {};
|
|
425
432
|
for (let i = 0; i < splitted.length; i++) {
|
|
426
433
|
const ascii = splitted[i].toAsciiString();
|
|
427
|
-
if (ascii?.indexOf(ecn) > -1) {
|
|
434
|
+
if (ascii?.indexOf(ecn) > -1 || i === 0 && ascii.length >= 12) {
|
|
428
435
|
json["ECN"] = ascii.cleanUp();
|
|
436
|
+
if (ascii?.indexOf(ecn) === -1) {
|
|
437
|
+
logger.warn({ level: "info", message: `ECN match mismatch but segment identified: expected ${ecn} in ${ascii}` });
|
|
438
|
+
}
|
|
429
439
|
} else {
|
|
430
440
|
const subStringArray = ascii.splitAtIndex(2);
|
|
431
441
|
if (subStringArray.length > 1) {
|
|
@@ -446,12 +456,12 @@ var require_parser = __commonJS({
|
|
|
446
456
|
};
|
|
447
457
|
module2.exports.creditPaymentParser = (hexCode, ecn) => {
|
|
448
458
|
try {
|
|
449
|
-
const splitted = hexCode.split("
|
|
459
|
+
const splitted = hexCode.split("1C");
|
|
450
460
|
let status = false;
|
|
451
461
|
const json = {};
|
|
452
462
|
for (let i = 0; i < splitted.length; i++) {
|
|
453
463
|
const ascii = splitted[i].toAsciiString();
|
|
454
|
-
if (ascii?.indexOf(ecn) > -1) {
|
|
464
|
+
if (ascii?.indexOf(ecn) > -1 || i === 0 && ascii.length >= 12) {
|
|
455
465
|
json["ECN"] = ascii.cleanUp();
|
|
456
466
|
} else {
|
|
457
467
|
const subStringArray = ascii.splitAtIndex(2);
|
|
@@ -654,308 +664,15 @@ var require_parser = __commonJS({
|
|
|
654
664
|
}
|
|
655
665
|
});
|
|
656
666
|
|
|
657
|
-
// src/db/autoPurge.js
|
|
658
|
-
var require_autoPurge = __commonJS({
|
|
659
|
-
"src/db/autoPurge.js"(exports2, module2) {
|
|
660
|
-
var cron = require("node-cron");
|
|
661
|
-
var logger = require_winston()(module2);
|
|
662
|
-
var { cleanupOldTransactions } = require_transactionStore();
|
|
663
|
-
var scheduledTask = null;
|
|
664
|
-
function startAutoPurge(daysToKeep = 7, cronSchedule = "0 2 * * 0") {
|
|
665
|
-
if (scheduledTask) {
|
|
666
|
-
logger.log({
|
|
667
|
-
level: "warn",
|
|
668
|
-
message: "Auto-purge already running. Stopping existing task first."
|
|
669
|
-
});
|
|
670
|
-
stopAutoPurge();
|
|
671
|
-
}
|
|
672
|
-
scheduledTask = cron.schedule(cronSchedule, () => {
|
|
673
|
-
logger.log({
|
|
674
|
-
level: "info",
|
|
675
|
-
message: `Running scheduled transaction cleanup (keeping last ${daysToKeep} days)`
|
|
676
|
-
});
|
|
677
|
-
try {
|
|
678
|
-
const deletedCount = cleanupOldTransactions(daysToKeep);
|
|
679
|
-
logger.log({
|
|
680
|
-
level: "info",
|
|
681
|
-
message: `Auto-purge completed: Deleted ${deletedCount} old transactions`
|
|
682
|
-
});
|
|
683
|
-
} catch (error) {
|
|
684
|
-
logger.log({
|
|
685
|
-
level: "error",
|
|
686
|
-
message: `Auto-purge failed: ${error.message}`
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
});
|
|
690
|
-
logger.log({
|
|
691
|
-
level: "info",
|
|
692
|
-
message: `Auto-purge scheduled: ${cronSchedule} (keeping last ${daysToKeep} days)`
|
|
693
|
-
});
|
|
694
|
-
return scheduledTask;
|
|
695
|
-
}
|
|
696
|
-
function stopAutoPurge() {
|
|
697
|
-
if (scheduledTask) {
|
|
698
|
-
scheduledTask.stop();
|
|
699
|
-
scheduledTask = null;
|
|
700
|
-
logger.log({
|
|
701
|
-
level: "info",
|
|
702
|
-
message: "Auto-purge stopped"
|
|
703
|
-
});
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
function isAutoPurgeRunning() {
|
|
707
|
-
return scheduledTask !== null;
|
|
708
|
-
}
|
|
709
|
-
module2.exports = {
|
|
710
|
-
startAutoPurge,
|
|
711
|
-
stopAutoPurge,
|
|
712
|
-
isAutoPurgeRunning
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
});
|
|
716
|
-
|
|
717
|
-
// src/db/transactionStore.js
|
|
718
|
-
var require_transactionStore = __commonJS({
|
|
719
|
-
"src/db/transactionStore.js"(exports2, module2) {
|
|
720
|
-
var Database = require("better-sqlite3");
|
|
721
|
-
var path = require("path");
|
|
722
|
-
var fs = require("fs");
|
|
723
|
-
var logger = require_winston()(module2);
|
|
724
|
-
var db = null;
|
|
725
|
-
function initDatabase(dbPath, autoPurgeConfig = {}) {
|
|
726
|
-
try {
|
|
727
|
-
const defaultPath = path.join(__dirname, "../../transactions.db");
|
|
728
|
-
const finalPath = dbPath || defaultPath;
|
|
729
|
-
const dir = path.dirname(finalPath);
|
|
730
|
-
if (!fs.existsSync(dir)) {
|
|
731
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
732
|
-
}
|
|
733
|
-
db = new Database(finalPath);
|
|
734
|
-
db.pragma("journal_mode = WAL");
|
|
735
|
-
const createTableSQL = `
|
|
736
|
-
CREATE TABLE IF NOT EXISTS transactions (
|
|
737
|
-
ecn TEXT PRIMARY KEY,
|
|
738
|
-
request_type TEXT NOT NULL,
|
|
739
|
-
request_payload TEXT,
|
|
740
|
-
request_hex TEXT,
|
|
741
|
-
request_timestamp INTEGER NOT NULL,
|
|
742
|
-
response_hex TEXT,
|
|
743
|
-
response_data TEXT,
|
|
744
|
-
response_timestamp INTEGER,
|
|
745
|
-
status TEXT DEFAULT 'PENDING',
|
|
746
|
-
created_at INTEGER NOT NULL,
|
|
747
|
-
updated_at INTEGER NOT NULL
|
|
748
|
-
)
|
|
749
|
-
`;
|
|
750
|
-
db.exec(createTableSQL);
|
|
751
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_request_timestamp ON transactions(request_timestamp DESC)");
|
|
752
|
-
db.exec("CREATE INDEX IF NOT EXISTS idx_status ON transactions(status)");
|
|
753
|
-
logger.log({
|
|
754
|
-
level: "info",
|
|
755
|
-
message: `Transaction database initialized at ${finalPath}`
|
|
756
|
-
});
|
|
757
|
-
const { enabled = true, daysToKeep = 7, schedule = "0 2 * * 0" } = autoPurgeConfig;
|
|
758
|
-
if (enabled) {
|
|
759
|
-
const autoPurge = require_autoPurge();
|
|
760
|
-
autoPurge.startAutoPurge(daysToKeep, schedule);
|
|
761
|
-
}
|
|
762
|
-
return db;
|
|
763
|
-
} catch (error) {
|
|
764
|
-
logger.log({
|
|
765
|
-
level: "error",
|
|
766
|
-
message: `Failed to initialize database: ${error.message}`
|
|
767
|
-
});
|
|
768
|
-
throw error;
|
|
769
|
-
}
|
|
770
|
-
}
|
|
771
|
-
function getDatabase() {
|
|
772
|
-
if (!db) {
|
|
773
|
-
initDatabase();
|
|
774
|
-
}
|
|
775
|
-
return db;
|
|
776
|
-
}
|
|
777
|
-
function createTransaction(data) {
|
|
778
|
-
try {
|
|
779
|
-
const database = getDatabase();
|
|
780
|
-
const now = Date.now();
|
|
781
|
-
const stmt = database.prepare(`
|
|
782
|
-
INSERT INTO transactions (
|
|
783
|
-
ecn, request_type, request_payload, request_hex,
|
|
784
|
-
request_timestamp, status, created_at, updated_at
|
|
785
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
786
|
-
`);
|
|
787
|
-
const result = stmt.run(
|
|
788
|
-
data.ecn,
|
|
789
|
-
data.requestType,
|
|
790
|
-
JSON.stringify(data.requestPayload || {}),
|
|
791
|
-
data.requestHex,
|
|
792
|
-
now,
|
|
793
|
-
"PENDING",
|
|
794
|
-
now,
|
|
795
|
-
now
|
|
796
|
-
);
|
|
797
|
-
logger.log({
|
|
798
|
-
level: "info",
|
|
799
|
-
message: `Transaction created: ECN=${data.ecn}, Type=${data.requestType}`
|
|
800
|
-
});
|
|
801
|
-
return result;
|
|
802
|
-
} catch (error) {
|
|
803
|
-
logger.log({
|
|
804
|
-
level: "error",
|
|
805
|
-
message: `Failed to create transaction: ${error.message}`
|
|
806
|
-
});
|
|
807
|
-
throw error;
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
function updateTransactionResponse(ecn, data) {
|
|
811
|
-
try {
|
|
812
|
-
const database = getDatabase();
|
|
813
|
-
const now = Date.now();
|
|
814
|
-
const stmt = database.prepare(`
|
|
815
|
-
UPDATE transactions
|
|
816
|
-
SET response_hex = ?,
|
|
817
|
-
response_data = ?,
|
|
818
|
-
response_timestamp = ?,
|
|
819
|
-
status = ?,
|
|
820
|
-
updated_at = ?
|
|
821
|
-
WHERE ecn = ?
|
|
822
|
-
`);
|
|
823
|
-
const result = stmt.run(
|
|
824
|
-
data.responseHex,
|
|
825
|
-
JSON.stringify(data.responseData || {}),
|
|
826
|
-
now,
|
|
827
|
-
data.status || "COMPLETED",
|
|
828
|
-
now,
|
|
829
|
-
ecn
|
|
830
|
-
);
|
|
831
|
-
logger.log({
|
|
832
|
-
level: "info",
|
|
833
|
-
message: `Transaction updated: ECN=${ecn}, Status=${data.status || "COMPLETED"}`
|
|
834
|
-
});
|
|
835
|
-
return result;
|
|
836
|
-
} catch (error) {
|
|
837
|
-
logger.log({
|
|
838
|
-
level: "error",
|
|
839
|
-
message: `Failed to update transaction: ${error.message}`
|
|
840
|
-
});
|
|
841
|
-
throw error;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
function getTransactionByEcn(ecn) {
|
|
845
|
-
try {
|
|
846
|
-
const database = getDatabase();
|
|
847
|
-
const stmt = database.prepare("SELECT * FROM transactions WHERE ecn = ?");
|
|
848
|
-
const row = stmt.get(ecn);
|
|
849
|
-
if (row) {
|
|
850
|
-
row.request_payload = JSON.parse(row.request_payload || "{}");
|
|
851
|
-
row.response_data = JSON.parse(row.response_data || "{}");
|
|
852
|
-
}
|
|
853
|
-
return row;
|
|
854
|
-
} catch (error) {
|
|
855
|
-
logger.log({
|
|
856
|
-
level: "error",
|
|
857
|
-
message: `Failed to get transaction: ${error.message}`
|
|
858
|
-
});
|
|
859
|
-
return null;
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
function getRecentTransactions(limit = 100) {
|
|
863
|
-
try {
|
|
864
|
-
const database = getDatabase();
|
|
865
|
-
const stmt = database.prepare(`
|
|
866
|
-
SELECT * FROM transactions
|
|
867
|
-
ORDER BY request_timestamp DESC
|
|
868
|
-
LIMIT ?
|
|
869
|
-
`);
|
|
870
|
-
const rows = stmt.all(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 recent transactions: ${error.message}`
|
|
880
|
-
});
|
|
881
|
-
return [];
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
function getTransactionsByStatus(status, limit = 100) {
|
|
885
|
-
try {
|
|
886
|
-
const database = getDatabase();
|
|
887
|
-
const stmt = database.prepare(`
|
|
888
|
-
SELECT * FROM transactions
|
|
889
|
-
WHERE status = ?
|
|
890
|
-
ORDER BY request_timestamp DESC
|
|
891
|
-
LIMIT ?
|
|
892
|
-
`);
|
|
893
|
-
const rows = stmt.all(status, limit);
|
|
894
|
-
return rows.map((row) => ({
|
|
895
|
-
...row,
|
|
896
|
-
request_payload: JSON.parse(row.request_payload || "{}"),
|
|
897
|
-
response_data: JSON.parse(row.response_data || "{}")
|
|
898
|
-
}));
|
|
899
|
-
} catch (error) {
|
|
900
|
-
logger.log({
|
|
901
|
-
level: "error",
|
|
902
|
-
message: `Failed to get transactions by status: ${error.message}`
|
|
903
|
-
});
|
|
904
|
-
return [];
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
function cleanupOldTransactions(daysToKeep = 90) {
|
|
908
|
-
try {
|
|
909
|
-
const database = getDatabase();
|
|
910
|
-
const cutoffTime = Date.now() - daysToKeep * 24 * 60 * 60 * 1e3;
|
|
911
|
-
const stmt = database.prepare("DELETE FROM transactions WHERE request_timestamp < ?");
|
|
912
|
-
const result = stmt.run(cutoffTime);
|
|
913
|
-
logger.log({
|
|
914
|
-
level: "info",
|
|
915
|
-
message: `Cleaned up ${result.changes} old transactions (older than ${daysToKeep} days)`
|
|
916
|
-
});
|
|
917
|
-
return result.changes;
|
|
918
|
-
} catch (error) {
|
|
919
|
-
logger.log({
|
|
920
|
-
level: "error",
|
|
921
|
-
message: `Failed to cleanup old transactions: ${error.message}`
|
|
922
|
-
});
|
|
923
|
-
return 0;
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
function closeDatabase() {
|
|
927
|
-
if (db) {
|
|
928
|
-
db.close();
|
|
929
|
-
db = null;
|
|
930
|
-
logger.log({
|
|
931
|
-
level: "info",
|
|
932
|
-
message: "Database connection closed"
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
module2.exports = {
|
|
937
|
-
initDatabase,
|
|
938
|
-
getDatabase,
|
|
939
|
-
createTransaction,
|
|
940
|
-
updateTransactionResponse,
|
|
941
|
-
getTransactionByEcn,
|
|
942
|
-
getRecentTransactions,
|
|
943
|
-
getTransactionsByStatus,
|
|
944
|
-
cleanupOldTransactions,
|
|
945
|
-
closeDatabase,
|
|
946
|
-
// Auto-purge control functions
|
|
947
|
-
startAutoPurge: require_autoPurge().startAutoPurge,
|
|
948
|
-
stopAutoPurge: require_autoPurge().stopAutoPurge,
|
|
949
|
-
isAutoPurgeRunning: require_autoPurge().isAutoPurgeRunning
|
|
950
|
-
};
|
|
951
|
-
}
|
|
952
|
-
});
|
|
953
|
-
|
|
954
667
|
// src/payment/responseHandler.js
|
|
955
668
|
var require_responseHandler = __commonJS({
|
|
956
669
|
"src/payment/responseHandler.js"(exports2, module2) {
|
|
957
670
|
var fs = require("fs");
|
|
671
|
+
var http = require("http");
|
|
672
|
+
var https = require("https");
|
|
958
673
|
var axios = require("axios");
|
|
674
|
+
var httpAgent = new http.Agent({ family: 4 });
|
|
675
|
+
var httpsAgent = new https.Agent({ family: 4 });
|
|
959
676
|
var { Queue, Node } = require_queue();
|
|
960
677
|
var { sendMessage: sendMessage2 } = require_sendMessage();
|
|
961
678
|
var logger = require_winston()(module2);
|
|
@@ -969,47 +686,53 @@ var require_responseHandler = __commonJS({
|
|
|
969
686
|
} = require_parser();
|
|
970
687
|
var fw = require_manageConfig();
|
|
971
688
|
var { xorCalculation } = require_utils_helper();
|
|
972
|
-
var { updateTransactionResponse } = require_transactionStore();
|
|
973
689
|
var queue2 = new Queue();
|
|
974
690
|
var paymentCloudUrl = null;
|
|
975
691
|
var paymentOrderId = null;
|
|
692
|
+
var activePaymentEcn = null;
|
|
976
693
|
module2.exports.setPaymentCloudUrl = (url) => {
|
|
977
694
|
paymentCloudUrl = url;
|
|
978
695
|
};
|
|
979
696
|
module2.exports.setPaymentOrderId = (orderId) => {
|
|
980
697
|
paymentOrderId = orderId;
|
|
981
698
|
};
|
|
699
|
+
module2.exports.setPaymentEcn = (ecn) => {
|
|
700
|
+
activePaymentEcn = ecn;
|
|
701
|
+
};
|
|
982
702
|
function sendPaymentResponseToCloud(translatedResponse) {
|
|
983
703
|
const cloudUrl = paymentCloudUrl || fw.getConfigByKey("cloud_url");
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
translatedResponse.retrievalReferenceNumber = paymentOrderId;
|
|
704
|
+
if (paymentOrderId && translatedResponse) {
|
|
705
|
+
translatedResponse.enhancedECRReferenceNumber = paymentOrderId;
|
|
987
706
|
}
|
|
988
707
|
paymentCloudUrl = null;
|
|
989
708
|
paymentOrderId = null;
|
|
990
709
|
if (!cloudUrl) {
|
|
991
|
-
logger.log({ level: "warn", message: "
|
|
710
|
+
logger.log({ level: "warn", message: "\u26A0\uFE0F cloud_url not configured, skipping payment response POST" });
|
|
992
711
|
return;
|
|
993
712
|
}
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
console.log(URl);
|
|
999
|
-
axios.post(URl, {
|
|
713
|
+
const targetUrl = `${cloudUrl}/payresponse/api/v1/nets/response`;
|
|
714
|
+
logger.log({ level: "info", message: `\u{1F4E1} POSTing payment response to: ${targetUrl}` });
|
|
715
|
+
logger.log({ level: "info", message: JSON.stringify(translatedResponse) });
|
|
716
|
+
axios.post(targetUrl, {
|
|
1000
717
|
netsRes: translatedResponse
|
|
718
|
+
}, {
|
|
719
|
+
httpAgent,
|
|
720
|
+
httpsAgent
|
|
1001
721
|
}).then((res) => {
|
|
1002
|
-
logger.log({ level: "info", message:
|
|
722
|
+
logger.log({ level: "info", message: `\u2705 Payment response sent to cloud: ${res.status}` });
|
|
1003
723
|
}).catch((err) => {
|
|
1004
|
-
logger.log({ level: "error", message:
|
|
724
|
+
logger.log({ level: "error", message: `\u274C Failed to send payment response to cloud: ${err.message}` });
|
|
1005
725
|
});
|
|
1006
726
|
}
|
|
1007
|
-
module2.exports.terminalStatus = (ecn) => {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
727
|
+
module2.exports.terminalStatus = (ecn, hexValue = null) => {
|
|
728
|
+
let fullHex = hexValue;
|
|
729
|
+
if (!fullHex) {
|
|
730
|
+
const length = queue2.size();
|
|
731
|
+
fullHex = "";
|
|
732
|
+
for (let i = 0; i < length; i++) {
|
|
733
|
+
const node = queue2.dequeue();
|
|
734
|
+
fullHex += node.data.hex;
|
|
735
|
+
}
|
|
1013
736
|
}
|
|
1014
737
|
const status = statusParser(fullHex, ecn);
|
|
1015
738
|
console.log(fullHex);
|
|
@@ -1017,18 +740,6 @@ var require_responseHandler = __commonJS({
|
|
|
1017
740
|
level: "info",
|
|
1018
741
|
message: status
|
|
1019
742
|
});
|
|
1020
|
-
try {
|
|
1021
|
-
updateTransactionResponse(ecn, {
|
|
1022
|
-
responseHex: fullHex,
|
|
1023
|
-
responseData: { status },
|
|
1024
|
-
status: status ? "COMPLETED" : "FAILED"
|
|
1025
|
-
});
|
|
1026
|
-
} catch (error) {
|
|
1027
|
-
logger.log({
|
|
1028
|
-
level: "error",
|
|
1029
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1030
|
-
});
|
|
1031
|
-
}
|
|
1032
743
|
if (status) {
|
|
1033
744
|
fw.updateConfig({ terminal_status: true });
|
|
1034
745
|
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
@@ -1045,26 +756,17 @@ var require_responseHandler = __commonJS({
|
|
|
1045
756
|
});
|
|
1046
757
|
}
|
|
1047
758
|
};
|
|
1048
|
-
module2.exports.terminalLogon = (ecn) => {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
759
|
+
module2.exports.terminalLogon = (ecn, hexValue = null) => {
|
|
760
|
+
let fullHex = hexValue;
|
|
761
|
+
if (!fullHex) {
|
|
762
|
+
const length = queue2.size();
|
|
763
|
+
fullHex = "";
|
|
764
|
+
for (let i = 0; i < length; i++) {
|
|
765
|
+
const node = queue2.dequeue();
|
|
766
|
+
fullHex += node.data.hex;
|
|
767
|
+
}
|
|
1054
768
|
}
|
|
1055
769
|
const response = logonParser(fullHex, ecn);
|
|
1056
|
-
try {
|
|
1057
|
-
updateTransactionResponse(ecn, {
|
|
1058
|
-
responseHex: fullHex,
|
|
1059
|
-
responseData: response,
|
|
1060
|
-
status: response.status ? "COMPLETED" : "FAILED"
|
|
1061
|
-
});
|
|
1062
|
-
} catch (error) {
|
|
1063
|
-
logger.log({
|
|
1064
|
-
level: "error",
|
|
1065
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1066
|
-
});
|
|
1067
|
-
}
|
|
1068
770
|
if (response.status) {
|
|
1069
771
|
fw.updateConfig({ last_logon: /* @__PURE__ */ new Date() });
|
|
1070
772
|
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
@@ -1089,25 +791,18 @@ var require_responseHandler = __commonJS({
|
|
|
1089
791
|
}
|
|
1090
792
|
return fullHex;
|
|
1091
793
|
};
|
|
1092
|
-
module2.exports.terminalPayment = (hexValue,
|
|
794
|
+
module2.exports.terminalPayment = (hexValue, ecnArg) => {
|
|
795
|
+
const ecn = activePaymentEcn || ecnArg;
|
|
1093
796
|
const parsedValue = netsPaymentParser(hexValue, ecn);
|
|
1094
797
|
const response = jsonProcessor(parsedValue);
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
responseHex: hexValue,
|
|
1098
|
-
responseData: response,
|
|
1099
|
-
status: response.translated?.status || "UNKNOWN"
|
|
1100
|
-
});
|
|
1101
|
-
} catch (error) {
|
|
1102
|
-
logger.log({
|
|
1103
|
-
level: "error",
|
|
1104
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1105
|
-
});
|
|
798
|
+
if (response.translated) {
|
|
799
|
+
activePaymentEcn = null;
|
|
1106
800
|
}
|
|
1107
801
|
if (response.translated) {
|
|
1108
802
|
if (response.translated.status == "APPROVED") {
|
|
1109
803
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1110
804
|
success: true,
|
|
805
|
+
status: "SUCCESS",
|
|
1111
806
|
response,
|
|
1112
807
|
action: "COMPLETED"
|
|
1113
808
|
});
|
|
@@ -1129,24 +824,17 @@ var require_responseHandler = __commonJS({
|
|
|
1129
824
|
}
|
|
1130
825
|
}
|
|
1131
826
|
};
|
|
1132
|
-
module2.exports.terminalCreditPayment = (hexValue,
|
|
827
|
+
module2.exports.terminalCreditPayment = (hexValue, ecnArg) => {
|
|
828
|
+
const ecn = activePaymentEcn || ecnArg;
|
|
1133
829
|
const parsedValue = creditPaymentParser(hexValue, ecn);
|
|
1134
830
|
const response = jsonProcessor(parsedValue);
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
responseHex: hexValue,
|
|
1138
|
-
responseData: response,
|
|
1139
|
-
status: response.translated?.status || "UNKNOWN"
|
|
1140
|
-
});
|
|
1141
|
-
} catch (error) {
|
|
1142
|
-
logger.log({
|
|
1143
|
-
level: "error",
|
|
1144
|
-
message: `Failed to update transaction: ${error.message}`
|
|
1145
|
-
});
|
|
831
|
+
if (response.translated) {
|
|
832
|
+
activePaymentEcn = null;
|
|
1146
833
|
}
|
|
1147
834
|
if (response.translated) {
|
|
1148
835
|
if (response.translated.status == "APPROVED") {
|
|
1149
836
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
837
|
+
success: true,
|
|
1150
838
|
status: "SUCCESS",
|
|
1151
839
|
response,
|
|
1152
840
|
action: "COMPLETED"
|
|
@@ -1190,11 +878,12 @@ var require_responseHandler = __commonJS({
|
|
|
1190
878
|
}
|
|
1191
879
|
};
|
|
1192
880
|
module2.exports.checkLrc = (hexValue) => {
|
|
881
|
+
if (!hexValue || hexValue.length < 4) return false;
|
|
1193
882
|
const value = hexValue.substring(2, hexValue.length - 2);
|
|
1194
|
-
const xor = xorCalculation(value);
|
|
1195
|
-
const lrcCheck = hexValue.substring(hexValue.length - 2, hexValue.length);
|
|
1196
|
-
|
|
1197
|
-
return xor
|
|
883
|
+
const xor = xorCalculation(value).toUpperCase();
|
|
884
|
+
const lrcCheck = hexValue.substring(hexValue.length - 2, hexValue.length).toUpperCase();
|
|
885
|
+
logger.info({ level: "info", message: `LRC Check: Calculated ${xor} vs Received ${lrcCheck}` });
|
|
886
|
+
return xor === lrcCheck;
|
|
1198
887
|
};
|
|
1199
888
|
}
|
|
1200
889
|
});
|
|
@@ -1217,93 +906,47 @@ var require_communication = __commonJS({
|
|
|
1217
906
|
terminalStatus,
|
|
1218
907
|
fetchFullDetails,
|
|
1219
908
|
checkLrc,
|
|
1220
|
-
verifyParser
|
|
909
|
+
verifyParser,
|
|
910
|
+
setPaymentEcn
|
|
1221
911
|
} = require_responseHandler();
|
|
1222
912
|
var { sendMessage: sendMessage2 } = require_sendMessage();
|
|
1223
913
|
var fw = require_manageConfig();
|
|
1224
|
-
var { createTransaction } = require_transactionStore();
|
|
1225
914
|
var { initialiseRequest } = require_httpRequest();
|
|
1226
915
|
var requestType;
|
|
1227
916
|
var calculated;
|
|
1228
|
-
var isTimeOutset = false;
|
|
1229
917
|
var MAX_COUNT = 3;
|
|
1230
918
|
var WRONG_LRC_MAX_COUNT = 3;
|
|
1231
919
|
var count;
|
|
1232
920
|
var lrcCount;
|
|
1233
921
|
var ackOrNack;
|
|
1234
922
|
var requestPayload;
|
|
923
|
+
var ackTimeout = null;
|
|
1235
924
|
var ACK = Buffer.alloc(1, 6, "hex");
|
|
1236
925
|
var NACK = Buffer.alloc(1, 21, "hex");
|
|
926
|
+
var dataBuffer = Buffer.alloc(0);
|
|
1237
927
|
var config = fw.getConfig();
|
|
1238
|
-
var
|
|
1239
|
-
|
|
1240
|
-
setTimeout(() => {
|
|
1241
|
-
isTimeOutset = false;
|
|
1242
|
-
terminalStatus(calculated.ecn);
|
|
1243
|
-
}, 3e3);
|
|
1244
|
-
}
|
|
1245
|
-
if (requestType == "LOGON") {
|
|
1246
|
-
setTimeout(() => {
|
|
1247
|
-
isTimeOutset = false;
|
|
1248
|
-
terminalLogon(calculated.ecn);
|
|
1249
|
-
}, 5e3);
|
|
1250
|
-
}
|
|
1251
|
-
if (requestType == "PAYMENT") {
|
|
1252
|
-
setTimeout(() => {
|
|
1253
|
-
isTimeOutset = false;
|
|
1254
|
-
lrcCount--;
|
|
1255
|
-
if (lrcCount >= 0) {
|
|
1256
|
-
const hexValue = fetchFullDetails();
|
|
1257
|
-
const lrcResponse = checkLrc(hexValue);
|
|
1258
|
-
if (!lrcResponse) {
|
|
1259
|
-
logger.log({
|
|
1260
|
-
level: "info",
|
|
1261
|
-
message: `Terminal send response data with wrong LRC, POS expected to send NACK`
|
|
1262
|
-
});
|
|
1263
|
-
global.port.write(NACK);
|
|
1264
|
-
} else {
|
|
1265
|
-
logger.log({
|
|
1266
|
-
level: "info",
|
|
1267
|
-
message: `Terminal resend response data with correct LRC, POS expected to send ACK`
|
|
1268
|
-
});
|
|
1269
|
-
global.port.write(ACK);
|
|
1270
|
-
terminalPayment(hexValue, calculated.ecn);
|
|
1271
|
-
}
|
|
1272
|
-
} else {
|
|
1273
|
-
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1274
|
-
success: true,
|
|
1275
|
-
action: "APPROVED_FLAGGED"
|
|
1276
|
-
});
|
|
1277
|
-
}
|
|
1278
|
-
}, 8e3);
|
|
1279
|
-
}
|
|
1280
|
-
if (requestType === "CREDIT_PAYMENT") {
|
|
1281
|
-
setTimeout(() => {
|
|
1282
|
-
isTimeOutset = false;
|
|
1283
|
-
const hexValue = fetchFullDetails();
|
|
1284
|
-
const lrcResponse = checkLrc(hexValue);
|
|
1285
|
-
if (!lrcResponse) {
|
|
1286
|
-
global.port.write(NACK);
|
|
1287
|
-
} else {
|
|
1288
|
-
global.port.write(ACK);
|
|
1289
|
-
terminalCreditPayment(hexValue, calculated.ecn);
|
|
1290
|
-
}
|
|
1291
|
-
}, 8e3);
|
|
1292
|
-
}
|
|
1293
|
-
};
|
|
928
|
+
var lastPaymentReference = null;
|
|
929
|
+
var lastPaymentTime = 0;
|
|
1294
930
|
module2.exports.reset = () => {
|
|
1295
931
|
count = MAX_COUNT;
|
|
1296
932
|
lrcCount = WRONG_LRC_MAX_COUNT;
|
|
1297
933
|
ackOrNack = null;
|
|
934
|
+
dataBuffer = Buffer.alloc(0);
|
|
935
|
+
if (ackTimeout) {
|
|
936
|
+
clearTimeout(ackTimeout);
|
|
937
|
+
ackTimeout = null;
|
|
938
|
+
}
|
|
1298
939
|
};
|
|
1299
940
|
module2.exports.checkACKorNACK = () => {
|
|
1300
|
-
|
|
941
|
+
if (ackTimeout) clearTimeout(ackTimeout);
|
|
942
|
+
ackTimeout = setTimeout(() => {
|
|
943
|
+
ackTimeout = null;
|
|
1301
944
|
if (ackOrNack == null) {
|
|
1302
945
|
count--;
|
|
1303
946
|
if (count >= 0) {
|
|
1304
947
|
logger.log({
|
|
1305
948
|
level: "info",
|
|
1306
|
-
message: `POS send Purchase command, Terminal doesn't send any reply
|
|
949
|
+
message: `POS send Purchase command, Terminal doesn't send any reply. Retrying...`
|
|
1307
950
|
});
|
|
1308
951
|
resendData();
|
|
1309
952
|
} else {
|
|
@@ -1313,7 +956,7 @@ var require_communication = __commonJS({
|
|
|
1313
956
|
});
|
|
1314
957
|
}
|
|
1315
958
|
}
|
|
1316
|
-
},
|
|
959
|
+
}, 4e3);
|
|
1317
960
|
};
|
|
1318
961
|
var resendData = () => {
|
|
1319
962
|
if (requestType == "STATUS_CHECK") {
|
|
@@ -1329,64 +972,149 @@ var require_communication = __commonJS({
|
|
|
1329
972
|
exports2.checkACKorNACK();
|
|
1330
973
|
};
|
|
1331
974
|
module2.exports.initialize = async (socketConnection = null) => {
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
});
|
|
1338
|
-
} else {
|
|
1339
|
-
logger.log({
|
|
1340
|
-
level: "info",
|
|
1341
|
-
message: "No socket connection provided, messages will be logged only"
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
if (!config.terminal || config.terminal === "enable") {
|
|
1345
|
-
global.port = new SerialPort({
|
|
1346
|
-
path: config.simulation ? config.simulationPort : config.com,
|
|
1347
|
-
baudRate: 9600,
|
|
1348
|
-
dataBits: 8,
|
|
1349
|
-
stopBits: 1,
|
|
1350
|
-
parity: "none"
|
|
1351
|
-
});
|
|
1352
|
-
global.port.on("open", () => {
|
|
975
|
+
try {
|
|
976
|
+
if (socketConnection) {
|
|
977
|
+
global.io = socketConnection;
|
|
978
|
+
}
|
|
979
|
+
if (global.io) {
|
|
1353
980
|
logger.log({
|
|
1354
981
|
level: "info",
|
|
1355
|
-
message:
|
|
982
|
+
message: "Socket connection available for terminal communication"
|
|
1356
983
|
});
|
|
1357
|
-
}
|
|
1358
|
-
global.port.on("data", (data) => {
|
|
984
|
+
} else {
|
|
1359
985
|
logger.log({
|
|
1360
|
-
level: "
|
|
1361
|
-
message:
|
|
986
|
+
level: "warn",
|
|
987
|
+
message: "No socket connection (global.io) found. Messages will be logged only."
|
|
1362
988
|
});
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
level: "info",
|
|
1369
|
-
|
|
1370
|
-
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
action: "TERMINAL_ERROR"
|
|
1376
|
-
});
|
|
1377
|
-
return;
|
|
1378
|
-
}
|
|
1379
|
-
} else if (data.toString("hex").length == 2 && data.toString("hex") == "06") {
|
|
1380
|
-
ackOrNack = data;
|
|
1381
|
-
} else if (data.length > 2) {
|
|
1382
|
-
global.port.write(ACK);
|
|
1383
|
-
if (!isTimeOutset) {
|
|
1384
|
-
isTimeOutset = true;
|
|
1385
|
-
triggerTimer();
|
|
989
|
+
}
|
|
990
|
+
if (!config.terminal || config.terminal === "enable") {
|
|
991
|
+
if (global.port) {
|
|
992
|
+
try {
|
|
993
|
+
if (global.port.isOpen) {
|
|
994
|
+
logger.log({ level: "info", message: "Closing existing serial port..." });
|
|
995
|
+
global.port.close();
|
|
996
|
+
}
|
|
997
|
+
global.port.removeAllListeners();
|
|
998
|
+
if (global.port.destroy) global.port.destroy();
|
|
999
|
+
} catch (e) {
|
|
1000
|
+
logger.log({ level: "debug", message: `Error during port cleanup: ${e.message}` });
|
|
1386
1001
|
}
|
|
1387
|
-
|
|
1002
|
+
global.port = null;
|
|
1388
1003
|
}
|
|
1389
|
-
|
|
1004
|
+
const portPath = config.simulation ? config.simulationPort : config.com;
|
|
1005
|
+
const openPort = () => {
|
|
1006
|
+
logger.log({ level: "info", message: `Initializing Serial Port on ${portPath}...` });
|
|
1007
|
+
global.port = new SerialPort({
|
|
1008
|
+
path: portPath,
|
|
1009
|
+
baudRate: 9600,
|
|
1010
|
+
dataBits: 8,
|
|
1011
|
+
stopBits: 1,
|
|
1012
|
+
parity: "none",
|
|
1013
|
+
autoOpen: false,
|
|
1014
|
+
lock: false,
|
|
1015
|
+
// Disable locking to prevent "Access Denied" or "Error 31" stuck files
|
|
1016
|
+
rtscts: false
|
|
1017
|
+
// Explicitly disable flow control
|
|
1018
|
+
});
|
|
1019
|
+
global.port.on("data", (data) => {
|
|
1020
|
+
logger.log({ level: "info", message: `Raw data: ${data.toString("hex")}` });
|
|
1021
|
+
dataBuffer = Buffer.concat([dataBuffer, data]);
|
|
1022
|
+
exports2.processIncomingData();
|
|
1023
|
+
});
|
|
1024
|
+
global.port.on("error", (err) => {
|
|
1025
|
+
logger.error({ level: "error", message: `SerialPort Error Event: ${err.message}` });
|
|
1026
|
+
});
|
|
1027
|
+
setTimeout(() => {
|
|
1028
|
+
global.port.open((err) => {
|
|
1029
|
+
if (err) {
|
|
1030
|
+
logger.error({ level: "error", message: `Error opening serial port ${portPath}: ${err.message}` });
|
|
1031
|
+
if (err.message.includes("31")) {
|
|
1032
|
+
logger.log({ level: "warn", message: "Error 31 detected. Attempting hardware reset retry in 2s..." });
|
|
1033
|
+
setTimeout(openPort, 2e3);
|
|
1034
|
+
}
|
|
1035
|
+
} else {
|
|
1036
|
+
logger.log({ level: "info", message: `Serial port ${portPath} opened successfully` });
|
|
1037
|
+
setTimeout(() => exports2.sentToTerminal("STATUS_CHECK"), 1e3);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
}, 1500);
|
|
1041
|
+
};
|
|
1042
|
+
openPort();
|
|
1043
|
+
}
|
|
1044
|
+
} catch (err) {
|
|
1045
|
+
console.error("Serial initialization error:", err.message);
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
module2.exports.processIncomingData = () => {
|
|
1049
|
+
while (dataBuffer.length >= 3) {
|
|
1050
|
+
if (dataBuffer[0] === 6 || dataBuffer[0] === 21) {
|
|
1051
|
+
const byte = dataBuffer[0];
|
|
1052
|
+
ackOrNack = Buffer.from([byte]);
|
|
1053
|
+
dataBuffer = dataBuffer.slice(1);
|
|
1054
|
+
logger.log({ level: "info", message: `Terminal sent ${byte === 6 ? "ACK" : "NACK"} (0x${byte.toString(16)})` });
|
|
1055
|
+
if (byte === 21) {
|
|
1056
|
+
count--;
|
|
1057
|
+
if (count >= 0) resendData();
|
|
1058
|
+
else sendMessage2("clientRoom", "PAYMENT_MESSAGE", { success: true, action: "TERMINAL_ERROR" });
|
|
1059
|
+
}
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
const stxIndex = dataBuffer.indexOf(2);
|
|
1063
|
+
if (stxIndex === -1) {
|
|
1064
|
+
dataBuffer = Buffer.alloc(0);
|
|
1065
|
+
break;
|
|
1066
|
+
}
|
|
1067
|
+
if (stxIndex > 0) {
|
|
1068
|
+
dataBuffer = dataBuffer.slice(stxIndex);
|
|
1069
|
+
}
|
|
1070
|
+
if (dataBuffer.length < 3) break;
|
|
1071
|
+
if (dataBuffer.length < 3) break;
|
|
1072
|
+
const lenBytes = dataBuffer.slice(1, 3).toString("hex");
|
|
1073
|
+
const bodyLen = parseInt(lenBytes, 10);
|
|
1074
|
+
const expectedFrameSize = 1 + 2 + bodyLen + 2;
|
|
1075
|
+
if (dataBuffer.length < expectedFrameSize) break;
|
|
1076
|
+
if (dataBuffer[3 + bodyLen] === 3) {
|
|
1077
|
+
const frame = dataBuffer.slice(0, expectedFrameSize);
|
|
1078
|
+
dataBuffer = dataBuffer.slice(expectedFrameSize);
|
|
1079
|
+
exports2.handleFullMessage(frame);
|
|
1080
|
+
} else {
|
|
1081
|
+
const etxIndex = dataBuffer.indexOf(3, 1);
|
|
1082
|
+
if (etxIndex !== -1 && dataBuffer.length > etxIndex + 1) {
|
|
1083
|
+
const frame = dataBuffer.slice(0, etxIndex + 2);
|
|
1084
|
+
dataBuffer = dataBuffer.slice(etxIndex + 2);
|
|
1085
|
+
exports2.handleFullMessage(frame);
|
|
1086
|
+
} else {
|
|
1087
|
+
if (dataBuffer.length > 1024) dataBuffer = Buffer.alloc(0);
|
|
1088
|
+
break;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
module2.exports.handleFullMessage = (fullFrame) => {
|
|
1094
|
+
const { checkLrc: checkLrc2, terminalStatus: terminalStatus2, terminalLogon: terminalLogon2, terminalPayment: terminalPayment2, terminalCreditPayment: terminalCreditPayment2 } = require_responseHandler();
|
|
1095
|
+
if (ackTimeout) {
|
|
1096
|
+
clearTimeout(ackTimeout);
|
|
1097
|
+
ackTimeout = null;
|
|
1098
|
+
}
|
|
1099
|
+
ackOrNack = Buffer.from([6]);
|
|
1100
|
+
const hexFrame = fullFrame.toString("hex").toUpperCase();
|
|
1101
|
+
logger.log({ level: "info", message: `Full frame processed: ${hexFrame}` });
|
|
1102
|
+
if (checkLrc2(hexFrame)) {
|
|
1103
|
+
logger.log({ level: "info", message: `LRC Correct. Sending ACK (0x06).` });
|
|
1104
|
+
global.port.write(ACK);
|
|
1105
|
+
logger.log({ level: "info", message: `requestType: ${requestType}` });
|
|
1106
|
+
if (requestType === "STATUS_CHECK") {
|
|
1107
|
+
terminalStatus2(calculated?.ecn, hexFrame);
|
|
1108
|
+
} else if (requestType === "LOGON") {
|
|
1109
|
+
terminalLogon2(calculated?.ecn, hexFrame);
|
|
1110
|
+
} else if (requestType === "PAYMENT") {
|
|
1111
|
+
terminalPayment2(hexFrame, calculated?.ecn);
|
|
1112
|
+
} else if (requestType === "CREDIT_PAYMENT") {
|
|
1113
|
+
terminalCreditPayment2(hexFrame, calculated?.ecn);
|
|
1114
|
+
}
|
|
1115
|
+
} else {
|
|
1116
|
+
logger.log({ level: "error", message: `LRC Incorrect. Sending NACK (0x15).` });
|
|
1117
|
+
global.port.write(NACK);
|
|
1390
1118
|
}
|
|
1391
1119
|
};
|
|
1392
1120
|
module2.exports.sentToTerminal = (type, body) => {
|
|
@@ -1394,23 +1122,6 @@ var require_communication = __commonJS({
|
|
|
1394
1122
|
requestPayload = body;
|
|
1395
1123
|
if (requestType == "STATUS_CHECK") {
|
|
1396
1124
|
calculated = generateStatusReq();
|
|
1397
|
-
logger.log({
|
|
1398
|
-
level: "info",
|
|
1399
|
-
message: `Calculated STATUS_CHECK request: ${calculated.buffer.toString("hex")}`
|
|
1400
|
-
});
|
|
1401
|
-
try {
|
|
1402
|
-
createTransaction({
|
|
1403
|
-
ecn: calculated.ecn,
|
|
1404
|
-
requestType,
|
|
1405
|
-
requestPayload: null,
|
|
1406
|
-
requestHex: calculated.buffer.toString("hex")
|
|
1407
|
-
});
|
|
1408
|
-
} catch (error) {
|
|
1409
|
-
logger.log({
|
|
1410
|
-
level: "error",
|
|
1411
|
-
message: `Failed to store transaction: ${error.message}`
|
|
1412
|
-
});
|
|
1413
|
-
}
|
|
1414
1125
|
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
1415
1126
|
status: "INITIATED",
|
|
1416
1127
|
action: "STATUS_CHECK_INITIATED"
|
|
@@ -1419,23 +1130,6 @@ var require_communication = __commonJS({
|
|
|
1419
1130
|
}
|
|
1420
1131
|
if (requestType == "LOGON") {
|
|
1421
1132
|
calculated = generateLogonRequest();
|
|
1422
|
-
logger.log({
|
|
1423
|
-
level: "info",
|
|
1424
|
-
message: `Calculated LOGON request: ${calculated.buffer.toString("hex")}`
|
|
1425
|
-
});
|
|
1426
|
-
try {
|
|
1427
|
-
createTransaction({
|
|
1428
|
-
ecn: calculated.ecn,
|
|
1429
|
-
requestType,
|
|
1430
|
-
requestPayload: null,
|
|
1431
|
-
requestHex: calculated.buffer.toString("hex")
|
|
1432
|
-
});
|
|
1433
|
-
} catch (error) {
|
|
1434
|
-
logger.log({
|
|
1435
|
-
level: "error",
|
|
1436
|
-
message: `Failed to store transaction: ${error.message}`
|
|
1437
|
-
});
|
|
1438
|
-
}
|
|
1439
1133
|
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
1440
1134
|
status: "INITIATED",
|
|
1441
1135
|
action: "LOGON_INITIATED"
|
|
@@ -1443,29 +1137,30 @@ var require_communication = __commonJS({
|
|
|
1443
1137
|
global.port.write(calculated.buffer);
|
|
1444
1138
|
}
|
|
1445
1139
|
if (requestType == "PAYMENT" || requestType == "CREDIT_PAYMENT") {
|
|
1140
|
+
if (body && body.reference) {
|
|
1141
|
+
const now = Date.now();
|
|
1142
|
+
if (body.reference === lastPaymentReference && now - lastPaymentTime < 1e4) {
|
|
1143
|
+
logger.log({
|
|
1144
|
+
level: "warn",
|
|
1145
|
+
message: `Duplicate payment request received for reference ${body.reference} within 10s. Ignored.`
|
|
1146
|
+
});
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
lastPaymentReference = body.reference;
|
|
1150
|
+
lastPaymentTime = now;
|
|
1151
|
+
}
|
|
1446
1152
|
exports2.reset();
|
|
1153
|
+
const { setPaymentCloudUrl, setPaymentOrderId } = require_responseHandler();
|
|
1154
|
+
if (body && body.cloud_url) setPaymentCloudUrl(body.cloud_url);
|
|
1155
|
+
if (body && (body.reference || body.orderId)) setPaymentOrderId(body.reference || body.orderId);
|
|
1447
1156
|
calculated = generatePaymentRequest(body);
|
|
1448
|
-
|
|
1449
|
-
level: "info",
|
|
1450
|
-
message: `Calculated payment request: ${calculated.hexString || calculated.buffer && calculated.buffer.toString("hex")}`
|
|
1451
|
-
});
|
|
1452
|
-
try {
|
|
1453
|
-
createTransaction({
|
|
1454
|
-
ecn: calculated.ecn,
|
|
1455
|
-
requestType,
|
|
1456
|
-
requestPayload: body,
|
|
1457
|
-
requestHex: calculated.hexString
|
|
1458
|
-
});
|
|
1459
|
-
} catch (error) {
|
|
1460
|
-
logger.log({
|
|
1461
|
-
level: "error",
|
|
1462
|
-
message: `Failed to store transaction: ${error.message}`
|
|
1463
|
-
});
|
|
1464
|
-
}
|
|
1157
|
+
console.log(calculated);
|
|
1465
1158
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1466
1159
|
status: "INITIATED",
|
|
1467
|
-
action: "PAYMENT_INITIATED"
|
|
1160
|
+
action: "PAYMENT_INITIATED",
|
|
1161
|
+
orderId: body.reference || body.orderId
|
|
1468
1162
|
});
|
|
1163
|
+
if (calculated && calculated.ecn) setPaymentEcn(calculated.ecn);
|
|
1469
1164
|
global.port.write(calculated.buffer);
|
|
1470
1165
|
exports2.checkACKorNACK();
|
|
1471
1166
|
}
|
|
@@ -1473,142 +1168,6 @@ var require_communication = __commonJS({
|
|
|
1473
1168
|
}
|
|
1474
1169
|
});
|
|
1475
1170
|
|
|
1476
|
-
// src/db/listTransactions.js
|
|
1477
|
-
var require_listTransactions = __commonJS({
|
|
1478
|
-
"src/db/listTransactions.js"(exports2, module2) {
|
|
1479
|
-
var { getRecentTransactions, getTransactionByEcn, getTransactionsByStatus } = require_transactionStore();
|
|
1480
|
-
var logger = require_winston()(module2);
|
|
1481
|
-
function listRecentTransactions(limit = 10) {
|
|
1482
|
-
try {
|
|
1483
|
-
const transactions = getRecentTransactions(limit);
|
|
1484
|
-
if (transactions.length === 0) {
|
|
1485
|
-
console.log("No transactions found in database.");
|
|
1486
|
-
return [];
|
|
1487
|
-
}
|
|
1488
|
-
console.log(`
|
|
1489
|
-
\u{1F4CA} Recent ${transactions.length} Transaction(s)
|
|
1490
|
-
`);
|
|
1491
|
-
console.log("=".repeat(80));
|
|
1492
|
-
transactions.forEach((txn, index) => {
|
|
1493
|
-
const time = new Date(txn.request_timestamp).toLocaleString();
|
|
1494
|
-
const duration = txn.response_timestamp ? `${txn.response_timestamp - txn.request_timestamp}ms` : "Pending";
|
|
1495
|
-
console.log(`
|
|
1496
|
-
${index + 1}. ECN: ${txn.ecn}`);
|
|
1497
|
-
console.log(` Type: ${txn.request_type}`);
|
|
1498
|
-
console.log(` Status: ${txn.status}`);
|
|
1499
|
-
console.log(` Time: ${time}`);
|
|
1500
|
-
console.log(` Duration: ${duration}`);
|
|
1501
|
-
if (txn.request_type === "PAYMENT" || txn.request_type === "CREDIT_PAYMENT") {
|
|
1502
|
-
const amount = txn.request_payload.amount ? `$${(txn.request_payload.amount / 100).toFixed(2)}` : "N/A";
|
|
1503
|
-
console.log(` Amount: ${amount}`);
|
|
1504
|
-
console.log(` Reference: ${txn.request_payload.reference || "N/A"}`);
|
|
1505
|
-
}
|
|
1506
|
-
});
|
|
1507
|
-
console.log("\n" + "=".repeat(80) + "\n");
|
|
1508
|
-
return transactions;
|
|
1509
|
-
} catch (error) {
|
|
1510
|
-
if (error.code === "SQLITE_CANTOPEN") {
|
|
1511
|
-
console.log("\u2139\uFE0F Database not found. No transactions have been created yet.");
|
|
1512
|
-
console.log(" Transactions will be automatically stored when you send requests to the terminal.\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 listTransactionsByStatus(status, limit = 50) {
|
|
1523
|
-
try {
|
|
1524
|
-
const transactions = getTransactionsByStatus(status, limit);
|
|
1525
|
-
if (transactions.length === 0) {
|
|
1526
|
-
console.log(`No ${status} transactions found.`);
|
|
1527
|
-
return [];
|
|
1528
|
-
}
|
|
1529
|
-
console.log(`
|
|
1530
|
-
\u{1F4CA} ${status} Transactions (${transactions.length})
|
|
1531
|
-
`);
|
|
1532
|
-
console.log("=".repeat(80));
|
|
1533
|
-
transactions.forEach((txn, index) => {
|
|
1534
|
-
const time = new Date(txn.request_timestamp).toLocaleString();
|
|
1535
|
-
console.log(`
|
|
1536
|
-
${index + 1}. ECN: ${txn.ecn} | ${txn.request_type} | ${time}`);
|
|
1537
|
-
});
|
|
1538
|
-
console.log("\n" + "=".repeat(80) + "\n");
|
|
1539
|
-
return transactions;
|
|
1540
|
-
} catch (error) {
|
|
1541
|
-
if (error.code === "SQLITE_CANTOPEN") {
|
|
1542
|
-
console.log("\u2139\uFE0F Database not found. No transactions have been created yet.\n");
|
|
1543
|
-
return [];
|
|
1544
|
-
}
|
|
1545
|
-
logger.log({
|
|
1546
|
-
level: "error",
|
|
1547
|
-
message: `Failed to list transactions: ${error.message}`
|
|
1548
|
-
});
|
|
1549
|
-
throw error;
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
function getTransactionDetails(ecn) {
|
|
1553
|
-
try {
|
|
1554
|
-
const transaction = getTransactionByEcn(ecn);
|
|
1555
|
-
if (!transaction) {
|
|
1556
|
-
console.log(`Transaction with ECN ${ecn} not found.`);
|
|
1557
|
-
return null;
|
|
1558
|
-
}
|
|
1559
|
-
console.log(`
|
|
1560
|
-
\u{1F4C4} Transaction Details: ${ecn}
|
|
1561
|
-
`);
|
|
1562
|
-
console.log("=".repeat(80));
|
|
1563
|
-
console.log(`Type: ${transaction.request_type}`);
|
|
1564
|
-
console.log(`Status: ${transaction.status}`);
|
|
1565
|
-
console.log(`Request Time: ${new Date(transaction.request_timestamp).toLocaleString()}`);
|
|
1566
|
-
if (transaction.response_timestamp) {
|
|
1567
|
-
console.log(`Response Time: ${new Date(transaction.response_timestamp).toLocaleString()}`);
|
|
1568
|
-
console.log(`Duration: ${transaction.response_timestamp - transaction.request_timestamp}ms`);
|
|
1569
|
-
}
|
|
1570
|
-
console.log(`
|
|
1571
|
-
Request Payload:`);
|
|
1572
|
-
console.log(JSON.stringify(transaction.request_payload, null, 2));
|
|
1573
|
-
if (transaction.response_data && Object.keys(transaction.response_data).length > 0) {
|
|
1574
|
-
console.log(`
|
|
1575
|
-
Response Data:`);
|
|
1576
|
-
console.log(JSON.stringify(transaction.response_data, null, 2));
|
|
1577
|
-
}
|
|
1578
|
-
console.log("\n" + "=".repeat(80) + "\n");
|
|
1579
|
-
return transaction;
|
|
1580
|
-
} catch (error) {
|
|
1581
|
-
if (error.code === "SQLITE_CANTOPEN") {
|
|
1582
|
-
console.log("\u2139\uFE0F Database not found. No transactions have been created yet.\n");
|
|
1583
|
-
return null;
|
|
1584
|
-
}
|
|
1585
|
-
logger.log({
|
|
1586
|
-
level: "error",
|
|
1587
|
-
message: `Failed to get transaction: ${error.message}`
|
|
1588
|
-
});
|
|
1589
|
-
throw error;
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
module2.exports = {
|
|
1593
|
-
listRecentTransactions,
|
|
1594
|
-
listTransactionsByStatus,
|
|
1595
|
-
getTransactionDetails
|
|
1596
|
-
};
|
|
1597
|
-
if (require.main === module2) {
|
|
1598
|
-
const args = process.argv.slice(2);
|
|
1599
|
-
if (args[0] === "--status" && args[1]) {
|
|
1600
|
-
const limit = args[2] ? parseInt(args[2]) : 50;
|
|
1601
|
-
listTransactionsByStatus(args[1], limit);
|
|
1602
|
-
} else if (args[0] === "--ecn" && args[1]) {
|
|
1603
|
-
getTransactionDetails(args[1]);
|
|
1604
|
-
} else {
|
|
1605
|
-
const limit = args[0] ? parseInt(args[0]) : 10;
|
|
1606
|
-
listRecentTransactions(limit);
|
|
1607
|
-
}
|
|
1608
|
-
}
|
|
1609
|
-
}
|
|
1610
|
-
});
|
|
1611
|
-
|
|
1612
1171
|
// src/index.js
|
|
1613
1172
|
var utilsHelper = require_utils_helper();
|
|
1614
1173
|
var winston = require_winston();
|
|
@@ -1620,8 +1179,6 @@ var hexRequest = require_hexRequest();
|
|
|
1620
1179
|
var httpRequest = require_httpRequest();
|
|
1621
1180
|
var parser = require_parser();
|
|
1622
1181
|
var responseHandler = require_responseHandler();
|
|
1623
|
-
var transactionStore = require_transactionStore();
|
|
1624
|
-
var listTransactions = require_listTransactions();
|
|
1625
1182
|
module.exports = {
|
|
1626
1183
|
...utilsHelper,
|
|
1627
1184
|
logger: winston,
|
|
@@ -1632,18 +1189,5 @@ module.exports = {
|
|
|
1632
1189
|
hexRequest,
|
|
1633
1190
|
httpRequest,
|
|
1634
1191
|
parser,
|
|
1635
|
-
responseHandler
|
|
1636
|
-
// Transaction store utilities
|
|
1637
|
-
getTransactionByEcn: transactionStore.getTransactionByEcn,
|
|
1638
|
-
getRecentTransactions: transactionStore.getRecentTransactions,
|
|
1639
|
-
getTransactionsByStatus: transactionStore.getTransactionsByStatus,
|
|
1640
|
-
cleanupOldTransactions: transactionStore.cleanupOldTransactions,
|
|
1641
|
-
// Auto-purge control
|
|
1642
|
-
startAutoPurge: transactionStore.startAutoPurge,
|
|
1643
|
-
stopAutoPurge: transactionStore.stopAutoPurge,
|
|
1644
|
-
isAutoPurgeRunning: transactionStore.isAutoPurgeRunning,
|
|
1645
|
-
// Transaction listing functions
|
|
1646
|
-
listRecentTransactions: listTransactions.listRecentTransactions,
|
|
1647
|
-
listTransactionsByStatus: listTransactions.listTransactionsByStatus,
|
|
1648
|
-
getTransactionDetails: listTransactions.getTransactionDetails
|
|
1192
|
+
responseHandler
|
|
1649
1193
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nets-service-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.11",
|
|
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
|
+
}
|