Skip to content

Transaction Handling


Use Meaningful Idempotency Keys

Every transaction request requires a merchantReference that acts as an idempotency key.

Why: Idempotency keys protect you from accidental duplicate charges. If a network timeout causes uncertainty about whether a request succeeded, you can safely retry with the same key — the gateway will not create a second transaction.

Recommended approach:

  • Use a UUID or a deterministic identifier derived from your business entity (e.g., order-{orderId}-attempt-{n}).
  • Generate a new key for every new transaction intent. Never reuse a key from a failed transaction to create a different transaction.
  • Store the mapping between your idempotency key and the gateway's gatewayReference immediately upon receiving the response.
# Good — deterministic, traceable
merchantReference: "order-12345-pay-1"

# Bad — random with no business meaning, hard to debug
merchantReference: "a1b2c3d4"

Design for Asynchronous Flows

The gateway uses an asynchronous processing model: a 200 OK response means the transaction was accepted, not completed.

Why: Payment processing involves external providers (MNOs, banks) that respond asynchronously. The initial response only confirms the gateway received and validated your request. Final status arrives later via callback or polling.

Recommended approach:

  • After receiving 200 OK, store the gatewayReference and mark the transaction as pending in your system.
  • Do not fulfil orders, release goods, or update user balances until you receive a terminal status (success or failed) via callback.
  • Display a "payment processing" state to end users rather than a success or failure message.
graph LR
    A[POST request] --> B[200 OK - pending]
    B --> C{Wait for callback}
    C -->|Success| D[Fulfil order]
    C -->|Failed| E[Notify user]

Prefer Webhooks Over Polling

Why: Webhooks deliver status updates in near-real-time with no wasted requests. Polling adds latency, wastes bandwidth, and risks hitting rate limits.

Recommended approach:

  • Implement a robust callback endpoint as your primary mechanism for receiving transaction updates.
  • Use polling via the Status Check API only as a fallback for missed callbacks (e.g., during a reconciliation sweep).
  • If you must poll, use a reasonable interval (e.g., every 30-60 seconds) and stop once you receive a terminal status.