> ## 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. Crea un producto (pago único o suscripción). Los productos de suscripción deben tener un precio mínimo de **\$1** (o el equivalente en la moneda elegida); no se admiten importes inferiores a este mínimo.

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

Elige el flujo de integración que se adapte a tu caso de uso:

* **Checkout Sessions (recomendado)**: ideal para la mayoría de las integraciones. Crea una sesión en tu servidor y redirige a los clientes a un checkout seguro y alojado.
* **Overlay Checkout**: úsalo cuando necesites una experiencia dentro de la página que abra el checkout como una ventana modal superpuesta en tu sitio.
* **Inline Checkout**: inserta el checkout directamente en el diseño de tu página para ofrecer experiencias de checkout totalmente integradas y con tu marca.
* **Static Payment Links**: URL sin código, compartibles al instante, para recopilar pagos rápidamente.
* **Dynamic Payment Links**: enlaces creados mediante programación. Sin embargo, se recomiendan Checkout Sessions, ya que ofrecen mayor flexibilidad.
* **[Mobile Checkout SDKs](/developer-resources/mobile-integration)**: para aplicaciones nativas de Android, iOS, React Native y Flutter. Crea la sesión en tu servidor como se indicó anteriormente y, a continuación, pasa `checkout_url` al SDK.

<Info>
  Overlay Checkout e Inline Checkout solo funcionan en navegadores: insertan el checkout en una
  página web. Si estás creando una aplicación móvil nativa, crea la sesión de checkout en
  tu servidor y ábrela con los
  [Mobile Checkout SDKs](/developer-resources/mobile-integration).
</Info>

#### 1. Checkout Sessions

Usa Checkout Sessions para crear una experiencia de checkout segura y alojada para pagos únicos o suscripciones. Crea una sesión en tu servidor y, a continuación, redirige al cliente al `checkout_url` devuelto.

<Info>
  Las sesiones de checkout son válidas durante 24 horas de forma predeterminada. Si pasas <code>confirm=true</code>, las sesiones son válidas durante 15 minutos y se deben proporcionar todos los campos obligatorios.
</Info>

<Steps>
  <Step title="Create a checkout session">
    Elige tu SDK preferido o llama a la 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">
    Después de crear la sesión, redirige al `checkout_url` para iniciar el flujo alojado.

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

<Tip>
  Prefiere Checkout Sessions para comenzar a aceptar pagos de la forma más rápida y fiable. Para una personalización avanzada, consulta la <a href="/developer-resources/checkout-session">guía de Checkout Sessions</a> completa y la <a href="/api-reference/checkout-sessions/create">API Reference</a>.
</Tip>

#### 2. Overlay Checkout

Para disfrutar de una experiencia de checkout fluida dentro de la página, consulta nuestra integración de [Overlay Checkout](/developer-resources/overlay-checkout), que permite a los clientes completar los pagos sin salir de tu sitio web.

#### 3. Inline Checkout

Para ofrecer experiencias de checkout totalmente integradas e insertadas directamente en tu página, usa nuestra integración de [Inline Checkout](/developer-resources/inline-checkout). Esto te permite crear resúmenes de pedidos personalizados y controlar por completo el diseño del checkout, mientras Dodo Payments gestiona de forma segura la recopilación de pagos.

#### 4. Static Payment Links

Los enlaces de pago estáticos te permiten aceptar pagos rápidamente compartiendo una URL sencilla. Puedes personalizar la experiencia de checkout pasando query parameters para rellenar previamente los datos del cliente, controlar los campos del formulario y añadir metadata personalizada.

<Steps>
  <Step title="Construct your payment link">
    Comienza con la URL base y añade tu ID de producto:

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

  <Step title="Add core parameters">
    Incluye los query parameters esenciales:

    * <ParamField query="quantity" type="integer" default="1">Número de artículos que se comprarán.</ParamField>
    * <ParamField query="redirect_url" type="string" required>URL a la que se redirigirá después de completar el pago.</ParamField>

    <Note>
      La URL de redirección incluirá los datos del pago como query parameters, por ejemplo:<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 />
      Si el producto tiene habilitadas las claves de licencia, también se añadirá un parámetro `license_key` (separado por comas cuando haya varias claves):<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)">
    Añade campos del cliente o de facturación como query parameters para agilizar el checkout.

    <AccordionGroup>
      <Accordion title="Supported Customer Fields">
        * <ParamField query="fullName" type="string">Nombre completo del cliente (se ignora si se proporciona firstName o lastName).</ParamField>
        * <ParamField query="firstName" type="string">Nombre del cliente.</ParamField>
        * <ParamField query="lastName" type="string">Apellidos del cliente.</ParamField>
        * <ParamField query="email" type="string">Dirección de correo electrónico del cliente.</ParamField>
        * <ParamField query="country" type="string">País del cliente.</ParamField>
        * <ParamField query="addressLine" type="string">Dirección postal.</ParamField>
        * <ParamField query="city" type="string">Ciudad.</ParamField>
        * <ParamField query="state" type="string">Estado o provincia.</ParamField>
        * <ParamField query="zipCode" type="string">Código postal/ZIP.</ParamField>
        * <ParamField query="showDiscounts" type="boolean">true o false</ParamField>
      </Accordion>
    </AccordionGroup>
  </Step>

  <Step title="Control form fields (optional)">
    <Info>
      Puedes deshabilitar campos específicos para que sean de solo lectura para el cliente. Esto resulta útil cuando ya tienes los datos del cliente (por ejemplo, usuarios que han iniciado sesión).
    </Info>

    Para deshabilitar un campo, proporciona su valor y establece el indicador <code>disable...</code> correspondiente en <code>true</code>:

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

    <Tabs>
      <Tab title="Disable Flags Table">
        | Campo              | Indicador de deshabilitación | Parámetro obligatorio |
        | ------------------ | ---------------------------- | --------------------- |
        | Nombre completo    | `disableFullName`            | `fullName`            |
        | Nombre             | `disableFirstName`           | `firstName`           |
        | Apellidos          | `disableLastName`            | `lastName`            |
        | Correo electrónico | `disableEmail`               | `email`               |
        | País               | `disableCountry`             | `country`             |
        | Dirección          | `disableAddressLine`         | `addressLine`         |
        | Ciudad             | `disableCity`                | `city`                |
        | Estado             | `disableState`               | `state`               |
        | Código ZIP         | `disableZipCode`             | `zipCode`             |
      </Tab>
    </Tabs>

    <Tip>
      Deshabilitar campos ayuda a evitar cambios accidentales y garantiza la coherencia de los datos.
    </Tip>

    <Note>
      Establecer <code>showDiscounts=false</code> deshabilitará y ocultará la sección de descuentos del formulario de checkout. Úsalo si quieres impedir que los clientes introduzcan códigos de cupón o promocionales durante el checkout.
    </Note>
  </Step>

  <Step title="Add advanced controls (optional)">
    * <ParamField query="paymentCurrency" type="string">Especifica la moneda del pago. De forma predeterminada, usa la moneda del país de facturación.</ParamField>
    * <ParamField query="showCurrencySelector" type="boolean" default="true">Muestra u oculta el selector de moneda.</ParamField>
    * <ParamField query="paymentAmount" type="integer">Importe en centavos (solo para precios Pay What You Want).</ParamField>
    * <ParamField query="metadata_*" type="string">Campos de metadata personalizada (por ejemplo, <code>metadata\_orderId=123</code>).</ParamField>
  </Step>

  <Step title="Share the link">
    Envía el enlace de pago completado a tu cliente. Cuando lo visite, todos los query parameters se recopilarán y almacenarán con un ID de sesión. A continuación, la URL se simplificará para incluir únicamente el parámetro de sesión (por ejemplo, <code>?session=sess\_1a2b3c4d</code>). La información almacenada persiste al actualizar la página y está disponible durante todo el proceso de checkout.

    <Check>
      La experiencia de checkout del cliente ahora será más sencilla y personalizada según tus parámetros.
    </Check>
  </Step>
</Steps>

#### 4. Dynamic Payment Links

<Tip>
  Prefiere Checkout Sessions para la mayoría de los casos de uso, ya que ofrecen mayor flexibilidad y control.
</Tip>

Se crean mediante una llamada a la API o nuestro SDK con los datos del cliente. Este es un ejemplo:

Hay dos API para crear enlaces de pago dinámicos:

* API de enlaces de pago único [referencia de la API](/api-reference/payments/post-payments)
* API de enlaces de pago de suscripción [referencia de la API](/api-reference/subscriptions/post-subscriptions)

La siguiente guía explica cómo crear un enlace de pago único.

Para obtener instrucciones detalladas sobre la integración de suscripciones, consulta esta [guía de integración de suscripciones](/developer-resources/subscription-integration-guide).

<Info>Asegúrate de pasar `payment_link = true` para obtener el enlace de pago </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>Después de crear el enlace de pago, redirige a tus clientes para que completen el pago.</Info>

### Implementación de Webhooks

Configura un endpoint de API para recibir notificaciones de pago. Este es un ejemplo con 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
}
```

Nuestra implementación de webhook sigue la especificación de [Standard Webhooks](https://standardwebhooks.com/). Para consultar las definiciones de los tipos de webhook, revisa nuestra [guía de eventos de Webhook](/developer-resources/webhooks/intents/webhook-events-guide).

#### Eventos que debes escuchar

Activa `payload.type` y gestiona los eventos relevantes para un flujo de pago único. Como mínimo, escucha los siguientes:

| Tipo de evento       | Cuándo se activa                                                     | Qué hacer                                                                               |
| -------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
| `payment.succeeded`  | El pago se procesa correctamente.                                    | Completa el pedido: concede acceso, aprovisiona el producto y envía el recibo.          |
| `payment.failed`     | El intento de pago falla (tarjeta rechazada, error, etc.).           | Notifica al cliente y solicita que vuelva a intentarlo; no completes el pedido.         |
| `payment.processing` | El pago se acepta, pero aún se está procesando (métodos asíncronos). | Espera a `payment.succeeded`/`payment.failed` terminal; todavía no completes el pedido. |
| `payment.cancelled`  | El pago se cancela antes de completarse.                             | Libera cualquier estado retenido y marca el pedido como abandonado.                     |

<Tip>
  Completa siempre el pedido en `payment.succeeded` desde el webhook, **no mediante la redirección del navegador**: la redirección puede omitirse si el cliente cierra la pestaña, mientras que el webhook se reintenta hasta que se confirma su recepción.
</Tip>

Si vendes productos digitales con claves de licencia, gestiona también `license_key.created`. Para consultar la lista completa de eventos, incluidos los eventos de suscripción, entitlement, crédito, recuperación y dunning, consulta la [guía de eventos de Webhook](/developer-resources/webhooks/intents/webhook-events-guide).

Puedes consultar este proyecto con una implementación de demostración en [GitHub](https://github.com/dodopayments/dodo-checkout-demo) usando Next.js y TypeScript.

Puedes consultar la implementación activa [aquí](https://atlas.dodopayments.com/).

## Aspectos clave sobre Checkout y Currency

<Warning>
  Los importes **Dynamic (Pay-What-You-Want) están en la moneda base del producto**, no en una moneda local arbitraria, y la moneda base está limitada a **USD, INR, GBP y EUR**. Para cobrar un importe fijo en otra moneda (por ejemplo, PHP), no puedes pasarlo directamente: usa **Adaptive Pricing** (convierte el importe base según el tipo de cambio actual) o **Localized Pricing** (precio fijo por moneda, pero no compatible con Pay-What-You-Want).
</Warning>

<Tip>
  **Fija la moneda explícitamente.** Pasa `billing_currency` e `billing_address.country` en la sesión de checkout. Si los omites, la moneda y el país se detectan a partir de la IP del cliente (Adaptive Currency) y podrían no coincidir con lo que quieres cobrar.
</Tip>

<Info>
  **Las sesiones de checkout caducan en 24 horas** (15 minutos cuando se usa `confirm: true`), y cada `checkout_url` es de **un solo uso**: genera una sesión nueva para cada cliente y cada intento de pago en lugar de reutilizar un enlace.
</Info>

<Info>
  **Compra recurrente con un clic.** Para un cliente recurrente con un método de pago guardado, pasa `payment_method_id` junto con `confirm: true` para realizar el cobro al instante y omitir por completo la selección del método.
</Info>

## Referencia de API relacionada

<CardGroup cols={2}>
  <Card title="Create Checkout Session" icon="code" href="/api-reference/checkout-sessions/create">
    Referencia de API para crear sesiones de checkout seguras y alojadas para pagos únicos y suscripciones
  </Card>

  <Card title="Create Payment Link" icon="link" href="/api-reference/payments/post-payments">
    Referencia de API para crear enlaces de pago dinámicos mediante programación
  </Card>
</CardGroup>
