C1
Verify every transaction, never trust the callback alone
A payment callback is just an HTTP request — anyone can send one. Record the callback, then confirm the transaction independently (query the transaction status API) before crediting a customer account or releasing goods. Only state transitions confirmed by verification should move money in your books.
The risk
A request arrives claiming success, the order is marked paid and goods are released — but the money was never actually collected.
The control
Record first, verify independently, then transition. A callback is a hint that verification is due, not permission to move money.
C2
Reconcile continuously, not monthly
Match every internal payment record against the provider's transaction references on a short cycle. Unmatched items — callbacks received but never recorded, records with no matching callback — surface the same day, while logs and staff memory are fresh.
The risk
Reconciliation deferred to a month-end spreadsheet, by which point the log lines and staff recollections needed to explain a gap are gone.
The control
Run the match on a short cycle and route every unmatched item to a named owner the same day it appears.
C3
Make processing idempotent
Networks retry, users double-tap, callbacks arrive twice. Design payment handlers so repeating the same request has the same effect as running it once: unique transaction references enforced at the database level, and duplicate deliveries acknowledged without re-applying their effects. This is the control that prevents double charges.
The risk
A handler that applies whatever effect each delivery describes, so a retried request processes the same payment a second time.
The control
Enforce uniqueness on the transaction reference in the database itself rather than in application code, and acknowledge duplicates without re-applying effects.
Without idempotency
Callback
Process paymentProcess payment
Double charge
With idempotency
Callback #1Callback #2
Transaction ID
Process
Already processed
No duplicate effect
C4
Keep secrets server-side with short-lived tokens
API credentials belong on the server, never in mobile apps or front-end bundles. Request short-lived access tokens, rotate credentials on a schedule, and scope every credential to the minimum it needs.
The risk
An integration key shipped inside a JavaScript bundle or a mobile app, where anyone who unzips the artefact holds your credential.
The control
Hold credentials on the server, request a short-lived token per operation, rotate on a schedule, and scope each credential to the minimum it needs.
C5
Put payments behind roles and audit trails
Refunds, voids and manual adjustments are the highest-risk actions in any commerce system. Restrict them to named roles and log who did what, when, and for how much — the same controls we ship in Simba Retail.
The risk
Every staff account can void a sale or issue a refund, and the only record of it is a line in a general application log.
The control
Map roles to capabilities explicitly — take a payment, issue a refund and change configuration are three different permissions — and write an audit entry for each privileged action.
C6
Handle timeouts and offline paths explicitly
In markets where connectivity drops, “unknown payment state” is a normal outcome, not an edge case. Queues with explicit pending states, safe retries and clear cashier-facing messaging keep sales moving without risking double collection.
The risk
A binary success-or-failure model, so a dropped connection leaves the sale in a state the system cannot represent — and staff resolve it by taking the money again.
The control
Model pending as a first-class state, retry safely under the same idempotency rules, and tell the cashier what is happening instead of asking them to guess.
Payment initiated → STK request
- Confirmed
- Receipt
- Unknown
- Verify
- Confirmed → receipt
- Unresolved → review
Scope of these recommendations
The control patterns above are Intelisav's recommended design for payment integrations. They are not statements of what a payment provider guarantees. For M-Pesa, the endpoints, request and response fields, callback delivery behaviour and any signature scheme are defined by Safaricom and can change over time.
Confirm current behaviour against the official Safaricom Daraja developer documentation before implementing, and treat verification and reconciliation as the controls that hold regardless of what the provider does or does not promise.
—
Secure payment architecture
Secure payment processing is not one control. It is a chain of controls: verification, idempotency and audit each guard a different failure, and reconciliation is what proves the chain held.
The chain
Payment
VerificationIs it really paid?
IdempotencyExactly once
AuditWho, what, when
Reconciliation
A state you can trust in your books