ConceptsSubscription lifecycle

Subscription lifecycle

If your store sells recurring products (a subscription box, a membership), each shopper’s recurring arrangement is a subscription. Subscriptions are an optional, billable capability — a store turns them on per the operator. This page describes the states a subscription moves through and which webhooks fire.

Subscriptions are powered by your store’s own Stripe account. Orchard records and exposes the subscription; Stripe drives the billing cycle.

States

   created ──────▶ active ──────▶ canceled
      │              │  ▲             │
      │              │  │ updated     │ ends at period end
      │              ▼  │             ▼
      │           (renews each      (no further
      │            period, may       charges)
      │            change plan)
      └─ first charge succeeds
StateMeaning
createdThe subscription has been set up
activeBilling on schedule; the customer has access
past_dueA renewal payment failed; Stripe is retrying
canceledEnded; no further charges

Orchard mirrors the status Stripe reports. The precise set of intermediate statuses (for example, trialing, past_due, unpaid, incomplete, or paused) follows Stripe’s subscription model.

Paused is not a separate status. When a subscription is paused (Stripe’s pause_collection), its status stays active — pausing stops billing without changing the reported status. If you’re gating access or entitlements on status, check pause_collection/paused_at too, or a paused subscription will read as fully active.

Transitions and webhooks

Subscription changes originate in Stripe (a renewal succeeds, a card fails, the customer or you cancel) and are reflected into Orchard, which then emits an outbound webhook to your subscribed endpoints.

Stripe eventOrchard webhookWhen
subscription createdsubscription.createdA new subscription is set up
subscription updatedsubscription.updatedRenewal, plan change, status change
subscription deletedsubscription.canceledThe subscription ends

Each webhook’s data includes the Orchard subscription id, the Stripe subscription id, the status, and (where applicable) the current period end and canceled-at time. See Webhook events for the exact shape.

As with orders, these deliver within about five minutes (queued, not instant) and may retry — dedupe on the delivery id.

created

The subscription is established and its first charge succeeds. Orchard emits subscription.created.

active and renewals

While active, Stripe charges the customer each period. Each renewal or change (plan swap, quantity change, a recovered payment) surfaces as subscription.updated. Use these to keep your own records — entitlements, access, fulfillment of the next box — in sync.

past_due

If a renewal payment fails, Stripe marks the subscription past_due and retries according to its dunning settings. You’ll see subscription.updated with the changed status. Decide how your app treats access during past_due (often: keep access during the retry window, revoke if it ultimately cancels).

canceled

When the subscription ends — the customer cancels, you cancel, or Stripe cancels after failed retries — Orchard emits subscription.canceled. Stop any recurring fulfillment and revoke access according to your policy. A cancel may take effect immediately or at period end depending on how it was issued.

Using the lifecycle

  • Drive entitlements from active/canceled. Grant access on created/active, revoke on canceled. Treat past_due per your grace policy.
  • Be idempotent. The same logical change can arrive more than once; dedupe on the delivery id and make your handlers safe to re-run.
  • Reconcile periodically. Webhooks are the fast path; for correctness, also reconcile against the subscription’s current state when in doubt.