PPayNow Docs
Menu — Session & State

API Reference

Session & State

PaymentSession is the canonical record. PaymentState enumerates every legal moment in a checkout.

PaymentSession

The single source of truth passed between every layer. Immutable; mutate via copyWith.

class PaymentSession {
              final String paymentId;
              final String invoiceId;
              final String merchantId;
              final int amountMinor;
              final String currency;
              final PaymentState state;
              final PaymentMethod? method;        // qr | account
              final String? qrPayload;            // data URL or qrCode string
              final String? statusMessage;
              final DateTime? expiresAt;
            
              // Customer / wallet metadata, populated as the flow progresses
              final String? customerName;
              final String? customerPhone;
              final String? walletAccountId;
              final String? institutionName;
            
              // Terminal-state fields, populated on success
              final String? transactionId;
              final String? referenceId;
              final String? dphReference;
              final String? receiverName;
              final String? receiverAccountNumber;
              final DateTime? completedAt;
            
              final String? remarks;
              final int version;
            
              bool get isTerminal =>
                  state == PaymentState.success ||
                  state == PaymentState.failed ||
                  state == PaymentState.expired;
            }
            

PaymentState

enum PaymentState {
              created,        // session exists, no flow chosen
              pending,        // initiate returned, not yet showing UI
              qrGenerated,    // QR is on screen, waiting for scan
              waitingPayment, // backend is processing (post-OTP for OnePay)
              otpRequired,    // OTP screen visible
              authorized,     // OTP accepted, settlement pending
              success,        // terminal — money moved
              failed,         // terminal — declined / rejected
              expired,        // terminal — session timed out
            }
            

Allowed transitions

PaymentStateMachine enforces this table. Self-transitions are always allowed.

From To
created pending, failed, expired
pending qrGenerated, waitingPayment, otpRequired, failed, expired
qrGenerated waitingPayment, otpRequired, success (fast-path), failed, expired
waitingPayment otpRequired, authorized, success, failed, expired
otpRequired authorized, success (fast-path), failed, expired
authorized success, failed
success — (terminal)
failed — (terminal)
expired — (terminal)

Two non-obvious "fast-path" edges exist for backends that collapse intermediate states:

  • qrGenerated → success — webhook fires before the gateway polls waitingPayment.
  • otpRequired → success — backend settles immediately after OTP.

Illegal transitions throw PaymentStateTransitionException.

PaymentMethod

enum PaymentMethod { qr, account }
            

account covers both OnePay bank accounts and PayNow wallets — the difference is the institutionCode on ValidateAccountRequest.

Currency helpers

The paynow_core/payment_models.dart library exposes:

  • int currencyFractionDigits(String currency) — handles 3-digit currencies (LYD/BHD/KWD/OMR/TND), 0-digit (JPY/KRW), and the 2-digit default.
  • String formatCurrencyAmount(int amountMinor, String currency) — returns LYD 25.900 etc., with thousand separators.

PaymentEvent

class PaymentEvent {
              final String paymentId;
              final PaymentSession? previousSession;
              final PaymentSession currentSession;
              final DateTime occurredAt;
            }
            

Emitted by PaymentEngine on every successful API call and every state transition. Subscribe via engine.events or sdk.sessionStream.