Finesse Capital
Digital wealth-management platform for Nigerian investors and advisors

Overview
A multi-role wealth-management platform where individuals and corporate accounts onboard with KYC, fund NGN/USD wallets, and invest across NGX equities and private-market products with assigned advisors, while an admin back-office handles compliance, pricing, and settlement.
My role
Full-stack developer. Designed and built the platform end to end: a 70+ model PostgreSQL/Prisma data model, 150+ API route handlers, four role-based areas (investor, advisor, corporate, admin) plus a public marketing/onboarding site, the wallet/ledger money layer, payments and broker integrations, Auth.js RBAC, and self-hosted deployment (Node via pm2 behind nginx).
The problem
Wealth management for Nigerian retail and corporate investors is largely manual and split across disconnected channels; funding, KYC, multi-asset investing, advisory, and compliance rarely live in one place. There was no single system where a client could fund an account, invest across equities and private-market products, and work with an advisor while the firm handled approvals, settlement, and a compliance audit trail.
The solution
A unified multi-role platform. Investors and corporate multi-signatory accounts onboard with KYC, fund NGN/USD wallets, and invest across NGX equities/ETFs and seven OTC product classes; advisors propose trades their clients consent to; and an admin back-office approves, prices, and settles under granular role-based permissions with full audit logging. Money movement runs through a per-currency wallet ledger keyed on idempotency references, and equities routing sits behind a provider abstraction so a deterministic mock can be swapped for a live NGX market-data feed without changing callers.
Key features
- NGN/USD wallet with an idempotent ledger, Paystack funding/payouts and manual USD flows, plus a maker-checker withdrawal approval workflow.
- Multi-asset investing: NGX equities & ETFs (order lifecycle, positions, watchlists) and seven OTC classes (bonds, money-market, private debt/equity/fund, real estate, OTC equity) with cashflow schedules and valuations.
- Advisor client-servicing: assignment, trade proposals → client consent → admin fulfilment, relationship notes, PDF statements, and advisor↔client messaging.
- Corporate accounts with multiple signatories and configurable multi-approval workflows.
- KYC/onboarding for individuals, advisors, and companies with document upload (Cloudinary) and risk/compliance categorisation.
- Admin back-office with granular RBAC (multiple admin role types), audit logging, and real-time in-app notifications over a self-hosted SSE hub.
Architecture & engineering
Next.js 16 (App Router): server components handle auth, permissions, and data access, with thin client feature components; Auth.js v5 with JWT sessions refreshed against the DB each request; Prisma 7 on PostgreSQL (Supabase), ~70 models, applied via bespoke idempotent SQL migrations. Money-path integrity is enforced with unique ledger references (idempotency keys) and atomic status-claim transitions before any funds move. Equities routing is decoupled behind a BrokerProvider interface (deterministic mock today; an InfoWARE EMSX adapter scaffolded for live NGX data). Notifications use a self-hosted SSE hub with the DB row as source of truth. Deployed self-hosted on a VPS, not serverless.
The hardest problem
The hardest problem was money-path correctness under concurrency: fund reservations and releases (withdrawals, equity order fills) had to be safe against double-submits and duplicate payment webhooks. The pattern used is an atomic status claim (a conditional updateMany on the current status inside the transaction) before any release, plus unique ledger references so retries dedupe. An adversarial pass over the codebase surfaced and fixed real concurrency bugs (e.g. a double-release on concurrent withdrawal rejections, and a fill settling against a just-cancelled order).
Key learnings
In a money-moving product, correctness under concurrency outweighs feature count; atomic status claims and idempotency keys are the load-bearing pattern. A provider abstraction pays off when a third-party integration is uncertain: the app ships on a deterministic mock and swaps in the real feed with no caller changes.
Want to see it in action?
Explore the live product, or reach out to talk through the build.