> ## 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 white-label do zero: token, setup intent, preview e payment intent

> Guia passo a passo pra montar um checkout 100% seu: tokenizar o cartão, salvar como payment method, mostrar o preço certo antes de cobrar e confirmar o pagamento — com o payload de cada etapa e por que ela existe.

Este guia junta as peças que normalmente aparecem espalhadas: **Chargefy.js**, **token**, **setup intent**, **payment preview** e **payment intent**. No fim, você tem um checkout inteiro na sua página, com sua marca — a Chargefy só aparece nas chamadas de API, o comprador nunca sai do seu site.

A ideia central, antes de qualquer código: **o número do cartão nunca chega no seu backend**. Ele nasce e morre no navegador do comprador, vira um token de uso único, e daí em diante seu backend só troca IDs com a Chargefy.

## As 5 peças e o papel de cada uma

| Peça                                                               | Nasce onde                | Papel                                                                                                                      |
| ------------------------------------------------------------------ | ------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| [`token`](/api-reference/tokens/object) (`tok_*`)                  | Navegador (Chargefy.js)   | O cartão recém-digitado, virou credencial de **uso único**. Nunca reutilize.                                               |
| [`customer`](/api-reference/customers/object) (`cus_*`)            | Seu backend               | A pessoa dona do cartão. Precisa existir antes do setup intent.                                                            |
| [`setup_intent`](/api-reference/setup-intents/object) (`seti_*`)   | Seu backend               | Troca o `token` por um cartão **salvo e reutilizável**. Não cobra nada.                                                    |
| [`payment_method`](/api-reference/payment-methods/object) (`pm_*`) | Resultado do setup intent | O cartão salvo. É isso que você guarda pra cobrar depois.                                                                  |
| [`payment_preview`](/api-reference/payment-previews/object)        | Seu backend, sob demanda  | Serve para você exibir no seu checkout a quantidade de parcelas e o valor exato de cada uma, já com o acréscimo calculado. |
| [`payment_intent`](/api-reference/payment-intents/object) (`pi_*`) | Seu backend               | A cobrança em si. Referencia o `customer` e o `payment_method`.                                                            |

<Warning>
  Um payment intent é cobrado com um `payment_method` (`pm_*`), nunca direto com um `token_id`. O caminho é sempre `token` → `setup_intent` → `payment_method` → `payment_intent`.
</Warning>

A ordem é sempre a mesma, uma etapa depois da outra:

```mermaid theme={}
flowchart TD
  A["1. Cartão digitado"] --> B["2. Token"]
  B --> C["3. Customer"]
  C --> D["4. Cartão salvo"]
  D --> E["5. Tabela de parcelas"]
  E --> F["6. Cobrança confirmada"]
```

## Passo a passo

<Steps>
  <Step title="Carregue o Chargefy.js na sua página">
    Sempre da origem oficial — não copie o script localmente, pra receber correções de segurança automaticamente.

    ```html theme={}
    <script src="https://api.chargefy.io/v1/chargefy.js"></script>
    ```

    Isso expõe um objeto global `Chargefy`. É a única peça que roda no navegador; todo o resto é chamada de backend.
  </Step>

  <Step title="Tokenize o cartão (token)">
    **Por que essa etapa existe:** é o que mantém o número do cartão longe do seu servidor. O navegador manda o cartão direto pra Chargefy e recebe de volta um ID sem valor nenhum sozinho — um `token` de uso único.

    ```js theme={}
    const chargefy = Chargefy({ environment: "live" });

    const token = await chargefy.createPaymentToken({
      number: cardNumber,
      exp_month: expMonth,
      exp_year: expYear,
      cvc: cvc,
      name: holderName,
    });

    // token.id (tok_123) é tudo que você manda pro seu backend.
    await fetch("/api/checkout", {
      method: "POST",
      body: JSON.stringify({ token_id: token.id }),
    });
    ```

    ```json theme={}
    {
      "id": "tok_123",
      "object": "token",
      "card": {
        "brand": "visa",
        "exp_month": 12,
        "exp_year": 2030,
        "last4": "4242"
      },
      "created_at": "2026-05-16T18:34:58Z",
      "livemode": true
    }
    ```

    <Tip>
      Em `environment: "test"` nenhum dado sai pro ar — use um [cartão de teste](/features/chargefy-js#cartões-de-teste) pra simular aprovação e recusa sem precisar de cartão de verdade.
    </Tip>
  </Step>

  <Step title="Tenha um customer (customer)">
    **Por que essa etapa existe:** o cartão salvo pertence a alguém. No Brasil, salvar cartão exige `document` (CPF/CNPJ) — sem isso o setup intent do próximo passo falha.

    ```bash theme={}
    curl -X POST "https://api.chargefy.io/v1/customers" \
      -H "Authorization: Bearer {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "document": "11144477735",
        "email": "nome@email.com",
        "name": "Cliente"
      }'
    ```

    ```json theme={}
    {
      "id": "cus_123",
      "object": "customer",
      "document": "11144477735",
      "email": "nome@email.com",
      "name": "Cliente"
    }
    ```

    <Tip>
      Já tem esse comprador cadastrado? Reutilize o `customer` existente em vez de criar um novo — assim o histórico e os cartões salvos ficam todos no mesmo perfil.
    </Tip>
  </Step>

  <Step title="Troque o token por um cartão salvo (setup intent)">
    **Por que essa etapa existe:** o `token` do passo 2 morre no primeiro uso. Pra poder cobrar de fato, você precisa de um cartão que sobrevive — é isso que o setup intent devolve. Com `confirm: true`, você cria e confirma em uma chamada só.

    ```bash theme={}
    curl -X POST "https://api.chargefy.io/v1/setup-intents" \
      -H "Authorization: Bearer {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "confirm": true,
        "customer": "cus_123",
        "token_id": "tok_123"
      }'
    ```

    ```json theme={}
    {
      "id": "seti_123",
      "object": "setup_intent",
      "customer": "cus_123",
      "payment_method": "pm_456",
      "status": "succeeded",
      "usage": "off_session"
    }
    ```

    A partir daqui, o que importa é `payment_method: "pm_456"` — guarde esse ID. É ele que cobra.
  </Step>

  <Step title="Exiba o valor exato das parcelas (payment preview)">
    **Por que essa etapa existe:** no Brasil, isso é basicamente pra uma coisa só — exibir no seu checkout a quantidade de parcelas e o valor exato de cada uma, já com o acréscimo calculado. Você **não calcula juro na mão**: manda o valor e a Chargefy devolve a tabela pronta. PIX e boleto vêm no mesmo payload, mas o motivo de esse objeto existir é resolver o parcelamento do cartão.

    ```bash theme={}
    curl -X POST "https://api.chargefy.io/v1/payment-previews" \
      -H "Authorization: Bearer {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": 29900
      }'
    ```

    ```json theme={}
    {
      "object": "payment_preview",
      "amount": 29900,
      "currency": "brl",
      "has_surcharge": false,
      "livemode": true,
      "payment_methods": {
        "boleto": {
          "amount_total": 29900,
          "surcharge_amount": 0
        },
        "credit_card": {
          "installments": {
            "max_count": 6,
            "options": [
              {
                "amount_total": 29900,
                "count": 1,
                "interest_amount": 0,
                "per_installment_amount": 29900,
                "surcharge_amount": 0
              },
              {
                "amount_total": 30800,
                "count": 2,
                "interest_amount": 900,
                "per_installment_amount": 15400,
                "surcharge_amount": 0
              },
              {
                "amount_total": 31105,
                "count": 3,
                "interest_amount": 1205,
                "per_installment_amount": 10368,
                "surcharge_amount": 0
              },
              {
                "amount_total": 31410,
                "count": 4,
                "interest_amount": 1510,
                "per_installment_amount": 7853,
                "surcharge_amount": 0
              },
              {
                "amount_total": 31718,
                "count": 5,
                "interest_amount": 1818,
                "per_installment_amount": 6344,
                "surcharge_amount": 0
              },
              {
                "amount_total": 32030,
                "count": 6,
                "interest_amount": 2130,
                "per_installment_amount": 5338,
                "surcharge_amount": 0
              }
            ]
          }
        },
        "pix": {
          "amount_total": 29900,
          "surcharge_amount": 0
        }
      }
    }
    ```

    `payment_methods.credit_card.installments.options[]` é a tabela de parcelas pronta pro seu select — uma linha por parcela, de 1x até o `max_count`. `count`, `per_installment_amount` e `amount_total` são exatamente o que aparece na tela.

    <Tip>
      Se você ligou **repasse de taxa** (o comprador cobre a sua taxa e você recebe o valor cheio), envie `has_surcharge: true`. Os totais sobem e `surcharge_amount` aparece preenchido — veja [Repasse de taxa](/features/surcharges).
    </Tip>

    <Warning>
      Isto é só exibição. Quando o comprador escolher a parcela, você envia essa escolha pro payment intent no próximo passo — e é lá, não aqui, que o valor é travado de verdade.
    </Warning>
  </Step>

  <Step title="Cobre (payment intent)">
    **Por que essa etapa existe:** é a cobrança de fato. Referencia o `customer` e o `payment_method` salvos, com o `count` de parcelas que o comprador escolheu na tela anterior.

    ```bash theme={}
    curl -X POST "https://api.chargefy.io/v1/payment-intents" \
      -H "Authorization: Bearer {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "amount": 29900,
        "confirm": true,
        "currency": "brl",
        "customer": "cus_123",
        "payment_method": "pm_456",
        "payment_method_options": {
          "credit_card": {
            "installments": {
              "count": 2
            }
          }
        }
      }'
    ```

    ```json theme={}
    {
      "id": "pi_123",
      "object": "payment_intent",
      "amount": 31207,
      "amount_details": {
        "installment_interest": 1307,
        "subtotal": 29900,
        "surcharge": 0,
        "total": 31207
      },
      "currency": "brl",
      "customer": "cus_123",
      "has_surcharge": false,
      "payment_method": "pm_456",
      "status": "succeeded"
    }
    ```

    `amount` já vem com os juros embutidos — é o total que saiu do cartão do comprador. `status: "succeeded"` significa pago; trate `failed` lendo `last_payment_error`.

    <Tip>
      Quer que o comprador cubra a sua taxa também? Envie `has_surcharge: true` aqui — o mesmo flag que você mandou no preview. A Chargefy recalcula tudo de novo no servidor; o preview nunca é a fonte de verdade da cobrança.
    </Tip>
  </Step>
</Steps>

## O fio que conecta tudo

Cada passo devolve exatamente o ID que o próximo passo pede:

```
token.id (tok_123)
  → setup_intent.token_id
  → setup_intent.payment_method (pm_456)
    → payment_intent.payment_method
```

O `payment_preview` fica de fora dessa corrente de propósito — ele não gera nenhum ID, só monta a lista de parcelas com o juro já calculado. Você chama quantas vezes quiser (a cada vez que o valor mudar, por exemplo com um cupom) sem afetar nada.

## Erros

| Onde                       | `code`                                                  | Quando acontece                                                                                                    |
| -------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `createPaymentToken`       | `invalid_number`, `invalid_cvc`, `invalid_expiry_month` | Dado do cartão inválido — trate na hora, no navegador.                                                             |
| `createPaymentToken`       | `tokenization_failed` / `invalid_card`                  | Cartão recusado já na tokenização.                                                                                 |
| `payment_intent` (confirm) | `card_error`                                            | Recusa do emissor — leia `last_payment_error.code`. Veja [Códigos de falha](/api-reference/charges/failure-codes). |

<Warning>
  Um `token` é de uso único. Se a criação do setup intent falhar, gere um `token` novo — não tente reenviar o mesmo.
</Warning>

## Próximos passos

<CardGroup cols={2}>
  <Card title="Checkout white-label" icon="bolt" href="/features/white-label-checkout">
    A visão completa da feature: o que fica com você, o que fica com a Chargefy.
  </Card>

  <Card title="Tokenizar um cartão" icon="credit-card" href="/guides/tokenizing-a-card">
    Aprofunda só as etapas de token + setup intent.
  </Card>

  <Card title="Objeto payment preview" icon="calculator" href="/api-reference/payment-previews/object">
    Todos os campos do preview, incluindo repasse de taxa.
  </Card>

  <Card title="Objeto payment intent" icon="cube" href="/api-reference/payment-intents/object">
    Contrato completo da cobrança: status, parcelas, próxima ação.
  </Card>
</CardGroup>
