Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

03 — User Journey (Payments)

End-to-end payment journeys. All flows assume Bearer JWT; every step uses the shared envelope contract (00-shared/07_API_Conventions.md).


1. Record a counter payment (Accountant / Receptionist)

sequenceDiagram
  participant U as Accountant
  participant A as App
  participant API as /payments
  U->>A: Open Payments > New payment
  A->>API: POST /payments {amount, gateway:cash, invoiceId, payerName, payerEmail}
  API-->>A: 201 {payment, receipt}
  A-->>U: Success screen: payment detail + receipt card (print/share)
  Note over A,U: invoice paidAmount/status updated server-side (PAID|PARTIAL)
  • Entry: Fees → invoice detail → "Record payment"; or Payments FAB → New payment.
  • Intent: credit an invoice instantly.
  • Decision points: gateway (cash/cheque/bank_transfer), prefill amount from invoice, attach payer identity.
  • System response: synchronous; skeleton → success view. Receipt number shown.
  • Failures: 400 validation (amount Min 0), 409 duplicate reference, network → retry prompt. Never optimistic — payment must be confirmed by the server.
  • Exit: "Done" → back to invoice (refreshed) or payments list.
  • Abandonment: form keeps entered values on back; not submitted until confirm.
  • Offline: blocked — must be online; guidance toast.
  • Multi-device: invoice status diverges if another clerk is mid-payment; pull-to-refresh.

2. Refund a payment (Accountant)

sequenceDiagram
  U->>A: Payments list > tap completed payment > Refund
  A->>A: Confirm sheet (reason required, shows refundable amount)
  A->>API: POST /payments/refund {paymentId, amount?}
  alt full refund
    API-->>A: status=refunded
  else partial
    API-->>A: status=partially_refunded
  end
  A-->>U: Success snackbar + updated payment
  • **Decision: full vs partial (amount optional = full).
  • Failures:
    • 409 "Only completed payments can be refunded." (status banner)
    • 409 "Refund amount exceeds payment amount." (inline amount error)
    • 404 payment missing.
  • Recovery: retry with corrected amount.

3. Reconcile a gateway outcome

flowchart LR
  G[Gateway async event] --> P[Payment PENDING]
  P --> R[Accountant opens reconcile screen]
  R --> S[PATCH /payments/:ref/reconcile {status}]
  S -->|success| C[completed]
  S -->|failed| F[failed]
  S -->|other / empty| PEND[pending]
  • Entry: payments list row with "pending/processing" badge → Reconcile; or gateway callback page.
  • Exit: back to list with updated status.
  • Permission denial: 403 → reconcile action hidden (client-gated per payments.reconcile).

4. Search an audit trail (Org Admin)

  • Payments list (paged GET /payments?page&limit, sorted createdAt desc) → filter by invoice / status (client-side; no server filters) → detail → view receipt (GET /payments/receipts/:id).
  • Deep link (forward-looking): studylyon://payments/:id.

Common journey attributes

ConcernBehaviour
LoadingAppSkeleton lists; button spinner on submit
Session expirysilent refresh; fail → re-login, payment preserved as draft
Timeout15 s; 429 → countdown + disable retry
Conflict resolutionrefresh list; server state is source of truth
Network lossoffline banner; no optimistic writes
Push entry(forward-looking) PaymentProcessed notification → deep link
Email entry(planned) receipt email via PaymentCompleted→emails routing (dead in map today)
Abandonmentforms discarded with confirm prompt