SubsieBeta
Getting started

Quickstart

Go from nothing to a settled test payment and a verified webhook. Everything here runs in test mode on Base Sepolia with faucet USDC — no real money involved. Switching to live mode at the end is a one-line change.

Prefer a runnable starting point?
Clone examples/node-quickstart — a ~120-line Express backend that creates a checkout session and verifies webhooks with @orven/node. npm run send-test fires a locally-signed webhook at your handler so you can watch it fulfill an order without paying. The steps below are the same flow, explained.

1. Get a test API key

Sign in and open the Developers page. Create a test key — it starts with orven_sk_test_. The key is shown once; store it as a server-side secret. Test keys operate on Base Sepolia and only ever see test data.

.env
ORVEN_SECRET_KEY=orven_sk_test_...

2. Verify a settlement wallet

Payments settle directly to a wallet you control — Subsie never holds funds. Before a session can be created, you prove you own the receiving wallet by signing a message (free, no transaction). The easiest path is the dashboard wallet flow on the Developers page; the API equivalent is two calls:

Register the wallet
curl https://pay.example.com/api/v1/wallets \
  -H "Authorization: Bearer $ORVEN_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "address": "0xYourWallet..." }'

# Response includes verification.message — an EIP-191 message to sign.
Prove ownership
curl https://pay.example.com/api/v1/wallets/wlt_.../verify \
  -H "Authorization: Bearer $ORVEN_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "signature": "0x..." }'

Sign the message with any wallet tooling (the dashboard does this with one click). Standard wallets and ERC-1271 smart wallets such as Coinbase Smart Wallet are both supported. A wallet verified once works for test and live sessions.

3. Create a checkout session

From your server, create a session with the terms of the charge. Amounts are decimal strings in USDC. With the @orven/node SDK once it is published, or use the plain HTTP example below today:

TypeScript
import { Orven } from "@orven/node";

const orven = new Orven(process.env.ORVEN_SECRET_KEY!, {
  baseUrl: "https://pay.example.com",
});

const session = await orven.checkoutSessions.create(
  {
    mode: "payment",
    title: "Pro plan — June",
    amount: "25",
    customer_reference: "user_8231",
    success_url: "https://example.com/billing/success",
    cancel_url: "https://example.com/billing/cancel",
  },
  { idempotencyKey: "user_8231_2026_06" },
);

// session.url is the hosted checkout page
console.log(session.url);

Or with plain HTTP:

curl
curl https://pay.example.com/api/v1/checkout/sessions \
  -H "Authorization: Bearer $ORVEN_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: user_8231_2026_06" \
  -d '{
    "mode": "payment",
    "title": "Pro plan — June",
    "amount": "25",
    "customer_reference": "user_8231",
    "success_url": "https://example.com/billing/success"
  }'
Idempotency
The Idempotency-Key header makes retries safe: replaying the same key with the same body within 24 hours returns the original session instead of creating a duplicate.

4. Send the buyer to pay

Redirect the buyer to session.url. The hosted page handles wallet connection and payment on Base Sepolia — fund a test wallet with Sepolia ETH (for gas) and test USDC from the Circle faucet.

Current test-mode settlement
Live payment sessions use the PaymentRouter path. Test mode will use the Sepolia PaymentRouter once BASE_SEPOLIA_PAYMENT_ROUTER_ADDRESS is configured; until then, test payments use the documented direct-transfer fallback with zero platform fee.

After paying, the buyer lands on your success_url. The session completes server-side either way: Subsie verifies the settlement on-chain and also reconciles directly from chain events, so a closed tab cannot lose a payment.

5. Listen for the webhook

Register an endpoint and Subsie will deliver signed events for everything that happens. The response contains the signing secret (shown once — store it next to your API key):

Register an endpoint
curl https://pay.example.com/api/v1/webhook_endpoints \
  -H "Authorization: Bearer $ORVEN_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/webhooks/orven" }'

Verify the signature and handle the completion event:

Express handler
import express from "express";
import { Orven, OrvenWebhookVerificationError } from "@orven/node";

const app = express();

app.post(
  "/webhooks/orven",
  express.raw({ type: "application/json" }), // raw body — required
  (req, res) => {
    let event;
    try {
      event = Orven.webhooks.constructEvent({
        payload: req.body,
        header: req.get("Orven-Signature") ?? "",
        secret: process.env.ORVEN_WEBHOOK_SECRET!,
      });
    } catch (error) {
      if (error instanceof OrvenWebhookVerificationError) {
        return res.status(400).send("invalid signature");
      }
      throw error;
    }

    if (event.type === "checkout.session.completed") {
      const session = event.data.object;
      // Fulfill the order for session.customer_reference
    }

    res.status(200).end();
  },
);

For local development, endpoints on http://localhost are allowed (everything else must be HTTPS). Deliveries retry up to five times with exponential backoff — details in the webhooks guide.

6. Confirm server-side

Webhooks are the push channel; the retrieval APIs are the pull channel. Both always agree:

TypeScript
const refreshed = await orven.checkoutSessions.retrieve(session.id);
// refreshed.status === "completed"
// refreshed.tx_hash — the on-chain settlement transaction

const payments = await orven.payments.list({ checkout_session: session.id });
// payments.data[0].amount           gross, e.g. "25"
// payments.data[0].fee_amount       Subsie fee from the session's fee snapshot
// payments.data[0].merchant_amount  what reached your wallet

7. Go live

Three changes, no code restructuring:

Checklist
1. Create a live key on /developers (orven_sk_live_...) and swap ORVEN_SECRET_KEY.
2. Register your production webhook endpoint with the live key
   (endpoints are mode-scoped; the test endpoint keeps receiving test events only).
3. Confirm your settlement wallet is verified — live sessions settle real USDC
   to it on Base.
Live mode is real money
Live sessions settle USDC on Base mainnet. Subsie applies your account fee at session creation and discloses it on every object (fee_bps, fee_amount, merchant_net_amount) before anyone pays.