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| State | Meaning |
|---|---|
| created | The subscription has been set up |
| active | Billing on schedule; the customer has access |
| past_due | A renewal payment failed; Stripe is retrying |
| canceled | Ended; 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), itsstatusstays active — pausing stops billing without changing the reported status. If you’re gating access or entitlements onstatus, checkpause_collection/paused_attoo, 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 event | Orchard webhook | When |
|---|---|---|
| subscription created | subscription.created | A new subscription is set up |
| subscription updated | subscription.updated | Renewal, plan change, status change |
| subscription deleted | subscription.canceled | The 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.
Related
- Webhook events — the exact subscription payloads.
- Customers — where subscriptions show up in the admin.
- Settings → Webhooks — subscribing to subscription events.