
Building Production KRA eTIMS and Safaricom M-Pesa Integrations for Odoo 19
Building business software in East Africa means dealing with two hard operational facts. First, the Kenya Revenue Authority requires every business invoice to carry a digital fiscal signature and a verifiable QR code via eTIMS. Second, over 80 percent of commercial transactions settle through Safaricom M-Pesa. If your ERP cannot sign invoices in real time or match incoming Paybill payments automatically, your accounting team spends their days doing manual data entry. If your retail POS goes offline when the fiber cuts, you cannot legally issue receipts. To solve these problems, we built and published three production-ready modules on the official Odoo App Store. They support Odoo 17.0, 18.0, and 19.0 across both Community and Enterprise editions. Here is the technical architecture behind...
Building business software in East Africa means dealing with two hard operational facts. First, the Kenya Revenue Authority requires every business invoice to carry a digital fiscal signature and a verifiable QR code via eTIMS. Second, over 80 percent of commercial transactions settle through Safaricom M-Pesa. If your ERP cannot sign invoices in real time or match incoming Paybill payments automatically, your accounting team spends their days doing manual data entry. If your retail POS goes offline when the fiber cuts, you cannot legally issue receipts. To solve these problems, we built and published three production-ready modules on the official Odoo App Store. They support Odoo 17.0, 18.0, and 19.0 across both Community and Enterprise editions. Here is the technical architecture behind how we built them, how we handle network failures, and what we learned along the way. The Three Integrations Module Purpose Edition & Versions JengaStack eTIMS Real-time KRA OSCU invoice signing and fiscal QR codes Community & Enterprise (17.0, 18.0, 19.0) JengaStack M-Pesa Daraja STK Push and C2B Paybill/Till ledger auto-reconciliation Community & Enterprise (17.0, 18.0, 19.0) JengaStack eTIMS VSCU Offline-first virtual control unit and batched compliance sync Community & Enterprise (17.0, 18.0, 19.0) 1. Real-Time Fiscal Signing Without ERP Worker Blocking The standard KRA eTIMS Online Sales Control Unit (OSCU) flow requires sending invoice line items, tax classification codes, and buyer PINs to KRA over HTTPS. KRA returns control unit internal data (CU Information), an invoice sequence number, and a verification URL encoded as a QR code. The immediate trap many developers fall into is making a synchronous HTTP call directly inside Odoo's invoice confirmation method: Under high traffic or during peak tax filing deadlines, KRA endpoints often take several seconds to respond. Tying up Odoo WSGI workers with blocking external calls quickly exhausts the server worker pool. The Asynchronous Buffer and Retry Engine In , we separate invoice confirmation from fiscal transmission. When an invoice posts: The module assigns an internal sequence and creates a pending fiscal record. If online mode is active, it attempts an immediate short-timeout call (maximum 4 seconds). If the call succeeds, the invoice receives its KRA control code and QR code right away. If the call fails or times out, the module marks the record as without rolling back the invoice. A dedicated scheduled cron worker sweeps pending records using exponential backoff with jitter. The resulting QR code and Control Unit ID are rendered on both PDF QWeb reports and OWL POS thermal receipts, satisfying statutory requirements without stalling billing operations. 2. Safaricom M-Pesa Dual-Mode Architecture and Auto-Reconciliation Most M-Pesa plugins for Odoo treat payments as an afterthought, forcing staff to manually read SMS strings and type confirmation codes into Odoo. We designed with a dual-mode engine: Odoo Payment Provider connects directly to standard customer invoice portals and eCommerce checkouts. Direct POS and Backoffice Engine allows cashiers to trigger an STK Push with a single click, or allows the system to accept unsolicited C2B customer payments sent to a Paybill or Till. The Webhook Reliability Gap Safaricom sends validation and confirmation callbacks via HTTPS. However, network hops between Safaricom and cloud servers can occasionally drop packets. Relying solely on incoming callbacks leads to unconfirmed payments. We solve this with a three-layer verification loop: Webhook Listener. Accepts incoming JSON payloads from Safaricom, stores the raw transaction in an immutable audit ledger, and acknowledges receipt within 200 milliseconds. Active Status Query Fallback. If an STK Push initiates and no confirmation arrives within 45 seconds, a background job queries Safaricom's transaction status API directly using the . Idempotency Guard. Before registering any payment, the trans