lomi.

Payment state machine

How transaction and provider statuses transition, expire, and map to balances.

Canonical behavior reference for payment lifecycle logic in lomi. For implementation details (database functions, jobs), see For contributors.

Money-in lifecycle

stateDiagram-v2
  [*] --> pending: live payment initiated
  [*] --> completed: test payment (sandbox)
  pending --> completed: provider confirms success
  pending --> failed: provider decline or hard failure
  pending --> expired: timeout or session expiry
  completed --> refunded: refund processed
  failed --> [*]
  expired --> [*]
  refunded --> [*]

In test, new payments often start as completed immediately. In live, they typically start pending until the customer approves mobile money or the card issuer confirms.

Transaction statuses

StatusMeaning
pendingPayment initiated; final outcome not yet confirmed (typical in live).
completedPayment succeeded; merchant balance and fees settle per platform rules.
failedPayment did not succeed (including some expiration paths).
expiredPending payment timed out.
refundedRefund processed against the original payment.

Provider-facing status mapping

Platform transaction_status is reflected in provider_payment_status on linked provider records:

Transaction statusProvider payment status
completedsucceeded
pendingprocessing
failedcancelled
expiredexpired
refundedrefunded

See Transactions for field names in API responses.

Status updates: idempotency and metadata

When status moves to completed:

  • Duplicate completion does not double-apply balance: metadata is merged, but balance is credited once.
  • Metadata is merged, not replaced, so webhook retries can append fields safely.

Pending expiration

Pending transactions can be expired by scheduled jobs:

  • By age (e.g. older than N hours).
  • Optionally by provider session expiry (e.g. Wave session when_expires).

Expired rows carry expiration_info in metadata describing why they closed.

What happens on completed

  1. Coupon usage may be incremented.
  2. Balances update via an idempotent path (metadata.balance_updated prevents double crediting).
  3. Subscriptions linked to the transaction may move from pending to active.

For balance timing, see Balance and settlement.

On this page