Skip to content

Latest commit

 

History

History
172 lines (123 loc) · 7.78 KB

File metadata and controls

172 lines (123 loc) · 7.78 KB

How Subscription State Transitions Work

A Chargebee subscription moves through defined states across its lifecycle. Understanding these transitions is essential for building reliable integrations — the wrong assumption about subscription state is one of the most common sources of integration bugs.


The state machine

                        ┌─────────┐
                        │  future │  (start_date in the future)
                        └────┬────┘
                             │ start_date reached
                             ▼
  create (plan has trial) ┌──────────┐
  ───────────────────────►│ in_trial │
                          └────┬─────┘
                               │ trial_end reached
                               │   or trial manually ended
                               ▼
  create (no trial)       ┌────────┐      cancel (end_of_term: true)    ┌──────────────┐
  ───────────────────────►│ active │ ──────────────────────────────────►│ non_renewing │
                          └───┬────┘                                     └──────┬───────┘
                              │                                                 │ term ends
                              │ pause()                                         │
                              ▼                                                 ▼
                          ┌────────┐    resume()                         ┌───────────┐
                          │ paused │◄───────────────────────────────────►│ cancelled │
                          └────────┘                                     └───────────┘
                              │
                              │ cancel (end_of_term: false)
                              ▼
                         (cancelled)

States

future

The subscription has been created but the start date hasn't arrived yet. No billing has occurred. Use this state to set up subscriptions in advance — for example, when a customer signs up mid-month and you want billing to start on the 1st.

Transitions out of future:

  • in_trial when start_date is reached, if the plan has a trial
  • active when start_date is reached, if the plan has no trial

in_trial

The trial period is active. The customer has access but has not been charged. An invoice is generated when the trial ends.

What to know:

  • Trial end is set by the plan's trial_period setting, or by the trial_end parameter you passed at creation.
  • You can end a trial early by calling subscription.end_trial().
  • A customer in in_trial with no payment method on file will move to active at trial end, but payment collection will fail. Handle payment_failed webhooks to recover these.

Transitions out of in_trial:

  • active when trial_end is reached and payment succeeds (or auto_collection: off)
  • cancelled if you cancel during trial

active

The subscription is billing normally. Chargebee generates an invoice and attempts collection at each current_term_end.

What to know:

  • current_term_end is the single most important timestamp in an active subscription. Your system should always know this value.
  • If collection fails at renewal, the subscription stays active but the invoice moves to payment_due. Chargebee's dunning workflow handles retries.
  • A subscription in active status with an unpaid invoice is still active — not suspended. You control access logic in your application based on invoice status.

Transitions out of active:

  • non_renewing if cancelled with end_of_term: true
  • cancelled if cancelled with end_of_term: false
  • paused if paused

non_renewing

The subscription is scheduled for cancellation at current_term_end. The customer retains access until then. No further invoices will be generated after the current term.

What to know:

  • You can reverse a non_renewing subscription by calling subscription.remove_scheduled_cancellation(). It returns to active.
  • non_renewing is the correct state to show a "cancellation pending" notice in your UI. The customer still has access.

Transitions out of non_renewing:

  • cancelled when current_term_end is reached
  • active if scheduled cancellation is removed

paused

Billing is suspended. The customer does not have access and no invoices are generated while paused. Billing resumes at resume_date.

What to know:

  • Pausing extends the billing term by the pause duration.
  • Use the pause() endpoint to pause, and resume() to resume early.
  • Not all plans support pausing. Configure pause settings at Product Catalog → Plans.

Transitions out of paused:

  • active when resume_date is reached, or via resume()
  • cancelled if cancelled while paused

cancelled

The subscription is terminated. No further billing occurs. A cancelled subscription cannot be reactivated — create a new subscription for the same customer instead.

Transitions out of cancelled:

  • None. Terminal state.

Common integration mistakes

Assuming active means paid. A subscription can be active with an overdue invoice. Always check invoice status separately if your access control depends on payment.

// ❌ Wrong — active doesn't mean paid
if (subscription.status === 'active') grantAccess();

// ✅ Correct — check both
const hasAccess = 
  subscription.status === 'active' || subscription.status === 'in_trial';
const isPaid = 
  !latestInvoice || latestInvoice.status === 'paid' || subscription.auto_collection === 'off';

if (hasAccess && isPaid) grantAccess();

Not handling non_renewing. Customers in non_renewing status still have access. If your access check only looks for active, you'll incorrectly block them.

// ❌ Misses non_renewing customers (they still have access)
const canAccess = subscription.status === 'active';

// ✅ Correct
const ACTIVE_STATES = ['active', 'in_trial', 'non_renewing'];
const canAccess = ACTIVE_STATES.includes(subscription.status);

Relying on polling instead of webhooks. Don't poll for subscription status changes. Use webhooks — specifically subscription_activated, subscription_cancelled, subscription_renewed, and payment_failed. See the Webhooks reference.


Webhook events by state transition

Transition Webhook event
futurein_trial or active subscription_activated
in_trialactive subscription_trial_end_reminder, then subscription_activated
activenon_renewing subscription_scheduled_cancellation
non_renewingactive subscription_scheduled_cancellation_removed
active or non_renewingcancelled subscription_cancelled
activepaused subscription_paused
pausedactive subscription_resumed
Renewal invoice generated invoice_generated
Payment collected payment_succeeded
Payment failed payment_failed

Related