|
| 1 | +# stackpay-backend 🪨 |
| 2 | + |
| 3 | +> Indexer + REST/WebSocket API for **StackPay** — turns Soroban payment events into queryable requests, balances, and webhooks. |
| 4 | +
|
| 5 | +[](https://github.com/Stack-Rocks/stackpay-backend/actions) |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | +The **off-chain brain** of StackPay. The contract ([`stackpay-contracts`](https://github.com/Stack-Rocks/stackpay-contracts)) is the source of truth; this service keeps a *materialized view* so the dApp and integrators can query requests, statuses, and history without replaying ledgers. |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## Table of contents |
| 15 | +- [What it does](#what-it-does) |
| 16 | +- [Architecture](#architecture) |
| 17 | +- [Tech stack](#tech-stack) |
| 18 | +- [Getting started](#getting-started) |
| 19 | +- [Configuration](#configuration) |
| 20 | +- [API reference](#api-reference) |
| 21 | +- [WebSocket events](#websocket-events) |
| 22 | +- [Indexer internals](#indexer-internals) |
| 23 | +- [Docker](#docker) |
| 24 | +- [Testing](#testing) |
| 25 | +- [Deployment](#deployment) |
| 26 | +- [Contributing](#contributing) |
| 27 | +- [License](#license) |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +## What it does |
| 32 | +1. **Indexes** `request_created`, `request_paid`, `request_cancelled` events from Soroban RPC. |
| 33 | +2. **Materializes** request state into PostgreSQL. |
| 34 | +3. **Serves** a REST API for the dApp and partners. |
| 35 | +4. **Streams** live updates over WebSocket. |
| 36 | +5. **Notifies** via webhooks on payment. |
| 37 | + |
| 38 | +## Architecture |
| 39 | +``` |
| 40 | + Stellar RPC / Horizon |
| 41 | + | poll ledger |
| 42 | + v |
| 43 | + +------------+ events +--------------+ |
| 44 | + | Indexer | -------> | PostgreSQL | |
| 45 | + | (worker) | +------+-------+ |
| 46 | + +------------+ | |
| 47 | + +-----v------+ |
| 48 | + | REST API |<-- dApp / partners |
| 49 | + | (Express) | |
| 50 | + +-----+------+ |
| 51 | + | publish |
| 52 | + +-----v------+ |
| 53 | + | WebSocket | |
| 54 | + | (Socket.IO)| |
| 55 | + +------------+ |
| 56 | +``` |
| 57 | + |
| 58 | +## Tech stack |
| 59 | +- Node.js 20 + TypeScript, Express, Socket.IO |
| 60 | +- PostgreSQL 16 (Prisma), Redis 7 (BullMQ) |
| 61 | +- `@stellar/stellar-sdk` for RPC/ledger access |
| 62 | + |
| 63 | +## Getting started |
| 64 | +```bash |
| 65 | +git clone https://github.com/Stack-Rocks/stackpay-backend.git |
| 66 | +cd stackpay-backend |
| 67 | +cp .env.example .env |
| 68 | +npm install |
| 69 | +npx prisma migrate dev |
| 70 | +npm run dev |
| 71 | +``` |
| 72 | +API at `http://localhost:4000`. |
| 73 | + |
| 74 | +## Configuration |
| 75 | +See [`.env.example`](./.env.example): |
| 76 | + |
| 77 | +| Variable | Description | |
| 78 | +| --- | --- | |
| 79 | +| `PORT` | HTTP port (default `4000`). | |
| 80 | +| `DATABASE_URL` | PostgreSQL connection string. | |
| 81 | +| `REDIS_URL` | Redis connection string. | |
| 82 | +| `STELLAR_RPC_URL` | Soroban RPC endpoint. | |
| 83 | +| `STELLAR_NETWORK` | `testnet` \| `mainnet`. | |
| 84 | +| `CONTRACT_PAYMENT` | Deployed PaymentRequest contract address. | |
| 85 | +| `START_LEDGER` | Ledger to begin indexing from. | |
| 86 | + |
| 87 | +## API reference |
| 88 | +Base: `http://localhost:4000/api/v1` |
| 89 | + |
| 90 | +### Requests |
| 91 | +| Method | Path | Description | |
| 92 | +| --- | --- | --- | |
| 93 | +| `GET` | `/requests` | List requests (filter `payee`, `payer`, `status`, `asset`). | |
| 94 | +| `GET` | `/requests/:id` | Get a request + on-chain status. | |
| 95 | +| `GET` | `/requests/:id/history` | Event history. | |
| 96 | +| `GET` | `/accounts/:address/requests` | Requests involving an address. | |
| 97 | +| `GET` | `/accounts/:address/received` | Total received by an address. | |
| 98 | + |
| 99 | +### Webhooks |
| 100 | +| Method | Path | Description | |
| 101 | +| --- | --- | --- | |
| 102 | +| `POST` | `/webhooks` | Register a URL to notify on `request_paid`. | |
| 103 | + |
| 104 | +Example: |
| 105 | +```bash |
| 106 | +curl http://localhost:4000/api/v1/requests/7 |
| 107 | +``` |
| 108 | +```json |
| 109 | +{ "id": 7, "payee": "GABC...", "asset": "C...", "amount": "5000000", |
| 110 | + "memo": "Invoice #12", "status": "Paid" } |
| 111 | +``` |
| 112 | + |
| 113 | +## WebSocket events |
| 114 | +Connect to `ws://localhost:4000`. Subscribe to a request room: |
| 115 | +```ts |
| 116 | +socket.emit("subscribe", { requestId: 7 }); |
| 117 | +socket.on("request:paid", (p) => console.log(p)); |
| 118 | +``` |
| 119 | +Events: `request:created`, `request:paid`, `request:cancelled`. |
| 120 | + |
| 121 | +## Indexer internals |
| 122 | +- Polls `getEvents` in contiguous ledger windows; resumable via a stored `last_ledger` cursor. |
| 123 | +- Maps events to DB upserts keyed by `contract + request_id`. |
| 124 | +- Idempotent — safe to run a single worker replica. |
| 125 | + |
| 126 | +## Docker |
| 127 | +```bash |
| 128 | +docker compose up --build |
| 129 | +``` |
| 130 | + |
| 131 | +## Testing |
| 132 | +```bash |
| 133 | +npm run test # vitest (PG via testcontainers) |
| 134 | +npm run test:e2e # against local soroban sandbox |
| 135 | +``` |
| 136 | + |
| 137 | +## Deployment |
| 138 | +Stateless API behind any Node host; one long-running indexer worker; managed Postgres + Redis. |
| 139 | + |
| 140 | +## Contributing |
| 141 | +Part of the **Stellar Wave Program** on Drips. Look for `Stellar Wave` / `Good first issue`. Run `npm run lint && npm run test` before a PR. |
| 142 | + |
| 143 | +## License |
| 144 | +[MIT](./LICENSE). |
0 commit comments