State Machine

Every RohoPay transaction follows this lifecycle:
                    ┌─────────┐
     API call →     │ PENDING │
                    └────┬────┘

            ┌────────────┼────────────┐
            ▼                         ▼
     ┌──────────────┐        ┌──────────────┐
     │  SUCCESSFUL  │        │    FAILED    │
     └──────────────┘        └──────────────┘
Terminal states (successful, failed) are permanent — a transaction never moves back to pending once it resolves.

Status Definitions

StatusDescriptionMobile MoneyCard
pendingTransaction created; awaiting user actionUSSD prompt sent3DS not yet completed
successfulPayment confirmed by provider webhookUser approved USSD3DS passed, card charged
failedPayment definitively rejected or timed outUser rejected USSD, expiredCard declined, 3DS failed

Transaction Type States

POST /api/v1/collect
     → status: "pending"     ← returned immediately

     User receives USSD prompt on phone

     ┌────┴────┐
     │  User   │
     │  Action │
     └────┬────┘
     Approve │    Reject / Timeout
             │         │
        "successful"  "failed"
Typical resolution time: 10–90 seconds.

Best Practices

Only mark an order as paid after receiving a successful status via webhook or confirmed by polling. Never fulfill on the initial pending response.
Show the user a clear error and provide a retry option. Avoid retry loops — generate a new idempotency key for each retry attempt.
After 2 minutes of polling with no resolution, treat the transaction as “in progress” and inform the user. The status will eventually resolve — your webhook will fire when it does.
Periodically cross-check your local order states against the RohoPay transaction list. The dashboard’s Reconciliation feature can help identify mismatches.

Order Status vs. Transaction Status

RohoPay distinguishes between:
  • Transaction status (pending / successful / failed) — the payment layer
  • Order status (for Digital Products: pending / paid / delivered / failed) — your fulfillment layer
Map them like this:
TransactionYour Order
pendingawaiting_payment
successfulpaid → trigger fulfillment
failedpayment_failed