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

# Adaptador Fastify

> Aprende a integrar Dodo Payments con tu proyecto Fastify App Router utilizando nuestro adaptador NextJS. Cubre el proceso de pago, el portal del cliente, webhooks y la configuración de un entorno seguro.

<CardGroup cols={2}>
  <Card title="Checkout Handler" icon="cart-shopping" href="#checkout-route-handler">
    Integra Dodo Payments checkout en tu app Fastify.
  </Card>

  <Card title="Customer Portal" icon="user" href="#customer-portal-route-handler">
    Permite que los clientes gestionen sus suscripciones y datos.
  </Card>

  <Card title="Webhooks" icon="bell" href="#webhook-route-handler">
    Recibe y procesa eventos webhook de Dodo Payments.
  </Card>
</CardGroup>

## Instalación

<Steps>
  <Step title="Install the package">
    Ejecuta el siguiente comando en la raíz de tu proyecto:

    ```bash theme={null}
    npm install @dodopayments/fastify
    ```
  </Step>

  <Step title="Set up environment variables">
    Crea un archivo <code>.env</code> en la raíz de tu proyecto:

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

    <Warning>
      Nunca subas tu <code>.env</code> ni secretos al control de versiones.
    </Warning>
  </Step>
</Steps>

## Ejemplos de Manejadores de Rutas

<Info>
  Todos los ejemplos asumen que estás usando el Fastify App Router.
</Info>

<Tabs>
  <Tab title="Checkout Handler">
    <Info>
      Usa este controlador para integrar Dodo Payments checkout en tu app Fastify. Admite flujos de pago estáticos (GET), dinámicos (POST) y de sesión (POST).
    </Info>

    <CodeGroup>
      ```typescript Fastify Route Handler expandable theme={null}
        // route.ts
        import { Checkout } from '@dodopayments/fastify';
        import Fastify from 'fastify'

        const fastify = Fastify({})
        const checkoutGet = Checkout({
            bearerToken: process.env.DODO_PAYMENTS_API_KEY,
            environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
            returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
            type: 'static'
        });

        const checkoutPost = Checkout({
            bearerToken: process.env.DODO_PAYMENTS_API_KEY,
            environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
            returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
            type: 'dynamic'
        });

        const checkoutSession = Checkout({
            bearerToken: process.env.DODO_PAYMENTS_API_KEY,
            environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
            returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
            type: 'session'
        });

        fastify.get('/api/checkout', checkoutGet.getHandler);
        fastify.post('/api/checkout', checkoutPost.postHandler);
        fastify.post('/api/checkout-session', checkoutSession.postHandler);
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Static Checkout Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/checkout?productId=pdt_fqJhl7pxKWiLhwQR042rh' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Dynamic Checkout Curl example expandable theme={null}
      curl --request POST \
      --url https://example.com/api/checkout \
      --header 'Content-Type: application/json' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test \
      --data '{
      "billing": {
        "city": "Texas",
        "country": "US",
        "state": "Texas",
        "street": "56, hhh",
        "zipcode": "560000"
      },
      "customer": {
        "email": "test@example.com",
        	"name": "test"
      },
      "metadata": {},
      "payment_link": true,
        "product_id": "pdt_QMDuvLkbVzCRWRQjLNcs",
        "quantity": 1,
        "billing_currency": "USD",
        "discount_codes": ["IKHZ23M9GQ"],
        "return_url": "https://example.com",
        "trial_period_days": 10
      }'
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Checkout Session Curl example expandable theme={null}
      curl --request POST \
      --url https://example.com/api/checkout-session \
      --header 'Content-Type: application/json' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test \
      --data '{
      "product_cart": [
        {
          "product_id": "pdt_QMDuvLkbVzCRWRQjLNcs",
          "quantity": 1
        }
      ],
      "customer": {
        "email": "test@example.com",
        "name": "test"
      },
      "return_url": "https://example.com/success"
      }'
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Customer Portal Handler">
    <Info>
      Usa este controlador para permitir que los clientes gestionen sus suscripciones y datos a través del portal de clientes de Dodo Payments.
    </Info>

    <CodeGroup>
      ```typescript Fastify Route Handler expandable theme={null}
      // route.ts
      import { CustomerPortal } from "@dodopayments/fastify";
      import Fastify from 'fastify'

      const fastify = Fastify({})
      const customerPortalHandler = CustomerPortal({
          bearerToken: process.env.DODO_PAYMENTS_API_KEY,
          environment: process.env.DODO_PAYMENTS_ENVIRONMENT
      });
      fastify.get('/api/customer-portal', customerPortalHandler);
      ```
    </CodeGroup>

    <CodeGroup>
      ```curl Customer Portal Curl example expandable theme={null}
      curl --request GET \
      --url 'https://example.com/api/customer-portal?customer_id=cus_9VuW4K7O3GHwasENg31m&send_email=true' \
      --header 'User-Agent: insomnia/11.2.0' \
      --cookie mode=test
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Webhook Handler">
    <Info>
      Usa este controlador para recibir y procesar eventos webhook de Dodo Payments de forma segura en tu app Fastify.
    </Info>

    <CodeGroup>
      ```typescript Fastify Route Handler expandable theme={null}
      // route.ts
      import Fastify from 'fastify'
      import { Webhooks } from '@dodopayments/fastify'

      const fastify = Fastify({})
      fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
          done(null, body)
      })

      fastify.post('/api/webhooks', Webhooks({
      webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
      onPayload: async (payload) => {
          // Handle Payload Here
          console.log(payload)
      }
      }));

      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Manejador de Ruta de Checkout

<Info>
  Dodo Payments admite tres tipos de flujos de pago para integrar pagos en tu sitio web; este adaptador soporta todos los flujos de pago.
</Info>

* **Enlaces de Pago Estáticos:** URLs instantáneamente compartibles para la recolección de pagos rápida y sin código.
* **Enlaces de Pago Dinámicos:** Genera programáticamente enlaces de pago con detalles personalizados utilizando la API o SDKs.
* **Sesiones de Checkout:** Crea experiencias de checkout seguras y personalizables con carritos de productos preconfigurados y detalles del cliente.

<AccordionGroup>
  <Accordion title="Static Checkout (GET)">
    ### Supported Query Parameters

    <ParamField query="productId" type="string" required>
      Identificador del producto (p. ej., <code>?productId=pdt\_nZuwz45WAs64n3l07zpQR</code>).
    </ParamField>

    <ParamField query="quantity" type="integer">
      Cantidad del producto.
    </ParamField>

    <ParamField query="fullName" type="string">
      Nombre completo del cliente.
    </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">
      Línea de dirección del cliente.
    </ParamField>

    <ParamField query="city" type="string">
      Ciudad del cliente.
    </ParamField>

    <ParamField query="state" type="string">
      Estado/provincia del cliente.
    </ParamField>

    <ParamField query="zipCode" type="string">
      Código postal del cliente.
    </ParamField>

    <ParamField query="disableFullName" type="boolean">
      Desactivar el campo de nombre completo.
    </ParamField>

    <ParamField query="disableFirstName" type="boolean">
      Desactivar el campo de nombre.
    </ParamField>

    <ParamField query="disableLastName" type="boolean">
      Desactivar el campo de apellidos.
    </ParamField>

    <ParamField query="disableEmail" type="boolean">
      Desactivar el campo de correo electrónico.
    </ParamField>

    <ParamField query="disableCountry" type="boolean">
      Desactivar el campo de país.
    </ParamField>

    <ParamField query="disableAddressLine" type="boolean">
      Desactivar el campo de dirección.
    </ParamField>

    <ParamField query="disableCity" type="boolean">
      Desactivar el campo de ciudad.
    </ParamField>

    <ParamField query="disableState" type="boolean">
      Desactivar el campo de estado.
    </ParamField>

    <ParamField query="disableZipCode" type="boolean">
      Desactivar el campo de código postal.
    </ParamField>

    <ParamField query="paymentCurrency" type="string">
      Especifica la moneda de pago (p. ej., <code>USD</code>).
    </ParamField>

    <ParamField query="showCurrencySelector" type="boolean">
      Mostrar selector de moneda.
    </ParamField>

    <ParamField query="paymentAmount" type="integer">
      Especifica el monto del pago (p. ej., <code>1000</code> para \$10.00).
    </ParamField>

    <ParamField query="showDiscounts" type="boolean">
      Mostrar campos de descuento.
    </ParamField>

    <ParamField query="metadata_*" type="string">
      Cualquier parámetro de consulta que comience con <code>metadata\_</code> se pasará como metadatos.
    </ParamField>

    <Warning>
      Si falta <code>productId</code>, el controlador devuelve una respuesta 400. Los parámetros de consulta no válidos también generan una respuesta 400.
    </Warning>

    ### Formato de Respuesta

    El checkout estático devuelve una respuesta JSON con la URL de checkout:

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/..."
    }
    ```
  </Accordion>

  <Accordion title="Dynamic Checkout (POST)">
    * Envía los parámetros como un cuerpo JSON en una solicitud POST.
    * Admite pagos únicos y recurrentes.
    * Para la lista completa de campos admitidos en el cuerpo POST, consulta:
      * [Request body for a One Time Payment Product](https://docs.dodopayments.com/api-reference/payments/post-payments)
      * [Request body for a Subscription Product](https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions)

    ### Formato de Respuesta

    El checkout dinámico devuelve una respuesta JSON con la URL de checkout:

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/..."
    }
    ```
  </Accordion>

  <Accordion title="Checkout Sessions (POST)">
    Las sesiones de checkout ofrecen una experiencia de pago más segura y alojada que maneja el flujo completo de pago tanto para compras únicas como suscripciones con control total de personalización.

    Consulta la [Guía de Integración de Sesiones de Checkout](https://docs.dodopayments.com/developer-resources/checkout-session) para más detalles y una lista completa de campos soportados.

    ### Formato de Respuesta

    Las sesiones de checkout devuelven una respuesta JSON con la URL de checkout:

    ```json theme={null}
    {
      "checkout_url": "https://checkout.dodopayments.com/session/..."
    }
    ```
  </Accordion>
</AccordionGroup>

## Manejador de Ruta del Portal del Cliente

El Manejador de Ruta del Portal del Cliente te permite integrar sin problemas el portal del cliente de Dodo Payments en tu aplicación Fastify.

### Parámetros de consulta

<ParamField query="customer_id" type="string" required>
  El identificador del cliente para la sesión del portal (p. ej., <code>?customer\_id=cus\_123</code>).
</ParamField>

<ParamField query="send_email" type="boolean">
  Si se establece en <code>true</code>, envía un correo electrónico al cliente con el enlace del portal.
</ParamField>

<Warning>
  Devuelve 400 si falta <code>customer\_id</code>.
</Warning>

## Manejador de Ruta de Webhook

* **Método:** Solo se admiten solicitudes POST. Otros métodos devuelven 405.
* **Verificación de Firma:** Verifica la firma del webhook utilizando <code>webhookKey</code>. Devuelve 401 si la verificación falla.
* **Validación de Payload:** Validado con Zod. Devuelve 400 para payloads inválidos.
* **Manejo de Errores:**
  * 401: Firma inválida
  * 400: Payload inválido
  * 500: Error interno durante la verificación
* **Enrutamiento de Eventos:** Llama al manejador de eventos apropiado según el tipo de payload.

### Controladores de eventos webhook compatibles

<CodeGroup>
  ```typescript Typescript expandable theme={null}
  onPayload?: (payload: WebhookPayload) => Promise<void>;
  onPaymentSucceeded?: (payload: WebhookPayload) => Promise<void>;
  onPaymentFailed?: (payload: WebhookPayload) => Promise<void>;
  onPaymentProcessing?: (payload: WebhookPayload) => Promise<void>;
  onPaymentCancelled?: (payload: WebhookPayload) => Promise<void>;
  onRefundSucceeded?: (payload: WebhookPayload) => Promise<void>;
  onRefundFailed?: (payload: WebhookPayload) => Promise<void>;
  onDisputeOpened?: (payload: WebhookPayload) => Promise<void>;
  onDisputeExpired?: (payload: WebhookPayload) => Promise<void>;
  onDisputeAccepted?: (payload: WebhookPayload) => Promise<void>;
  onDisputeCancelled?: (payload: WebhookPayload) => Promise<void>;
  onDisputeChallenged?: (payload: WebhookPayload) => Promise<void>;
  onDisputeWon?: (payload: WebhookPayload) => Promise<void>;
  onDisputeLost?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionActive?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionOnHold?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionRenewed?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionPlanChanged?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionCancelled?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionFailed?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionExpired?: (payload: WebhookPayload) => Promise<void>;
  onSubscriptionUpdated?: (payload: WebhookPayload) => Promise<void>;
  onLicenseKeyCreated?: (payload: WebhookPayload) => Promise<void>;
  onAbandonedCheckoutDetected?: (payload: WebhookPayload) => Promise<void>;
  onAbandonedCheckoutRecovered?: (payload: WebhookPayload) => Promise<void>;
  onDunningStarted?: (payload: WebhookPayload) => Promise<void>;
  onDunningRecovered?: (payload: WebhookPayload) => Promise<void>;
  onCreditAdded?: (payload: WebhookPayload) => Promise<void>;
  onCreditDeducted?: (payload: WebhookPayload) => Promise<void>;
  onCreditExpired?: (payload: WebhookPayload) => Promise<void>;
  onCreditRolledOver?: (payload: WebhookPayload) => Promise<void>;
  onCreditRolloverForfeited?: (payload: WebhookPayload) => Promise<void>;
  onCreditOverageCharged?: (payload: WebhookPayload) => Promise<void>;
  onCreditManualAdjustment?: (payload: WebhookPayload) => Promise<void>;
  onCreditBalanceLow?: (payload: WebhookPayload) => Promise<void>;
  ```
</CodeGroup>

***

## Solicitud para LLM

```
Eres un asistente experto en desarrollo Fastify. Tu tarea es guiar a un usuario a través de la integración del adaptador @dodopayments/fastify en su proyecto Fastify existente.

El adaptador @dodopayments/fastify proporciona manejadores de ruta para las funcionalidades de Checkout, Portal del Cliente y Webhook de Dodo Payments, diseñados para integrarse directamente en una aplicación Fastify.

Primero, instala el paquete necesario. Usa el gestor de paquetes apropiado para el proyecto del usuario (npm, yarn o bun):

npm install @dodopayments/fastify

---

Aquí te mostramos cómo deberías estructurar tu respuesta:

1. Pregunta al usuario qué funcionalidades desea integrar.

"¿Qué partes del adaptador @dodopayments/fastify te gustaría integrar en tu proyecto? Puedes elegir una o más de las siguientes:

- Manejador de Ruta de Checkout (para manejar checkouts de productos)
- Manejador de Ruta del Portal del Cliente (para gestionar suscripciones/detalles del cliente)
- Manejador de Ruta de Webhook (para recibir eventos de webhook de Dodo Payments)
- Todo (integrar los tres)"

---

2. Según la selección del usuario, proporciona pasos de integración detallados para cada funcionalidad elegida.

---

**Si se selecciona el Manejador de Ruta de Checkout:**

**Propósito**: Este manejador redirige a los usuarios a la página de checkout de Dodo Payments.

**Integración**:
Crea dos rutas en tu aplicación Fastify: una para checkout estático (GET) y otra para checkout dinámico (POST).

import { Checkout } from '@dodopayments/fastify';
import Fastify from 'fastify'

const fastify = Fastify({})
const checkoutGet = Checkout({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
    environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
    returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
    type: 'static'
});

const checkoutPost = Checkout({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
    environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
    returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
    type: 'dynamic'
});

const checkoutSession = Checkout({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
    environment: process.env.DODO_PAYMENTS_ENVIRONMENT,
    returnUrl: process.env.DODO_PAYMENTS_RETURN_URL,
    type: 'session'
});

fastify.get('/api/checkout', checkoutGet.getHandler);
fastify.post('/api/checkout', checkoutPost.postHandler);
fastify.post('/api/checkout-session', checkoutSession.postHandler);

Opciones de Configuración:

    bearerToken: Tu clave API de Dodo Payments (se recomienda almacenarla en la variable de entorno DODO_PAYMENTS_API_KEY).

    returnUrl (opcional): URL para redirigir al usuario después de un checkout exitoso.

    environment: "test_mode" o "live_mode"

    type: "static" (GET), "dynamic" (POST) o "session" (POST)

GET (checkout estático) espera parámetros de consulta:

    productId (requerido)

    quantity, campos del cliente (fullName, email, etc.) y metadatos (metadata_*) son opcionales.

    Devuelve: {"checkout_url": "https://checkout.dodopayments.com/..."}

POST (checkout dinámico) espera un cuerpo JSON con detalles de pago (único o suscripción). Devuelve: {"checkout_url": "https://checkout.dodopayments.com/..."}. Consulta la documentación para el esquema completo de POST:

    Pagos únicos: https://docs.dodopayments.com/api-reference/payments/post-payments

    Suscripciones: https://docs.dodopayments.com/api-reference/subscriptions/post-subscriptions

POST (sesiones de checkout) - (Recomendado) Una experiencia de checkout más personalizable. Devuelve JSON con checkout_url: Los parámetros se envían como un cuerpo JSON. Soporta tanto pagos únicos como recurrentes. Devuelve: {"checkout_url": "https://checkout.dodopayments.com/session/..."}. Para una lista completa de campos soportados, consulta:

    Guía de Integración de Sesiones de Checkout: https://docs.dodopayments.com/developer-resources/checkout-session

Si se selecciona el Manejador de Ruta del Portal del Cliente:

Propósito: Esta ruta permite a los clientes gestionar sus suscripciones a través del portal de Dodo Payments.

Integración:

import { CustomerPortal } from "@dodopayments/fastify";
import Fastify from 'fastify'

const fastify = Fastify({})
const customerPortalHandler = CustomerPortal({
    bearerToken: process.env.DODO_PAYMENTS_API_KEY,
    environment: process.env.DODO_PAYMENTS_ENVIRONMENT
});
fastify.get('/api/customer-portal', customerPortalHandler);

Parámetros de Consulta:

    customer_id (requerido): por ejemplo, ?customer_id=cus_123

    send_email (opcional): si es verdadero, se envía al cliente el enlace del portal

Devuelve 400 si falta customer_id.

Si se selecciona el Manejador de Ruta de Webhook:

Propósito: Procesa eventos de webhook entrantes de Dodo Payments para activar eventos en tu aplicación.

Integración:

import Fastify from 'fastify'
import { Webhooks } from '@dodopayments/fastify'

const fastify = Fastify({})
fastify.addContentTypeParser('application/json', { parseAs: 'string' }, function (req, body, done) {
    done(null, body)
})

fastify.post('/api/webhooks', Webhooks({
  webhookKey: process.env.DODO_PAYMENTS_WEBHOOK_KEY,
  onPayload: async (payload) => {
    // Manejar Payload Aquí
    console.log(payload)
  }
}));

Características:

    Solo se permite el método POST — otros devuelven 405

    La verificación de firma se realiza utilizando webhookKey. Devuelve 401 si es inválido.

    Validación de payload basada en Zod. Devuelve 400 si el esquema es inválido.

    Todos los manejadores son funciones asíncronas.

Manejadores de Eventos de Webhook Soportados:

Puedes pasar cualquiera de los siguientes manejadores:

    onPayload

    onPaymentSucceeded

    onPaymentFailed

    onPaymentProcessing

    onPaymentCancelled

    onRefundSucceeded

    onRefundFailed

    onDisputeOpened, onDisputeExpired, onDisputeAccepted, onDisputeCancelled, onDisputeChallenged, onDisputeWon, onDisputeLost

    onSubscriptionActive, onSubscriptionOnHold, onSubscriptionRenewed, onSubscriptionPlanChanged, onSubscriptionCancelled, onSubscriptionFailed, onSubscriptionExpired, onSubscriptionUpdated

    onLicenseKeyCreated

    onAbandonedCheckoutDetected, onAbandonedCheckoutRecovered

    onDunningStarted, onDunningRecovered

    onCreditAdded, onCreditDeducted, onCreditExpired, onCreditRolledOver, onCreditRolloverForfeited, onCreditOverageCharged, onCreditManualAdjustment, onCreditBalanceLow

Configuración de Variables de Entorno:

Asegúrate de definir estas variables de entorno en tu proyecto:

DODO_PAYMENTS_API_KEY=your-api-key
DODO_PAYMENTS_RETURN_URL=https://yourapp.com/success
DODO_PAYMENTS_WEBHOOK_KEY=your-webhook-secret
DODO_PAYMENTS_ENVIRONMENT="test_mode" or "live_mode""

Utiliza estas dentro de tu código como:

process.env.DODO_PAYMENTS_API_KEY
process.env.DODO_PAYMENTS_WEBHOOK_KEY

Nota de Seguridad: NO subas secretos al control de versiones. Usa archivos .env localmente y gestores de secretos en entornos de despliegue (por ejemplo, AWS, Vercel, Heroku, etc.).
```
