Developers
Three calls to a settled payment.
Zero runtime dependencies, no build step, and an API shaped like the one you already know. Node 22.5+ is the only requirement.
Quickstart
From nothing to a settled payment in about a minute.
Get keys
POST to /v1/merchants with your platform key. The secret is shown once. Self-hosting instead? cd server && npm start — no install step.
Point the SDK at it
The SDK defaults to the hosted API. Pass baseUrl for your own deployment.
Take a payment
Create, select an asset, confirm. Settlement is observed, never asserted by the client.
Handle the webhook
Verify the signature, then fulfil on payment.succeeded. Never on confirm.
The whole integration
This is not an excerpt — it is the entire server-side flow.
import StockPay from '@stockpay/node'; const stockpay = new StockPay(process.env.STOCKPAY_SECRET_KEY, { baseUrl: 'https://stockpay.tech' }); // 1. Create. Amount is an integer in cents — 125000 is $1,250.00. const payment = await stockpay.payments.create({ amount: 125000, currency: 'usd', allowed_assets: ['AAPL', 'TSLA', 'ETH'], metadata: { order_id: 'ord_5512' } }, { idempotencyKey: 'ord_5512' }); // 2. The payer picks an asset. This locks a binding rate for 15 minutes. await stockpay.payments.selectAsset(payment.id, { asset: 'AAPL' }); // 3. Confirm opens the settlement channel — it does NOT mean paid. const confirmed = await stockpay.payments.confirm(payment.id); console.log(confirmed.next_action); // 4. Fulfil from the webhook, not from this call.
Verify a webhook
Pass the raw body. A re-serialized object will not match the signature.
import { constructEvent } from '@stockpay/node'; app.post('/hooks', express.raw({ type: 'application/json' }), async (req, res) => { let event; try { event = await constructEvent({ payload: req.body, signature: req.headers['stockpay-signature'], secret: process.env.STOCKPAY_WEBHOOK_SECRET }); } catch { return res.status(400).send('invalid signature'); } if (event.type === 'payment.succeeded') fulfil(event.data.object.metadata.order_id); res.json({ received: true }); });
Settlement is on Robinhood Chain
Chain 4663 — a live EVM L2 carrying 194 tokenized equities as ERC-20, plus native ETH. Payments are verified from the chain, not trusted from the client.
# mainnet is the default; this is the whole config RH_CHAIN_RPC_URL=https://robinhood-mainnet.g.alchemy.com/v2/KEY EVM_RECEIVING_ADDRESS=0xYourTreasury RH_CHAIN_CONFIRMATIONS=20 # exercise it against a chain first npm run test:chain # live Robinhood Chain + Sepolia
Scans blocks for native ETH transfers, queries ERC-20 Transfer logs for USDC, matches on an exact dust-tagged amount so concurrent payments to one treasury stay distinguishable, and re-derives confirmation depth from the receipt on every pass so a reorg un-settles rather than silently sticking.
Full API reference
Every endpoint, error code, event type and configuration flag.