Como fazer

Como cobrar assinatura com retentativa automática

Crie a assinatura com period no link de pagamento, deixe a retentativa com o HeroPay e reaja à recusa por webhook. Passo a passo com curl real.

Para cobrar assinatura com retentativa no HeroPay, crie um link com period em POST /payment_links: o HeroPay cobra cada ciclo e reprocessa sozinho a cobrança recusada. Seu código só reage aos webhooks do ciclo de vida.

O period aceita monthly, quarterly, semiannual ou annual, e a assinatura aceita cartão, boleto e Pix. Não existe job de retentativa do seu lado: payment_credit_cart_refused avisa a recusa com o motivo da operadora, spark_payment_confirmed e subscription_update avisam que a cobrança entrou, e subscription_cancel avisa quando a assinatura cai. Cobrança recusada não paga tarifa.

Antes de começar

  • Conta sandbox e token JWT em app.heropay.tech, guardados em HEROPAY_API_URL e HEROPAY_JWT_TOKEN no servidor.
  • Um endpoint HTTPS para webhooks, que valide X-HeroPay-Signature (código em /webhooks).
  • Uma tabela de assinantes no seu banco com, no mínimo: seu ID de cliente, o src que você usou e o status de acesso.

Passo a passo

curl -X POST "$HEROPAY_API_URL/payment_links" \
  -H "Authorization: Bearer $HEROPAY_JWT_TOKEN" \
  -H "Accept: application/vnd.herospark.com; version=1" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_link": {
      "name": "Plano Pro mensal",
      "description": "Plano Pro, cobrado todo mês",
      "price_cents": 9700,
      "absorbs_fees": true,
      "period": "monthly",
      "frequency_type": "unlimited",
      "payment_methods": ["credit_card", "bank_slip", "pix"],
      "src": "cliente-1042"
    }
  }'

Resposta 201 Created (recorte):

{
  "message": "Payment link created successfully",
  "data": {
    "id": 137,
    "price_cents": 9700,
    "period": "monthly",
    "frequency_type": "unlimited",
    "frequency_limit": null,
    "overdue_type": "none",
    "overdue_limit": null,
    "offer": {
      "id": 6095,
      "kind": "payment_link_subscription",
      "url": "https://pay.beta.herospark.com/fc280e25-7cbd-446f-b34f-5b8824bb5124-6095?src=cliente-1042",
      "accepted_payment_methods": ["credit_card", "bank_slip", "pix"]
    }
  }
}

data.offer.url é o botão "Assinar". Para um plano com fim, troque para "frequency_type": "limited" e "frequency_limit": 12: a assinatura encerra sozinha depois de 12 cobranças.

Métodos: a assinatura aceita cartão, boleto e Pix (/assinaturas). No Pix, a recorrência é o Pix Automático, e a regra do Banco Central exige recebedor com CNPJ. Confira em data.offer.accepted_payment_methods quais métodos ficaram ativos no link.

2. Deixe a retentativa com o HeroPay

Quando a renovação falha, o HeroPay reprocessa a cobrança automaticamente. Você não agenda nada. Três regras para saber o que esperar:

  • Tentativa recusada não paga tarifa. Cartão cobra 3,49% só na transação aprovada (preços).
  • Recusa por limite é a mais recuperável: o limite disponível do cartão muda de um dia para outro, e é nela que a retentativa mais resolve.
  • Recusa irreversível (cartão cancelado, bloqueado, vencido) não se resolve retentando: o caminho é o assinante trocar o cartão, e a troca é self-service, sem passar pelo seu suporte.

O calendário de tentativas segue a regra do HeroPay e os limites das bandeiras; você não agenda nem configura nada.

3. Cadastre os gatilhos do ciclo de vida

Um POST /webhook por gatilho, com a mesma URL:

for trigger in subscription_activate spark_payment_confirmed \
               payment_credit_cart_refused subscription_update subscription_cancel; do
  curl -s -X POST "$HEROPAY_API_URL/webhook" \
    -H "Authorization: Bearer $HEROPAY_JWT_TOKEN" \
    -H "Accept: application/vnd.herospark.com; version=1" \
    -H "Content-Type: application/json" \
    -d "{\"webhook\":{\"trigger\":\"$trigger\",\"webhook_url\":\"https://seuapp.com/webhooks/heropay\",\"request_method\":\"post\"}}"
done
GatilhoQuando chegaO que o seu sistema faz
subscription_activatePrimeiro pagamento confirmadoCria a conta e libera o acesso
spark_payment_confirmedCada cobrança paga, inclusive após retentativaEstende o acesso até subscription.next_invoice_at
payment_credit_cart_refusedRenovação recusadaMostra aviso no app e orienta o assinante a trocar o cartão
subscription_updateMudança de status ou novo pagamentoSincroniza o status local
subscription_cancelCancelada por cliente, vendedor, estorno, chargeback ou atrasoSuspende o acesso

O evento de recusa traz o motivo. Recorte dos campos que o handler usa (valores ilustrativos, campos reais do template; o payload completo está na documentação de webhooks):

{
  "payment": { "id": "8812399", "method": "credit_card" },
  "cart": { "src": "cliente-1042" },
  "subscription": { "id": "3020", "next_invoice_at": "2026-10-23" },
  "payment_methods": {
    "credit_card": { "refused_message": "Saldo insuficiente" }
  }
}

O texto de refused_message vem da operadora e varia: mostre ou registre, mas não faça if sobre a frase exata. Decida pelo gatilho: não suspenda o acesso na primeira recusa, porque a retentativa ainda vai rodar, e suspenda em subscription_cancel. Deduplique por payment.id + gatilho.

4. Monitore e cancele por API

Lista de assinaturas com cobrança recusada, para um painel de risco:

curl -G "$HEROPAY_API_URL/sales/recurring" \
  -H "Authorization: Bearer $HEROPAY_JWT_TOKEN" \
  -H "Accept: application/vnd.herospark.com; version=1" \
  --data-urlencode "paymentStatus[]=refused" \
  -d "limit=100"

Cada item traz cart.subscriptionId, cart.subscriptionStatus e a lista purchases com cada tentativa. Para cancelar quando o cliente pede:

curl -X POST "$HEROPAY_API_URL/recurring_payment/cancel" \
  -H "Authorization: Bearer $HEROPAY_JWT_TOKEN" \
  -H "Accept: application/vnd.herospark.com; version=1" \
  -H "Content-Type: application/json" \
  -d '{ "recurring_payment": { "recurring_payment_id": 3020 } }'

Resposta 200: "message": "Recurring payment cancelled successfully" e data.status: "cancelled". O cancelamento é imediato e irreversível; o histórico fica preservado.

Variações

Por prompt, com o llms.txt

Cole https://heropay.ai/llms.txt no Claude, ChatGPT, Cursor, Lovable e outros (mais em /ai) e peça:

Cria um plano mensal de R$ 97 no cartão, renovação ilimitada, src "cliente-1042",
e cadastra webhooks de recusa, pagamento confirmado e cancelamento de assinatura
para https://seuapp.com/webhooks/heropay.

Com o contrato da API no contexto, a IA monta o POST /payment_links com period e os cadastros de webhook. Revise as chamadas antes de rodar. Um MCP server oficial está em desenvolvimento.

Sem código, pelo painel

Crie o link recorrente em app.heropay.tech escolhendo a periodicidade e a duração, copie a URL e use no botão "Assinar".

Erros comuns e como ler a mensagem

StatusMensagem (real)Causa e correção
422Price cents must be greater than or equal to 500Valor do ciclo abaixo de R$ 5,00 ou enviado em reais.
422Period is not included in the listperiod fora de monthly, quarterly, semiannual, annual (não existe weekly nem yearly).
sem erro de validaçãonenhuma"limited" enviado sem frequency_limit: a validação da API não bloqueia a combinação. Mande sempre os dois juntos e confira frequency_limit na resposta.
422 (cancelamento)Recurring payment id can't be blankFaltou recurring_payment.recurring_payment_id.
404 ou 400 (cancelamento)Failed to cancel recurring paymentID inexistente, de outra conta ou assinatura já cancelada. Consulte GET /sales/recurring antes de repetir.
422 (listagem)Limit deve ser um número inteiro entre 10 e 1000limit fora da faixa em /sales/recurring.

Perguntas frequentes

Quantas vezes o HeroPay retenta uma assinatura recusada?

O reprocessamento é automático, segue a regra do HeroPay e os limites das bandeiras, e você não configura job. O que vale para o seu planejamento: tentativa recusada não paga tarifa, a recusa por limite é a mais recuperável e o assinante pode trocar o cartão sozinho quando retentar não resolve. As bandeiras também limitam retentativas no mesmo cartão, por isso retentativa sozinha nunca é a estratégia inteira.

Devo cortar o acesso quando chega payment_credit_cart_refused?

Não. A recusa é o começo da recuperação, não o fim da assinatura. Mostre um aviso no app, oriente a troca de cartão e mantenha o acesso enquanto a retentativa roda. Corte quando chegar subscription_cancel, que é disparado quando a assinatura cai por atraso, cancelamento, estorno ou chargeback. Cortar cedo transforma churn involuntário em churn real.

Como leio o motivo da recusa?

No campo payment_methods.credit_card.refused_message do webhook payment_credit_cart_refused. O texto vem da operadora. O texto vem da operadora e varia, então use para orientar a mensagem, sem depender da frase exata: limite pede outro método, cartão vencido pede troca de cartão. Para ver o padrão da base inteira, GET /reports/purchase/refused/reasons_count agrupa as recusas por motivo num período (/como/recuperar-vendas-recusadas).

Dá para cobrar assinatura por Pix ou boleto?

Sim. A assinatura aceita cartão, boleto e Pix. No Pix, a recorrência é o Pix Automático: o cliente autoriza uma vez e as cobranças seguintes caem sozinhas, e a regra do Banco Central exige recebedor com CNPJ. Pix e boleto custam R$ 0 por cobrança. Mande os métodos em payment_methods e confira accepted_payment_methods na resposta.

Como cancelo uma assinatura pela API?

Com POST /recurring_payment/cancel e o corpo {"recurring_payment": {"recurring_payment_id": 3020}}. A resposta traz status: cancelled, o cancelamento é imediato, não gera cobranças futuras e não pode ser desfeito. Apenas assinaturas ativas podem ser canceladas; repetir o pedido numa assinatura já cancelada devolve Failed to cancel recurring payment. O webhook subscription_cancel chega em seguida para você suspender o acesso.

Quanto custa a recorrência?

Não há preço à parte para assinatura nem para retentativa. Cada cobrança aprovada paga a tarifa normal: cartão 3,49%, Pix R$ 0 e boleto R$ 0. Numa assinatura de R$ 97 no cartão, cada ciclo aprovado custa R$ 3,39; as tentativas recusadas custam zero. Sem mensalidade, sem ativação, sem mínimo. Tabela em /precos.

Comece agora

Execute este passo a passo agora

Sandbox grátis e idêntico à produção: cole o código e rode.

Pix e boleto R$ 0 · cartão 3,49% em até 12x · saque sem tarifa