Skip to content

FX-GENE/stargate-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

161 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stargate Backend — API & Reconciler

The engine behind Stargate: invoices in Postgres, payments confirmed from the Stellar ledger, webhooks that always arrive.

Stargate Protocol — The Stripe for Stellar. Founded and built by dreamgene · Founder & CEO

🌍 Live app stargate-frontend-psi.vercel.app — the product this API powers
📖 Docs stargate-frontend-psi.vercel.app/docs · in-repo guides under docs/
🖥️ Frontend FX-GENE/stargate-frontend — dashboard, checkout, widget
⛓️ Contracts FX-GENE/stargate-contracts — Soroban invoice / treasury / compliance

The problem this repo solves

On-chain payments don't tell you why money arrived. A raw Stellar payment has no invoice number, no merchant, no idempotency, no callback. Turning "a payment landed on the ledger" into "invoice #1042 is paid, the merchant was notified exactly once, and settlement is queued" requires an always-on reconciliation loop, durable delivery, and compliance gates — that's this repository.

What it does

  • Invoice engine — REST API for merchants: invoice CRUD with per-tier fee calculation and an atomic Postgres sequence (claim_invoice_seq()) that eliminates numbering races. Each invoice gets a unique muxed Stellar address so inbound payments self-identify.
  • Payer-aware XDR buildingGET /payments/:id/prepare-tx?payer=G... loads the payer account from Horizon and returns unsigned XDR; the payer signs in their own wallet. The API never holds a customer-fund key.
  • On-chain compliance oracle — before any XDR is built, the payer address is checked against the Soroban ComplianceContract (is_allowed()), with results cached 1h in Redis. Blocked addresses never get a transaction to sign.
  • Rust reconciler sidecar — streams Horizon payments over SSE, deduplicates by paging token, filters USDC by issuer, matches invoices by muxed ID (stellar-strkey), verifies amounts, and marks invoices paid inside a single DB transaction — then publishes to Redis for real-time UI updates.
  • Webhooks that arrive — Redis Streams consumer group (XREADGROUP) with at-least-once delivery, exponential backoff with jitter, HMAC-SHA256 signatures (X-Stargate-Signature), and SSRF hardening: private/loopback IPs are rejected with re-resolution on every redirect hop.
  • Automated settlement — batches merchant balances and disburses through the TreasuryContract (propose_settlement + multi-sig approval, KMS-backed signing in production).
  • Real-time statusGET /payments/:id/stream relays reconciler confirmations to checkout pages over Server-Sent Events; heartbeats every 15s.
  • Observability — OpenTelemetry tracing with X-Trace-ID on every response; one trace ID correlates API, reconciler, and webhook worker. Prometheus metrics, structured JSON logs.

Architecture

                    ┌────────────────────────────────────────────┐
Merchant ──REST────▶│  NestJS API                                 │
Checkout ──SSE─────▶│  auth · invoices · payments · webhooks      │──▶ PostgreSQL 15 (RLS)
                    │  compliance cache · settlement · health     │──▶ Redis 7 (cache, streams, pub/sub)
                    └───────────────┬────────────────────────────┘
                                    │ Soroban RPC (is_allowed / propose_settlement)
                                    ▼
                      stargate-contracts (compliance · treasury)

Stellar Horizon ──SSE payments──▶ Rust reconciler ──▶ Postgres (mark paid, ledger entries)
                                        └────────────▶ Redis publish → API SSE → checkout
Webhook worker ◀── Redis Streams (XREADGROUP STARGATE-WEBHOOKS) ──▶ merchant endpoints (HMAC)

The Stargate trio

Repo Role
stargate-frontend Next.js dashboard, hosted checkout, embeddable widget, public docs
stargate-backend (this) NestJS API, PostgreSQL, Redis Streams webhooks, Rust reconciler
stargate-contracts Soroban contracts: invoice escrow, treasury multi-sig settlement, compliance oracle

How they communicate: the frontend calls this API (NEXT_PUBLIC_API_URL); this API calls Soroban RPC using contract IDs from env (COMPLIANCE_CONTRACT_ID, TREASURY_CONTRACT_ID, INVOICE_CONTRACT_ID) and validates against the committed ABI snapshots in stargate-contracts/abis/; the reconciler talks to Horizon and Postgres directly.

Stack

Layer Technology
API NestJS 10 · Node.js 20 · TypeScript 5.5
Database PostgreSQL 15 · Row-Level Security
Cache / Streams Redis 7 · ioredis · XREADGROUP consumer groups
Reconciler Rust 2021 · tokio · sqlx · stellar-strkey
Smart contracts Soroban RPC (ComplianceContract + TreasuryContract)
Auth JWT + httpOnly cookie BFF · sk_live_ API keys with IP allowlists
Observability OpenTelemetry · Prometheus · structured JSON logs

Repo layout

src/                   NestJS application (modules per domain)
reconciler/            Rust sidecar — Horizon SSE → Postgres reconciliation
db/                    SQL migrations
packages/              shared internal packages
scripts/               ops scripts (pre-launch audit, …)
test/                  e2e suites
docs/                  runbooks & guides (see below)
Dockerfile.api / Dockerfile.reconciler / docker-compose.yml

Key documents in docs/: MERCHANT_QUICKSTART.md · INVOICE_PAYMENT_FLOW.md (sequence diagrams) · RECONCILER.md · LAUNCH_RUNBOOK.md · RECOVERY.md · E2E_TESTING.md · VERSIONING.md · openapi.yaml

Running locally

Requirements: Node.js 20, Rust toolchain, Docker.

docker compose up -d postgres redis
cp .env.example .env
npm ci
npm run db:migrate
npm run start:dev              # API → http://localhost:3001

Reconciler (separate process):

cd reconciler
cargo run

Point the frontend at it with NEXT_PUBLIC_API_URL=http://localhost:3001.

Verification

npm run typecheck
npm test
npm run test:e2e
npm run build
npm run generate:openapi
cargo test --manifest-path reconciler/Cargo.toml

API quickstart

# Create an invoice (authenticated merchant)
curl -X POST http://localhost:3001/invoices \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{ "amount_usdc": "10.00", "memo": "Order #1001", "expires_in_seconds": 900 }'
# → { "id": "inv_…", "muxed_address": "M…", "payment_url": "…/pay/inv_…", … }

# Public invoice lookup (what the checkout page uses)
curl http://localhost:3001/invoices/public/inv_abc123

# Payer-specific unsigned XDR
curl "http://localhost:3001/payments/inv_abc123/prepare-tx?payer=G..."

# Live status stream (SSE)
curl -N http://localhost:3001/payments/inv_abc123/stream

Verify a webhook before trusting it:

const digest = crypto.createHmac('sha256', webhookSecret).update(rawBody).digest('hex');
if (`sha256=${digest}` !== req.headers['x-stargate-signature']) throw new Error('bad signature');

Events: invoice.paid · invoice.expired · invoice.cancelled · settlement.completed · merchant.kyc.approved · merchant.kyc.rejected. Delivery is at-least-once — make handlers idempotent on invoice.id.

Health endpoints

Endpoint Purpose
GET /health Postgres + Redis liveness
GET /health/deep Full dependency check including Soroban RPC
GET /health/rpc Stellar Horizon + Soroban reachability

What's real vs. what's not (yet)

Real, end to end (locally):

  • Invoice → unsigned XDR → wallet signature → Horizon → reconciler match → invoice.paid webhook is a working loop against Stellar testnet when contracts are deployed and env is configured.
  • Muxed-address matching, atomic invoice sequencing, HMAC webhooks with backoff, SSRF/private-IP blocking, compliance-before-XDR, and OTel tracing are implemented with unit and e2e tests.

Simplified, or honestly unfinished:

  • Not publicly hosted yet — there is no public API URL; the deployed frontend runs sandbox/mock flows until this service gets production hosting (current milestone; see docs/LAUNCH_RUNBOOK.md).
  • Contract integration is env-gated — compliance/treasury calls require deployed contract IDs (stargate-contracts testnet scripts); without them the API runs with those features degraded.
  • Settlement signing uses KMS in the production design; local dev uses plain keys from env.
  • TODO.md tracks the open product item: auto-cancel of unpaid invoices after a merchant-configured TTL.

Environment

Copy .env.example into your secret manager — never commit real secrets. Notable variables: DATABASE_URL, REDIS_URL, JWT_SECRET, STELLAR_NETWORK, USDC_ISSUER, COMPLIANCE_CONTRACT_ID, TREASURY_CONTRACT_ID, SETTLEMENT_SIGNER_KEYS, OTEL_EXPORTER_OTLP_ENDPOINT.

License

MIT

About

Stargate NestJS backend and reconciler

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages