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

# Assinaturas

> Cobrança recorrente com planos, cupons e ciclos de faturamento.

## Visão geral

O sistema de assinaturas da PlugToPay opera em três camadas:

| Objeto         | Responsabilidade                                            |
| -------------- | ----------------------------------------------------------- |
| **Plano**      | Define valor, intervalo, período de trial e regras do ciclo |
| **Cupom**      | Desconto percentual ou fixo aplicável a N ciclos            |
| **Assinatura** | Vincula 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`.

```json theme={null}
{
  "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"
  }
}
```

| Campo            | Tipo           | Obrigatório | Descrição                                                                                         |
| ---------------- | -------------- | ----------- | ------------------------------------------------------------------------------------------------- |
| `plan_uuid`      | uuid           | sim         | UUID do plano existente e ativo                                                                   |
| `customer_token` | string         | sim         | Token do cliente (`customer.token`)                                                               |
| `card_token`     | string         | não\*       | Token do cartão (`card.token`). \*Obrigatório para planos sem trial ou com `trial_period_days: 0` |
| `coupon_code`    | string         | não         | Código do cupom a aplicar desde o primeiro ciclo                                                  |
| `billing_day`    | inteiro (1–28) | não         | Sobrepõe o `billing_day` do plano para esta assinatura                                            |
| `metadata`       | objeto         | não         | Dados livres associados à assinatura (IDs internos, referências, etc.)                            |

<Info>
  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](/subscriptions/management#trocar-plano) em vez de cancelar e recriar.
</Info>

**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`:**

```json theme={null}
{
  "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

| Status       | Descrição                                                |
| ------------ | -------------------------------------------------------- |
| `incomplete` | Assinatura criada, aguardando cartão do cliente          |
| `trialing`   | Em período de trial — nenhuma cobrança ainda             |
| `active`     | Faturando normalmente                                    |
| `past_due`   | Todas as retentativas esgotadas — requer ação do cliente |
| `paused`     | Faturamento suspenso temporariamente                     |
| `canceled`   | Encerrada                                                |

***

## 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:**

| Tentativa     | Quando                        |
| ------------- | ----------------------------- |
| 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:

```json theme={null}
[
  {
    "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,
    "failed_reason": 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"
  }
]
```

| Campo                    | Descrição                                                                                                                                                                              |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`                 | `pending\|paid\|failed\|waived`                                                                                                                                                        |
| `retry_count`            | Número de retentativas já realizadas (máx. 3)                                                                                                                                          |
| `next_retry_at`          | Próxima tentativa agendada (`null` se esgotadas ou bem-sucedido)                                                                                                                       |
| `gateway_transaction_id` | UUID da transação no `gateway_transactions` (consulta via `GET /api/v1/payments/{id}`), preenchido mesmo quando a cobrança falha, desde que uma transação tenha sido criada no gateway |
| `failed_reason`          | Motivo do erro retornado pelo gateway (`null` se não houve falha)                                                                                                                      |

***

## 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.

<Info>
  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.
</Info>

### Passo 1 — Criar a assinatura sem cartão

`POST /api/v1/subscriptions`

```json theme={null}
{
  "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`.

```json theme={null}
{
  "amount": 2990,
  "currency": "BRL",
  "merchant_order_id": "SIGNUP-user_789",
  "payment_method": "card",
  "subscription_id": "01a2b3c4-d5e6-7f8a-9b0c-1d2e3f4a5b6c",
  "customer": {
    "token": "cus_7556..."
  }
}
```

| Campo             | Descrição                                                                                                           |
| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
| `subscription_id` | UUID da assinatura em `incomplete` ou `trialing`. A sessão só pode ser vinculada a assinaturas nestes dois estados. |
| `customer`        | Identifica o cliente para carregar os cartões salvos (`saved_cards` na resposta).                                   |

**Resposta:**

```json theme={null}
{
  "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`.

```http theme={null}
X-Checkout-Token: cs_live_a1b2c3d4...
```

```json theme={null}
{
  "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"
  }
}
```

| Campo                          | Obrigatório | Descrição                                         |
| ------------------------------ | ----------- | ------------------------------------------------- |
| `card.holder`                  | sim         | Nome do titular (mínimo 2 palavras)               |
| `card.number`                  | sim         | Número do cartão (16 dígitos)                     |
| `card.cvv`                     | sim         | Código de segurança (3 ou 4 dígitos)              |
| `card.expiration_month`        | sim         | Mês de validade (`01`–`12`)                       |
| `card.expiration_year`         | sim         | Ano de validade (4 dígitos)                       |
| `card.holder_document`         | sim         | CPF ou CNPJ do titular                            |
| `card.holder_birthdate`        | sim         | Data de nascimento (`yyyy-mm-dd` ou `dd/mm/yyyy`) |
| `card.holder_phone`            | sim         | Telefone com DDD (ex: `11987654321`)              |
| `billing_address.street`       | sim         | Logradouro                                        |
| `billing_address.number`       | sim         | Número                                            |
| `billing_address.neighborhood` | sim         | Bairro                                            |
| `billing_address.zip_code`     | sim         | CEP (8 dígitos, somente números)                  |
| `billing_address.city`         | sim         | Cidade                                            |
| `billing_address.state`        | sim         | UF (2 letras, ex: `SP`)                           |
| `billing_address.country`      | sim         | País (ex: `BR`)                                   |

<Warning>
  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.
</Warning>

**Resposta — assinatura atualizada:**

```json theme={null}
{
  "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étodo   | Rota                                      | Descrição                                        |
| -------- | ----------------------------------------- | ------------------------------------------------ |
| `POST`   | `/api/v1/subscriptions/plans`             | Criar plano                                      |
| `GET`    | `/api/v1/subscriptions/plans`             | Listar 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/coupons`           | Criar cupom                                      |
| `GET`    | `/api/v1/subscriptions/coupons`           | Listar 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/subscriptions`                   | Criar assinatura                                 |
| `GET`    | `/api/v1/subscriptions`                   | Listar assinaturas                               |
| `GET`    | `/api/v1/subscriptions/{uuid}`            | Detalhar assinatura                              |
| `DELETE` | `/api/v1/subscriptions/{uuid}`            | Cancelar assinatura                              |
| `PATCH`  | `/api/v1/subscriptions/{uuid}/pause`      | Pausar                                           |
| `PATCH`  | `/api/v1/subscriptions/{uuid}/resume`     | Retomar                                          |
| `PATCH`  | `/api/v1/subscriptions/{uuid}/plan`       | Trocar plano                                     |
| `PATCH`  | `/api/v1/subscriptions/{uuid}/card`       | Atualizar cartão                                 |
| `POST`   | `/api/v1/subscriptions/{uuid}/anticipate` | Antecipar cobrança                               |
| `POST`   | `/api/v1/subscriptions/{uuid}/coupon`     | Aplicar cupom                                    |
| `GET`    | `/api/v1/subscriptions/{uuid}/cycles`     | Listar ciclos                                    |
| `POST`   | `/api/v1/checkout/sessions`               | Criar sessão de checkout (com `subscription_id`) |
| `POST`   | `/api/v1/checkout/subscriptions/card`     | Submeter cartão via checkout token               |
