# Server-to-server na ShadowBank

> Integração por API, sem Throne Elements, na adquirente ShadowBank: cobrança na moeda do comprador, com recorrência e upsell em um clique, para quem já tem checkout próprio. Habilitação caso a caso.

Fonte: https://app.thronecorporate.com/docs/cartao/shadowbank-direto

## O que é esta integração

A integração direta da ShadowBank é uma alternativa ao Throne Elements para quem tem um checkout de cartão próprio e quer cobrar pela API, do próprio servidor, sem o iframe do Throne. Ela existe para a **Eminence**, para a **Vortex** e para a **ShadowBank**, e esta página cobre a ShadowBank. Nas outras adquirentes a integração continua sendo o [Throne Elements](/docs/cartao).

Você cobra na moeda do seu comprador, as mesmas 48 da [lista](/docs/cartao/moeda#moedas-aceitas), e a cotação que credita o seu saldo é fixada na criação da cobrança, como no checkout comum.

Quando usar:

- Você é um gateway, subadquirente ou agregador e já tem a própria tela de cartão.
- Você quer cobrança recorrente e upsell em um clique sem mandar o comprador a uma tela do Throne.

Quando **não** usar:

- Você quer um checkout pronto, sem desenvolver a tela do cartão: use o [Throne Elements](/docs/cartao).
- Você precisa de parcelamento: nesta modalidade a cobrança é sempre à vista.

**Upsell em um clique e recorrência funcionam aqui, e o upsell também pode ser recorrente.** O upsell tem rota própria, o [`POST /api/v1/shadowbank/upsell`](#upsell-em-um-clique). O campo `one_click_from` do checkout comum não existe nesta modalidade: ele volta `422 field_not_supported` e a mensagem aponta a rota certa.

> [!WARNING]
> Na integração direta o número do cartão passa pelo **seu** servidor antes de chegar ao Throne. A guarda desses dados do seu lado (PCI DSS) é responsabilidade sua: nunca grave número, validade ou CVV em banco, arquivo ou log.

## Habilitação

A integração direta é liberada **caso a caso**. [Fale com o suporte](https://app.thronecorporate.com/painel). São três requisitos:

- A ShadowBank habilitada na sua conta (confira em `GET /api/v1/elements/acquirers`).
- A integração direta da ShadowBank liberada na sua conta.
- Uma chave de API com o escopo `transactions:write` (gere no painel, em Chaves de API).

Sem a liberação, as chamadas respondem `422 partner_api_not_enabled`. Sem a ShadowBank, `422 acquirer_not_enabled`. Sem o escopo, `403 scope_required`.

A liberação é por adquirente: quem já usa a integração direta da Eminence ou da Vortex precisa pedir a da ShadowBank à parte.

Para o **upsell em um clique** há um quarto requisito, que é por conta e não por adquirente: o upsell em um clique habilitado. Sem ele a rota do upsell responde `403 one_click_not_enabled`, e a cobrança comum continua funcionando.

## O caminho, em dois passos

Toda cobrança tem duas chamadas, nesta ordem, as duas feitas **do seu servidor** e com a sua chave de API:

1. **Tokenizar o cartão.** Você manda o cartão e recebe um `card_token`.
2. **Cobrar** com esse `card_token`. A resposta já traz o resultado.

O cartão nunca é enviado ao endpoint de cobrança. Se você mandar qualquer campo de cartão nele, a resposta é `400 card_data_not_allowed` e nada é cobrado.

### Passo 1: tokenizar o cartão

```
POST /api/v1/vault/shadowbank/cards
Authorization: Bearer pk_live_...:sk_live_...
Content-Type: application/json
```

```json
{
  "number": "5555555555554444",
  "exp_month": 11,
  "exp_year": 2031,
  "cvv": "737",
  "holder_name": "MARIA S SOUZA",
  "customer": {
    "name": "Maria Souza",
    "email": "maria@exemplo.com"
  }
}
```

Resposta `201`:

```json
{
  "card_token": "sbt_7b58f4a1600bd9419cb20b7f111bc831",
  "brand": "mastercard",
  "last4": "4444",
  "exp_month": 11,
  "exp_year": 2031,
  "expires_at": "2026-11-09T03:00:00Z"
}
```

O `card_token` vale até `expires_at` — no máximo sete dias, e nunca depois do fim do mês de validade do cartão.

O bloco `customer` neste passo é **opcional e recomendado**. Ele não é repassado à adquirente além do nome: serve para que o mesmo comprador caia sempre no mesmo cadastro do lado dela quando voltar a comprar. Mande o mesmo `customer` que você vai mandar na cobrança.

Erros deste passo:

| HTTP | `error` | Quando |
|---|---|---|
| `422` | `invalid_card` | Número, validade, CVV ou titular fora do formato, ou o cartão recusado na tokenização. O campo problemático vem em `field`. |
| `401` | `unauthorized` | Chave de API ausente ou inválida. |
| `403` | `scope_required` | A chave não tem `transactions:write`. |
| `422` | `partner_api_not_enabled` | A integração direta não está liberada na conta. |
| `422` | `acquirer_not_enabled` | A ShadowBank não está habilitada na conta. |
| `429` | `rate_limit_exceeded` | Limite por segundo da conta. Aguarde 1 segundo. |
| `503` | `temporarily_unavailable` | Tokenização indisponível. Tente de novo em instantes. |

### Passo 2: cobrar

```
POST /api/v1/shadowbank/payments
Authorization: Bearer pk_live_...:sk_live_...
Idempotency-Key: pedido-9823
Content-Type: application/json
```

```json
{
  "amount_cents": 2500,
  "currency": "BRL",
  "card_token": "sbt_7b58f4a1600bd9419cb20b7f111bc831",
  "description": "Plano Pro",
  "customer": {
    "name": "Maria Souza",
    "email": "maria@exemplo.com"
  },
  "metadata": {
    "pedido": "9823"
  }
}
```

Resposta `201`:

```json
{
  "transaction_id": "a960cd5a-a166-4e16-8e28-72a64b4013ed",
  "status": "paid",
  "authorized": true,
  "amount_cents": 2500,
  "currency": "BRL",
  "installments": 1,
  "message": "Pagamento aprovado.",
  "created_at": "2026-11-02T01:15:35-03:00",
  "card": { "brand": "mastercard", "last4": "4444" }
}
```

### Como ler a resposta

Depois que a venda é criada, **a resposta é sempre `201`**, e o que muda é o `status`:

| `status` | O que fazer |
|---|---|
| `paid` | Aprovado e liberado. Entregue o pedido. |
| `pending` | Em análise da adquirente, ou resultado indefinido. **Não repita a cobrança**: espere o webhook `transaction.paid` ou consulte a venda. |
| `failed` | Recusado, ou nada processado. Veja `retryable`. |

Em `failed`, o campo `retryable` diz o que fazer:

- `retryable: true` — **nada foi cobrado**. Você pode tentar de novo, com um `card_token` novo.
- `retryable: false` — a adquirente recusou. Não insista no mesmo cartão; `decline_code` traz o código dela.

`pending` é desfecho normal nesta adquirente, não erro: a cobrança pode cair em análise e fechar depois, pelo webhook.

Antes de a venda ser criada (erro de validação, conta sem liberação, valor fora da faixa), a resposta é `4xx` ou `503` e a `Idempotency-Key` fica livre para ser repetida.

### Campos do passo 2

| Campo | Obrigatório | Regra |
|---|---|---|
| `amount_cents` | sim | Inteiro, na menor unidade da moeda. |
| `currency` | sim | Três letras, uma das [48 aceitas](/docs/cartao/moeda#moedas-aceitas). |
| `card_token` | sim | O token do passo 1. |
| `customer.name` | sim | Nome do comprador, até 120 caracteres. |
| `customer.email` | não | E-mail do comprador. Opcional nesta adquirente. |
| `description` | não | Até 255 caracteres. Vira o nome do produto no extrato interno. |
| `metadata` | não | Até 20 chaves de texto, número ou booleano. |
| `installments` | não | Só `1`. Qualquer outro valor é `422`. |
| `recurrence` | não | Veja [Recorrência](#recorrência). |

Campos recusados nesta rota, com o motivo na resposta: `one_click_from` (use a rota do upsell).

### Campos da resposta

| Campo | Sempre | O que é |
|---|---|---|
| `transaction_id` | sim | O identificador da venda no Throne. Guarde este. |
| `status` | sim | `paid`, `pending` ou `failed`. |
| `authorized` | sim | `true` só em `paid`. |
| `amount_cents`, `currency` | sim | Ecoam **o seu pedido**, não o valor interno. |
| `installments` | sim | Sempre `1`. |
| `message` | sim | Frase pronta para registro. |
| `card` | quase sempre | `brand` e `last4`. |
| `decline_code` | em `failed` | O código da adquirente. |
| `retryable` | em `failed` | Se pode tentar de novo. |
| `recurrence_id`, `cycle`, `next_charge_at` | com recorrência | Veja [Recorrência](#recorrência). |

## Consulta

`GET /api/v1/transactions/{transaction_id}`, igual a qualquer venda. Veja [Transações](/docs/transacoes).

## Estorno

O estorno é feito pelo Throne, não pela API do parceiro. Fale com o suporte.

## Webhook

O webhook não muda por causa desta modalidade: `transaction.paid`, `transaction.failed` e os demais chegam no mesmo formato de sempre, no mesmo endpoint. Veja [Webhooks](/docs/webhooks).

Libere o pedido pelo webhook ou pela consulta, nunca só pela resposta da cobrança.

## Upsell em um clique

Cobra o **mesmo cartão** de uma venda que já foi paga, sem pedir o cartão ao comprador de novo e sem `card_token` nenhum. Você manda só o `transaction_id` da venda de origem.

```
POST /api/v1/shadowbank/upsell
Authorization: Bearer pk_live_...:sk_live_...
Idempotency-Key: upsell-9823-1
Content-Type: application/json
```

```json
{
  "parent_transaction_id": "a960cd5a-a166-4e16-8e28-72a64b4013ed",
  "amount_cents": 1500,
  "currency": "BRL",
  "description": "Garantia estendida"
}
```

Resposta `201`:

```json
{
  "transaction_id": "7fb09848-911d-45ee-97ee-2d0847c2d3eb",
  "status": "paid",
  "authorized": true,
  "amount_cents": 1500,
  "currency": "BRL",
  "installments": 1,
  "one_click": true,
  "message": "Pagamento aprovado.",
  "created_at": "2026-11-02T01:24:35-03:00",
  "card": { "brand": "visa", "last4": "1881" }
}
```

O `status` se lê igual ao da cobrança comum.

### O upsell pode ser recorrente

Mande `recurrence` no corpo do upsell e ele vira a primeira cobrança de uma assinatura:

```json
{
  "parent_transaction_id": "a960cd5a-a166-4e16-8e28-72a64b4013ed",
  "amount_cents": 2000,
  "currency": "BRL",
  "description": "Clube mensal",
  "recurrence": { "cycle": "monthly", "max_payments": 6 }
}
```

A resposta traz, além dos campos de sempre, os três da recorrência:

```json
{
  "transaction_id": "45f577d8-e706-4198-91b0-323f64697b93",
  "status": "paid",
  "one_click": true,
  "recurrence_id": "rec_a8158e00a2a6e23bc68f1532",
  "cycle": "monthly",
  "next_charge_at": "2026-12-02T15:00:00Z"
}
```

### O que a venda de origem precisa ser

| Requisito | Por quê |
|---|---|
| Sua | Venda de outra conta responde como se não existisse. |
| Paga | O upsell cobra o cartão de uma venda que deu certo. |
| Na ShadowBank | O upsell cai na mesma adquirente da venda de origem. |
| Dentro da janela | A janela padrão é de 2 horas desde o pagamento. |
| Com cartão guardado | Vendas pagas antes desta versão não têm o cartão guardado. |
| Abaixo do limite de upsells | Até 5 upsells por venda de origem. |

O valor do upsell tem um teto próprio: o menor entre um limite absoluto e vinte vezes o valor da venda de origem. Ele existe porque o comprador não digita nada nesta cobrança — um erro de valor do seu lado debitaria algo que ele nunca viu.

### Erros do upsell

| HTTP | `error` | Quando |
|---|---|---|
| `409` | `parent_not_found` | Não existe venda sua com esse `parent_transaction_id`. |
| `409` | `parent_not_paid` | A venda de origem não está paga. |
| `409` | `parent_refunded` | A venda de origem foi estornada. |
| `409` | `parent_charged_back` | A venda de origem foi contestada. |
| `409` | `parent_too_old` | Fora da janela do um clique. |
| `409` | `upsell_limit_reached` | A venda de origem já atingiu o limite de upsells. |
| `409` | `parent_other_acquirer` | A venda de origem é de outra adquirente. |
| `409` | `one_click_unavailable` | O um clique não está disponível para essa venda. |
| `403` | `one_click_not_enabled` | O upsell em um clique não está habilitado na conta. |
| `422` | `field_not_supported` | Você mandou `card_token`, `customer`, `installments` ou `one_click_from`. O campo vem em `field`. |
| `422` | `amount_out_of_range` | Acima do teto do upsell ou fora da faixa da adquirente. |
| `503` | `busy` | Outro upsell desta venda de origem está em andamento. Tente em 3 segundos. |

O upsell não recebe comprador: ele é o da venda de origem, e mandar `customer` é `422`.

## Recorrência

Mande `recurrence` na cobrança e ela se torna a primeira de uma assinatura. A adquirente guarda o cartão e cobra os ciclos seguintes sozinha.

```json
{
  "amount_cents": 2500,
  "currency": "BRL",
  "card_token": "sbt_7b58f4a1600bd9419cb20b7f111bc831",
  "customer": { "name": "Maria Souza" },
  "recurrence": { "cycle": "monthly", "max_payments": 12 }
}
```

| Campo | Obrigatório | Regra |
|---|---|---|
| `recurrence.cycle` | sim, se mandar `recurrence` | Um dos sete: `weekly`, `biweekly`, `monthly`, `bimonthly`, `quarterly`, `semiannually`, `yearly`. |
| `recurrence.max_payments` | não | De 2 a 120, contando a primeira cobrança. Sem ele, a assinatura não tem fim. |

A resposta ganha três campos:

| Campo | O que é |
|---|---|
| `recurrence_id` | O identificador da recorrência no Throne. |
| `cycle` | O ciclo, de volta. |
| `next_charge_at` | Quando a próxima cobrança cai. |

Pontos que importam:

- **A primeira cobrança é a desta chamada.** A assinatura na adquirente começa a valer a partir do ciclo seguinte, então o comprador nunca é cobrado duas vezes no mesmo dia.
- **A assinatura só abre se a primeira cobrança for aprovada.** Recusada, a resposta traz `recurrence_id` e a recorrência fica pendente, sem nada cobrando.
- **Os ciclos seguintes chegam pelo webhook**, como vendas novas ligadas à mesma recorrência.
- Ciclo fora dos sete é `422 invalid_cycle`, com a lista na mensagem.
- Recorrência e parcelamento não se combinam: esta modalidade é sempre à vista.

Para listar, consultar e cancelar recorrências, veja [Recorrências](/docs/cartao/recorrencias).

## Dinheiro

Taxa, cautela, maturação, antecipação e saque seguem as regras da sua conta, iguais às de qualquer venda de cartão. Veja [Como o dinheiro entra](/docs/dinheiro).

## Limites e tempos

- Limite por segundo: o da sua conta, compartilhado entre a tokenização e a cobrança.
- A cobrança pode levar até 85 segundos. Configure o seu cliente HTTP com no mínimo 90 segundos de leitura.
- Repetir a mesma `Idempotency-Key` devolve a resposta da primeira chamada, sem cobrar de novo.

> [!WARNING]
> Nunca repita uma cobrança que respondeu `pending`. Ela pode ter sido aprovada do outro lado, e uma segunda chamada cobraria o comprador duas vezes.

## Testar

Não há ambiente de testes nesta modalidade: as chamadas são reais. Comece com um valor baixo e um cartão seu.

## Checklist

- [ ] A ShadowBank habilitada na conta.
- [ ] A integração direta da ShadowBank liberada.
- [ ] Chave de API com `transactions:write`.
- [ ] Para o upsell: o um clique habilitado na conta.
- [ ] O cartão nunca vai ao endpoint de cobrança.
- [ ] Número, validade e CVV não são gravados em banco, arquivo nem log do seu lado.
- [ ] `Idempotency-Key` em toda cobrança e todo upsell.
- [ ] Cliente HTTP com 90 segundos ou mais de leitura.
- [ ] `pending` nunca é repetido.
- [ ] O pedido é liberado pelo webhook ou pela consulta, nunca só pela resposta.
