Integrating M-Pesa Daraja the Right Way
STK Push, callbacks, reconciliation, security, and the production gotchas nobody tells you about until your first failed transaction.
A strong M-Pesa integration needs callback validation, transaction status checks, duplicate protection, clear logs, and a reconciliation view for admins. The payment flow should assume network delays and failed callbacks from day one.
Treat Payments As A State Machine
M-Pesa Daraja integrations become fragile when a payment is treated as a single request and response. In production, a customer can start STK Push, delay entering the PIN, enter the wrong PIN, lose network, pay successfully while your callback arrives late, or retry because the interface did not update quickly enough.
The safer model is to treat every payment as a state machine. A transaction can be pending, successful, failed, cancelled, expired, reversed, or awaiting manual reconciliation. Your database should store those states clearly, and the user interface should not assume the payment has succeeded until the right confirmation has been received and verified.
Callbacks, Reconciliation, And Admin Visibility
The callback endpoint should validate incoming data, protect against duplicate updates, and write useful logs. It should not silently fail, because the finance team will eventually need to answer a direct question from a customer: did this payment reach us, and what order or invoice did it settle?
A reconciliation screen is not optional for serious business systems. Admins need to search by phone number, checkout request ID, receipt number, customer, invoice, date, and payment status. That visibility prevents support teams from depending on developers every time a transaction looks unusual.
Production Gotchas To Design Around
Timeouts and delayed callbacks are normal. The system should be able to query transaction status, retry safe operations, and prevent duplicate fulfillment. For example, a paid order should not create two deliveries just because a callback was resent or a user clicked the payment button twice.
Security also matters. Credentials should live in environment variables, callback URLs should use HTTPS, sensitive logs should avoid exposing secrets, and access to payment records should be restricted by role. Payment code is business-critical code, so it needs deliberate handling from the first implementation.
