Billing Cycles
Billing cycles determine how often customers are charged and when usage counters reset:| Cycle | Duration | Use Case |
|---|---|---|
weekly | 7 days | High-frequency, short-commitment services |
monthly | 30 days | Standard SaaS subscriptions |
quarterly | 90 days | Discounted longer-term plans |
yearly | 365 days | Annual subscriptions with savings |
custom | 30 days (default) | Custom billing arrangements |
- When the next payment is due (
nextBillingDateon the purchase) - The time window for usage aggregation (
periodStarttoperiodEnd) - When usage counters reset
Purchase States
A purchase moves through these states during its lifecycle:| State | Description |
|---|---|
trialing | Customer is in a free trial period (set by trialDays on the plan) |
active | Purchase is active and the customer has access |
active (pending cancellation) | Active with cancelledAt set — access continues until period end, can be reactivated |
cancelled | Cancel confirmed at period end |
expired | Purchase has ended (trial expired without payment, cancelled and period ended, or replaced by plan switch) |
past_due | Payment failed; purchase may be suspended |
Reactivation
When a customer cancels a recurring purchase, the purchase enters a “pending cancellation” state — it remainsactive with cancelledAt set, and the customer keeps access until the current billing period ends.
Before the period ends, the cancellation can be undone:
cancelledAt and restores autoRenew, so the purchase continues renewing as normal. A purchase.updated webhook fires.
Preconditions: the purchase must be active, have cancelledAt set, and endDate must not have passed.
Plan Switching
When a customer wants to change plans on a product, callactivatePlan with the new plan reference. If the customer already has an active purchase on a different plan for that product, the system automatically:
- Expires the existing purchase
- Creates a new purchase on the requested plan
purchase.expired for the old purchase and purchase.created for the new one.
Usage Tracking on Purchases
For usage-based and hybrid plans, each purchase maintains ausage subdocument that tracks the current billing period:
usage.used field. The timeseries is the source of truth — limit checks query UsageService.sumForMeter() from periodStart to the current time.
Limit Checking Flow
When a customer makes a request to a protected endpoint, access is determined by:- Find the active purchase for the customer and product
- Look up the plan to get the
limit - If
limitis0— unlimited access, allow immediately - Resolve the plan’s auto-assigned requests meter
- Query the Usage timeseries —
UsageService.sumForMeter(providerId, meterName, customerRef, periodStart, now) - Compare against the hard cap — if
used >= limit, deny with a paywall response
End-of-Period Billing
A daily cron job (11:00 AM UTC) processes end-of-period billing for usage-based and hybrid plans. For each active recurring purchase whose billing period has ended:Usage-Based Plans
- Query the Usage timeseries for total usage in the period
- Subtract
freeUnitsfrom the total - Apply the
creditsPerUnitto the billable amount - Respect the
limitas a hard cap (usage beyond the cap is not billed unless overage is allowed) - Create a payment intent for the calculated cost
Hybrid Plans
Hybrid plans combine a recurring base fee with usage-based charges:- The base price is billed as part of the regular renewal
- Usage beyond the included amount is calculated separately
- Tiered pricing is applied if
usageTiersare defined — each tier has its owncreditsPerUnitfor a range of usage - Overage beyond the plan’s
limitis charged at theoveragePolicy.overageRate(orcreditsPerUnitas fallback) - The
overagePolicy.maxOveragecaps the maximum overage units
Renewal Processing
A daily cron job (10:00 AM UTC) handles recurring plan renewals:- Find purchases where
nextBillingDate ≤ nowandautoRenewis enabled - For paid plans, create a payment intent for the plan price
- For free plans, process the renewal directly
- Advance
nextBillingDateto the next billing cycle
Trial Expiration
A daily cron job (8:00 AM UTC) handles trial expirations:- If
requiresPaymentistrue: the purchase is suspended until payment is received - If
requiresPaymentisfalse: the purchase transitions to active
Usage Reset
A periodic job (every 30 minutes) resets usage counters on purchases whose reset date has passed:- Set
usage.usedto0(or carry-over amount ifrolloverUnusedUnitsis enabled) - Advance
periodStartandperiodEndto the next period - Calculate the next
resetDate
periodStart window, advancing periodStart effectively “resets” visible usage without deleting any usage records.
Billing Strategies (Hybrid Plans)
Hybrid plans support different billing strategies:| Strategy | Description |
|---|---|
recurring_first | Bill the base fee first, then calculate usage charges |
usage_first | Calculate usage first, then add the base fee |
combined | Bill both components together |
Proration (Recurring Plans)
When a customer upgrades or downgrades mid-cycle, proration policies determine how the change is handled:| Method | Description |
|---|---|
proportional | Charge/credit proportional to remaining days in the cycle |
full | Charge the full new price immediately |
none | Wait until the next billing cycle to apply the change |
Next Steps
- Plans — plan types and configuration
- Meters — meter definitions
- Usage Records — how usage data is recorded