Skip to main content

Transaction Lifecycle

Both STK Push and B2C payout transactions follow the same set of states, though they progress through them differently. Understanding the lifecycle helps you build reliable integrations that handle all outcomes correctly.

Transaction states

StatusDescription
PENDINGInitial state. Awaiting customer action (STK) or provider acceptance (B2C).
PROCESSINGProvider accepted the request. Final confirmation pending.
SUCCESSTransaction completed successfully. Funds moved.
FAILEDTransaction failed — provider declined, timeout, or customer cancelled.
REVERSEDFunds previously charged to B2C wallet were credited back after failure.
CANCELLEDCancelled before processing began.
Terminal states (no further updates): SUCCESS, FAILED, REVERSED, CANCELLED

STK Push lifecycle

POST /payments/stk


    PENDING ──── Customer receives phone prompt ──── Customer cancels
        │                                                │
    Customer enters PIN                              FAILED / CANCELLED


   PROCESSING ──── Provider processing ──── Provider timeout / error
        │                                           │
    M-Pesa confirms                              FAILED


    SUCCESS
Typical completion time: 30–90 seconds after the customer enters their PIN. What happens on failure:
  • The service fee deducted when you initiated the STK Push is not refunded.
  • Your application should handle FAILED and CANCELLED states as separate cases.
  • The customer is never charged on failure.

B2C payout lifecycle

POST /b2c/payouts


    PENDING ──── Queued for processing


   PROCESSING ──── Provider sends to M-Pesa ──── Provider error / timeout
        │                                                │
    M-Pesa confirms                                  FAILED
        │                                                │
        ▼                                           REVERSED
    SUCCESS                                   (B2C wallet credited back)
Typical completion time: 30 seconds to 5 minutes. What happens on failure:
  • The B2C wallet balance debited on initiation is automatically reversed.
  • The service fee is not refunded.
  • Your webhook receives the FAILED state with a result_code from M-Pesa.

Receiving state updates

Supply a callbackUrl on every payment request. PalPluss delivers POST requests to that URL whenever the transaction reaches a terminal state. See the Webhooks guide for payload structure and retry policy.

Polling

If you cannot use webhooks, poll GET /transactions/{id} at intervals:
curl https://api.palpluss.com/v1/transactions/{transactionId} \
  -u "$PALPLUSS_API_KEY:"
Poll at reasonable intervals — every 5–10 seconds is sufficient. Aggressive polling consumes your rate limit (60 requests/minute) and delays results for other requests.
Recommended polling strategy:
  1. Poll at 5 seconds after initiation.
  2. If still PENDING or PROCESSING, poll every 10 seconds.
  3. Stop polling when a terminal state is reached (SUCCESS, FAILED, REVERSED, CANCELLED).
  4. Treat transactions still in PENDING after 5 minutes as likely failed — re-confirm with support if needed.

Idempotency

STK Push requests do not support client-supplied idempotency keys. However:
  • Each POST /payments/stk creates a distinct transaction and a distinct provider checkout.
  • If the same customer confirms multiple prompts (e.g. from retries), multiple SUCCESS transactions will result.
  • Prevent duplicate charges in your application before calling the API again.
Service wallet top-ups (POST /wallets/service/topups) support the Idempotency-Key header to safely retry without creating duplicate top-ups.