Accept your first stablecoin payment.
OpenSettle exposes three independent entry points. The walkthrough below covers the invoice + checkout path; the other two link out to their own short guides.
1. Install the SDK
Typed SDKs ship in Node, Python, Go, and Rust. Or use the REST API directly from any HTTP client.
# Node
npm install @opensettle/sdk
# Python
pip install opensettle
# Go
go get github.com/OpenSettle/opensettle-sdk-go
# Rust
cargo add opensettle2. Create an invoice + hosted checkout
Two calls: register the customer, then build a checkout session that sends them to a hosted page where they pay with USDC on Base.
import { OpenSettle } from "@opensettle/sdk";
const os = new OpenSettle({
apiKey: process.env.OPENSETTLE_TEST_KEY!,
workspaceId: process.env.OPENSETTLE_WORKSPACE!,
testMode: true,
});
const customer = await os.customers.create({
email: "ada@example.com",
name: "Ada Lovelace",
});
const invoice = await os.invoices.create({
customerId: customer.id,
currency: "USD", // optional — defaults to USD
chain: "base",
token: "USDC",
lineItems: [{ description: "Pro plan", quantity: 1, unitAmountMinor: 19_900 }],
});
const checkout = await os.checkouts.create({
mode: "payment",
customerId: customer.id,
invoiceId: invoice.id,
successUrl: "https://yourapp.com/success",
cancelUrl: "https://yourapp.com/pricing",
});
// Redirect the buyer to the hosted checkout page.
// hostedUrl is already absolute (https://opensettle.io/checkout/...);
// just hand it to Response.redirect verbatim.
return Response.redirect(checkout.hostedUrl, 303);3. Handle the webhook
Verify the HMAC signature, then fulfil the order. The SDK's verifier is constant-time and rejects stale or tampered deliveries.
import { verifyWebhook, WebhookVerificationError } from "@opensettle/sdk";
app.post("/webhook", async (req, res) => {
try {
const { data } = verifyWebhook<{ id: string; type: string; data: any }>({
rawBody: req.rawBody, // exact bytes — not parsed JSON
signatureHeader: req.header("x-opensettle-signature"),
secret: process.env.WEBHOOK_SIGNING_SECRET!,
});
if (data.type === "payment.confirmed") {
const payment = data.data;
await grantAccess(payment.customerId);
await sendReceipt(payment.customerId, payment.id);
}
res.sendStatus(200);
} catch (err) {
if (err instanceof WebhookVerificationError) {
return res.status(400).end(err.reason);
}
throw err;
}
});4. Send a test payment
Open the checkout URL in a wallet on Base testnet and complete the flow. As soon as the payment confirms on-chain (typically within seconds — exact latency depends on the chain), your webhook handler fires with payment.confirmed. Use os.webhookEndpoints.test() to queue a synthetic webhook.endpoint.test event so you can verify the signature flow end-to-end without a real transfer:
const { eventId } = await os.webhookEndpoints.test(endpoint.id);
// Server queues a "webhook.endpoint.test" event for delivery — inspect
// the resulting attempt in the dashboard by eventId.