A retry timer should not ignore why the first attempt failed
“Retry in 30 minutes” is easy to implement and often the wrong policy. The next action should depend on the provider outcome and the customer's control over it.
| Failure reason | Retry now? | Safer next action |
|---|---|---|
| Customer cancelled (1032) | Usually no automatic retry | Return control to customer; offer retry when they choose |
| Timeout / unreachable (1037) | After final result, yes | Offer a fresh attempt; if result is merely missing, query first |
| Insufficient balance (1) | Not immediately | Wait/remind later or offer another enabled method |
| Wrong PIN (2001) | Not automatically | Let customer resolve PIN entry in M-PESA-controlled flow |
| Unknown / no final result | No | Query original request by CheckoutRequestID before any retry |
| Wallet/transaction limit | Usually same route will still fail | Offer another valid route or merchant-approved arrangement |
Customer cancelled: give control back to the customer
A cancellation is a final outcome for the attempt, but it usually signals intent rather than a provider fault. Let the customer choose when to try again. An automatic immediate reprompt can feel coercive and may repeat the same problem.
Timeout: allow a fresh attempt after the prior request is final
If the provider returns a final timeout/expiry, the previous prompt is no longer active. A fresh attempt can be offered.
If the callback is merely missing, query by CheckoutRequestID first. Missing evidence is not the same as a timeout code.
Insufficient balance: avoid rapid repeated prompts
The customer's wallet balance is the constraint. Repeating the same request seconds later is unlikely to improve the outcome.
A better policy is to return control to the customer, allow another enabled payment method, or send a later reminder according to the merchant's billing cycle. Do not infer an individual's payday or financial situation unless the business relationship legitimately provides that schedule.
Wrong PIN: do not automate repeated retries
The customer should correct the issue inside the M-PESA-controlled experience. The merchant should never ask for the PIN and should not repeatedly trigger new prompts while the customer is resolving it.
Unknown result: do not retry
UNKNOWN is the strongest retry block. Query the original request. Only create another payment attempt after the earlier one resolves to a state that makes retry safe.
Wallet limits or policy declines
If the provider indicates a transaction or balance limit problem, retrying the same amount through the same route may fail again. The customer may need another enabled method, a different payment arrangement allowed by the merchant, or support.
Retry policy should also consider merchant rules
- Maximum attempts per billing period.
- Grace-period stage.
- Whether another wallet is enabled.
- Whether the invoice amount has changed.
- Whether the invoice has been paid externally.
- Whether the provider is experiencing a wider incident.
The failure reason tells you what is technically safe; the merchant policy tells you what is commercially appropriate.
Sources
- Safaricom M-PESA Express Query API: https://developer.safaricom.co.ke/apis/MpesaExpressQuery
- Safaricom M-PESA Express API: https://developer.safaricom.co.ke/apis/MpesaExpressSimulate



