Transaction Verification API — Python library for verifying Ethiopian payment transactions (CBE, Telebirr, Dashen, Abyssinia, CBE Birr, M-Pesa).
Project description
tx-verify
Python library for verifying Ethiopian payment transactions across multiple providers: CBE, Telebirr, Dashen Bank, Bank of Abyssinia, CBE Birr, and M-Pesa.
Each verifier fetches the official receipt from the provider (PDF or HTML),
parses it, and returns typed result objects. No headless browser is bundled —
PDFs are parsed with pypdf and HTML with BeautifulSoup — so it runs
anywhere Python does.
Supported Providers
| Provider | Function | Input (example) |
|---|---|---|
| CBE | verify_cbe() |
reference="FT…", account_suffix="…" |
| Telebirr | verify_telebirr() |
reference="CE12345678" |
| Dashen Bank | verify_dashen() |
transaction_reference="123…" (16 dig) |
| Bank of Abyssinia | verify_abyssinia() |
reference="FT…", suffix="…" (5 dig) |
| CBE Birr | verify_cbe_birr() |
receipt="…", phone="09…" (local) |
| M-Pesa | verify_mpesa() |
transaction_id="UE20VG1GS8" |
| Image (Mistral AI) | verify_image() |
image_bytes, auto-detects provider |
| Universal | verify_universal() |
reference — auto-routes by format |
Installation
pip install tx-verify
Or with uv:
uv pip install tx-verify
Quick Start
import asyncio
from tx_verify import verify_telebirr, verify_cbe
async def main():
# --- Telebirr ---
receipt = await verify_telebirr("CE12345678")
if receipt:
print(receipt.payer_name, receipt.settled_amount)
# --- CBE ---
result = await verify_cbe("FT23062669JJ", account_suffix="12345678")
if result.success:
print(f"Paid {result.amount} ETB to {result.receiver}")
asyncio.run(main())
Examples
See the examples/ directory for a runnable example per
provider:
| File | What it shows |
|---|---|
telebirr.py |
Verify a Telebirr receipt by reference number |
cbe.py |
Fetch and parse a CBE PDF receipt |
cbe_birr.py |
Verify a CBE Birr wallet transaction |
dashen.py |
Verify a Dashen Bank receipt with retry logic |
abyssinia.py |
Verify a Bank of Abyssinia transaction |
mpesa.py |
Verify an Ethiopian M-Pesa transaction |
image.py |
Analyse a receipt image with Mistral Vision AI |
universal.py |
Let the library auto-route to the right provider |
error_handling.py |
Catch provider-specific errors gracefully |
Provider Reference
CBE — Commercial Bank of Ethiopia
CBE references are 12 characters starting with FT. You must supply the
last 8 digits of the account number as a suffix. The bank returns a PDF
that is fetched and parsed automatically.
from tx_verify import verify_cbe
result = await verify_cbe("FT23062669JJ", "12345678")
# result.success → bool
# result.payer → str | None
# result.receiver → str | None
# result.amount → float | None
# result.date → datetime | None
# result.reference → str | None
# result.reason → str | None
# result.error → str | None
Telebirr
Telebirr references are 10-character alphanumeric codes. The library scrapes the public Ethio Telecom receipt page.
from tx_verify import verify_telebirr
receipt = await verify_telebirr("CE12345678")
# receipt.payer_name, receipt.settled_amount, receipt.total_paid_amount, …
Dashen Bank
Dashen references are 16-digit numbers starting with 3 digits (e.g.
1234567890123456). The verifier fetches a PDF with built-in retry logic
(up to 5 attempts).
from tx_verify import verify_dashen
result = await verify_dashen("1234567890123456")
# result.sender_name, result.transaction_amount, result.total, …
Bank of Abyssinia
Abyssinia references are also 12 characters starting with FT, but the
suffix is the last 5 digits of the account number. The bank returns JSON
rather than a PDF.
from tx_verify import verify_abyssinia
result = await verify_abyssinia("FT23062669JJ", "90172")
# result.payer, result.amount, result.date, …
CBE Birr
CBE Birr receipts are 10-character alphanumeric codes. You also need the
wallet phone number in local Ethiopian format (e.g. 0911234567).
from tx_verify import verify_cbe_birr
result = await verify_cbe_birr("AB1234CD56", "0911234567")
# result.customer_name, result.amount, result.paid_amount, …
M-Pesa
M-Pesa references are 10-character alphanumeric codes. The verifier hits the Safaricom primary API.
from tx_verify import verify_mpesa
result = await verify_mpesa("UE20VG1GS8")
# result.payer_name, result.amount, result.service_fee, result.vat, …
Image verification (Mistral Vision)
Upload a receipt image (JPEG/PNG) and Mistral Vision AI will detect whether it is a CBE or Telebirr receipt, extract the reference, and optionally verify it automatically.
from tx_verify import verify_image
with open("receipt.jpg", "rb") as f:
image_bytes = f.read()
# Detect only
info = await verify_image(image_bytes, auto_verify=False)
print(info.type, info.reference, info.forward_to)
# Auto-verify (account_suffix required for CBE)
info = await verify_image(
image_bytes,
auto_verify=True,
account_suffix="12345678",
)
print(info.verified, info.details)
Requires
MISTRAL_API_KEYenvironment variable and themistralaipackage (installed automatically).
Universal — auto-route by reference format
Hand any reference to verify_universal() and it routes to the correct provider
based on length and prefix:
| Reference format | Routed to |
|---|---|
16 digits starting with 3 |
Dashen Bank |
12 chars starting with FT + 8-digit suffix |
CBE |
12 chars starting with FT + 5-digit suffix |
Bank of Abyssinia |
10 chars + phone_number |
CBE Birr |
| 10 chars (no phone) | Telebirr |
from tx_verify import verify_universal
result = await verify_universal("CE12345678")
print(result.success, result.data, result.error)
Proxy Support
All receipt verifiers accept an explicit proxies argument. Environment
variables are never read automatically — you must pass the proxy yourself.
Supported schemes:
| Scheme | Description |
|---|---|
http |
Plain HTTP forward proxy |
https |
HTTPS proxy (CONNECT tunnel) |
socks4 |
SOCKS4 proxy |
socks5 |
SOCKS5 proxy (client resolves DNS) |
socks5h |
SOCKS5 proxy (proxy resolves DNS) |
Authentication is embedded in the URL:
# Single global proxy
proxies = "http://user:pass@proxy.example.com:8080"
# Per-scheme mapping
proxies = {
"http://": "http://proxy.example.com:8080",
"https://": "socks5://localhost:1080",
}
Pass it to any verifier:
from tx_verify import verify_telebirr, verify_cbe, verify_mpesa
# Telebirr through an HTTP proxy
receipt = await verify_telebirr("CE12345678", proxies="http://proxy:8080")
# CBE through SOCKS5
result = await verify_cbe("FT23062669JJ", "12345678", proxies="socks5://127.0.0.1:1080")
# M-Pesa with per-scheme mapping
result = await verify_mpesa("UE20VG1GS8", proxies={
"http://": "http://proxy:8080",
"https://": "socks5h://proxy:1080",
})
verify_universal and verify_image also forward proxies to the
underlying provider automatically.
SOCKS tip:
socks5h://tells the proxy server to resolve hostnames, which is useful when the client cannot reach DNS directly.
Error Handling
All verifiers return result objects rather than raising for expected
failures (network errors, missing receipts, parsing failures). Inspect
result.success and result.error.
Telebirr may raise TelebirrVerificationError when a proxy returns an explicit
error message. Catch it if you want to show the user a friendly message:
from tx_verify import TelebirrVerificationError, verify_telebirr
try:
receipt = await verify_telebirr("INVALID_REF")
except TelebirrVerificationError as exc:
print(f"Telebirr error: {exc}")
if exc.details:
print(f"Details: {exc.details}")
The library also provides a generic error handler for wrapping database or internal errors:
from tx_verify.utils.error_handler import AppError, ErrorType
Environment Variables
| Variable | Purpose |
|---|---|
MISTRAL_API_KEY |
Required for verify_image() |
LOG_LEVEL |
DEBUG or INFO (default INFO) |
Development
# Clone
git clone https://github.com/YOUR_USERNAME/tx-verify.git
cd tx-verify
# Install with dev dependencies
pip install -e ".[dev]"
# Install pre-commit hooks
pre-commit install
# Lint & format
ruff check .
ruff format .
# Type-check
mypy tx_verify/
# Run tests
pytest
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tx_verify-0.1.1.tar.gz.
File metadata
- Download URL: tx_verify-0.1.1.tar.gz
- Upload date:
- Size: 134.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
76c334cd110ec63c9ce190da64fbdd04dbca5eb851e479e69fdc069a0b620f75
|
|
| MD5 |
12e2c4614ba3b7c81aed8270c838abc9
|
|
| BLAKE2b-256 |
20af9d5ebbbe1aeca0cbd89bd61cf017489f5c966361dfa76b0acca2f7f9dbb0
|
Provenance
The following attestation bundles were made for tx_verify-0.1.1.tar.gz:
Publisher:
publish.yml on nahom-network/tx-verify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tx_verify-0.1.1.tar.gz -
Subject digest:
76c334cd110ec63c9ce190da64fbdd04dbca5eb851e479e69fdc069a0b620f75 - Sigstore transparency entry: 1452483025
- Sigstore integration time:
-
Permalink:
nahom-network/tx-verify@d2825ec665230f3a5fc8cf03f9b403522dce9257 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/nahom-network
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d2825ec665230f3a5fc8cf03f9b403522dce9257 -
Trigger Event:
push
-
Statement type:
File details
Details for the file tx_verify-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tx_verify-0.1.1-py3-none-any.whl
- Upload date:
- Size: 31.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9f0d659615c7e75673b874f348a25f7bd920c0cdd5e52911442018f1c6d3f677
|
|
| MD5 |
80929b9d5a936e34ec0932ea4754a369
|
|
| BLAKE2b-256 |
94a54faf378c1bdf41dbef3a2ee003b641ee04161d0ff095f5df22f513f30e0f
|
Provenance
The following attestation bundles were made for tx_verify-0.1.1-py3-none-any.whl:
Publisher:
publish.yml on nahom-network/tx-verify
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
tx_verify-0.1.1-py3-none-any.whl -
Subject digest:
9f0d659615c7e75673b874f348a25f7bd920c0cdd5e52911442018f1c6d3f677 - Sigstore transparency entry: 1452483184
- Sigstore integration time:
-
Permalink:
nahom-network/tx-verify@d2825ec665230f3a5fc8cf03f9b403522dce9257 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/nahom-network
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@d2825ec665230f3a5fc8cf03f9b403522dce9257 -
Trigger Event:
push
-
Statement type: