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 siempre que lo necesites, en lugar de en un horario fijo.
Esta función puede necesitar ser habilitada en tu cuenta. Contacta al soporte si no la ves en tu panel de control.
Usa esta guía para:
  • Crear una suscripción bajo demanda (autorizar un mandato con un precio inicial opcional)
  • Activar cargos subsiguientes con montos personalizados
  • Rastrear resultados usando 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 a través de un checkout alojado, establece payment_link: true y proporciona un return_url.

Cómo funciona la suscripción 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 tarde, creas cargos contra esa suscripción con montos personalizados usando el endpoint de cargo dedicado.
  3. Escuchas los webhooks (por ejemplo, payment.succeeded, payment.failed) para actualizar tu sistema.

Crear una suscripción bajo demanda

Endpoint: POST /subscriptions Campos clave de la solicitud (cuerpo):
product_id
string
required
ID del producto para la suscripción.
quantity
integer
required
Número de unidades. Mínimo 1.
billing
object
required
Dirección de facturación del cliente.
customer
object
required
Ya sea adjuntar un cliente existente o proporcionar detalles del cliente.
Si es verdadero, crea un enlace de checkout alojado para la autorización del mandato y el pago inicial opcional.
return_url
string
A dónde redirigir al cliente después de completar el checkout alojado.
on_demand.mandate_only
boolean
required
Si es verdadero, autoriza el método de pago sin cobrar al cliente durante la creación.
on_demand.product_price
integer
Monto del cargo inicial (en la unidad de moneda más pequeña). Si se especifica, este valor anula el precio original del producto establecido durante la creación del producto. Si se omite, se utiliza el precio almacenado del producto. Ejemplo: para cobrar $1.00, pasa 100.
on_demand.product_currency
string
Anulación de moneda opcional para el cargo inicial. Por defecto, se utiliza la moneda del producto.
on_demand.product_description
string
Anulación de descripción opcional para la facturación y los artículos de línea.
on_demand.adaptive_currency_fees_inclusive
boolean
Si es verdadero, incluye las tarifas de moneda adaptativa dentro de product_price. Si es falso, las tarifas se añaden por encima. Ignorado cuando la fijación de precios adaptativa está deshabilitada.

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.subscriptions.create({
    billing: { city: 'SF', country: 'US', state: 'CA', street: '1 Market St', zipcode: '94105' },
    customer: { customer_id: 'customer_123' },
    product_id: 'prod_sub_123',
    quantity: 1,
    payment_link: true,
    return_url: 'https://example.com/billing/success',
    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,
    },
  });

  // If payment_link was true, redirect the customer to authorize the mandate
  console.log(subscription.payment_link);
}

main().catch(console.error);
Establece payment_link: true, redirige al cliente a payment_link para completar la autorización del mandato.
Success
{
  "subscription_id": "sub_123",
  "payment_link": "https://pay.dodopayments.com/checkout/...",
  "customer": { "customer_id": "customer_123", "email": "[email protected]", "name": "Alex Doe" },
  "metadata": {},
  "recurring_pre_tax_amount": 0,
  "addons": []
}

Cobrar 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
required
Monto a cobrar (en la unidad de moneda más pequeña). Ejemplo: para cobrar $25.00, pasa 2500.
product_currency
string
Anulación de moneda opcional para el cargo.
product_description
string
Anulación de descripción opcional para este cargo.
adaptive_currency_fees_inclusive
boolean
Si es verdadero, incluye las tarifas de moneda adaptativa dentro de product_price. Si es falso, las tarifas se añaden por 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,
  environment: 'test_mode', // defaults to 'live_mode'
});

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.

Reintentos de pago

Nuestro sistema de detección de fraude puede bloquear patrones de reintento agresivos (y puede marcarlos como pruebas de tarjeta potenciales). Sigue una política de reintento segura.
Los patrones de reintento en ráfaga pueden ser marcados como fraudulentos o sospechosos de pruebas de tarjeta por nuestros sistemas de riesgo y procesadores. Evita reintentos agrupados; sigue el cronograma de retroceso y la guía de alineación de tiempo a continuación.

Principios para políticas de reintento seguras

  • Mecanismo de retroceso: Usa retroceso exponencial entre reintentos.
  • Límites de reintento: Limita el total de reintentos (máximo 3-4 intentos).
  • Filtrado inteligente: Reintenta solo en fallos reintentables (por ejemplo, errores de red/emisor, fondos insuficientes); nunca reintentes rechazos duros.
  • Prevención de pruebas de tarjeta: No reintentes fallos como DO_NOT_HONOR, STOLEN_CARD, LOST_CARD, PICKUP_CARD, FRAUDULENT, AUTHENTICATION_FAILURE.
  • Variar metadatos (opcional): Si mantienes tu propio sistema de reintento, diferencia los reintentos a través de metadatos (por ejemplo, retry_attempt).

Cronograma de reintento sugerido (suscripciones)

  • 1er intento: Inmediato cuando creas el cargo
  • 2do intento: Después de 3 días
  • 3er intento: Después de 7 días más (10 días en total)
  • 4to intento (final): Después de otros 7 días (17 días en total)
Paso final: si aún no se ha pagado, marca la suscripción como impaga o cancélala, según tu política. Notifica al cliente durante el período para actualizar su método de pago.

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

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

Códigos de rechazo que no debes reintentar

  • 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 Transacción.
Solo reintenta 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.

Directrices de implementación (sin código)

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

Rastrear resultados con webhooks

Implementa el manejo de webhooks para rastrear el viaje del cliente. Consulta Implementación de Webhooks.
  • subscription.active: Mandato autorizado y suscripción activada
  • subscription.failed: La creación falló (por ejemplo, fallo del mandato)
  • subscription.on_hold: Suscripción en espera (por ejemplo, estado impago)
  • payment.succeeded: Cargo exitoso
  • payment.failed: Cargo fallido
Para flujos bajo demanda, concéntrate en payment.succeeded y payment.failed para reconciliar cargos basados en el uso.

Pruebas y próximos pasos

1

Crear en modo de prueba

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

Activar un cargo

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

Poner en producción

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 en la creación y product_price se proporcione para los cargos.
  • Errores de moneda: Si anulas product_currency, confirma que esté soportada para tu cuenta y cliente.
  • No se recibieron webhooks: Verifica tu URL de webhook y la configuración del secreto de firma.