A Solana-based stablecoin payment gateway. Merchants accept payments in USDC/USDT while users can pay with SOL or any supported token — auto-converted via Jupiter DEX (just for MVP ofc).
Built for Colosseum Solana Frontier Hackathon 2026.
Final-demo-1080.mp4
No shared addresses. Every order gets its own unique deposit wallet derived from a master seed — child private keys are never stored.
- HD Wallet Derivation — one master seed generates unlimited unique deposit addresses deterministically
- Merchant API — API key auth, merchant registration, payout wallet configuration
- Payment Sessions — create payments with configurable expiry, unique deposit address per order
- On-chain Detection — Helius webhooks fire the moment SOL/USDC hits a deposit address
- Dynamic Address Watching — deposit addresses registered with Helius automatically on payment creation, unregistered on completion
- Auto-swap — Jupiter DEX integration converts received token to USDC automatically
- Background Jobs — BullMQ + Redis job queue handles swaps asynchronously, never blocking the webhook handler
- USDC Transfer — after swap, USDC transferred from deposit wallet to merchant's payout wallet via SPL token transfer
- Merchant Notifications — merchant's configured webhook URL called on payment completion
- Idempotent webhook handling — duplicate Helius events safely ignored
- Auto-expiry — pending payments past expiry automatically marked expired
- Retry logic — failed swap jobs retry up to 3 times with exponential backoff
| Layer | Choice |
|---|---|
| Runtime | Node.js + TypeScript |
| Server | Fastify |
| Database | PostgreSQL (Supabase) |
| Cache / Queue | Redis + BullMQ |
| Blockchain | Solana (Devnet / Mainnet) |
| On-chain Indexing | Helius Webhooks |
| DEX / Swap | Jupiter Aggregator |
| Token Transfer | @solana/spl-token |
| HD Wallet | bip39 + ed25519-hd-key + tweetnacl |
fluxpay/
├── src/
│ ├── index.ts ← Fastify server entry point
│ ├── types/index.ts ← shared TypeScript types
│ ├── db/
│ │ ├── index.ts ← PostgreSQL pool + schema init
│ │ └── redis.ts ← Redis client
│ ├── middleware/
│ │ └── auth.ts ← API key validation
│ ├── services/
│ │ ├── wallet.ts ← HD wallet derivation (core logic)
│ │ ├── helius.ts ← register/unregister addresses with Helius
│ │ ├── jupiter.ts ← Jupiter quote + swap (+ mock for Devnet)
│ │ ├── transfer.ts ← SPL token transfer to merchant wallet
│ │ ├── notify.ts ← merchant webhook notification
│ │ └── swapWorker.ts ← BullMQ worker for background swaps
│ └── routes/
│ ├── payments.ts ← POST /payments/create, GET /payments/:id
│ ├── merchants.ts ← POST /merchants/register
│ └── webhooks.ts ← POST /webhooks/helius
├── .env.example
├── .gitignore
├── Week1.md
├── week-2-till-now.md
├── package.json
└── tsconfig.json
- Node.js 18+
- PostgreSQL (or Supabase free tier)
- Redis (
docker run -d --name fluxpay-redis -p 6379:6379 redis:alpine) - Helius account (free tier)
- ngrok for local webhook testing
# Clone the repo
git clone https://github.com/Cypher-CP0/fluxpay.git
cd fluxpay
# Install dependencies
npm install
# Configure environment
cp .env.example .env
# Fill in your values
# Start Redis
docker start fluxpay-redis
# Run in development
npm run devYou should see:
✅ Redis connected
✅ DB schema ready
✅ Swap worker started
🚀 FluxPay running on port 3000
# Server
PORT=3000
NODE_ENV=development
# Database
DATABASE_URL=postgresql://postgres:password@host:5432/postgres
# HD Wallet master seed — NEVER commit the real value
# Generate: node -e "const b = require('bip39'); console.log(b.generateMnemonic())"
MASTER_MNEMONIC="twelve word bip39 mnemonic seed phrase goes here"
# Helius
HELIUS_API_KEY=your_helius_api_key
HELIUS_WEBHOOK_ID=your_webhook_id_from_helius_dashboard
# Redis
REDIS_URL=redis://localhost:6379
# Network: devnet | mainnet-beta
SOLANA_NETWORK=devnet
# Set to true for Devnet testing (skips real Jupiter swap + transfer)
MOCK_SWAP=truePOST /api/merchants/register
{
"name": "My Store",
"payout_wallet": "<solana_wallet_address>",
"webhook_url": "https://mystore.com/webhooks/fluxpay"
}Response:
{
"id": "uuid",
"name": "My Store",
"api_key": "fp_live_...",
"payout_wallet": "<address>",
"webhook_url": "https://...",
"created_at": "..."
}POST /api/payments/create
x-api-key: fp_live_...
{
"order_id": "order_123",
"amount_usdc": 25.00
}Response:
{
"payment_id": "uuid",
"deposit_address": "<unique solana address>",
"amount_usdc": "25.000000",
"expires_at": "2026-04-18T12:00:00.000Z",
"status": "pending",
"network": "devnet"
}Show deposit_address to the user as a QR code. They send SOL or USDC to this address.
GET /api/payments/:payment_id
x-api-key: fp_live_...
Response:
{
"payment_id": "uuid",
"order_id": "order_123",
"deposit_address": "<address>",
"amount_usdc": "25.000000",
"amount_received": "1.00",
"token_received": "SOL",
"status": "completed",
"expires_at": "...",
"created_at": "..."
}pending → detected → swapping → completed
→ failed (retries up to 3x with exponential backoff)
pending → expired (after expiry window with no payment)
1. POST /api/payments/create
→ HD wallet derives unique deposit address
→ Address registered with Helius for watching
→ Payment record created in DB (status: pending)
2. User sends SOL/USDC to deposit address
3. Helius detects transaction → POST /webhooks/helius
→ Webhook identifies token + amount received
→ Payment updated (status: detected)
→ Swap job enqueued in BullMQ (webhook returns immediately)
4. BullMQ swap worker picks up job
→ Status: swapping
→ Jupiter swaps received token → USDC
→ USDC transferred via SPL token transfer to merchant payout wallet
→ Status: completed
→ Address unregistered from Helius
→ Merchant webhook_url notified
merchants
id UUID PRIMARY KEY
name TEXT
api_key TEXT UNIQUE
payout_wallet TEXT
webhook_url TEXT
created_at TIMESTAMPTZpayments
id UUID PRIMARY KEY
merchant_id UUID → merchants.id
order_id TEXT
deposit_address TEXT UNIQUE
derivation_path TEXT
amount_usdc NUMERIC
amount_received NUMERIC
token_received TEXT
status TEXT
expires_at TIMESTAMPTZ
created_at TIMESTAMPTZEvery payment gets a unique deposit address derived from a master seed using BIP44:
m/44'/501'/{merchantIndex}'/{orderIndex}'
↑ 501 = Solana coin type
Only the derivation path is stored in DB — never child private keys. To sweep funds, the keypair is re-derived on demand using the stored path.
# Terminal 1 — run the server
npm run dev
# Terminal 2 — expose to internet
ngrok http 3000Use the ngrok URL as your Helius webhook URL:
https://your-ngrok-url.ngrok-free.dev/webhooks/helius
Keep ngrok running — free tier gives a new URL on every restart.
- Jupiter has no liquidity on Devnet — set
MOCK_SWAP=truein.envto simulate swaps and transfers - Get Devnet SOL from faucet.solana.com (2 airdrops per 8 hours)
- Set
MOCK_SWAP=falseandSOLANA_NETWORK=mainnet-betafor production
main ← stable, tested, merged at end of each week
week-2 ← current (pending final end-to-end test)
week-3 ← UI layer (coming next)
- HD wallet derivation
- Merchant registration + API key auth
- Payment create / status endpoints
- Helius webhook receiver
- Dynamic address registration with Helius
- BullMQ + Redis job queue
- Jupiter swap integration (+ mock for Devnet)
- USDC SPL token transfer to merchant payout wallet
- Merchant webhook notification on completion
- Retry logic with exponential backoff
- Full end-to-end Devnet test (pending faucet cooldown)
- Checkout widget UI (week 3)
- Merchant dashboard (week 3)
- Mainnet deployment (week 4)