Pular para o conteúdo principal

Visão Geral

Assinaturas sob demanda permitem que você autorize o método de pagamento de um cliente uma vez e, em seguida, cobre valores variáveis sempre que precisar, em vez de em um cronograma fixo.
Esse recurso pode precisar ser habilitado na sua conta. Entre em contato com o suporte se você não o vê no seu painel.
Use este guia para:
  • Criar uma assinatura sob demanda (autorizar um mandato com preço inicial opcional)
  • Acionar cobranças subsequentes com valores personalizados
  • Rastrear resultados usando webhooks
Para uma configuração geral de assinatura, consulte o Guia de Integração de Assinaturas.

Pré-requisitos

  • Conta de comerciante Dodo Payments e chave API
  • Segredo de webhook configurado e um endpoint para receber eventos
  • Um produto de assinatura no seu catálogo
Se você quiser que o cliente aprove o mandato via checkout hospedado, defina payment_link: true e forneça um return_url.

Como funciona o sob demanda

  1. Você cria uma assinatura com o objeto on_demand para autorizar um método de pagamento e, opcionalmente, coletar uma cobrança inicial.
  2. Mais tarde, você cria cobranças contra essa assinatura com valores personalizados usando o endpoint de cobrança dedicado.
  3. Você escuta webhooks (por exemplo, payment.succeeded, payment.failed) para atualizar seu sistema.

Criar uma assinatura sob demanda

Endpoint: POST /subscriptions Principais campos de solicitação (corpo):
product_id
string
required
ID do produto para a assinatura.
quantity
integer
required
Número de unidades. Mínimo 1.
billing
object
required
Endereço de cobrança do cliente.
customer
object
required
Anexe um cliente existente ou forneça os detalhes do cliente.
Se verdadeiro, cria um link de checkout hospedado para autorização de mandato e pagamento inicial opcional.
return_url
string
Para onde redirecionar o cliente após concluir o checkout hospedado.
on_demand.mandate_only
boolean
required
Se verdadeiro, autoriza o método de pagamento sem cobrar o cliente durante a criação.
on_demand.product_price
integer
Valor da cobrança inicial (na menor unidade monetária). Se especificado, esse valor substitui o preço original do produto definido durante a criação do produto. Se omitido, o preço armazenado do produto é usado. Exemplo: para cobrar $1,00, passe 100.
on_demand.product_currency
string
Substituição de moeda opcional para a cobrança inicial. Padrão é a moeda do produto.
on_demand.product_description
string
Substituição de descrição opcional para cobrança e itens de linha.
on_demand.adaptive_currency_fees_inclusive
boolean
Se verdadeiro, inclui taxas de moeda adaptativa dentro de product_price. Se falso, as taxas são adicionadas em cima. Ignorado quando a precificação adaptativa está desativada.

Criar uma assinatura sob 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);
Defina payment_link: true, redirecione o cliente para payment_link para concluir a autorização do 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 uma assinatura sob demanda

Após o mandato ser autorizado, crie cobranças conforme necessário. Endpoint: POST /subscriptions/{subscription_id}/charge Principais campos de solicitação (corpo):
product_price
integer
required
Valor a ser cobrado (na menor unidade monetária). Exemplo: para cobrar $25,00, passe 2500.
product_currency
string
Substituição de moeda opcional para a cobrança.
product_description
string
Substituição de descrição opcional para esta cobrança.
adaptive_currency_fees_inclusive
boolean
Se verdadeiro, inclui taxas de moeda adaptativa dentro de product_price. Se falso, as taxas são adicionadas em cima.
metadata
object
Metadados adicionais para o pagamento. Se omitido, os metadados da assinatura são usados.
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 uma assinatura que não é sob demanda pode falhar. Certifique-se de que a assinatura tenha on_demand: true em seus detalhes antes de cobrar.

Tentativas de pagamento

Nosso sistema de detecção de fraudes pode bloquear padrões de retry agressivos (e pode sinalizá-los como testes de cartão potenciais). Siga uma política de retry segura.
Padrões de retry em explosão podem ser sinalizados como fraudulentos ou suspeitos de teste de cartão pelos nossos sistemas de risco e processadores. Evite retries agrupados; siga o cronograma de backoff e as orientações de alinhamento de tempo abaixo.

Princípios para políticas de retry seguras

  • Mecanismo de backoff: Use backoff exponencial entre as tentativas.
  • Limites de retry: Limite o total de tentativas (máximo de 3–4 tentativas).
  • Filtragem inteligente: Tente novamente apenas em falhas recuperáveis (por exemplo, erros de rede/emissor, fundos insuficientes); nunca tente novamente em recusas definitivas.
  • Prevenção de teste de cartão: Não tente novamente falhas como DO_NOT_HONOR, STOLEN_CARD, LOST_CARD, PICKUP_CARD, FRAUDULENT, AUTHENTICATION_FAILURE.
  • Variar metadados (opcional): Se você mantiver seu próprio sistema de retry, diferencie as tentativas via metadados (por exemplo, retry_attempt).

Cronograma de retry sugerido (assinaturas)

  • 1ª tentativa: Imediata quando você criar a cobrança
  • 2ª tentativa: Após 3 dias
  • 3ª tentativa: Após mais 7 dias (10 dias no total)
  • 4ª tentativa (final): Após mais 7 dias (17 dias no total)
Passo final: se ainda não pago, marque a assinatura como não paga ou cancele-a, com base na sua política. Notifique o cliente durante a janela para atualizar seu método de pagamento.

Evite retries em explosão; alinhe ao tempo de autorização

  • Ancore as tentativas ao timestamp de autorização original para evitar comportamento de “explosão” em seu portfólio.
  • Exemplo: Se o cliente iniciar um teste ou mandato às 13h10 de hoje, agende as tentativas de follow-up às 13h10 nos dias subsequentes conforme seu backoff (por exemplo, +3 dias → 13h10, +7 dias → 13h10).
  • Alternativamente, se você armazenar o último horário de pagamento bem-sucedido T, agende a próxima tentativa em T + X days para preservar o alinhamento do horário do dia.
Fuso horário e DST: use um padrão de tempo consistente para agendamento e converta apenas para exibição para manter os intervalos.

Códigos de recusa que você não deve tentar novamente

  • STOLEN_CARD
  • DO_NOT_HONOR
  • FRAUDULENT
  • PICKUP_CARD
  • AUTHENTICATION_FAILURE
  • LOST_CARD
Para uma lista abrangente de razões de recusa e se são corrigíveis pelo usuário, consulte a documentação de Falhas de Transação.
Tente novamente apenas em problemas temporários/suaves (por exemplo, insufficient_funds, issuer_unavailable, processing_error, timeouts de rede). Se a mesma recusa se repetir, pause novas tentativas.

Diretrizes de implementação (sem código)

  • Use um agendador/fila que persista timestamps precisos; calcule a próxima tentativa no exato deslocamento de horário (por exemplo, T + 3 days no mesmo HH:MM).
  • Mantenha e faça referência ao último timestamp de pagamento bem-sucedido T para calcular a próxima tentativa; não agrupe várias assinaturas no mesmo instante.
  • Sempre avalie a última razão de recusa; pare as tentativas para recusas definitivas na lista de exclusão acima.
  • Limite tentativas simultâneas por cliente e por conta para evitar picos acidentais.
  • Comunique-se proativamente: envie e-mail/SMS ao cliente para atualizar seu método de pagamento antes da próxima tentativa agendada.
  • Use metadados apenas para observabilidade (por exemplo, retry_attempt); nunca tente “evadir” sistemas de fraude/risco rotacionando campos irrelevantes.

Rastrear resultados com webhooks

Implemente o manuseio de webhooks para rastrear a jornada do cliente. Veja Implementando Webhooks.
  • subscription.active: Mandato autorizado e assinatura ativada
  • subscription.failed: Criação falhou (por exemplo, falha de mandato)
  • subscription.on_hold: Assinatura colocada em espera (por exemplo, estado não pago)
  • payment.succeeded: Cobrança bem-sucedida
  • payment.failed: Cobrança falhou
Para fluxos sob demanda, concentre-se em payment.succeeded e payment.failed para reconciliar cobranças baseadas em uso.

Testes e próximos passos

1

Criar em modo de teste

Use sua chave API de teste para criar a assinatura com payment_link: true, depois abra o link e conclua o mandato.
2

Acionar uma cobrança

Chame o endpoint de cobrança com um pequeno product_price (por exemplo, 100) e verifique se você recebe payment.succeeded.
3

Ir ao vivo

Mude para sua chave API ao vivo uma vez que você tenha validado eventos e atualizações de estado interno.

Resolução de Problemas

  • 422 Solicitação Inválida: Certifique-se de que on_demand.mandate_only seja fornecido na criação e product_price seja fornecido para cobranças.
  • Erros de moeda: Se você substituir product_currency, confirme se é suportado para sua conta e cliente.
  • Nenhum webhook recebido: Verifique sua URL de webhook e configuração de segredo de assinatura.