> ## Documentation Index
> Fetch the complete documentation index at: https://docs.solvapay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Billing

> How SolvaPay bills from plan options: cycles, usage, trials, and renewals.

SolvaPay bills from the plan’s `options[]`. A `billingCycle` option sets when recurring charges run. Per-unit `charge` and `tier` options set usage rates. A `limit` option sets included usage. See [Plans](/plans/overview) for the option catalog.

Credits are only the prepaid wallet. Usage rates are money per meter noun. See [Plans and billing glossary](/plans/glossary).

## Billing cycles

A `billingCycle` option makes the plan recurring:

| Field      | Values                  | Meaning                            |
| ---------- | ----------------------- | ---------------------------------- |
| `interval` | `week`, `month`, `year` | Recurring interval                 |
| `count`    | integer ≥ 1             | Every N intervals. Defaults to `1` |

Quarterly is `{ "kind": "billingCycle", "interval": "month", "count": 3 }`. There is no `quarterly` or `custom` cycle enum.

The cycle drives:

* When the next payment is due (`nextBillingDate` on the purchase)
* The window for usage aggregation (`periodStart` to `periodEnd`)
* When included usage resets

A plan with no `billingCycle` is one-time (flat charge only) or usage-based (metered, billed as usage happens).

## How usage is billed

Metered usage comes from usage events on a meter. The HTTP path stays `POST /v1/sdk/meter-events`.

| Option                      | Role                                                                  |
| --------------------------- | --------------------------------------------------------------------- |
| `charge` with `per: "unit"` | Money per counted item on `meter`                                     |
| `tier`                      | Graduated or volume rates instead of a single per-unit charge         |
| `limit`                     | Included cap. `cap: 0` is unlimited. A missing limit is pay-as-you-go |
| `limit.onExceed`            | `block` stops access; `charge` bills overage at the per-item rate     |
| `rollover`                  | Unused included usage at period end (`forfeit`, `carry_forward`, …)   |

Overage is usage beyond the included cap, charged per meter noun. A plan with no limit bills every counted item.

Limit checks:

1. Find the active purchase for the customer and product
2. Read the frozen `planSnapshot.options`
3. If the meter’s `limit.cap` is `0`, allow immediately
4. Sum usage events for that meter in the current period
5. Compare against `cap`. The leftover included count is `LimitResponse.remaining` (`-1` means unlimited)

Wallet coverage (`remainingUnits`) is separate: how many metered items the credit balance still covers. Do not mix the two remainings.

A provider-level **minimum top-up** (`minTopUpMinor` in SolvaPay Console settings) applies to every credit top-up and auto-recharge for that provider. Amounts below it are rejected. See [Auto-recharge](/sdks/typescript/guides/auto-recharge).

## Purchase states

A purchase moves through these states:

| State                           | Description                                                       |
| ------------------------------- | ----------------------------------------------------------------- |
| `trialing`                      | Customer is in a `trial` option period                            |
| `active`                        | Purchase is active and the customer has access                    |
| `active` (pending cancellation) | Active with `cancelledAt` set — access continues until period end |
| `cancelled`                     | Cancel confirmed at period end                                    |
| `expired`                       | Purchase has ended                                                |
| `past_due`                      | Payment failed; purchase may be suspended                         |

## Reactivation

When a customer cancels a recurring purchase, it stays `active` with `cancelledAt` set until the period ends.

```typescript theme={null}
import { reactivateRenewal } from '@solvapay/next'

await reactivateRenewal(request, { purchaseRef: 'pur_...' })
```

This clears `cancelledAt` and restores auto-renew. A `purchase.updated` webhook fires.

Preconditions: the purchase must be `active`, have `cancelledAt` set, and `endDate` must not have passed.

## Plan switching

Call `activatePlan` with the new plan reference. If the customer already has an active purchase on a different plan for that product:

1. The existing purchase expires
2. A new purchase is created on the requested plan

```typescript theme={null}
import { activatePlan } from '@solvapay/next'

const result = await activatePlan(request, {
  productRef: 'prd_myapi',
  planRef: 'pln_pro',
})
```

This produces `purchase.expired` for the old purchase and `purchase.created` for the new one.

## Usage on the purchase

Each metered purchase can carry a `usage` subdocument for the current period. The **source of truth is the usage timeseries**, not `usage.used`. Limit checks sum events from `periodStart` to now.

```json theme={null}
{
  "usage": {
    "used": 0,
    "periodStart": "2026-01-01T00:00:00Z",
    "periodEnd": "2026-02-01T00:00:00Z",
    "resetDate": "2026-02-01T00:00:00Z",
    "overageUnits": 0,
    "overageCost": 0,
    "carriedOverUnits": 0
  }
}
```

## Recurring renewal

A daily job renews purchases whose `nextBillingDate` has passed and that still auto-renew:

1. Charge the recurring flat `charge` (and any due usage) when `requiresPayment` is true
2. Advance `nextBillingDate` by the `billingCycle` interval

`requiresPayment` is derived: any positive charge or tier amount.

## Trial end

A `trial` option sets `days` and `onEnd` (`convert`, `cancel`, or `downgrade`). When the trial ends:

* `convert` — move to the paid plan terms
* `cancel` — end access
* `downgrade` — move to `downgradeToPricingId`

If the plan requires payment and no payment method is on file, the purchase is suspended until checkout completes.

## Usage reset and rollover

When a period ends, included usage resets with the new `periodStart`. A `rollover` option can carry unused included usage forward (`carry_forward`) or forfeit it. Advancing `periodStart` resets visible usage without deleting usage events.

## Next steps

* [Plans](/plans/overview) — compose pricing from options
* [Plans and billing glossary](/plans/glossary) — credits, meter noun, included vs wallet remaining
