# Banking routes ## Purpose This module builds the OpenAPIHono sub-app exposed by the banking plugin. It takes a `BankingRoutesDeps` containing the `BankingService` and an optional typed `emitEvent` callback, and returns a Hono app that mounts `/banks` and `/accounts` route trees. Every route consumes the Zod schemas from `schemas/schemas.ts` so the generated OpenAPI document is correct, every error path maps a `BankingError` to a precise HTTP status, and every state-changing route fires a typed event through `emitEvent` when supplied. ## Features - 14 HTTP routes total: 7 on `/banks` (list, create, by-id, by-sort-code, search, update, soft-delete) and 7 on `/accounts` (list, create, by-id, by-number, by-user, get-owner, update, delete, status, transfer, add-funds, subtract-funds — list above combines the count). - Uniform error mapping: a single `errorBody(err)` helper maps every thrown `BankingError` to the canonical status; non-`BankingError` throws become `500`. - Typed events emitted on every state transition; the `emitEvent` callback's signature is constrained by the `BankingEventMap` union so there are no casts at emit sites. - Discriminated `RepositoryResponse` outcomes from `transferFunds` and `subtractFunds` are flattened to `(status, body)` pairs without exception-driven control flow. ## Usage: Basic ```ts import { createBankingRoutes, BankingService } from "@suaveplan/api-plugin-banking"; const service = new BankingService(adapter, config); const app = createBankingRoutes({ service }); host.route("/banking", app); ``` ## Usage: Advanced Wire the event emitter to a typed event bus so audit, projection, and notification plugins observe every transition: ```ts import { createBankingRoutes } from "@suaveplan/api-plugin-banking"; import type { BankingEventMap } from "@suaveplan/api-plugin-banking"; import { EventBus } from "@suaveplan/events"; const bus = new EventBus(); bus.on("banking:transfer:succeeded", (e) => audit.log(e)); const app = createBankingRoutes({ service, emitEvent: (type, payload) => bus.emit(type, payload), }); ``` The OpenAPI spec is generated by `@hono/zod-openapi` from each route's `request` / `responses` Zod schemas — call `OpenAPIHono.doc("/openapi.json", { ... })` on the parent app to expose it. ## API Reference ### `interface BankingRoutesDeps` - `service: BankingService` — the coordinator; routes resolve every repository call through it so policy and event emission stay in one place. - `emitEvent?: (type: K, payload) => void` — optional typed event emitter; if absent, every state transition silently proceeds without fan-out. ### `createBankingRoutes(deps): OpenAPIHono` Returns the sub-app. Mount it at any prefix; the route paths themselves are relative. ### Route catalogue `GET /banks` — list (filterable by `status`, `site_id`, paginated). `POST /banks` — create. Emits `banking:bank:created`. `GET /banks/{id}` — by id (404 on miss). `PATCH /banks/{id}` — update. Emits `banking:bank:updated`. `DELETE /banks/{id}` — soft-delete. Emits `banking:bank:soft-deleted`. `GET /banks/sort-code/{sortCode}` — by `XX-XX-XX` sort code. `GET /banks/search/{query}` — case-insensitive name search. `GET /accounts` — list (filterable by `user_id`, `bank_id`, `status`). `POST /accounts` — create. Emits `banking:account:created`. `GET /accounts/{id}` — by id. `PUT /accounts/{id}` — update. Emits `banking:account:updated`. `DELETE /accounts/{id}` — delete. Emits `banking:account:deleted`. `PATCH /accounts/{id}/status` — status FSM. Emits `banking:account:status-changed`. `POST /accounts/transfer` — fund transfer. Emits `banking:transfer:succeeded` or `banking:transfer:failed`. `POST /accounts/{id}/add-funds` — add funds. `POST /accounts/{id}/subtract-funds` — subtract funds. `GET /accounts/by-number/{accountNumber}` — by numeric account-number. `GET /accounts/user/{userId}` — list a user's accounts. `GET /accounts/{id}/owner` — owner user-id lookup. ## Implementation Notes Routes that may produce both 200/204 success and an error status code return `c.json(body, statusNumber)` rather than relying on the Hono default — this lets the `errorBody` helper map every `BankingError.code` to a precise status without re-running the narrowing logic in each route handler. `emitEvent` is invoked **after** the underlying repository write succeeds. Failure-mode events (`banking:transfer:failed`) are emitted on the negative branch so projections can record the attempt even when no balance changed. This matches the upstream package's contract that "every transfer attempt is observable". The route layer does NOT re-parse request bodies with `safeParse` — `@hono/zod-openapi` already validates via `c.req.valid("json")` before the handler runs, and any validation failure short-circuits with a 400 before the handler executes. Catching at the handler level only sees thrown `BankingError`s and unexpected database failures.