StockCheckout.DocumentationStart building
Documentation / Get started
QUICKSTART

Your first payment,
in minutes.

Create a checkout, let your customer choose an asset, and receive a verified payment event. Your prices stay in dollars.

TypeScript & React Test mode first≈ 15 minutes after setup
Start with a real local-chain transaction. This release includes test USDC, NVDA, HOOD, and ETH on Anvil. Test assets and prices have no monetary value. Live stock-linked payments remain disabled.
01

Run your development environment

Clone this workspace and install the dependencies. You’ll need Node.js 24.x and Foundry’s forge, anvil, and cast commands.

Terminal
npm install
cp .env.example .env.local

# Generate two different secrets for .env.local:
openssl rand -hex 32  # WEBHOOK_MASTER_KEY
openssl rand -hex 32  # WORKER_SECRET

npm run dev
# In another terminal:
npm run chain:local
# In a third terminal:
npm run worker

Open your app at the exact origin configured in APP_URL. Register a merchant account, create a project, and verify your receiving wallet in Settlement. Generate a test API key in Developers.

Use a separate development wallet with Anvil’s public test accounts. Never put real assets in a wallet derived from a public test mnemonic.

Authentication

Keep your sc_sk_test_… key on your server. Send it as a Bearer token. Publishable keys start with sc_pk_test_… and cannot create sessions.

HTTP
Authorization: Bearer sc_sk_test_...
Content-Type: application/json
Idempotency-Key: order_1042

Keys belong to a project and have explicit scopes: checkout:write, checkout:read, payments:read, webhooks:read, and webhooks:write. Secrets are shown only once; create a replacement before revoking a deployed key.

02

Create a checkout session

The SDK packages are included in this npm workspace. Build them with npm run build:sdk. For another repository, pack each package locally and install the resulting tarball; these packages are not published to the public npm registry.

POST/v1/checkout/sessions
TypeScript
import { StockCheckout } from '@stockcheckout/js/server';

const stockcheckout = new StockCheckout(
  process.env.STOCKCHECKOUT_SECRET_KEY!,
  { apiBase: process.env.STOCKCHECKOUT_API_BASE! }
);

const session = await stockcheckout.checkout.sessions.create({
  amount: 1000, // Exactly $10.00 in USD cents
  currency: 'USD',
  acceptedAssets: ['USDC', 'NVDA', 'HOOD'],
  successUrl: 'https://your-store.com/success',
  cancelUrl: 'https://your-store.com/cart',
  metadata: { order_id: 'order_1042' },
}, { idempotencyKey: 'order_1042' });

// Redirect your customer to this hosted checkout:
return Response.redirect(session.checkout_url, 303);

Calculate prices on your server. Use integer cents and one idempotency key for each logical order. Reusing a key with different parameters returns 409 idempotency_conflict. Sessions expire after 24 hours.

Hosted checkout

Redirect to the session’s checkout_url, or use the React button. It opens the hosted checkout, where your customer reviews the asset amount and authorizes the payment.

React
import { StockCheckoutButton } from '@stockcheckout/react';

<StockCheckoutButton
  sessionId={session.id}
  publishableKey="sc_pk_test_..."
  stockcheckoutOrigin="https://your-stockcheckout-app.com"
  onError={(error) => setError(error.message)}
/>

The success_url receives a session_id query parameter. A redirect is not proof of payment: verify the session on your server or process the signed paid event before fulfilling the order.

Quotes & expiry

A quote binds the exact token amount, merchant wallet, asset, chain, router, session, nonce, and expiry. Amounts use integer arithmetic and round up to the smallest token unit.

POST/v1/checkout/sessions/:id/quotes
JSON
{ "asset": "NVDA" }

Quotes last at most two minutes and never outlive the session. Expired quotes require a new price and customer review. Token approval is for the exact amount. A payment mined within the signed expiry can still be reconciled after the quote expires.

Local prices are deterministic test fixtures. Live prices must come from a verified, fresh provider; pricing failures never fall back to made-up prices.
03

Receive the paid event

Add your endpoint in Webhooks before taking a payment, and save the signing secret. Verify the original bytes before parsing the body.

TypeScript
import { constructEvent } from '@stockcheckout/js/server';

const rawBody = await request.text();
const signature = request.headers.get('StockCheckout-Signature');
const event = constructEvent(
  rawBody,
  signature!,
  process.env.STOCKCHECKOUT_WEBHOOK_SECRET!
);

if (event.type === 'checkout.session.paid') {
  // In one database transaction:
  // 1. Deduplicate event.id.
  // 2. Retrieve the authenticated session.
  // 3. Match the order, amount, currency and paid status.
  // 4. Record fulfillment and the processed event ID.
}

return new Response('ok', { status: 200 });

The SDK checks the HMAC signature in constant time and rejects timestamps outside a five-minute window. Your application must persist processed event IDs to reject duplicate deliveries.

StockCheckout uses a durable outbox, database leases, response logging, and automatic retries. Run the worker regularly. The complete merchant example under examples/merchant implements durable order fulfillment and event deduplication.

EventMeaning
checkout.session.paidSettlement verified and recorded. Safe to begin fulfillment after matching your order.
Delivery retries60 seconds, 5 minutes, 30 minutes, 2 hours, 12 hours, 24 hours, and 48 hours.

Payments & receipts

Only the backend can mark a checkout paid. It checks the canonical successful receipt, transaction calldata and signature, router, session, merchant, token, raw amount, and confirmation depth. Each payment creates an immutable accounting record exactly once.

TypeScript
const session = await stockcheckout.checkout.sessions.retrieve(sessionId);
if (session.status === 'paid') {
  // Match session.metadata.order_id and session.amount.
}

const { payments } = await stockcheckout.payments.list();
const { payment } = await stockcheckout.payments.retrieve(paymentId);

Open /receipt/:sessionId for a verified receipt. Export your payment records from the dashboard. A worker scans the router’s confirmed events so a payment can be recovered even if the customer closes the browser immediately after broadcasting.

Refunds

Settlement is direct to the merchant. A refund requires the merchant to sign and send a separate transaction. This release does not implement automated refund verification or partial refunds, and never reports an unverified refund as completed.

Test mode

The local chain uses ID 31337 and RPC http://127.0.0.1:18544. The managed setup deploys the router and funds the first three public Anvil accounts with Test USDC, Test NVDA, and Test HOOD.

Terminal
# Public local-development keys only.
# Merchant: account 1. Customer: account 2.
cast wallet private-key \
  "test test test test test test test test test test test junk" 1
cast wallet private-key \
  "test test test test test test test test test test test junk" 2

Test rejection by canceling a wallet request. Test an expired quote by waiting two minutes, and insufficient balance by selecting an unfunded wallet. Test webhook retry by returning HTTP 500, then restoring a 2xx response and retrying the delivery.

Stock-linked tokens represent exposure to a stock. They are not actual brokerage shares. Live tokens require canonical contract verification and applicable transfer and jurisdiction controls.

Errors

Errors return a non-2xx HTTP status and an object containing error.code and a clear error.message. The server SDK throws StockCheckoutError with the status, code, and request ID.

JSON
{
  "error": {
    "code": "idempotency_conflict",
    "message": "Idempotency key was already used with a different request."
  }
}
CodeNext step
wallet_requiredVerify your receiving wallet first.
asset_unavailableChoose a supported asset or check chain setup.
session_expiredCreate a new checkout.
idempotency_conflictReuse the original request or create a new order key.
insufficient_scopeUse a key with the required scope.
origin_mismatchOpen the exact origin configured in APP_URL.
rate_limitedWait for the limit window before retrying.

SDK reference

@stockcheckout/js/server

Server session creation and retrieval, payments, endpoints, and webhook verification.

@stockcheckout/js

Browser-safe hosted checkout redirect. Accepts a publishable key.

@stockcheckout/react

StockCheckoutButton with styling, error handling, and optional cancellation.

Source, type declarations, complete setup instructions, security boundaries, and operations guidance ship in this repository. Build with npm run build:sdk; run npm test to validate the SDK and backend.

Build your first checkout