Recurring billing

Subscriptions

Throttle subscriptions turn a one-time embed payment into a recurring billing relationship. The card vaulted during checkout is reused on a 5-minute renewal cron, with built-in dunning and a webhook stream that lets your backend stay in sync.

What ships in v1

  • Recurring on the embed. Pass a recurring block on a checkout session, the embed vaults the card, and (when create: 'auto' is set) Throttle creates the subscription on your behalf.
  • Lifecycle API. Cancel, pause, resume, change plan, list, and fetch subscriptions through @usethrottle/subscriptions/server from your backend, or through the React hooks routed via your backend proxy.
  • React management package. @usethrottle/subscriptions exposes hooks and primitive components so your buyer-facing portal does not have to re-derive the API plumbing.
  • Built-in dunning. Hardcoded retry curve of [1, 3, 7] days; after three failed renewal attempts the subscription auto-cancels with adunning_exhausted reason.
  • Trial-fraud protection. Auto-create checks the card fingerprint against prior subscriptions on this merchant; same card already used for a trial → new subscription is created without a trial and a subscription.trial_blocked webhook fires. Pre-flight via POST /api/v1/subscriptions/eligibility-check. See Trial Fraud Protection.
  • Webhook stream. subscription.created, .updated, .activated, .renewed, .past_due, .payment_failed, .paused, .resumed, .cancelled.

What is intentionally out of v1

  • No-card free trials. Trials always require a vaulted card up front; the embed does not yet support a "subscribe now, add card later" flow. Trials with a card behave the way you expect — Throttle delays the first charge until trialEnd.
  • Net30 recurring. Renewals charge a stored card via the payment provider. Net30 is supported for one-time orders today; Net30 recurring (invoice every period) is not part of the current subscription package surface.
  • Customer-scoped JWTs. The React package authenticates by proxying through your backend. Direct browser → Throttle calls require a customer token system that is not yet built. See Buyer Portal for the proxy pattern documented for v1.
  • Throttle-sent emails. All notifications flow through your webhook endpoint; you decide which emails to send. Subscribing to subscription.payment_failed is enough to wire the standard "your card was declined" copy.
  • Mid-period proration. PATCH /api/v1/subscriptions/:id swaps the plan/amount; the new value applies on the next renewal. No proration credit is issued for the current period.
  • Recurring discounts. Promotion codes apply to cart-backed checkout totals today. Ongoing subscription-cycle discounts are deferred.
  • Configurable dunning curve. The retry schedule is fixed at [1, 3, 7] days. Per-merchant or per-subscription overrides are deferred.
Read these in order
Subscriptions touch checkout, customer identity, the renewal cron, and a buyer-facing surface. Start with the Quickstart for the end-to-end flow, then read Customer Identity before you wire your buyer portal.

How the pieces fit

  1. Subscribe. Your backend creates a checkout session with a recurring block. Buyer pays through <PaymentEmbed />. Card is vaulted. Throttle auto-creates the subscription. onSucceeded returns subscriptionId.
  2. Renew. A cron runs every 5 minutes. For any subscription whose currentPeriodEnd has passed, it charges the stored card with an idempotent chargeStored call and advances the period. On failure it follows the [1, 3, 7] retry curve.
  3. Manage. Your buyer portal calls your backend, which proxies to the Throttle API. The React package gives you hooks for the common flows.
  4. React. Your webhook handler stays in sync — record renewals, send dunning emails, gate access on past_due.

Sequence: auto-subscribe on card capture

When a session ships with a recurring block and create: 'auto', Throttle vaults the card during the embed's capture, runs the trial-fraud check, and creates the subscription before returning to the parent page.

sequenceDiagram
  participant B as Buyer
  participant F as Throttle Iframe
  participant P as Payment Provider
  participant T as Throttle Backend
  participant H as Webhook Endpoint
  B->>F: fills card details
  F->>P: authorize + tokenize
  P-->>F: paymentMethodId
  F->>T: POST /complete (recurring + create:auto)
  T->>P: capture + vault payment method
  P-->>T: vaulted token
  T->>T: checkTrialEligibility(card fingerprint)
  alt eligible for trial
    T->>T: set trialEnd = now + trialDays
  else fingerprint already used
    T->>H: subscription.trial_blocked
  end
  T->>T: subscriptionService.create(...)
  T-->>F: { orderId, paymentId, subscriptionId }
  T->>H: subscription.created
  F->>B: throttle.completed
Auto-subscribe flow: from card capture through subscription.created webhook.

Pages in this section