Skip to content

Blog · 2026-09-22 · 9 min read

Callbacks and reconciliation: when the phone says paid but your app disagrees

Why M-Pesa prompts and application state drift apart—and how to design webhooks, idempotency, and support playbooks that keep the books honest.

Support tickets that start with “I paid but nothing happened” are expensive. Usually the money moved; the application never applied the result. The fix is not louder SMS reminders—it is treating the provider callback as a contract and designing for duplicates, delays, and partial failures.

What a good callback handler does

Accept the payload over HTTPS, verify authenticity according to your provider, and update payment state in an idempotent way. If Safaricom or your gateway retries the same success, you should not double-fulfil an order.

Log enough to debug (request ids, amounts, masked MSISDNs) and never log secrets or full card-equivalent data. Kenya’s data protection expectations treat phone numbers as personal data—mask them in routine logs.

Reconciliation as a daily habit

At close of day, compare provider settlements or transaction exports with your internal ledger of payment intents. Gaps point to missed callbacks, wrong environment credentials, or bugs in status mapping.

For shop operations that also run a till, see how Tawala thinks about cash, M-Pesa, and deni in one place—the same discipline applies: one source of truth per payment attempt.

Questions

Should I poll if the callback is late?
Many stacks support a status query after STK Push. Use it as a backup, not a replacement for a correct callback URL.
Explore NetPayAll posts

Continue reading