> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chargefy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Crie assinaturas para seu SaaS

> Trial, pró-rata, régua de cobrança recorrente automática e mais.

Este guia leva seu SaaS **do plano ao segundo mês cobrado sozinho**. Você modela
o plano uma vez, o cliente assina uma vez — e a partir daí cada renovação abre a
fatura, cobra o cartão salvo e avisa seu sistema por evento. Recorrência de
verdade não é um cron passando cartão: **as retentativas, a régua de cobrança e
os eventos de ciclo já vêm prontos**. Seu trabalho é reagir a meia dúzia de
webhooks.

A linha do tempo de um assinante com trial de 14 dias:

| Quando        | O que acontece                                 | Resultado                     |
| ------------- | ---------------------------------------------- | ----------------------------- |
| Dia 0         | O cliente assina no checkout                   | `status: trialing`            |
| Dia 11        | A Chargefy avisa que o trial está perto do fim | `subscription.trial.will.end` |
| Dia 14        | O cartão salvo recebe a primeira cobrança      | `status: active`              |
| Dia 44        | O segundo mês é cobrado automaticamente        | `invoice.paid`                |
| Dias 74, 104… | O mesmo processo se repete                     | Um novo ciclo por período     |

<Note>
  Como nos outros guias, tudo roda em **test mode** (`ch_test_...`), sem
  dinheiro real. Se esta é a sua primeira integração, faça antes o [Aceitar o
  primeiro pagamento](/accept-your-first-payment) — ele deixa prontos
  a API key e o receiver de webhook que este guia reaproveita. Daqui em diante
  os exemplos mostram só o **payload**: o método e a rota ficam no título de
  cada bloco.
</Note>

## 1. Modele os planos: preço recorrente, trial no preço

Plano de SaaS na Chargefy é um **produto com preço recorrente**. O mesmo request
que você já conhece cria os dois — a diferença é o bloco `recurring`. E o trial
pode morar **no próprio preço**: todo checkout desse plano já nasce com o
período gratuito, sem configuração extra por venda.

```json POST /v1/products theme={"theme":"css-variables"}
{
  "name": "Plano Starter",
  "prices": [
    {
      "currency": "brl",
      "recurring": {
        "interval": "month",
        "trial_period_days": 14
      },
      "type": "recurring",
      "unit_amount": 4990
    }
  ]
}
```

A resposta traz o produto com o preço dentro — guarde o `prices[0].id`
(`price_LECZy65rvF8ws5V9` nos exemplos). Repita para o plano de cima, sem trial desta vez:

```json POST /v1/products theme={"theme":"css-variables"}
{
  "name": "Plano Pro",
  "prices": [
    {
      "currency": "brl",
      "recurring": {
        "interval": "month"
      },
      "type": "recurring",
      "unit_amount": 9990
    }
  ]
}
```

Esse é o `price_W8pwrLwf2Mup6hd4` — vamos usá-lo no upgrade. Intervalos disponíveis: `day`,
`week`, `month` e `year` (um plano anual é só outro preço no mesmo produto).

## 2. Venda a assinatura: o checkout faz o trabalho

Você não cria a assinatura na mão: cria uma **checkout session** com o preço
recorrente, e a sessão nasce em `mode: "subscription"` automaticamente. O
comprador preenche os dados, o cartão é salvo — e como o preço tem trial,
**nenhuma cobrança acontece agora**.

```json POST /v1/checkout-sessions theme={"theme":"css-variables"}
{
  "line_items": [
    {
      "price_id": "price_LECZy65rvF8ws5V9"
    }
  ],
  "metadata": {},
  "success_url": "https://meusite.com/app/bem-vindo"
}
```

Antes de redirecionar o usuário para a `url` da resposta, grave no seu banco a
relação entre o usuário do app e o `id` da checkout session. Essa relação é o
que permite reconhecer o usuário quando o webhook chegar.

Quando o comprador conclui, chega o `checkout.session.completed` com
`payment_status: "no_payment_required"` (trial: cartão salvo, nada cobrado) e
dois campos que você deve gravar:

```javascript theme={"theme":"css-variables"}
case 'checkout.session.completed': {
  const session = evt.data.object;
  // Ligue o usuário do seu app à assinatura e ao customer da Chargefy.
  const appUserId = await findAppUserByCheckoutSession(session.id);
  await linkSubscription(
    appUserId,
    session.subscription,      // "sub_SrDq4teYKmrQ3w8g"
    session.customer           // "cus_i218TvPPHTXPzk1z"
  );
  break;
}
```

Na `success_url`, não trate o tempo entre esse evento e a liberação do acesso
como falha. Mostre uma tela curta de ativação e consulte seu backend até a
tentativa ficar pronta. Veja o fluxo completo em
[Após receber com um Checkout](/payments/checkout-post-payment).

A partir daqui, `sub_SrDq4teYKmrQ3w8g` é o contrato recorrente. Os campos dele que seu SaaS
vai consultar ([objeto completo](/api-reference/subscriptions/object)):

| Campo                    | O que te diz                                                               |
| ------------------------ | -------------------------------------------------------------------------- |
| `status`                 | Onde a assinatura está no ciclo de vida (tabela na seção 4).               |
| `current_period_end`     | Fim do período já pago — até quando o acesso vale.                         |
| `trial_end`              | Quando o trial termina e a primeira cobrança acontece.                     |
| `items.data[]`           | O que é cobrado por ciclo. `items.data[0].id` (`si_*`) serve pro upgrade.  |
| `default_payment_method` | O cartão salvo que paga as renovações.                                     |
| `latest_invoice`         | A fatura mais recente do ciclo.                                            |
| `cancel_at`              | Quando a assinatura termina. `null` quando ela renova indefinidamente.     |
| `cancel_at_period_end`   | `true` quando o término foi expresso como "não renova depois deste ciclo". |

<Tip>
  Também dá para criar assinatura **direto pela API** (`POST /v1/subscriptions`
  com `customer` e `items`), sem checkout — útil em vendas assistidas e
  migrações. Veja [Criar assinatura](/api-reference/subscriptions/create).
</Tip>

### Vender com prazo

Nem toda assinatura renova para sempre. Plano anual entregue como um ano, curso
em 12 mensalidades, contrato com vigência — nesses casos o término faz parte da
venda, não é uma correção depois.

Declare o prazo junto com a venda e a assinatura encerra sozinha na data. Sem
lembrete no calendário, sem clique no dia certo, sem risco de cobrar de novo um
cliente que já tinha combinado o fim.

| Intenção do contrato       | Campo                               | Onde declarar                                                                            |
| -------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------- |
| Não renova após este ciclo | `cancel_at_period_end: true`        | `subscription_data` no checkout, ou create/update                                        |
| Termina nesta data         | `cancel_at: "2027-06-19T18:00:00Z"` | `subscription_data` no checkout, ou create/update                                        |
| N cobranças e encerra      | `phases[].iterations`               | [agendamento](/api-reference/subscription-schedules/create) com `end_behavior: "cancel"` |

Pelo checkout, o prazo viaja no mesmo `subscription_data` que já carrega o
trial:

```json theme={"theme":"css-variables"}
{
  "line_items": [
    {
      "price_id": "price_HT2k884zy9GpPH52",
      "quantity": 1
    }
  ],
  "subscription_data": {
    "cancel_at_period_end": true
  },
  "success_url": "https://meusite.com/sucesso"
}
```

Para "12 mensalidades e encerra", o agendamento converte a contagem em data
usando a cadência da assinatura — você não precisa calcular o vencimento nem
lidar com fim de mês:

```json theme={"theme":"css-variables"}
{
  "customer": "cus_i218TvPPHTXPzk1z",
  "end_behavior": "cancel",
  "phases": [
    {
      "default_payment_method": "pm_dzJyqbJ6Gzf4a9fQ",
      "items": [
        {
          "price": "price_HT2k884zy9GpPH52"
        }
      ],
      "iterations": 12
    }
  ]
}
```

<Note>
  Terminar no prazo combinado **não é cancelamento**. `canceled_at` continua
  `null` numa assinatura com prazo declarado, e ela conta como ativa enquanto
  estiver faturando. Quem quer encerrar agora usa [`DELETE /v1/subscriptions/   {id}`](/api-reference/subscriptions/delete).
</Note>

## 3. Do trial ao segundo mês: ninguém agenda nada

Sem cron, sem job, sem coluna `next_charge_at` no seu banco. A Chargefy cuida da
régua e te avisa em cada passo:

1. **Dia 11** — `subscription.trial.will.end` chega **3 dias antes** do fim do
   trial. É o gatilho pro e-mail de "seu período gratuito termina em 3 dias".
2. **Dia 14** — o trial vira cobrança: a fatura do primeiro ciclo é criada e o
   cartão salvo é cobrado. Chegam `invoice.paid` e `subscription.updated` com
   `status: "active"`.
3. **Dia 44** — o segundo mês cobra **sozinho**: nova fatura
   (`billing_reason: "subscription_cycle"`), cobrança no cartão padrão, período
   avançado. De novo `invoice.paid` e `subscription.updated` — agora com
   `previous_attributes` mostrando o `current_period_end` anterior.
4. **Dia 74, 104, 134...** — repete, ciclo após ciclo, até alguém cancelar.

<Note>
  E se o trial terminar **sem cartão salvo**?
  `trial_settings.end_behavior.missing_payment_method` decide: `create_invoice`
  (padrão) gera a fatura mesmo assim e a assinatura pode ficar `past_due`;
  `pause` congela em `paused` até voltar com cartão; `cancel` encerra. Detalhes
  em [Trial](/payments/subscription-trial).
</Note>

## 4. Libere e bloqueie acesso por eventos

O gate de acesso do seu SaaS é uma função do `status` da assinatura — e o status
chega por webhook, então seu banco fica em sincronia sem polling. No mesmo
receiver do guia anterior:

```javascript theme={"theme":"css-variables"}
case 'subscription.updated': {
  const sub = evt.data.object;
  // trialing e active têm acesso; past_due é a sua régua de tolerância.
  const hasAccess = ['trialing', 'active', 'past_due'].includes(sub.status);
  await setAccess(sub.id, hasAccess, sub.current_period_end);
  break;
}
case 'subscription.canceled':
  await revokeAccess(evt.data.object.id);
  break;
case 'invoice.payment.failed':
  await notifyBillingIssue(evt.data.object.subscription);
  break;
```

O mapa completo de status, na visão de quem libera acesso:

| `status`     | O que significa                                                    | Acesso (sugestão)                                                             |
| ------------ | ------------------------------------------------------------------ | ----------------------------------------------------------------------------- |
| `incomplete` | Criada sem trial, esperando a 1ª cobrança fechar.                  | Ainda não.                                                                    |
| `trialing`   | Período gratuito rodando; 1ª cobrança em `trial_end`.              | Sim.                                                                          |
| `active`     | Cobrança em dia.                                                   | Sim.                                                                          |
| `past_due`   | Uma renovação falhou; a recuperação automática está em curso.      | Você decide — muitos SaaS dão uns dias de graça.                              |
| `unpaid`     | Tentativas esgotadas; faturas seguem abertas para cobrança manual. | Não — mas é recuperável (seção 5).                                            |
| `paused`     | Trial acabou sem cartão e a política era pausar.                   | Não — retome com cartão via [`/resume`](/api-reference/subscriptions/resume). |
| `canceled`   | Encerrada. Voltar é assinar de novo.                               | Não.                                                                          |

## 5. Cartão falhou? A régua de recuperação já vem pronta

É aqui que "um cron passando cartão" quebra — e a Chargefy não. Quando uma
renovação é recusada:

1. A assinatura vai para `past_due` e a fatura fica `open`. Você recebe
   `invoice.payment.failed`, e a fatura passa a expor o estado da régua:
   `attempt_count` (quantas tentativas a agenda consumiu) e
   `next_payment_attempt` (quando a próxima roda). Cada tentativa executada
   continua registrada em `payments.data[]`.
2. A Chargefy **reprocessa a cobrança sozinha**, nos dias definidos pela
   política de recuperação da organização — configurável em Configurações →
   Recuperação, com agenda recomendada de até 8 tentativas em 2 semanas ou
   passos personalizados. O assinante recebe um e-mail por tentativa que
   falhou, com a marca da organização e um link que troca o cartão e recobra
   a fatura na hora. Recuperou: volta para `active` e chega `invoice.paid`.
3. Recusas que repetir não resolve — cartão perdido, roubado, número inválido
   — não são reapresentadas: a agenda continua viva (`next_payment_attempt`
   segue preenchido, `attempt_count` segue contando), esperando o cliente
   salvar um cartão novo. Salvou, a próxima tentativa executa com ele.
4. Esgotou a janela: aplica-se a ação final configurada — marcar como não
   paga (padrão), manter em atraso, ou cancelar a assinatura. Em `unpaid`, a
   cobrança automática para, mas as faturas continuam **abertas e cobráveis**
   — e **pagar qualquer fatura aberta reativa a assinatura**.

O funcionamento completo da régua — agenda, contadores, recusas definitivas e
métricas — está em
[Recuperação inteligente de receita](/payments/revenue-recovery).

E quando a régua automática não resolve, a recuperação manual tem dois atalhos
prontos:

* **Mande a fatura** — toda invoice tem `hosted_invoice_url`, uma página de
  pagamento pronta para WhatsApp ou e-mail.
* **Ou mande direto a troca de cartão** — um deep link do portal que já abre no
  formulário de novo cartão e reprocessa a fatura em aberto:

```json POST /v1/customer-portal-sessions theme={"theme":"css-variables"}
{
  "customer": "cus_i218TvPPHTXPzk1z",
  "flow_data": {
    "payment_method_update": {
      "invoice": "inv_58C5BSMPdJENTSgE"
    },
    "type": "payment_method_update"
  }
}
```

Quando o cliente conclui, o cartão novo vira o padrão e a fatura informada é
cobrada nele.

## 6. Upgrade e downgrade com pró-rata

Mudar de plano é **trocar o preço do item** da assinatura — um `POST`, e a
Chargefy calcula o pró-rata do período atual:

```json POST /v1/subscriptions/sub_SrDq4teYKmrQ3w8g theme={"theme":"css-variables"}
{
  "items": [
    {
      "id": "si_Etg7CZEuk71HZVAW",
      "price": "price_W8pwrLwf2Mup6hd4"
    }
  ],
  "proration_behavior": "create_prorations"
}
```

(`si_Etg7CZEuk71HZVAW` é o `items.data[0].id` da assinatura; `price_W8pwrLwf2Mup6hd4` é o Plano Pro da
seção 1. O preço novo precisa ter a mesma moeda e o mesmo intervalo.)

O que acontece com o meio do ciclo depende de `proration_behavior`:

| Valor                          | Efeito                                                                           | Uso típico                    |
| ------------------------------ | -------------------------------------------------------------------------------- | ----------------------------- |
| `create_prorations` *(padrão)* | Calcula o ajuste e lança na **próxima fatura**.                                  | Downgrade (vira crédito).     |
| `always_invoice`               | Calcula o ajuste e **fatura agora**; crédito do cliente entra antes da cobrança. | Upgrade com acesso imediato.  |
| `none`                         | Sem ajuste: o valor novo vale a partir da próxima renovação.                     | Cortesia ou correção interna. |

A conta é a justa: upgrade no meio do mês credita o tempo não usado do plano
antigo e cobra só o restante do ciclo no plano novo; downgrade gera **crédito
automático** abatido das próximas faturas. Em trial, a troca não gera ajuste — o
valor novo vale quando o trial acabar.

<Tip>
  Upgrade que só pode valer **depois de pago**? Combine `proration_behavior:
      "always_invoice"` com `payment_behavior: "pending_if_incomplete"`: a mudança
  fica retida em `pending_update` até a fatura de update ser paga. E para
  mostrar o valor do ajuste **antes** de aplicar, simule com [Prévia de
  invoice](/api-reference/invoice-previews/create). O passo a passo pelo
  dashboard está em [Atualizando assinaturas
  ativas](/payments/update-subscriptions-with-proration).
</Tip>

## 7. Portal do cliente: troca de cartão sem abrir chamado

O **portal do cliente** é a área de billing hospedada do seu SaaS: o cliente
troca o cartão, corrige dados de cobrança, baixa segunda via de fatura e cancela
a assinatura — com a sua marca, sem você construir tela nenhuma e sem ticket no
suporte.

Coloque um botão "Gerenciar assinatura" no seu app que cria uma sessão e
redireciona:

```json POST /v1/customer-portal-sessions theme={"theme":"css-variables"}
{
  "customer": "cus_i218TvPPHTXPzk1z",
  "return_url": "https://meusite.com/app/conta"
}
```

```json Resposta theme={"theme":"css-variables"}
{
  "id": "cps_UoqhC5g28Tt2EvH3oDsyuJ8H",
  "object": "customer_portal.session",
  "configuration": null,
  "created_at": "2026-07-14T15:00:00Z",
  "customer": "cus_i218TvPPHTXPzk1z",
  "expires_at": "2026-07-14T16:00:00Z",
  "flow": null,
  "livemode": false,
  "locale": null,
  "metadata": {},
  "return_url": "https://meusite.com/app/conta",
  "status": "created",
  "updated_at": null,
  "url": "https://billing.chargefy.io/portal/session/cps_UoqhC5g28Tt2EvH3oDsyuJ8H?authorization_code=..."
}
```

Redirecione para `url` na hora — o link é de uso único e vale por 1 hora. Com
`flow_data`, a sessão abre direto na tarefa: `payment_method_update` (trocar
cartão), `customer_update` (corrigir dados) ou `subscription_cancel`
(cancelamento guiado, agendado pro fim do período já pago). Tudo que o cliente
fizer no portal chega no seu backend pelos mesmos webhooks das seções
anteriores.

<Note>
  Upgrade e downgrade de plano são uma decisão de **pricing do seu produto** —
  exponha os planos no seu app e aplique a troca com a chamada da seção 6. O
  portal cuida do resto do autoatendimento: cartão, dados, faturas e
  cancelamento. Sem backend, há ainda o **link público** do portal (login por
  e-mail) — veja [Portal do cliente](/payments/customer-portal).
</Note>

## Checklist de produção

1. **Ambiente live**: chave `ch_live_...`, endpoint de webhook live e planos
   recriados no live mode (dados de teste não migram) — o mesmo checklist do
   [primeiro pagamento](/accept-your-first-payment#ir-para-produção).
2. **Política de trial definida**: dias de trial no preço e
   `trial_settings.end_behavior` escolhido conscientemente.
3. **Régua de `past_due` decidida**: bloquear na hora ou dar período de graça —
   e o e-mail de cobrança pronto para o `invoice.payment.failed`.
4. **Gate por evento, não por palpite**: acesso sai de `subscription.updated`,
   nunca de um cálculo local de datas.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Trial a fundo" icon="hourglass-half" href="/payments/subscription-trial">
    Trial por dias ou data fixa, coleta de cartão e as três políticas de fim de
    trial.
  </Card>

  <Card title="Atualizando assinaturas ativas" icon="scale-balanced" href="/payments/update-subscriptions-with-proration">
    O mesmo upgrade/downgrade da seção 6, feito pelo dashboard e com prévia
    visual.
  </Card>

  <Card title="Como ativar assinaturas incompletas" icon="circle-half-stroke" href="/payments/activate-incomplete-subscriptions">
    Assinatura criada sem trial: como ela sai de `incomplete` para `active`.
  </Card>

  <Card title="Migrar assinaturas" icon="truck-ramp-box" href="/payments/import-subscriptions">
    Já fatura em outro sistema? Importe as assinaturas ativas sem cobrar duas
    vezes.
  </Card>
</CardGroup>
