@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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 LocalPay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,251 @@
1
+ # @localpay/verification-engine
2
+
3
+ Bank receipt verification engine for Ethiopian and East African banks.
4
+
5
+ Supports: **CBE**, **Telebirr**, **Bank of Abyssinia**, **E-Birr**
6
+
7
+ - Works in any Node.js project
8
+ - Proxy support via a simple interface (you provide the config, we route the traffic)
9
+ - Amount matching built into verification
10
+ - Supports receipt links, SMS text, transaction references, and OCR images
11
+ - Extensible — add new bank parsers by implementing one interface
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @localpay/verification-engine
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Quick start
24
+
25
+ ```ts
26
+ import { VerificationEngine } from "@localpay/verification-engine";
27
+
28
+ const engine = new VerificationEngine();
29
+
30
+ const result = await engine.verify({
31
+ bank: "CBE",
32
+ amount: 500,
33
+ verMethod: "LINK",
34
+ rawProof: "https://apps.cbe.com.et:100/?id=FT26093JCD3218872366",
35
+ });
36
+
37
+ if (result.status === "SUCCESS") {
38
+ console.log(result.receipt.receipt.transactionNumber); // FT26093JCD32
39
+ console.log(result.receipt.receipt.amount); // "500"
40
+ console.log(result.receipt.receipt.receiverAccount); // account number
41
+ }
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Supported banks
47
+
48
+ | parserKey | Bank | Methods |
49
+ | ----------- | --------------------------- | ------------------------------- |
50
+ | `CBE` | Commercial Bank of Ethiopia | LINK, SMS, TRANSACTION_REF, OCR |
51
+ | `TELEBIRR` | Telebirr | LINK, SMS, TRANSACTION_REF, OCR |
52
+ | `ABYSSINIA` | Bank of Abyssinia | LINK, SMS, TRANSACTION_REF, OCR |
53
+ | `EBIRR` | E-Birr | LINK, SMS, TRANSACTION_REF, OCR |
54
+
55
+ ---
56
+
57
+ ## Verification methods
58
+
59
+ | Method | Description |
60
+ | ----------------- | --------------------------------------------------- |
61
+ | `LINK` | Pass the receipt URL directly |
62
+ | `SMS` | Pass the raw SMS body — the parser extracts the URL |
63
+ | `TRANSACTION_REF` | Pass only the transaction/reference number |
64
+ | `OCR` | Pass an image path or `Buffer`; OCR runs internally |
65
+ | `SCREENSHOT` | Alias of `OCR` for screenshot image verification |
66
+
67
+ ```ts
68
+ const byReference = await engine.verify({
69
+ bank: "TELEBIRR",
70
+ amount: 250,
71
+ verMethod: "TRANSACTION_REF",
72
+ rawProof: "CG7X9A2B",
73
+ });
74
+
75
+ const byScreenshot = await engine.verify({
76
+ bank: "TELEBIRR",
77
+ amount: 250,
78
+ verMethod: "OCR",
79
+ rawProof: "/tmp/receipt-screenshot.png",
80
+ });
81
+ ```
82
+
83
+ The engine compares `payload.amount` with the parsed receipt amount. Use
84
+ `amountTolerance` when small rounding differences are expected.
85
+
86
+ ```ts
87
+ await engine.verify({
88
+ bank: "CBE",
89
+ amount: 500,
90
+ amountTolerance: 0.05,
91
+ verMethod: "LINK",
92
+ rawProof: "https://apps.cbe.com.et:100/?id=...",
93
+ });
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Proxy support
99
+
100
+ Some banks block foreign IPs (e.g. Telebirr). Implement the `ProxyResolver`
101
+ interface to provide per-country proxy config from your data store. The built-in
102
+ parsers route HTTP and browser-backed fetches through this fetch service.
103
+
104
+ ```ts
105
+ import {
106
+ VerificationEngine,
107
+ ProxyResolver,
108
+ ProxyConfig,
109
+ ProxyType,
110
+ } from "@localpay/verification-engine";
111
+
112
+ class MyProxyResolver implements ProxyResolver {
113
+ async resolve(countryCode: string): Promise<ProxyConfig | null> {
114
+ // Read from your database, config file, environment variable, etc.
115
+ const row = await db.countryProxy.findUnique({ where: { countryCode } });
116
+ if (!row?.proxyEnabled) return null;
117
+ return {
118
+ enabled: true,
119
+ url: row.proxyUrl,
120
+ type: row.proxyType as ProxyType,
121
+ };
122
+ }
123
+ }
124
+
125
+ const engine = new VerificationEngine({
126
+ proxyResolver: new MyProxyResolver(),
127
+ });
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Adding a new bank
133
+
134
+ 1. Implement the `ParserAndExtractor` interface:
135
+
136
+ ```ts
137
+ import {
138
+ ParserAndExtractor,
139
+ ParserFetchContext,
140
+ RawReceipt,
141
+ } from "@localpay/verification-engine";
142
+
143
+ export class MyBankParser implements ParserAndExtractor {
144
+ extract(text: string, accountNumber?: string): { link: string } {
145
+ // Extract the receipt URL from SMS/OCR text
146
+ const match = text.match(/my-bank\.com\/receipt\/([A-Z0-9]+)/i);
147
+ if (!match) return { link: "" };
148
+ return { link: `https://my-bank.com/receipt/${match[1]}` };
149
+ }
150
+
151
+ transactionRef(transactionRef: string): { link: string } {
152
+ return { link: `https://my-bank.com/receipt/${transactionRef}` };
153
+ }
154
+
155
+ async fetch(
156
+ link: string,
157
+ context?: ParserFetchContext,
158
+ ): Promise<{ page: any }> {
159
+ const response = context
160
+ ? await context.fetcher.fetch(link, context.countryCode)
161
+ : await fetch(link).then(async (res) => ({ data: await res.text() }));
162
+
163
+ return { page: response.data };
164
+ }
165
+
166
+ async receiptParser(
167
+ page: any,
168
+ ): Promise<{ bank: string; receipt: RawReceipt }> {
169
+ // Parse the page and return structured data
170
+ return {
171
+ bank: "MY_BANK",
172
+ receipt: {
173
+ transactionNumber: "...",
174
+ date: "...",
175
+ amount: "...",
176
+ receiverAccount: "...",
177
+ receiverName: "...",
178
+ },
179
+ };
180
+ }
181
+ }
182
+ ```
183
+
184
+ 2. Pass it to the engine:
185
+
186
+ ```ts
187
+ import { PARSER_REGISTRY } from "@localpay/verification-engine";
188
+ import { MyBankParser } from "./my-bank.parser";
189
+
190
+ const engine = new VerificationEngine({
191
+ parsers: {
192
+ ...PARSER_REGISTRY,
193
+ MY_BANK: new MyBankParser(),
194
+ },
195
+ });
196
+ ```
197
+
198
+ ---
199
+
200
+ ## NestJS integration
201
+
202
+ ```ts
203
+ import { Injectable } from "@nestjs/common";
204
+ import {
205
+ VerificationEngine,
206
+ ProxyResolver,
207
+ } from "@localpay/verification-engine";
208
+ import { PrismaService } from "./prisma.service";
209
+
210
+ @Injectable()
211
+ export class VerificationService {
212
+ private readonly engine: VerificationEngine;
213
+
214
+ constructor(private readonly prisma: PrismaService) {
215
+ const proxyResolver: ProxyResolver = {
216
+ resolve: async (countryCode) => {
217
+ const row = await prisma.countryProxy.findUnique({
218
+ where: { countryCode },
219
+ });
220
+ if (!row?.proxyEnabled) return null;
221
+ return { enabled: true, url: row.proxyUrl, type: row.proxyType as any };
222
+ },
223
+ };
224
+
225
+ this.engine = new VerificationEngine({ proxyResolver });
226
+ }
227
+
228
+ verify(payload: Parameters<VerificationEngine["verify"]>[0]) {
229
+ return this.engine.verify(payload);
230
+ }
231
+ }
232
+ ```
233
+
234
+ ---
235
+
236
+ ## Publishing
237
+
238
+ ```bash
239
+ # Build and test
240
+ npm test
241
+
242
+ # Publish (private)
243
+ npm publish --access restricted
244
+
245
+ # Make public later
246
+ npm publish --access public
247
+ ```
248
+
249
+ `puppeteer` is an optional peer dependency used by parsers that need a
250
+ browser-rendered receipt page. HTTP/PDF-only parsers do not load it. Install it
251
+ in host apps that verify Telebirr or Bank of Abyssinia browser-rendered receipts.
@@ -0,0 +1,51 @@
1
+ import { ProxyResolver } from "../shared/proxy.types";
2
+ export interface BankFetchOptions {
3
+ responseType?: "arraybuffer" | "json" | "text";
4
+ headers?: Record<string, string>;
5
+ timeoutMs?: number;
6
+ validateStatus?: (status: number) => boolean;
7
+ }
8
+ export interface BrowserFetchOptions {
9
+ waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
10
+ waitForSelector?: string;
11
+ timeoutMs?: number;
12
+ }
13
+ /**
14
+ * FetchError is thrown when all retry attempts are exhausted.
15
+ * Catch this in your service layer to handle network failures gracefully.
16
+ */
17
+ export declare class FetchError extends Error {
18
+ readonly url: string;
19
+ readonly countryCode: string;
20
+ readonly cause: unknown;
21
+ constructor(url: string, countryCode: string, cause: unknown);
22
+ }
23
+ /**
24
+ * BankFetchService — single HTTP gateway for all bank parsers.
25
+ *
26
+ * Parsers never call axios directly. They call this service, which
27
+ * handles proxy routing, retries, and timeouts transparently.
28
+ *
29
+ * Usage:
30
+ * const fetcher = new BankFetchService(myProxyResolver);
31
+ * const { data } = await fetcher.fetch(url, 'ET', { responseType: 'text' });
32
+ *
33
+ * @param proxyResolver Implement ProxyResolver in your host app to provide
34
+ * proxy config from your database or config store.
35
+ * Pass null to disable proxy support entirely.
36
+ */
37
+ export declare class BankFetchService {
38
+ private readonly proxyResolver;
39
+ private readonly MAX_RETRIES;
40
+ private readonly DEFAULT_TIMEOUT_MS;
41
+ constructor(proxyResolver?: ProxyResolver | null);
42
+ fetch(url: string, countryCode: string, options?: BankFetchOptions): Promise<{
43
+ data: any;
44
+ status: number;
45
+ headers: any;
46
+ }>;
47
+ fetchBrowserPage(url: string, countryCode: string, options?: BrowserFetchOptions): Promise<{
48
+ data: string;
49
+ }>;
50
+ }
51
+ //# sourceMappingURL=bank-fetch.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bank-fetch.service.d.ts","sourceRoot":"","sources":["../../src/core/bank-fetch.service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAa,MAAM,uBAAuB,CAAC;AAEjE,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,EAAE,aAAa,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,kBAAkB,GAAG,cAAc,GAAG,cAAc,CAAC;IAC1E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,UAAW,SAAQ,KAAK;aAEjB,GAAG,EAAE,MAAM;aACX,WAAW,EAAE,MAAM;aACnB,KAAK,EAAE,OAAO;gBAFd,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,OAAO;CAOjC;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,gBAAgB;IAIf,OAAO,CAAC,QAAQ,CAAC,aAAa;IAH1C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAK;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;gBAEhB,aAAa,GAAE,aAAa,GAAG,IAAW;IAEjE,KAAK,CACT,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC;QAAE,IAAI,EAAE,GAAG,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,GAAG,CAAA;KAAE,CAAC;IA0CjD,gBAAgB,CACpB,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAiC7B"}
@@ -0,0 +1,147 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.BankFetchService = exports.FetchError = void 0;
40
+ const axios_1 = __importDefault(require("axios"));
41
+ const https_proxy_agent_1 = require("https-proxy-agent");
42
+ const socks_proxy_agent_1 = require("socks-proxy-agent");
43
+ const proxy_types_1 = require("../shared/proxy.types");
44
+ /**
45
+ * FetchError is thrown when all retry attempts are exhausted.
46
+ * Catch this in your service layer to handle network failures gracefully.
47
+ */
48
+ class FetchError extends Error {
49
+ constructor(url, countryCode, cause) {
50
+ const reason = cause instanceof Error ? cause.message : "Unknown network error";
51
+ super(`Fetch failed for ${countryCode} — ${url}: ${reason}`);
52
+ this.url = url;
53
+ this.countryCode = countryCode;
54
+ this.cause = cause;
55
+ this.name = "FetchError";
56
+ }
57
+ }
58
+ exports.FetchError = FetchError;
59
+ /**
60
+ * BankFetchService — single HTTP gateway for all bank parsers.
61
+ *
62
+ * Parsers never call axios directly. They call this service, which
63
+ * handles proxy routing, retries, and timeouts transparently.
64
+ *
65
+ * Usage:
66
+ * const fetcher = new BankFetchService(myProxyResolver);
67
+ * const { data } = await fetcher.fetch(url, 'ET', { responseType: 'text' });
68
+ *
69
+ * @param proxyResolver Implement ProxyResolver in your host app to provide
70
+ * proxy config from your database or config store.
71
+ * Pass null to disable proxy support entirely.
72
+ */
73
+ class BankFetchService {
74
+ constructor(proxyResolver = null) {
75
+ this.proxyResolver = proxyResolver;
76
+ this.MAX_RETRIES = 2;
77
+ this.DEFAULT_TIMEOUT_MS = 10000;
78
+ }
79
+ async fetch(url, countryCode, options = {}) {
80
+ const proxy = (await this.proxyResolver?.resolve(countryCode)) ?? null;
81
+ const timeoutMs = options.timeoutMs ?? this.DEFAULT_TIMEOUT_MS;
82
+ let lastError;
83
+ for (let attempt = 1; attempt <= this.MAX_RETRIES; attempt++) {
84
+ try {
85
+ const config = {
86
+ url,
87
+ method: "GET",
88
+ responseType: options.responseType ?? "text",
89
+ headers: options.headers ?? {},
90
+ timeout: timeoutMs,
91
+ validateStatus: options.validateStatus,
92
+ };
93
+ if (proxy?.enabled && proxy.url) {
94
+ config.httpsAgent =
95
+ proxy.type === proxy_types_1.ProxyType.SOCKS5
96
+ ? new socks_proxy_agent_1.SocksProxyAgent(proxy.url)
97
+ : new https_proxy_agent_1.HttpsProxyAgent(proxy.url);
98
+ config.proxy = false;
99
+ }
100
+ const response = await (0, axios_1.default)(config);
101
+ return {
102
+ data: response.data,
103
+ status: response.status,
104
+ headers: response.headers,
105
+ };
106
+ }
107
+ catch (err) {
108
+ lastError = err;
109
+ if (attempt < this.MAX_RETRIES) {
110
+ await new Promise((r) => setTimeout(r, 1000));
111
+ }
112
+ }
113
+ }
114
+ throw new FetchError(url, countryCode, lastError);
115
+ }
116
+ async fetchBrowserPage(url, countryCode, options = {}) {
117
+ const proxy = (await this.proxyResolver?.resolve(countryCode)) ?? null;
118
+ const timeoutMs = options.timeoutMs ?? 60000;
119
+ const args = ["--no-sandbox", "--disable-setuid-sandbox"];
120
+ if (proxy?.enabled && proxy.url) {
121
+ args.push(`--proxy-server=${proxy.url}`);
122
+ }
123
+ const puppeteer = await Promise.resolve().then(() => __importStar(require("puppeteer")));
124
+ const browser = await puppeteer.default.launch({
125
+ headless: true,
126
+ args,
127
+ });
128
+ try {
129
+ const page = await browser.newPage();
130
+ await page.goto(url, {
131
+ waitUntil: options.waitUntil ?? "domcontentloaded",
132
+ timeout: timeoutMs,
133
+ });
134
+ if (options.waitForSelector) {
135
+ await page
136
+ .waitForSelector(options.waitForSelector, { timeout: timeoutMs })
137
+ .catch(() => { });
138
+ }
139
+ return { data: await page.content() };
140
+ }
141
+ finally {
142
+ await browser.close();
143
+ }
144
+ }
145
+ }
146
+ exports.BankFetchService = BankFetchService;
147
+ //# sourceMappingURL=bank-fetch.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bank-fetch.service.js","sourceRoot":"","sources":["../../src/core/bank-fetch.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { ParserRegistry } from "../shared/parser.interface";
2
+ import { ProxyResolver } from "../shared/proxy.types";
3
+ export type VerificationMethod = "LINK" | "SMS" | "TRANSACTION_REF" | "OCR" | "SCREENSHOT";
4
+ export type RawProof = string | Buffer;
5
+ export interface VerifyPayload {
6
+ /** parserKey — e.g. 'CBE', 'TELEBIRR', 'ABYSSINIA', 'EBIRR' */
7
+ bank: string;
8
+ amount: number;
9
+ verMethod: VerificationMethod;
10
+ rawProof: RawProof;
11
+ accountNumber?: string;
12
+ countryCode?: string;
13
+ amountTolerance?: number;
14
+ }
15
+ export type VerifyResult = {
16
+ status: "SUCCESS";
17
+ receipt: {
18
+ bank: string;
19
+ receipt: {
20
+ transactionNumber: string;
21
+ date: string;
22
+ amount: string;
23
+ receiverAccount: string;
24
+ receiverName: string;
25
+ };
26
+ };
27
+ } | {
28
+ status: "FAIL";
29
+ reason: string;
30
+ };
31
+ /**
32
+ * VerificationEngine — the main entry point for the package.
33
+ *
34
+ * Plain TypeScript class. No NestJS, no Prisma, no framework.
35
+ *
36
+ * Usage:
37
+ *
38
+ * // Minimal — no proxy support
39
+ * const engine = new VerificationEngine();
40
+ *
41
+ * // With proxy support — implement ProxyResolver in your app
42
+ * const engine = new VerificationEngine({ proxyResolver: myResolver });
43
+ *
44
+ * // With custom parsers (extend or override)
45
+ * const engine = new VerificationEngine({ parsers: { ...PARSER_REGISTRY, MY_BANK: new MyParser() } });
46
+ *
47
+ * const result = await engine.verify({
48
+ * bank: 'CBE',
49
+ * amount: 500,
50
+ * verMethod: 'LINK',
51
+ * rawProof: 'https://apps.cbe.com.et:100/?id=FT26093JCD3218872366',
52
+ * });
53
+ */
54
+ export declare class VerificationEngine {
55
+ private readonly parsers;
56
+ private readonly fetcher;
57
+ private readonly ocrReader;
58
+ constructor(options?: {
59
+ proxyResolver?: ProxyResolver | null;
60
+ ocrReader?: (input: RawProof) => Promise<string>;
61
+ /** Override or extend the default parser registry */
62
+ parsers?: ParserRegistry;
63
+ });
64
+ verify(payload: VerifyPayload): Promise<VerifyResult>;
65
+ /** List all registered parser keys */
66
+ getSupportedBanks(): string[];
67
+ }
68
+ //# sourceMappingURL=verification-engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verification-engine.d.ts","sourceRoot":"","sources":["../../src/core/verification-engine.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGtD,MAAM,MAAM,kBAAkB,GAC1B,MAAM,GACN,KAAK,GACL,iBAAiB,GACjB,KAAK,GACL,YAAY,CAAC;AAEjB,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,kBAAkB,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,YAAY,GACpB;IACE,MAAM,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE;YACP,iBAAiB,EAAE,MAAM,CAAC;YAC1B,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,eAAe,EAAE,MAAM,CAAC;YACxB,YAAY,EAAE,MAAM,CAAC;SACtB,CAAC;KACH,CAAC;CACH,GACD;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;gBAErD,OAAO,CAAC,EAAE;QACpB,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;QACrC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QACjD,qDAAqD;QACrD,OAAO,CAAC,EAAE,cAAc,CAAC;KAC1B;IAMK,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAwG3D,sCAAsC;IACtC,iBAAiB,IAAI,MAAM,EAAE;CAG9B"}