Saltar al contenido principal

Descripción general

Las suscripciones bajo demanda te permiten autorizar el método de pago de un cliente una vez y luego cobrar montos variables cuando lo necesites, en lugar de en un horario fijo. Esta función está disponible para todas las cuentas; no se requiere aprobación. Utiliza esta guía para:
  • Crear una suscripción bajo demanda (autorizar un mandato con un precio inicial opcional)
  • Activar cargos posteriores con montos personalizados
  • Rastrear resultados utilizando webhooks
Para una configuración general de suscripción, consulta la Guía de Integración de Suscripciones.

Requisitos previos

  • Cuenta de comerciante de Dodo Payments y clave API
  • Secreto de webhook configurado y un endpoint para recibir eventos
  • Un producto de suscripción en tu catálogo
Si deseas que el cliente apruebe el mandato mediante el pago alojado, establece payment_link: true y facilita un return_url.

Cómo funciona bajo demanda

  1. Creas una suscripción con el objeto on_demand para autorizar un método de pago y, opcionalmente, cobrar un cargo inicial.
  2. Más adelante, creas cargos contra esa suscripción con importes personalizados usando el endpoint dedicado de cargos.
  3. Escuchas webhooks (p. ej., payment.succeeded, payment.failed) para actualizar tu sistema.

Crear una suscripción bajo demanda

Endpoint: POST /checkouts Campos clave de la solicitud (cuerpo):
Consúltalos en Crear Sesión de Pago

Crear una suscripción bajo demanda

import DodoPayments from 'dodopayments';

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

async function main() {
  const subscription = await client.checkoutSessions.create({
    product_cart: [{ product_id: 'pdt_123', quantity: 1 }],
    billing_address:  { city: 'SF', country: 'US', state: 'CA', street: '1 Market St', zipcode: '94105' },
    customer: { customer_id: 'cus_123' },
    return_url: 'https://example.com/billing/success',
    subscription_data: {
        on_demand: {
            mandate_only: true // set false to collect an initial charge
            // product_price: 1000, // optional: charge $10.00 now if mandate_only is false
            // product_currency: 'USD',
            // product_description: 'Custom initial charge',
            // adaptive_currency_fees_inclusive: false,
        }
    }
  });

  console.log(subscription.checkout_url);
}

main().catch(console.error);
Success
{
  "session_id": "cks_123",
  "checkout_url": "https://test.checkout.dodopayments.com/session/cks123"
}

Cargar una suscripción bajo demanda

Después de que se autorice el mandato, crea cargos según sea necesario. Endpoint: POST /subscriptions/{subscription_id}/charge Campos clave de la solicitud (cuerpo):
product_price
integer
requerido
Importe a cobrar (en la unidad monetaria más pequeña). Ejemplo: para cobrar $25.00, pasa 2500.
product_currency
string
Anulación opcional de moneda para el cargo.
product_description
string
Anulación opcional de descripción para este cargo.
adaptive_currency_fees_inclusive
boolean
Si es verdadero, incluye tarifas de moneda adaptativa dentro de product_price. Si es falso, las tarifas se agregan encima.
metadata
object
Metadatos adicionales para el pago. Si se omite, se utilizan los metadatos de la suscripción.
import DodoPayments from 'dodopayments';

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

async function chargeNow(subscriptionId) {
  const res = await client.subscriptions.charge(subscriptionId, { product_price: 2500 });
  console.log(res.payment_id);
}

chargeNow('sub_123').catch(console.error);
Success
{ "payment_id": "pay_abc123" }
Cobrar una suscripción que no es bajo demanda puede fallar. Asegúrate de que la suscripción tenga on_demand: true en sus detalles antes de cobrar.

Manejo de cargos fallidos

Cuando un cargo contra una suscripción bajo demanda falla, decides qué ocurre a continuación. A diferencia de las suscripciones programadas — donde una renovación fallida detiene la facturación automática — las suscripciones bajo demanda permanecen cargables tras un fallo. Puedes llamar al endpoint de carga nuevamente como parte de tu propia lógica de reintento.

Qué sucede en caso de fallo

1

Charge attempt fails

La solicitud POST /subscriptions/{subscription_id}/charge ya sea devuelve una respuesta de error o se completa de forma asincrónica y emite un webhook payment.failed con la razón del rechazo.
2

Subscription may transition to on_hold

La suscripción puede pasar al estado on_hold y emitir un webhook subscription.on_hold (ver Estados de Suscripción → En Espera). Esto es una señal, no un bloqueo. Para suscripciones bajo demanda, on_hold no impide que vuelvas a cobrar.
3

Retry the charge (your call)

Para flujos bajo demanda, Dodo no realiza reintentos automáticos. Puedes llamar a POST /subscriptions/{subscription_id}/charge en cualquier momento para intentar de nuevo. Aplica la política de reintentos segura a continuación: usa retroceso exponencial, omite rechazos fuertes y evita patrones de ráfaga, para que los reintentos no sean señalados por nuestros sistemas de fraude y riesgo.
4

Optionally, ask the customer for a new payment method

Si los reintentos siguen fallando porque el método de pago en sí está roto (tarjeta expirada, cuenta cerrada, etc.), utiliza POST /subscriptions/{subscription_id}/update-payment-method para recopilar uno nuevo del cliente. Si tiene éxito, la suscripción vuelve a active y payment.succeeded seguido de subscription.active se emiten webhooks.
Bajo demanda vs programadas: Para suscripciones programadas, Dodo gestiona sus propios reintentos de renovación y gestión del cobro. Para suscripciones bajo demanda, tú controlas la política de reintentos porque solo tú sabes cuándo debe ocurrir el siguiente cobro (está impulsado por tus eventos de uso, no por un calendario).

Secuencia de webhook en un cobro fallido bajo demanda

OrdenEventoSignificado
1payment.failedEl intento de cobro bajo demanda no tuvo éxito (incluye la razón del rechazo)
2subscription.on_holdLa suscripción fue puesta en espera (informativo; no bloquea más cargos)
3*payment.succeededUn cobro posterior, ya sea tu reintento o después de una actualización del método de pago, tuvo éxito
4*subscription.activeLa suscripción regresó a active después de un cobro exitoso
Los eventos 3 y 4 solo se activan después de que un cobro de seguimiento tiene éxito.

Responsabilidad de reintentos

Dodo Payments no realiza reintentos automáticos de cargos fallidos bajo demanda. Tú controlas la política de reintentos. Sigue las pautas de reintentos seguros a continuación para evitar ser señalado por nuestros sistemas de detección de fraude como prueba de tarjetas.
Gestión del Cobro de Suscripciones — la secuencia de recuperación por correo electrónico integrada — se aplica a pagos de renovación fallidos en suscripciones programadas y cancelaciones iniciadas por el cliente. No está diseñada para fallos de cargos bajo demanda. Comunícate con el cliente directamente (por ejemplo, correo electrónico transaccional o mensaje en app) cuando decidas que se necesita actualizar el método de pago.

Reintentos de pago

Nuestro sistema de detección de fraude puede bloquear patrones de reintento agresivos (y puede señalarlos como posible prueba de tarjetas). Sigue una política de reintentos segura.
Patrones de reintentos en ráfaga pueden ser señalados como fraudulentos o sospechosos de ser pruebas de tarjetas por nuestros sistemas de riesgo y procesadores. Evita reintentos agrupados; sigue las guías de horario de retroceso y alineación de tiempo a continuación.

Principios para políticas de reintentos seguros

  • Mecanismo de retroceso: Usa retroceso exponencial entre reintentos.
  • Límites de reintentos: Limita los reintentos totales (máximo 3–4 intentos).
  • Filtrado inteligente: Reintenta solo en fallos que se pueden reintentar (por ejemplo, errores de red/emisor, fondos insuficientes); nunca reintentes rechazos fuertes.
  • Prevención de pruebas de tarjetas: No reintentes fallos como DO_NOT_HONOR, STOLEN_CARD, LOST_CARD, PICKUP_CARD, FRAUDULENT, AUTHENTICATION_FAILURE.
  • Varía metadatos (opcional): Si mantienes tu propio sistema de reintentos, diferencia los reintentos mediante metadatos (por ejemplo, retry_attempt).

Horario de reintentos sugerido (suscripciones)

  • 1er intento: Inmediato cuando creas el cargo
  • 2º intento: Después de 3 días
  • 3er intento: Después de 7 días más (10 días en total)
  • 4º intento (final): Después de otros 7 días (17 días en total)
Paso final: si aún está impago, marca la suscripción como impaga o cancélala, según tu política. Notifica al cliente durante el intervalo para que actualice su método de pago.

Evita reintentos en ráfaga; alinea con el tiempo de autorización

  • Ancla los reintentos a la marca de tiempo de la autorización original para evitar comportamiento de “ráfaga” en tu portafolio.
  • Ejemplo: Si el cliente inicia una prueba o mandato a la 1:10 pm hoy, programa reintentos de seguimiento a la 1:10 pm en días posteriores según tu retroceso (por ejemplo, +3 días → 1:10 pm, +7 días → 1:10 pm).
  • Alternativamente, si almacenas el tiempo del último pago exitoso T, programa el siguiente intento en T + X days para preservar la alineación del tiempo del día.
Zona horaria y DST: usa un estándar de tiempo consistente para programar y convierte para mostrar solo para mantener los intervalos.

Códigos de rechazo que no deberías volver a intentar

  • STOLEN_CARD
  • DO_NOT_HONOR
  • FRAUDULENT
  • PICKUP_CARD
  • AUTHENTICATION_FAILURE
  • LOST_CARD
Para una lista completa de razones de rechazo y si son corregibles por el usuario, consulta la documentación de Fallos de Transacciones.
Solo reintente en problemas suaves/temporales (por ejemplo, insufficient_funds, issuer_unavailable, processing_error, tiempos de espera de red). Si el mismo rechazo se repite, pausa más reintentos.

Pautas de implementación (sin código)

  • Usa un programador/cola que persista marcas de tiempo precisas; calcula el próximo intento con el mismo desfase de hora del día (por ejemplo, T + 3 days a la misma HH:MM).
  • Mantén y consulta la última marca de tiempo de pago exitoso T para calcular el próximo intento; no agrupe múltiples suscripciones al mismo instante.
  • Siempre evalúa la última razón de rechazo; detén los reintentos para rechazos fuertes en la lista de omisión arriba.
  • Límite de reintentos concurrentes por cliente y por cuenta para prevenir aumentos accidentales.
  • Comunica proactivamente: envía correo electrónico/SMS al cliente para actualizar su método de pago antes del próximo intento programado.
  • Usa metadatos solo para observabilidad (por ejemplo, retry_attempt); nunca intentes “evadir” sistemas de fraude/riesgo rotando campos inconsecuentes.

Cancelación

Las suscripciones a demanda siguen un flujo de cancelación diferente al de las suscripciones programadas porque no hay un ciclo de facturación fijo para determinar una fecha de finalización inmediata.

Comportamiento del portal del cliente

Cuando un cliente cancela una suscripción a demanda desde el Portal del Cliente, la cancelación se programa para la próxima fecha de facturación por defecto. La opción Cancelar ahora no se muestra intencionadamente para suscripciones a demanda. La razón: las suscripciones a demanda no tienen fechas de renovación recurrentes predecibles; el tiempo del próximo cargo se determina completamente por los eventos de uso. Programar la cancelación en la próxima fecha de facturación mantiene el mandato activo hasta el límite del período, de modo que cualquier uso en curso aún puede ser cobrado, y luego la suscripción termina limpiamente. Después de que el cliente confirma la cancelación:
  • La suscripción permanece active y continúa siendo cobrada a través de POST /subscriptions/{id}/charge hasta la fecha de cancelación programada.
  • cancel_at_next_billing_date se establece en true en la suscripción.
  • Se emite un webhook subscription.cancelled cuando la cancelación entra en vigor.
Si necesitas terminar la suscripción inmediatamente (por ejemplo, en respuesta a un reembolso o una solicitud de soporte), cancélala programáticamente a través de la API en lugar de depender del flujo del portal del cliente.

Cancelar programáticamente

Puedes cancelar una suscripción a demanda a través de la API en cualquier momento. Tú controlas si la cancelación es inmediata o programada. Endpoint: PATCH /subscriptions/{subscription_id}
Establece la suscripción status en cancelled para finalizarla de inmediato. El mandato es revocado y no se pueden crear más cargos.
import DodoPayments from 'dodopayments';

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

await client.subscriptions.update('sub_123', {
  status: 'cancelled',
});
cURL
curl -X PATCH "$DODO_API/subscriptions/sub_123" \
  -H "Authorization: Bearer $DODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "cancelled" }'

Webhooks en la cancelación

EventoCuándo se dispara
subscription.cancelledLa suscripción está completamente cancelada y ya no es cobrable
subscription.plan_changedcancel_at_next_billing_date fue activado (cancelación programada establecida o deshecha)
Para distinguir las cancelaciones a demanda de las cancelaciones de suscripciones programadas en tu manejador, verifica la marca on_demand de la suscripción al procesar el webhook.

Seguimiento de resultados con webhooks

Implementa el manejo de webhooks para seguir el recorrido del cliente. Consulta Implementación de Webhooks.
  • subscription.active: Mandato autorizado y suscripción activada
  • subscription.failed: Creación fallida (por ejemplo, fallo de mandato)
  • subscription.on_hold: Suscripción en espera (por ejemplo, estado impago)
  • subscription.cancelled: Suscripción completamente cancelada (ver Cancelación)
  • payment.succeeded: Cobro exitoso
  • payment.failed: Cobro fallido
Para flujos a demanda, concéntrate en payment.succeeded e payment.failed para conciliar los cargos basados en el uso. Cuando payment.failed es seguido por subscription.on_hold, consulta Manejo de cobros fallidos para recuperar la suscripción.

Pruebas y próximos pasos

1

Create in test mode

Usa tu clave API de prueba para crear la suscripción con payment_link: true, luego abre el enlace y completa el mandato.
2

Trigger a charge

Llama al endpoint de cobro con un pequeño product_price (por ejemplo, 100) y verifica que recibas payment.succeeded.
3

Go live

Cambia a tu clave API en vivo una vez que hayas validado eventos y actualizaciones de estado interno.

Solución de problemas

  • 422 Solicitud inválida: Asegúrate de que on_demand.mandate_only se proporcione al crear y product_price se proporcione para los cargos.
  • Errores de moneda: Si sobrescribes product_currency, confirma que está soportado para tu cuenta y cliente.
  • No se reciben webhooks: Verifica la configuración de tu URL de webhook y el secreto de la firma.
Last modified on May 28, 2026