
为Odoo建立生产KRA eTIMS和Safaricom M-Pesa 集成系统
Building Production KRA eTIMS and Safaricom M-Pesa Integrations for Odoo 19
在东非建立商业软件意味着处理两个确凿的操作事实. 第一,肯尼亚税务局要求每张商业发票都通过eTIMS携带数字财政签名和可核查的QR码. 第二,超过80%的商业交易通过Safaricom M-Pesa结算。 如果您的ERP无法实时签名发票或自动匹配收到的Paybill付款,则您的会计团队会花几天时间进行人工数据输入. 如果你的零售POS断了纤维就下线了 你不能合法发行收据 为了解决这些问题,我们在Odoo App Store的官方网站上建造并发布了三个制作准备模块. 他们支持Odoo 17.0, 18.0和 19.0 两种版本的社区和企业。 这是背后的技术架构...
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 transaction code (such as ) is checked against an indexed unique database constraint. Duplicate callbacks never produce duplicate journal entries. Compliance With Data Protection Regulations Under the Kenya Data Protection Act 2019 (ODPC), customer phone numbers are personally identifiable information. Exposing plain customer MSISDNs in application logs or shared user screens creates serious regulatory risk. Our module masks customer phone numbers in all logging and reporting layers: When payments settle, the module finds the corresponding , creates the entry, and runs Odoo's automated reconciliation engine against the outstanding receivable balance. 3. Dealing With Spotty Internet and The VSCU Offline Bridge Retail branches and distribution depots outside major urban centers frequently suffer from unstable internet. When connectivity drops, an online-only compliance setup stops all business operations because invoices cannot be legally cleared. To solve this, we built . The Virtual Sales Control Unit operates on an offline-first model: Local Sequential Monotonic Counter. Every sale generated while offline receives a strictly incrementing, tamper-evident local sequence number. Local Cryptographic Buffer. Invoices are signed using the local business security parameters and queued into a persistent on-disk buffer. Batch Transmission on Reconnection. When internet connectivity returns, the module flushes invoices in managed chunks of 20 to 50 records. Serial Reconciliation. Once KRA acknowledges the batch, the module updates the master fiscal register and stores the return tokens. This architecture ensures retail counters can continue ringing up customers without violating tax regulations or slowing down queues during internet dropouts. 4. Multi-Version Odoo Craft for 17, 18, and 19 Odoo releases a new major version every year, introducing changes to OWL components, JavaScript framework bindings, and ORM interfaces. Between Odoo 17 and Odoo 19, significant updates were made to the Point of Sale UI and payment provider classes. To support all three versions reliably: We maintain dedicated release branches (, , ) with exact branch names matching the target series. We standardized our testing pipeline to run 378 unit and integration tests across versions. We followed strict UI craft guidelines with clean Tailwind-inspired layout cards, clear badge states, and responsive forms designed for daily warehouse and accounting operations. Summary and Availability All three modules are available now on the official Odoo App Store: JengaStack eTIMS (KRA Compliance Bridge): https://apps.odoo.com/apps/modules/19.0/jengastack_etims JengaStack M-Pesa (Daraja Payment Provider & POS): https://apps.odoo.com/apps/modules/19.0/jengastack_mpesa JengaStack eTIMS VSCU (Offline Compliance Bridge): https://apps.odoo.com/apps/modules/19.0/jengastack_vscu For deployment guides, sandbox test environments, or managed cloud hosting for your Odoo instances in East Africa, check our documentation at jengastack.app.