05 — Screen Inventory (Payments)
- S1 — Payments list
- S2 — Payment detail
- S3 — New payment (form screen)
- S4 — Refund sheet (bottom sheet)
- S5 — Reconcile sheet
- S6 — Receipt view
- S7 — Receipts list
- S8 — Payment context on invoice detail (fees module)
Every screen in the payments module. States, permissions, a11y, analytics per screen.
Screen codes: S1 Payments list · S2 Payment detail · S3 New payment · S4 Refund sheet · S5 Reconcile sheet · S6 Receipt view · S7 Receipts list · S8 Payment on invoice (context).
S1 — Payments list
- Purpose: browse all tenant payments, newest first; drill into details; record new.
- Route:
/payments - Entry: Fees tab → Payments; deep link; after successful payment.
- Exit: payment detail, new payment, receipts list.
- Widgets:
AppSearchBar(client-side filter), statusFilterChips (client-side),AppCard/AppListTilerows (transactionReference, amount, payerName, gateway icon, statusAppBadge, date), FAB "Record payment", pagination footer. - Buttons: FAB (primary CTA), row menu (overflow).
- States: loading skeleton (8 rows) → empty → error (retry) → data; offline banner.
- Pagination:
page/limit(default 20) via server{data,total}; "Load more" button (no auto-scroll). - Permissions: visible with
payments.read(client-gated; server(planned)). - Keyboard: Enter submits filter;
/focuses search (desktop). - Responsive: single column phone; two-column grid tablet; table + filters desktop.
- A11y: rows announce "Payment TXN-… amount … status"; status never color-only (icon + label).
- Micro-interactions: row tap ripple; status chip pop on update.
- Analytics:
payments.list.view,payments.list.filter,payments.list.open_detail.
S2 — Payment detail
- Purpose: full payment record + linked invoice + receipt access + actions.
- Route:
/payments/:id - Entry: list tap, deep link, notification.
- Exit: receipt, refund/reconcile sheets, invoice detail.
- Sections: payment header (amount large, status badge, transaction reference), payer info card, invoice link card (status, paidAmount), gateway + timestamps card, refund history (refundedAmount vs amount), actions.
- Actions: Receipt (primary tonal), Refund (destructive, only COMPLETED), Reconcile (only PENDING/PROCESSING), Copy transaction reference.
- States: skeleton → data / 404 error (
RESOURCE_NOT_FOUNDempty state with copy "Payment not found"). - Permissions: view
payments.read; actions gatedpayments.refund/payments.reconcile(hide when absent). - A11y: money amounts
tabularFigures; actions ≥ 48 dp. - Analytics:
payments.detail.view,payments.detail.refund_start,payments.detail.reconcile.
S3 — New payment (form screen)
- Purpose: record a payment; always server-confirmed (never optimistic).
- Route:
/payments/new(or prefilled via invoice context) - Widgets: amount field (decimal, prefilled from invoice), currency (default USD, read-only until server supports multi-currency), gateway dropdown (enum incl. cash/cheque/bank_transfer), gatewayTransactionId (optional, gateway methods), invoice picker (optional, searchable), payer fields (name, email, id — optional), description, submit "Record payment" with loading spinner.
- States: pristine → validation errors → submitting (button spinner, fields locked) → success (payment + receipt card) → failure (error banner + retry).
- Keyboard: amount numeric-decimal; payer email email type.
- Permission:
payments.process. - Analytics:
payments.new.submit,payments.new.success,payments.new.failed.
S4 — Refund sheet (bottom sheet)
- Purpose: full/partial refund with reason.
- Route: modal from S2.
- Widgets: refundable amount display, amount field (default = full), reason (required), submit, destructive styling.
- Errors: 409 messages inline/banner; refund amount > payable blocked client-side (min 0, max = amount − refundedAmount).
- Analytics:
payments.refund.submit,payments.refund.success,payments.refund.failed.
S5 — Reconcile sheet
- Purpose: set payment status from gateway outcome.
- Widgets: status selector (success → completed / failed → failed / other → pending), optional gateway response text, submit.
- Permission:
payments.reconcile. - Analytics:
payments.reconcile.submit.
S6 — Receipt view
- Purpose: display receipt (data row) for printing/sharing.
- Route:
/payments/receipts/:receiptId - Sections: header (receipt number, issuedAt), payment reference, amount/fee/currency, payer, description, invoice ref.
- Actions: Share/Print
(planned)via reports; Copy receipt number. - State: 404 empty state if missing.
- Analytics:
payments.receipt.view,payments.receipt.share.
S7 — Receipts list
- Purpose: browse all receipts (paged, newest first).
- Route:
/payments/receipts - Mirrors S1 with receipt rows; tap → S6.
- Analytics:
payments.receipts.view.
S8 — Payment context on invoice detail (fees module)
- Invoice detail shows payments tab/row list (
GET /payments/invoice/:invoiceId), paidAmount progress, "Record payment" entry point. Details owned by fees docs.