- Go 92.8%
- PLpgSQL 5.2%
- Makefile 1.2%
- Dockerfile 0.8%
Implement a production-grade Go backend for accepting Solana payments in SOL, USDC, and USDT with PostgreSQL persistence. Key features: - Deterministically derived ed25519 wallets per invoice to prevent fund collisions - Idempotent write APIs with Idempotency-Key header support - Rate limiting per API key - Durable webhook delivery with exponential backoff and concurrent-safe queue draining - Embedded database migrations applied on boot - Minimal Solana JSON-RPC client for balance and transaction queries - AES-256-GCM encryption for wallet secrets at rest Project structure includes command-line tools (server, migrations, test wallet generation), core domain logic, data access layer, HTTP API with chi router, and background workers for scanning, webhook dispatch, and cleanup. Includes comprehensive documentation, Docker setup, and configuration examples. |
||
|---|---|---|
| cmd | ||
| internal | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| PRODUCTION.md | ||
| README.md | ||
| USAGE.md | ||
crypto-gateway
A production-style Go backend for accepting payments on Solana in SOL, USDC, and USDT, backed by PostgreSQL.
The service is built around a few non-negotiables:
- Every invoice gets its own deterministically derived ed25519 wallet, so a given payer can never collide with another payer's funds.
- All write APIs are idempotent (
Idempotency-Keyheader) and rate-limited per API key. - Webhook delivery is durable: it survives restarts, retries with
exponential backoff, and uses
FOR UPDATE SKIP LOCKEDso multiple replicas can drain the queue safely. - Database migrations are embedded in the binary; the server applies them on boot.
Project layout
cmd/
server/ crypto-gateway HTTP + workers
migrate/ apply database migrations and exit
testwallet/ generate ed25519 keypairs for local testing
internal/
config/ env-driven configuration with validation
logger/ structured slog logger
db/ pgx pool + embedded migrations
domain/ core types (Invoice, Wallet, Payment, ...)
repository/ Postgres data access layer
chain/solana/ minimal JSON-RPC client (balances, sigs, txns)
wallet/ ed25519 derivation + AES-256-GCM at-rest encryption
service/ invoice, payment and webhook services
httpapi/ chi router + handlers + middleware
idempotency/ replay-safe POST middleware backed by Postgres
ratelimit/ in-memory token bucket per API key/IP
worker/ scanner, webhook dispatcher, janitor
Quick start
# 1. Boot Postgres
docker compose up -d postgres
# 2. Generate a master derivation secret + a fee-payer wallet
make wallet # one-off random keypair
go run ./cmd/testwallet -gen-secret # 32-byte hex master secret
# 3. Configure the server
cp .env.example .env
# edit .env: set DATABASE_URL, WALLET_DERIVATION_SECRET, SOLANA_FEE_PAYER_SECRET
# 4. Run migrations and start the server
make migrate
make run
The server boots on :8080 by default. Health checks: GET /healthz,
GET /readyz.
Creating an invoice
curl -sS -X POST http://localhost:8080/v1/invoices \
-H "X-API-Key: dev-secret-please-change" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"reference": "order-42",
"asset": "USDC",
"amount": "12.50",
"ttl": "30m",
"metadata": {"customer_id": "u_001"}
}'
Response (truncated):
{
"id": "f5b3...e9",
"reference": "order-42",
"chain": "solana",
"asset": "USDC",
"amount": "12.5",
"amount_received": "0",
"address": "9xQe...derived address...",
"status": "pending",
"expires_at": "..."
}
The merchant should display address as the destination for the payer. The
scanner watches that address and updates the invoice status to confirming,
underpaid, or paid. On every status change a webhook is fired to the
merchant's configured URL with header
X-Gateway-Signature: sha256=<hmac of body using webhook_secret>.
Test wallets
Generate one or more wallets:
go run ./cmd/testwallet -count 3
go run ./cmd/testwallet -gen-secret
go run ./cmd/testwallet -derived -secret <hex32> -from 0 -count 5
Fund a devnet wallet:
solana airdrop 2 <address> --url https://api.devnet.solana.com
For SPL tokens, use spl-token transfer with the appropriate devnet mint or
mint your own dev USDC.
Configuration
All configuration is environment-driven. See .env.example for the full list.
Key items:
| Variable | Purpose |
|---|---|
DATABASE_URL |
Postgres connection string |
SOLANA_RPC_URL |
RPC endpoint (devnet / mainnet) |
SOLANA_USDC_MINT / SOLANA_USDT_MINT |
Network-specific SPL token mints |
WALLET_DERIVATION_SECRET |
Master HMAC secret used to derive invoice wallets |
API_KEYS |
Comma list of <merchant>:<key> entries |
RATE_LIMIT_RPS / RATE_LIMIT_BURST |
Per-key token bucket settings |
Running the test suite
make test # unit tests
make test-int # integration tests (needs DATABASE_URL_TEST)
Operational notes
- The HTTP server enforces a 1 MiB max request body, a 2048-request in-flight cap, request IDs, panic recovery, security headers, structured access logs, and per-key rate limiting before any handler runs.
- Postgres connection pool sizes are configurable; defaults assume a single
replica. Tune
DB_MAX_CONNSandDB_MIN_CONNSfor horizontal scale. - Workers are safe to run as multiple replicas: webhook delivery uses
FOR UPDATE SKIP LOCKED, and the scanner is idempotent at the payment level (ON CONFLICT DO NOTHING on(chain, tx_signature, asset)). - Wallet secrets are encrypted at rest with AES-256-GCM, key derived from the
master secret. The plaintext private key never leaves the gateway. A
treasury sweep workflow is wired in (see
cmd/server/main.go); enable it withSWEEPER_INTERVALand a configuredSOLANA_TREASURY_ADDRESS.
License
MIT