gava-connect-plus 0.1.0__tar.gz
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.
- gava_connect_plus-0.1.0/PKG-INFO +248 -0
- gava_connect_plus-0.1.0/README.md +234 -0
- gava_connect_plus-0.1.0/pyproject.toml +36 -0
- gava_connect_plus-0.1.0/src/gavaconnect/__init__.py +34 -0
- gava_connect_plus-0.1.0/src/gavaconnect/apis/__init__.py +6 -0
- gava_connect_plus-0.1.0/src/gavaconnect/apis/invoice.py +143 -0
- gava_connect_plus-0.1.0/src/gavaconnect/apis/pin.py +221 -0
- gava_connect_plus-0.1.0/src/gavaconnect/apis/station.py +126 -0
- gava_connect_plus-0.1.0/src/gavaconnect/async_client.py +57 -0
- gava_connect_plus-0.1.0/src/gavaconnect/client_base.py +79 -0
- gava_connect_plus-0.1.0/src/gavaconnect/exceptions.py +64 -0
- gava_connect_plus-0.1.0/src/gavaconnect/models.py +82 -0
- gava_connect_plus-0.1.0/src/gavaconnect/py.typed +0 -0
- gava_connect_plus-0.1.0/src/gavaconnect/sync_client.py +64 -0
- gava_connect_plus-0.1.0/src/gavaconnect/tui.py +181 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: gava-connect-plus
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async-first production-grade SDK for Kenya's GavaConnect Enterprise API platform.
|
|
5
|
+
Author: Erick
|
|
6
|
+
Author-email: Erick <hearteric57@gmail.com>
|
|
7
|
+
Requires-Dist: blessed>=1.44.0
|
|
8
|
+
Requires-Dist: httpx>=0.28.1
|
|
9
|
+
Requires-Dist: pydantic>=2.13.4
|
|
10
|
+
Requires-Dist: pytest>=9.1.1
|
|
11
|
+
Requires-Dist: respx>=0.23.1
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<img alt="gava-connect-plus banner" src="https://raw.githubusercontent.com/IamMuuo/gava-connect-plus/master/assets/github-header-banner.png" width="100%" />
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/flag/ke.svg?theme=gray" /><img alt="built in" src="https://shieldcn.dev/flag/ke.svg?theme=gray&mode=light" /></picture>
|
|
21
|
+
<a href="https://github.com/iammuuo/gava-connect-plus"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/license.svg?theme=green" /><img alt="license" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/license.svg?theme=green&mode=light" /></picture></a>
|
|
22
|
+
<a href="https://github.com/iammuuo/gava-connect-plus/commits"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/last-commit.svg?theme=cyan" /><img alt="last commit" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/last-commit.svg?theme=cyan&mode=light" /></picture></a>
|
|
23
|
+
<a href="https://github.com/iammuuo/gava-connect-plus/actions"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/ci.svg" /><img alt="CI" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/ci.svg?mode=light" /></picture></a>
|
|
24
|
+
<a href="https://github.com/iammuuo/gava-connect-plus/graphs/contributors"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/contributors.svg" /><img alt="contributors" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/contributors.svg?mode=light" /></picture></a>
|
|
25
|
+
<a href="https://github.com/iammuuo/gava-connect-plus"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/stars.svg?theme=rose" /><img alt="stars" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/stars.svg?theme=rose&mode=light" /></picture></a>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
# gava-connect-plus
|
|
29
|
+
|
|
30
|
+
Every Kenyan developer who has built an e-commerce platform, an accounting tool, or a payroll system eventually faces the same formidable milestone: integrating with the Kenya Revenue Authority (KRA) APIs.
|
|
31
|
+
|
|
32
|
+
Historically, this meant wading through fragmented PDF documentation, wrestling with clunky authentication flows, and writing thousands of lines of fragile boilerplate just to check if a PIN is valid or an eTIMS invoice is authentic. The process wasn't just slow—it was a tax on developer sanity.
|
|
33
|
+
|
|
34
|
+
gava-connect-plus was born out of a simple, rebellious realization: Integrating with local compliance infrastructure shouldn't feel like standing in a physical KRA line. This library serves as a beautiful, unofficial bridge between modern Python applications and the government gateway—built to handle the heavy lifting of compliance safely, invisibly, and instantly.
|
|
35
|
+
|
|
36
|
+
## Core Principles
|
|
37
|
+
|
|
38
|
+
The architecture of gava-connect-plus is guided by three simple truths:
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
### 1. Developer Zen
|
|
42
|
+
|
|
43
|
+
The code you write should look like clean Python, not government bureaucracy. Complex SOAP/REST structures, weird payload mappings, and authentication handshakes are abstracted away behind an elegant, predictable API surface. One method call does exactly what it says.
|
|
44
|
+
|
|
45
|
+
### 2. Radical Isolation
|
|
46
|
+
|
|
47
|
+
Security is peace of mind. Every corporate department or microservice deserves its own cryptographic boundaries. By keeping service credentials strictly isolated (Invoice, PIN, and Station each running on their own terms), the library prevents permission creep and ensures your production tokens never bleed into one another.
|
|
48
|
+
|
|
49
|
+
### 3. Execution "Chap Chap"
|
|
50
|
+
|
|
51
|
+
Time is the only non-renewable asset. Whether running automated testing pipelines in the cloud or deploying to live production, the library is lightweight, optimized, and zero-fluff. It does its job and gets out of your way.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## Installation & Quick Start
|
|
55
|
+
|
|
56
|
+
True to the principle of execution chap chap, getting the library into your environment takes a single command. We recommend using uv for lightning-fast environment resolution, but standard tools work perfectly too.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# For the mordenist usinv uv
|
|
60
|
+
uv add gava-connect-plus
|
|
61
|
+
|
|
62
|
+
# The classic way
|
|
63
|
+
pip install gava-connect-plus
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
### Obtaining passwords, keys etc
|
|
68
|
+
|
|
69
|
+
Before writing any code, you need to secure your cryptographic credentials from the official gateway ecosystem.
|
|
70
|
+
1. Head over to the Kenyan Government [Developer Portal](https://developer.go.ke/).
|
|
71
|
+
2. Register an account and navigate to your dashboard workspace to provision access keys.
|
|
72
|
+
|
|
73
|
+
> Tutorials for obtaining passwords available on youtube and beyond the scope of this doc
|
|
74
|
+
|
|
75
|
+
### The Multi-App Architecture (Crucial Note)
|
|
76
|
+
|
|
77
|
+
The KRA API gateway relies on strict security scoping. Permissions are segregated at the application layer rather than bundled into a single master key. This means credentials must be obtained per module/API.
|
|
78
|
+
|
|
79
|
+
If your software requires both taxpayer identity verification and invoice tracking, you cannot use a single client key. You must create two separate applications inside the portal:
|
|
80
|
+
|
|
81
|
+
- App #1: Provisioned explicitly with the `PIN Checker by PIN` scope.
|
|
82
|
+
- App #2: Provisioned explicitly with the `Invoice Checker` scope.
|
|
83
|
+
|
|
84
|
+
Once you have your isolated keys, gava-connect-plus takes complete control of the administrative mess.
|
|
85
|
+
|
|
86
|
+
**The Promise**: You do not need to worry about the underlying Authorization endpoints, token expiration thresholds, or handshake state machines. Once the library is supplied with your application keys, internal OAuth token provisioning, caching, and background refreshes are executed seamlessly on autopilot.
|
|
87
|
+
|
|
88
|
+
## 1. Configure the Environment Sanctuary
|
|
89
|
+
Expose your isolated sandbox or production credential pairs directly to your system shell thread. The library automatically tracks these exact environment keys, keeping your production secrets completely out of source control:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# App #1: PIN Validation Credentials
|
|
93
|
+
export KRA_PIN_KEY="your_pin_app_consumer_key"
|
|
94
|
+
export KRA_PIN_SECRET="your_pin_app_consumer_secret"
|
|
95
|
+
|
|
96
|
+
# App #2: eTIMS Invoice Credentials
|
|
97
|
+
export KRA_INVOICE_KEY="your_invoice_app_consumer_key"
|
|
98
|
+
export KRA_INVOICE_SECRET="your_invoice_app_consumer_secret"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## 2. Write Pure, Frictionless Python
|
|
102
|
+
With your system environments aligned, initialize the workspace context. The library dynamically routes internal token handshakes to their respective apps in the background based on the API you invoke.
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from gavaconnect import GavaConnect
|
|
106
|
+
from gavaconnect.exceptions import GavaConnectError
|
|
107
|
+
|
|
108
|
+
# Initialize the engine targeting the KRA gateway sandbox
|
|
109
|
+
with GavaConnect(environment="sandbox") as gava:
|
|
110
|
+
try:
|
|
111
|
+
# Validates identity via your PIN-scoped application keys
|
|
112
|
+
record = gava.pin.check("A001234567Z")
|
|
113
|
+
print(f"Identity Verified: {record.taxpayer_name}")
|
|
114
|
+
|
|
115
|
+
# Audits eTIMS status via your Invoice-scoped application keys
|
|
116
|
+
# All background OAuth tokens swap silently behind the scenes
|
|
117
|
+
invoice = gava.invoice.get("INV-99824-X")
|
|
118
|
+
print(f"Gross Invoice Value: KES {invoice.total_invoice_amount:,.2f}")
|
|
119
|
+
|
|
120
|
+
except GavaConnectError as e:
|
|
121
|
+
print(f"The gateway returned a peaceful error: {e}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 3. Explicit Dependency Injection (Alternative)
|
|
125
|
+
|
|
126
|
+
If you are managing configuration objects programmatically at runtime (e.g., retrieving secrets from an encrypted vault), bypass the environment variables by injecting the multi-app map dictionary directly into the builder constructor:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from gavaconnect import GavaConnect
|
|
130
|
+
|
|
131
|
+
# Map explicit credentials precisely to their module domain targets
|
|
132
|
+
custom_config = {
|
|
133
|
+
"pin": {
|
|
134
|
+
"consumer_key": "vault_pin_key_xyz",
|
|
135
|
+
"consumer_secret": "vault_pin_secret_abc"
|
|
136
|
+
},
|
|
137
|
+
"invoice": {
|
|
138
|
+
"consumer_key": "vault_invoice_key_123",
|
|
139
|
+
"consumer_secret": "vault_invoice_secret_789"
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
with GavaConnect(environment="sandbox", **custom_config) as gava:
|
|
144
|
+
station = gava.station.get("A001234567Z")
|
|
145
|
+
print(f"Assigned Tax Station: {station.station_name}")
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
# A word about using the library
|
|
151
|
+
|
|
152
|
+
## Sync & Async Harmony
|
|
153
|
+
|
|
154
|
+
Python architectures are diverse. Some systems demand the raw speed of non-blocking concurrency, while others require the rock-solid predictability of standard sequential execution. `gava-connect-plus` honors both paths by providing native, separate engines for both synchronous and asynchronous workflows.
|
|
155
|
+
|
|
156
|
+
When initializing the library, you can choose the engine that perfectly aligns with your environment constraints and runtime architecture.
|
|
157
|
+
|
|
158
|
+
### The Asynchronous Path (For High-Concurrence API Gateways)
|
|
159
|
+
If you are building high-throughput microservices using modern async frameworks like FastAPI, Litestar, or Sanic, use the async flavor to ensure your application threads never sleep while waiting for network I/O from the KRA gateway:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
import asyncio
|
|
163
|
+
from gavaconnect import GavaConnect
|
|
164
|
+
|
|
165
|
+
async def main():
|
|
166
|
+
# Non-blocking connection pooling executing natively inside your event loop
|
|
167
|
+
async with GavaConnectAsync(environment="sandbox") as gava:
|
|
168
|
+
record = await gava.pin.check("A001234567Z")
|
|
169
|
+
print(f"Async Identity Verified: {record.taxpayer_name}")
|
|
170
|
+
|
|
171
|
+
asyncio.run(main())
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### The Synchronous Path (For Predictable, Thread-Safe Workflows)
|
|
175
|
+
|
|
176
|
+
For applications built on standard blocking architectures, the synchronous engine provides a clean, dependency-free wrapper that executes line-by-line without an active event loop loop manager:
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
from gavaconnect import GavaConnectSync
|
|
180
|
+
|
|
181
|
+
with GavaConnect(environment="sandbox") as gava:
|
|
182
|
+
record = gava.pin.check("A001234567Z")
|
|
183
|
+
print(f"Sync Identity Verified: {record.taxpayer_name}")
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Architectural Nuance: When to stay Synchronous (The Django Paradigm)
|
|
188
|
+
|
|
189
|
+
It is tempting to default to async for all network utilities, but context dictates performance. For example, if you are integrating this library into a traditional Django application served via a standard WSGI stack (like Gunicorn or uWSGI), you should choose the synchronous version.
|
|
190
|
+
|
|
191
|
+
The Technical Reason: Django’s core engine, request lifecycle middlewares, and database ORM layers are fundamentally synchronous by design. While Django offers async views, mixing them into a WSGI runtime forces the framework to wrap execution threads in thread-switching utility layers (`async_to_sync` and `sync_to_async`).
|
|
192
|
+
|
|
193
|
+
If your view is already handling a synchronous database transaction, throwing an asynchronous API call into the middle of it triggers complex context-switching overhead and thread hopping inside the Python runtime. This context switching can actually degrade performance and introduce subtle race conditions with thread-local data. Staying purely synchronous inside a synchronous application architecture keeps your execution path flat, memory overhead minimal, and debugging straightforward.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
# Path to Completeness (Module Roadmap)
|
|
198
|
+
The KRA API ecosystem is vast, covering everything from basic identity checks to complex customs calculations and tax filing routines. Rome wasn't built in a day, and neither is the ultimate developer gateway toolkit.
|
|
199
|
+
|
|
200
|
+
gava-connect-plus is a living blueprint. While the core verification bedrock is fully operational and production-ready, several advanced transactional modules are currently slated for future integration cycles.
|
|
201
|
+
|
|
202
|
+
Below is the current state of alignment between the SDK and the official developer.go.ke portal services:
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
<div align="center">
|
|
207
|
+
|
|
208
|
+
| Feature / API Endpoint | Status | Tested |
|
|
209
|
+
| --- | --- | :---: |
|
|
210
|
+
| Automated OAuth Token Lifecycle Management & Caching | ✅ | ✅ |
|
|
211
|
+
| PIN Checker by PIN (`PIN_Validation_by_PIN`) | ✅ | ✅ |
|
|
212
|
+
| PIN Checker by ID (`DTD_PINChecker`) | ✅ | ✅ |
|
|
213
|
+
| Know KRA Tax Service Office/Station (`SUC-iTax-USSD_Know_Your_Station`) | ✅ | ✅ |
|
|
214
|
+
| eTIMS Invoice Checker (`Invoice-Checker`) | ✅ | ✅ |
|
|
215
|
+
| Fetch Taxpayer Obligations (`TaxPayer_Tax_Obligations_Fetcher`) | ⏳ Planned | - |
|
|
216
|
+
| Withholding Tax PRN Generation (Income Tax, Rental, VAT) | ⏳ Planned | - |
|
|
217
|
+
| Tax Compliance Certificate (TCC) Validation & Application | ⏳ Planned | - |
|
|
218
|
+
| Automated NIL Return Filing (`iTax_NIL_Return`) | ⏳ Planned | - |
|
|
219
|
+
| Income Tax & VAT Exemption Checker | ⏳ Planned | - |
|
|
220
|
+
| Turnover Tax (TOT) Return Filing | ⏳ Planned | - |
|
|
221
|
+
| Customs Declaration Status Checker & Tax Calculator | ⏳ Planned | - |
|
|
222
|
+
| Import Certificate Checker (By Number / PIN) | ⏳ Planned | - |
|
|
223
|
+
| Individual KRA PIN Registration Gateway | ⏳ Planned | - |
|
|
224
|
+
| eTIMS OSCU Integrator Automated Testing Suite | ⏳ Planned | - |
|
|
225
|
+
| Excise License Checker (By Pin / Certificate Number) | ⏳ Planned | - |
|
|
226
|
+
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
# The Genesis & Contributing
|
|
230
|
+
|
|
231
|
+
Let’s clear the air right away: I am not affiliated, associated, authorized, or in any way officially connected with the Kenya Revenue Authority (KRA). This project was born out of pure, unadulterated developer frustration. While building a commercial product that required KRA portal integrations, I looked around the ecosystem for a robust, production-grade Python package that could handle the gateway safely and seamlessly. I found absolutely nothing.
|
|
232
|
+
|
|
233
|
+
Faced with a wall of fragmented PDFs and custom network scripts, I decided to take a deep breath, skip a few nights of sleep, and jump straight down the rabbit hole to build the tool that should have already existed. `gava-connect-plus` is the result of that journey.
|
|
234
|
+
|
|
235
|
+
## Join the Journey
|
|
236
|
+
Because this is a community-driven initiative, your hands and minds are needed to help shape it. Whether you want to squash an edge-case bug, optimize the internal caching layers, or drag one of the *Planned transactional* modules into operational reality, contributions are deeply welcomed.
|
|
237
|
+
|
|
238
|
+
Feel free to open an issue, start a discussion thread, or send a pull request over the wall. Let's make integration painless for the next developer.
|
|
239
|
+
|
|
240
|
+
## Support the energy
|
|
241
|
+
If gava-connect-plus saves you days of digging through portal specifications, keeps your production builds green, or spares your team a minor existential crisis, consider giving back to the project:
|
|
242
|
+
- **⭐ Star the Repository**: It costs nothing and directly helps other Kenyan developers discover the project on GitHub.
|
|
243
|
+
- **🗣️ Spread the Word**: Share the project link in your local tech WhatsApp groups, Discord servers, or on X (Twitter). Let's build a stronger local open-source culture.
|
|
244
|
+
- **☕ Buy Me a Coffee**: If this library saved your business real billable hours, consider fueling the midnight oil for the next sprint of module developments. Your support keeps the zen flowing.
|
|
245
|
+
|
|
246
|
+
<p align="center">
|
|
247
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/chart/github/stars/iammuuo/gava-connect-plus.svg?theme=green&font=geist&title=Github+stars+over+time&icon=refinedgithub" /><img alt="chart" src="https://shieldcn.dev/chart/github/stars/iammuuo/gava-connect-plus.svg?mode=light&theme=green&font=geist&title=Github+stars+over+time&icon=refinedgithub" /></picture>
|
|
248
|
+
</p>
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img alt="gava-connect-plus banner" src="https://raw.githubusercontent.com/IamMuuo/gava-connect-plus/master/assets/github-header-banner.png" width="100%" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/flag/ke.svg?theme=gray" /><img alt="built in" src="https://shieldcn.dev/flag/ke.svg?theme=gray&mode=light" /></picture>
|
|
7
|
+
<a href="https://github.com/iammuuo/gava-connect-plus"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/license.svg?theme=green" /><img alt="license" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/license.svg?theme=green&mode=light" /></picture></a>
|
|
8
|
+
<a href="https://github.com/iammuuo/gava-connect-plus/commits"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/last-commit.svg?theme=cyan" /><img alt="last commit" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/last-commit.svg?theme=cyan&mode=light" /></picture></a>
|
|
9
|
+
<a href="https://github.com/iammuuo/gava-connect-plus/actions"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/ci.svg" /><img alt="CI" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/ci.svg?mode=light" /></picture></a>
|
|
10
|
+
<a href="https://github.com/iammuuo/gava-connect-plus/graphs/contributors"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/contributors.svg" /><img alt="contributors" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/contributors.svg?mode=light" /></picture></a>
|
|
11
|
+
<a href="https://github.com/iammuuo/gava-connect-plus"><picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/github/iammuuo/gava-connect-plus/stars.svg?theme=rose" /><img alt="stars" src="https://shieldcn.dev/github/iammuuo/gava-connect-plus/stars.svg?theme=rose&mode=light" /></picture></a>
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
# gava-connect-plus
|
|
15
|
+
|
|
16
|
+
Every Kenyan developer who has built an e-commerce platform, an accounting tool, or a payroll system eventually faces the same formidable milestone: integrating with the Kenya Revenue Authority (KRA) APIs.
|
|
17
|
+
|
|
18
|
+
Historically, this meant wading through fragmented PDF documentation, wrestling with clunky authentication flows, and writing thousands of lines of fragile boilerplate just to check if a PIN is valid or an eTIMS invoice is authentic. The process wasn't just slow—it was a tax on developer sanity.
|
|
19
|
+
|
|
20
|
+
gava-connect-plus was born out of a simple, rebellious realization: Integrating with local compliance infrastructure shouldn't feel like standing in a physical KRA line. This library serves as a beautiful, unofficial bridge between modern Python applications and the government gateway—built to handle the heavy lifting of compliance safely, invisibly, and instantly.
|
|
21
|
+
|
|
22
|
+
## Core Principles
|
|
23
|
+
|
|
24
|
+
The architecture of gava-connect-plus is guided by three simple truths:
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
### 1. Developer Zen
|
|
28
|
+
|
|
29
|
+
The code you write should look like clean Python, not government bureaucracy. Complex SOAP/REST structures, weird payload mappings, and authentication handshakes are abstracted away behind an elegant, predictable API surface. One method call does exactly what it says.
|
|
30
|
+
|
|
31
|
+
### 2. Radical Isolation
|
|
32
|
+
|
|
33
|
+
Security is peace of mind. Every corporate department or microservice deserves its own cryptographic boundaries. By keeping service credentials strictly isolated (Invoice, PIN, and Station each running on their own terms), the library prevents permission creep and ensures your production tokens never bleed into one another.
|
|
34
|
+
|
|
35
|
+
### 3. Execution "Chap Chap"
|
|
36
|
+
|
|
37
|
+
Time is the only non-renewable asset. Whether running automated testing pipelines in the cloud or deploying to live production, the library is lightweight, optimized, and zero-fluff. It does its job and gets out of your way.
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## Installation & Quick Start
|
|
41
|
+
|
|
42
|
+
True to the principle of execution chap chap, getting the library into your environment takes a single command. We recommend using uv for lightning-fast environment resolution, but standard tools work perfectly too.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# For the mordenist usinv uv
|
|
46
|
+
uv add gava-connect-plus
|
|
47
|
+
|
|
48
|
+
# The classic way
|
|
49
|
+
pip install gava-connect-plus
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
### Obtaining passwords, keys etc
|
|
54
|
+
|
|
55
|
+
Before writing any code, you need to secure your cryptographic credentials from the official gateway ecosystem.
|
|
56
|
+
1. Head over to the Kenyan Government [Developer Portal](https://developer.go.ke/).
|
|
57
|
+
2. Register an account and navigate to your dashboard workspace to provision access keys.
|
|
58
|
+
|
|
59
|
+
> Tutorials for obtaining passwords available on youtube and beyond the scope of this doc
|
|
60
|
+
|
|
61
|
+
### The Multi-App Architecture (Crucial Note)
|
|
62
|
+
|
|
63
|
+
The KRA API gateway relies on strict security scoping. Permissions are segregated at the application layer rather than bundled into a single master key. This means credentials must be obtained per module/API.
|
|
64
|
+
|
|
65
|
+
If your software requires both taxpayer identity verification and invoice tracking, you cannot use a single client key. You must create two separate applications inside the portal:
|
|
66
|
+
|
|
67
|
+
- App #1: Provisioned explicitly with the `PIN Checker by PIN` scope.
|
|
68
|
+
- App #2: Provisioned explicitly with the `Invoice Checker` scope.
|
|
69
|
+
|
|
70
|
+
Once you have your isolated keys, gava-connect-plus takes complete control of the administrative mess.
|
|
71
|
+
|
|
72
|
+
**The Promise**: You do not need to worry about the underlying Authorization endpoints, token expiration thresholds, or handshake state machines. Once the library is supplied with your application keys, internal OAuth token provisioning, caching, and background refreshes are executed seamlessly on autopilot.
|
|
73
|
+
|
|
74
|
+
## 1. Configure the Environment Sanctuary
|
|
75
|
+
Expose your isolated sandbox or production credential pairs directly to your system shell thread. The library automatically tracks these exact environment keys, keeping your production secrets completely out of source control:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# App #1: PIN Validation Credentials
|
|
79
|
+
export KRA_PIN_KEY="your_pin_app_consumer_key"
|
|
80
|
+
export KRA_PIN_SECRET="your_pin_app_consumer_secret"
|
|
81
|
+
|
|
82
|
+
# App #2: eTIMS Invoice Credentials
|
|
83
|
+
export KRA_INVOICE_KEY="your_invoice_app_consumer_key"
|
|
84
|
+
export KRA_INVOICE_SECRET="your_invoice_app_consumer_secret"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## 2. Write Pure, Frictionless Python
|
|
88
|
+
With your system environments aligned, initialize the workspace context. The library dynamically routes internal token handshakes to their respective apps in the background based on the API you invoke.
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from gavaconnect import GavaConnect
|
|
92
|
+
from gavaconnect.exceptions import GavaConnectError
|
|
93
|
+
|
|
94
|
+
# Initialize the engine targeting the KRA gateway sandbox
|
|
95
|
+
with GavaConnect(environment="sandbox") as gava:
|
|
96
|
+
try:
|
|
97
|
+
# Validates identity via your PIN-scoped application keys
|
|
98
|
+
record = gava.pin.check("A001234567Z")
|
|
99
|
+
print(f"Identity Verified: {record.taxpayer_name}")
|
|
100
|
+
|
|
101
|
+
# Audits eTIMS status via your Invoice-scoped application keys
|
|
102
|
+
# All background OAuth tokens swap silently behind the scenes
|
|
103
|
+
invoice = gava.invoice.get("INV-99824-X")
|
|
104
|
+
print(f"Gross Invoice Value: KES {invoice.total_invoice_amount:,.2f}")
|
|
105
|
+
|
|
106
|
+
except GavaConnectError as e:
|
|
107
|
+
print(f"The gateway returned a peaceful error: {e}")
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## 3. Explicit Dependency Injection (Alternative)
|
|
111
|
+
|
|
112
|
+
If you are managing configuration objects programmatically at runtime (e.g., retrieving secrets from an encrypted vault), bypass the environment variables by injecting the multi-app map dictionary directly into the builder constructor:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from gavaconnect import GavaConnect
|
|
116
|
+
|
|
117
|
+
# Map explicit credentials precisely to their module domain targets
|
|
118
|
+
custom_config = {
|
|
119
|
+
"pin": {
|
|
120
|
+
"consumer_key": "vault_pin_key_xyz",
|
|
121
|
+
"consumer_secret": "vault_pin_secret_abc"
|
|
122
|
+
},
|
|
123
|
+
"invoice": {
|
|
124
|
+
"consumer_key": "vault_invoice_key_123",
|
|
125
|
+
"consumer_secret": "vault_invoice_secret_789"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
with GavaConnect(environment="sandbox", **custom_config) as gava:
|
|
130
|
+
station = gava.station.get("A001234567Z")
|
|
131
|
+
print(f"Assigned Tax Station: {station.station_name}")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
# A word about using the library
|
|
137
|
+
|
|
138
|
+
## Sync & Async Harmony
|
|
139
|
+
|
|
140
|
+
Python architectures are diverse. Some systems demand the raw speed of non-blocking concurrency, while others require the rock-solid predictability of standard sequential execution. `gava-connect-plus` honors both paths by providing native, separate engines for both synchronous and asynchronous workflows.
|
|
141
|
+
|
|
142
|
+
When initializing the library, you can choose the engine that perfectly aligns with your environment constraints and runtime architecture.
|
|
143
|
+
|
|
144
|
+
### The Asynchronous Path (For High-Concurrence API Gateways)
|
|
145
|
+
If you are building high-throughput microservices using modern async frameworks like FastAPI, Litestar, or Sanic, use the async flavor to ensure your application threads never sleep while waiting for network I/O from the KRA gateway:
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
import asyncio
|
|
149
|
+
from gavaconnect import GavaConnect
|
|
150
|
+
|
|
151
|
+
async def main():
|
|
152
|
+
# Non-blocking connection pooling executing natively inside your event loop
|
|
153
|
+
async with GavaConnectAsync(environment="sandbox") as gava:
|
|
154
|
+
record = await gava.pin.check("A001234567Z")
|
|
155
|
+
print(f"Async Identity Verified: {record.taxpayer_name}")
|
|
156
|
+
|
|
157
|
+
asyncio.run(main())
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### The Synchronous Path (For Predictable, Thread-Safe Workflows)
|
|
161
|
+
|
|
162
|
+
For applications built on standard blocking architectures, the synchronous engine provides a clean, dependency-free wrapper that executes line-by-line without an active event loop loop manager:
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from gavaconnect import GavaConnectSync
|
|
166
|
+
|
|
167
|
+
with GavaConnect(environment="sandbox") as gava:
|
|
168
|
+
record = gava.pin.check("A001234567Z")
|
|
169
|
+
print(f"Sync Identity Verified: {record.taxpayer_name}")
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Architectural Nuance: When to stay Synchronous (The Django Paradigm)
|
|
174
|
+
|
|
175
|
+
It is tempting to default to async for all network utilities, but context dictates performance. For example, if you are integrating this library into a traditional Django application served via a standard WSGI stack (like Gunicorn or uWSGI), you should choose the synchronous version.
|
|
176
|
+
|
|
177
|
+
The Technical Reason: Django’s core engine, request lifecycle middlewares, and database ORM layers are fundamentally synchronous by design. While Django offers async views, mixing them into a WSGI runtime forces the framework to wrap execution threads in thread-switching utility layers (`async_to_sync` and `sync_to_async`).
|
|
178
|
+
|
|
179
|
+
If your view is already handling a synchronous database transaction, throwing an asynchronous API call into the middle of it triggers complex context-switching overhead and thread hopping inside the Python runtime. This context switching can actually degrade performance and introduce subtle race conditions with thread-local data. Staying purely synchronous inside a synchronous application architecture keeps your execution path flat, memory overhead minimal, and debugging straightforward.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
# Path to Completeness (Module Roadmap)
|
|
184
|
+
The KRA API ecosystem is vast, covering everything from basic identity checks to complex customs calculations and tax filing routines. Rome wasn't built in a day, and neither is the ultimate developer gateway toolkit.
|
|
185
|
+
|
|
186
|
+
gava-connect-plus is a living blueprint. While the core verification bedrock is fully operational and production-ready, several advanced transactional modules are currently slated for future integration cycles.
|
|
187
|
+
|
|
188
|
+
Below is the current state of alignment between the SDK and the official developer.go.ke portal services:
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
<div align="center">
|
|
193
|
+
|
|
194
|
+
| Feature / API Endpoint | Status | Tested |
|
|
195
|
+
| --- | --- | :---: |
|
|
196
|
+
| Automated OAuth Token Lifecycle Management & Caching | ✅ | ✅ |
|
|
197
|
+
| PIN Checker by PIN (`PIN_Validation_by_PIN`) | ✅ | ✅ |
|
|
198
|
+
| PIN Checker by ID (`DTD_PINChecker`) | ✅ | ✅ |
|
|
199
|
+
| Know KRA Tax Service Office/Station (`SUC-iTax-USSD_Know_Your_Station`) | ✅ | ✅ |
|
|
200
|
+
| eTIMS Invoice Checker (`Invoice-Checker`) | ✅ | ✅ |
|
|
201
|
+
| Fetch Taxpayer Obligations (`TaxPayer_Tax_Obligations_Fetcher`) | ⏳ Planned | - |
|
|
202
|
+
| Withholding Tax PRN Generation (Income Tax, Rental, VAT) | ⏳ Planned | - |
|
|
203
|
+
| Tax Compliance Certificate (TCC) Validation & Application | ⏳ Planned | - |
|
|
204
|
+
| Automated NIL Return Filing (`iTax_NIL_Return`) | ⏳ Planned | - |
|
|
205
|
+
| Income Tax & VAT Exemption Checker | ⏳ Planned | - |
|
|
206
|
+
| Turnover Tax (TOT) Return Filing | ⏳ Planned | - |
|
|
207
|
+
| Customs Declaration Status Checker & Tax Calculator | ⏳ Planned | - |
|
|
208
|
+
| Import Certificate Checker (By Number / PIN) | ⏳ Planned | - |
|
|
209
|
+
| Individual KRA PIN Registration Gateway | ⏳ Planned | - |
|
|
210
|
+
| eTIMS OSCU Integrator Automated Testing Suite | ⏳ Planned | - |
|
|
211
|
+
| Excise License Checker (By Pin / Certificate Number) | ⏳ Planned | - |
|
|
212
|
+
|
|
213
|
+
</div>
|
|
214
|
+
|
|
215
|
+
# The Genesis & Contributing
|
|
216
|
+
|
|
217
|
+
Let’s clear the air right away: I am not affiliated, associated, authorized, or in any way officially connected with the Kenya Revenue Authority (KRA). This project was born out of pure, unadulterated developer frustration. While building a commercial product that required KRA portal integrations, I looked around the ecosystem for a robust, production-grade Python package that could handle the gateway safely and seamlessly. I found absolutely nothing.
|
|
218
|
+
|
|
219
|
+
Faced with a wall of fragmented PDFs and custom network scripts, I decided to take a deep breath, skip a few nights of sleep, and jump straight down the rabbit hole to build the tool that should have already existed. `gava-connect-plus` is the result of that journey.
|
|
220
|
+
|
|
221
|
+
## Join the Journey
|
|
222
|
+
Because this is a community-driven initiative, your hands and minds are needed to help shape it. Whether you want to squash an edge-case bug, optimize the internal caching layers, or drag one of the *Planned transactional* modules into operational reality, contributions are deeply welcomed.
|
|
223
|
+
|
|
224
|
+
Feel free to open an issue, start a discussion thread, or send a pull request over the wall. Let's make integration painless for the next developer.
|
|
225
|
+
|
|
226
|
+
## Support the energy
|
|
227
|
+
If gava-connect-plus saves you days of digging through portal specifications, keeps your production builds green, or spares your team a minor existential crisis, consider giving back to the project:
|
|
228
|
+
- **⭐ Star the Repository**: It costs nothing and directly helps other Kenyan developers discover the project on GitHub.
|
|
229
|
+
- **🗣️ Spread the Word**: Share the project link in your local tech WhatsApp groups, Discord servers, or on X (Twitter). Let's build a stronger local open-source culture.
|
|
230
|
+
- **☕ Buy Me a Coffee**: If this library saved your business real billable hours, consider fueling the midnight oil for the next sprint of module developments. Your support keeps the zen flowing.
|
|
231
|
+
|
|
232
|
+
<p align="center">
|
|
233
|
+
<picture><source media="(prefers-color-scheme: dark)" srcset="https://shieldcn.dev/chart/github/stars/iammuuo/gava-connect-plus.svg?theme=green&font=geist&title=Github+stars+over+time&icon=refinedgithub" /><img alt="chart" src="https://shieldcn.dev/chart/github/stars/iammuuo/gava-connect-plus.svg?mode=light&theme=green&font=geist&title=Github+stars+over+time&icon=refinedgithub" /></picture>
|
|
234
|
+
</p>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "gava-connect-plus"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Async-first production-grade SDK for Kenya's GavaConnect Enterprise API platform."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
authors = [
|
|
7
|
+
{ name = "Erick", email = "hearteric57@gmail.com" }
|
|
8
|
+
]
|
|
9
|
+
requires-python = ">=3.11"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"blessed>=1.44.0",
|
|
12
|
+
"httpx>=0.28.1",
|
|
13
|
+
"pydantic>=2.13.4",
|
|
14
|
+
"pytest>=9.1.1",
|
|
15
|
+
"respx>=0.23.1",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["uv_build>=0.11.23,<0.12.0"]
|
|
20
|
+
build-backend = "uv_build"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
[tool.uv.build-backend]
|
|
24
|
+
module-name = "gavaconnect"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = [
|
|
28
|
+
"pytest>=9.1.1",
|
|
29
|
+
"pytest-asyncio>=1.4.0",
|
|
30
|
+
"pytest-cov>=7.1.0",
|
|
31
|
+
"respx>=0.23.1",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
gava-connect = "gavaconnect.cli:main"
|
|
36
|
+
gava-tui = "gavaconnect.tui:run_tui"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from .async_client import GavaConnect
|
|
2
|
+
from .sync_client import GavaConnectSync
|
|
3
|
+
|
|
4
|
+
from .exceptions import (
|
|
5
|
+
GavaConnectError,
|
|
6
|
+
AuthenticationError,
|
|
7
|
+
InvoiceNotFoundError,
|
|
8
|
+
TransientError,
|
|
9
|
+
ValidationError,
|
|
10
|
+
APIError,
|
|
11
|
+
RateLimitError,
|
|
12
|
+
InvalidPINError,
|
|
13
|
+
InvalidTaxpayerIDError,
|
|
14
|
+
InvalidStationPINError,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"GavaConnect",
|
|
19
|
+
"GavaConnectSync",
|
|
20
|
+
"GavaConnectError",
|
|
21
|
+
"AuthenticationError",
|
|
22
|
+
"InvoiceNotFoundError",
|
|
23
|
+
"TransientError",
|
|
24
|
+
"ValidationError",
|
|
25
|
+
"RateLimitError",
|
|
26
|
+
"APIError",
|
|
27
|
+
"InvalidTaxpayerIDError",
|
|
28
|
+
"InvalidPINError",
|
|
29
|
+
"InvalidStationPINError",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def hello() -> str:
|
|
34
|
+
return "Hello from gava-connect-plus!"
|