Skip to main content
BillingSubscriptions

Subscriptions.

OpenSettle handles the full recurring-billing lifecycle: renewal, invoicing, dunning, refunds, and self-service cancellation. Invoice-and-pay works on every wallet and chain; ERC-20 allowance autopay is live on Base, Polygon, and Arbitrum (EOA wallets approve a cap once, renewals auto-pull). ERC-4337 smart-wallet session keys are on the roadmap.

Invoice-and-pay (live today)

The renewal-worker emits a renewal invoice ahead of each billing period. The customer receives an email with a hosted-invoice link, signs the transfer from their wallet, and the subscription renews when the chain-reader observes the deposit. Works on every wallet on every supported chain.

create-subscription.ts
const sub = await os.subscriptions.create({
  customerId: "cus_9fX0a2E1",
  priceId: "price_9fX0a2E1",
  chain: "base",
  token: "USDC",
  autopay: "manual",            // "manual" or "allowance" (live on Base/Polygon/Arbitrum); smart-wallet is roadmap
});
create_subscription.py
sub = client.subscriptions.create(
    customer_id="cus_9fX0a2E1",
    price_id="price_9fX0a2E1",
    chain="base",
    token="USDC",
    autopay="manual",
)
create_subscription.go
sub, err := client.Subscriptions.Create(ctx, &opensettle.CreateSubscriptionRequest{
  CustomerID: "cus_9fX0a2E1",
  PriceID:    "price_9fX0a2E1",
  Chain:      "base",
  Token:      "USDC",
  Autopay:    "manual",
})
create_subscription.rs
let sub = client
    .subscriptions()
    .create(CreateSubscriptionRequest {
        customer_id: "cus_9fX0a2E1".into(),
        price_id: "price_9fX0a2E1".into(),
        chain: Some("base".into()),
        token: Some("USDC".into()),
        autopay: Some("manual".into()),
        ..Default::default()
    })
    .await?;

Dunning cascade

When a renewal invoice goes unpaid, the renewal-worker emits a subscription.past_due webhook and runs the retry cascade: 1, 3, 7, and 14 days. If the customer hasn’t paid by day 14, the subscription is canceled and the customer is notified. Dunning behavior (cadence, copy, webhook fanout) will be configurable under Settings → Dunning once the dashboard surface ships.

Subscription lifecycle

  • trialing — created with a trial period; no charge yet.
  • active — current and paid.
  • past_due — renewal failed; dunning cascade engaged.
  • paused — merchant-initiated hold; renewal worker skips this row until you resume it.
  • canceled — ended after dunning exhausted (reason="dunning_exhausted") or after self-service cancellation. Terminal state.

Cancel: at period end vs immediately

cancel takes a mode. The default, mode: "at_period_end", keeps the subscription active until nextBillingDate — the customer keeps access through the period they already paid for, and the subscription simply doesn’t renew. It’s reversible: clear a pending at-period-end cancellation any time before the period ends with POST /subscriptions/{id}/uncancel — the same “Keep subscription” action exposed in the dashboard. mode: "immediately" ends it now and moves it to canceled straight away. An optional reason is recorded on the audit log either way.

cancel-subscription.ts
// Default: stays active until nextBillingDate, then stops renewing
await os.subscriptions.cancel(sub.id);

// Or end it right now
await os.subscriptions.cancel(sub.id, { mode: "immediately" });

ERC-20 allowance autopay (live on Base, Polygon, Arbitrum)

On Base, Polygon, and Arbitrum, an EOA customer approves a spending cap once and the renewal-worker pulls each renewal directly from the on-chain allowance — revocable any time. It’s the closest thing to card-on-file in stablecoins. On Ethereum the same allowance flow is supported, but L1 gas means it’s worth reserving for large invoices. Solana has no ERC-20 allowance equivalent and stays invoice-and-pay; Tron allowance autopay is on the roadmap.

On the roadmap

Smart-wallet autopay (ERC-4337). For customers on Safe, Coinbase Smart Wallet, Privy, etc., a scoped session key (cap + duration + allowed target) is the natural fit. Implementation follows the allowance work and is sequenced after first paying merchants ship in production.