Subscription Lifecycle
Detailed documentation on billing cycles, retry schedules, state transitions, and how to handle each stage of a subscription.
Billing cycle
Each subscription tracks a cycle counter that increments after every successful charge. The billing date is anchored to the subscription start date.
Example: Monthly subscription started January 15
Anchor date preservation
Retry schedule
When a payment fails, Mazad automatically retries using an exponential backoff schedule. The number of retries depends on the plan's grace_period_days.
| Attempt | Delay after failure | Action |
|---|---|---|
| #1 | 1 day | Charge wallet, send webhook subscription.payment_retry |
| #2 | 3 days | Charge wallet, send webhook subscription.payment_retry |
| #3 | 7 days | Charge wallet, send webhook subscription.payment_retry |
| #4 | 14 days | Final attempt before grace expiry evaluation |
Grace period limits retries
grace_period_days is shorter than the retry schedule (e.g., 3 days), only retries within that window will be attempted. The subscription is canceled once the grace period expires regardless of remaining retries.State transitions
Complete reference of all possible subscription state transitions.
| From | To | Trigger |
|---|---|---|
| trialing | active | Trial period ends and first charge succeeds |
| trialing | past_due | Trial ends and first charge fails |
| trialing | canceled | Merchant or subscriber cancels during trial |
| active | past_due | Recurring payment fails |
| active | paused | Merchant or subscriber pauses |
| active | canceled | Merchant or subscriber cancels, or max_cycles reached |
| past_due | active | Retry payment succeeds |
| past_due | canceled | Grace period expires with no successful retry |
| paused | active | Merchant or subscriber resumes |
| paused | canceled | Merchant cancels while paused |
Handling lifecycle in your app
Use webhooks to keep your application in sync with subscription state changes. Here is a typical webhook handler.
app.post('/webhooks/mazad', (req, res) => {
const event = req.body;
switch (event.type) {
case 'subscription.activated':
// Grant access to the subscriber
grantAccess(event.data.customer_id);
break;
case 'subscription.past_due':
// Show a warning banner to the user
showPaymentWarning(event.data.customer_id);
break;
case 'subscription.canceled':
// Revoke access immediately
revokeAccess(event.data.customer_id);
break;
case 'subscription.paused':
// Optionally restrict features
restrictFeatures(event.data.customer_id);
break;
case 'subscription.resumed':
// Restore full access
grantAccess(event.data.customer_id);
break;
}
res.status(200).json({ received: true });
});Fixed-term subscriptions
Set max_cycles on a plan to automatically cancel the subscription after a specific number of billing cycles.
Example: 12-month commitment
Plan interval
monthly
Max cycles
12
Result
Auto-cancels after 12 months
Unlimited subscriptions
max_cycles to null (the default) for subscriptions that renew indefinitely until explicitly canceled.