@localpay/verification-engine 0.1.0 → 0.1.2
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/README.md
CHANGED
|
@@ -1,14 +1,38 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="https://res.cloudinary.com/dghrszcz9/image/upload/v1781601985/logo_l9vw1h.jpg" alt="LocalPay" width="72" height="72" />
|
|
4
|
+
|
|
1
5
|
# @localpay/verification-engine
|
|
2
6
|
|
|
3
|
-
Bank receipt verification engine for Ethiopian
|
|
7
|
+
**Bank receipt verification engine for Ethiopian banks**
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/@localpay/verification-engine)
|
|
10
|
+
[](https://www.npmjs.com/package/@localpay/verification-engine)
|
|
11
|
+
[](./LICENSE)
|
|
12
|
+
[](https://local-pay-ten.vercel.app/)
|
|
13
|
+
[](https://nodejs.org)
|
|
14
|
+
[](https://github.com/oneshotEFA/verification-engine/actions)
|
|
15
|
+
|
|
16
|
+
<p>
|
|
17
|
+
Verify bank transfer receipts in real time — from SMS, links, transaction IDs, or screenshots.<br/>
|
|
18
|
+
No framework lock-in. Works in any Node.js project.
|
|
19
|
+
</p>
|
|
4
20
|
|
|
5
|
-
|
|
21
|
+
[Installation](#installation) · [Quick Start](#quick-start) · [Supported Banks](#supported-banks) · [Verification Methods](#verification-methods) · [Proxy Support](#proxy-support) · [Adding a Bank](#adding-a-new-bank) · [NestJS](#nestjs-integration)
|
|
6
22
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- 🏦 **4 Ethiopian banks** — CBE, Telebirr, Bank of Abyssinia, E-Birr
|
|
30
|
+
- 🔗 **5 verification methods** — link, SMS, transaction reference, OCR, screenshot
|
|
31
|
+
- 🌍 **Proxy support** — per-country routing for banks that block foreign IPs
|
|
32
|
+
- ✅ **Amount matching** — configurable tolerance, strips currency symbols automatically
|
|
33
|
+
- 🔌 **Zero framework lock-in** — plain TypeScript, no NestJS, no Prisma
|
|
34
|
+
- 🧩 **Extensible** — add any bank by implementing one interface
|
|
35
|
+
- 📦 **Tree-shakeable** — `sideEffects: false`
|
|
12
36
|
|
|
13
37
|
---
|
|
14
38
|
|
|
@@ -18,9 +42,16 @@ Supports: **CBE**, **Telebirr**, **Bank of Abyssinia**, **E-Birr**
|
|
|
18
42
|
npm install @localpay/verification-engine
|
|
19
43
|
```
|
|
20
44
|
|
|
45
|
+
> **Optional:** Install `puppeteer` if you verify **Telebirr** or **Bank of Abyssinia** receipts.
|
|
46
|
+
> These banks serve JavaScript-rendered pages that require a headless browser.
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install puppeteer
|
|
50
|
+
```
|
|
51
|
+
|
|
21
52
|
---
|
|
22
53
|
|
|
23
|
-
## Quick
|
|
54
|
+
## Quick Start
|
|
24
55
|
|
|
25
56
|
```ts
|
|
26
57
|
import { VerificationEngine } from "@localpay/verification-engine";
|
|
@@ -35,71 +66,134 @@ const result = await engine.verify({
|
|
|
35
66
|
});
|
|
36
67
|
|
|
37
68
|
if (result.status === "SUCCESS") {
|
|
38
|
-
console.log(result.receipt.receipt.transactionNumber); // FT26093JCD32
|
|
39
|
-
console.log(result.receipt.receipt.amount);
|
|
40
|
-
console.log(result.receipt.receipt.receiverAccount);
|
|
69
|
+
console.log(result.receipt.receipt.transactionNumber); // "FT26093JCD32"
|
|
70
|
+
console.log(result.receipt.receipt.amount); // "500"
|
|
71
|
+
console.log(result.receipt.receipt.receiverAccount); // account number
|
|
72
|
+
console.log(result.receipt.receipt.receiverName); // account holder name
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (result.status === "FAIL") {
|
|
76
|
+
console.error(result.reason); // human-readable failure reason
|
|
41
77
|
}
|
|
42
78
|
```
|
|
43
79
|
|
|
44
80
|
---
|
|
45
81
|
|
|
46
|
-
## Supported
|
|
82
|
+
## Supported Banks
|
|
47
83
|
|
|
48
|
-
|
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
| `TELEBIRR`
|
|
52
|
-
|
|
|
53
|
-
| `EBIRR`
|
|
84
|
+
| Bank | `parserKey` | Methods |
|
|
85
|
+
|------|------------|---------|
|
|
86
|
+
| Commercial Bank of Ethiopia | `CBE` | LINK · SMS · TRANSACTION_REF · OCR |
|
|
87
|
+
| Telebirr | `TELEBIRR` | LINK · SMS · TRANSACTION_REF · OCR |
|
|
88
|
+
| Bank of Abyssinia | `ABYSSINIA` | LINK · SMS · TRANSACTION_REF · OCR |
|
|
89
|
+
| E-Birr | `EBIRR` | LINK · SMS · TRANSACTION_REF · OCR |
|
|
54
90
|
|
|
55
91
|
---
|
|
56
92
|
|
|
57
|
-
## Verification
|
|
93
|
+
## Verification Methods
|
|
94
|
+
|
|
95
|
+
| Method | `verMethod` | Pass as `rawProof` |
|
|
96
|
+
|--------|------------|-------------------|
|
|
97
|
+
| Receipt URL | `LINK` | Full URL string |
|
|
98
|
+
| SMS body | `SMS` | Raw SMS text string |
|
|
99
|
+
| Transaction reference | `TRANSACTION_REF` | Transaction / reference number |
|
|
100
|
+
| Image (OCR) | `OCR` | File path string or `Buffer` |
|
|
101
|
+
| Screenshot | `SCREENSHOT` | Alias of `OCR` |
|
|
58
102
|
|
|
59
|
-
|
|
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 |
|
|
103
|
+
### By link
|
|
66
104
|
|
|
67
105
|
```ts
|
|
68
|
-
const
|
|
106
|
+
const result = await engine.verify({
|
|
107
|
+
bank: "CBE",
|
|
108
|
+
amount: 500,
|
|
109
|
+
verMethod: "LINK",
|
|
110
|
+
rawProof: "https://apps.cbe.com.et:100/?id=FT26093JCD3218872366",
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### By SMS
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
const result = await engine.verify({
|
|
69
118
|
bank: "TELEBIRR",
|
|
70
119
|
amount: 250,
|
|
71
|
-
verMethod: "
|
|
72
|
-
rawProof:
|
|
120
|
+
verMethod: "SMS",
|
|
121
|
+
rawProof:
|
|
122
|
+
"Dear Ephrem, You have transferred ETB 250.00. Your transaction number is DEV6HKJX7K.",
|
|
73
123
|
});
|
|
124
|
+
```
|
|
74
125
|
|
|
75
|
-
|
|
126
|
+
### By transaction reference
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const result = await engine.verify({
|
|
76
130
|
bank: "TELEBIRR",
|
|
77
131
|
amount: 250,
|
|
132
|
+
verMethod: "TRANSACTION_REF",
|
|
133
|
+
rawProof: "DEV6HKJX7K",
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
> **CBE note:** When passing a 12-character base reference (e.g. `FT26093JCD32`),
|
|
138
|
+
> also provide `accountNumber` so the engine can build the full receipt URL.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const result = await engine.verify({
|
|
142
|
+
bank: "CBE",
|
|
143
|
+
amount: 500,
|
|
144
|
+
verMethod: "TRANSACTION_REF",
|
|
145
|
+
rawProof: "FT26093JCD32",
|
|
146
|
+
accountNumber: "1000000018872366",
|
|
147
|
+
});
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### By screenshot / OCR
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const result = await engine.verify({
|
|
154
|
+
bank: "CBE",
|
|
155
|
+
amount: 500,
|
|
78
156
|
verMethod: "OCR",
|
|
79
|
-
rawProof: "/tmp/receipt-screenshot.png",
|
|
157
|
+
rawProof: "/tmp/receipt-screenshot.png", // or a Buffer
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
> OCR uses Tesseract.js internally by default.
|
|
162
|
+
> Supply a custom `ocrReader` for higher accuracy (e.g. Google Cloud Vision):
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
const engine = new VerificationEngine({
|
|
166
|
+
ocrReader: async (input) => {
|
|
167
|
+
// input is a string path or Buffer
|
|
168
|
+
const text = await myVisionApi.recognize(input);
|
|
169
|
+
return text;
|
|
170
|
+
},
|
|
80
171
|
});
|
|
81
172
|
```
|
|
82
173
|
|
|
83
|
-
|
|
84
|
-
|
|
174
|
+
### Amount tolerance
|
|
175
|
+
|
|
176
|
+
The engine compares `payload.amount` with the parsed receipt amount.
|
|
177
|
+
Use `amountTolerance` when small rounding differences are expected.
|
|
85
178
|
|
|
86
179
|
```ts
|
|
87
180
|
await engine.verify({
|
|
88
181
|
bank: "CBE",
|
|
89
182
|
amount: 500,
|
|
90
|
-
amountTolerance: 0.05,
|
|
183
|
+
amountTolerance: 0.05, // default is 0.01
|
|
91
184
|
verMethod: "LINK",
|
|
92
|
-
rawProof: "https
|
|
185
|
+
rawProof: "https://...",
|
|
93
186
|
});
|
|
94
187
|
```
|
|
95
188
|
|
|
96
189
|
---
|
|
97
190
|
|
|
98
|
-
## Proxy
|
|
191
|
+
## Proxy Support
|
|
99
192
|
|
|
100
|
-
Some banks
|
|
101
|
-
interface to provide per-country proxy config
|
|
102
|
-
|
|
193
|
+
Some banks (notably Telebirr) block requests from foreign IP addresses.
|
|
194
|
+
Implement the `ProxyResolver` interface to provide per-country proxy config
|
|
195
|
+
from your own data store. The engine resolves it transparently — parsers
|
|
196
|
+
never deal with proxy config directly.
|
|
103
197
|
|
|
104
198
|
```ts
|
|
105
199
|
import {
|
|
@@ -111,13 +205,12 @@ import {
|
|
|
111
205
|
|
|
112
206
|
class MyProxyResolver implements ProxyResolver {
|
|
113
207
|
async resolve(countryCode: string): Promise<ProxyConfig | null> {
|
|
114
|
-
// Read from your database, config file, environment variable, etc.
|
|
115
208
|
const row = await db.countryProxy.findUnique({ where: { countryCode } });
|
|
116
209
|
if (!row?.proxyEnabled) return null;
|
|
117
210
|
return {
|
|
118
211
|
enabled: true,
|
|
119
|
-
url: row.proxyUrl,
|
|
120
|
-
type: row.proxyType as ProxyType,
|
|
212
|
+
url: row.proxyUrl, // "http://user:pass@proxy.host:8080"
|
|
213
|
+
type: row.proxyType as ProxyType, // HTTP_CONNECT or SOCKS5
|
|
121
214
|
};
|
|
122
215
|
}
|
|
123
216
|
}
|
|
@@ -127,11 +220,15 @@ const engine = new VerificationEngine({
|
|
|
127
220
|
});
|
|
128
221
|
```
|
|
129
222
|
|
|
223
|
+
Both `HTTP_CONNECT` (default) and `SOCKS5` proxy types are supported.
|
|
224
|
+
Proxy config is resolved per request — toggling it in your database
|
|
225
|
+
takes effect immediately with no redeploy.
|
|
226
|
+
|
|
130
227
|
---
|
|
131
228
|
|
|
132
|
-
## Adding a
|
|
229
|
+
## Adding a New Bank
|
|
133
230
|
|
|
134
|
-
|
|
231
|
+
Implement the `ParserAndExtractor` interface:
|
|
135
232
|
|
|
136
233
|
```ts
|
|
137
234
|
import {
|
|
@@ -141,32 +238,28 @@ import {
|
|
|
141
238
|
} from "@localpay/verification-engine";
|
|
142
239
|
|
|
143
240
|
export class MyBankParser implements ParserAndExtractor {
|
|
241
|
+
/** Extract receipt URL from SMS body or OCR text */
|
|
144
242
|
extract(text: string, accountNumber?: string): { link: string } {
|
|
145
|
-
// Extract the receipt URL from SMS/OCR text
|
|
146
243
|
const match = text.match(/my-bank\.com\/receipt\/([A-Z0-9]+)/i);
|
|
147
244
|
if (!match) return { link: "" };
|
|
148
245
|
return { link: `https://my-bank.com/receipt/${match[1]}` };
|
|
149
246
|
}
|
|
150
247
|
|
|
151
|
-
|
|
152
|
-
|
|
248
|
+
/** Build receipt URL directly from a transaction reference */
|
|
249
|
+
transactionRef(ref: string): { link: string } {
|
|
250
|
+
return { link: `https://my-bank.com/receipt/${ref}` };
|
|
153
251
|
}
|
|
154
252
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
context?: ParserFetchContext,
|
|
158
|
-
): Promise<{ page: any }> {
|
|
253
|
+
/** Download the receipt page — always use context.fetcher for proxy support */
|
|
254
|
+
async fetch(link: string, context?: ParserFetchContext): Promise<{ page: any }> {
|
|
159
255
|
const response = context
|
|
160
256
|
? await context.fetcher.fetch(link, context.countryCode)
|
|
161
|
-
: await fetch(link).then(async (
|
|
162
|
-
|
|
257
|
+
: await fetch(link).then(async (r) => ({ data: await r.text() }));
|
|
163
258
|
return { page: response.data };
|
|
164
259
|
}
|
|
165
260
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
): Promise<{ bank: string; receipt: RawReceipt }> {
|
|
169
|
-
// Parse the page and return structured data
|
|
261
|
+
/** Parse the page into a structured receipt */
|
|
262
|
+
async receiptParser(page: any): Promise<{ bank: string; receipt: RawReceipt }> {
|
|
170
263
|
return {
|
|
171
264
|
bank: "MY_BANK",
|
|
172
265
|
receipt: {
|
|
@@ -181,30 +274,36 @@ export class MyBankParser implements ParserAndExtractor {
|
|
|
181
274
|
}
|
|
182
275
|
```
|
|
183
276
|
|
|
184
|
-
|
|
277
|
+
Pass it to the engine alongside the built-in parsers:
|
|
185
278
|
|
|
186
279
|
```ts
|
|
187
|
-
import { PARSER_REGISTRY } from "@localpay/verification-engine";
|
|
280
|
+
import { VerificationEngine, PARSER_REGISTRY } from "@localpay/verification-engine";
|
|
188
281
|
import { MyBankParser } from "./my-bank.parser";
|
|
189
282
|
|
|
190
283
|
const engine = new VerificationEngine({
|
|
191
284
|
parsers: {
|
|
192
|
-
...PARSER_REGISTRY,
|
|
285
|
+
...PARSER_REGISTRY, // keep all built-in banks
|
|
193
286
|
MY_BANK: new MyBankParser(),
|
|
194
287
|
},
|
|
195
288
|
});
|
|
196
289
|
```
|
|
197
290
|
|
|
291
|
+
Check registered banks at runtime:
|
|
292
|
+
|
|
293
|
+
```ts
|
|
294
|
+
engine.getSupportedBanks();
|
|
295
|
+
// → ["CBE", "TELEBIRR", "ABYSSINIA", "EBIRR", "MY_BANK"]
|
|
296
|
+
```
|
|
297
|
+
|
|
198
298
|
---
|
|
199
299
|
|
|
200
|
-
## NestJS
|
|
300
|
+
## NestJS Integration
|
|
301
|
+
|
|
302
|
+
The engine is a plain class — wrap it in a NestJS service:
|
|
201
303
|
|
|
202
304
|
```ts
|
|
203
305
|
import { Injectable } from "@nestjs/common";
|
|
204
|
-
import {
|
|
205
|
-
VerificationEngine,
|
|
206
|
-
ProxyResolver,
|
|
207
|
-
} from "@localpay/verification-engine";
|
|
306
|
+
import { VerificationEngine, ProxyResolver } from "@localpay/verification-engine";
|
|
208
307
|
import { PrismaService } from "./prisma.service";
|
|
209
308
|
|
|
210
309
|
@Injectable()
|
|
@@ -214,7 +313,7 @@ export class VerificationService {
|
|
|
214
313
|
constructor(private readonly prisma: PrismaService) {
|
|
215
314
|
const proxyResolver: ProxyResolver = {
|
|
216
315
|
resolve: async (countryCode) => {
|
|
217
|
-
const row = await prisma.countryProxy.findUnique({
|
|
316
|
+
const row = await this.prisma.countryProxy.findUnique({
|
|
218
317
|
where: { countryCode },
|
|
219
318
|
});
|
|
220
319
|
if (!row?.proxyEnabled) return null;
|
|
@@ -233,19 +332,102 @@ export class VerificationService {
|
|
|
233
332
|
|
|
234
333
|
---
|
|
235
334
|
|
|
236
|
-
##
|
|
335
|
+
## API Reference
|
|
336
|
+
|
|
337
|
+
### `new VerificationEngine(options?)`
|
|
338
|
+
|
|
339
|
+
| Option | Type | Description |
|
|
340
|
+
|--------|------|-------------|
|
|
341
|
+
| `proxyResolver` | `ProxyResolver \| null` | Per-country proxy config provider |
|
|
342
|
+
| `ocrReader` | `(input: RawProof) => Promise<string>` | Custom OCR function |
|
|
343
|
+
| `parsers` | `ParserRegistry` | Override or extend the parser registry |
|
|
344
|
+
|
|
345
|
+
### `engine.verify(payload)`
|
|
346
|
+
|
|
347
|
+
| Field | Type | Required | Description |
|
|
348
|
+
|-------|------|----------|-------------|
|
|
349
|
+
| `bank` | `string` | ✅ | Parser key e.g. `"CBE"`, `"TELEBIRR"` |
|
|
350
|
+
| `amount` | `number` | ✅ | Expected transfer amount |
|
|
351
|
+
| `verMethod` | `VerificationMethod` | ✅ | How to verify |
|
|
352
|
+
| `rawProof` | `string \| Buffer` | ✅ | The proof to verify |
|
|
353
|
+
| `accountNumber` | `string` | — | Sender account (CBE 12-char refs) |
|
|
354
|
+
| `countryCode` | `string` | — | ISO country code, defaults to `"ET"` |
|
|
355
|
+
| `amountTolerance` | `number` | — | Amount diff tolerance, defaults to `0.01` |
|
|
356
|
+
|
|
357
|
+
### Result
|
|
358
|
+
|
|
359
|
+
```ts
|
|
360
|
+
type VerifyResult =
|
|
361
|
+
| { status: "SUCCESS"; receipt: { bank: string; receipt: RawReceipt } }
|
|
362
|
+
| { status: "FAIL"; reason: string };
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### `RawReceipt`
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
interface RawReceipt {
|
|
369
|
+
transactionNumber: string;
|
|
370
|
+
date: string;
|
|
371
|
+
amount: string;
|
|
372
|
+
receiverAccount: string;
|
|
373
|
+
receiverName: string;
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Utilities
|
|
380
|
+
|
|
381
|
+
```ts
|
|
382
|
+
import { safeParsDate, parseDate } from "@localpay/verification-engine";
|
|
383
|
+
|
|
384
|
+
// Returns null instead of throwing on bad input
|
|
385
|
+
safeParsDate("18-03-2026 21:46:09"); // → Date object
|
|
386
|
+
safeParsDate("not a date"); // → null
|
|
387
|
+
|
|
388
|
+
// Throws on unrecognised format
|
|
389
|
+
parseDate("18-03-2026 21:46:09"); // → Date object
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
Supported date formats:
|
|
393
|
+
|
|
394
|
+
| Bank | Format example |
|
|
395
|
+
|------|---------------|
|
|
396
|
+
| eBirr | `2026-02-11 20:07:02 +0300 EAT` |
|
|
397
|
+
| Telebirr | `18-03-2026 21:46:09` |
|
|
398
|
+
| CBE PDF | `3/11/2026, 6:15:00 PM` |
|
|
399
|
+
| BOA | `23/01/26 14:04` · `23/01/2026 14:04` |
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## Contributing
|
|
237
404
|
|
|
238
405
|
```bash
|
|
239
|
-
#
|
|
240
|
-
|
|
406
|
+
# Clone and install
|
|
407
|
+
git clone https://github.com/oneshotEFA/verification-engine.git
|
|
408
|
+
cd verification-engine
|
|
409
|
+
npm install
|
|
410
|
+
|
|
411
|
+
# Build
|
|
412
|
+
npm run build
|
|
241
413
|
|
|
242
|
-
#
|
|
243
|
-
npm
|
|
414
|
+
# Run tests
|
|
415
|
+
npm test
|
|
244
416
|
|
|
245
|
-
#
|
|
246
|
-
npm
|
|
417
|
+
# Type-check only
|
|
418
|
+
npm run lint
|
|
247
419
|
```
|
|
248
420
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
421
|
+
To add a new bank:
|
|
422
|
+
|
|
423
|
+
1. Create `src/parsers/<country>/<bank>.parser.ts`
|
|
424
|
+
2. Implement `ParserAndExtractor`
|
|
425
|
+
3. Export from `src/parsers/<country>/index.ts`
|
|
426
|
+
4. Spread into `src/parsers/index.ts`
|
|
427
|
+
5. Add test cases in `tests/verification-engine.test.js`
|
|
428
|
+
|
|
429
|
+
---
|
|
430
|
+
|
|
431
|
+
## License
|
|
432
|
+
|
|
433
|
+
[MIT](./LICENSE) · Built by [LocalPay](https://github.com/oneshotEFA)
|
|
@@ -1 +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;
|
|
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;IAkC/D,cAAc,CACZ,cAAc,EAAE,MAAM,EACtB,aAAa,CAAC,EAAE,MAAM,GACrB;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE;IAIb,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;YA2CT,QAAQ;IA4CtB,OAAO,CAAC,SAAS;YAyBH,QAAQ;IAgDtB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,aAAa;CAoBtB"}
|
|
@@ -57,14 +57,14 @@ class CbeParser {
|
|
|
57
57
|
return this.parsePdf(pdfBuf);
|
|
58
58
|
}
|
|
59
59
|
async fetchJson(link, context) {
|
|
60
|
-
const match = link.match(/
|
|
60
|
+
const match = link.match(/Mbreciept\.cbe\.com\.et\/([A-Z0-9][A-Z0-9-]+)/i);
|
|
61
61
|
if (!match)
|
|
62
62
|
throw new Error("Invalid new-format CBE link");
|
|
63
|
-
const txnId = match[1]
|
|
63
|
+
const txnId = match[1];
|
|
64
64
|
const url = `https://mb.cbe.com.et/api/v1/transactions/public/transaction-detail/${txnId}`;
|
|
65
65
|
const fetcher = context?.fetcher ?? this.fallbackFetcher;
|
|
66
66
|
const response = await fetcher.fetch(url, context?.countryCode ?? "ET", {
|
|
67
|
-
timeoutMs:
|
|
67
|
+
timeoutMs: 25000,
|
|
68
68
|
validateStatus: () => true,
|
|
69
69
|
headers: {
|
|
70
70
|
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
@@ -92,7 +92,7 @@ class CbeParser {
|
|
|
92
92
|
const fetcher = context?.fetcher ?? this.fallbackFetcher;
|
|
93
93
|
const response = await fetcher.fetch(link, context?.countryCode ?? "ET", {
|
|
94
94
|
responseType: "arraybuffer",
|
|
95
|
-
timeoutMs:
|
|
95
|
+
timeoutMs: 25000,
|
|
96
96
|
validateStatus: () => true,
|
|
97
97
|
headers: { "User-Agent": "Mozilla/5.0", Accept: "application/pdf" },
|
|
98
98
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cbe.parser.js","sourceRoot":"","sources":["../../../src/parsers/ethiopia/cbe.parser.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA4B;AAC5B,sEAAiE;AAOjE,MAAa,SAAS;IAAtB;QACmB,oBAAe,GAAG,IAAI,qCAAgB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"cbe.parser.js","sourceRoot":"","sources":["../../../src/parsers/ethiopia/cbe.parser.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@localpay/verification-engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Bank receipt verification engine for Ethiopian and East African banks",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -10,6 +10,14 @@
|
|
|
10
10
|
"default": "./dist/index.js"
|
|
11
11
|
}
|
|
12
12
|
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/oneshotEFA/verification-engine.git"
|
|
16
|
+
},
|
|
17
|
+
"bugs": {
|
|
18
|
+
"url": "https://github.com/oneshotEFA/verification-engine/issues"
|
|
19
|
+
},
|
|
20
|
+
"homepage": "https://local-pay-ten.vercel.app/",
|
|
13
21
|
"files": [
|
|
14
22
|
"dist",
|
|
15
23
|
"README.md",
|