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

# Aceitar seu primeiro pagamento: produto, link de pagamento e webhook

> Guia passo a passo para receber seu primeiro pagamento: criar um produto com preço, gerar uma página de pagamento hospedada e confirmar via webhook.

Este guia mostra o caminho mais curto até o primeiro pagamento confirmado: criar um produto com preço, gerar uma página de pagamento hospedada e escutar a confirmação por webhook. No fim, você paga essa página com um cartão de teste e vê a confirmação chegar — a mesma mecânica que roda em produção, sem SDK e sem escrever frontend.

A ideia central, antes de qualquer chamada: **a página de sucesso não é confirmação de pagamento**. Quem confirma é o webhook. Com cartão isso acontece na hora, durante o checkout; com PIX e boleto acontece depois, de forma assíncrona — a mesma página aceita os três métodos sem nenhuma mudança de código. Este guia mostra os três; para o comparativo lado a lado e como simular cada cenário no sandbox, veja [Crie e comece a usar um sandbox](/test-payments-in-sandbox).

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

| Peça                | Nasce onde                 | Papel                                                                                        |
| ------------------- | -------------------------- | -------------------------------------------------------------------------------------------- |
| `product` + `price` | Seu backend                | O que você vende e quanto custa.                                                             |
| `payment_link`      | Seu backend                | URL pública e reutilizável que abre a página de pagamento hospedada.                         |
| `checkout_session`  | Seu backend, por comprador | Alternativa ao payment link quando a cobrança precisa ficar amarrada a um pedido específico. |
| Evento de webhook   | Chargefy → seu servidor    | A confirmação real de que o dinheiro entrou.                                                 |

| Etapa | O que acontece                                        |
| ----- | ----------------------------------------------------- |
| 1     | Você cadastra o produto e o preço.                    |
| 2     | Você cria o link de pagamento.                        |
| 3     | Você cadastra o webhook que confirmará o resultado.   |
| 4     | O comprador paga.                                     |
| 5     | Seu sistema recebe a confirmação e entrega o produto. |

<Note>
  Tudo aqui roda em **test mode** (chave `ch_test_...`): sem dinheiro real, sem
  risco. Os objetos de teste ficam isolados dos de produção, e ir para produção
  no final é trocar a chave. Veja [Sandbox](/api-reference/sandbox).
</Note>

## Antes de começar

Você só precisa de três coisas:

1. **Uma conta Chargefy** com uma organização criada —
   [cadastre-se aqui](https://chargefy.io/signup) se ainda não tem.
2. **Uma API key de teste**: no dashboard, abra **Developers → Chaves de API**,
   clique em **Nova chave** e selecione o ambiente `test`. O token começa com
   `ch_test_`. Detalhes em [API keys](/api-reference/api-keys).
3. **Um terminal com `curl`** (ou qualquer cliente HTTP). Nos exemplos abaixo,
   troque `{{API_KEY}}` pela sua chave de teste.

## Passo a passo

<Steps>
  <Step title="Crie um produto com preço">
    **Por que essa etapa existe:** é o que você vende. `POST /v1/products`
    aceita `prices[]` inline — um único request resolve os dois, e o primeiro
    preço do array já vira o `default_price` do produto.

    ```bash theme={"theme":"css-variables"}
    curl -X POST "https://api.chargefy.io/v1/products" \
      -H "Authorization: Bearer {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Curso de violão",
        "prices": [
          {
            "currency": "brl",
            "unit_amount": 19990
          }
        ]
      }'
    ```

    <Note>
      Valores são sempre **inteiros em centavos**: `19990` = R\$ 199,90. Sem decimal,
      sem vírgula, sem erro de arredondamento.
    </Note>

    A resposta traz o produto completo com o preço dentro:

    ```json theme={"theme":"css-variables"}
    {
    "id": "prod_r5xQiZZxbFTntWgL",
    "object": "product",
    "created_at": "2026-07-14T12:00:00Z",
    "default_price": "price_9Fi9fZyEeA9WuR38",
    "description": null,
    "image_url": null,
    "is_active": true,
    "is_tax_applicable": true,
    "livemode": false,
    "marketing_features": [],
    "metadata": {},
    "name": "Curso de violão",
    "prices": [
    {
      "id": "price_9Fi9fZyEeA9WuR38",
      "object": "price",
      "created_at": "2026-07-14T12:00:00Z",
      "currency": "brl",
      "is_active": true,
      "livemode": false,
      "metadata": {},
      "name": null,
      "product": "prod_r5xQiZZxbFTntWgL",
      "recurring": null,
      "tax_behavior": "unspecified",
      "type": "one_time",
      "unit_amount": 19990,
      "updated_at": null
    }
    ],
    "updated_at": null
    }
    ```

    **Guarde o `id` do preço** (`price_9Fi9fZyEeA9WuR38` no exemplo): é ele que você vai vender
    no próximo passo.
  </Step>

  <Step title="Gere um link de pagamento">
    **Por que essa etapa existe:** é o caminho mais curto até o primeiro
    pagamento. Um **payment link** é uma URL pública e reutilizável — cada
    clique de um comprador abre uma página de checkout nova. Serve para bio de
    rede social, e-mail, QR code ou botão "comprar" em qualquer site.

    ```bash theme={"theme":"css-variables"}
    curl -X POST "https://api.chargefy.io/v1/payment-links" \
      -H "Authorization: Bearer {{API_KEY}}" \
      -H "Content-Type: application/json" \
      -d '{
        "line_items": [
          {
            "price_id": "price_9Fi9fZyEeA9WuR38"
          }
        ]
      }'
    ```

    ```json theme={"theme":"css-variables"}
    {
    "id": "plink_yLsJdGZu9E7vPFC6",
    "object": "payment_link",
    "allow_discount_codes": false,
    "cancel_url": null,
    "created_at": "2026-07-14T12:01:00Z",
    "discount": null,
    "has_surcharge": false,
    "is_active": true,
    "label": null,
    "line_items": [
    {
      "amount_discount": 0,
      "amount_subtotal": 19990,
      "amount_tax": 0,
      "amount_total": 19990,
      "currency": "brl",
      "description": "Curso de violão",
      "metadata": {},
      "position": 0,
      "price": "price_9Fi9fZyEeA9WuR38",
      "price_data": null,
      "product": "prod_r5xQiZZxbFTntWgL",
      "quantity": 1,
      "recurring_interval": null,
      "recurring_interval_count": null,
      "unit_amount": 19990
    }
    ],
    "livemode": false,
    "metadata": {},
    "payment_method_collection": "always",
    "subscription_data": {},
    "success_url": null,
    "updated_at": null,
    "url": "https://pay.chargefy.io/link/9a1bc3d2e4f5..."
    }
    ```

    **Abra a `url` no navegador.** Essa é a sua página de pagamento — hospedada
    pela Chargefy, responsiva, com a identidade visual da sua organização e as
    abas de **cartão** (com parcelamento), **PIX** e **boleto** já habilitadas.
    Você não escreveu nenhum frontend, e a mesma página atende os três métodos.

    <Frame caption="A mesma página hospedada — as três abas de método de pagamento aparecem sem nenhuma configuração extra.">
      <img src="https://mintcdn.com/scaleup-28315a31/AL7TxjaPFJ8w5l48/assets/accept-your-first-payment/checkout-card.png?fit=max&auto=format&n=AL7TxjaPFJ8w5l48&q=85&s=be39c5ad8c1972d9c372b40935ae1330" alt="Página de pagamento hospedada com as abas Cartão, PIX e Boleto, formulário de cartão preenchido" width="2560" height="1800" data-path="assets/accept-your-first-payment/checkout-card.png" />
    </Frame>

    <Tip>
      Prefere não usar terminal agora? Dá para criar o mesmo link no dashboard, em
      **Payment Links → Novo link** — sem código nenhum. Veja
      [Links de pagamento](/payments/create-payment-link).
    </Tip>

    ### Alternativa: uma checkout session por comprador

    O payment link é a mesma URL para todo mundo. Quando o **seu backend** inicia
    a compra — um carrinho, um pedido específico —, crie uma **checkout
    session**: uma sessão descartável, de um único comprador, que você amarra ao
    seu pedido via `metadata`.

    ```bash theme={"theme":"css-variables"}
    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_9Fi9fZyEeA9WuR38"
          }
        ],
        "metadata": {},
        "payment_method_collection": "always",
        "subscription_data": {},
        "success_url": "https://meusite.com/obrigado"
      }'
    ```

    A resposta traz a mesma página hospedada em `url` — redirecione o comprador
    para ela e pronto. O `metadata` volta ecoado em **todos os webhooks** da
    sessão, então você reconcilia o pagamento com o seu pedido sem guardar nada
    além do seu próprio `order_id`. Todas as opções do create (cliente travado,
    aparência, descontos, trial) estão em
    [Checkout Sessions](/payments/create-checkout-page).
  </Step>

  <Step title="Cadastre o endpoint de webhook">
    **Por que essa etapa existe:** aqui está a parte que separa "página bonita"
    de "dinheiro confirmado". A página de sucesso não é confirmação de
    pagamento — quem confirma é o **webhook**, o `POST` que a Chargefy faz no
    seu servidor quando algo acontece de verdade.

    No dashboard, abra **Configurações → Webhooks** e cadastre uma URL **HTTPS**
    no ambiente `test`. Guarde o secret (`whsec_...`) — é com ele que você
    verifica a assinatura de cada entrega.

    Para desenvolver na sua máquina, exponha a porta local com um tunnel:

    ```bash theme={"theme":"css-variables"}
    ngrok http 3000
    # cadastre a URL https gerada, ex.: https://abc123.ngrok.io/webhooks/chargefy
    ```
  </Step>

  <Step title="Suba um receiver mínimo">
    **Por que essa etapa existe:** é o código que verifica a assinatura de cada
    entrega e decide o que fazer com o evento.

    Verifique a assinatura com qualquer biblioteca compatível com
    [Standard Webhooks](/integrate/webhooks/delivery#verificação) e trate três
    eventos:

    <CodeGroup>
      ```javascript Node.js / Express theme={"theme":"css-variables"}
      import express from 'express';
      import { Webhook } from 'svix';

      const app = express();
      const wh = new Webhook(process.env.CHARGEFY_WEBHOOK_SECRET); // whsec_...

      // IMPORTANTE: corpo bruto — parsear o JSON antes quebra a assinatura.
      app.post('/webhooks/chargefy', express.raw({ type: 'application/json' }), (req, res) => {
        let evt;
        try {
          evt = wh.verify(req.body, {
            'webhook-id': req.headers['webhook-id'],
            'webhook-timestamp': req.headers['webhook-timestamp'],
            'webhook-signature': req.headers['webhook-signature']
          });
        } catch {
          return res.status(401).json({ error: 'Invalid signature' });
        }

        const session = evt.data.object;

        switch (evt.type) {
          case 'checkout.session.completed':
            if (session.payment_status === 'paid') {
              // Cartão aprovado na hora: pode entregar.
              deliverOrder(session);
            } else {
              // PIX ou boleto: o comprador recebeu as instruções.
              // O dinheiro ainda não entrou — aguarde o evento assíncrono.
              markOrderPending(session);
            }
            break;
          case 'checkout.session.async.payment.succeeded':
            // PIX/boleto compensou: agora sim, entregue.
            deliverOrder(session);
            break;
          case 'checkout.session.async.payment.failed':
            // O pagamento assíncrono falhou ou expirou.
            cancelOrder(session);
            break;
        }

        res.status(200).json({ received: true });
      });

      app.listen(3000);
      ```

      ```typescript Next.js (App Router) theme={"theme":"css-variables"}
      import { NextRequest, NextResponse } from 'next/server';
      import { Webhook } from 'svix';

      const wh = new Webhook(process.env.CHARGEFY_WEBHOOK_SECRET!); // whsec_...

      export async function POST(req: NextRequest) {
        const body = await req.text(); // corpo bruto — não use req.json()
        let evt: any;
        try {
          evt = wh.verify(body, {
            'webhook-id': req.headers.get('webhook-id')!,
            'webhook-timestamp': req.headers.get('webhook-timestamp')!,
            'webhook-signature': req.headers.get('webhook-signature')!
          });
        } catch {
          return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
        }

        const session = evt.data.object;

        if (evt.type === 'checkout.session.completed' && session.payment_status === 'paid') {
          await deliverOrder(session); // cartão: pago na hora
        }
        if (evt.type === 'checkout.session.async.payment.succeeded') {
          await deliverOrder(session); // PIX/boleto: compensou agora
        }
        if (evt.type === 'checkout.session.async.payment.failed') {
          await cancelOrder(session);
        }

        return NextResponse.json({ received: true });
      }
      ```
    </CodeGroup>
  </Step>

  <Step title="Responda 2xx em até 20 segundos">
    **Por que essa etapa existe:** entregas que não recebem `2xx` a tempo são
    reenviadas — sem isso, você corre o risco de processar o mesmo pagamento
    mais de uma vez ou perder a confirmação.

    Verifique a assinatura, persista o evento e responda `200` na hora; processe
    o resto em background. Entregas que falham são reenviadas automaticamente —
    detalhes em [Entrega de webhooks](/integrate/webhooks/delivery).
  </Step>

  <Step title="Pague com cartão, PIX e boleto">
    **Por que essa etapa existe:** é o jeito mais rápido de ver o fluxo inteiro
    funcionando de ponta a ponta — produto, link, checkout e webhook. Comece
    pelo cartão: é o caminho **síncrono**, a aprovação acontece durante o
    checkout, sem esperar nenhum evento assíncrono.

    Abra a `url` do seu link, preencha os dados e pague com o cartão de teste
    `4242 4242 4242 4242` (qualquer CVC de 3 dígitos, qualquer validade futura).
    A aprovação é **síncrona**: acontece ali, durante o checkout. Segundos
    depois, seu endpoint recebe o `checkout.session.completed` já pago:

    ```json theme={"theme":"css-variables"}
    {
    "id": "evt_dP25sBMFYjNiMJcB",
    "object": "event",
    "created_at": "2026-07-14T12:05:00Z",
    "data": {
    "object": {
      "id": "cs_ojNtBNSfT7hh3Nap",
      "object": "checkout.session",
      "allow_discount_codes": false,
      "amount_discount": 0,
      "amount_subtotal": 19990,
      "amount_tax": 0,
      "amount_total": 19990,
      "cancel_url": null,
      "client_reference_id": null,
      "client_secret": "42d7438bcf42997777c8f0f0ec893eee684d5547c5757f8c055325d810c8d689",
      "created_at": "2026-07-14T12:03:00Z",
      "currency": "brl",
      "customer": "cus_p8p6pszJNEfTX6Cs",
      "customer_document": "123.456.789-00",
      "customer_document_type": "cpf",
      "customer_email": "nome@email.com",
      "customer_name": "Cliente",
      "discount": null,
      "expires_at": "2026-07-15T12:03:00Z",
      "has_surcharge": false,
      "invoice_creation": false,
      "line_items": [],
      "livemode": false,
      "marketing_attribution": null,
      "metadata": {},
      "mode": "payment",
      "payment_data": {
        "installments": 1,
        "payment_method": "credit_card",
        "status": "succeeded"
      },
      "payment_method_collection": "always",
      "payment_status": "paid",
      "status": "complete",
      "submit_type": "auto",
      "subscription": null,
      "success_url": null,
      "url": "https://pay.chargefy.io/session/..."
    }
    },
    "livemode": false,
    "organization": "org_FvsnDcbq64FR5pL4",
    "request": {
    "id": null
    },
    "type": "checkout.session.completed"
    }
    ```

    `payment_status: "paid"` — pode entregar. Esse é o seu primeiro pagamento
    confirmado.

    ### PIX e boleto: mesma página, confirmação assíncrona

    Abra o link de novo e escolha a aba **PIX**. O comprador recebe um QR code
    para pagar — nenhuma configuração adicional foi necessária:

    <Frame caption="PIX gerado na mesma página hospedada: QR code, instruções e o código copia-e-cola.">
      <img src="https://mintcdn.com/scaleup-28315a31/AL7TxjaPFJ8w5l48/assets/accept-your-first-payment/checkout-pix.png?fit=max&auto=format&n=AL7TxjaPFJ8w5l48&q=85&s=dd6037f110d728adef6d457de6d2ecac" alt="Página de pagamento hospedada mostrando QR code PIX gerado, com instruções de pagamento" width="2560" height="1800" data-path="assets/accept-your-first-payment/checkout-pix.png" />
    </Frame>

    Ou escolha **Boleto** — o checkout passa a exigir CPF/CNPJ e endereço, e o
    comprador recebe um boleto com código de barras:

    <Frame caption="Boleto gerado na mesma página: código de barras, linha digitável e link para o PDF.">
      <img src="https://mintcdn.com/scaleup-28315a31/AL7TxjaPFJ8w5l48/assets/accept-your-first-payment/checkout-boleto.png?fit=max&auto=format&n=AL7TxjaPFJ8w5l48&q=85&s=2dd2ef6a39f508ac58f294b08735bb60" alt="Página de pagamento hospedada mostrando boleto gerado, com código de barras e linha digitável" width="2560" height="1800" data-path="assets/accept-your-first-payment/checkout-boleto.png" />
    </Frame>

    Nos dois casos o seu endpoint recebe o `checkout.session.completed` com
    `payment_status: "unpaid"` assim que o comprador conclui o checkout — o
    dinheiro ainda não confirmou. Em produção, isso resolve sozinho: PIX
    compensa em segundos, boleto em 1–2 dias úteis. No sandbox, use um e-mail de
    teste para escolher um resultado imediato ou atrasado; no checkout
    hospedado, a barra de sandbox também permite simular pagamento ou expiração.
    O guia [Crie e comece a usar um
    sandbox](/test-payments-in-sandbox) ensina o fluxo passo a passo.
  </Step>
</Steps>

Esses três eventos são tudo que o seu receiver precisa tratar — mesmo que hoje
você só tenha testado o caminho do cartão:

| Evento                                     | O que significa                                                                          | O que fazer                                         |
| ------------------------------------------ | ---------------------------------------------------------------------------------------- | --------------------------------------------------- |
| `checkout.session.completed`               | O comprador concluiu o checkout. Cartão aprovado já vem `paid`; PIX/boleto vêm `unpaid`. | `paid` → entregar. `unpaid` → marcar como pendente. |
| `checkout.session.async.payment.succeeded` | O PIX foi pago ou o boleto compensou.                                                    | Entregar.                                           |
| `checkout.session.async.payment.failed`    | O pagamento assíncrono falhou ou expirou.                                                | Cancelar o pedido e avisar o comprador.             |

<Warning>
  **Regra de ouro:** entregue o produto quando `payment_status` virar `"paid"` —
  nunca só porque o comprador voltou para a página de sucesso. Isso vale sempre,
  e importa ainda mais em PIX e boleto, onde o redirect acontece **antes** de o
  comprador pagar. Comparação lado a lado de cada método e como simular cada
  cenário: [Crie e comece a usar um
  sandbox](/test-payments-in-sandbox).
</Warning>

## Ir para produção

O fluxo é o mesmo em produção — muda o ambiente, não o código:

1. **Ative a organização.** Complete o cadastro do negócio no dashboard (dados
   da empresa e conta para saques). Pagamentos reais só processam depois da
   aprovação do cadastro financeiro — veja [KYC](/business-model/kyc).
2. **Crie uma chave live** (`ch_live_...`) e troque no lugar da `ch_test_`.
   Dados de teste não migram: recrie produto, preço e link com a chave live.
3. **Cadastre o endpoint de webhook no ambiente live.** Endpoints e secrets são
   separados por ambiente.
4. Compartilhe o link — e deixe o webhook decidir quando entregar.

## Próximos passos

<CardGroup cols={2}>
  <Card title="Crie e comece a usar um sandbox" icon="flask" href="/test-payments-in-sandbox">
    Por que sandbox é mais rápido, como ativar e como simular a confirmação de
    PIX e boleto sem esperar nada.
  </Card>

  <Card title="Criando checkout sessions" icon="sliders" href="/payments/create-hosted-checkout-page">
    Todas as decisões do create: cliente travado, aparência, descontos, trial e
    campos obrigatórios.
  </Card>

  <Card title="Crie assinaturas para seu SaaS" icon="arrows-rotate" href="/payments/create-subscriptions">
    Recorrência com o mesmo checkout: trial, renovação automática, pró-rata e
    portal do cliente.
  </Card>

  <Card title="Entrega de webhooks" icon="bell" href="/integrate/webhooks/delivery">
    Assinatura, retries, idempotência e reentrega manual — o contrato completo.
  </Card>

  <Card title="Sandbox completo" icon="list-check" href="/api-reference/sandbox">
    Todos os cartões e e-mails para cenários de teste.
  </Card>
</CardGroup>
