# Source: https://www.better-auth.com/llms.txt/docs/plugins/dodopayments.md

# Dodo Payments

Better Auth Plugin for Dodo Payments



[Dodo Payments](https://dodopayments.com) is a global Merchant-of-Record platform that lets AI, SaaS and digital businesses sell in 150+ countries without touching tax, fraud, or compliance. A single, developer-friendly API powers checkout, billing, and payouts so you can launch worldwide in minutes.

<Callout>
  This plugin is maintained by the Dodo Payments team. For bugs, issues or feature requests,
  please visit the [Dodo Payments GitHub repo](https://github.com/dodopayments/dodo-adapters).
</Callout>

<Card href="https://discord.gg/bYqAp4ayYh" title="Get support on Dodo Payments' Discord">
  Have questions? Our team is available on Discord to assist you anytime.
</Card>

## Features

* Automatic customer creation on sign-up
* Type-safe checkout flows with product slug mapping
* Self-service customer portal
* Metered usage ingestion and recent usage reporting
* Real-time webhook event processing with signature verification

<Card href="https://app.dodopayments.com" title="Get started with Dodo Payments">
  You need a Dodo Payments account and API keys to use this integration.
</Card>

## Installation

<Steps>
  <Step title="Install dependencies">
    Run the following command in your project root:

    ```bash
    npm install @dodopayments/better-auth dodopayments better-auth zod
    ```
  </Step>

  <Step title="Configure environment variables">
    Add these to your `.env` file:

    ```txt
    DODO_PAYMENTS_API_KEY=your_api_key_here
    DODO_PAYMENTS_WEBHOOK_SECRET=your_webhook_secret_here
    ```
  </Step>

  <Step title="Set up server-side integration">
    Create or update `src/lib/auth.ts`:

    ```typescript
    import { betterAuth } from "better-auth";
    import {
      dodopayments,
      checkout,
      portal,
      webhooks,
      usage,
    } from "@dodopayments/better-auth";
    import DodoPayments from "dodopayments";

    export const dodoPayments = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY!,
      environment: "test_mode"
    });

    export const auth = betterAuth({
      plugins: [
        dodopayments({
          client: dodoPayments,
          createCustomerOnSignUp: true,
          use: [
            checkout({
              products: [
                {
                  productId: "pdt_xxxxxxxxxxxxxxxxxxxxx",
                  slug: "premium-plan",
                },
              ],
              successUrl: "/dashboard/success",
              authenticatedUsersOnly: true,
            }),
            portal(),
            webhooks({
              webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET!,
              onPayload: async (payload) => {
                console.log("Received webhook:", payload.event_type);
              },
            }),
            usage(),
          ],
        }),
      ],
    });
    ```

    <Card>
      Set `environment` to `live_mode` for production.
    </Card>
  </Step>

  <Step title="Set up client-side integration">
    Create or update `src/lib/auth-client.ts`:

    ```typescript
    import { dodopaymentsClient } from "@dodopayments/better-auth";

    export const authClient = createAuthClient({
      baseURL: process.env.BETTER_AUTH_URL || "http://localhost:3000",
      plugins: [dodopaymentsClient()],
    });
    ```
  </Step>
</Steps>

## Usage

### Creating a Checkout Session (preferred)

<Callout type="info">
  Use <code>authClient.dodopayments.checkoutSession</code> for new integrations.
  It maps one-to-one with Dodo Payments' Create Checkout Session API and honors
  the product slugs you configured on the server.
</Callout>

#### Using a configured slug

```typescript
const { data: session, error } = await authClient.dodopayments.checkoutSession({
  slug: "premium-plan",
  referenceId: "order_123",
});

if (session) {
  window.location.href = session.url;
}
```

#### Using a product cart

```typescript
const { data: session, error } = await authClient.dodopayments.checkoutSession({
  product_cart: [
    {
      product_id: "pdt_xxxxxxxxxxxxxxxxxxxxx",
      quantity: 1,
    },
  ],
  referenceId: "order_123",
});

if (session) {
  window.location.href = session.url;
}
```

<Callout type="info">
  Checkout Sessions automatically pull the customer's email and name from their
  Better Auth session. Override them (or supply billing details upfront) by
  passing <code>customer</code> or <code>billing</code> in the request body when
  necessary.
</Callout>

<Callout>
  The redirect URL comes from the <code>successUrl</code> configured in the
  server-side checkout plugin, so you do not need to send a <code>return\_url</code>
  from the client.
</Callout>

### Legacy Checkout (deprecated)

<Callout type="warn">
  The <code>authClient.dodopayments.checkout</code> helper remains for backward
  compatibility only. Prefer <code>checkoutSession</code> for new builds.
</Callout>

```typescript
const { data: checkout, error } = await authClient.dodopayments.checkout({
  slug: "premium-plan",
  customer: {
    email: "customer@example.com",
    name: "John Doe",
  },
  billing: {
    city: "San Francisco",
    country: "US",
    state: "CA",
    street: "123 Market St",
    zipcode: "94103",
  },
  referenceId: "order_123",
});

if (checkout) {
  window.location.href = checkout.url;
}
```

### Accessing the Customer Portal

```typescript
const { data: customerPortal, error } = await authClient.dodopayments.customer.portal();
if (customerPortal && customerPortal.redirect) {
  window.location.href = customerPortal.url;
}
```

### Listing Customer Data

```typescript
// Get subscriptions
const { data: subscriptions, error } =
  await authClient.dodopayments.customer.subscriptions.list({
    query: {
      limit: 10,
      page: 1,
      active: true,
    },
  });

// Get payment history
const { data: payments, error } = await authClient.dodopayments.customer.payments.list({
  query: {
    limit: 10,
    page: 1,
    status: "succeeded",
  },
});
```

### Tracking Metered Usage

Enable the <code>usage()</code> plugin (shown in the server setup) to ingest
metered events for the signed-in, email-verified customer and expose recent
usage history.

```typescript
const { error: ingestError } = await authClient.dodopayments.usage.ingest({
  event_id: crypto.randomUUID(),
  event_name: "api_request",
  metadata: {
    route: "/reports",
    method: "GET",
  },
  timestamp: new Date(),
});

if (ingestError) {
  console.error("Failed to record usage", ingestError);
}

const { data: usage, error: usageError } =
  await authClient.dodopayments.usage.meters.list({
    query: {
      page_size: 20,
      meter_id: "mtr_yourMeterId",
    },
  });

if (usage?.items) {
  usage.items.forEach((event) => {
    console.log(event.event_name, event.timestamp, event.metadata);
  });
}
```

<Callout type="info">
  Usage timestamps older than one hour or more than five minutes in the future
  are rejected.
</Callout>

<Callout type="info">
  Omit <code>meter\_id</code> to return all meters tied to the customer's active
  subscriptions.
</Callout>

### Webhooks

<Card>
  The webhooks plugin processes real-time payment events from Dodo Payments with secure signature verification. The default endpoint is `/api/auth/dodopayments/webhooks`.
</Card>

<Steps>
  <Step title="Generate and set webhook secret">
    Generate a webhook secret for your endpoint URL (e.g., `https://your-domain.com/api/auth/dodopayments/webhooks`) in the Dodo Payments Dashboard and set it in your .env file:

    ```txt
    DODO_PAYMENTS_WEBHOOK_SECRET=your_webhook_secret_here
    ```
  </Step>

  <Step title="Handle webhook events">
    Example handler:

    ```typescript
    webhooks({
      webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_SECRET!,
      onPayload: async (payload) => {
        console.log("Received webhook:", payload.event_type);
      },
    });
    ```
  </Step>
</Steps>

## Configuration Reference

### Plugin Options

* **client** (required): DodoPayments client instance
* **createCustomerOnSignUp** (optional): Auto-create customers on user signup
* **use** (required): Array of plugins to enable (checkout, portal, usage, webhooks)

### Checkout Plugin Options

* **products**: Array of products or async function returning products
* **successUrl**: URL to redirect after successful payment
* **authenticatedUsersOnly**: Require user authentication (default: false)

If you encounter any issues, please refer to the [Dodo Payments documentation](https://docs.dodopayments.com) for troubleshooting steps.