Allya Payments
Assinaturas

Criar assinatura

Criar uma assinatura recorrente com cartão de crédito via checkout hospedado em POST /v1/subscriptions.

A Allya Payments aceita assinaturas recorrentes nos três gateways usando cartão de crédito e checkout hospedado pelo próprio gateway. Você nunca envia número, CVV, titular ou validade: a Allya retorna checkoutUrl e o cliente final preenche os dados na página do gateway.

Pré-requisitos

  1. Conectar um gateway que suporte assinatura recorrente (Abacate Pay, Asaas ou Pagar.me): veja Gateways.
  2. Configurar o roteamento de assinatura em Roteamento → Assinaturas recorrentes para que card resolva para o gateway desejado.
  3. Apenas Pagar.me: criar um plano previamente na API do Pagar.me (POST /plans) e ter o plan.id em mãos: veja a seção Pagar.me: plano obrigatório abaixo.

Sem roteamento, a chamada retorna 409 no_routing_rule. Sem gateway.pagarme.planId quando o roteamento resolver para Pagar.me, retorna 400 invalid_input.

Endpoint

POST /v1/subscriptions
Authorization: Bearer sk_test_... (ou sk_live_...)
Content-Type: application/json

Body

{
  amount: number;        // BRL centavos, por ciclo
  currency: "BRL";
  interval: "monthly" | "yearly";
  trialDays?: number;    // dias de trial. Default 0.
  externalId: string;    // obrigatório. Idempotência por (environmentId, externalId).
  customer: {
    name: string;
    email: string;
    document: string;    // CPF/CNPJ
    phone?: string;      // obrigatório no Pagar.me
  };
  metadata?: Record<string, unknown>;
  card?: {
    installments?: number;  // ignorado; assinatura não parcela
  };
  gateway?: {
    pagarme?: {
      planId: string;       // ID do plano pré-criado no Pagar.me
    };
  };
}
CampoTipoNotas
amountnumberCentavos. Cobrado a cada ciclo. No Pagar.me, o valor real é o do plano referenciado por gateway.pagarme.planId; o amount aqui fica informacional.
currencystringApenas BRL. Aceito em qualquer caixa, normalizado para BRL. Outras moedas retornam 400 invalid_input.
intervalstringmonthly ou yearly. No Pagar.me, o intervalo real é o do plano.
trialDaysnumber0 (padrão) cria assinatura já cobrável; valores > 0 deixam a assinatura em trialing até o trial expirar. O suporte varia por gateway: veja Período de trial abaixo.
externalIdstringObrigatório em todos os gateways. Identificador único do seu sistema usado como chave de idempotência por ambiente. Recomendado identificar o cliente + oferta, não o ciclo (ex.: assinatura_cliente_42_plano_pro_mensal). Máx. 255 caracteres.
customer.phonestringObrigatório no Pagar.me.
gateway.pagarme.planIdstringObrigatório quando o roteamento resolver para Pagar.me. ID retornado por POST /plans na API do Pagar.me.

A Allya não usa header Idempotency-Key. Para assinaturas, a idempotência vem de externalId no body, obrigatório. Use um valor que identifique a assinatura no seu sistema, como assinatura_cliente_42_plano_pro_mensal, não a fatura de um ciclo específico.

Resposta

{
  "id": "sub_4a1c0e7b9d224f5fb8c6102d7e3a9014",
  "status": "active",
  "method": "card",
  "gateway": "asaas",
  "amount": 4990,
  "currency": "BRL",
  "interval": "monthly",
  "trialDays": 0,
  "trialEndsAt": null,
  "nextBillingAt": "2026-06-25T00:00:00.000Z",
  "cancelAtPeriodEnd": false,
  "canceledAt": null,
  "externalId": "plano-pro-12345",
  "gatewayRef": "gateway_subscription_id",
  "checkoutUrl": "https://checkout.gateway.com/...",
  "customer": {
    "name": "Cliente Teste",
    "email": "cliente@example.com",
    "documentLast4": "1234"
  },
  "createdAt": "2026-05-25T12:00:00.000Z"
}
CampoQuando aparece
checkoutUrlURL hospedada do gateway onde o cliente final insere o cartão. Redirecione para essa URL.
trialEndsAtISO date: só presente quando trialDays > 0.
nextBillingAtISO date: pode demorar a aparecer (depende do gateway confirmar o cartão).
cancelAtPeriodEndtrue enquanto o cancelamento agendado não foi efetivado pelo gateway.

Idempotência

Se você reenviar a mesma requisição com o mesmo externalId, a Allya retorna a assinatura existente em vez de criar uma duplicada. Não há 409 conflict por reenvio idempotente: o servidor retorna 201 com o mesmo recurso.

Período de trial

O comportamento de trialDays muda bastante entre os gateways. A Allya não esconde essas diferenças: cada gateway impõe as próprias regras e isso afeta o que você pode prometer ao cliente final.

GatewayAceita trialDays > 0?Como funciona
Abacate Pay✅ até 90 diasO valor de trialDays é enviado para o produto recorrente criado internamente. trialDays > 90 retorna 400 invalid_input antes de chamar o gateway.
AsaasA Allya retorna 400 invalid_input quando trialDays > 0. Use 0. Motivo abaixo.
Pagar.me✅ via planoO trial real é definido no campo trial_period_days do Plan criado em POST /plans no Pagar.me. O trialDays enviado para a Allya só preserva o status trialing no domínio local: não é repassado ao gateway.

Por que o Asaas não aceita trial

O fluxo de assinatura no Asaas envia o cliente para um checkout hospedado pelo próprio Asaas (a invoiceUrl da primeira cobrança da assinatura). A API do Asaas documenta que, nesse fluxo, assim que o cliente final insere o cartão na página hospedada, a cobrança é capturada imediatamente: independentemente da nextDueDate configurada na assinatura.

Como a Allya nunca recebe os dados do cartão (eles vão direto para o Asaas), não há como adiar a captura: o trial pediria que a primeira cobrança esperasse N dias, mas o gateway captura na hora do tap. Em vez de devolver um trial que silenciosamente não acontece, a Allya rejeita trialDays > 0 no Asaas no momento da chamada.

Se você precisa de trial e quer continuar no Asaas, gerencie o trial fora da Allya: libere acesso ao cliente no seu sistema durante o trial sem criar a assinatura, e chame POST /v1/subscriptions apenas quando o trial expirar. Quando o PIX recorrente do Asaas entrar, o fluxo de trial poderá voltar usando outro caminho.

Trial no Pagar.me

Como o Plan no Pagar.me carrega o trial, mudar o tempo de trial significa criar um plano novo: a API não permite editar trial_period_days de um plano em uso. Exemplo de criação com trial:

curl -X POST https://api.pagar.me/core/v5/plans \
  -u "sk_test_xxx:" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Mensal Pro com trial",
    "currency": "BRL",
    "interval": "month",
    "interval_count": 1,
    "billing_type": "prepaid",
    "payment_methods": ["credit_card"],
    "trial_period_days": 7,
    "items": [{ "name": "Pro", "quantity": 1, "pricing_scheme": { "price": 4990 } }]
  }'

Ao criar a assinatura na Allya, envie o mesmo trialDays para manter os campos trialEndsAt e o status trialing consistentes: mas tenha em mente que o que governa a cobrança é o plano.

Status normalizados

StatusSignificado
trialingTrial em andamento, sem cobrança real ainda.
activeCobrança em dia.
past_dueA última cobrança falhou; o gateway pode estar retentando.
canceledAssinatura encerrada (imediata ou ao fim do período).

Webhooks

Após criar, você recebe subscription.created. A cada ciclo bem-sucedido, subscription.renewed. Falhas de cobrança disparam subscription.payment_failed. No Pagar.me, o subscription.created público é emitido apenas quando o gateway confirmar a criação da assinatura real via webhook; a resposta inicial da API contém o checkout pl_.... Veja Webhooks.

Cada cobrança de ciclo também gera um Payment local com seus próprios eventos payment.*. Veja Status de assinatura para o significado de cada estado.

Abacate Pay

Para o Abacate Pay, a Allya cria internamente um produto recorrente antes de abrir o checkout de assinatura:

  • interval: "monthly" vira produto com cycle: "MONTHLY".
  • interval: "yearly" vira produto com cycle: "ANNUALLY".
  • trialDays, quando maior que zero, é enviado no produto recorrente, como exigido pelo gateway. Limite imposto pela Abacate Pay: 90 dias. Acima disso, 400 invalid_input antes da chamada.

O checkout de assinatura usa exatamente um item e retorna uma URL hospedada. Depois que o cliente conclui o checkout, a Abacate Pay envia webhooks subscription.*; a Allya usa esses eventos para trocar a referência inicial do checkout pelo ID real da assinatura no gateway.

Asaas

Para o Asaas, a Allya cria uma assinatura com billingType: "CREDIT_CARD" e sem dados sensíveis de cartão. Em seguida, consulta as cobranças da assinatura em /subscriptions/{id}/payments e retorna o invoiceUrl da primeira cobrança como checkoutUrl. É essa URL que o cliente final usa para informar o cartão no ambiente hospedado do Asaas.

As assinaturas do Asaas geram cobranças (PAYMENT_*) a cada ciclo. Quando o webhook da cobrança contém payment.subscription, a Allya cria ou atualiza um Payment local vinculado à assinatura, dispara o webhook de pagamento correspondente e sincroniza subscription.renewed ou subscription.payment_failed.

Trial não é suportado nesse fluxo: veja Por que o Asaas não aceita trial.

Pagar.me: plano obrigatório

Diferente do Abacate Pay e do Asaas (onde a Allya monta o plano inline a partir do request), o Pagar.me v5 exige que a recorrência referencie um Plan pré-cadastrado via POST /plans na API deles. O plan.id retornado é passado em gateway.pagarme.planId ao criar a assinatura pela Allya.

O fluxo completo, com exemplos e boas práticas, está em Plano obrigatório no Pagar.me.

Resumo: você cria o plano uma vez (na API do Pagar.me, com sk_test_… ou sk_live_…), guarda o plan.id, e passa em cada POST /v1/subscriptions quando o gateway roteado for Pagar.me. Quando o gateway é outro, o campo é ignorado: você pode passar sempre.

Limitações atuais

A versão atual cobre o caminho mais comum:

  • Apenas cartão de crédito nos três gateways.
  • PIX recorrente (suportado pelo Abacate Pay) está em breve.
  • Débito recorrente (suportado pelo Asaas) não está exposto.
  • Catálogo de planos, trocar cartão sem cancelar, upgrade/downgrade, proration, pause/resume e cupom ainda não estão disponíveis. Se precisar de algum destes hoje, fale com o time.

Veja também

On this page