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

# Framework Adaptors Overview

> Add Dodo Payments checkout, customer portal, and webhook handlers to Next.js, Nuxt, Express, Hono, SvelteKit, Convex, Better Auth, and other frameworks.

Framework adaptors add Dodo Payments checkout, Customer Portal, and webhook handlers to your app using your framework's request and response types. Each adaptor is an npm package in the `@dodopayments` scope. The packages use `@dodopayments/core` for checkout validation, Dodo Payments API requests, webhook payload validation, and typed event callbacks.

<Tip>
  Each adaptor accepts your API key and environment in its handler configuration. Keep those values in environment variables and pass them when you create the handler.
</Tip>

To have a coding agent add an adaptor for you, install the [Agent Plugin](/developer-resources/build-with-ai-coding-agents).

## Available Framework Adaptors

Choose the adaptor that matches your framework:

<CardGroup cols={2}>
  <Card title="Next.js" icon="react" href="/developer-resources/nextjs-adaptor">
    Route handlers for checkout, the customer portal, and webhooks in the App Router.
  </Card>

  <Card title="Nuxt" icon="vuejs" href="/developer-resources/nuxt-adaptor">
    Server route handlers for Nuxt 3, a full-stack Vue framework.
  </Card>

  <Card title="Express" icon="node-js" href="/developer-resources/express-adaptor">
    Request handlers for Express 5 routes in Node.js apps.
  </Card>

  <Card title="Fastify" icon="bolt" href="/developer-resources/fastify-adaptor">
    Route handlers for Fastify 5, a Node.js framework with a plugin architecture.
  </Card>

  <Card title="Hono" icon="cloud" href="/developer-resources/hono-adaptor">
    Route handlers for Hono 4, a web framework for Cloudflare Workers, other edge runtimes, and Node.js.
  </Card>

  <Card title="Astro" icon="star" href="/developer-resources/astro-adaptor">
    Server endpoint handlers for Astro 4 and 5, a content-focused web framework.
  </Card>

  <Card title="SvelteKit" icon="code" href="/developer-resources/sveltekit-adaptor">
    Server route handlers for SvelteKit 2, a full-stack Svelte framework.
  </Card>

  <Card title="Remix" icon="react" href="/developer-resources/remix-adaptor">
    Loader and action handlers for Remix 2, a full-stack React framework.
  </Card>

  <Card title="TanStack Start" icon="chart-line" href="/developer-resources/tanstack-adaptor">
    Server route handlers for TanStack Start, a type-safe full-stack React framework.
  </Card>

  <Card title="Better Auth" icon="shield" href="/developer-resources/better-auth-adaptor">
    A plugin for the Better Auth authentication framework that adds checkout, portal, usage, and webhook endpoints, and can create a customer at sign-up.
  </Card>

  <Card title="Convex" icon="database" href="/developer-resources/convex-component">
    A component for the Convex backend with checkout and customer portal actions and a webhook HTTP action.
  </Card>

  <Card title="Bun" icon="bolt" href="/developer-resources/bun-adaptor">
    Native `Bun.serve()` handlers for checkout, the customer portal, and webhooks.
  </Card>
</CardGroup>

## Core Features

The route handler adaptors share these capabilities:

| Feature | Description |
| - | - |
| **Checkout Handler** | Static, dynamic, and checkout session flows |
| **Customer Portal** | A handler that opens the Customer Portal, where customers manage subscriptions and billing details |
| **Webhook Handler** | Signature verification and typed callbacks for individual event types |
| **Environment Config** | API credentials and environment passed through typed handler options |
| **Type Safety** | TypeScript types for handler options and webhook payloads |

The Better Auth plugin and the Convex component provide the same capabilities through their own APIs. See their pages for the differences.

## Quick Start

Set up a route handler adaptor in three steps.

<Steps>
  <Step title="Install the Adaptor">
    Install the package for your framework:

    <Tabs>
      <Tab title="Next.js">
        ```bash theme={null}
        npm install @dodopayments/nextjs
        ```
      </Tab>

      <Tab title="Nuxt">
        ```bash theme={null}
        npm install @dodopayments/nuxt
        ```
      </Tab>

      <Tab title="Express">
        ```bash theme={null}
        npm install @dodopayments/express
        ```
      </Tab>

      <Tab title="Hono">
        ```bash theme={null}
        npm install @dodopayments/hono
        ```
      </Tab>

      <Tab title="Astro">
        ```bash theme={null}
        npm install @dodopayments/astro
        ```
      </Tab>

      <Tab title="SvelteKit">
        ```bash theme={null}
        npm install @dodopayments/sveltekit
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configure Environment Variables">
    Add your Dodo Payments credentials to your environment. Create the API key under **Developer → API Keys** and the webhook secret under **Developer → Webhooks** in the dashboard. A test mode API key works only with `test_mode`.

    ```env theme={null}
    DODO_PAYMENTS_API_KEY=your-api-key
    DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
    DODO_PAYMENTS_RETURN_URL=https://yourdomain.com/checkout/success
    DODO_PAYMENTS_ENVIRONMENT="test_mode" # or "live_mode"
    ```

    If you don't pass an environment, the adaptor uses `live_mode`.

    <Warning>
      Never commit your `.env` file or secrets to version control.
    </Warning>
  </Step>

  <Step title="Create Route Handlers">
    Create a checkout route. The customer portal and webhook routes follow the same pattern:

    <Tabs>
      <Tab title="Next.js">
        ```typescript expandable theme={null}
        // app/checkout/route.ts
        import { Checkout } from "@dodopayments/nextjs";

        export const GET = Checkout({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT as "test_mode" | "live_mode",
        });
        ```
      </Tab>

      <Tab title="Express">
        ```typescript expandable theme={null}
        import express from 'express';
        import { checkoutHandler } from '@dodopayments/express';

        const app = express();

        app.get('/api/checkout', checkoutHandler({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT as "test_mode" | "live_mode",
        }));
        ```
      </Tab>

      <Tab title="Hono">
        ```typescript expandable theme={null}
        import { Hono } from "hono";
        import { Checkout } from "@dodopayments/hono";

        const app = new Hono();

        app.get('/checkout', Checkout({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT as "test_mode" | "live_mode",
        }));
        ```
      </Tab>
    </Tabs>

    <Check>
      Your checkout route now returns a checkout URL. Each adaptor's page covers the customer portal and webhook handlers and lists every option.
    </Check>
  </Step>
</Steps>

## Checkout Flow Types

The route handler adaptors support three checkout flow types. The Better Auth plugin supports checkout sessions and a deprecated dynamic checkout. The Convex component supports checkout sessions only.

<AccordionGroup>
  <Accordion title="Static Checkout (GET)">
    Static checkout returns a payment link for one product, so you can share it or redirect to it. Pass the product ID as a query parameter:

    ```text theme={null}
    /api/checkout?productId=pdt_xxx&quantity=1
    ```

    The handler checks that the product exists and returns the link as `checkout_url`. Optional query parameters pre-fill customer details and control the checkout form.
  </Accordion>

  <Accordion title="Dynamic Checkout (POST)">
    Dynamic checkout creates a payment link from a JSON body with custom details. `billing` and `customer` are required:

    ```json theme={null}
    {
      "product_id": "pdt_xxx",
      "quantity": 1,
      "billing": {
        "city": "San Francisco",
        "country": "US",
        "state": "CA",
        "street": "123 Main St",
        "zipcode": "94102"
      },
      "customer": {
        "email": "customer@example.com",
        "name": "John Doe"
      }
    }
    ```

    It supports both one-time payments and subscriptions. It calls the deprecated `POST /payments` or `POST /subscriptions` endpoint, depending on the product type, so use checkout sessions for new integrations.
  </Accordion>

  <Accordion title="Checkout Sessions (POST)">
    Checkout sessions are the recommended flow. They offer the most options and accept a cart with several products:

    ```json theme={null}
    {
      "product_cart": [
        { "product_id": "pdt_xxx", "quantity": 1 },
        { "product_id": "pdt_yyy", "quantity": 2 }
      ],
      "customer": {
        "email": "customer@example.com"
      }
    }
    ```

    For every supported field, see the [Checkout Sessions Guide](/developer-resources/checkout-session).
  </Accordion>
</AccordionGroup>

## Webhook Event Handling

Every route handler adaptor exports a `Webhooks` handler. It takes your webhook secret and optional typed callbacks for individual event types. `onPayload` runs for every event:

```typescript expandable theme={null}
Webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY!,
  onPayload: async (payload) => {
    // Handle any webhook event
  },
  onPaymentSucceeded: async (payload) => {
    // Handle successful payments
  },
  onSubscriptionActive: async (payload) => {
    // Handle new subscriptions
  },
  // ...more than 40 optional event callbacks in total
});
```

The callbacks cover payment, refund, dispute, subscription, license key, abandoned checkout, dunning, credit, entitlement grant, and payout events. The Better Auth plugin accepts the same callbacks in `webhooks()`, and the Convex component accepts them in `createDodoWebhookHandler`.

<Info>
  Each webhook handler verifies the `webhook-id`, `webhook-signature`, and `webhook-timestamp` headers with the Standard Webhooks library, then validates the payload with Zod schemas. Invalid requests are rejected with an error status before your callbacks run.
</Info>

## Choosing the Right Adaptor

| Framework | Best For | Runtime |
| - | - | - |
| **Next.js** | Full-stack React apps with App Router | Node.js, Edge |
| **Nuxt** | Full-stack Vue.js applications | Node.js |
| **Express** | REST APIs and traditional Node.js apps | Node.js |
| **Fastify** | High-performance APIs | Node.js |
| **Hono** | Edge deployments, Cloudflare Workers | Edge, Node.js |
| **Astro** | Content sites with server endpoints | Node.js, Edge |
| **SvelteKit** | Full-stack Svelte applications | Node.js |
| **Remix** | Full-stack React with nested routing | Node.js |
| **TanStack Start** | Type-safe full-stack React | Node.js |
| **Better Auth** | Apps already using Better Auth | Various |
| **Convex** | Apps using Convex for backend | Convex Runtime |
| **Bun** | Native Bun server applications | Bun |

## Getting Help

For help with a framework adaptor:

* **Discord**: Ask in the [community server](https://discord.gg/bYqAp4ayYh).
* **Email**: Contact [support@dodopayments.com](mailto:support@dodopayments.com).
* **GitHub**: Open an issue in the [dodo-adapters repository](https://github.com/dodopayments/dodo-adapters), which holds every adaptor.
* **Documentation**: See the [API reference](/api-reference/introduction).


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