Backend API server for the ACBU (African Currency Basket Unit) platform.
- Runtime: Node.js 20+ with TypeScript
- Framework: Express.js
- Database: Prisma Accelerate (managed PostgreSQL; no local PostgreSQL)
- Cache: MongoDB Atlas (managed; no local MongoDB)
- Message Queue: RabbitMQ
- Testing: Jest
- Logging: Winston
- Development: Nodemon
- Node.js 20 or higher
- Docker and Docker Compose
- pnpm 10+ (Required package manager)
pnpm installCopy the example environment file and configure it:
cp .env.example .envEdit .env and configure:
- Prisma Accelerate URL (
PRISMA_ACCELERATE_URL) as the runtime database connection;DATABASE_URLfor migrations if your setup uses it - MongoDB Atlas connection string (
MONGODB_URI), e.g.mongodb+srv://... - RabbitMQ URL (or use a managed queue)
- API keys for fintech partners (Flutterwave, etc.)
- JWT secrets
- Other service configurations
Two separate environment variables serve distinct purposes — using the wrong one for the wrong purpose causes silent failures or boot errors.
| Variable | Protocol | Used by | Must NOT be used for |
|---|---|---|---|
DATABASE_URL |
postgresql:// or postgres:// |
prisma migrate (schema migrations) |
Runtime app server; Prisma Accelerate |
PRISMA_ACCELERATE_URL |
prisma:// or prisma+postgres:// |
App server at runtime (connection pooling, caching) | Migrations (prisma migrate will fail with this URL) |
Rules:
DATABASE_URLmust always be a direct PostgreSQL connection string. The server asserts this at boot and will refuse to start if it detects aprisma://protocol here.PRISMA_ACCELERATE_URLis optional. When set, the app uses it for all runtime queries. When absent, the app falls back toDATABASE_URLdirectly (suitable for local dev).- Never run
pnpm prisma:migratepointing atPRISMA_ACCELERATE_URL. Always run migrations against the directDATABASE_URL. - The boot log prints which URL type is active:
Runtime connection: Prisma Accelerate (pooled)orRuntime connection: direct PostgreSQL.
The app uses Prisma Accelerate and MongoDB Atlas; it does not require local PostgreSQL or MongoDB. You need a RabbitMQ instance (e.g. from Docker or a managed provider).
To run only RabbitMQ via Docker:
docker-compose up -d rabbitmqRabbitMQ will be on port 5672 (Management UI: http://localhost:15672).
The same docker-compose.yml can start local PostgreSQL and MongoDB for migrations or local dev; use those services only if you need them.
Initialize Prisma and run migrations:
# Generate Prisma Client
pnpm prisma:generate
# Run database migrations
pnpm prisma:migrate
# (Optional) Seed database
pnpm prisma:seedpnpm devThe server will start on http://localhost:5000 (or the port specified in .env).
Nodemon will automatically restart the server when you make changes to the code.
pnpm dev- Start development server with hot reloadingpnpm build- Build TypeScript to JavaScriptpnpm start- Start production serverpnpm test- Run testspnpm lint- Run ESLintpnpm lint:fix- Fix ESLint errorspnpm format- Format code with Prettierpnpm format:check- Check code formattingpnpm prisma:generate- Generate Prisma Clientpnpm prisma:migrate- Run database migrationspnpm prisma:studio- Open Prisma Studiopnpm prisma:seed- Seed database with initial datapnpm changelog- Regenerate the full CHANGELOG.md from all git historypnpm changelog:update- Append the latest unreleased changes to CHANGELOG.md (after tagging a release)
The backend follows semantic versioning and expects all release artifacts to be tagged consistently.
- Versioning format:
vMAJOR.MINOR.PATCHand optional pre-release suffixes such asv1.2.0-rc.1 MAJOR: breaking API or protocol changesMINOR: backward-compatible features or new endpointsPATCH: bug fixes, hardening, and compatibility patches- Release changes should be documented in
CHANGELOG.mdand reflected in the Git tag workflow used by the release pipeline
For the maintainers' checklist and the recommended release flow, see RELEASE.md and docs/RELEASING.md.
backend/
├── src/
│ ├── config/ # Configuration files
│ ├── controllers/ # Route controllers
│ ├── services/ # Business logic services
│ ├── models/ # Data models
│ ├── routes/ # API routes
│ ├── middleware/ # Express middleware
│ ├── utils/ # Utility functions
│ └── types/ # TypeScript type definitions
├── tests/ # Test files
├── prisma/ # Prisma schema and migrations
├── scripts/ # Utility scripts
├── docker-compose.yml # Docker services configuration
└── package.json
For the proposed alignment of partner reconciliation, attestation, and audit requirements (W10-DOC-016), see Partner audit cadence proposal. The proposal includes source-document integration steps pending maintainer review.
Once the server is running, API documentation is available at:
- Swagger UI:
http://localhost:5000/api-docs(development only, disabled in production for security)
Segment routes (require API key with segment scope): /v1/p2p, /v1/sme, /v1/international, /v1/salary, /v1/enterprise, /v1/savings, /v1/lending, /v1/gateway, /v1/bills. For a full list of routes and smart contracts, see the repository docs in the docs/ folder.
This API uses different signature encodings depending on the protocol and transport layer. The encoding should be treated as part of the contract and documented consistently to avoid verification failures.
- Webhook HMAC signatures: hex strings, typically generated as
crypto.createHmac(...).digest("hex")and verified by comparingBuffer.from(signature, "hex") - JWT and URL-safe token payloads: base64url encoding, such as the standard JSON Web Token header/payload format
- Ed25519/Stellar keys: base32/checksum encoded public keys or raw secret-key formats, not plain hex unless the specific library or API explicitly requires it
When a verifier expects hex, the sender must send a lowercase or uppercase hex digest exactly as specified; when a verifier expects base64url, the value must be base64url-encoded and not hex. The same API should not mix encodings for the same signing scheme without explicit documentation.
View and edit database data using Prisma Studio:
pnpm prisma:studioCreate a new migration:
pnpm prisma:migrateRun all tests:
pnpm testRun tests with coverage:
pnpm test:coverageFull list: See ENV_VARS.md. No mock data; all values must be real or explicitly empty.
Required: DATABASE_URL (migrations / fallback), MONGODB_URI (MongoDB Atlas), RABBITMQ_URL, JWT_SECRET. Runtime DB: Prisma Accelerate via PRISMA_ACCELERATE_URL (see ENV_VARS.md).
Fintech: Flutterwave (FLUTTERWAVE_SECRET_KEY, etc.), Paystack (PAYSTACK_SECRET_KEY), MTN MoMo (MTN_MOMO_SUBSCRIPTION_KEY, MTN_MOMO_API_USER_ID, MTN_MOMO_API_KEY). Optional: FINTECH_CURRENCY_PROVIDERS.
Stellar: STELLAR_NETWORK, STELLAR_SECRET_KEY, and after deploy: CONTRACT_ORACLE, CONTRACT_RESERVE_TRACKER, CONTRACT_MINTING, CONTRACT_BURNING. Optional for segment features: CONTRACT_SAVINGS_VAULT, CONTRACT_LENDING_POOL, CONTRACT_ESCROW.
Stellar Fee Configuration: Control transaction fees under varying network congestion:
STELLAR_USE_DYNAMIC_FEES=true- Fetch live base fee from Horizon before each transaction (recommended for production)STELLAR_BASE_FEE_STROOPS=100- Fallback base fee when dynamic fetch unavailable (default: 100)STELLAR_SOROBAN_MIN_FEE_STROOPS=5000- Minimum total fee for Soroban transactions to prevent underpricing (default: 5000)STELLAR_SOROBAN_MAX_FEE_STROOPS=10000000- Maximum total fee for Soroban transactions to prevent overpaying (~50 XLM at base=100; default: 10M stroops)
With Prisma Accelerate and MongoDB Atlas, the app does not use local PostgreSQL or MongoDB. Only RabbitMQ is required from Docker (or use a managed RabbitMQ). The compose file also defines optional postgres and mongodb services for migrations or local development.
- RabbitMQ Management UI:
http://localhost:15672(username: acbu, password: acbu_password) - Optional local Postgres:
localhost:5432(if runningdocker-compose up -d postgres) - Optional local MongoDB:
localhost:27017(if runningdocker-compose up -d mongodb)
docker-compose downdocker-compose logs -fThe API provides three health check endpoints with different purposes:
- Path:
GET /api/v1/health - Status: Always returns
200 OK - Purpose: For load balancers to verify the process is alive
- Response:
{ status: "ok", timestamp, uptime, version } - Note: Does not probe dependencies; fast and reliable
- Path:
GET /api/v1/health/ready - Status: Returns
200if all dependencies up,503if any down - Purpose: For Kubernetes readinessProbe configurations
- Probes: PostgreSQL, MongoDB, RabbitMQ
- Startup Guard: Returns
503during application startup until all infrastructure connections and background jobs are fully initialized, preventing load balancers from routing traffic to partially-initialized instances - Recommendation: Use this endpoint in K8s deployment readinessProbe
- Path:
GET /api/v1/health/deep - Status: Returns
200if all dependencies up,503if any down - Purpose: Detailed dependency status for monitoring dashboards
- Response: Full report with status of each dependency
- Startup Guard: Returns
503during application startup until all infrastructure connections and background jobs are fully initialized
readinessProbe:
httpGet:
path: /api/v1/health/ready
port: 5000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
livenessProbe:
httpGet:
path: /api/v1/health
port: 5000
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3GitHub Actions CI pipeline runs on:
- Push to
main,dev, ordevelopbranches - Pull requests to
main,dev, ordevelop
The CI pipeline:
- Runs linter and formatter checks
- Runs all tests
- Builds the project
- Validates database migrations
- Blocks destructive Prisma migrations unless the pull request carries the
allow-destructive-migrationlabel
- Transfer service (b6b7036): Implemented
createTransferintransferService.tswith input validation, KYC checks, and blockchain transaction handling, along with full unit test coverage intests/transfer.test.ts - Float-interest revenue & segregated partner reserves (W10-S-012, #1141): Resolved revenue model contradiction where float interest was assumed on partner-held segregated reserves. Established conservative baseline zeroing uncontracted partner float ($0.00), defined the Partner Reserve & Interest Agreement (PRIA) framework for conditional entitlement, re-ran breakeven projections to $442M volume, and implemented programmatic governance in
FloatRevenueService.
For the ACBU platform development roadmap with committed timelines, owners, dependencies, and acceptance criteria, see docs/ROADMAP.md.
- Create a feature branch
- Make your changes
- Run tests and linter:
pnpm test && pnpm lint - Commit and push
- Create a pull request (prefer relative references like
#123instead of hardcodedgithub.com/<owner>/...links)
Apache License 2.0