nets-service-sdk 1.1.9 → 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 +227 -698
- 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 = [];
|
|
@@ -376,7 +376,7 @@ var require_parser = __commonJS({
|
|
|
376
376
|
"src/payment/parser.js"(exports2, module2) {
|
|
377
377
|
var logger = require_winston()(module2);
|
|
378
378
|
module2.exports.statusParser = (hexCode, ecn) => {
|
|
379
|
-
const splitted = hexCode.split("
|
|
379
|
+
const splitted = hexCode.split("1C");
|
|
380
380
|
for (let i = 0; i < splitted.length; i++) {
|
|
381
381
|
if (splitted[i]?.toAsciiString()?.indexOf(ecn) > -1) {
|
|
382
382
|
logger.log({ level: "info", message: `statusParser: ECN ${ecn} found in response` });
|
|
@@ -388,7 +388,7 @@ var require_parser = __commonJS({
|
|
|
388
388
|
};
|
|
389
389
|
module2.exports.logonParser = (hexCode, ecn) => {
|
|
390
390
|
try {
|
|
391
|
-
const splitted = hexCode.split("
|
|
391
|
+
const splitted = hexCode.split("1C");
|
|
392
392
|
let status = false;
|
|
393
393
|
const json = {};
|
|
394
394
|
for (let i = 0; i < splitted.length; i++) {
|
|
@@ -419,13 +419,16 @@ var require_parser = __commonJS({
|
|
|
419
419
|
};
|
|
420
420
|
module2.exports.netsPaymentParser = (hexCode, ecn) => {
|
|
421
421
|
try {
|
|
422
|
-
const splitted = hexCode.split("
|
|
422
|
+
const splitted = hexCode.split("1C");
|
|
423
423
|
let status = false;
|
|
424
424
|
const json = {};
|
|
425
425
|
for (let i = 0; i < splitted.length; i++) {
|
|
426
426
|
const ascii = splitted[i].toAsciiString();
|
|
427
|
-
if (ascii?.indexOf(ecn) > -1) {
|
|
427
|
+
if (ascii?.indexOf(ecn) > -1 || i === 0 && ascii.length >= 12) {
|
|
428
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
|
+
}
|
|
429
432
|
} else {
|
|
430
433
|
const subStringArray = ascii.splitAtIndex(2);
|
|
431
434
|
if (subStringArray.length > 1) {
|
|
@@ -446,12 +449,12 @@ var require_parser = __commonJS({
|
|
|
446
449
|
};
|
|
447
450
|
module2.exports.creditPaymentParser = (hexCode, ecn) => {
|
|
448
451
|
try {
|
|
449
|
-
const splitted = hexCode.split("
|
|
452
|
+
const splitted = hexCode.split("1C");
|
|
450
453
|
let status = false;
|
|
451
454
|
const json = {};
|
|
452
455
|
for (let i = 0; i < splitted.length; i++) {
|
|
453
456
|
const ascii = splitted[i].toAsciiString();
|
|
454
|
-
if (ascii?.indexOf(ecn) > -1) {
|
|
457
|
+
if (ascii?.indexOf(ecn) > -1 || i === 0 && ascii.length >= 12) {
|
|
455
458
|
json["ECN"] = ascii.cleanUp();
|
|
456
459
|
} else {
|
|
457
460
|
const subStringArray = ascii.splitAtIndex(2);
|
|
@@ -654,303 +657,6 @@ var require_parser = __commonJS({
|
|
|
654
657
|
}
|
|
655
658
|
});
|
|
656
659
|
|
|
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
660
|
// src/payment/responseHandler.js
|
|
955
661
|
var require_responseHandler = __commonJS({
|
|
956
662
|
"src/payment/responseHandler.js"(exports2, module2) {
|
|
@@ -969,47 +675,49 @@ var require_responseHandler = __commonJS({
|
|
|
969
675
|
} = require_parser();
|
|
970
676
|
var fw = require_manageConfig();
|
|
971
677
|
var { xorCalculation } = require_utils_helper();
|
|
972
|
-
var { updateTransactionResponse } = require_transactionStore();
|
|
973
678
|
var queue2 = new Queue();
|
|
974
679
|
var paymentCloudUrl = null;
|
|
975
680
|
var paymentOrderId = null;
|
|
681
|
+
var activePaymentEcn = null;
|
|
976
682
|
module2.exports.setPaymentCloudUrl = (url) => {
|
|
977
683
|
paymentCloudUrl = url;
|
|
978
684
|
};
|
|
979
685
|
module2.exports.setPaymentOrderId = (orderId) => {
|
|
980
686
|
paymentOrderId = orderId;
|
|
981
687
|
};
|
|
688
|
+
module2.exports.setPaymentEcn = (ecn) => {
|
|
689
|
+
activePaymentEcn = ecn;
|
|
690
|
+
};
|
|
982
691
|
function sendPaymentResponseToCloud(translatedResponse) {
|
|
983
692
|
const cloudUrl = paymentCloudUrl || fw.getConfigByKey("cloud_url");
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
translatedResponse.retrievalReferenceNumber = paymentOrderId;
|
|
693
|
+
if (paymentOrderId && translatedResponse) {
|
|
694
|
+
translatedResponse.enhancedECRReferenceNumber = paymentOrderId;
|
|
987
695
|
}
|
|
988
696
|
paymentCloudUrl = null;
|
|
989
697
|
paymentOrderId = null;
|
|
990
698
|
if (!cloudUrl) {
|
|
991
|
-
logger.log({ level: "warn", message: "
|
|
699
|
+
logger.log({ level: "warn", message: "\u26A0\uFE0F cloud_url not configured, skipping payment response POST" });
|
|
992
700
|
return;
|
|
993
701
|
}
|
|
994
|
-
const
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
}));
|
|
998
|
-
console.log(URl);
|
|
999
|
-
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, {
|
|
1000
705
|
netsRes: translatedResponse
|
|
1001
706
|
}).then((res) => {
|
|
1002
|
-
logger.log({ level: "info", message:
|
|
707
|
+
logger.log({ level: "info", message: `\u2705 Payment response sent to cloud: ${res.status}` });
|
|
1003
708
|
}).catch((err) => {
|
|
1004
|
-
logger.log({ level: "error", message:
|
|
709
|
+
logger.log({ level: "error", message: `\u274C Failed to send payment response to cloud: ${err.message}` });
|
|
1005
710
|
});
|
|
1006
711
|
}
|
|
1007
|
-
module2.exports.terminalStatus = (ecn) => {
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
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
|
+
}
|
|
1013
721
|
}
|
|
1014
722
|
const status = statusParser(fullHex, ecn);
|
|
1015
723
|
console.log(fullHex);
|
|
@@ -1017,18 +725,6 @@ var require_responseHandler = __commonJS({
|
|
|
1017
725
|
level: "info",
|
|
1018
726
|
message: status
|
|
1019
727
|
});
|
|
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
728
|
if (status) {
|
|
1033
729
|
fw.updateConfig({ terminal_status: true });
|
|
1034
730
|
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
@@ -1045,26 +741,17 @@ var require_responseHandler = __commonJS({
|
|
|
1045
741
|
});
|
|
1046
742
|
}
|
|
1047
743
|
};
|
|
1048
|
-
module2.exports.terminalLogon = (ecn) => {
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
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
|
+
}
|
|
1054
753
|
}
|
|
1055
754
|
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
755
|
if (response.status) {
|
|
1069
756
|
fw.updateConfig({ last_logon: /* @__PURE__ */ new Date() });
|
|
1070
757
|
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
@@ -1089,25 +776,18 @@ var require_responseHandler = __commonJS({
|
|
|
1089
776
|
}
|
|
1090
777
|
return fullHex;
|
|
1091
778
|
};
|
|
1092
|
-
module2.exports.terminalPayment = (hexValue,
|
|
779
|
+
module2.exports.terminalPayment = (hexValue, ecnArg) => {
|
|
780
|
+
const ecn = activePaymentEcn || ecnArg;
|
|
1093
781
|
const parsedValue = netsPaymentParser(hexValue, ecn);
|
|
1094
782
|
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
|
-
});
|
|
783
|
+
if (response.translated) {
|
|
784
|
+
activePaymentEcn = null;
|
|
1106
785
|
}
|
|
1107
786
|
if (response.translated) {
|
|
1108
787
|
if (response.translated.status == "APPROVED") {
|
|
1109
788
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1110
789
|
success: true,
|
|
790
|
+
status: "SUCCESS",
|
|
1111
791
|
response,
|
|
1112
792
|
action: "COMPLETED"
|
|
1113
793
|
});
|
|
@@ -1129,24 +809,17 @@ var require_responseHandler = __commonJS({
|
|
|
1129
809
|
}
|
|
1130
810
|
}
|
|
1131
811
|
};
|
|
1132
|
-
module2.exports.terminalCreditPayment = (hexValue,
|
|
812
|
+
module2.exports.terminalCreditPayment = (hexValue, ecnArg) => {
|
|
813
|
+
const ecn = activePaymentEcn || ecnArg;
|
|
1133
814
|
const parsedValue = creditPaymentParser(hexValue, ecn);
|
|
1134
815
|
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
|
-
});
|
|
816
|
+
if (response.translated) {
|
|
817
|
+
activePaymentEcn = null;
|
|
1146
818
|
}
|
|
1147
819
|
if (response.translated) {
|
|
1148
820
|
if (response.translated.status == "APPROVED") {
|
|
1149
821
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
822
|
+
success: true,
|
|
1150
823
|
status: "SUCCESS",
|
|
1151
824
|
response,
|
|
1152
825
|
action: "COMPLETED"
|
|
@@ -1190,11 +863,12 @@ var require_responseHandler = __commonJS({
|
|
|
1190
863
|
}
|
|
1191
864
|
};
|
|
1192
865
|
module2.exports.checkLrc = (hexValue) => {
|
|
866
|
+
if (!hexValue || hexValue.length < 4) return false;
|
|
1193
867
|
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
|
|
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;
|
|
1198
872
|
};
|
|
1199
873
|
}
|
|
1200
874
|
});
|
|
@@ -1217,93 +891,47 @@ var require_communication = __commonJS({
|
|
|
1217
891
|
terminalStatus,
|
|
1218
892
|
fetchFullDetails,
|
|
1219
893
|
checkLrc,
|
|
1220
|
-
verifyParser
|
|
894
|
+
verifyParser,
|
|
895
|
+
setPaymentEcn
|
|
1221
896
|
} = require_responseHandler();
|
|
1222
897
|
var { sendMessage: sendMessage2 } = require_sendMessage();
|
|
1223
898
|
var fw = require_manageConfig();
|
|
1224
|
-
var { createTransaction } = require_transactionStore();
|
|
1225
899
|
var { initialiseRequest } = require_httpRequest();
|
|
1226
900
|
var requestType;
|
|
1227
901
|
var calculated;
|
|
1228
|
-
var isTimeOutset = false;
|
|
1229
902
|
var MAX_COUNT = 3;
|
|
1230
903
|
var WRONG_LRC_MAX_COUNT = 3;
|
|
1231
904
|
var count;
|
|
1232
905
|
var lrcCount;
|
|
1233
906
|
var ackOrNack;
|
|
1234
907
|
var requestPayload;
|
|
908
|
+
var ackTimeout = null;
|
|
1235
909
|
var ACK = Buffer.alloc(1, 6, "hex");
|
|
1236
910
|
var NACK = Buffer.alloc(1, 21, "hex");
|
|
911
|
+
var dataBuffer = Buffer.alloc(0);
|
|
1237
912
|
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
|
-
};
|
|
913
|
+
var lastPaymentReference = null;
|
|
914
|
+
var lastPaymentTime = 0;
|
|
1294
915
|
module2.exports.reset = () => {
|
|
1295
916
|
count = MAX_COUNT;
|
|
1296
917
|
lrcCount = WRONG_LRC_MAX_COUNT;
|
|
1297
918
|
ackOrNack = null;
|
|
919
|
+
dataBuffer = Buffer.alloc(0);
|
|
920
|
+
if (ackTimeout) {
|
|
921
|
+
clearTimeout(ackTimeout);
|
|
922
|
+
ackTimeout = null;
|
|
923
|
+
}
|
|
1298
924
|
};
|
|
1299
925
|
module2.exports.checkACKorNACK = () => {
|
|
1300
|
-
|
|
926
|
+
if (ackTimeout) clearTimeout(ackTimeout);
|
|
927
|
+
ackTimeout = setTimeout(() => {
|
|
928
|
+
ackTimeout = null;
|
|
1301
929
|
if (ackOrNack == null) {
|
|
1302
930
|
count--;
|
|
1303
931
|
if (count >= 0) {
|
|
1304
932
|
logger.log({
|
|
1305
933
|
level: "info",
|
|
1306
|
-
message: `POS send Purchase command, Terminal doesn't send any reply
|
|
934
|
+
message: `POS send Purchase command, Terminal doesn't send any reply. Retrying...`
|
|
1307
935
|
});
|
|
1308
936
|
resendData();
|
|
1309
937
|
} else {
|
|
@@ -1313,7 +941,7 @@ var require_communication = __commonJS({
|
|
|
1313
941
|
});
|
|
1314
942
|
}
|
|
1315
943
|
}
|
|
1316
|
-
},
|
|
944
|
+
}, 4e3);
|
|
1317
945
|
};
|
|
1318
946
|
var resendData = () => {
|
|
1319
947
|
if (requestType == "STATUS_CHECK") {
|
|
@@ -1329,64 +957,149 @@ var require_communication = __commonJS({
|
|
|
1329
957
|
exports2.checkACKorNACK();
|
|
1330
958
|
};
|
|
1331
959
|
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", () => {
|
|
960
|
+
try {
|
|
961
|
+
if (socketConnection) {
|
|
962
|
+
global.io = socketConnection;
|
|
963
|
+
}
|
|
964
|
+
if (global.io) {
|
|
1353
965
|
logger.log({
|
|
1354
966
|
level: "info",
|
|
1355
|
-
message:
|
|
967
|
+
message: "Socket connection available for terminal communication"
|
|
1356
968
|
});
|
|
1357
|
-
}
|
|
1358
|
-
global.port.on("data", (data) => {
|
|
969
|
+
} else {
|
|
1359
970
|
logger.log({
|
|
1360
|
-
level: "
|
|
1361
|
-
message:
|
|
971
|
+
level: "warn",
|
|
972
|
+
message: "No socket connection (global.io) found. Messages will be logged only."
|
|
1362
973
|
});
|
|
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();
|
|
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}` });
|
|
1386
986
|
}
|
|
1387
|
-
|
|
987
|
+
global.port = null;
|
|
1388
988
|
}
|
|
1389
|
-
|
|
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);
|
|
1031
|
+
}
|
|
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) {
|
|
1041
|
+
count--;
|
|
1042
|
+
if (count >= 0) resendData();
|
|
1043
|
+
else sendMessage2("clientRoom", "PAYMENT_MESSAGE", { success: true, action: "TERMINAL_ERROR" });
|
|
1044
|
+
}
|
|
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);
|
|
1390
1103
|
}
|
|
1391
1104
|
};
|
|
1392
1105
|
module2.exports.sentToTerminal = (type, body) => {
|
|
@@ -1394,23 +1107,6 @@ var require_communication = __commonJS({
|
|
|
1394
1107
|
requestPayload = body;
|
|
1395
1108
|
if (requestType == "STATUS_CHECK") {
|
|
1396
1109
|
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
1110
|
sendMessage2("clientRoom", "STATUS_MESSAGE", {
|
|
1415
1111
|
status: "INITIATED",
|
|
1416
1112
|
action: "STATUS_CHECK_INITIATED"
|
|
@@ -1419,23 +1115,6 @@ var require_communication = __commonJS({
|
|
|
1419
1115
|
}
|
|
1420
1116
|
if (requestType == "LOGON") {
|
|
1421
1117
|
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
1118
|
sendMessage2("clientRoom", "LOGON_MESSAGE", {
|
|
1440
1119
|
status: "INITIATED",
|
|
1441
1120
|
action: "LOGON_INITIATED"
|
|
@@ -1443,29 +1122,30 @@ var require_communication = __commonJS({
|
|
|
1443
1122
|
global.port.write(calculated.buffer);
|
|
1444
1123
|
}
|
|
1445
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
|
+
}
|
|
1446
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);
|
|
1447
1141
|
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
|
-
}
|
|
1142
|
+
console.log(calculated);
|
|
1465
1143
|
sendMessage2("clientRoom", "PAYMENT_MESSAGE", {
|
|
1466
1144
|
status: "INITIATED",
|
|
1467
|
-
action: "PAYMENT_INITIATED"
|
|
1145
|
+
action: "PAYMENT_INITIATED",
|
|
1146
|
+
orderId: body.reference || body.orderId
|
|
1468
1147
|
});
|
|
1148
|
+
if (calculated && calculated.ecn) setPaymentEcn(calculated.ecn);
|
|
1469
1149
|
global.port.write(calculated.buffer);
|
|
1470
1150
|
exports2.checkACKorNACK();
|
|
1471
1151
|
}
|
|
@@ -1473,142 +1153,6 @@ var require_communication = __commonJS({
|
|
|
1473
1153
|
}
|
|
1474
1154
|
});
|
|
1475
1155
|
|
|
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
1156
|
// src/index.js
|
|
1613
1157
|
var utilsHelper = require_utils_helper();
|
|
1614
1158
|
var winston = require_winston();
|
|
@@ -1620,8 +1164,6 @@ var hexRequest = require_hexRequest();
|
|
|
1620
1164
|
var httpRequest = require_httpRequest();
|
|
1621
1165
|
var parser = require_parser();
|
|
1622
1166
|
var responseHandler = require_responseHandler();
|
|
1623
|
-
var transactionStore = require_transactionStore();
|
|
1624
|
-
var listTransactions = require_listTransactions();
|
|
1625
1167
|
module.exports = {
|
|
1626
1168
|
...utilsHelper,
|
|
1627
1169
|
logger: winston,
|
|
@@ -1632,18 +1174,5 @@ module.exports = {
|
|
|
1632
1174
|
hexRequest,
|
|
1633
1175
|
httpRequest,
|
|
1634
1176
|
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
|
|
1177
|
+
responseHandler
|
|
1649
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
|
+
}
|