Skip to main content
BillingCheckouts

Checkouts.

A checkout session is a short-lived intent to collect a stablecoin payment. Create one with a single POST, then redirect the customer to the returned URL or render the embedded widget. Funds settle directly to the merchant wallet specified in the session; OpenSettle never holds them.

Create a session

Four SDKs, one shape. Two modes: payment (one-off) or subscription (pass a priceId to spin up a recurring subscription on first payment). For mode payment, supply exactly one of an existing invoiceId, a one-time priceId, or an inline ad-hoc amount (minor units / cents). The chain + token fix the settlement rail (required for an inline amount; otherwise they override the defaults inherited from the invoice / price). The customer is optional for mode payment — omit customerEmail for a guest checkout.

create-checkout.ts
import { OpenSettle } from "@opensettle/sdk";

const os = new OpenSettle({
  apiKey: process.env.OPENSETTLE_KEY!,
  workspaceId: process.env.OPENSETTLE_WORKSPACE!,
});

const checkout = await os.checkouts.create({
  mode: "payment",
  customerEmail: "ada@lovelace.dev",
  invoiceId: "in_01jt…",
  chain: "base",
  token: "USDC",
  successUrl: "https://yourapp.com/billing/done",
  cancelUrl:  "https://yourapp.com/billing",
  expiresInMinutes: 30,
  metadata: { order_id: "ord_4118" },
});

// hostedUrl is an absolute URL (changed 2026-05-20); redirect as-is.
return Response.redirect(checkout.hostedUrl, 303);
create_checkout.py
import os
from opensettle import OpenSettle

client = OpenSettle(
    api_key=os.environ["OPENSETTLE_API_KEY"],
    workspace_id=os.environ["OPENSETTLE_WORKSPACE_ID"],
)

checkout = client.checkouts.create(
    mode="payment",
    customer_email="ada@lovelace.dev",
    invoice_id="in_01jt...",
    chain="base",
    token="USDC",
    success_url="https://yourapp.com/billing/done",
    cancel_url="https://yourapp.com/billing",
    expires_in_minutes=30,
    metadata={"order_id": "ord_4118"},
)

# Redirect the buyer to checkout["hostedUrl"]
create_checkout.go
client := opensettle.NewClient(
  os.Getenv("OPENSETTLE_API_KEY"),
  opensettle.WithWorkspace(os.Getenv("OPENSETTLE_WORKSPACE_ID")),
)

checkout, err := client.Checkouts.Create(ctx, &opensettle.CreateCheckoutRequest{
  Mode:             "payment",
  CustomerEmail:    "ada@lovelace.dev",
  InvoiceID:        "in_01jt...",
  Chain:            "base",
  Token:            "USDC",
  SuccessURL:       "https://yourapp.com/billing/done",
  CancelURL:        "https://yourapp.com/billing",
  ExpiresInMinutes: 30,
  Metadata:         map[string]string{"order_id": "ord_4118"},
})

// http.Redirect(w, r, checkout.HostedURL, http.StatusSeeOther)
create_checkout.rs
use opensettle::{Client, CreateCheckoutRequest};

let client = Client::new(
    std::env::var("OPENSETTLE_API_KEY")?,
    std::env::var("OPENSETTLE_WORKSPACE_ID")?,
);

let checkout = client
    .checkouts()
    .create(CreateCheckoutRequest {
        mode: "payment".into(),
        customer_email: Some("ada@lovelace.dev".into()),
        invoice_id: Some("in_01jt...".into()),
        chain: Some("base".into()),
        token: Some("USDC".into()),
        success_url: "https://yourapp.com/billing/done".into(),
        cancel_url: Some("https://yourapp.com/billing".into()),
        expires_in_minutes: Some(30),
        ..Default::default()
    })
    .await?;

The hosted page

The URL we return resolves to https://opensettle.io/checkout/<hostedToken>. The page detects the customer's wallet, prompts for the chain and token (constrained to what you allowed), and shows a live transaction tracker after submit. On confirmation, we redirect to your successUrl unchanged — use metadata or a query param of your own if you need to correlate it back to a checkout.

Expiration

Sessions expire after expiresInMinutes minutes. Default 30, maximum 1,440 (24 hours). After expiration the hosted page returns a "session expired" view and the chain reader ignores any in-flight tx, firing checkout.expired once per session. There's no force-expire endpoint today — set a short expiresInMinutes if you need tight inventory holds.

One-off vs recurring

A mode: "payment" checkout charges exactly one of three things: an existing invoice (invoiceId), a one-time price (priceId), or an inline ad-hoc amount (minor units) — an inline amount needs its own chain + token. Use mode: "subscription" with priceId to spin up a recurring subscription on first payment. The hosted page reads the amount, chain, and token off whichever you pass. For a reusable link that many buyers can pay (instead of a single-use session), reach for a payment link.

Webhooks fired

  • checkout.createdsession created.
  • payment.confirmedchain reader observed the deposit; required confirmations reached and funds are in the merchant wallet.
  • checkout.succeededsession resolved: the deposit landed and the linked invoice or subscription was settled.
  • checkout.expiredTTL elapsed without a confirmed deposit.