StreamPay is a real-time payment-streaming application built on top of the Stellar / Soroban network. This repository contains the backend REST API.
A payment stream locks a total amount from a sender and releases it linearly
to a recipient over a time window (startTime to endTime). The recipient can
withdraw whatever has streamed so far at any moment, and the sender can cancel
to reclaim the unstreamed remainder.
Note: the Stellar / Soroban layer is mocked in this project. No real on-chain transactions are submitted. The streaming math, balances and analytics are computed in-memory.
- Node.js + Express
- In-memory store (no database)
cors,dotenv,morgan,uuid
npm install
cp .env.example .env
npm startThe server listens on PORT (default 4000).
All endpoints are mounted under /api.
GET /api/health — health probe with runtime context.
GET /api/health/live — liveness probe; confirms the process is up.
GET /api/health/ready — readiness probe; confirms the app can serve traffic.
GET /api/version — service name, version and Node runtime, for deploy checks.
POST /api/streams — create a stream.
{
"sender": "GALICE...",
"recipient": "GBOB...",
"total": 1000,
"startTime": 1700000000,
"endTime": 1700003600
}startTime is optional (defaults to now). endTime is required and must be
after startTime; the window must be at least 60 seconds and at most 10 years.
total must be a positive number not exceeding 1e12.
GET /api/streams — list streams. Optional query filters: sender,
recipient, status (active | completed | cancelled). Paginated via
limit (default 50, max 200) and offset (default 0); the response includes
count (this page), total (all matches), limit and offset.
GET /api/streams/:id — fetch a single stream.
GET /api/streams/:id/schedule — the stream's vesting schedule: window,
duration, per-second release rate and projected milestones (0/25/50/75/100%).
GET /api/streams/:id/stats — live point-in-time figures: streamed, withdrawn,
withdrawable and locked amounts plus the percentage streamed/withdrawn.
POST /api/streams/:id/withdraw — release streamed-so-far to the recipient.
Optional body { "amount": 100 } for a partial withdrawal; omitting it
withdraws the full available balance.
POST /api/streams/:id/cancel — sender cancels and reclaims the remainder.
GET /api/balances?user=GBOB... — total withdrawable for a user across streams.
GET /api/withdrawable — protocol-wide withdrawable balances grouped by
recipient, sorted from largest to smallest.
GET /api/analytics — protocol-wide totals: total streamed, active streams,
total locked.
Errors use a consistent JSON envelope:
{
"error": {
"message": "Stream stream_x not found",
"status": 404,
"code": "NOT_FOUND"
}
}code is a stable, machine-readable identifier (BAD_REQUEST, FORBIDDEN,
NOT_FOUND, CONFLICT, UNPROCESSABLE_ENTITY, RATE_LIMITED,
SERVICE_UNAVAILABLE, INTERNAL_ERROR) that clients can switch on
independently of the human-readable message. Every response also carries an
X-Request-Id header (echoed from the request when supplied) for log
correlation, and /api responses are returned with Cache-Control: no-store
since the figures are time-sensitive.
All settings are read from environment variables (see .env.example):
PORT,NODE_ENV,LOG_LEVEL— server basics.REQUEST_TIMEOUT_MS— deadline after which a request is failed with503 SERVICE_UNAVAILABLE(default: 15000).RATE_LIMIT_WINDOW_MS/RATE_LIMIT_MAX— fixed-window rate limit applied to/apiper client IP (defaults: 60s / 120 requests). Responses includeX-RateLimit-*headers; exceeding the limit returns429 RATE_LIMITED.CORS_ORIGINS— comma-separated list of allowed origins, or*for any.STELLAR_*/NATIVE_ASSET— mock Stellar / Soroban settings.
Unit tests use the built-in Node test runner (no extra dependencies):
npm testMIT