> Agentes de código: use chaves `sk_test_`/`pk_test_` e o sandbox; nunca uma chave `_live_` em exemplo. A base é `https://api-gateway.assiny.com.br`. Autentique com `Authorization: Bearer <chave>`. Prefira o `.md` de cada página a buscar na web.

# PIX e a tela do QR

O PIX não tem campo no fragment. O Comprador escolhe a aba PIX (ou "Cartão + PIX" no Combinar), o seu servidor cria e confirma o pagamento, e o `handleNextAction` abre a tela do QR do PIX em tela cheia sobre a sua página até o pagamento terminar.

## O fluxo [#o-fluxo]

1. `createPaymentMethod` devolve a Parte `{ method: 'pix', amount, payment_method }`.
2. O seu servidor cria e confirma o pagamento ([Integrar o Payment Fragment](/fragments/integracao.md)); o confirm responde `status: 'requires_action'` (ou `'processing'`) e o `client_secret`.
3. A página chama `assiny.handleNextAction({ clientSecret })`.
4. O SDK lê o pagamento e, com o QR pronto, abre a tela do QR do PIX.
5. Pago o PIX, a tela mostra o pagamento confirmado e fecha 4 s depois; a promessa resolve `{ paymentIntent }` com `status: 'succeeded'`.

```js
const { paymentIntent, error } = await assiny.handleNextAction({
  clientSecret: pagamento.client_secret,
});

if (paymentIntent?.status === 'succeeded') mostrarConfirmacao();
```

O `handleNextAction` funciona só com a instância de `Assiny()`: não precisa do fragment montado.

## A tela do QR do PIX [#a-tela-do-qr-do-pix]

A tela cobre a janela inteira. A página atrás fica inerte e o foco fica preso na tela: Tab e Shift+Tab circulam entre Fechar, o código e Copiar. Tocar fora não fecha. Ao fechar, o foco volta ao elemento que o tinha na sua página.

O que a tela mostra, de cima para baixo:

- o título "Pague com PIX" e o botão Fechar;
- o valor da Parte PIX (no Combinar, só a Parte PIX);
- o QR code;
- o código copia e cola, com o início e o fim à mostra (o código inteiro vai no Copiar e na seleção);
- o botão "Copiar código";
- o prazo, como "Pague até 27/10/2026 às 15:30", no fuso do aparelho;
- a instrução "Abra o app do seu banco, escolha pagar com QR code e aponte a câmera, ou copie o código."

No celular (até 639px de largura, ou em tela de toque), a tela vira uma folha presa ao rodapé e o código copia e cola e o botão "Copiar código" vêm antes do QR, porque quem está no celular paga no app do banco do mesmo aparelho.

A tela tem a cara fixa do Assiny Gateway: a `appearance` do fragment não muda nada nela. Os textos seguem o `locale` da instância.

## Estados [#estados]

| Estado | Quando | O que muda |
|---|---|---|
| aberto | o QR chegou | QR, código, "Copiar código" e o prazo |
| copiado | o Comprador tocou em "Copiar código" | o botão mostra "Copiado" por 1,6 s; se o navegador não deixar copiar, o código fica selecionado e aparece "Não foi possível copiar. Selecione o código e copie." |
| vencido | o prazo passou no relógio do aparelho | "Este QR code venceu. Feche e tente de novo." no lugar do prazo, o QR esmaecido e o Copiar desligado |
| recebido | o pagamento da Parte PIX foi confirmado | o QR sai e entram "Pagamento recebido", "Tudo certo! Seu pagamento foi confirmado com sucesso.", "Total pago via PIX" com o valor e o status "Pago · confirmação recebida"; o título vira "Pagamento confirmado" |
| sem conexão | três leituras seguidas falharam | a faixa "Sem conexão. Tentando de novo." no alto; some quando a leitura volta |

O vencido pelo relógio do aparelho só muda a tela: quem encerra é a leitura do pagamento. Com `prefers-reduced-motion`, a passagem para o recebido acontece sem animação.

## A leitura do pagamento [#a-leitura-do-pagamento]

- O SDK lê o pagamento a cada 3 s enquanto a tela está aberta.
- Com a aba do navegador oculta, a leitura pausa; ao voltar, lê na hora.
- Uma resposta 429 não é erro: o SDK espera o `Retry-After` (em segundos ou data, no mínimo 1 s; sem ele, 3 s) e lê de novo.
- Cada leitura tem 8 s. Depois da primeira, uma leitura que falha não fecha a tela: o SDK tenta na volta seguinte.
- A primeira leitura tem até 20 s; se ela falhar, a promessa resolve `{ error }` e a tela não abre.

## Como termina [#como-termina]

| O que aconteceu | A tela | A promessa |
|---|---|---|
| `succeeded` com a tela aberta | mostra o recebido e fecha 4 s depois | `{ paymentIntent }` com `succeeded` |
| `succeeded` sem a tela aberta, `requires_payment_method` (o PIX venceu ou falhou), `canceled` ou `requires_confirmation` | fecha, se estava aberta | `{ paymentIntent }` com o status |
| o Comprador apertou Esc ou Fechar | fecha e a leitura para | `{ paymentIntent }` com o estado da última leitura, em geral `requires_action` |
| uma ação que o SDK não conhece | fecha | `{ error }` com `next_action_unsupported` |
| a tela não abriu em 10 s | não aparece | `{ error }` com `overlay_not_ready` |

Depois de Esc ou Fechar, chamar `handleNextAction` de novo reabre o QR atual, sem criar outro pagamento.

O que a página recebe:

```json
{
  "paymentIntent": {
    "id": "pi_...",
    "object": "payment_intent",
    "livemode": false,
    "status": "succeeded",
    "amount": 12990,
    "currency": "brl",
    "parts": [
      { "id": "pip_...", "position": 1, "method": "pix", "amount": 12990, "status": "succeeded" }
    ],
    "last_payment_error": null
  }
}
```

`last_payment_error` é `{ type, code, message }` ou `null`. A verdade do pagamento continua sendo o seu servidor, pelo webhook ou lendo o pagamento com a `sk_`.

## O prazo [#o-prazo]

O prazo do PIX vem do create, em `payment_method_options.pix.expires_after_seconds` (segundos, inteiro maior que zero):

```json
{
  "payment_method_options": { "pix": { "expires_after_seconds": 3600 } }
}
```

Sem o campo, vale o padrão da política da sua Conta. No Combinar (cartão + PIX), vale o prazo da política da Conta para o Combinar, não o enviado. Um prazo acima do máximo da política é recusado no create com `expires_after_too_large`. Vencido o prazo sem pagamento, o pagamento volta a `requires_payment_method`, com o `last_payment_error` do vencimento.

## A Content-Security-Policy [#a-content-security-policy]

A tela do QR é um iframe do host `https://sdk-gateway-overlay.assiny.com.br`. Se a sua página tem Content-Security-Policy, ele precisa estar em `frame-src`, com os outros três hosts do SDK ([Integrar o Payment Fragment](/fragments/integracao.md)).

## Testar no Modo de teste [#testar-no-modo-de-teste]

- Na faixa de valor que a política da sua Conta aceita (na política padrão, a partir de R$ 3,50), a Parte PIX de teste é sempre paga sozinha em cerca de 5 s: a tela passa ao recebido e fecha.
