gebi.verify
v0.1.2
Published
Verify Ethiopian financial transactions across banks and wallets
Maintainers
Readme
gebi-verify
Verify Ethiopian financial transactions across banks and wallets. Pass a transaction reference, get back structured transaction data.
Auto-detects the provider from the reference format, fetches the receipt from the provider's endpoint, and parses the response (HTML, PDF, or JSON) into a consistent shape.
Install
pnpm add gebi-verify
# or
npm install gebi-verifyRequires Node.js 18+.
Usage
import { verify } from 'gebi-verify';
const result = await verify(['FT12345AB67C98765432']);
console.log(result.provider); // 'cbe'
console.log(result.providerName); // 'Commercial Bank of Ethiopia'
console.log(result.parsed); // { transactionId, amount, sender, receiver, ... }
console.log(result.error); // null if successfulInput format
verify() takes an array of strings — fragments of a single transaction. The first element is always the reference/transaction ID. Some providers require additional context:
// Single reference (most providers)
await verify(['FT12345AB67C98765432']);
// Reference with extra context
await verify(['-1A2B3CDEFG-4H5I6J']);Options
await verify(['FT12345AB67C98765432'], {
timeout: 10000, // fetch timeout in ms (default: 15000)
});Result shape
interface VerifyResult {
reference: string; // the input reference
provider: string | null; // provider id (e.g. 'cbe', 'telebirr')
providerName: string | null; // display name
url: string | null; // the URL that was fetched
raw: string | Buffer | null; // raw response body
rawType: 'html' | 'pdf' | 'json' | null;
parsed: ParsedTransaction | null;
error: VerifyError | null; // null = success
}
interface ParsedTransaction {
transactionId: string;
amount: string;
currency: string;
date: string;
sender: string;
receiver: string;
status: string;
[key: string]: unknown; // provider-specific extra fields
}
type VerifyError =
| 'UNKNOWN_PROVIDER' // reference didn't match any provider pattern
| 'INVALID_INPUT' // input failed validation
| 'FETCH_ERROR' // network/HTTP error
| 'PARSE_ERROR'; // response couldn't be parsedThe function never throws — errors are returned in the error field.
Supported providers
| Provider | ID | Reference format | Response | Example |
|----------|-----|-----------------|----------|---------|
| Commercial Bank of Ethiopia | cbe | FT + 5 digits + 2 letters + 2 digits + 1 letter + 8 account digits | PDF | FT12345AB67C98765432 |
| Telebirr | telebirr | 10 alphanumeric characters | HTML | AB12CD34EF |
| Dashen Bank | dashen | 3 digits + 4 letters + 9 digits | PDF | 123ABCD456789012 |
| Bank of Abyssinia | abyssinia | FT + 8 digits + alphanumeric + 5 account digits | JSON | FT12345678AB54321 |
| Amhara Bank | amhara | FT + 7 digits + alphanumeric | JSON | FT1234567XY9 |
| Awash Bank | awash | - + alphanumeric + - + alphanumeric | HTML | -1A2B3CDEFG-4H5I6J |
Detection order matters — CBE, Abyssinia, and Amhara all start with FT but have increasingly general patterns. The first match wins.
Parsed fields by provider
Each provider returns different fields depending on what the receipt contains:
CBE: transactionId, amount, commission, vat, totalDebited, currency, date, sender, senderAccount, receiver, receiverAccount, reason, status
Telebirr: transactionId, amount, date, sender, senderPhone, receiver, receiverPhone, status, currency, reason
Dashen: transactionId, transferReference, amount, total, serviceCharge, vat, date, sender, senderAccount, receiver, receiverAccount, serviceType, narrative, institution, currency, status
Bank of Abyssinia: transactionId, amount, date, sender, senderAccount, receiver, reason, status
Amhara Bank: transactionId, amount, totalAmount, currency, date, sender, senderAccount, receiver, receiverAccount, transactionType, charge, status, referenceNo
Awash Bank: transactionId, amount, date, transactionType, sender, senderAccount, accountNo, phoneNumber, charge, vat, reason, branch, city
Adding a new provider
Create a config file in src/providers/:
import type { ProviderConfig } from '../types';
export const mybank: ProviderConfig = {
id: 'mybank',
name: 'My Bank',
pattern: /^(?<txId>MB\d{10})$/, // named capture groups become template variables
inputs: [
{ name: 'reference', index: 0, required: true, pattern: /^MB\d/ },
],
url: {
template: 'https://mybank.com/receipt/{{txId}}',
method: 'GET',
headers: { 'Content-Type': 'application/json' }, // optional
},
response: {
type: 'json', // 'html' | 'pdf' | 'json'
// For JSON responses — dot-path field mapping:
jsonFields: {
transactionId: 'data.reference',
amount: 'data.amount',
},
// For HTML responses — table label matching:
// tableFields: { amount: 'Amount', sender: 'Sender Name' },
// For HTML responses — CSS selector matching:
// selectors: { amount: '.amount-value', sender: '#sender' },
// For PDF responses — regex patterns (capture group 1 = value):
// pdfFields: { amount: /Amount:\s*(.+)/i },
},
};Then register it in src/providers/index.ts:
import { mybank } from './mybank';
export const providers: ProviderConfig[] = [
// ... existing providers
mybank, // add here — order matters if patterns overlap
];Provider config reference
pattern: Regex with named capture groups. Groups become available as {{groupName}} in URL templates. Must match the full reference string (^...$).
inputs: Define expected input fragments. Index 0 is the primary reference. Optional transform can be 'last8', 'last4', 'upper', or 'lower'.
url.template: Supports {{placeholder}} interpolation from capture groups and validated inputs.
response.type: Determines how the response is fetched and parsed:
'json'— response text is JSON-parsed, fields extracted via dot-path notation (supports array indices likebody.0.fieldName)'html'— parsed with Cheerio; usetableFieldsfor label-value table rows orselectorsfor CSS selectors'pdf'— response buffer is parsed with unpdf; fields extracted via regex patterns
Development
pnpm install
pnpm test # run tests
pnpm test:watch # watch mode
pnpm typecheck # type check
pnpm build # build ESM + CJSLicense
ISC
