Add Positive Pay, statement guide v1.0, onboarding, and switch every feature on

- Positive Pay on the 12 Oct /pps contract (account number, not account ID):
  OTP enrollment, cheque submit, saved cheques, detail and stop payment.
  Full flow passed on the phone against mobile-banking-backend.lincpay.
- Statement & Mini Statement guide v1.0 (14 Oct): mini statement JSON on
  Home and the Statement screen; dated PDF export with the password rule.
- Onboarding slides and splash screen, Inter font, shared Icon and
  TypewriterText components, nearby branches via OpenStreetMap.
- Every feature switched on; the 14 Oct switch set is kept in
  featureConfig.stage-2026-10-14.ts. Loans run on demo data until the
  backend has /loans routes.
- npm run android wraps run-android for shells spawned from VS Code.
- OpenProject work packages updated to 14-10-2026 status, with screenshots
  of Positive Pay, statement, loans and cards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Abin Toppo
2026-10-14 18:18:40 +05:30
co-authored by Claude Opus 5.5
parent 5fbd13ba12
commit f960a119e8
510 changed files with 20268 additions and 6483 deletions
+34 -42
View File
@@ -74,22 +74,20 @@ export const endpoints = {
* apart rather than answering both from one endpoint.
*/
balance: '/accounts/balance',
/**
* A page of ledger entries for one account. Takes the full account number
* from `list` above, and an optional `fromDate`/`toDate` pair that the
* bank rejects unless both are given or neither is.
/*
* There is no `/accounts/statement` any more: the paged JSON ledger was
* removed from the backend (Statement guide, 14 Oct 2026).
*/
statement: '/accounts/statement',
/**
* The same range `statement` pages through, as one PDF. Takes the identical
* `accountNumber` and optional `fromDate`/`toDate` pair and no
* `page`/`size` — the whole range becomes one document. Answers
* `application/pdf`, not the JSON envelope every other endpoint returns.
* Every entry in an inclusive date range, as one password-protected PDF.
* `accountNumber`, `fromDate` and `toDate` (`yyyy-MM-dd`) are all
* required. Answers `application/pdf` on success and the JSON envelope on
* any refusal — see `services/files/pdfDownload.ts`.
*/
statementExport: '/accounts/statement/export',
/**
* The last 10 entries as a short PDF, led by the current available balance.
* `accountNumber` only — no date range, and no envelope.
* The latest ten entries, newest first, as the JSON envelope.
* `accountNumber` only; no date filter is accepted.
*/
miniStatement: '/accounts/mini-statement',
/**
@@ -153,40 +151,33 @@ export const endpoints = {
chat: '/services/chat',
},
/**
* Declares a cheque before it is presented. Write-only: the bank is the
* system of record and keeps no client-side copy, so there is nothing to
* list or fetch back — `referenceId` in the response is the only handle.
* Positive Pay. An account is first enabled with an OTP (`initiate` →
* `verifyOtp`, with `resendOtp` reusing the same `requestId`), which never
* reaches the bank. Only then can cheques be submitted, and a submitted one
* stopped — those two do reach the bank, and the backend saves a record of
* each, which `cheques` (paged) and `cheque` read back.
*
* The reads return the backend's saved state and never ask the bank for a
* fresh status, so refreshing cannot settle an uncertain outcome. Ids in a
* path are encoded: `chequeId` is the backend's, but it is still data.
*
* The account is always its `accountNumber` — a query parameter on
* `enrollment`, a body field elsewhere. There is no account id in this API.
*/
positivePay: {
register: '/positive-pay',
/**
* Checks the bank does *not* already hold this cheque, before the customer
* fills in the full registration form. "Not found" is the happy path here
* and answers 200 with a null `data`; a cheque already on file is refused
* with `CHEQUE_ALREADY_REGISTERED`.
*/
verify: '/positive-pay/verify',
pps: {
enrollment: '/pps/enrollment/accounts',
initiate: '/pps/enrollment/initiate',
verifyOtp: '/pps/enrollment/verify-otp',
resendOtp: '/pps/enrollment/resend-otp',
cheques: '/pps/cheques',
cheque: (chequeId: string) => `/pps/cheques/${encodeURIComponent(chequeId)}`,
stopPayment: (chequeId: string) =>
`/pps/cheques/${encodeURIComponent(chequeId)}/stop-payment`,
},
/**
* Stopping a cheque already declared under Positive Pay.
* Cheque book issuance.
*
* `verify` resolves to the same core-banking lookup as
* `positivePay.verify` — what differs is which answer is good news. There,
* finding the cheque is a rejection; here it is the whole point, and the
* cheque's own detail comes back so the customer can confirm what they are
* about to stop. A cheque that never went through `positivePay.register`
* does not exist to either of these.
*/
cheque: {
verify: '/check/verify',
stopPayment: '/check/stop-payment',
},
/**
* Cheque book issuance — the one cheque journey the bank keeps a history
* for, unlike Positive Pay and stop payment above.
*
* `charges` quotes the fee before the customer commits to `issue`, the same
* preview-then-act convention as `positivePay.verify`. `issue` is
* `charges` quotes the fee before the customer commits to `issue`. `issue` is
* synchronous: the serial range comes back in its own response, so there is
* no pending state and nothing to poll. `issue` and `list` are the same
* path under different methods, named apart because the screens reach for
@@ -246,7 +237,8 @@ export const endpoints = {
settings: '/cards/settings',
},
profile: {
me: '/profile/me',
/** The signed-in customer, resolved from the session — no parameters. */
me: '/profile',
},
branches: {
list: '/branches',