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

# One-time Payments Integration Guide

> This guide will help you integrate the Dodo Payments API into your website.

## Prerequisites

To integrate the Dodo Payments API, you'll need:

* A Dodo Payments merchant account
* API Credentials (API key and webhook secret key) from dashboard

## Dashboard Setup

1. Navigate to the [Dodo Payments Dashboard](https://app.dodopayments.com/)

2. Create a product (one-time payment or subscription)

3. Generate your API key:
   * Go to Developer > API
   * [Detailed Guide](/api-reference/introduction#api-key-generation)
   * Copy the API key the in env named DODO\_PAYMENTS\_API\_KEY

4. Configure webhooks:
   * Go to Developer > Webhooks
   * Create a webhook URL for payment notifications
   * Copy the webhook secret key in env

## Integration

### Payment Links

Choose the integration path that fits your use case:

* **Checkout Sessions (recommended)**: Best for most integrations. Create a session on your server and redirect customers to a secure, hosted checkout.
* **Overlay Checkout**: Use when you need an in-page experience that opens checkout as a modal overlay on your site.
* **Inline Checkout**: Embed checkout directly into your page layout for fully integrated, branded checkout experiences.
* **Static Payment Links**: No-code, instantly shareable URLs for quick payment collection.
* **Dynamic Payment Links**: Programmatically created links. However, Checkout Sessions are recommended and provide more flexibility.

#### 1. Checkout Sessions

Use Checkout Sessions to create a secure, hosted checkout experience for one-time payments or subscriptions. You create a session on your server, then redirect the customer to the returned `checkout_url`.

<Info>
  Checkout sessions are valid for 24 hours by default. If you pass <code>confirm=true</code>, sessions are valid for 15 minutes and all required fields must be provided.
</Info>

<Steps>
  <Step title="Create a checkout session">
    Choose your preferred SDK or call the REST API.

    <Tabs>
      <Tab title="Node.js SDK">
        ```javascript 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: 'prod_123', quantity: 1 }],
          customer: { email: 'customer@example.com', name: 'John Doe' },
          return_url: 'https://yourapp.com/checkout/success',
        });
        ```
      </Tab>

      <Tab title="Python SDK">
        ```python 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": "prod_123", "quantity": 1}],
            customer={"email": "customer@example.com", "name": "John Doe"},
            return_url="https://yourapp.com/checkout/success",
        )
        ```
      </Tab>

      <Tab title="REST API">
        ```javascript theme={null}
        const response = await fetch('https://test.dodopayments.com/checkouts', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${process.env.DODO_PAYMENTS_API_KEY}`,
          },
          body: JSON.stringify({
            product_cart: [{ product_id: 'prod_123', quantity: 1 }],
            customer: { email: 'customer@example.com', name: 'John Doe' },
            return_url: 'https://yourapp.com/checkout/success',
          }),
        });
        const session = await response.json();
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Redirect customer to checkout">
    After session creation, redirect to the `checkout_url` to start the hosted flow.

    ```javascript theme={null}
    // Example in a browser context
    window.location.href = session.checkout_url;
    ```
  </Step>
</Steps>

<Tip>
  Prefer Checkout Sessions for the fastest, most reliable way to start taking payments. For advanced customization, see the full <a href="/developer-resources/checkout-session">Checkout Sessions guide</a> and the <a href="/api-reference/checkout-sessions/create">API Reference</a>.
</Tip>

#### 2. Overlay Checkout

For a seamless in-page checkout experience, explore our [Overlay Checkout](/developer-resources/overlay-checkout) integration that allows customers to complete payments without leaving your website.

#### 3. Inline Checkout

For fully integrated checkout experiences embedded directly in your page, use our [Inline Checkout](/developer-resources/inline-checkout) integration. This allows you to build custom order summaries and have complete control over the checkout layout while Dodo Payments securely handles payment collection.

#### 4. Static Payment Links

Static payment links let you quickly accept payments by sharing a simple URL. You can customize the checkout experience by passing query parameters to pre-fill customer details, control form fields, and add custom metadata.

<Steps>
  <Step title="Construct your payment link">
    Start with the base URL and append your product ID:

    ```text theme={null}
    https://checkout.dodopayments.com/buy/{productid}
    ```
  </Step>

  <Step title="Add core parameters">
    Include essential query parameters:

    * <ParamField query="quantity" type="integer" default="1">Number of items to purchase.</ParamField>
    * <ParamField query="redirect_url" type="string" required>URL to redirect after payment completion.</ParamField>

    <Note>
      The redirect URL will include payment details as query parameters, for example:<br />
      <code>[https://example.com/?payment\_id=pay\_ts2ySpzg07phGeBZqePbH\&status=succeeded\&email=customer%40example.com](https://example.com/?payment_id=pay_ts2ySpzg07phGeBZqePbH\&status=succeeded\&email=customer%40example.com)</code><br /><br />
      If the product has license keys enabled, a `license_key` parameter is also appended (comma-separated for multiple keys):<br />
      <code>[https://example.com/?payment\_id=pay\_xxx\&status=succeeded\&license\_key=LK-001\&email=customer%40example.com](https://example.com/?payment_id=pay_xxx\&status=succeeded\&license_key=LK-001\&email=customer%40example.com)</code>
    </Note>
  </Step>

  <Step title="Pre-fill customer information (optional)">
    Add customer or billing fields as query parameters to streamline checkout.

    <AccordionGroup>
      <Accordion title="Supported Customer Fields">
        * <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.</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/ZIP code.</ParamField>
        * <ParamField query="showDiscounts" type="boolean">true or false</ParamField>
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Control form fields (optional)">
    <Info>
      You can disable specific fields to make them read-only for the customer. This is useful when you already have the customer's details (e.g., logged-in users).
    </Info>

    To disable a field, provide its value and set the corresponding <code>disable...</code> flag to <code>true</code>:

    <CodeGroup>
      ```text Example theme={null}
      &email=alice@example.com&disableEmail=true
      ```
    </CodeGroup>

    <Tabs>
      <Tab title="Disable Flags Table">
        | 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`          |
      </Tab>
    </Tabs>

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

    <Note>
      Setting <code>showDiscounts=false</code> will disable and hide the discounts section in the checkout form. Use this if you want to prevent customers from entering coupon or promo codes during checkout.
    </Note>
  </Step>

  <Step title="Add advanced controls (optional)">
    * <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="paymentAmount" type="integer">Amount in cents (for Pay What You Want pricing only).</ParamField>
    * <ParamField query="metadata_*" type="string">Custom metadata fields (e.g., <code>metadata\_orderId=123</code>).</ParamField>
  </Step>

  <Step title="Share the link">
    Send the completed payment link to your customer. When they visit, all query parameters are collected and stored with a session ID. The URL is then simplified to include just the session parameter (e.g., <code>?session=sess\_1a2b3c4d</code>). The stored information persists through page refreshes and is accessible throughout the checkout process.

    <Check>
      The customer's checkout experience is now streamlined and personalized based on your parameters.
    </Check>
  </Step>
</Steps>

#### 4. Dynamic Payment Links

<Tip>
  Prefer Checkout Sessions for most use cases, they offer more flexibility and control.
</Tip>

Created via API call or our SDK with customer details. Here's an example:

There are two APIs for creating dynamic payment links:

* One-time Payment Link API [API reference](/api-reference/payments/post-payments)
* Subscription Payment Link API [API reference](/api-reference/subscriptions/post-subscriptions)

The guide below is for one-time payment link creation.

For detailed instructions on integrating subscriptions, refer to this [Subscription Integration Guide](/developer-resources/subscription-integration-guide).

<Info>Make sure you are passing `payment_link = true` to get payment link </Info>

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

    const client = new DodoPayments({
    bearerToken: process.env['DODO_PAYMENTS_API_KEY'], // This is the default and can be omitted
    environment: 'test_mode', // defaults to 'live_mode'
    });

    async function main() {
    const payment = await client.payments.create({
    payment_link: true,
    billing: { city: 'city', country: 'AF', state: 'state', street: 'street', zipcode: 0 },
    customer: { email: 'email@email.com', name: 'name' },
    product_cart: [{ product_id: 'product_id', quantity: 0 }],
    });

    console.log(payment.payment_id);
    }

    main();
    ```
  </Tab>

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

    client = DodoPayments(
    bearer_token=os.environ.get("DODO_PAYMENTS_API_KEY"),  # This is the default and can be omitted (if using same name `DODO_PAYMENTS_API_KEY`)
    environment="test_mode",  # defaults to "live_mode"
    )
    payment = client.payments.create(
    payment_link=True,
    billing={
        "city": "city",
        "country": "AF",
        "state": "state",
        "street": "street",
        "zipcode": 0,
    },
    customer={
        "email": "email@email.com",
        "name": "name",
    },
    product_cart=[{
        "product_id": "product_id",
        "quantity": 0,
    }],
    )
    print(payment.payment_link)
    ```
  </Tab>

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

    import (
    "context"
    "fmt"

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

    func main() {
    client := dodopayments.NewClient(
    option.WithBearerToken("My Bearer Token"), // defaults to os.LookupEnv("DODO_PAYMENTS_API_KEY")
    )
    payment, err := client.Payments.New(context.TODO(), dodopayments.PaymentNewParams{
    PaymentLink: dodopayments.F(true),
    Billing: dodopayments.F(dodopayments.PaymentNewParamsBilling{
      City: dodopayments.F("city"),
      Country: dodopayments.F(dodopayments.CountryCodeAf),
      State: dodopayments.F("state"),
      Street: dodopayments.F("street"),
      Zipcode: dodopayments.F(int64(0)),
    }),
    Customer: dodopayments.F(dodopayments.PaymentNewParamsCustomer{
      Email: dodopayments.F("email"),
      Name: dodopayments.F("name"),
    }),
    ProductCart: dodopayments.F([]dodopayments.PaymentNewParamsProductCart{dodopayments.PaymentNewParamsProductCart{
      ProductID: dodopayments.F("product_id"),
      Quantity: dodopayments.F(int64(0)),
    }}),
    })
    if err != nil {
    panic(err.Error())
    }
    fmt.Printf("%+v\n", payment.PaymentLink)
    }

    ```
  </Tab>

  <Tab title="Api Reference">
    ```javascript theme={null}
    import { NextRequest, NextResponse } from "next/server";      

    export async function POST(request: NextRequest) {
    try {
    const body = await request.json();
    const { formData, cartItems } = paymentRequestSchema.parse(body);

    const response = await fetch(`${process.env.NEXT_PUBLIC_DODO_TEST_API}/payments`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.DODO_API_KEY}`, // Replace with your API secret key generated from the Dodo Payments Dashboard
      },
      body: JSON.stringify({
        billing: {
          city: formData.city,
          country: formData.country,
          state: formData.state,
          street: formData.addressLine,
          zipcode: parseInt(formData.zipCode),
        },
        customer: {
          email: formData.email,
          name: `${formData.firstName} ${formData.lastName}`,
          phone_number: formData.phoneNumber || undefined,
        },
        payment_link: true,
        product_cart: cartItems.map((id) => ({
          product_id: id,
          quantity: 1,
        })),
        return_url: process.env.NEXT_PUBLIC_RETURN_URL,
      }),
    });

    if (!response.ok) {
      const errorData = await response.json().catch(() => null);
      return NextResponse.json(
        { error: "Payment link creation failed", details: errorData },
        { status: response.status }
      );
    }

    const data = await response.json();
    return NextResponse.json({ paymentLink: data.payment_link });
    } catch (err) {
    console.error("Payment error:", err);
    return NextResponse.json(
      {
        error: err instanceof Error ? err.message : "An unknown error occurred",
      },
      { status: 500 }
    );
    }
    }
    ```
  </Tab>
</Tabs>

<Info>After creating the payment link, redirect your customers to complete their payment.</Info>

### Implementing Webhooks

Set up an API endpoint to receive payment notifications. Here's an example using Next.js:

```javascript theme={null}
import { Webhook } from "standardwebhooks";
import { headers } from "next/headers";
import { WebhookPayload } from "@/types/api-types";

const webhook = new Webhook(process.env.DODO_WEBHOOK_KEY!); // Replace with your secret key generated from the Dodo Payments Dashboard

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

  const webhookHeaders = {
    "webhook-id": headersList.get("webhook-id") || "",
    "webhook-signature": headersList.get("webhook-signature") || "",
    "webhook-timestamp": headersList.get("webhook-timestamp") || "",
  };

  await webhook.verify(rawBody, webhookHeaders);
  const payload = JSON.parse(rawBody) as WebhookPayload;
  
  // Process the payload according to your business logic
}
```

Our webhook implementation follows the [Standard Webhooks](https://standardwebhooks.com/) specification. For webhook type definitions, refer to our [Webhook Event Guide](/developer-resources/webhooks/intents/webhook-events-guide).

You can refer to this project with demo implementation on [GitHub](https://github.com/dodopayments/dodo-checkout-demo) using Next.js and TypeScript.

You can check out the live implementation [here](https://atlas.dodopayments.com/).

## Related API Reference

<CardGroup cols={2}>
  <Card title="Create Checkout Session" icon="code" href="/api-reference/checkout-sessions/create">
    API reference for creating secure, hosted checkout sessions for one-time payments and subscriptions
  </Card>

  <Card title="Create Payment Link" icon="link" href="/api-reference/payments/post-payments">
    API reference for creating dynamic payment links programmatically
  </Card>
</CardGroup>
