Files
CBS-Mobile-App/src/services/api/endpoints.ts
T
Abin ToppoandClaude Opus 5.5 f960a119e8 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>
2026-10-14 18:18:40 +05:30

259 lines
9.5 KiB
TypeScript

export const endpoints = {
/** Unauthenticated by necessity — between them these produce a session. */
registration: {
start: '/registration',
verifyOtp: '/registration/verify-otp',
mpin: '/registration/mpin',
},
crypto: {
publicKey: '/crypto/public-key',
},
/**
* Forgot MPIN. Unauthenticated, like registration — the customer has no MPIN
* to open a session with. `initiate` checks the customer with the CBS and
* sends an OTP; `verifyOtp` swaps the OTP for a one-time reset token;
* `confirm` spends that token on the new, encrypted MPIN and revokes every
* open session. There is no resend endpoint: a fresh `initiate` is the resend.
*/
mpinReset: {
initiate: '/mpin-reset/initiate',
verifyOtp: '/mpin-reset/verify-otp',
confirm: '/mpin-reset/confirm',
},
/**
* Server-driven feature switches. Deliberately callable without a session —
* the sign-in screen's own controls are gated, so this has to resolve before
* anyone has signed in.
*/
features: {
config: '/features/config',
},
auth: {
login: '/auth/login',
me: '/auth/me',
logout: '/auth/logout',
},
/**
* Creating the Transaction PIN that authorises fund transfers. Signed-in
* only: org, customer, mobile, device and profile all come from the session,
* so none of them go in a body. `createInitiate` re-checks the current MPIN
* and sends an OTP; `createVerifyOtp` swaps the OTP for a one-time create
* token; `createConfirm` spends that token on the new, encrypted TPIN. There
* is no resend endpoint: a fresh `createInitiate` — MPIN and all — is the
* resend.
*
* Resetting it follows the same three steps under `/tpin/reset`, with two
* differences: `resetInitiate` takes an empty body — the session alone
* identifies the customer, and the bank re-verifies them before the OTP goes
* out — and it needs a TPIN to already be active. The same session must carry
* all three calls; the server refuses a flow that changes user or device.
*/
tpin: {
createInitiate: '/tpin/create/initiate',
createVerifyOtp: '/tpin/create/verify-otp',
createConfirm: '/tpin/create/confirm',
resetInitiate: '/tpin/reset/initiate',
resetVerifyOtp: '/tpin/reset/verify-otp',
resetConfirm: '/tpin/reset/confirm',
},
accounts: {
/**
* Every account on the signed-in profile. No parameters — the profile comes
* entirely from the session. This is the only endpoint that returns a full
* account number rather than a masked one.
*/
list: '/accounts',
mmid: '/accounts/mmid',
/**
* One transactable account's current balance, by `accountNumber` query
* parameter. Read live from the bank on every call — nothing is cached
* here, and nothing about a balance may be.
*
* SAVINGS and CURRENT only. A deposit's balance is a term-deposit concept
* and lives on `depositDetail` below, which is why the bank keeps the two
* apart rather than answering both from one endpoint.
*/
balance: '/accounts/balance',
/*
* There is no `/accounts/statement` any more: the paged JSON ledger was
* removed from the backend (Statement guide, 14 Oct 2026).
*/
/**
* 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 latest ten entries, newest first, as the JSON envelope.
* `accountNumber` only; no date filter is accepted.
*/
miniStatement: '/accounts/mini-statement',
/**
* One deposit's terms, by `accountNumber` query parameter. Takes the full
* number from `list` above — the masked form every other endpoint returns
* will not resolve.
*/
depositDetail: '/accounts/deposits/detail',
},
beneficiaries: {
list: '/beneficiaries',
save: '/beneficiaries',
validate: '/beneficiaries/validate',
},
/**
* IMPS transfers, in two steps. `initiate` runs the name inquiry and sends an
* OTP but moves no money; `verifyOtp` consumes the OTP and sends ReqPay. The
* server never sends ReqPay before a verified OTP, and never retries it.
*/
fundTransfers: {
initiate: '/fund-transfers/initiate',
verifyOtp: (transferId: string) => `/fund-transfers/${transferId}/verify-otp`,
},
/**
* The customer's own IMPS transfers, as the server recorded them — the status
* authority for how each one ended. The list is paginated and filterable by
* status, mode and an inclusive `fromDate`/`toDate`; the detail takes a
* `transferId` from the list. Payee identifiers arrive already masked.
*/
fundTransferHistory: {
list: '/fund-transfer-history',
detail: (transferId: string) => `/fund-transfer-history/${transferId}`,
},
dashboard: {
summary: '/dashboard/summary',
},
transfer: {
beneficiaries: '/transfers/beneficiaries',
submit: '/transfers',
},
payments: {
upi: '/payments/upi',
},
deposits: {
products: '/deposits/products',
fixed: {
list: '/fd/list',
create: '/fd/create',
detail: (id: string) => `/fd/${id}`,
close: '/fd/close',
},
recurring: {
list: '/rd/list',
create: '/rd/create',
detail: (id: string) => `/rd/${id}`,
close: '/rd/close',
},
},
services: {
requests: '/services/requests',
chat: '/services/chat',
},
/**
* 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.
*/
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`,
},
/**
* Cheque book issuance.
*
* `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
* one or the other and a shared constant reads like a copy-paste slip.
*/
chequeBook: {
charges: '/cheque-book/charges',
issue: '/cheque-book',
list: '/cheque-book',
},
loans: {
products: '/loans/products',
active: '/loans/active',
detail: (id: string) => `/loans/${id}`,
apply: '/loans/apply',
eligibility: '/loans/eligibility',
emiSchedule: (id: string) => `/loans/${id}/emi-schedule`,
payEmi: '/loans/pay-emi',
foreclosureQuote: (id: string) => `/loans/${id}/foreclosure`,
prepay: (id: string) => `/loans/${id}/prepay`,
applications: '/loans/applications',
documents: '/loans/documents',
uploadDocument: '/loans/documents/upload',
history: '/loans/history',
notifications: '/loans/notifications',
},
insurance: {
dashboard: '/insurance/dashboard',
plans: '/insurance/plans',
comparePlans: '/insurance/plans/compare',
calculator: '/insurance/calculator',
policies: '/insurance/policies',
policyDetail: (id: string) => `/insurance/policies/${id}`,
purchase: '/insurance/policies/purchase',
premiumDues: '/insurance/premiums/due',
payPremium: '/insurance/premiums/pay',
autoRenew: (policyId: string) => `/insurance/policies/${policyId}/auto-renew`,
claims: '/insurance/claims',
submitClaim: '/insurance/claims/submit',
documents: '/insurance/documents',
uploadDocument: '/insurance/documents/upload',
notifications: '/insurance/notifications',
markNotificationRead: (notificationId: string) =>
`/insurance/notifications/${notificationId}/read`,
otp: '/insurance/otp',
},
cards: {
overview: '/cards',
detail: '/cards/detail',
controls: '/cards/controls',
limits: '/cards/limits',
status: '/cards/status',
notifications: '/cards/notifications',
virtualCard: '/cards/virtual-card',
statements: '/cards/statements',
rewards: '/cards/rewards',
settings: '/cards/settings',
},
profile: {
/** The signed-in customer, resolved from the session — no parameters. */
me: '/profile',
},
branches: {
list: '/branches',
detail: (id: string) => `/branches/${id}`,
},
agent: {
dashboard: '/agent/dashboard',
customers: '/agent/customers',
customerDetail: (id: string) => `/agent/customers/${id}`,
collections: '/agent/collections',
collectionHistory: '/agent/collections/history',
commission: '/agent/commission',
loans: '/agent/loans',
sms: '/agent/sms',
routes: '/agent/routes',
},
};