Lesson D1 · Payment design
The state machine
By the end of this track you can design a system that never loses money.
● Sandbox only3 steps + checkpoint
0/4 finished
Step 1
The idea
Every payment moves through a few states and never skips one.
Step 2
How it works
The states
- created: you have an order and are about to call Daraja.
- accepted: Daraja acknowledged the request.
- confirmed: a callback or query says
ResultCode 0with the right amount. - failed: a callback or query gave a non-zero result.
- unknown: you sent it and couldn't learn the outcome. Only a query moves it on.
The rules
- confirmed and failed are final. Nothing moves a payment out of them.
- Every change records what caused it: which callback or which query.
- Only a callback or query reaches confirmed. The first response never does.
Enforce it in one place
Put every state change through one function, so no code path can skip a rule.
Python
ALLOWED = { "created": {"accepted", "failed", "unknown"}, "accepted": {"confirmed", "failed", "unknown"}, "unknown": {"confirmed", "failed"},}def move(payment, new_state, cause): if new_state not in ALLOWED.get(payment.state, set()): return False # final, or not allowed: ignore payment.state = new_state payment.history.append((new_state, cause)) return TrueStep 3
Common mistakes
- Letting a late failed callback overwrite confirmed.
- Setting the status directly from many places in the code.
Go deeper
Which signal is allowed to move a payment to confirmed?
Answer in your own words first.
Show the answerHide the answer
Only a callback or status query with ResultCode 0. Never the first response.