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

# Checkout Sessions

> A tentativa única de compra — uma sessão descartável que coleta o comprador, apresenta os métodos de pagamento e confirma a cobrança.

Uma **checkout session** representa **uma única tentativa de compra**. Ela nasce para uma cobrança específica — com itens, valores e configuração visual já resolvidos — coleta os dados do comprador, apresenta os métodos de pagamento disponíveis e confirma a escolha dele. Quando você cria uma sessão, a Chargefy devolve uma `url` (a página hospedada onde o comprador paga) e um `client_secret` (lido pelo browser do comprador para abrir essa página).

<Info>
  **Session ≠ Payment Link**

  Um [payment link](/features/payment-links) é o **molde**: uma URL pública e reutilizável que vende sempre a mesma oferta. A **session** é **uma instância** dessa intenção de compra — descartável, ligada a um comprador, que expira e não volta a `open` depois de confirmada. Cada clique no payment link materializa uma session nova.
</Info>

## Quando usar

Use checkout sessions quando o seu backend precisa criar uma tentativa de compra **sob demanda**, com dados que mudam a cada pedido.

* O cliente clicou em "Finalizar compra" no seu app e o carrinho dele é único.
* O valor, os itens ou o cupom mudam a cada pedido.
* Você quer `success_url`/`cancel_url` específicas daquela compra.
* Você quer controlar a experiência pelo seu próprio frontend, usando o `client_secret`.

Se você quer uma URL fixa para vender sempre a mesma oferta, sem um backend criando cada tentativa, use [Payment Links](/features/payment-links).

## Modelo conceitual

```mermaid theme={}
flowchart LR
  B[Seu backend] -- POST /v1/checkout-sessions --> S[checkout.session]
  PL[Payment Link] -- clique do comprador --> S
  S -- url --> H[Página hospedada]
  S -- client_secret --> F[Frontend próprio]
  H -- confirm --> PI[payment_intent / invoices / webhooks]
  F -- confirm --> PI
```

A session é o **palco da compra**, não o livro-razão financeiro. Quem registra o resultado do pagamento são o `payment_status`, o `payment_intent` relacionado, as invoices (quando aplicável) e os webhooks. A session só reflete o que aconteceu.

| Objeto               | Responsabilidade                                                      |
| -------------------- | --------------------------------------------------------------------- |
| **checkout.session** | A tentativa de compra: itens, comprador, métodos, status, expiração.  |
| **payment\_intent**  | O lifecycle financeiro da cobrança ligada à sessão.                   |
| **invoice**          | Documento fiscal/contábil materializado no sucesso, quando aplicável. |
| **webhook**          | A fonte confiável do resultado — use-o para liberar produto.          |

## Como funciona

<Steps>
  <Step title="Seu backend cria a sessão">
    `POST /v1/checkout-sessions` com `line_items`, customer opcional, URLs de retorno e `metadata`.
  </Step>

  <Step title="A Chargefy devolve url + client_secret">
    A resposta inclui `url` (página hospedada) e `client_secret` (runtime do browser). A sessão nasce em `status: "open"`, válida por **24 horas**.
  </Step>

  <Step title="O comprador paga">
    Na página hospedada, a Chargefy coleta os dados e confirma. Em frontend próprio, o browser do comprador chama o endpoint público de confirmação com o `client_secret`.

    A coleta tem um piso por método: **cartão e PIX** pedem nome e email; **boleto** pede nome, email, endereço e documento (CPF/CNPJ), por regulamentação. Sobre esse piso, a **política de checkout da organização** decide se também exige documento (padrão: sim), telefone e endereço em qualquer método — a sessão carrega isso em `require_document`, `require_phone` e `require_billing_address`. A política só adiciona exigências, nunca afrouxa o piso do método. Cartão é sempre **tokenizado no browser** — o número nunca toca seu servidor. Detalhes em [Confirmar checkout session](/api-reference/checkout-sessions/confirm).
  </Step>

  <Step title="Você acompanha por webhooks">
    Use os eventos `checkout.session.*` e os de pagamento para liberar produto, atualizar pedido e reconciliar valores.
  </Step>
</Steps>

## Criar uma sessão

Cada item de `line_items` aceita **exatamente uma** de três formas. O schema completo do create está em [Criar checkout session](/api-reference/checkout-sessions/create).

<CodeGroup>
  ```bash Preço do catálogo theme={}
  curl -X POST https://api.chargefy.io/v1/checkout-sessions \
    -H "Authorization: Bearer {{API_KEY}}" \
    -H "Content-Type: application/json" \
    -d '{
      "line_items": [
        { "price_id": "price_789", "quantity": 1 }
      ],
      "success_url": "https://meusite.com/sucesso",
      "metadata": { "order_id": "id_456" }
    }'
  ```

  ```bash Produto do catálogo + preço ad-hoc theme={}
  curl -X POST https://api.chargefy.io/v1/checkout-sessions \
    -H "Authorization: Bearer {{API_KEY}}" \
    -H "Content-Type: application/json" \
    -d '{
      "line_items": [
        {
          "price_data": {
            "currency": "brl",
            "product_id": "prod_456",
            "unit_amount": 12990
          },
          "quantity": 1
        }
      ],
      "success_url": "https://meusite.com/sucesso"
    }'
  ```

  ```bash Produto e preço ad-hoc theme={}
  curl -X POST https://api.chargefy.io/v1/checkout-sessions \
    -H "Authorization: Bearer {{API_KEY}}" \
    -H "Content-Type: application/json" \
    -d '{
      "line_items": [
        {
          "price_data": {
            "currency": "brl",
            "product_data": { "name": "Consultoria avulsa" },
            "unit_amount": 4990
          },
          "quantity": 1
        }
      ],
      "success_url": "https://meusite.com/sucesso"
    }'
  ```
</CodeGroup>

Resposta (reduzida — o objeto completo está em [Objeto checkout.session](/api-reference/checkout-sessions)):

```json theme={}
{
  "id": "cs_123",
  "object": "checkout.session",
  "client_secret": "9f4c2a1b8e3d7f06a5c4b2e1d8f3a6b09c5e2a1f4b7d8c3e6a9f1d2c4b5e8a0f",
  "metadata": {
    "order_id": "id_456"
  },
  "mode": "payment",
  "payment_status": "unpaid",
  "status": "open",
  "url": "https://pay.chargefy.io/session/9f4c2a1b8e3d7f06a5c4b2e1d8f3a6b09c5e2a1f4b7d8c3e6a9f1d2c4b5e8a0f"
}
```

### As três variantes de `line_items`

| Variante                      | Quando usar                                           | O que a Chargefy resolve                                  |
| ----------------------------- | ----------------------------------------------------- | --------------------------------------------------------- |
| `price_id`                    | Caminho mais curto — preço já cadastrado no catálogo. | Produto, valor e recorrência a partir do preço.           |
| `price_data` + `product_id`   | Produto cadastrado, mas com valor único desta sessão. | Reusa nome/descrição do produto; **não** cria preço novo. |
| `price_data` + `product_data` | Integração headless que não cadastra catálogo.        | Nada — produto e valor são ad-hoc, só desta sessão.       |

<Warning>
  Regras do array, validadas no create:

  * Todos os itens compartilham a mesma `currency`.
  * Ou **todos** os itens são recorrentes, ou **nenhum** é — misturar retorna `400`.
  * Cada item tem exatamente um entre `price_id` e `price_data`.
  * Com `price_data`, exatamente um entre `product_id` e `product_data`.
</Warning>

## Vincular a um cliente

Como a sessão se relaciona ao `customer` depende do que você envia no create:

| O que você envia                                        | Comportamento                                                                                                                 |
| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `customer` (id de um cliente existente)                 | A sessão fica **travada** nesse cliente. O e-mail aparece preenchido e somente leitura na página; o `id` do cliente não muda. |
| `customer_email` / `customer_document` (sem `customer`) | Apenas **pré-preenchem** os campos. Nenhum cliente é criado no create — a resolução acontece no confirm.                      |
| Nada                                                    | Checkout de convidado. Os campos ficam editáveis e o cliente é criado no confirm.                                             |

Em todos os casos, **o cliente é resolvido uma única vez no confirm**, com os dados finais preenchidos pelo comprador. Sem `customer`, a Chargefy **cria um cliente novo** — o e-mail não é usado para reaproveitar um cliente existente (ele não é chave de identidade). Para reusar um cliente do seu sistema, faça o lookup na sua base e passe o `customer`. O `metadata` continua sendo só seu — a Chargefy nunca injeta chaves próprias nele.

## url vs. client\_secret

A resposta entrega dois caminhos para o checkout. Você escolhe um.

| Campo           | Para que serve                                                                    | Quem lê                                              |
| --------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------- |
| `url`           | A página hospedada da Chargefy, pronta. Redirecione o comprador para ela.         | O navegador do comprador (redirect).                 |
| `client_secret` | String opaca de 64 caracteres hex, sem prefixo. Abre a sessão sem o token de API. | O **browser** do comprador, no seu frontend próprio. |

<Note>
  O `client_secret` **não é segredo de servidor** — é credencial de runtime do
  browser. Ele autentica as chamadas públicas de consulta e de confirmação da
  sessão (`GET /v1/checkout-sessions/public/:client_secret` e `POST
      .../confirm`), então o seu frontend custom pode operar sem expor a sua API
  key. O `id` (`cs_123`) é o handle server-side, usado em `GET
      /v1/checkout-sessions/:id` com a sua API key.
</Note>

## O que pode ser vendido

A `mode` da sessão é **derivada** dos `line_items` — você não a envia.

| `mode`         | Origem                                     | Resultado                                                |
| -------------- | ------------------------------------------ | -------------------------------------------------------- |
| `payment`      | Itens de cobrança única (`one_time`).      | O checkout cobra uma vez e encerra a sessão.             |
| `subscription` | Itens recorrentes (preço com `recurring`). | Após a confirmação, a sessão materializa a subscription. |

## Métodos de pagamento

Os métodos disponíveis vêm em `payment_method_types`, resolvido **no momento do create**: a organização precisa ter o cadastro financeiro ativo e escolhe, na política de checkout, quais métodos oferecer (padrão: cartão e PIX). Esse conjunto é congelado na sessão (snapshot imutável) — a hosted page só oferece esses métodos, e a consulta, a confirmação e os webhooks devolvem exatamente o mesmo conjunto, ainda que a política da organização mude depois.

| Método        | Comportamento                                                                                        |
| ------------- | ---------------------------------------------------------------------------------------------------- |
| `credit_card` | Confirmação normalmente síncrona; `payment_status` pode virar `paid` na própria resposta do confirm. |
| `pix`         | O checkout gera o QR Code e aguarda a compensação **assíncrona**.                                    |
| `boleto`      | O checkout gera o boleto e aguarda a compensação **assíncrona**.                                     |

<Warning>
  **PIX e boleto liquidam de forma assíncrona.** A sessão pode ficar `status:
      "complete"` com `payment_status: "unpaid"` até a compensação chegar. Confie no
  webhook
  [`checkout.session.async.payment.succeeded`](/api-reference/webhooks/checkout.session.async.payment.succeeded),
  nunca no retorno imediato do confirm, para liberar o produto.
</Warning>

<Info>
  Se a organização ainda não concluiu o cadastro financeiro, a sessão nasce com
  `payment_method_types: []`. Trate o array vazio antes de redirecionar o
  comprador.
</Info>

## Estados da sessão

A sessão tem dois eixos de estado independentes: `status` (o ciclo da tentativa) e `payment_status` (o resultado financeiro).

| `status`   | Significado                                                         |
| ---------- | ------------------------------------------------------------------- |
| `open`     | Criada, aguardando o comprador.                                     |
| `complete` | O comprador concluiu o checkout. **Terminal** — não volta a `open`. |
| `expired`  | 24h sem confirmação. **Terminal**.                                  |

| `payment_status`      | Significado                                                                |
| --------------------- | -------------------------------------------------------------------------- |
| `unpaid`              | Pagamento ainda não confirmado (inclui PIX/boleto aguardando compensação). |
| `paid`                | Pagamento confirmado.                                                      |
| `no_payment_required` | Total igual a zero — nada a cobrar.                                        |

<Note>
  `complete` e `paid` são eixos diferentes: uma sessão de PIX/boleto fica
  `complete` (o comprador terminou o formulário e recebeu os dados de pagamento)
  mas `unpaid` até a compensação. A reconciliação financeira sobe o
  `payment_status` para `paid` quando a cobrança liquida.
</Note>

## Expiração

Toda sessão expira em **24 horas** após o `created_at` — veja `expires_at` no objeto. Uma sessão `expired` é terminal: ela não volta a `open` e não pode ser confirmada. Para tentar a compra de novo, crie uma sessão nova.

<Tip>
  Como a session é descartável por natureza, não a use como referência de longo
  prazo no seu sistema. Correlacione pelo seu próprio identificador via
  `metadata` (veja abaixo) e trate o resultado pelos webhooks.
</Tip>

## Aparência (branding)

A página hospedada usa a identidade visual da **organização**: cor de marca, cor de destaque, fonte, tema e formato de borda. Você configura isso uma vez em [Atualizar organização](/api-reference/organizations/update) (campo `branding_settings`) e vale para todas as sessões dela.

Uma sessão pode **sobrescrever** campos visuais pontualmente — útil para uma campanha ou página específica — enviando `branding_settings` no create. O contrato é por campo: cada campo presente vence o default; cada campo ausente herda. Omitir o objeto (ou enviar `null`) herda tudo.

| Campo          | O que controla                                |
| -------------- | --------------------------------------------- |
| `brand_color`  | Cor de marca — painel e cabeçalho.            |
| `accent_color` | Cor de destaque — botão de ação, foco, links. |
| `font_family`  | `system`, `inter`, `geist` ou `bitter`.       |
| `theme`        | `light` ou `dark`.                            |
| `border_style` | `pill`, `rounded` ou `sharp`.                 |

<Info>
  A resposta da API devolve o override **cru** — só o que você sobrescreveu. A
  mescla final com os defaults da organização é resolvida pela página hospedada
  ao renderizar, então o que você envia é o que você lê de volta.
</Info>

### Template

`template` controla a estrutura do painel esquerdo da página hospedada. O painel
de pagamento continua igual em todos os templates.

<CardGroup cols={3}>
  <Card title="minimal" icon="square" href="/features/checkout-templates">
    <img src="https://mintcdn.com/scaleup-28315a31/oU8B8kPbmC19xOC_/assets/checkout-template-minimal.jpg?fit=max&auto=format&n=oU8B8kPbmC19xOC_&q=85&s=eb14fae53618ea43855b1ca0c1e4c8cf" alt="Miniatura do template minimal com produto, resumo e formulário de pagamento" width="1280" height="800" data-path="assets/checkout-template-minimal.jpg" />
  </Card>

  <Card title="booking" icon="calendar-days" href="/features/checkout-templates">
    <img src="https://mintcdn.com/scaleup-28315a31/JkjvjdzrXEcQicBB/assets/checkout-template-booking.jpg?fit=max&auto=format&n=JkjvjdzrXEcQicBB&q=85&s=563b122cd34ff72a8e69cba6f65c1c7f" alt="Miniatura do template booking com estadia, noites e adicionais" width="1280" height="800" data-path="assets/checkout-template-booking.jpg" />
  </Card>

  <Card title="subscription" icon="arrows-rotate" href="/features/checkout-templates">
    <img src="https://mintcdn.com/scaleup-28315a31/kSlvsTruhKR3KP8Z/assets/checkout-template-subscription.jpg?fit=max&auto=format&n=kSlvsTruhKR3KP8Z&q=85&s=0f1ba4b30ca5e6f529ea9cc5caeb88af" alt="Miniatura do template subscription com trial, ciclo de cobrança e valor por mês" width="1280" height="800" data-path="assets/checkout-template-subscription.jpg" />
  </Card>
</CardGroup>

| `template`     | Uso                                                           |
| -------------- | ------------------------------------------------------------- |
| `minimal`      | Produto/preço e resumo padrão.                                |
| `booking`      | Reserva de hospedagem: acomodação, noites e adicionais/taxas. |
| `subscription` | Assinatura: trial, ciclo de cobrança e valor por mês.         |

Veja [Checkout templates](/features/checkout-templates) para exemplos de
modelagem.

### Cópia do botão (`submit_type`)

O texto do botão de finalização vem de `submit_type` — **por sessão, na raiz, não é branding**. A página hospedada renderiza a cópia no idioma do comprador.

| `submit_type`   | Botão (pt-BR)                                     |
| --------------- | ------------------------------------------------- |
| `auto` (padrão) | "Pagar" em compra única, "Assinar" em recorrente. |
| `pay`           | "Pagar"                                           |
| `subscribe`     | "Assinar"                                         |
| `book`          | "Reservar"                                        |
| `donate`        | "Doar"                                            |

## Confirmação e dados do pagamento

A confirmação acontece no browser do comprador via `POST /v1/checkout-sessions/public/:client_secret/confirm` — a página hospedada faz isso sozinha; em frontend próprio, é o seu código. O seu servidor **não** precisa criar nem confirmar nenhuma movimentação interna de processamento.

Após o confirm, o campo `payment_data` deixa de ser `null` e traz o que o comprador precisa para concluir, conforme o método:

| Método        | Campos em `payment_data`                                  |
| ------------- | --------------------------------------------------------- |
| `pix`         | `qr_code` (string EMV), `qr_code_url`, `expiration_date`. |
| `boleto`      | `pdf_url`, `barcode`, `digitable_line`, `due_date`.       |
| `credit_card` | `installments`.                                           |

<Tip>
  A confirmação não expõe nem exige IDs internos de processamento. Para
  reconciliar, use a `checkout.session`, o `payment_intent` relacionado e os
  webhooks. Veja [Confirmar checkout
  session](/api-reference/checkout-sessions/confirm).
</Tip>

## Metadata e reconciliação

Use `metadata` para correlacionar a sessão com o seu sistema sem depender de IDs internos da Chargefy. É um objeto livre `string → string`, opcional e controlado por você (limite de 50 chaves).

Exemplos de chaves comuns:

* `order_id`
* `cart_id`
* `campaign`
* `sales_channel`
* `customer_reference`

Esse `metadata` é ecoado em todos os webhooks `checkout.session.*` da sessão, então você reconcilia o resultado contra o seu pedido sem guardar nada além do seu próprio identificador.

## Eventos importantes

<AccordionGroup>
  <Accordion title="checkout.session.created">
    A sessão foi criada. Bom para registrar a tentativa no seu lado.
  </Accordion>

  <Accordion title="checkout.session.completed">
    O comprador concluiu o checkout. Em cartão, normalmente já vem pago; em
    PIX/boleto, ainda aguarda compensação.
  </Accordion>

  <Accordion title="checkout.session.expired">
    A sessão passou das 24h sem confirmação. Terminal.
  </Accordion>

  <Accordion title="checkout.session.async.payment.succeeded">
    PIX ou boleto compensou depois. **Este é o evento confiável para liberar
    produto em pagamento assíncrono.**
  </Accordion>

  <Accordion title="checkout.session.async.payment.failed">
    O pagamento assíncrono falhou ou expirou sem compensar.
  </Accordion>
</AccordionGroup>

O payload sempre carrega o objeto `checkout.session` completo em `data.object`; eventos que alteram a sessão também incluem `data.previous_attributes` com os valores anteriores dos campos alterados.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Objeto checkout.session" icon="cube" href="/api-reference/checkout-sessions">
    Schema público completo e campos retornados.
  </Card>

  <Card title="Criar checkout session (API)" icon="code" href="/api-reference/checkout-sessions/create">
    Contrato do `POST /v1/checkout-sessions` com as três variantes de
    `line_items`.
  </Card>

  <Card title="Confirmar checkout session" icon="circle-check" href="/api-reference/checkout-sessions/confirm">
    O endpoint público que conclui a compra no browser do comprador.
  </Card>

  <Card title="Payment Links" icon="link" href="/features/payment-links">
    A URL reutilizável que materializa uma session a cada clique.
  </Card>

  <Card title="Payment Intents" icon="receipt" href="/features/payment-intents">
    O lifecycle financeiro por trás da cobrança da sessão.
  </Card>

  <Card title="Produtos" icon="cube" href="/features/products">
    O catálogo de onde vêm os `price_id` dos `line_items`.
  </Card>
</CardGroup>
