Monorepo design
This document outlines the core architecture principles of the Payofy codebase.
Five system boundaries
- Only
@payofy/kurvcommunicates with Kurv. Server modules interact with an abstract payment rail interface. If upstream gateway details change, changes stay confined to one package. - Only the ledger module writes money state. Payments, refunds, payouts, and residuals write journal postings to the ledger. Current balances are always computed from ledger rows.
- Contracts first. Developers define schemas in
@payofy/contractsbefore writing endpoints. Both the OpenAPI specification and SDK types derive directly from these schemas. - Packages never import apps. SDKs never import server code. Turborepo build graphs and ESLint enforce this separation in continuous integration.
- Every database query runs with tenant context. The server assigns the tenant context per transaction, and PostgreSQL row-level security isolates tenant records.
Project segregation
Each deployable component has its own dedicated folder:
apps/api: Runspayofy-apion Fastify.apps/worker: Runspayofy-workeron BullMQ.packages/core: Houses shared backend domain modules and services.examples/budofy-integration/backend: Demonstrates server-to-server integration.examples/budofy-integration/frontend: Demonstrates embedded client widgets.
Build order
Development follows an incremental path where each step produces a verifiable milestone:
- Skeleton: config, ui, contracts, db, CI, and dev infrastructure.
- Kurv adapter: kurv and testing packages with sandbox verification.
- Money core: tenants, merchants, customers, payments, webhooks-in, and ledger modules.
- SDKs: node, js, react, and budofy-integration example.
- Recurring billing: billing, dunning, webhooks-out, and notifications on the worker.
- Operations: payouts, reconciliation, residuals, reports, audit, and payofy-admin.
- Budofy pilot: run live traffic for an initial school.
- Phase 2: Tap to Pay mobile bridge, partner portal, and documentation site.