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

# Checkout transparente com cartão: passo a passo

> Cobre cartão de crédito dentro da sua página, sem redirecionar o cliente. Um exemplo mínimo com página e backend, pronto para copiar e rodar.

Neste tutorial, você coloca o formulário de cartão dentro do seu site: o cliente digita o cartão, paga e não sai da página. No fim, você tem uma página funcionando no [Modo Teste](https://docs.abacatepay.com/pages/comecar/modo-teste.md).

## Como funciona

```mermaid theme={null}
sequenceDiagram
    participant N as Seu site (abacatepay.js)
    participant B as Seu backend
    participant A as API AbacatePay
    N->>A: tokenize() com a chave pública
    A-->>N: ctok_ (60 s, uso único)
    N->>B: ctok_ + dados do cliente
    B->>A: POST /v2/transparents/create
    A-->>B: cobrança + cardToken
```

1. **O número do cartão nunca passa pelo seu servidor.** O formulário é um iframe seguro. O seu código só recebe um token.
2. **São duas chaves.** A [chave pública](https://docs.abacatepay.com/pages/pagamentos/cartao-de-credito.md#chave-pública-e-chave-de-api) (`pk_...`) vai no navegador. A [chave de API](https://docs.abacatepay.com/pages/comecar/chaves-de-api.md) (`abc_...`) fica só no backend.
3. **O token `ctok_` vale 60 segundos e uma cobrança.** Para cobrar o mesmo cartão de novo depois, use o `cardToken` da resposta. Veja [One click buy](https://docs.abacatepay.com/pages/pagamentos/one-click-buy.md).

Para seguir o tutorial, você precisa de uma conta com o cartão de crédito liberado e do Node.js 20.6 ou superior, que roda o exemplo com `--env-file`.

## Passo 1: pegue as duas chaves

Ligue o **Modo Teste** no dashboard e copie:

| Chave | Onde copiar | Onde usar |
| - | - | - |
| Chave pública (`pk_...`, rótulo **Teste (DEV)**) | **Integração → Checkout próprio** | Na página (passo 2) |
| Chave de API (`abc_dev_...`, permissão `TRANSPARENT:CREATE`) | **Integração → API** | No backend (passo 3) |

> [!WARNING]
>
> As duas chaves precisam ser do **mesmo ambiente**: as duas de teste ou as duas de produção. Misturar faz a cobrança falhar com `Invalid or expired card token`.

## Passo 2: a página

Crie a pasta do projeto:

```bash theme={null}
mkdir minha-loja && cd minha-loja
npm init -y && npm install express
mkdir public
```

Crie `public/index.html` e troque `pk_SUA_CHAVE_PUBLICA` pela sua chave pública:

```html public/index.html theme={null}
<!doctype html>
<html lang="pt-BR">
  <head>
    <meta charset="utf-8" />
    <script src="https://abacatepay.com/abacatepay.js"></script>
  </head>
  <body>
    <div id="cartao"></div>
    <button id="pagar" disabled>Pagar R$ 100,00</button>
    <p id="mensagem"></p>

    <script type="module">
      const botao = document.getElementById('pagar')
      const mensagem = document.getElementById('mensagem')

      const checkout = AbacatePay.create({
        publicKey: 'pk_SUA_CHAVE_PUBLICA',
        amount: 10000, // R$ 100,00 em centavos: o MESMO valor que o backend cobra
        onCardChange: ({ canTokenize }) => (botao.disabled = !canTokenize),
      })
      await checkout.mount('#cartao').catch((error) => (mensagem.textContent = error.message))

      botao.onclick = async () => {
        botao.disabled = true // evita cobrança em dobro
        // Na sua loja, estes dados vêm do seu formulário
        const cliente = { name: 'Maria Silva', email: 'maria@exemplo.com', taxId: '529.982.247-25', cellphone: '11999999999' }
        try {
          const { token } = await checkout.tokenize(cliente) // abre o 3DS sozinho se o banco pedir
          const resposta = await fetch('/pagar', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ token, cliente }),
          })
          const { success, status, error } = await resposta.json()
          mensagem.textContent = success ? `Pagamento recebido (${status})` : error
          if (success) return
        } catch (error) {
          mensagem.textContent = error.message
        }
        botao.disabled = !checkout.canTokenize
      }
    </script>
  </body>
</html>
```

## Passo 3: o backend

Crie `server.mjs` na pasta `minha-loja`:

```js server.mjs theme={null}
import express from 'express'

const app = express()
app.use(express.json())
app.use(express.static('public'))

app.post('/pagar', async (req, res) => {
  const { token, cliente } = req.body

  const resposta = await fetch('https://api.abacatepay.com/v2/transparents/create', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.ABACATEPAY_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      method: 'CARD',
      data: {
        amount: 10000, // o MESMO valor do AbacatePay.create
        token, // ctok_ que veio do navegador
        customer: cliente, // name, email, taxId e cellphone: todos obrigatórios
      },
    }),
  })
  const { success, data, error } = await resposta.json()

  // Guarde data.cardToken no seu banco para cobrar este cartão de novo depois
  if (success) console.log('Cobrança criada:', data.id, data.status, data.cardToken)

  res.json({ success, status: data?.status, error }) // o cardToken não vai para o navegador
})

app.listen(3000, () => console.log('Abra http://localhost:3000'))
```

E o arquivo `.env`, com a chave de API:

```bash .env theme={null}
ABACATEPAY_API_KEY=abc_dev_sua_chave_de_api
```

## Passo 4: teste

```bash theme={null}
node --env-file=.env server.mjs
```

Abra [http://localhost:3000](http://localhost:3000) e clique em **Pagar**. No Modo Teste, o formulário já vem preenchido com o cartão de teste `4242 4242 4242 4242`, e nada é cobrado de verdade. No terminal aparece a cobrança aprovada e o `cardToken` (no Modo Teste, `dev_card_4242`).

Para testar recusas, troque o número no formulário por um dos [cartões de teste](https://docs.abacatepay.com/pages/comecar/modo-teste.md#cartões-de-teste) que são recusados.

Toda recusa volta com a mesma mensagem, `Não foi possível processar o pagamento com este cartão`. É de propósito: detalhar o motivo ajudaria quem testa cartão roubado.

## Passo 5: produção

O código não muda. Desligue o **Modo Teste**, copie as duas chaves de **Produção** e troque na página e no `.env`.

Em produção, o formulário é o seguro de verdade. Alguns bancos pedem confirmação (3DS) numa janela que o abacatepay.js abre e fecha sozinho. São aceitos cartões de crédito **Visa** e **Mastercard**.

Antes de ir ao ar, ajuste o exemplo para a sua loja:

* **O valor vem do seu pedido, no backend.** Não confie no valor que chega do navegador.
* **Os dados do cliente vêm do seu formulário.** O CPF ou CNPJ precisa ser válido.
* **Libere o pedido quando o `status` for `APPROVED`.** Se vier `PENDING`, espere o webhook [`transparent.completed`](https://docs.abacatepay.com/pages/webhooks/events/transparent).
* **Timeout não é recusa.** Se a chamada de cobrança der timeout, não repita na hora: confira na [listagem](https://docs.abacatepay.com/pages/transparents/list.md) ou espere o webhook.

Use o [checklist de produção](https://docs.abacatepay.com/pages/comecar/producao.md#checklist) para conferir o resto da integração.

## Erros mais comuns

| Mensagem | O que fazer |
| - | - |
| `Invalid or revoked public key` | Copie a chave pública de novo em **Integração → Checkout próprio**. |
| `Invalid or expired card token` | O token passou de 60 segundos, já foi usado ou as chaves são de ambientes diferentes. Gere outro com `tokenize()`. |
| `amount does not match the tokenized amount` | Use o mesmo `amount` na página e no backend. |
| `Property 'data.customer.cellphone' is missing` | Falta um campo na cobrança. A mensagem diz qual: aqui, o celular do cliente. |
| `Não foi possível processar o pagamento com este cartão` | O cartão foi recusado. Peça outro cartão. |

Todos os erros estão em [Criar cobrança Cartão](https://docs.abacatepay.com/pages/transparents/card.md#erros) e em [abacatepay.js](https://docs.abacatepay.com/pages/transparents/abacatepay-js.md#erros).

## Próximos passos

> [card] [One click buy](https://docs.abacatepay.com/pages/pagamentos/one-click-buy.md)
>
> Use o `cardToken` para cobrar o mesmo cartão sem o cliente digitar nada.

> [card] [Parcelamento no cartão](https://docs.abacatepay.com/pages/pagamentos/parcelamento.md)
>
> Deixe o cliente parcelar em até 12x.

> [card] [Cartão de crédito](https://docs.abacatepay.com/pages/pagamentos/cartao-de-credito.md)
>
> Como o cartão funciona na AbacatePay.

> [card] [Criar cobrança Cartão](https://docs.abacatepay.com/pages/transparents/card.md)
>
> Todos os campos, respostas e status do endpoint.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.