checkbirr 0.0.0-stage → 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2011-2026 The Bootstrap Authors
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:
package/README.md CHANGED
@@ -1,3 +1,85 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # CheckBirr 💵
2
+
3
+ Verify payment screenshots instantly ⚡
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install checkbirr
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import checkbirr from "checkbirr";
17
+
18
+ async function main() {
19
+ try {
20
+ const output = await checkbirr({
21
+ src: "./screenshot.jpg",
22
+ amount: 5000,
23
+ receiverAccounts: [
24
+ {
25
+ bank: "CBE",
26
+ receiverName: "John Doe Smith",
27
+ receiverAccountNumber: "1000*****",
28
+ },
29
+ ],
30
+ });
31
+
32
+ console.log(output);
33
+ } catch (error) {
34
+ console.log(error);
35
+ }
36
+ }
37
+
38
+ main();
39
+ ```
40
+
41
+ ## Output
42
+
43
+ ```ts
44
+ {
45
+ verified:true,
46
+ bank:"CBE",
47
+ senderName:"Rip Van Winkle",
48
+ senderAccountNumber:"1000*****",
49
+ receiverName:"John Doe Smith",
50
+ receiverAccountNumber:"1000*****",
51
+ date:"Nov 02, 2026, 4:36 PM",
52
+ referenceNumber:"FM43812X",
53
+ amount:5000,
54
+ checkBirrID:"asd8f87a0d9f8a0as89f8"
55
+ }
56
+ ```
57
+
58
+ ## How it works
59
+
60
+ CheckBirr scans the QrCode present on the screenshot and navigates to the official website of the bank to extract transaction details.
61
+ After the details are extracted it simply compares the values provided by the developer with the ones retrieved from the website.
62
+ Finally, deming the transaction as verified or not depending on the similarity of the two values.
63
+
64
+ ## Why use CheckBirr
65
+
66
+ CheckBirr does **NOT** simply extract the transaction details from the image using OCR scanning, But rather
67
+ it retrieves them directly from the official site of the bank using web scrapping.
68
+ This makes it very reliable and impossible to trick with fake or edited screenshots.
69
+
70
+ ## Duplicate Receipts
71
+
72
+ This library does **NOT** detect duplicate transactions nor does it keep a record of previous transactions.
73
+ It is up to the developer to store a history of **checkBirrID**s (which are unique IDs given to each transaction by the library) and compare new receipts in order to detect duplicates.
74
+
75
+ ## Supported Banks
76
+
77
+ ![Commercial Bank of Ethiopia](/assets/cbe.jpg)
78
+
79
+ ## Disclaimer
80
+
81
+ This is **NOT** an official library published by any of the banks supported.
82
+
83
+ ## License
84
+
85
+ This project is licensed under the **MIT License**.
@@ -0,0 +1,3 @@
1
+ import Transaction from "../types/transaction.js";
2
+ import type { Receipt } from "../utils/validator.js";
3
+ export default function checkCBE(url: string, receipt: Receipt): Promise<Transaction>;
@@ -0,0 +1,43 @@
1
+ import puppeteer from "puppeteer";
2
+ import hash from "../utils/hasher.js";
3
+ export default async function checkCBE(url, receipt) {
4
+ const browser = await puppeteer.launch({ headless: true });
5
+ const page = await browser.newPage();
6
+ await page.goto(url, { waitUntil: "networkidle2" });
7
+ await page.waitForSelector(".md\\:text-lg.text-sm.font-semibold.py-1.md\\:py-2.px-4.text-center.border-b.m-0", { visible: true, timeout: 10000 });
8
+ const selector = await page.$$eval(`.text-black.text-right.md\\:text-sm.md\\:col-span-1`, (elements) => elements.map((el) => el.textContent?.trim() ?? ""));
9
+ const bank = "CBE";
10
+ const senderName = selector[0];
11
+ const senderAccountNumber = selector[1];
12
+ const receiverName = selector[2];
13
+ const receiverAccountNumber = selector[3];
14
+ const date = selector[5];
15
+ const referenceNumber = selector[6];
16
+ const amount = parseFloat(selector[8]);
17
+ await browser.close();
18
+ const checkBirrID = hash(bank, referenceNumber);
19
+ let verified = false;
20
+ for (let i = 0; i < receipt.receiverAccounts.length; i++) {
21
+ if (receipt.receiverAccounts[i].bank === "CBE") {
22
+ if (receipt.amount === amount &&
23
+ receipt.receiverAccounts[i].receiverAccountNumber.slice(-4) ===
24
+ receiverAccountNumber.slice(-4) &&
25
+ receipt.receiverAccounts[i].receiverName === receiverName) {
26
+ verified = true;
27
+ }
28
+ }
29
+ }
30
+ const transaction = {
31
+ verified,
32
+ bank,
33
+ senderName,
34
+ senderAccountNumber,
35
+ receiverName,
36
+ receiverAccountNumber,
37
+ date,
38
+ referenceNumber,
39
+ amount,
40
+ checkBirrID,
41
+ };
42
+ return transaction;
43
+ }
@@ -0,0 +1,2 @@
1
+ import { type Receipt } from "./utils/validator.js";
2
+ export default function checkbirr(receipt: Receipt): Promise<import("./types/transaction.js").default>;
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ import scan from "./utils/scan.js";
2
+ import identifyBank from "./utils/identifyBank.js";
3
+ import validator from "./utils/validator.js";
4
+ import checkCBE from "./banks/cbe.js";
5
+ export default async function checkbirr(receipt) {
6
+ const validationResult = validator(receipt);
7
+ if (!validationResult.success) {
8
+ const messages = validationResult.error.issues.map((issue) => {
9
+ const path = issue.path.join(".");
10
+ return path ? `"${path}": ${issue.message}` : issue.message;
11
+ });
12
+ throw new Error(`checkbirr: invalid receipt:\n${messages.join("\n")}`);
13
+ }
14
+ const validReceipt = validationResult.data;
15
+ const url = await scan(validReceipt.src);
16
+ const bank = identifyBank(url);
17
+ switch (bank) {
18
+ case "CBE": {
19
+ const output = await checkCBE(url, validReceipt);
20
+ return output;
21
+ }
22
+ default:
23
+ throw new Error(`Unsupported bank: ${bank}`);
24
+ }
25
+ }
@@ -0,0 +1,9 @@
1
+ export default interface Receipt {
2
+ imagePath: string;
3
+ receiverAccounts: {
4
+ bank: string;
5
+ receiverAccountNumber: string;
6
+ receiverName: string;
7
+ }[];
8
+ amount: number;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,12 @@
1
+ export default interface Transaction {
2
+ verified: boolean;
3
+ referenceNumber: string;
4
+ bank: string;
5
+ senderAccountNumber: string;
6
+ senderName: string;
7
+ receiverAccountNumber: string;
8
+ receiverName: string;
9
+ amount: number;
10
+ date: string;
11
+ checkBirrID: string;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ declare const banks: {
2
+ name: string;
3
+ url: string;
4
+ }[];
5
+ export default banks;
@@ -0,0 +1,7 @@
1
+ const banks = [
2
+ {
3
+ name: "CBE",
4
+ url: "mbreciept.cbe.com.et",
5
+ }
6
+ ];
7
+ export default banks;
@@ -0,0 +1 @@
1
+ export default function hash(bank: string, referenceNumber: string): string;
@@ -0,0 +1,7 @@
1
+ import { createHash } from "node:crypto";
2
+ export default function hash(bank, referenceNumber) {
3
+ const checkBirrID = createHash("sha256")
4
+ .update(`${bank}:${referenceNumber}`)
5
+ .digest("hex");
6
+ return checkBirrID;
7
+ }
@@ -0,0 +1 @@
1
+ export default function identifyBank(url: string): string;
@@ -0,0 +1,8 @@
1
+ import banks from "./banks.js";
2
+ export default function identifyBank(url) {
3
+ const baseURL = new URL(url).hostname;
4
+ const bank = banks.find(b => b.url === baseURL);
5
+ if (!bank)
6
+ throw new Error(`Unknown bank for URL: ${url}`);
7
+ return bank.name;
8
+ }
@@ -0,0 +1 @@
1
+ export default function scan(imagePath: string): Promise<string>;
@@ -0,0 +1,20 @@
1
+ import jsQR from "jsqr";
2
+ import { Jimp } from "jimp";
3
+ export default async function scan(imagePath) {
4
+ try {
5
+ const image = await Jimp.read(imagePath);
6
+ const { data, width, height } = image.bitmap;
7
+ const code = jsQR.default(new Uint8ClampedArray(data), width, height);
8
+ if (code) {
9
+ return code.data;
10
+ }
11
+ else {
12
+ console.log("No QR code found");
13
+ return `Error: QrCode Not Found`;
14
+ }
15
+ }
16
+ catch (error) {
17
+ console.log(error);
18
+ return `Error: ${error}`;
19
+ }
20
+ }
@@ -0,0 +1,21 @@
1
+ import * as z from "zod";
2
+ declare const receiptSchema: z.ZodObject<{
3
+ src: z.ZodString;
4
+ receiverAccounts: z.ZodArray<z.ZodObject<{
5
+ bank: z.ZodString;
6
+ receiverName: z.ZodString;
7
+ receiverAccountNumber: z.ZodString;
8
+ }, z.core.$strip>>;
9
+ amount: z.ZodNumber;
10
+ }, z.core.$strip>;
11
+ export type Receipt = z.infer<typeof receiptSchema>;
12
+ export default function validateReceipt(receipt: unknown): z.ZodSafeParseResult<{
13
+ src: string;
14
+ receiverAccounts: {
15
+ bank: string;
16
+ receiverName: string;
17
+ receiverAccountNumber: string;
18
+ }[];
19
+ amount: number;
20
+ }>;
21
+ export {};
@@ -0,0 +1,15 @@
1
+ import * as z from "zod";
2
+ const receiptSchema = z.object({
3
+ src: z.string(),
4
+ receiverAccounts: z
5
+ .array(z.object({
6
+ bank: z.string(),
7
+ receiverName: z.string(),
8
+ receiverAccountNumber: z.string(),
9
+ }))
10
+ .min(1, "receiverAccounts must have at least one entry"),
11
+ amount: z.number(),
12
+ });
13
+ export default function validateReceipt(receipt) {
14
+ return receiptSchema.safeParse(receipt);
15
+ }
package/package.json CHANGED
@@ -1,6 +1,41 @@
1
1
  {
2
2
  "name": "checkbirr",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.0.1",
4
+ "description": "Automatic payment verification for Ethiopian bank receipts",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "type": "module",
11
+ "scripts": {
12
+ "build": "tsc"
13
+ },
14
+ "keywords": [
15
+ "birr",
16
+ "checkbirr",
17
+ "verification",
18
+ "payment",
19
+ "payment-verification",
20
+ "screenshot",
21
+ "cbe",
22
+ "Ethiopia"
23
+ ],
24
+ "author": "Amieldev",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/Amieldev/checkbirr"
29
+ },
30
+ "dependencies": {
31
+ "jimp": "^1.6.1",
32
+ "jsqr": "^1.4.0",
33
+ "puppeteer": "^25.10.0",
34
+ "zod": "^4.6.7"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^26.4.0",
38
+ "tsx": "^4.23.15",
39
+ "typescript": "^7.0.2"
40
+ }
41
+ }