# Switch plans (https://docs.billwave.example/subscriptions/plan-switching)

How to switch plans [#how-to-switch-plans]

This guide shows how to move a customer between plans using `attach()`. Plans must be in the same **plan group** for Billwave to treat the change as a switch rather than a new subscription.

Upgrade a customer [#upgrade-a-customer]

Call `attach()` with the new plan. Billwave detects that the customer already has a subscription in the same plan group and initiates an upgrade.

```ts
const result = await billwave.attach({
  customer: "user_123",
  product: "pro-monthly", // higher-priced plan in the same group
});

if (result.checkoutUrl) {
  // Customer needs to pay the difference
  console.log(result.checkoutUrl);
} else {
  // Upgrade applied immediately (e.g. free → paid handled inline)
  console.log("Upgraded:", result.subscription);
}
```

Upgrades take effect immediately. Entitlements are re-provisioned so the customer sees the new plan's features right away.

Provider note:

* Paystack upgrades use a one-time prorated checkout when there is a payable difference.
* Stripe, Dodo Payments, and Polar use native provider-side plan changes when available.
* If the remaining upgrade difference is `0` or too small for the provider to charge directly, Billwave switches immediately and keeps the next renewal aligned to the new cycle.

Downgrade a customer [#downgrade-a-customer]

Same call, lower-priced plan:

```ts
const result = await billwave.attach({
  customer: "user_123",
  product: "starter", // lower-priced plan in the same group
});
```

Downgrades are **scheduled for the end of the current billing period**. The customer keeps their current plan's features until then. After the period ends, Billwave switches the plan and re-provisions entitlements.

Lateral moves [#lateral-moves]

If the new plan has the same price as the current one, the switch is applied immediately with no checkout required.

```ts
const result = await billwave.attach({
  customer: "user_123",
  product: "pro-annual", // same price tier, different interval
});

// result.subscription.status === "active" — no checkout needed
```

Plan group requirement [#plan-group-requirement]

Plans must share a **plan group** for switching to work. If you call `attach()` with a plan in a different group, Billwave creates a second subscription instead of switching.

Set the plan group when creating plans in the dashboard or via `plan()` in code:

```ts
plan("starter", {
  name: "Starter",
  price: 0,
  currency: "USD",
  interval: "monthly",
  planGroup: "main",
  features: [apiCalls.limit(100)],
});

plan("pro", {
  name: "Pro",
  price: 2000,
  currency: "USD",
  interval: "monthly",
  planGroup: "main", 
  features: [apiCalls.limit(10000)],
});
```

What changes during a switch [#what-changes-during-a-switch]

* Subscription state may be updated at the provider
* Entitlements are re-provisioned so the customer’s features match the new plan
* Scheduled downgrades are stored until the period ends
* Checkout-based upgrades keep renewal setup state on the subscription if the follow-up recurring subscription cannot be created immediately

Recommended UX [#recommended-ux]

* Use a preview endpoint (or dry-run) to show:
  * whether it’s immediate vs scheduled
  * whether payment is required
  * what proration will be charged

If you’re using the dashboard/API, expose this preview in your billing settings page.