Subscription Billing Patterns
Universal patterns for the full subscription lifecycle. These apply to any billing system — the examples use @commet/node but the logic is the same everywhere.
Subscription State Machine
Every subscription moves through these states:
draft → pending_payment → trialing → active → canceled
│ │
└──────────┴→ past_due
│
├→ active
└→ canceled
The persisted statuses are draft, pending_payment, trialing, active, past_due, and canceled. Commet does not expose a pause/resume status.
One active subscription relationship per customer. Treat the active endpoint and current API errors as the source of truth when deciding whether a new subscription may be created. Do not recreate lifecycle rules from stale status lists.
Quick Reference
| Task | File |
|---|---|
| Implement free trials | trials-and-intro-offers.md |
| Add intro offer discounts | trials-and-intro-offers.md |
| Handle plan upgrades | upgrades-downgrades.md |
| Handle plan downgrades | upgrades-downgrades.md |
| Change billing interval | upgrades-downgrades.md |
| Calculate proration | proration-logic.md |
| Handle mid-cycle plan changes | proration-logic.md |
| Implement failed payment retries | dunning-and-retries.md |
| Build grace period logic | dunning-and-retries.md |
| Cancel a subscription | cancellation-flows.md |
| Reactivate a canceled subscription | cancellation-flows.md |
| Add purchasable add-ons | addons-and-extras.md |
| Prorate mid-cycle add-on activation | addons-and-extras.md |
Key Principle
Do not infer subscription behavior from a generic fairness rule. In Commet, plan changes are classified from billing interval, plan-group order, and paid/free transitions. Price edits affect renewal through the selected catalog price, while accepted Offer phases remain immutable. Use the linked reference for the exact operation you are implementing.