Skip to main content

Visão geral

O sistema de assinaturas da PlugToPay opera em três camadas:
ObjetoResponsabilidade
PlanoDefine valor, intervalo, período de trial e regras do ciclo
CupomDesconto percentual ou fixo aplicável a N ciclos
AssinaturaVincula cliente + cartão + plano e orquestra as cobranças
O fluxo básico é: criar plano → criar assinatura → coletar cartão via Checkout → cobrar. A partir daí, a PlugToPay cobra automaticamente a cada ciclo, com retentativas em caso de falha, e dispara webhooks a cada evento relevante.

Criar assinatura

POST /api/v1/subscriptions — requer X-Client-ID + X-API-Key.
{
  "plan_uuid": "018f1a2b-3c4d-7e5f-a6b7-c8d9e0f1a2b3",
  "customer_token": "cus_7556...",
  "card_token": "card_86123...",
  "coupon_code": "VERAO30",
  "billing_day": 10,
  "metadata": {
    "internal_ref": "user_789"
  }
}
CampoTipoObrigatórioDescrição
plan_uuiduuidsimUUID do plano existente e ativo
customer_tokenstringsimToken do cliente (customer.token)
card_tokenstringnão*Token do cartão (card.token). *Obrigatório para planos sem trial ou com trial_period_days: 0
coupon_codestringnãoCódigo do cupom a aplicar desde o primeiro ciclo
billing_dayinteiro (1–28)nãoSobrepõe o billing_day do plano para esta assinatura
metadataobjetonãoDados livres associados à assinatura (IDs internos, referências, etc.)
O sistema impede a criação de uma segunda assinatura ativa do mesmo cliente no mesmo plano. Para mudar o plano, use a troca de plano em vez de cancelar e recriar.
Validação de min_card_months: se o plano tiver esse campo definido, o cartão informado deve ter validade mínima de min_card_months meses a partir de hoje. Uma assinatura anual com min_card_months: 13, por exemplo, impede o uso de um cartão que vence em 6 meses. Resposta 201:
{
  "id": "01a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
  "status": "trialing",
  "plan": {
    "id": "018f1a2b-3c4d-7e5f-a6b7-c8d9e0f1a2b3",
    "name": "Pro Mensal",
    "amount": 2990,
    "interval": "monthly",
    "interval_count": 1
  },
  "customer": {
    "id": 42,
    "name": "João Silva",
    "email": "joao@example.com",
    "token": "cus_7556..."
  },
  "card": {
    "last_4_digits": "1111",
    "brand": "visa",
    "expire_month": "12",
    "expire_year": "2028"
  },
  "coupon": {
    "code": "VERAO30",
    "discount_type": "percentage",
    "discount_value": 30,
    "max_cycles": 3
  },
  "amount": 2093,
  "original_amount": 2990,
  "discount_amount": 897,
  "currency": "BRL",
  "interval": "monthly",
  "interval_count": 1,
  "billing_day": 10,
  "trial": {
    "period_days": 7,
    "start": "2026-06-25T12:00:00+00:00",
    "end": "2026-07-02T12:00:00+00:00"
  },
  "current_period": {
    "start": "2026-06-25T12:00:00+00:00",
    "end": "2026-07-02T12:00:00+00:00"
  },
  "next_billing_at": "2026-07-02T12:00:00+00:00",
  "paused_at": null,
  "paused_until": null,
  "canceled_at": null,
  "cancel_at": null,
  "cancel_at_period_end": false,
  "plan_change_at": null,
  "new_plan_id": null,
  "metadata": { "internal_ref": "user_789" },
  "created_at": "2026-06-25T12:00:00+00:00",
  "updated_at": "2026-06-25T12:00:00+00:00"
}

Status possíveis

StatusDescrição
incompleteAssinatura criada, aguardando cartão do cliente
trialingEm período de trial — nenhuma cobrança ainda
activeFaturando normalmente
past_dueTodas as retentativas esgotadas — requer ação do cliente
pausedFaturamento suspenso temporariamente
canceledEncerrada

Ciclo de faturamento

Quando next_billing_at é atingido, a PlugToPay:
  1. Cria um ciclo pending para o período
  2. Aplica o desconto do cupom (se elegível pelo max_cycles)
  3. Tenta cobrar o cartão via CreatePaymentCardService (mesmo fluxo de pagamento avulso com cartão tokenizado)
  4. Sucesso → ciclo marcado como paid, período avança, webhook subscription.payment.success
  5. Falha → ciclo marcado como failed, retentativas agendadas, webhook subscription.payment.failed
Lógica de retentativas:
TentativaQuando
1ª (original)Na data de vencimento
2ª retry+1 dia após a falha original
3ª retry+3 dias após a falha original
4ª retry+5 dias após a falha original
Após a 4ª tentativa sem sucesso, a assinatura vai para past_due e o webhook subscription.past_due é disparado. Nesse estado, nenhuma nova cobrança automática ocorre — o cliente deve atualizar o cartão. Cálculo do billing_day:
  • weekly: o billing_day é ignorado; o ciclo avança exatamente pelo número de semanas definido
  • monthly / yearly: a cobrança ocorre no billing_day do próximo mês/ano (sempre limitado a 28)

Listar assinaturas

GET /api/v1/subscriptions — requer X-Client-ID + X-API-Key. Retorna lista paginada. Suporta filtros (consultar via parâmetros de query).

Consultar assinatura

GET /api/v1/subscriptions/{uuid} — requer X-Client-ID + X-API-Key. Retorna o objeto completo com plan, customer, card, coupon e datas de período.

Listar ciclos

GET /api/v1/subscriptions/{uuid}/cycles — requer X-Client-ID + X-API-Key. Retorna o histórico de ciclos de faturamento da assinatura:
[
  {
    "id": "01b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d",
    "subscription_id": "01a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
    "period_start": "2026-07-02T00:00:00+00:00",
    "period_end": "2026-08-10T00:00:00+00:00",
    "amount": 2093,
    "discount_amount": 897,
    "status": "paid",
    "due_at": "2026-07-10T00:00:00+00:00",
    "paid_at": "2026-07-10T08:45:00+00:00",
    "failed_at": null,
    "retry_count": 0,
    "next_retry_at": null,
    "gateway_transaction_id": "019ddb56-fbe3-72e1-9f3c-8d0b2faf8f73",
    "created_at": "2026-07-10T00:00:00+00:00"
  }
]
CampoDescrição
statuspending|paid|failed|waived
retry_countNúmero de retentativas já realizadas (máx. 3)
next_retry_atPróxima tentativa agendada (null se esgotadas ou bem-sucedido)
gateway_transaction_idUUID da transação no gateway_transactions (consulta via GET /api/v1/payments/{id})

Coletando o cartão via Checkout

Use o Checkout Session para coletar os dados do cartão do cliente no frontend sem expor sua chave de API. O fluxo funciona em dois estágios: primeiro a assinatura é criada (ficando no estado incomplete), depois o cartão é coletado e a primeira cobrança é disparada automaticamente.
Assinaturas em planos com trial ficam em trialing ao invés de incomplete. Nesse caso, o cartão é salvo mas a cobrança só ocorre quando o trial encerrar.

Passo 1 — Criar a assinatura sem cartão

POST /api/v1/subscriptions
{
  "plan_uuid": "018f1a2b-3c4d-7e5f-a6b7-c8d9e0f1a2b3",
  "customer_token": "cus_7556..."
}
A resposta retorna a assinatura com status: "incomplete" e o id que será usado no próximo passo.

Passo 2 — Criar uma sessão de checkout vinculada à assinatura

POST /api/v1/checkout/sessions — requer X-Client-ID + X-API-Key.
{
  "amount": 2990,
  "currency": "BRL",
  "merchant_order_id": "SIGNUP-user_789",
  "payment_method": "card",
  "subscription_id": "01a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
  "customer": {
    "token": "cus_7556..."
  }
}
CampoDescrição
subscription_idUUID da assinatura em incomplete ou trialing. A sessão só pode ser vinculada a assinaturas nestes dois estados.
customerIdentifica o cliente para carregar os cartões salvos (saved_cards na resposta).
Resposta:
{
  "token": "cs_live_a1b2c3d4...",
  "amount": 2990,
  "currency": "BRL",
  "merchant_order_id": "SIGNUP-user_789",
  "payment_method": "card",
  "expires_at": "2026-06-26T15:00:00+00:00",
  "saved_cards": [
    {
      "token": "card_abc123",
      "holder_name": "JOAO SILVA",
      "brand": "visa",
      "first_6_digits": "411111",
      "last_4_digits": "1111",
      "expire_month": "12",
      "expire_year": "2029"
    }
  ]
}
Passe o token (cs_live_...) ao frontend. O array saved_cards pode ser exibido para que o cliente escolha um cartão já cadastrado.

Passo 3 — Submeter o cartão pelo frontend

POST /api/v1/checkout/subscriptions/card — autenticado via X-Checkout-Token.
X-Checkout-Token: cs_live_a1b2c3d4...
{
  "card": {
    "holder": "JOAO SILVA",
    "number": "4111111111111111",
    "cvv": "123",
    "expiration_month": "12",
    "expiration_year": "2028",
    "holder_document": "12345678901",
    "holder_birthdate": "1990-01-15",
    "holder_phone": "11987654321"
  },
  "billing_address": {
    "street": "Rua das Flores",
    "number": "123",
    "neighborhood": "Centro",
    "zip_code": "01310100",
    "city": "São Paulo",
    "state": "SP",
    "country": "BR"
  }
}
CampoObrigatórioDescrição
card.holdersimNome do titular (mínimo 2 palavras)
card.numbersimNúmero do cartão (16 dígitos)
card.cvvsimCódigo de segurança (3 ou 4 dígitos)
card.expiration_monthsimMês de validade (0112)
card.expiration_yearsimAno de validade (4 dígitos)
card.holder_documentsimCPF ou CNPJ do titular
card.holder_birthdatesimData de nascimento (yyyy-mm-dd ou dd/mm/yyyy)
card.holder_phonesimTelefone com DDD (ex: 11987654321)
billing_address.streetsimLogradouro
billing_address.numbersimNúmero
billing_address.neighborhoodsimBairro
billing_address.zip_codesimCEP (8 dígitos, somente números)
billing_address.citysimCidade
billing_address.statesimUF (2 letras, ex: SP)
billing_address.countrysimPaís (ex: BR)
O X-Checkout-Token é de uso único. Após este request ser processado, o token é invalidado automaticamente. Crie uma nova sessão se precisar tentar novamente.
Resposta — assinatura atualizada:
{
  "id": "01a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
  "status": "active",
  "card": {
    "last_4_digits": "1111",
    "brand": "visa",
    "expire_month": "12",
    "expire_year": "2028"
  },
  "next_billing_at": "2026-07-26T00:00:00+00:00"
}
Para assinaturas que estavam em incomplete, a cobrança do primeiro ciclo é disparada imediatamente. Se o cartão for aprovado, status passa para active. Se for recusado, passa para past_due e as retentativas são agendadas automaticamente.

Diagrama do fluxo completo

Seu backend                     PlugToPay                    Frontend do cliente
     │                               │                               │
     │── POST /subscriptions ───────►│                               │
     │◄─ { id, status: incomplete } ─│                               │
     │                               │                               │
     │── POST /checkout/sessions ───►│                               │
     │   { subscription_id, ... }    │                               │
     │◄─ { token: "cs_live_..." } ───│                               │
     │                               │                               │
     │─── envia cs_live_... ────────────────────────────────────────►│
     │                               │                               │
     │                               │◄─ POST /checkout/subscriptions/card ─│
     │                               │   X-Checkout-Token: cs_live_... │
     │                               │   { card: { ... } }           │
     │                               │                               │
     │                               │── tokeniza cartão ──────────► │
     │                               │── cobra primeira parcela ────► │
     │                               │                               │
     │                               │─── { status: active, ... } ──►│
     │◄──── webhook subscription.payment.success ────────────────────│

Referência rápida de rotas

Todos os endpoints requerem X-Client-ID + X-API-Key, exceto POST /api/v1/checkout/subscriptions/card que usa X-Checkout-Token.
MétodoRotaDescrição
POST/api/v1/subscriptions/plansCriar plano
GET/api/v1/subscriptions/plansListar planos
GET/api/v1/subscriptions/plans/{uuid}Detalhar plano
PUT/api/v1/subscriptions/plans/{uuid}Atualizar plano
DELETE/api/v1/subscriptions/plans/{uuid}Deletar plano
POST/api/v1/subscriptions/couponsCriar cupom
GET/api/v1/subscriptions/couponsListar cupons
GET/api/v1/subscriptions/coupons/{uuid}Detalhar cupom
PUT/api/v1/subscriptions/coupons/{uuid}Atualizar cupom
DELETE/api/v1/subscriptions/coupons/{uuid}Deletar cupom
POST/api/v1/subscriptionsCriar assinatura
GET/api/v1/subscriptionsListar assinaturas
GET/api/v1/subscriptions/{uuid}Detalhar assinatura
DELETE/api/v1/subscriptions/{uuid}Cancelar assinatura
PATCH/api/v1/subscriptions/{uuid}/pausePausar
PATCH/api/v1/subscriptions/{uuid}/resumeRetomar
PATCH/api/v1/subscriptions/{uuid}/planTrocar plano
PATCH/api/v1/subscriptions/{uuid}/cardAtualizar cartão
POST/api/v1/subscriptions/{uuid}/anticipateAntecipar cobrança
POST/api/v1/subscriptions/{uuid}/couponAplicar cupom
GET/api/v1/subscriptions/{uuid}/cyclesListar ciclos
POST/api/v1/checkout/sessionsCriar sessão de checkout (com subscription_id)
POST/api/v1/checkout/subscriptions/cardSubmeter cartão via checkout token