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

# Swapping Plans

> Upgrade or downgrade subscriptions with configurable proration

# Swapping Plans

The `SwapPlan` action changes a subscription's plan mid-cycle. It handles proration, reconciles PlanItem quantities, and syncs with the gateway — all inside a database transaction.

## Basic plan swap

```php theme={null}
use Pixelworxio\Subscribd\Actions\SwapPlan;
use Pixelworxio\Subscribd\Models\Plan;

$subscription = $user->subscription();
$newPlan = Plan::where('key', 'enterprise')->firstOrFail();

app(SwapPlan::class)->execute($subscription, $newPlan);
```

This:

1. Computes a proration adjustment based on the configured strategy
2. Updates the subscription at the gateway
3. Reconciles `SubscriptionItem` records for PlanItems (adds, removes, or updates quantities)
4. Updates the local subscription record
5. Fires `SubscriptionUpdated`

## Proration strategies

The strategy is set globally in `config/subscribd.php` and can be overridden per-swap via the options array:

```php theme={null}
// Global default
'proration_strategy' => env('SUBSCRIBD_PRORATION', 'now'),
```

| Strategy  | Behaviour                                              |
| --------- | ------------------------------------------------------ |
| `now`     | Immediate prorated credit/charge (default)             |
| `renewal` | New price applies at next renewal; no immediate charge |
| `none`    | New price at next renewal; no credit                   |

```php theme={null}
// Override per-swap
app(SwapPlan::class)->execute($subscription, $newPlan, ['prorate' => 'renewal']);
```

For gateways that support native proration (Stripe), the strategy is forwarded to the gateway. For others, Subscribd's built-in `ProrationEngine` computes the adjustment and applies it via a charge or credit balance sync.

See [Proration](/advanced/proration) for a detailed breakdown with worked examples.

## Overriding PlanItem quantities on swap

Pass a `plan_items` map to set specific quantities for the new plan's items:

```php theme={null}
app(SwapPlan::class)->execute($subscription, $newPlan, [
    'plan_items' => [
        'extra_projects' => 5,
        'api_add_on'     => 2,
    ],
]);
```

**Reconciliation rules:**

* Items present on both the old and new plan: `price_snapshot` is updated; quantity uses the override if provided, otherwise the existing quantity is preserved.
* Items absent from the new plan: the `SubscriptionItem` is deleted.
* Items new on the new plan: quantity uses the override if provided, otherwise the PlanItem's `included_quantity`.
* The gateway base item (with `plan_item_key = null`) is never touched by reconciliation.

## Swap on a named subscription slot

```php theme={null}
$teamSub = $user->subscription('team');
$newPlan = Plan::where('key', 'team-enterprise')->firstOrFail();

app(SwapPlan::class)->execute($teamSub, $newPlan);
```

## Listening to swap events

`SwapPlan` fires `SubscriptionUpdated` on success:

```php theme={null}
use Pixelworxio\Subscribd\Events\SubscriptionUpdated;

Event::listen(SubscriptionUpdated::class, function (SubscriptionUpdated $event): void {
    $subscription = $event->subscription;
    $billable     = $subscription->billable;

    // Send a plan-change confirmation email
    Mail::to($billable->billingEmail())->send(new PlanChangedMail($subscription));
});
```

## Preventing swaps in your UI

Validate before calling `SwapPlan` to enforce business rules:

```php theme={null}
public function swap(Request $request): RedirectResponse
{
    $request->validate(['plan' => 'required|string']);

    $newPlan = Plan::where('key', $request->plan)->firstOrFail();
    $subscription = auth()->user()->subscription();

    // Prevent downgrade without confirmation
    if ($newPlan->amount < $subscription->plan->amount) {
        return redirect()->route('downgrade.confirm')
            ->with('new_plan', $newPlan->key);
    }

    app(SwapPlan::class)->execute($subscription, $newPlan);

    return redirect()->route('billing')->with('success', 'Plan updated.');
}
```

## Using the SubscriptionManager Livewire component

The `subscription-manager` component handles plan swapping in the UI automatically, including a confirmation modal and proration preview:

```blade theme={null}
<livewire:subscribd::subscriber.subscription-manager />
```

See [Livewire Components](/components/livewire) for props and customisation.

## Next steps

* **[Proration](/advanced/proration)** — Deep dive into all three proration strategies
* **[Canceling](/subscriptions/canceling)** — Cancel immediately or at period end
* **[Subscription Status](/subscriptions/status)** — Full status and date reference
