Recorrência no cartão de ponta a ponta — plano, assinatura, adesão do cartão com a SDK web, cobranças e webhooks.
Assinaturas com cartão — guia de implementação
O estabelecimento cria um plano e uma assinatura por cliente. O cliente cadastra o cartão uma vez numa página segura da Autra, aberta dentro do site. Depois disso, a Autra cobra sozinha a cada vencimento, sem pedir o cartão de novo.
O site nunca recebe o número do cartão, a validade ou o CVV. Eles são digitados na página da Autra, em cards.autra.io, e o site recebe só os 4 últimos dígitos e a bandeira.
A integração tem duas partes:
- Backend do estabelecimento: cria o plano, a assinatura e a sessão de cartão, e recebe os webhooks. Todas as chamadas à API saem do backend, nunca do navegador.
- Site do estabelecimento: usa a SDK web
@autra-io/card-sdk-webpara abrir a página de cartão e mostrar o resultado ao cliente.
O fluxo completo, do plano à primeira cobrança:
- O backend cria o plano, uma única vez.
- O backend cria a assinatura do cliente. Ela nasce aguardando o cartão e ainda não é cobrada.
- O backend abre uma sessão de cartão ligada a essa assinatura e recebe um link da página.
- O site abre a página com a SDK e o cliente digita o cartão.
- A Autra valida o cartão sem cobrar nada, salva o cartão e ativa a assinatura.
- O backend recebe o webhook de assinatura ativada e libera o acesso do cliente.
- A Autra gera cada fatura 3 dias antes do vencimento e cobra o cartão salvo no dia.
Antes de começar
Cinco itens precisam estar prontos antes da primeira chamada. Os três primeiros são feitos pela Autra, a pedido do estabelecimento.
| Item | Quem faz | Como confirmar |
|---|---|---|
| Módulo de assinaturas e SDK web habilitados no tenant | Autra | As rotas de plano param de responder FEATURE_DISABLED e a sessão web para de responder CARD_SDK_WEB_DISABLED |
| Estabelecimento credenciado para cartão | Autra | A sessão de cartão para de responder ACQUIRING_NOT_READY |
| Endereço do site cadastrado como origem permitida | Autra, com o endereço enviado pelo estabelecimento | A sessão web devolve iframe em embedModes |
Credenciais de API (clientId e clientSecret) e IP do backend liberado | Autra entrega as credenciais; o estabelecimento informa os IPs de saída | POST /v1/oauth/token devolve um token |
| URL do backend para receber webhooks | Estabelecimento | Cadastro com PUT /v1/banking/webhooks/{domain}, na seção de webhooks |
Origem do site. É o endereço exato em que a página será aberta, no formato https://loja.exemplo.com.br. Vai sem caminho, sem barra no fim e em letras minúsculas. Cada subdomínio é uma origem diferente, e produção e sandbox são cadastradas separadamente.
Token de acesso. O backend gera o token com o fluxo OAuth 2.0 client_credentials e o envia em todas as chamadas:
curl -X POST https://api.autra.io/v1/oauth/token \
-H "Authorization: Basic <base64(clientId:clientSecret)>" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials"Toda chamada às rotas de assinatura e de cartão leva também o header X-Merchant-Document, com o CPF ou CNPJ do estabelecimento, só números.
Referência: Gerar token de acesso.
Passo a passo no backend
São três chamadas, sempre a partir do backend, em https://api.autra.io.
1. Criar o plano (uma vez)
O plano define quanto cobrar, de quanto em quanto tempo e quando vence a primeira cobrança. Ele é reaproveitado por todos os clientes daquele produto.
Referência: Criar plano de assinatura.
curl -X POST https://api.autra.io/v1/acquiring/subscription-plans \
-H "Authorization: Bearer <token>" \
-H "X-Merchant-Document: 60116920000100" \
-H "Content-Type: application/json" \
-d '{
"name": "Plano Mensal Ouro",
"amount": 49.90,
"periodicity": "MONTHLY",
"allowedMethods": ["CARD"],
"enrollmentCharge": "FIRST_DUE_DATE",
"firstDue": { "rule": "FIXED_DAY", "billingDay": 10 },
"cardRetryDays": [1, 3, 7]
}'| Campo | Regra |
|---|---|
name | Único por estabelecimento, até 120 caracteres |
amount | Valor em reais, maior que zero, até 2 casas decimais |
periodicity | WEEKLY, MONTHLY ou ANNUAL |
allowedMethods | Use ["CARD"]. A cobrança automática desta versão é só no cartão |
enrollmentCharge | Use FIRST_DUE_DATE: o cadastro do cartão não cobra, e a primeira cobrança acontece no primeiro vencimento |
firstDue | FIXED_DAY com billingDay de 1 a 31, ou DAYS_AFTER_ENROLLMENT com offsetDays de 0 a 365 |
cardRetryDays | Opcional. Dias após o vencimento para tentar de novo depois de uma recusa. O padrão é [1, 3, 7] |
Valor e periodicidade não mudam depois de criados. Para mudar o preço, crie outro plano e arquive o antigo. As assinaturas existentes continuam no plano delas.
2. Criar a assinatura do cliente
A assinatura liga um cliente a um plano. Ela nasce em PENDING_ENROLLMENT e só é cobrada depois que o cliente cadastrar o cartão. Guarde o id devolvido.
Referência: Criar assinatura.
curl -X POST https://api.autra.io/v1/acquiring/subscriptions \
-H "Authorization: Bearer <token>" \
-H "X-Merchant-Document: 60116920000100" \
-H "Content-Type: application/json" \
-d '{
"planId": "2814b0fd-25b7-476c-affe-34f30fe46d6d",
"externalRef": "cliente-123-plano-ouro",
"customer": {
"document": "12345678909",
"name": "Maria da Silva",
"email": "[email protected]"
}
}'externalRefé o identificador do estabelecimento para essa assinatura e serve de chave contra duplicidade. Repetir a chamada com o mesmoexternalRef, plano e cliente devolve a mesma assinatura, sem criar outra.firstDueDateé opcional, no formatoAAAA-MM-DD. Sem ele, vale a regra do plano. O dia do primeiro vencimento vira o dia fixo das cobranças seguintes.metadataé opcional, até 4 KiB, e volta como foi enviado. Não coloque dado sensível nele.
3. Abrir a sessão de cartão da adesão
A sessão gera o link da página onde o cliente digita o cartão. Crie uma sessão nova a cada tentativa: ela vale 15 minutos e é de uso único.
Referência: Create hosted card session.
curl -X POST https://api.autra.io/v1/acquiring/card-sessions \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"documentId": "60116920000100",
"purpose": "ENROLLMENT",
"channel": "web",
"origin": "https://loja.exemplo.com.br",
"subscriptionId": "<id da assinatura do passo 2>"
}'A resposta traz os campos que o site vai usar:
| Campo | Uso |
|---|---|
hostedCardUrl | Link da página de cartão. Entregue ao site exatamente como veio |
hostedCardSessionId | Identificador da sessão. Necessário no modo redirect |
embedModes | Modos de abertura permitidos: iframe, popup e redirect |
expiresAt | Fim da validade da sessão, 15 minutos depois da criação |
O origin precisa ser igual, caractere por caractere, à origem cadastrada. A hostedCardUrl carrega uma credencial temporária da página: não grave esse link em log.
Passo a passo no site
O site pede ao próprio backend uma sessão e abre a página de cartão com a SDK. A SDK tem cerca de 5 KB e nenhuma dependência.
4. Instalar a SDK
npm install @autra-io/card-sdk-web5. Abrir a página de cartão
import { AutraCard, AutraCardError } from '@autra-io/card-sdk-web';
botaoAssinar.addEventListener('click', async () => {
// Seu backend cria a sessão do passo 3 e devolve a hostedCardUrl.
const { hostedCardUrl } = await fetch('/api/assinatura/cartao', { method: 'POST' }).then((r) => r.json());
const card = new AutraCard({ hostedCardUrl, mode: 'modal' });
card.on('ready', () => esconderCarregando());
card.on('submitting', () => mostrarMensagem('Validando seu cartão…'));
card.on('enrollment_denied', ({ reasonText }) => {
// Não é o fim: a página mostra o motivo e o cliente pode tentar outro cartão.
});
try {
const result = await card.open();
mostrarMensagem(`Assinatura ativa no cartão final ${result.card.last4}`);
} catch (error) {
if (error instanceof AutraCardError && error.indeterminate) {
// O cartão já tinha sido enviado e pode ter sido aprovado. Vale o webhook.
return mostrarMensagem('Estamos confirmando seu cartão…');
}
if (error instanceof AutraCardError && error.code === 'canceled') return;
mostrarErro(error instanceof AutraCardError ? error.code : 'erro_desconhecido');
}
});O resultado de card.open() traz os 4 últimos dígitos, a bandeira e a confirmação da adesão com o subscriptionId. Ele serve para a tela do cliente. A liberação do acesso deve vir do webhook no backend.
Modos de abertura
Use um modo que apareça em embedModes na resposta da sessão. O modo modal da SDK corresponde a iframe na API.
| Modo | Quando usar | Cuidados |
|---|---|---|
modal | Padrão. A página abre por cima do site | Exige a origem cadastrada e ativa |
popup | Quando o site não pode ter iframe | Chame card.open() direto no clique, sem await antes. Crie a sessão antes do clique |
redirect | Web no celular ou webviews onde modal e popup falham | Exige returnUrl na origem cadastrada. O resultado volta no endereço de retorno |
No modo redirect, a página de retorno lê o resultado assim:
const outcome = AutraCard.readRedirectResult(location.hash, { sessionId: hostedCardSessionId });
if (outcome?.type === 'result') mostrarMensagem(`Cartão final ${outcome.result.card.last4} salvo`);
else if (outcome?.type === 'error') mostrarErro(outcome.error.code);O resultado no endereço de retorno pode ser forjado por qualquer pessoa. Use-o só para a tela. Ative a conta do cliente apenas pelo webhook ou por uma consulta do backend.
Erros da SDK
Quando card.open() falha, o erro traz um code:
code | O que fazer |
|---|---|
canceled | O cliente fechou a página. Com indeterminate: true, o cartão pode ter sido aprovado: aguarde o webhook |
timeout | A página não carregou em 15 segundos. Causas comuns: sessão vencida ou origem não cadastrada. Crie outra sessão |
session_expired | A sessão passou de 15 minutos ou já foi usada. Crie outra |
origin_not_allowed | A origem do site não está cadastrada ou ativa |
enrollment_denied | O emissor recusou o cartão e o cliente desistiu. reasonText traz o motivo |
enrollment_pending_review | Resultado incerto. Não abra outra adesão e aguarde o webhook |
provider_unavailable | Instabilidade momentânea. Tente de novo em instantes |
tokenization_failed | Falha ao salvar o cartão. Crie outra sessão e tente de novo |
validation_error | Dados do cartão inválidos e o cliente desistiu |
popup_blocked | O navegador bloqueou o popup. Chame open() direto no clique ou use modal |
configuration_error | Parâmetro inválido na SDK, open() repetido ou outra modal já aberta |
message_invalid | Mensagem fora do formato esperado. Atualize a SDK |
Depois da adesão
A partir da adesão aprovada, a Autra cobra sozinha e avisa o backend por webhook a cada mudança.
6. Cadastrar os webhooks
Cadastre a URL do backend nos domínios subscriptions e acquiring, uma chamada para cada um:
Referência: Criar ou atualizar webhook.
curl -X PUT https://api.autra.io/v1/banking/webhooks/subscriptions \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://backend.exemplo.com.br/webhooks/autra",
"eventTypes": ["*"],
"enabled": true
}'A resposta do primeiro cadastro traz o secret, que só aparece nessa vez. Guarde-o num cofre de senhas. Todo webhook chega com o header X-Autra-Signature: sha256=<hex>, que é o HMAC-SHA256 do corpo bruto com esse secret. Recuse o webhook se a assinatura não bater. O guia Outbound Webhooks tem exemplos de validação em várias linguagens.
Se a entrega falhar, a Autra tenta de novo por até 16 vezes, em cerca de 60 minutos. O backend deve responder 2xx rápido e tratar eventos repetidos sem efeito duplicado.
| Evento | Quando chega | Ação sugerida |
|---|---|---|
subscription.activated | Cartão aprovado na adesão | Liberar o acesso do cliente |
invoice.created | 3 dias antes de cada vencimento | Avisar o cliente da cobrança |
invoice.paid | Cobrança aprovada | Registrar o pagamento |
invoice.payment_failed | Tentativa recusada; há nova tentativa agendada | Pedir ao cliente que confira o cartão |
invoice.overdue | As 3 tentativas falharam | Tratar a inadimplência |
subscription.past_due | A assinatura entrou em atraso | Restringir o acesso, se for a política |
subscription.reactivated | A fatura em atraso foi paga | Devolver o acesso |
subscription.suspended e subscription.canceled | O estabelecimento suspendeu ou cancelou | Atualizar o cadastro |
acquiring.card_enrollment.completed ou .failed | Fim da sessão de cartão da adesão | Registro da adesão, independente do navegador |
Os webhooks trazem subscriptionId e externalRef para localizar o cliente. O documento do cliente vem mascarado.
Como as cobranças funcionam
Cada vencimento gera uma fatura, criada 3 dias antes. No dia do vencimento, a Autra cobra o cartão salvo, sem pedir CVV.
- Recusa: cada fatura tem até 3 tentativas. São o dia do vencimento e as duas primeiras datas de
cardRetryDays, que pelo padrão são 1 e 3 dias depois. - Atraso: se as 3 falham, a fatura fica
OVERDUEe a assinatura ficaPAST_DUE. Nenhum período novo é cobrado até essa fatura ser paga. Quando é paga, a assinatura volta aACTIVEsozinha. - Consulta:
GET /v1/acquiring/subscriptions/{id}/invoiceslista as faturas da assinatura, da mais recente para a mais antiga.
| Status da fatura | Significado |
|---|---|
SCHEDULED | Criada, aguardando o vencimento |
OPEN | Cobrança em andamento |
RETRYING | Uma tentativa falhou e há outra agendada |
PAID | Paga |
OVERDUE | As 3 tentativas falharam |
CANCELED | Cancelada junto com a assinatura |
Gerenciar a assinatura
| Ação | Rota | Efeito |
|---|---|---|
| Consultar | GET /v1/acquiring/subscriptions/{id} | Status, próximo vencimento e cartão |
| Listar | GET /v1/acquiring/subscriptions | Assinaturas do estabelecimento; aceita filtro por externalRef |
| Suspender | POST /v1/acquiring/subscriptions/{id}/suspend | Pausa as cobranças |
| Reativar | POST /v1/acquiring/subscriptions/{id}/reactivate | Volta a cobrar uma assinatura suspensa |
| Cancelar | POST /v1/acquiring/subscriptions/{id}/cancel | Encerra de vez e cancela as faturas não cobradas |
Uma cobrança já em andamento no momento da suspensão ou do cancelamento termina normalmente e pode ser debitada. Faturas pagas não são estornadas automaticamente. Se precisar devolver, use o estorno do pagamento.
Erros comuns e checklist
Os erros da API vêm no corpo {"errors":[{"code":"...","msg":"..."}]}. Estes são os que mais aparecem na implantação:
code | HTTP | Causa | O que fazer |
|---|---|---|---|
FEATURE_DISABLED | 403 | Módulo de assinaturas não habilitado | Pedir a habilitação à Autra |
CARD_SDK_WEB_DISABLED | 403 | SDK web não habilitada | Pedir a habilitação à Autra |
CARD_SDK_ORIGIN_NOT_REGISTERED | 422 | O origin da sessão não está cadastrado | Conferir a grafia ou pedir o cadastro à Autra |
CARD_SDK_ORIGIN_INVALID | 422 | Origem fora do formato https://host | Remover caminho, barra final e :443 |
ACQUIRING_NOT_READY | 409 | Estabelecimento ainda não credenciado para cartão | Aguardar o credenciamento |
SUBSCRIPTION_NOT_AWAITING_ENROLLMENT | 409 | A assinatura já tem cartão ou foi cancelada | Consultar a assinatura antes de abrir a sessão |
SUBSCRIPTION_NOT_FOUND | 404 | subscriptionId de outro estabelecimento ou inexistente | Conferir o id e o X-Merchant-Document |
EXTERNAL_REF_IN_USE | 409 | externalRef já usado com outro plano ou cliente | Usar outro externalRef |
PLAN_ARCHIVED | 422 | O plano está arquivado | Usar um plano ativo |
A referência completa de cada rota, com todos os campos, exemplos e erros:
| Rota | Referência |
|---|---|
POST /v1/oauth/token | Gerar token de acesso |
POST /v1/acquiring/subscription-plans | Criar plano de assinatura |
GET /v1/acquiring/subscription-plans | Listar planos do estabelecimento |
GET /v1/acquiring/subscription-plans/{id} | Consultar plano |
PATCH /v1/acquiring/subscription-plans/{id} | Renomear ou arquivar plano |
POST /v1/acquiring/subscriptions | Criar assinatura |
GET /v1/acquiring/subscriptions | Listar assinaturas |
GET /v1/acquiring/subscriptions/{id} | Consultar assinatura |
GET /v1/acquiring/subscriptions/{id}/invoices | Listar faturas da assinatura |
POST /v1/acquiring/subscriptions/{id}/suspend | Suspender assinatura |
POST /v1/acquiring/subscriptions/{id}/reactivate | Reativar assinatura suspensa |
POST /v1/acquiring/subscriptions/{id}/cancel | Cancelar assinatura |
POST /v1/acquiring/card-sessions | Create hosted card session |
PUT /v1/banking/webhooks/{domain} | Criar ou atualizar webhook |
| Eventos e validação da assinatura | Outbound Webhooks |
Checklist para produção
- Módulo de assinaturas e SDK web habilitados no tenant
- Estabelecimento credenciado para cartão
- Origem de produção do site cadastrada
- Credenciais de produção no backend e IPs de saída liberados
- Webhooks cadastrados em
subscriptionseacquiring, com a assinaturaX-Autra-Signaturevalidada - Acesso do cliente liberado pelo webhook
subscription.activated, e não pelo navegador - Tratamento de eventos repetidos sem efeito duplicado
- Uma adesão de teste feita de ponta a ponta com cartão real
