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.
@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.
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:
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.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:
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 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-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.
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):
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:
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:
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 wallet7. Go live
Three changes, no code restructuring:
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.fee_bps, fee_amount, merchant_net_amount) before anyone pays.