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

# Checkout SDK

> React SDK for embedding Lava's hosted checkout flow in your application

The `@lavapayments/checkout` package provides a React hook for embedding Lava's checkout flow as a full-screen iframe overlay. It handles the complete payment flow — phone verification, payment setup, and subscription creation.

## Installation

```bash theme={null}
npm install @lavapayments/checkout
```

**Requirements**: React 16.8+ (hooks support)

## How It Works

Checkout is a two-step flow:

1. **Your backend** creates a checkout session using the [Node.js SDK](/sdk/nodejs), which returns a `checkout_session_token`
2. **Your frontend** passes that token to `open()`, which renders the checkout as a full-screen iframe overlay

Your secret key is never exposed to the browser — session creation always happens server-side.

## Checkout Modes

The checkout mode is set when creating the session on your backend, not in the React hook. See [Checkout](/monetize/checkout) for full details on each mode.

| Mode            | Purpose                       | Required Params                   |
| --------------- | ----------------------------- | --------------------------------- |
| `subscription`  | Subscribe to a recurring plan | `plan_id`                         |
| `credit_bundle` | Buy a fixed credit pack       | `customer_id`, `credit_bundle_id` |

## useLavaCheckout

The SDK exports a single hook: `useLavaCheckout`. It returns an `open` function that launches the checkout iframe.

```typescript theme={null}
const { open } = useLavaCheckout({
  onSuccess: ({ checkoutSessionId, customerId }) => {
    // Checkout completed — customerId is the billing relationship ID.
    // Store it in your database linked to your user.
  },
  onCancel: ({ checkoutSessionId }) => {
    // User closed the checkout overlay without completing.
  },
  onError: ({ checkoutSessionId, error }) => {
    // Session token is invalid or malformed.
  },
});

// Pass the checkout_session_token created by your backend (see below).
open(checkoutSessionToken);
```

All callbacks are optional.

<Note>
  `onSuccess` is the easiest way to capture the `customerId`, but the user can close their browser before it fires. For reliable server-side capture, use the [`customer.created` webhook](/monetize/checkout#backend-webhooks-recommended). In production, use both: the callback for instant UI feedback, the webhook for backend persistence.
</Note>

### Behavior

* `open()` renders a **full-screen fixed iframe** (`z-index: 2147483647`) over your app
* The iframe is sandboxed with `allow-forms allow-scripts allow-same-origin`
* A `beforeunload` handler warns users if they try to navigate away mid-checkout
* The iframe is automatically removed when checkout completes or is cancelled

## Full Example

### 1. Backend — create a checkout session (Next.js route handler)

Use the [`@lavapayments/nodejs` SDK](/sdk/nodejs) to create a session. The SDK returns a `checkout_session_token` that you pass to your frontend.

```typescript theme={null}
import { Lava } from '@lavapayments/nodejs';

const lava = new Lava(); // reads LAVA_SECRET_KEY from env

export async function POST(request: Request) {
  const { plan_id } = await request.json();

  const session = await lava.checkoutSessions.create({
    checkout_mode: 'subscription',
    origin_url: 'https://yourapp.com',
    plan_id,
  });

  return Response.json({
    checkoutSessionToken: session.checkout_session_token,
  });
}
```

### 2. Frontend — open checkout with the session token

```tsx theme={null}
'use client'; // Next.js App Router

import { useLavaCheckout } from '@lavapayments/checkout';
import { useState } from 'react';

export function SubscribeButton({ planId }: { planId: string }) {
  const [loading, setLoading] = useState(false);

  const { open } = useLavaCheckout({
    onSuccess: ({ customerId }) => {
      // Store customerId in your database, linked to your user
      fetch('/api/save-customer', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ customerId }),
      });
    },
    onCancel: () => {
      // User closed checkout without completing
    },
  });

  async function handleClick() {
    setLoading(true);
    try {
      // Call your backend to create a checkout session
      const res = await fetch('/api/checkout', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ plan_id: planId }),
      });
      const { checkoutSessionToken } = await res.json();

      // Open the checkout iframe with the token from your backend
      open(checkoutSessionToken);
    } finally {
      setLoading(false);
    }
  }

  return (
    <button onClick={handleClick} disabled={loading}>
      {loading ? 'Loading...' : 'Subscribe'}
    </button>
  );
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Checkout Guide" icon="credit-card" href="/monetize/checkout">
    Detailed guide on checkout modes, completion handling, and webhooks
  </Card>

  <Card title="Node.js SDK" icon="node-js" href="/sdk/nodejs">
    Server-side SDK for creating checkout sessions and managing resources
  </Card>
</CardGroup>
