Hosted card-payment (onramp) page for AntSeed. The desktop app opens this page in a chromeless browser window when a buyer adds credits by card. The page's only job:
- Read the destination address + amount from the URL.
- Mint a single-use Coinbase Onramp session token server-side (the CDP secret never reaches any client).
- Send the user to Coinbase's hosted buy page. USDC is delivered on Base to the buyer's hot-wallet address; the desktop app detects the incoming USDC and sweeps it into the AntseedDeposits escrow contract automatically.
Session tokens are mandatory for Coinbase Onramp (secure-init, required since July 2025). Each token is single-use and expires after 5 minutes.
https://pay.antseed.com/?address=0x<40 hex>&amount=20&chain=base
address(required) — buyer hot-wallet EVM address,/^0x[0-9a-fA-F]{40}$/amount(optional) — USD amount; if absent the page shows an editable inputchain(optional) — defaults tobase; onlybaseis supported
GET /— checkout page (static, built with Vite)POST /api/onramp/session— body{ address, amount?, chain? }; validates, mints a session token via the CDP API, returns{ url }— a one-clickhttps://pay.coinbase.com/buy/select-asset?sessionToken=...URL. Rate-limited to 10 requests/min per IP.GET /healthz— liveness probe, returns{ ok: true }
POST https://api.developer.coinbase.com/onramp/v1/tokenwithAuthorization: Bearer <JWT>— JWT signed with the CDP Secret API key viagenerateJwtfrom@coinbase/cdp-sdk/auth(Ed25519 or ES256, 120 s expiry).- Docs: Create session token, JWT authentication, Generating an Onramp URL
| Variable | Description |
|---|---|
CDP_API_KEY_ID |
CDP Secret API key ID (portal.cdp.coinbase.com) |
CDP_API_KEY_SECRET |
CDP Secret API key secret — server-side only, never log |
PORT |
HTTP port (default 8787) |
ONRAMP_SANDBOX |
Set to 1 for Coinbase's sandbox checkout (see Testing) |
Copy .env.example to .env for local development.
pnpm install
pnpm dev # Fastify server on :8787 (serves API; static from dist/web if built)
pnpm dev:web # Vite dev server on :5173, proxies /api to :8787Open http://localhost:5173/?address=0x1111111111111111111111111111111111111111&amount=20.
pnpm run typecheck
pnpm run build # vite build (dist/web) + tsc (dist/server)
node dist/server/server.jsdocker build -t antseed-pay .
docker run -p 8787:8787 -e CDP_API_KEY_ID=... -e CDP_API_KEY_SECRET=... antseed-paySet ONRAMP_SANDBOX=1 and the buy URL targets pay-sandbox.coinbase.com
instead of production. Real CDP API keys are still required — the session
token is minted the same way — but the payment itself is simulated:
- Card
4242 4242 4242 4242simulates a successful purchase; any other 16-digit number simulates a decline. - Phone verification accepts any 6-digit code (e.g.
000000). - No real USDC is delivered, so the desktop's watch-and-sweep flow will not trigger from a sandbox purchase — test that separately on a local chain.
Sandbox currently covers the guest-checkout flow only.
- Terminate TLS in front (the page must be served over HTTPS; Coinbase requires your domain to be allow-listed in the CDP portal under Onramp/Offramp domain settings).
- The server trusts
X-Forwarded-For(trustProxy: true) for rate limiting — run it behind a proxy you control. - Stateless: no database, no sessions; safe to run multiple replicas.