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

# Switch Plan on an Open Checkout

> Change the plan or price of a client session while the checkout is mounted, then remount so the customer sees the new price.

Use this flow when the customer picks a different plan on your page while the checkout is already mounted — for example, switching from a monthly to a yearly plan on a paywall.

The checkout reads the session — amount, plan, and available payment methods — once, when it mounts. Updating the session on your server doesn't change a checkout that is already on screen, and [`checkout.update()`](/sdk-reference/web-sdk/customization/behavior#update-configuration-at-runtime) can't change it either: it updates only display settings such as `locale` and `theme`. To show the new price, update the session and then remount the checkout.

<Warning>
  If you skip the remount, the checkout keeps showing the previous price. Apple Pay and Google Pay open their payment sheets with that previous amount, so the customer may approve a price that differs from the updated session.
</Warning>

## Steps

1. **Pause payments.** Call `checkout.setPaymentsEnabled(false)` so the customer can't start a payment while the session is changing.
2. **Update the session on your server.** Call [`PATCH /client-session/{id}`](/api-reference/v2.0.0/client-session/update-a-client-session) with the new `plan`. The session ID stays the same.
3. **Remount the checkout.** Call `await checkout.unmount()`, then `mount()` again with the same `clientToken`. The checkout loads the updated session and shows the new price.

Remounting clears the form, so anything the customer has typed — such as card details — is lost.

## Update the Session

Send only the objects you want to change. Each top-level object you send — `plan`, `customer`, `subscription`, `payment`, `options` — **replaces** that object in the session as a whole. Objects you don't send stay as they are.

```bash cURL theme={"system"}
curl -X PATCH https://api.paynext.com/client-session/cs_d3b07384-d9a5-4d16-a5b1-3fa3d9b0b123 \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "X-API-Version: 2.0.0" \
  -H "Content-Type: application/json" \
  -d '{
    "plan": { "id": "plan_8b9c0d1e-2f3a-4b5c-6d7e-8f9a0b1c2d3e" }
  }'
```

The response is the updated session, in the same format as the create response.

<Note>
  Because `plan` is replaced as a whole, a custom `plan.price` you set earlier is dropped unless you send it again. A custom `price` is supported for one-off plans only.
</Note>

## Remount the Checkout

Call your server endpoint from the client, then remount with the same token:

```typescript theme={"system"}
import { PayNextCheckout, type PayNextConfig } from '@paynext/sdk'

const config: PayNextConfig = {
  clientToken: 'cs_d3b07384-d9a5-4d16-a5b1-3fa3d9b0b123',
  environment: 'sandbox',
  // ... other checkout config options
}

let checkout = new PayNextCheckout()
await checkout.mount('paynext-checkout', config)

async function switchPlan(planId: string) {
  // 1. Block payments while the session changes.
  checkout.setPaymentsEnabled(false)

  try {
    // 2. Your server calls PATCH /client-session/{id} with the new plan.
    const response = await fetch('/api/switch-plan', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ sessionId: config.clientToken, planId }),
    })
    if (!response.ok) throw new Error('Failed to update the session')
  } catch (error) {
    // The session is unchanged — re-enable payments on the current plan.
    checkout.setPaymentsEnabled(true)
    throw error
  }

  // 3. Remount so the checkout reads the updated session.
  await checkout.unmount()
  checkout = new PayNextCheckout()
  await checkout.mount('paynext-checkout', config)
}
```

The remount takes `paymentsEnabled` from `config`, so payments work again after it — unless your config sets `paymentsEnabled: false` to gate them behind your own UI.

<Tip>
  Instead of updating the session, you can also create a new session for the new plan and mount the checkout with its ID. Updating keeps the same session ID, so you don't need to replace the token you stored.
</Tip>

## Related

* [Update Configuration at Runtime](/sdk-reference/web-sdk/customization/behavior#update-configuration-at-runtime) — what `checkout.update()` can change without a remount
* [Handle Expired Sessions](/sdk-reference/use-cases/handle-expired-sessions) — create a new session when the current one expires


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.