# Gerar cobrança Cartão 3DS

**Área:** Checkout Transparente

Para gerar uma cobrança no **cartão de crédito com autenticação 3DS** é necessário fazer a autenticação com o SDK `@validapay/3ds` ([Os detalhes podem ser consultados em Autenticação do cartão](/sdks/3ds)). Depois disso, o frontend tokeniza o cartão com o SDK `@validapay/tokenize` ([Tokenização de cartão](/sdks/tokenizacao)). Esta etapa garante que os dados sensíveis (PAN e CVV) **nunca passam pelo seu servidor**. O SDK de tokenização retorna o `cardToken` e o `deviceId` usados na cobrança. O fluxo autentica o portador no banco emissor antes da cobrança, transferindo a responsabilidade de chargeback por fraude do lojista para o emissor (**liability shift**).

`POST /v1/charges`

**Base URL Produção:** `https://api.validapay.com.br`  
**Base URL Sandbox:** `https://sandbox.validapay.com.br`

**Scopes necessários:** `charges/write`

## Visão geral

> ℹ️ Esta rota é a **autorização** (após o 3DS). A autenticação do comprador com o banco acontece no navegador via `authenticate(...)` **antes** de chamar a API. O `paymentMethod` continua `creditcard` — a ValidaPay não usa um método `threeDs` separado.

O fluxo completo tem quatro passos:

1. **Tokenizar o dispositivo** — `collectDevice(...)` devolve o `deviceId`.
2. **Autenticar o comprador** — `authenticate(...)` no `@validapay/3ds` abre o desafio do banco (quando necessário) e devolve `authenticationId` (e opcionalmente `cardId`).
3. **Tokenizar o cartão** — `tokenize(...)` com o mesmo `authentication`, gerando um `cardToken` de 5 minutos vinculado ao cartão autenticado.
4. **Autorizar o pagamento (esta rota)** — `POST /v1/charges` com `paymentMethod: "creditcard"`, `cardToken`, `deviceId` e `authentication`.

Para cobrança **sem** 3DS, use [Gerar cobrança Cartão](/referencia/post-gerar-cobranca-cartao).

## Campos do cartão

- **`cardToken`** (obrigatório) — vem de `tokenize(...)` no pacote `@validapay/tokenize`, gerado **depois** do `authenticate`. Vale **5 minutos**, é de uso único e só funciona na conta que o gerou.
- **`deviceId`** (obrigatório) — vem de `collectDevice(...)`. Identifica o dispositivo e alimenta o antifraude.
- **`authentication`** (obrigatório nesta sessão) — resultado de `authenticate(...)` no `@validapay/3ds`. Envie exatamente o objeto devolvido:
  - `authenticationId` (obrigatório)
  - `cardId` (opcional)

`SEU_ID_PUBLICO` está no painel em **Gerenciamento > Minha Conta > Id Público**. É público: pode ficar no código da página.

O valor enviado em `amount` (ou o total resolvido via `items`) deve ser o **mesmo** `amount` usado no `authenticate`.


Detalhes do fluxo no front-end: [Autenticação do dono do cartão (3DS)](/sdks/3ds) e [Tokenização de cartão](/sdks/tokenizacao).

## Idempotência

Envie `externalId` como chave de idempotência do pedido. Se duas cobranças forem enviadas com o mesmo valor, a segunda é recusada com `409 DUPLICATE_CHARGE` e o corpo traz o `chargeId` da cobrança original em `error.details.chargeId`.

> ⚠️ O `cardToken` expira em **5 minutos**. A idempotência **não** devolve a resposta da cobrança original: a segunda chamada com o mesmo `externalId` é recusada com `409 DUPLICATE_CHARGE` e o cartão não é reprocessado. Para cobrar de novo, gere um `cardToken` novo e use outro `externalId`.

Use `externalTxid` só para identificar a **loja**, o **caixa** ou o **vendedor**. Ele não entra na idempotência.

**Correlação com o pedido:** use `externalId` — ele é persistido e devolvido. O campo `metadata` é aceito nesta rota, mas **não é gravado na cobrança** nem volta no webhook.

## Produto, valor e parcelas

- Envie `items` com os produtos **ou** `amount` para uma cobrança avulsa — nunca os dois.
- O valor autenticado no 3DS deve bater com o valor cobrado.
- Dá para parcelar com `installments` e, se quiser, repassar as taxas ao comprador.

## Notificações por e-mail

Use `notifications` para escolher quais e-mails saem nesta cobrança:

- `oneoff.payment.success` — confirmação ao comprador quando o cartão é aprovado
- `oneoff.payment.failed` — aviso ao comprador quando o cartão é recusado
- `new.sale` — avisa você (vendedor) da venda paga

Eventos destinados ao comprador exigem `customer.email`.

## Resposta

- **`200`** — cartão aprovado: `success: true`, `chargeId`, `customerId`, `status: "paid"`.
- **`402`** — cartão recusado pelo banco ou adquirente (inclui falha de autenticação 3DS): `success: false`, `status: "failed"` e o motivo em `error` (texto).

## Erros comuns

- `400 MISSING_CARD_DATA` — faltou o `cardToken`
- `400 CARD_TOKEN_EXPIRED` — passou dos 5 minutos: gere outro na página
- `404 CARD_TOKEN_NOT_FOUND` — token inexistente ou de outra conta
- `404 PRICE_NOT_FOUND` — o `priceId` de algum item não existe
- `409 DUPLICATE_CHARGE` — o `externalId` já foi usado; use o `chargeId` retornado
- `402` — pagamento recusado (`card_declined`, `failed_authentication`, `insufficient_funds`, etc.)

No front-end, erros do SDK 3DS (`THREE_DS_FAILED`, `THREE_DS_TIMEOUT`, etc.) ocorrem **antes** desta chamada — veja [Autenticação do dono do cartão (3DS)](/sdks/3ds).

## Cartões de teste (sandbox)

Em conta de sandbox nenhuma cobrança é cobrada de verdade — o **número digitado na tokenização** define o resultado:

- `4111111111111111` — aprovado
- `4000000000000002` — recusado (`card_declined`)
- `4000000000000004` — saldo insuficiente (`insufficient_funds`)
- `4000000000000006` — cartão expirado (`expired_card`)
- `4000000000000008` — CVV inválido (`invalid_cvv`)
- `4000000000000010` — suspeita de fraude (`fraud_suspected`)

Qualquer outro número de 16 dígitos é aprovado. Em produção o número não muda nada: quem decide é o banco emissor.

## Boas práticas

- **Sempre autentique e tokenização na sequência**, imediatamente antes desta rota. O `cardToken` expira em 5 minutos.
- **Passe o mesmo `authentication` ao `tokenize` e à cobrança** — assim a ValidaPay guarda o cartão que o banco autenticou (útil em assinaturas).
- **Envie `deviceId` sempre**.
- **Não confunda falha no SDK com `402`.** Se `authenticate` falhar, não chame esta rota.
- **Não use `metadata` para correlacionar o pedido nesta rota** — use `externalId`.

### Request body

```json
{
  "paymentMethod": "creditcard",
  "externalId": "pedido-2026-0001",
  "externalTxid": "loja-01-caixa-03",
  "customer": {
    "name": "João da Silva",
    "email": "joao@email.com",
    "documentNumber": "12345678901",  
    "metadata": { "origem": "site" },
    "phone": "+5511999998888",  
    "cep": "01310100",
    "address": {
      "type": "BILLING",
      "street": "Av. Paulista",
      "number": "1000",
      "complement": "Apto 52",
      "neighborhood": "Bela Vista",
      "city": "Sao Paulo",
      "state": "SP",
      "zipCode": "01310100",
      "country": "BR",
      "cityCode": "3550308"
    }
  },
  "cardToken": "ctk_9f8s7d6f5g4h3j2k1l0",
  "deviceId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "authentication": {
    "authenticationId": "9WDHbW3Mipl6pFQ2LnM2",
    "cardId": "card_p7s6je7w90uomjkgn1siy2xkw"
  },
  "amount": 10.00,
  "items": [
    {
      "priceId": "price_abc123",
      "quantity": 1,
      "isOrderBump": false
    }
  ],
  "installments": 1,
  "freeInstallments": 1,
  "passFeesToCustomer": false,
  "couponCode": "PROMO10",
  "metadata": { "referencia": "pedido-001" },
  "description": "Assinatura Premium",
  "allowedPaymentMethods": ["pix", "creditcard"],
  "nfConfigId": "nfc_1788364755079_psuecwgdc",
  "notifications": ["oneoff.payment.success", "oneoff.payment.failed", "new.sale"],
  "discounts": [
    {
      "type": "percentage",
      "value": 10,
      "paymentMethod": "creditcard",
      "couponCode": "PROMO10"
    }
  ],
  "cartId": "cart_2026_0001",
  "productId": "prod_123456_example"
}
```

### Campos do body

**Obrigatórios:** `paymentMethod`, `customer`, `customer.name`, `customer.email`, `customer.documentNumber`, `cardToken`, `deviceId`, `authentication`, `authentication.authenticationId`, `items.priceId`, `discounts.type`, `discounts.value`

| Campo | Descrição |
|---|---|
| `paymentMethod` | Forma de pagamento (fixo: creditcard). Com 3DS o método continua creditcard |
| `customer` | Dados do comprador |
| `customer.name` | Nome completo |
| `customer.email` | E-mail |
| `customer.documentNumber` | CPF (11) ou CNPJ (14 dígitos) |
| `cardToken` | Token do cartão, gerado no navegador pelo SDK @validapay/tokenize depois do authenticate. Vale 5 minutos e só funciona na conta que o gerou |
| `deviceId` | Identificação do dispositivo do comprador, devolvida pelo collectDevice. Envie sempre |
| `authentication` | Resultado de authenticate(...) no pacote @validapay/3ds. Envie exatamente o que o SDK devolveu |
| `authentication.authenticationId` | Identificador da autenticação aprovada pelo banco |
| `items.priceId` | ID do preço do produto |
| `discounts.type` | Tipo do desconto: percentual sobre o valor ou abatimento fixo em reais |
| `discounts.value` | Percentual de 0 a 100 quando type é percentage; valor em reais quando fixed |

**Opcionais**

| Campo | Descrição |
|---|---|
| `externalId` | Identificador único do pedido no seu sistema; usado como idempotencyKey (evita cobrança duplicada) |
| `externalTxid` | Identifica a loja, o caixa ou o vendedor responsável pela cobrança |
| `customer.metadata` | Objeto livre guardado junto do cadastro do comprador |
| `customer.phone` | Telefone (no fluxo 3DS o SDK exige telefone e endereço no authenticate) |
| `customer.cep` | CEP do pagador, só números |
| `customer.address` | Endereço do comprador. Obrigatório quando nfConfigId emite nota fiscal |
| `customer.address.type` | BILLING para cobrança, SHIPPING para entrega (default BILLING) |
| `customer.address.street` | Rua ou logradouro |
| `customer.address.number` | Número |
| `customer.address.complement` | Complemento |
| `customer.address.neighborhood` | Bairro |
| `customer.address.city` | Cidade |
| `customer.address.state` | UF com 2 letras |
| `customer.address.zipCode` | CEP com 8 dígitos, só números |
| `customer.address.country` | Pais (default BR) |
| `customer.address.cityCode` | Código IBGE do município; a nota fiscal exige e o backend resolve pelo CEP quando ausente |
| `authentication.cardId` | Cartão autenticado, devolvido junto com a autenticação |
| `amount` | Valor total em reais para cobrança avulsa. Ignorado quando items é enviado. Deve ser o mesmo amount usado no authenticate (mín.: 0.01) |
| `items` | Produtos da compra (ou use amount para cobrança avulsa) |
| `items.quantity` | Quantidade (default 1) |
| `items.isOrderBump` | Marca o item como oferta adicional aceita no checkout |
| `installments` | Parcelas no cartão, de 1 a 12 |
| `freeInstallments` | Parcelas sem juros para o comprador, de 0 a 12 |
| `passFeesToCustomer` | Repassa a taxa de parcelamento ao comprador |
| `couponCode` | Código de cupom de desconto |
| `metadata` | Aceito mas NÃO persistido nesta rota; para correlacionar o pedido use externalId |
| `description` | Descrição livre da cobrança |
| `allowedPaymentMethods` | Métodos aceitos quando a cobrança vira link |
| `nfConfigId` | Emite nota fiscal com esta configuração. Exige customer.address |
| `notifications` | E-mails disparados nesta cobrança |
| `discounts` | Descontos aplicados a cobrança |
| `discounts.paymentMethod` | Aplica o desconto só neste método de pagamento |
| `discounts.couponCode` | Cupom que originou este desconto |
| `cartId` | Agrupa numa única compra as assinaturas criadas juntas e deduplica o evento de conversão |
| `productId` | Cria a cobrança a partir de um produto |

### Resposta 200: 200

```json
{
  "success": true,
  "customerId": "cus_xxx",
  "chargeId": "cha_abc123",
  "status": "paid"
}
```

### Resposta 402: 402

```json
{
    "success": false,
    "chargeId": "cha_1784065113577_5dw2oyfic",
    "status": "failed",
    "error": "Cartão recusado"
}
```

### Resposta 400: 400 MISSING_CARD_DATA

```json
{
  "error": {
    "message": "cardToken é obrigatório na cobrança com cartão; gere um com o SDK @validapay/tokenize",
    "code": "MISSING_CARD_DATA"
  }
}
```

### Resposta 404: 404 PRICE_NOT_FOUND

```json
{
  "error": {
    "message": "Preço não encontrado",
    "code": "PRICE_NOT_FOUND"
  }
}
```

### Resposta 409: 409 DUPLICATE_CHARGE

```json
{
  "error": {
    "message": "Cobrança duplicada: já existe uma cobrança para este pedido",
    "code": "DUPLICATE_CHARGE",
    "details": {
      "chargeId": "cha_1784065113577_5dw2oyfic"
    },
    "timestamp": "2026-07-14T21:39:36.322Z"
  }
}
```

---

Página: https://docs.validapay.com.br/referencia/post-gerar-cobranca-cartao-3ds  
Contrato OpenAPI: https://docs.validapay.com.br/openapi.json