> ## 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.

# Integration Guide

> Create checkout sessions, payment links, and webhooks to accept payments with Dodo Payments. Start with an SDK or REST API.

<CardGroup cols={2}>
  <Card title="Checkout Sessions" icon="cart-shopping" href="#checkout-sessions">
    Create a secure, hosted checkout for one-time payments and subscriptions.
  </Card>

  <Card title="Payment Links" icon="link" href="#payment-links">
    Share a URL to collect payments without code.
  </Card>

  <Card title="Webhooks" icon="webhook" href="#webhooks">
    Listen for payment events and fulfill orders.
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/checkout-sessions/create">
    Full endpoint documentation and live testing.
  </Card>
</CardGroup>

## Prerequisites

Before you start, you need:

* A Dodo Payments account.
* At least one product. Create it under **Products** in the dashboard. A subscription product with a non-zero price must meet the [subscription minimum](/features/adaptive-currency#minimum-amounts) for the currency the customer pays in: \$1.00 for USD. Currencies other than USD, EUR, and GBP must also be worth at least \$1.00. A \$0 subscription is also supported.
* An API key. Create it under **Developer → API Keys** and store it in the `DODO_PAYMENTS_API_KEY` environment variable. Create the key in test mode while you build: the examples on this page use test mode, and a test mode key works only against test mode. See [Authentication](/api-reference/introduction#authentication).
* The SDK for your language. The Node.js SDK requires Node.js 20 or later, the Python SDK requires Python 3.9 or later, and the Go SDK requires Go 1.22 or later. The cURL examples need no SDK.

<CodeGroup>
  ```bash Node.js theme={null}
  npm install dodopayments
  ```

  ```bash Python theme={null}
  pip install dodopayments
  ```

  ```bash Go theme={null}
  go get github.com/dodopayments/dodopayments-go
  ```
</CodeGroup>

The [webhook example](#webhooks) also uses the `standardwebhooks` package. Install it with `npm install standardwebhooks`.

## Choose an Integration Path

| Path | Use it when | Guide |
| - | - | - |
| Checkout sessions | You want a hosted checkout created from your server. This is the recommended path for most integrations. | [Checkout Sessions](#checkout-sessions) |
| Overlay checkout | You want checkout to open as a modal on your web page. | [Overlay Checkout](/developer-resources/overlay-checkout) |
| Inline checkout | You want checkout embedded in your page layout, next to your own order summary. | [Inline Checkout](/developer-resources/inline-checkout) |
| Static payment links | You want a shareable URL with no code. | [Payment Links](#payment-links) |
| Mobile checkout SDKs | You're building a native Android, iOS, React Native, or Flutter app. | [Mobile Integration](/developer-resources/mobile-integration) |

Overlay and inline checkout run in a web page only. In a native mobile app, create the checkout session on your server and open its `checkout_url` with a mobile checkout SDK.

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

## Checkout Sessions

Create a secure, hosted checkout experience. You create a session on your server, then redirect the customer to the returned `checkout_url`.

<Warning>
  Each `checkout_url` works once and expires after 24 hours, or after 15 minutes when you pass `confirm: true`. With `confirm: true`, you must also provide every required field. Create a new session for each customer and each payment attempt.
</Warning>

### Create a Checkout Session

<Tabs>
  <Tab title="Node.js SDK">
    ```javascript expandable theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'test_mode', // defaults to 'live_mode'
    });

    const session = await client.checkoutSessions.create({
      product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
      customer: { email: 'customer@example.com', name: 'John Doe' },
      return_url: 'https://yourapp.com/checkout/success',
    });

    console.log(session.checkout_url);
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python expandable theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
        environment="test_mode",  # defaults to "live_mode"
    )

    session = client.checkout_sessions.create(
        product_cart=[{"product_id": "pdt_123", "quantity": 1}],
        customer={"email": "customer@example.com", "name": "John Doe"},
        return_url="https://yourapp.com/checkout/success",
    )

    print(session.checkout_url)
    ```
  </Tab>

  <Tab title="cURL">
    ```bash theme={null}
    curl -X POST https://test.dodopayments.com/checkouts \
      -H "Authorization: Bearer $DODO_PAYMENTS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "product_cart": [{"product_id": "pdt_123", "quantity": 1}],
        "customer": {"email": "customer@example.com", "name": "John Doe"},
        "return_url": "https://yourapp.com/checkout/success"
      }'
    ```
  </Tab>
</Tabs>

### Redirect to Checkout

After creating a session, redirect the customer to the `checkout_url`:

```javascript theme={null}
window.location.href = session.checkout_url;
```

<Tip>
  For advanced customization, see the full [Checkout Sessions](/developer-resources/checkout-session) guide and the [API Reference](/api-reference/checkout-sessions/create).
</Tip>

### Handle Errors

When a request fails, the API returns an HTTP status code and a JSON body with a `code` and a `message`. Branch your error handling on `code`, not on `message`. For every code, its cause, and how to resolve it, see [Error Codes](/api-reference/error-codes). A failed payment is reported separately: the payment's `status` is `failed`, its `error_code` gives the reason, and you receive a `payment.failed` [webhook](#webhooks). To decide whether to retry, see [Handle Payment Failures](/developer-resources/handle-payment-failures).

## Payment Links

A payment link is a URL that opens checkout for a product, so you can collect payments without writing code. Query parameters pre-fill customer details and control the checkout form. When a customer opens the link, checkout stores the parameters in a session and shortens the URL to a `session` parameter, so a page refresh keeps them.

### Static Payment Links

A static payment link is a URL you create once and share multiple times. The base URL is:

```text theme={null}
https://checkout.dodopayments.com/buy/{product_id}
```

Add query parameters to customize the checkout:

<ParamField query="quantity" type="integer" default="1">
  Number of items to purchase.
</ParamField>

<ParamField query="redirect_url" type="string" required>
  Payment links use `redirect_url`. The Checkout Sessions API uses `return_url` for the same purpose.

  URL to redirect to after payment. Dodo Payments appends the payment details as query parameters, for example `https://example.com/?payment_id=pay_ts2ySpzg07phGeBZqePbH&status=succeeded&email=customer%40example.com`. If the product issues license keys, a `license_key` parameter is also appended, with multiple keys separated by commas.
</ParamField>

<ParamField query="paymentCurrency" type="string">
  Specifies the payment currency. Defaults to the billing country's currency.
</ParamField>

<ParamField query="showCurrencySelector" type="boolean" default="true">
  Show or hide the currency selector.
</ParamField>

<ParamField query="showDiscounts" type="boolean" default="true">
  Show or hide the discounts section. Set to `false` to prevent customers from entering coupon codes.
</ParamField>

<ParamField query="paymentAmount" type="number">
  Fixes the amount charged, in major currency units, for example `12.5` for \$12.50. Works with Pay What You Want products only, and is ignored if it's below the product's minimum price.
</ParamField>

<Warning>
  `paymentAmount` uses major currency units (`12.5` is \$12.50). The Checkout Sessions API field `product_cart[].amount` uses the smallest currency unit (`1250` is \$12.50). See [Dynamic Pricing](/developer-resources/dynamic-pricing-checkout).
</Warning>

<ParamField query="metadata_*" type="string">
  Custom metadata fields, for example `metadata_orderId=123`.
</ParamField>

### Pre-fill Customer Information

Add customer fields as query parameters to streamline checkout:

<ParamField query="fullName" type="string">
  Customer's full name (ignored if firstName or lastName is provided).
</ParamField>

<ParamField query="firstName" type="string">
  Customer's first name.
</ParamField>

<ParamField query="lastName" type="string">
  Customer's last name.
</ParamField>

<ParamField query="email" type="string">
  Customer's email address.
</ParamField>

<ParamField query="country" type="string">
  Customer's country (ISO 3166-1 alpha-2 code).
</ParamField>

<ParamField query="addressLine" type="string">
  Street address.
</ParamField>

<ParamField query="city" type="string">
  City.
</ParamField>

<ParamField query="state" type="string">
  State or province.
</ParamField>

<ParamField query="zipCode" type="string">
  Postal or ZIP code.
</ParamField>

### Disable Form Fields

To prevent customers from changing pre-filled information, disable a field by providing its value and setting the corresponding `disable...` flag to `true`:

```text theme={null}
?email=alice@example.com&disableEmail=true
```

| Field | Disable Flag | Required Parameter |
| - | - | - |
| Full Name | `disableFullName` | `fullName` |
| First Name | `disableFirstName` | `firstName` |
| Last Name | `disableLastName` | `lastName` |
| Email | `disableEmail` | `email` |
| Country | `disableCountry` | `country` |
| Address Line | `disableAddressLine` | `addressLine` |
| City | `disableCity` | `city` |
| State | `disableState` | `state` |
| ZIP Code | `disableZipCode` | `zipCode` |

### Example Static Payment Link

```text theme={null}
https://checkout.dodopayments.com/buy/pdt_123?quantity=1&email=alice@example.com&disableEmail=true&redirect_url=https://example.com/success
```

<Tip>
  Disabling fields prevents accidental changes and ensures data consistency.
</Tip>

### Dynamic Payment Links (Deprecated)

<Warning>
  The `POST /payments` and `POST /subscriptions` endpoints are deprecated. Use [Checkout Sessions](/developer-resources/checkout-session) instead for new integrations.
</Warning>

For existing integrations using dynamic payment links, pass `payment_link: true` to [Create One-Time Payment](/api-reference/payments/post-payments) or [Create Subscription](/api-reference/subscriptions/post-subscriptions) to create a link. The examples below create a one-time payment link. For subscriptions, see the [Subscription Integration Guide](/developer-resources/subscription-integration-guide).

<Tabs>
  <Tab title="Node.js SDK">
    ```javascript expandable theme={null}
    import DodoPayments from 'dodopayments';

    const client = new DodoPayments({
      bearerToken: process.env.DODO_PAYMENTS_API_KEY,
      environment: 'test_mode',
    });

    const payment = await client.payments.create({
      payment_link: true,
      billing: {
        city: 'San Francisco',
        country: 'US',
        state: 'CA',
        street: '123 Main St',
        zipcode: '94102',
      },
      customer: { email: 'customer@example.com', name: 'John Doe' },
      product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
    });

    console.log(payment.payment_link);
    ```
  </Tab>

  <Tab title="Python SDK">
    ```python expandable theme={null}
    import os
    from dodopayments import DodoPayments

    client = DodoPayments(
        bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),
        environment="test_mode",
    )

    payment = client.payments.create(
        payment_link=True,
        billing={
            "city": "San Francisco",
            "country": "US",
            "state": "CA",
            "street": "123 Main St",
            "zipcode": "94102",
        },
        customer={"email": "customer@example.com", "name": "John Doe"},
        product_cart=[{"product_id": "pdt_123", "quantity": 1}],
    )

    print(payment.payment_link)
    ```
  </Tab>

  <Tab title="Go SDK">
    ```go expandable theme={null}
    package main

    import (
    	"context"
    	"fmt"

    	"github.com/dodopayments/dodopayments-go"
    	"github.com/dodopayments/dodopayments-go/option"
    )

    func main() {
    	// Reads DODO_PAYMENTS_API_KEY from the environment.
    	client := dodopayments.NewClient(option.WithEnvironmentTestMode())

    	payment, err := client.Payments.New(context.Background(), dodopayments.PaymentNewParams{
    		PaymentLink: dodopayments.F(true),
    		Billing: dodopayments.F(dodopayments.BillingAddressParam{
    			City:    dodopayments.F("San Francisco"),
    			Country: dodopayments.F(dodopayments.CountryCodeUs),
    			State:   dodopayments.F("CA"),
    			Street:  dodopayments.F("123 Main St"),
    			Zipcode: dodopayments.F("94102"),
    		}),
    		Customer: dodopayments.F[dodopayments.CustomerRequestUnionParam](dodopayments.NewCustomerParam{
    			Email: dodopayments.F("customer@example.com"),
    			Name:  dodopayments.F("John Doe"),
    		}),
    		ProductCart: dodopayments.F([]dodopayments.OneTimeProductCartItemParam{{
    			ProductID: dodopayments.F("pdt_123"),
    			Quantity:  dodopayments.F(int64(1)),
    		}}),
    	})
    	if err != nil {
    		panic(err.Error())
    	}

    	fmt.Println(payment.PaymentLink)
    }
    ```
  </Tab>
</Tabs>

## Webhooks

Webhooks tell your server when a payment succeeds or fails, so you can fulfill the order.

### Create a Webhook Endpoint

Go to **Developer → Webhooks** in the dashboard and add your endpoint URL. Copy the endpoint's signing secret into the `DODO_PAYMENTS_WEBHOOK_KEY` environment variable.

Here's an example using Next.js:

```typescript app/api/webhooks/dodo/route.ts expandable theme={null}
import { Webhook } from "standardwebhooks";

const webhook = new Webhook(process.env.DODO_PAYMENTS_WEBHOOK_KEY!);

export async function POST(request: Request) {
  const rawBody = await request.text();

  const webhookHeaders = {
    "webhook-id": request.headers.get("webhook-id") ?? "",
    "webhook-signature": request.headers.get("webhook-signature") ?? "",
    "webhook-timestamp": request.headers.get("webhook-timestamp") ?? "",
  };

  try {
    await webhook.verify(rawBody, webhookHeaders);
  } catch {
    return new Response("Invalid signature", { status: 400 });
  }

  const payload = JSON.parse(rawBody);

  switch (payload.type) {
    case "payment.succeeded":
      // Fulfill the order
      break;
    case "payment.failed":
      // Notify the customer
      break;
  }

  return new Response(null, { status: 200 });
}
```

Our webhook implementation follows the [Standard Webhooks](https://standardwebhooks.com/) specification.

### Events to Listen For

At minimum, listen for these events in a one-time payment flow:

| Event | When it fires | What to do |
| - | - | - |
| `payment.succeeded` | Payment is successfully processed. | Fulfill the order — grant access, provision the product, send the receipt. |
| `payment.failed` | Payment attempt fails (declined card, error, etc.). | Notify the customer and prompt a retry. |
| `payment.processing` | Payment is accepted but still being processed (async methods). | Wait for `payment.succeeded` or `payment.failed` before fulfilling. |
| `payment.cancelled` | Payment is cancelled before completion. | Release any held state and mark the order abandoned. |

<Tip>
  Always fulfill on `payment.succeeded` from the webhook, not on the browser redirect. The redirect can be missed if the customer closes the tab, whereas the webhook is retried until acknowledged.
</Tip>

If you sell products with license keys, also handle `license_key.created`. For the complete list of events, including subscription, entitlement, credit, recovery, and dunning events, see the [Webhook Event Guide](/developer-resources/webhooks/intents/webhook-events-guide).

For a complete Next.js and TypeScript example, see the [demo repository](https://github.com/dodopayments/dodo-checkout-demo) and its [live deployment](https://atlas.dodopayments.com/).

## Currency and Billing Address

To charge in a specific currency, pass `billing_currency` and `billing_address.country` when you create the checkout session. If you omit them, [Adaptive Currency](/features/adaptive-currency) picks the currency and country from the customer's IP address, which may not be the currency you intend to charge.

Pay What You Want amounts are in the product's base currency, which must be USD, GBP, or EUR. To collect a fixed amount in another currency, use [Adaptive Currency](/features/adaptive-currency), which converts your base price at live exchange rates, or [Localized Pricing](/features/localized-pricing), which sets a fixed price per currency. Localized Pricing doesn't work with Pay What You Want.

## One-Click Repeat Purchase

To charge a returning customer with a saved payment method, pass its `payment_method_id` with `confirm: true`. `payment_method_id` is accepted only when `confirm` is `true`, and you must also pass the existing customer's `customer_id`. Because `confirm` is `true`, you must also pass a complete `billing_address`, or only `country` and `zipcode` when `minimal_address` is `true`. The session charges the saved payment method directly, so it returns no `checkout_url`. Use [webhooks](#webhooks) to learn whether the payment succeeded.

```typescript theme={null}
const session = await client.checkoutSessions.create({
  product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
  customer: { customer_id: 'cus_123' },
  payment_method_id: 'pm_123',
  billing_address: {
    country: 'US',
    state: 'CA',
    city: 'San Francisco',
    street: '123 Main St',
    zipcode: '94102',
  },
  confirm: true,
});
```

## Related Pages

<CardGroup cols={2}>
  <Card title="Checkout Sessions" icon="cart-shopping" href="/developer-resources/checkout-session">
    Full guide with advanced customization options.
  </Card>

  <Card title="Overlay Checkout" icon="layer-group" href="/developer-resources/overlay-checkout">
    Embed checkout as a modal overlay on your page.
  </Card>

  <Card title="Inline Checkout" icon="credit-card" href="/developer-resources/inline-checkout">
    Embed checkout directly in your page layout.
  </Card>

  <Card title="Subscription Integration" icon="repeat" href="/developer-resources/subscription-integration-guide">
    Set up recurring billing.
  </Card>

  <Card title="Webhook Event Guide" icon="webhook" href="/developer-resources/webhooks/intents/webhook-events-guide">
    Complete list of all webhook events.
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/checkout-sessions/create">
    Checkout Sessions API documentation.
  </Card>
</CardGroup>


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