# Throne Elements

> Atributos do elemento, eventos com código estável, sandbox, página de uma tela só e navegador de app.

Fonte: https://app.thronecorporate.com/docs/cartao/elements

## O que é

O Throne Elements é o checkout de cartão embutido na sua página. Você cria o checkout no seu servidor com `POST /elements/checkout` (ou com `POST /elements/checkout/br`, o [Checkout Brasil](/docs/cartao/brasil)) e coloca o `transaction_id` num container. O script do Throne monta o elemento ali dentro. O comprador digita o cartão no elemento e o seu servidor nunca toca no número do cartão.

O mesmo elemento serve para as seis adquirentes. O que muda entre elas está em [Comparar adquirentes](/docs/cartao/comparar).

## Colocar na página

```html
<!-- onde o checkout deve aparecer -->
<div data-throne-elements
     data-id="ae3bf1da-0c9b-4818-9140-c6432152c23e"
     data-color="#7c3aed"
     data-theme="light"
     data-radius="10"></div>

<script src="https://app.thronecorporate.com/throne-elements.js" async></script>
```

| Atributo | O que faz |
|---|---|
| `data-throne-elements` | Marca o container. Pode haver mais de um na página |
| `data-id` | O `transaction_id` da criação. Precisa ser o UUID exato, senão nada aparece |
| `data-color` | Cor do botão e dos realces, no formato `#rrggbb` |
| `data-theme` | `light` ou `dark`. O elemento também detecta sozinho a cor de fundo do seu site e se ajusta a ela |
| `data-radius` | Arredondamento dos cantos, de `0` a `32` (px). Padrão `10` |

> [!NOTE]
> No Checkout Brasil, a criação também pode pedir o texto do botão, as cores, o formato e uma moldura pelo objeto `appearance`, e o que vier nele vence `data-color` e `data-radius`. Veja [Aparência](/docs/cartao/brasil#aparencia).

## Quando criar o checkout

- Crie no clique de compra, não quando a página carrega. Assim você não gasta checkout com quem só olhou a página.
- Guarde o `transaction_id` na sessão do comprador. Se ele voltar à página em até 24 horas, mostre o mesmo checkout em vez de criar outro.
- O mesmo checkout aceita várias tentativas de cartão. Crie um novo só depois de `failed` ou `expired`.

## Boas práticas

- Carregue o script **do endereço acima**. Não hospede uma cópia: o elemento depende do endereço de origem do script.
- Não fixe a altura do container nem esconda o overflow. O elemento ajusta a própria altura conforme a tela do checkout.
- Não coloque o elemento dentro de outro iframe com `sandbox`. Se não der para tirar o `sandbox`, ele precisa incluir no mínimo `allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation`. Sem isso, a verificação do banco e o pagamento em navegador de app não funcionam em algumas adquirentes.
- O pagamento precisa começar pelo clique do comprador no botão do elemento. Não dispare o pagamento por código.
- Se a sua página envia o header `Cross-Origin-Opener-Policy: same-origin`, a janela de verificação do banco pode perder o contato com o elemento. Sem esse header, nada a fazer. Com ele, teste um pagamento completo no computador e no celular antes de publicar.
- Se o seu site usa Content-Security-Policy, libere `https://app.thronecorporate.com` em `script-src` (o script) e em `img-src`, e em `frame-src` libere esse domínio e o domínio do checkout (o mesmo do `checkout_url` da resposta).

## Página de uma tela só (React, Vue e outras)

O script monta os containers que existem quando ele carrega. Se você inserir o container depois, chame:

```js
window.ThroneElements.mount();
```

Chame de novo sempre que inserir um container novo. Um container já montado não muda: trocar o `data-id` dele e chamar `mount()` não troca o checkout, e o elemento continua mostrando o anterior, com o valor antigo.

Para mostrar outro checkout, remova o container antigo, insira um novo com o `data-id` novo e chame `mount()`. Em React, use o `transaction_id` como `key` do container (`<div key={transactionId} data-throne-elements data-id={transactionId} />`) e chame `mount()` num `useEffect` que depende do `transactionId`: o React cria um container novo a cada checkout.

## Eventos na sua página

```js
window.addEventListener('throne-elements:ready', () => {
  // elemento montado, pronto para receber o cartão
});

window.addEventListener('throne-elements:paid', (e) => {
  // e.detail.transaction_id: o comprador concluiu. Mostre o "obrigado".
});

window.addEventListener('throne-elements:error', (e) => {
  const code = e.detail.code || e.detail.message; // code é fixo; message é texto
  // decida pelo code; mostre o texto só como apoio
});
```

Também funcionam as funções `window.ThroneElements.onready`, `onpaid` e `onerror`, que recebem o mesmo objeto de `e.detail`.

Os eventos são **sinais de tela**. A confirmação que vale para liberar o pedido é o webhook `transaction.paid` ou o `GET /transactions/{id}` com `status: "paid"`. O elemento pode terminar sem `paid` mesmo numa venda aprovada: o comprador pode fechar a página, a conexão pode cair ou o pagamento pode seguir por outro caminho, como o do navegador de app abaixo.

### Códigos de erro

Em `throne-elements:error`, decida pelo campo `e.detail.code`. Ele é um código fixo, em inglês, que não muda com o idioma do comprador. O campo `e.detail.message` traz o texto mostrado ao comprador, no idioma dele, e serve só para log. Se `code` não vier, use `message` quando ele for um dos códigos abaixo e trate qualquer outro texto como `declined`.

| `code` | Significado | Sterling | Imperium | NovaCorp | Eminence | Apex | ShadowBank | Vortex |
|---|---|---|---|---|---|---|---|---|
| `declined` | Cartão recusado. O comprador pode tentar de novo | Sim | Sim | Sim | Sim | Sim | Sim | Sim |
| `attempts_exhausted` | Acabaram as tentativas deste checkout | Sim | Em parte (veja abaixo) | Sim | Sim | Sim | Sim | Sim |
| `closed` | O checkout fechou por recusa enquanto a página estava aberta | Não | Não | Sim | Não | Não | Não | Não |
| `window_closed` | O comprador fechou a janela de verificação sem concluir | Não | Não | Sim | Não | Não | Não | Não |
| `canceled` | O comprador cancelou a verificação do banco | Não | Não | Sim | Não | Não | Não | Não |
| `popup_blocked` | A janela de verificação fechou logo depois de abrir, antes do envio do cartão. Nada foi cobrado e o comprador pode tentar de novo | Não | Não | Sim | Não | Não | Não | Não |
| `timeout` | A confirmação demorou mais que o esperado. O resultado final chega pelo webhook ou pela consulta | Não | Não | Sim | Sim | Sim | Sim | Sim |
| `limit` | A adquirente não aceita o valor desta venda no cartão. O checkout fechou | Não | Não | Não | Sim | Sim | Não | Não |
| `network` | Falha de conexão do comprador | Sim | Sim | Sim | Sim | Sim | Sim | Sim |
| `token` | Os dados do cartão não passaram na validação inicial. Não consome tentativa | Sim | Não | Não | Sim (veja abaixo) | Sim | Sim | Sim |
| `expired` | O checkout passou da validade | Sim | Sim | Sim | Sim | Sim | Sim | Sim |
| `ended` | O checkout foi encerrado | Sim | Sim | Sim | Sim | Sim | Sim | Sim |

Uma janela de verificação bloqueada na abertura não gera `popup_blocked`: na NovaCorp, o pagamento segue pela própria aba, como no [navegador de app](#navegador-de-app).

Na Eminence, a verificação do banco aparece dentro do próprio elemento, sem janela, e o comprador pode sair dela pelo botão Cancelar. O Cancelar não gera evento na hora: o elemento espera o banco encerrar a verificação pendente e então envia `declined` ou `network`; se isso não acontecer em alguns minutos, envia `timeout`. `token` também chega quando a Eminence recusa os dados do cartão depois do envio; nesse caso a tentativa é contada. `limit` fecha o checkout como `failed`: crie um checkout novo com outra adquirente habilitada.

Na Apex não há verificação do banco: o resultado sai na própria tentativa, dentro do elemento. `timeout` chega quando a análise antifraude demora; o resultado final vem pelo webhook ou pela consulta. `token` chega quando os dados do cartão não passam na validação, antes da cobrança, e não consome tentativa. `limit` fecha o checkout como `failed`, como na Eminence: crie um checkout novo com outra adquirente habilitada.

Na ShadowBank também não há verificação do banco: o resultado sai na própria tentativa, dentro do elemento. `timeout` chega quando a análise antifraude demora; o resultado final vem pelo webhook ou pela consulta. `token` chega quando os dados do cartão não passam na validação, antes da cobrança, e não consome tentativa. A ShadowBank não envia `limit`.

Na Imperium, `e.detail` não traz `code`, só `message`. Por isso o exemplo acima lê `e.detail.code || e.detail.message`. Falha de conexão, validade e encerramento chegam como `network`, `expired` e `ended`. Numa recusa durante o pagamento, inclusive a última, `message` traz o texto mostrado ao comprador, no idioma dele, e não diz se ainda há tentativa: `attempts_exhausted` só chega quando o elemento abre num checkout que já fechou. Trate esse texto como `declined` e deixe o próprio elemento mostrar ao comprador se ele pode tentar de novo. Para saber se o checkout fechou, consulte `GET /transactions/{id}`: com `failed`, crie um checkout novo.

## Navegador de app

Instagram, Facebook, o app do Google, TikTok e outros apps abrem links num navegador próprio. Esses navegadores não abrem uma segunda janela.

- **Sterling, Imperium, Eminence, Apex, ShadowBank e Vortex**: nada muda. O pagamento acontece todo dentro do elemento e os eventos chegam normalmente.
- **NovaCorp**: a própria aba sai da sua página e vai para a página segura de pagamento. O comprador passa pela tela do banco e termina lá. A sua página **não recebe** `throne-elements:paid` nem outro evento, e o comprador não volta sozinho para a sua loja. O mesmo acontece quando um navegador comum bloqueia a janela de verificação.

A venda continua sendo confirmada normalmente pelo webhook `transaction.paid` e pelo `GET`. Por isso:

- libere o pedido pelo webhook, nunca só pelo evento da página;
- envie a conversão da campanha pelo seu servidor ao receber `transaction.paid`, como em [Webhooks](/docs/webhooks#conversao-de-campanha-pelo-servidor);
- mande o e-mail de confirmação do pedido pelo servidor, porque o comprador pode não ver a sua página de obrigado.

Se o comprador voltar à sua página pelo botão voltar, o elemento pode montar de novo já pago e disparar `throne-elements:paid`. Isso não é garantido: não dependa disso.

## Idioma

O checkout sai no idioma do país do comprador, identificado pelo IP. Compradores no Brasil, em Portugal ou de país não identificado veem em inglês. O idioma não é configurado pela API. Detalhes por adquirente em [Comparar adquirentes](/docs/cartao/comparar). No [Checkout Brasil](/docs/cartao/brasil), a página sai sempre em português.
