@localpay/verification-engine 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/core/bank-fetch.service.d.ts +51 -0
  4. package/dist/core/bank-fetch.service.d.ts.map +1 -0
  5. package/dist/core/bank-fetch.service.js +147 -0
  6. package/dist/core/bank-fetch.service.js.map +1 -0
  7. package/dist/core/verification-engine.d.ts +68 -0
  8. package/dist/core/verification-engine.d.ts.map +1 -0
  9. package/dist/core/verification-engine.js +188 -0
  10. package/dist/core/verification-engine.js.map +1 -0
  11. package/dist/index.d.ts +14 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +28 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/parsers/ethiopia/abyssinia.parser.d.ts +19 -0
  16. package/dist/parsers/ethiopia/abyssinia.parser.d.ts.map +1 -0
  17. package/dist/parsers/ethiopia/abyssinia.parser.js +79 -0
  18. package/dist/parsers/ethiopia/abyssinia.parser.js.map +1 -0
  19. package/dist/parsers/ethiopia/cbe.parser.d.ts +25 -0
  20. package/dist/parsers/ethiopia/cbe.parser.d.ts.map +1 -0
  21. package/dist/parsers/ethiopia/cbe.parser.js +209 -0
  22. package/dist/parsers/ethiopia/cbe.parser.js.map +1 -0
  23. package/dist/parsers/ethiopia/ebirr.parser.d.ts +19 -0
  24. package/dist/parsers/ethiopia/ebirr.parser.d.ts.map +1 -0
  25. package/dist/parsers/ethiopia/ebirr.parser.js +75 -0
  26. package/dist/parsers/ethiopia/ebirr.parser.js.map +1 -0
  27. package/dist/parsers/ethiopia/index.d.ts +3 -0
  28. package/dist/parsers/ethiopia/index.d.ts.map +1 -0
  29. package/dist/parsers/ethiopia/index.js +14 -0
  30. package/dist/parsers/ethiopia/index.js.map +1 -0
  31. package/dist/parsers/ethiopia/telebirr.parser.d.ts +19 -0
  32. package/dist/parsers/ethiopia/telebirr.parser.d.ts.map +1 -0
  33. package/dist/parsers/ethiopia/telebirr.parser.js +123 -0
  34. package/dist/parsers/ethiopia/telebirr.parser.js.map +1 -0
  35. package/dist/parsers/index.d.ts +13 -0
  36. package/dist/parsers/index.d.ts.map +1 -0
  37. package/dist/parsers/index.js +20 -0
  38. package/dist/parsers/index.js.map +1 -0
  39. package/dist/parsers/kenya/index.d.ts +8 -0
  40. package/dist/parsers/kenya/index.d.ts.map +1 -0
  41. package/dist/parsers/kenya/index.js +10 -0
  42. package/dist/parsers/kenya/index.js.map +1 -0
  43. package/dist/shared/date-parser.util.d.ts +16 -0
  44. package/dist/shared/date-parser.util.d.ts.map +1 -0
  45. package/dist/shared/date-parser.util.js +68 -0
  46. package/dist/shared/date-parser.util.js.map +1 -0
  47. package/dist/shared/parser.interface.d.ts +41 -0
  48. package/dist/shared/parser.interface.d.ts.map +1 -0
  49. package/dist/shared/parser.interface.js +3 -0
  50. package/dist/shared/parser.interface.js.map +1 -0
  51. package/dist/shared/proxy.types.d.ts +27 -0
  52. package/dist/shared/proxy.types.d.ts.map +1 -0
  53. package/dist/shared/proxy.types.js +9 -0
  54. package/dist/shared/proxy.types.js.map +1 -0
  55. package/package.json +62 -0
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.VerificationEngine = void 0;
37
+ const parsers_1 = require("../parsers");
38
+ const bank_fetch_service_1 = require("./bank-fetch.service");
39
+ /**
40
+ * VerificationEngine — the main entry point for the package.
41
+ *
42
+ * Plain TypeScript class. No NestJS, no Prisma, no framework.
43
+ *
44
+ * Usage:
45
+ *
46
+ * // Minimal — no proxy support
47
+ * const engine = new VerificationEngine();
48
+ *
49
+ * // With proxy support — implement ProxyResolver in your app
50
+ * const engine = new VerificationEngine({ proxyResolver: myResolver });
51
+ *
52
+ * // With custom parsers (extend or override)
53
+ * const engine = new VerificationEngine({ parsers: { ...PARSER_REGISTRY, MY_BANK: new MyParser() } });
54
+ *
55
+ * const result = await engine.verify({
56
+ * bank: 'CBE',
57
+ * amount: 500,
58
+ * verMethod: 'LINK',
59
+ * rawProof: 'https://apps.cbe.com.et:100/?id=FT26093JCD3218872366',
60
+ * });
61
+ */
62
+ class VerificationEngine {
63
+ constructor(options) {
64
+ this.fetcher = new bank_fetch_service_1.BankFetchService(options?.proxyResolver ?? null);
65
+ this.parsers = options?.parsers ?? parsers_1.PARSER_REGISTRY;
66
+ this.ocrReader = options?.ocrReader ?? readTextWithTesseract;
67
+ }
68
+ async verify(payload) {
69
+ const parser = this.parsers[payload.bank];
70
+ const countryCode = payload.countryCode ?? "ET";
71
+ if (!parser) {
72
+ return {
73
+ status: "FAIL",
74
+ reason: `No parser registered for bank: ${payload.bank}`,
75
+ };
76
+ }
77
+ try {
78
+ let link;
79
+ switch (payload.verMethod) {
80
+ case "LINK":
81
+ if (typeof payload.rawProof !== "string") {
82
+ return {
83
+ status: "FAIL",
84
+ reason: "LINK verification requires rawProof to be a URL string",
85
+ };
86
+ }
87
+ link = payload.rawProof;
88
+ break;
89
+ case "SMS":
90
+ if (typeof payload.rawProof !== "string") {
91
+ return {
92
+ status: "FAIL",
93
+ reason: "SMS verification requires rawProof to be text",
94
+ };
95
+ }
96
+ link = parser.extract(payload.rawProof, payload.accountNumber).link;
97
+ if (!link)
98
+ return {
99
+ status: "FAIL",
100
+ reason: "Could not extract link from SMS",
101
+ };
102
+ break;
103
+ case "TRANSACTION_REF":
104
+ if (typeof payload.rawProof !== "string") {
105
+ return {
106
+ status: "FAIL",
107
+ reason: "TRANSACTION_REF verification requires rawProof to be a transaction reference string",
108
+ };
109
+ }
110
+ link = (parser.transactionRef?.(payload.rawProof, payload.accountNumber) ??
111
+ parser.extract(payload.rawProof, payload.accountNumber)).link;
112
+ if (!link)
113
+ return {
114
+ status: "FAIL",
115
+ reason: "Could not build link from transaction reference",
116
+ };
117
+ break;
118
+ case "OCR":
119
+ case "SCREENSHOT":
120
+ {
121
+ const text = await this.ocrReader(payload.rawProof);
122
+ link = parser.extract(text, payload.accountNumber).link;
123
+ }
124
+ if (!link)
125
+ return {
126
+ status: "FAIL",
127
+ reason: "Could not extract link from OCR result",
128
+ };
129
+ break;
130
+ default:
131
+ return {
132
+ status: "FAIL",
133
+ reason: `Unsupported verification method: ${payload.verMethod}`,
134
+ };
135
+ }
136
+ const fetched = await parser.fetch(link, {
137
+ fetcher: this.fetcher,
138
+ countryCode,
139
+ });
140
+ const receipt = await parser.receiptParser(fetched.page);
141
+ if (!receipt?.receipt) {
142
+ return { status: "FAIL", reason: "Parser returned no receipt data" };
143
+ }
144
+ if (!amountsMatch(payload.amount, receipt.receipt.amount, payload.amountTolerance)) {
145
+ return {
146
+ status: "FAIL",
147
+ reason: `Receipt amount ${receipt.receipt.amount || "(empty)"} does not match expected amount ${payload.amount}`,
148
+ };
149
+ }
150
+ return { status: "SUCCESS", receipt };
151
+ }
152
+ catch (err) {
153
+ return {
154
+ status: "FAIL",
155
+ reason: err instanceof Error
156
+ ? err.message
157
+ : "Unexpected error during verification",
158
+ };
159
+ }
160
+ }
161
+ /** List all registered parser keys */
162
+ getSupportedBanks() {
163
+ return Object.keys(this.parsers);
164
+ }
165
+ }
166
+ exports.VerificationEngine = VerificationEngine;
167
+ const readTextWithTesseract = async (input) => {
168
+ const { recognize } = await Promise.resolve().then(() => __importStar(require("tesseract.js")));
169
+ const result = await recognize(input, "eng");
170
+ return result.data.text ?? "";
171
+ };
172
+ const parseAmount = (value) => {
173
+ const normalized = value
174
+ .toString()
175
+ .replace(/[^\d.-]/g, "")
176
+ .trim();
177
+ if (!normalized)
178
+ return null;
179
+ const parsed = Number(normalized);
180
+ return Number.isFinite(parsed) ? parsed : null;
181
+ };
182
+ const amountsMatch = (expected, actual, tolerance = 0.01) => {
183
+ const parsedActual = parseAmount(actual);
184
+ if (parsedActual === null)
185
+ return false;
186
+ return Math.abs(parsedActual - expected) <= tolerance;
187
+ };
188
+ //# sourceMappingURL=verification-engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-engine.js","sourceRoot":"","sources":["../../src/core/verification-engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ export { VerificationEngine } from "./core/verification-engine";
2
+ export type { VerifyPayload, VerifyResult, VerificationMethod, } from "./core/verification-engine";
3
+ export { BankFetchService, FetchError } from "./core/bank-fetch.service";
4
+ export type { BankFetchOptions } from "./core/bank-fetch.service";
5
+ export { PARSER_REGISTRY } from "./parsers";
6
+ export { CbeParser } from "./parsers/ethiopia/cbe.parser";
7
+ export { TelebirrParser } from "./parsers/ethiopia/telebirr.parser";
8
+ export { AbyssiniaParser } from "./parsers/ethiopia/abyssinia.parser";
9
+ export { EBirrParser } from "./parsers/ethiopia/ebirr.parser";
10
+ export type { ParserAndExtractor, ParserFetchContext, ParserRegistry, RawReceipt, } from "./shared/parser.interface";
11
+ export type { ProxyConfig, ProxyResolver } from "./shared/proxy.types";
12
+ export { ProxyType } from "./shared/proxy.types";
13
+ export { safeParsDate, parseDate } from "./shared/date-parser.util";
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,YAAY,EACV,aAAa,EACb,YAAY,EACZ,kBAAkB,GACnB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACzE,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAGlE,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACpE,OAAO,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AACtE,OAAO,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAG9D,YAAY,EACV,kBAAkB,EAClB,kBAAkB,EAClB,cAAc,EACd,UAAU,GACX,MAAM,2BAA2B,CAAC;AACnC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseDate = exports.safeParsDate = exports.ProxyType = exports.EBirrParser = exports.AbyssiniaParser = exports.TelebirrParser = exports.CbeParser = exports.PARSER_REGISTRY = exports.FetchError = exports.BankFetchService = exports.VerificationEngine = void 0;
4
+ // ── Main entry point ─────────────────────────────────────────────────────────
5
+ var verification_engine_1 = require("./core/verification-engine");
6
+ Object.defineProperty(exports, "VerificationEngine", { enumerable: true, get: function () { return verification_engine_1.VerificationEngine; } });
7
+ // ── HTTP fetcher ──────────────────────────────────────────────────────────────
8
+ var bank_fetch_service_1 = require("./core/bank-fetch.service");
9
+ Object.defineProperty(exports, "BankFetchService", { enumerable: true, get: function () { return bank_fetch_service_1.BankFetchService; } });
10
+ Object.defineProperty(exports, "FetchError", { enumerable: true, get: function () { return bank_fetch_service_1.FetchError; } });
11
+ // ── Parsers ───────────────────────────────────────────────────────────────────
12
+ var parsers_1 = require("./parsers");
13
+ Object.defineProperty(exports, "PARSER_REGISTRY", { enumerable: true, get: function () { return parsers_1.PARSER_REGISTRY; } });
14
+ var cbe_parser_1 = require("./parsers/ethiopia/cbe.parser");
15
+ Object.defineProperty(exports, "CbeParser", { enumerable: true, get: function () { return cbe_parser_1.CbeParser; } });
16
+ var telebirr_parser_1 = require("./parsers/ethiopia/telebirr.parser");
17
+ Object.defineProperty(exports, "TelebirrParser", { enumerable: true, get: function () { return telebirr_parser_1.TelebirrParser; } });
18
+ var abyssinia_parser_1 = require("./parsers/ethiopia/abyssinia.parser");
19
+ Object.defineProperty(exports, "AbyssiniaParser", { enumerable: true, get: function () { return abyssinia_parser_1.AbyssiniaParser; } });
20
+ var ebirr_parser_1 = require("./parsers/ethiopia/ebirr.parser");
21
+ Object.defineProperty(exports, "EBirrParser", { enumerable: true, get: function () { return ebirr_parser_1.EBirrParser; } });
22
+ var proxy_types_1 = require("./shared/proxy.types");
23
+ Object.defineProperty(exports, "ProxyType", { enumerable: true, get: function () { return proxy_types_1.ProxyType; } });
24
+ // ── Utilities ─────────────────────────────────────────────────────────────────
25
+ var date_parser_util_1 = require("./shared/date-parser.util");
26
+ Object.defineProperty(exports, "safeParsDate", { enumerable: true, get: function () { return date_parser_util_1.safeParsDate; } });
27
+ Object.defineProperty(exports, "parseDate", { enumerable: true, get: function () { return date_parser_util_1.parseDate; } });
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,gFAAgF;AAChF,kEAAgE;AAAvD,yHAAA,kBAAkB,OAAA;AAO3B,iFAAiF;AACjF,gEAAyE;AAAhE,sHAAA,gBAAgB,OAAA;AAAE,gHAAA,UAAU,OAAA;AAGrC,iFAAiF;AACjF,qCAA4C;AAAnC,0GAAA,eAAe,OAAA;AACxB,4DAA0D;AAAjD,uGAAA,SAAS,OAAA;AAClB,sEAAoE;AAA3D,iHAAA,cAAc,OAAA;AACvB,wEAAsE;AAA7D,mHAAA,eAAe,OAAA;AACxB,gEAA8D;AAArD,2GAAA,WAAW,OAAA;AAUpB,oDAAiD;AAAxC,wGAAA,SAAS,OAAA;AAElB,iFAAiF;AACjF,8DAAoE;AAA3D,gHAAA,YAAY,OAAA;AAAE,6GAAA,SAAS,OAAA"}
@@ -0,0 +1,19 @@
1
+ import { ParserAndExtractor, ParserFetchContext, RawReceipt } from "../../shared/parser.interface";
2
+ export declare class AbyssiniaParser implements ParserAndExtractor {
3
+ private readonly fallbackFetcher;
4
+ extract(sms: string, accNumber?: string): {
5
+ link: string;
6
+ };
7
+ transactionRef(transactionRef: string, accountNumber?: string): {
8
+ link: string;
9
+ };
10
+ fetch(link: string, context?: ParserFetchContext): Promise<{
11
+ page: any;
12
+ }>;
13
+ receiptParser(html: string): Promise<{
14
+ bank: string;
15
+ receipt: RawReceipt;
16
+ }>;
17
+ private buildLink;
18
+ }
19
+ //# sourceMappingURL=abyssinia.parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abyssinia.parser.d.ts","sourceRoot":"","sources":["../../../src/parsers/ethiopia/abyssinia.parser.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACX,MAAM,+BAA+B,CAAC;AAEvC,qBAAa,eAAgB,YAAW,kBAAkB;IACxD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAE1D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAiB1D,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAI1E,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IAenB,aAAa,CACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,CAAA;KAAE,CAAC;IAsCjD,OAAO,CAAC,SAAS;CAOlB"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbyssiniaParser = void 0;
4
+ const jsdom_1 = require("jsdom");
5
+ const bank_fetch_service_1 = require("../../core/bank-fetch.service");
6
+ class AbyssiniaParser {
7
+ constructor() {
8
+ this.fallbackFetcher = new bank_fetch_service_1.BankFetchService();
9
+ }
10
+ extract(sms, accNumber) {
11
+ const cleaned = sms.replace(/hitps/gi, "https").replace(/\s+/g, " ").trim();
12
+ const urlMatch = cleaned.match(/trx\s*=\s*([A-Z0-9]{8,})/i);
13
+ if (urlMatch)
14
+ return { link: this.buildLink(urlMatch[1], accNumber) };
15
+ const ocrMatch = cleaned.match(/Transaction\s+(?:Reference\s+)?([A-Z]{2}\d{5}[A-Z0-9]+)/i);
16
+ if (ocrMatch)
17
+ return { link: this.buildLink(ocrMatch[1], accNumber) };
18
+ const refMatch = cleaned.match(/\b(FT\d{5}[A-Z0-9]{4,})\b/i);
19
+ if (refMatch)
20
+ return { link: this.buildLink(refMatch[1], accNumber) };
21
+ return { link: "" };
22
+ }
23
+ transactionRef(transactionRef, accountNumber) {
24
+ return { link: this.buildLink(transactionRef, accountNumber) };
25
+ }
26
+ async fetch(link, context) {
27
+ if (!link)
28
+ throw new Error("No link provided");
29
+ const fetcher = context?.fetcher ?? this.fallbackFetcher;
30
+ const response = await fetcher.fetchBrowserPage(link, context?.countryCode ?? "ET", {
31
+ waitUntil: "networkidle2",
32
+ waitForSelector: "table",
33
+ timeoutMs: 60000,
34
+ });
35
+ return { page: response.data };
36
+ }
37
+ async receiptParser(html) {
38
+ const dom = new jsdom_1.JSDOM(html);
39
+ const document = dom.window.document;
40
+ const rows = Array.from(document.querySelectorAll("table tr"));
41
+ const result = {};
42
+ rows.forEach((row) => {
43
+ const cells = Array.from(row.querySelectorAll("td"));
44
+ if (cells.length < 2)
45
+ return;
46
+ const label = cells[0].textContent?.trim().toLowerCase() ?? "";
47
+ const value = cells[1].textContent?.trim() ?? "";
48
+ if (label.includes("receiver's account"))
49
+ result.receiverAccount = value;
50
+ if (label.includes("receiver's name"))
51
+ result.receiverName = value;
52
+ if (label.includes("transferred amount"))
53
+ result.amount = value;
54
+ if (label.includes("transaction date"))
55
+ result.date = value;
56
+ if (label.includes("transaction reference"))
57
+ result.transactionNumber = value;
58
+ });
59
+ return {
60
+ bank: "ABYSSINIA",
61
+ receipt: {
62
+ date: result.date ?? "",
63
+ receiverAccount: result.receiverAccount ?? "",
64
+ receiverName: result.receiverName ?? "",
65
+ amount: (result.amount ?? "").replace(/^ETB\s*/i, "").trim(),
66
+ transactionNumber: result.transactionNumber ?? "",
67
+ },
68
+ };
69
+ }
70
+ buildLink(trx, accountNumber) {
71
+ const upper = trx.replace(/[^A-Z0-9]/gi, "").toUpperCase();
72
+ if (upper.length === 12 && accountNumber && accountNumber.length >= 5) {
73
+ return `https://cs.bankofabyssinia.com/slip/?trx=${upper}${accountNumber.slice(-5)}`;
74
+ }
75
+ return `https://cs.bankofabyssinia.com/slip/?trx=${upper}`;
76
+ }
77
+ }
78
+ exports.AbyssiniaParser = AbyssiniaParser;
79
+ //# sourceMappingURL=abyssinia.parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abyssinia.parser.js","sourceRoot":"","sources":["../../../src/parsers/ethiopia/abyssinia.parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { ParserAndExtractor, ParserFetchContext, RawReceipt } from "../../shared/parser.interface";
2
+ export declare class CbeParser implements ParserAndExtractor {
3
+ private readonly fallbackFetcher;
4
+ extract(text: string, accountNumber?: string): {
5
+ link: string;
6
+ };
7
+ transactionRef(transactionRef: string, accountNumber?: string): {
8
+ link: string;
9
+ };
10
+ fetch(link: string, context?: ParserFetchContext): Promise<{
11
+ page: any;
12
+ }>;
13
+ receiptParser(input: any): Promise<{
14
+ bank: string;
15
+ receipt: RawReceipt;
16
+ }>;
17
+ private fetchJson;
18
+ private fetchPdf;
19
+ private parseJson;
20
+ private parsePdf;
21
+ private isNewFormat;
22
+ private buildOldLink;
23
+ private normalizeDate;
24
+ }
25
+ //# sourceMappingURL=cbe.parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cbe.parser.d.ts","sourceRoot":"","sources":["../../../src/parsers/ethiopia/cbe.parser.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACX,MAAM,+BAA+B,CAAC;AAEvC,qBAAa,SAAU,YAAW,kBAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAE1D,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAmC/D,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAI1E,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IAMnB,aAAa,CACjB,KAAK,EAAE,GAAG,GACT,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,CAAA;KAAE,CAAC;YAiBnC,SAAS;YA4CT,QAAQ;IA4CtB,OAAO,CAAC,SAAS;YAyBH,QAAQ;IAgDtB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;CAoBtB"}
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CbeParser = void 0;
7
+ const pdf_parse_1 = __importDefault(require("pdf-parse"));
8
+ const bank_fetch_service_1 = require("../../core/bank-fetch.service");
9
+ class CbeParser {
10
+ constructor() {
11
+ this.fallbackFetcher = new bank_fetch_service_1.BankFetchService();
12
+ }
13
+ extract(text, accountNumber) {
14
+ if (!text)
15
+ throw new Error("SMS text is empty");
16
+ const cleaned = text
17
+ .replace(/https?:\/\/\s+/gi, "https://")
18
+ .replace(/\?\s+/g, "?")
19
+ .replace(/=\s+/g, "=");
20
+ // New format: https://Mbreciept.cbe.com.et/FT26093JCD32-18872366
21
+ const newFormat = cleaned.match(/https?:\/\/[Mm]breciept\.cbe\.com\.et\/([A-Z0-9]+-\d+)/i);
22
+ if (newFormat) {
23
+ return {
24
+ link: `https://mbreciept.cbe.com.et/${newFormat[1].toUpperCase()}`,
25
+ };
26
+ }
27
+ const oldUrlMatch = cleaned.match(/https?:\/\/apps\.cbe\.com\.et:\d+\/\?i{1,2}d=([A-Z0-9]+)/i);
28
+ if (oldUrlMatch)
29
+ return { link: this.buildOldLink(oldUrlMatch[1], accountNumber) };
30
+ const idParamMatch = cleaned.match(/\bi{1,2}[dD]=([A-Z0-9]{10,})/i);
31
+ if (idParamMatch)
32
+ return { link: this.buildOldLink(idParamMatch[1], accountNumber) };
33
+ const ftMatch = cleaned.match(/\b(FT[A-Z0-9]{8,})\b/i);
34
+ if (ftMatch)
35
+ return { link: this.buildOldLink(ftMatch[1], accountNumber) };
36
+ throw new Error("No valid CBE transaction link found in SMS");
37
+ }
38
+ transactionRef(transactionRef, accountNumber) {
39
+ return { link: this.buildOldLink(transactionRef, accountNumber) };
40
+ }
41
+ async fetch(link, context) {
42
+ return this.isNewFormat(link)
43
+ ? this.fetchJson(link, context)
44
+ : this.fetchPdf(link, context);
45
+ }
46
+ async receiptParser(input) {
47
+ if (!input)
48
+ throw new Error("No receipt data to parse");
49
+ const buf = Buffer.isBuffer(input)
50
+ ? input
51
+ : Buffer.from(input, "utf-8");
52
+ const prefix = buf.slice(0, 5).toString("utf-8");
53
+ if (prefix === "JSON:") {
54
+ return this.parseJson(buf.slice(5));
55
+ }
56
+ const pdfBuf = prefix === "PDF:" ? buf.slice(4) : buf;
57
+ return this.parsePdf(pdfBuf);
58
+ }
59
+ async fetchJson(link, context) {
60
+ const match = link.match(/mbreciept\.cbe\.com\.et\/([A-Z0-9]+-\d+)/i);
61
+ if (!match)
62
+ throw new Error("Invalid new-format CBE link");
63
+ const txnId = match[1].toUpperCase();
64
+ const url = `https://mb.cbe.com.et/api/v1/transactions/public/transaction-detail/${txnId}`;
65
+ const fetcher = context?.fetcher ?? this.fallbackFetcher;
66
+ const response = await fetcher.fetch(url, context?.countryCode ?? "ET", {
67
+ timeoutMs: 30000,
68
+ validateStatus: () => true,
69
+ headers: {
70
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
71
+ Accept: "application/json, text/plain, */*",
72
+ Origin: "https://mbreciept.cbe.com.et",
73
+ Referer: "https://mbreciept.cbe.com.et/",
74
+ "x-app-id": "d1292e42-7400-49de-a2d3-9731caa4c819",
75
+ "x-app-version": "0a01980b-9859-1369-8198-59f403820000",
76
+ },
77
+ });
78
+ if (response.status !== 200 || !response.data) {
79
+ throw new Error(`CBE API returned status ${response.status} for ${txnId}`);
80
+ }
81
+ const json = typeof response.data === "string"
82
+ ? response.data
83
+ : JSON.stringify(response.data);
84
+ return {
85
+ page: Buffer.concat([
86
+ Buffer.from("JSON:", "utf-8"),
87
+ Buffer.from(json, "utf-8"),
88
+ ]),
89
+ };
90
+ }
91
+ async fetchPdf(link, context) {
92
+ const fetcher = context?.fetcher ?? this.fallbackFetcher;
93
+ const response = await fetcher.fetch(link, context?.countryCode ?? "ET", {
94
+ responseType: "arraybuffer",
95
+ timeoutMs: 30000,
96
+ validateStatus: () => true,
97
+ headers: { "User-Agent": "Mozilla/5.0", Accept: "application/pdf" },
98
+ });
99
+ const buffer = Buffer.from(response.data);
100
+ if (response.status === 404) {
101
+ const txnIdMatch = link.match(/id=([A-Z0-9]+)/i);
102
+ if (txnIdMatch) {
103
+ const txnId = txnIdMatch[1].slice(0, 12);
104
+ const accountSuffix = txnIdMatch[1].slice(12);
105
+ return this.fetchJson(`https://mbreciept.cbe.com.et/${txnId}-${accountSuffix}`, context);
106
+ }
107
+ throw new Error("CBE PDF not found and no fallback possible");
108
+ }
109
+ if (response.status !== 200)
110
+ throw new Error(`Unexpected HTTP status: ${response.status}`);
111
+ const preview = buffer.toString("utf-8", 0, 300).toLowerCase();
112
+ if (preview.includes("<html") || preview.includes("access denied")) {
113
+ throw new Error("Server returned HTML instead of PDF");
114
+ }
115
+ if (!buffer.subarray(0, 4).toString().startsWith("%PDF")) {
116
+ throw new Error("Response is not a valid PDF");
117
+ }
118
+ return {
119
+ page: Buffer.concat([Buffer.from("PDF:", "utf-8"), buffer]),
120
+ };
121
+ }
122
+ parseJson(input) {
123
+ let data;
124
+ try {
125
+ data = JSON.parse(input.toString("utf-8"));
126
+ }
127
+ catch {
128
+ throw new Error("Failed to parse CBE JSON receipt");
129
+ }
130
+ return {
131
+ bank: "CBE",
132
+ receipt: {
133
+ transactionNumber: (data.id ?? "").toString().toUpperCase().trim(),
134
+ date: this.normalizeDate(data.dateTimes?.[0] ?? data.processingDate ?? ""),
135
+ receiverAccount: (data.creditAccountNo ?? "").trim(),
136
+ receiverName: (data.creditAccountHolder ?? "").trim(),
137
+ amount: (data.amountDebited ?? data.debitAmount ?? "")
138
+ .toString()
139
+ .replace(/,/g, "")
140
+ .trim(),
141
+ },
142
+ };
143
+ }
144
+ async parsePdf(input) {
145
+ let text;
146
+ try {
147
+ const parsed = await (0, pdf_parse_1.default)(input);
148
+ text = parsed.text ?? "";
149
+ }
150
+ catch (err) {
151
+ throw new Error("Failed to parse PDF: " + err.message);
152
+ }
153
+ if (!text.trim())
154
+ throw new Error("PDF parsed but no text extracted");
155
+ const s = text.replace(/\s+/g, " ").trim();
156
+ return {
157
+ bank: "CBE",
158
+ receipt: {
159
+ transactionNumber: (s.match(/Reference\s*No[.\s]*(?:\(VAT\s*Invoice\s*No\))?\s*([A-Z0-9]{8,})/i)?.[1] ??
160
+ s.match(/\b(FT[A-Z0-9]{8,})\b/)?.[1] ??
161
+ "")
162
+ .trim()
163
+ .toUpperCase(),
164
+ date: s
165
+ .match(/Payment\s*Date\s*&\s*Time\s*([\d\/,: ]+(?:AM|PM))/i)?.[1]
166
+ ?.trim() ?? "",
167
+ receiverAccount: s.match(/Receiver[A-Z\s]+Account([\d*]+)/i)?.[1]?.trim() ?? "",
168
+ receiverName: s.match(/Receiver([A-Z][A-Z\s]+?)(?=Account)/i)?.[1]?.trim() ?? "",
169
+ amount: s
170
+ .match(/Transferred\s*Amount\s*([\d,]+(?:\.\d{2})?)\s*ETB/i)?.[1]
171
+ ?.replace(/,/g, "")
172
+ .trim() ?? "",
173
+ },
174
+ };
175
+ }
176
+ isNewFormat(link) {
177
+ return /mbreciept\.cbe\.com\.et/i.test(link);
178
+ }
179
+ buildOldLink(trxId, accountNumber) {
180
+ const upper = trxId.replace(/[^A-Z0-9]/gi, "").toUpperCase();
181
+ if (upper.length <= 19 && accountNumber && accountNumber.length >= 8) {
182
+ return `https://apps.cbe.com.et:100/?id=${upper.slice(0, 12)}${accountNumber.slice(-8)}`;
183
+ }
184
+ return `https://apps.cbe.com.et:100/?id=${upper}`;
185
+ }
186
+ normalizeDate(raw) {
187
+ if (!raw)
188
+ return "";
189
+ if (raw.includes("T")) {
190
+ const d = new Date(raw);
191
+ if (!isNaN(d.getTime()))
192
+ return d.toLocaleString("en-US", {
193
+ year: "numeric",
194
+ month: "2-digit",
195
+ day: "2-digit",
196
+ hour: "2-digit",
197
+ minute: "2-digit",
198
+ second: "2-digit",
199
+ hour12: true,
200
+ });
201
+ }
202
+ if (/^\d{8}$/.test(raw)) {
203
+ return `${raw.slice(4, 6)}/${raw.slice(6, 8)}/${raw.slice(0, 4)}`;
204
+ }
205
+ return raw;
206
+ }
207
+ }
208
+ exports.CbeParser = CbeParser;
209
+ //# sourceMappingURL=cbe.parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cbe.parser.js","sourceRoot":"","sources":["../../../src/parsers/ethiopia/cbe.parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import { ParserAndExtractor, ParserFetchContext, RawReceipt } from "../../shared/parser.interface";
2
+ export declare class EBirrParser implements ParserAndExtractor {
3
+ private readonly fallbackFetcher;
4
+ extract(sms: string, _accountNumber?: string): {
5
+ link: string;
6
+ };
7
+ transactionRef(transactionRef: string): {
8
+ link: string;
9
+ };
10
+ fetch(link: string, context?: ParserFetchContext): Promise<{
11
+ page: any;
12
+ }>;
13
+ receiptParser(html: string): Promise<{
14
+ bank: string;
15
+ receipt: RawReceipt;
16
+ }>;
17
+ private cleanText;
18
+ }
19
+ //# sourceMappingURL=ebirr.parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ebirr.parser.d.ts","sourceRoot":"","sources":["../../../src/parsers/ethiopia/ebirr.parser.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACX,MAAM,+BAA+B,CAAC;AAEvC,qBAAa,WAAY,YAAW,kBAAkB;IACpD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA0B;IAE1D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAa/D,cAAc,CAAC,cAAc,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAOlD,KAAK,CACT,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAA;KAAE,CAAC;IASnB,aAAa,CACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,CAAA;KAAE,CAAC;IAkDjD,OAAO,CAAC,SAAS;CAOlB"}