Write the amount once
Integer minor units internally. Humans see KES 2,450.00. Retail still sends major-unit KES. Never floats.
Home · How it works
Orchestration engine · §12–20
Oda asks six questions: who pays, who is paid, which source funds it, which destination receives it, which rail can execute, which route is fastest and available. Then we coordinate — we do not vault.
Integer minor units internally. Humans see KES 2,450.00. Retail still sends major-unit KES. Never floats.
M-PESA, linked bank, card. Confirm with the provider. Oda never collects their PIN, OTP, or CVV.
Created → source selected → auth → processing → succeeded, failed, or UNKNOWN. Simulator stays sandbox.
Merchant PAID. Webhook fires. Shadow ledger explains. It does not authorize the movement.
A network timeout is never automatically a failed payment. It becomes UNKNOWN, then RECONCILING. That is how you do not double-charge when M-PESA is slow.
M-PESA may say success while Oda still says processing and the bank has not settled. The transactions table is not the ledger. Debits equal credits. Entries are immutable. Fees and refunds are new rows, never edits.
Payer clearing receivable +2,450. Merchant settlement payable −2,450. We can argue with a partner.
Static QR is merchant identity. Dynamic QR carries amount, expiry, destination, checksum. No customer secrets in the payload.