> 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.

# AssinyGateway Payment Fragment

O AssinyGateway Payment Fragment recebe o pagamento com cartão e PIX dentro da sua página, num iframe do Assiny Gateway: o que o Comprador digita no cartão nunca passa pela sua página.

(veja o fragment ao vivo em /fragments/payment-fragment)

## Métodos de pagamento [#métodos-de-pagamento]

Cada método de `paymentMethodTypes` vira uma aba, na ordem da lista; a primeira começa selecionada.

- **Cartão** (`'card'`): número, validade e CVC, mais os campos Nome no cartão e País, que você pode esconder em `fields`. Bandeiras aceitas: Visa, Mastercard, Amex, Elo e Hipercard.
- **PIX** (`'pix'`): só um texto, sem campo. O QR code aparece depois que o seu servidor cria o pagamento, na tela do QR do PIX ([PIX e a tela do QR](/fragments/pix.md)). O PIX exige `currency: 'brl'`.

Com `combine: 'auto'` (o padrão), o fragment mostra também a aba **Combinar métodos**, em que o Comprador divide o total em duas Partes: "Cartão + Cartão" com `'card'` habilitado e "Cartão + PIX" com `'card'` e `'pix'`. A aba só aparece com `amount` de pelo menos 2 centavos; só com `'pix'` não há o que combinar. `combine: 'never'` tira a aba; uma lista de `'card_card'` e `'card_pix'` mostra só as combinações listadas, na ordem de `'auto'` e com o mesmo mínimo de 2 centavos.

No Combinar, o Comprador digita o valor da primeira Parte e a segunda é o resto. Com `partsFrom: 'page'`, quem divide é a sua página: o fragment esconde o campo de valor e mostra a divisão que você manda por `fragments.update({ parts })` ([Referência do SDK](/fragments/referencia.md)).

```js
const payment = fragments.create('payment', {
  paymentMethodTypes: ['card', 'pix'],
  combine: 'auto', // ['card_pix'] oferece só Cartão + PIX
});
payment.mount('#pagamento');
```

## Layout [#layout]

Dois layouts, escolhidos na aparência:

- `tabs` (o padrão): os métodos em abas, lado a lado; as setas, `Home` e `End` trocam de aba.
- `accordion`: uma linha por método, com o conteúdo aberto embaixo da linha escolhida; a primeira começa aberta.

```js
const fragments = assiny.fragments({
  mode: 'payment',
  amount: 12990,
  currency: 'brl',
  appearance: { layout: 'accordion' },
});
```

## Aparência [#aparência]

A `appearance` do `assiny.fragments()` ajusta o fragment à sua marca: o tema (`assiny`, `night` ou `flat`), a posição dos rótulos (`above` ou `floating`), o layout, 15 variáveis (cores, fonte, tamanho, espaçamento, raio e o anel de foco), regras por seletor e as fontes do catálogo. Item fora da lista ou da faixa é ignorado, só ele, com aviso no console. Os controles do fragment acima mostram o objeto `appearance` de cada escolha. Tudo em [Aparência](/fragments/aparencia.md).

```js
appearance: {
  theme: 'night',
  labels: 'floating',
  variables: { colorPrimary: '#9b8afb', borderRadius: '8px' },
}
```

## Opções [#opções]

As opções de `fragments.create('payment', opções)`:

| Opção | Valores | O que faz |
|---|---|---|
| `paymentMethodTypes` | lista não vazia de `'card'` e `'pix'` (obrigatória) | as abas, nessa ordem; item repetido é removido com aviso no console |
| `combine` | `'auto'` (padrão), `'never'` ou lista de `'card_card'` e `'card_pix'` | mostra ou não a aba Combinar métodos; a lista mostra só as combinações listadas, na ordem de `'auto'` e com `amount` de pelo menos 2 centavos: `['card_card', 'card_pix']` com `'card'` e `'pix'` vale `'auto'` (com só `'card'`, passe `['card_card']`; a lista cheia é `options_invalid`) e `[]` vale `'never'`; item repetido, desconhecido ou que `paymentMethodTypes` não permite (`['card_pix']` sem `'pix'`) é `options_invalid` |
| `partsFrom` | `'component'` (padrão) ou `'page'` | no Combinar, quem divide o total: o Comprador ou a sua página, por `fragments.update({ parts })`; `'page'` com `combine: 'never'` ou `[]` é `options_invalid`; Partes numa combinação fora da lista são `parts_invalid` |
| `fields.billingDetails.name` | `'auto'` (padrão) ou `'never'` | `'never'` esconde o campo Nome no cartão |
| `fields.billingDetails.address.country` | `'auto'` (padrão) ou `'never'` | `'never'` esconde o campo País |
| `wallets.applePay` | `'auto'` (padrão) ou `'never'` | reservado: aceito e sem efeito nesta versão |

Lista de métodos vazia ou com valor fora da lista é `payment_method_types_invalid`; qualquer outra opção com valor fora da lista, ou chave desconhecida, é `options_invalid`. Nos dois casos o fragment não monta e o erro chega pelo evento `loaderror`.

## Eventos [#eventos]

```js
payment.on('change', event => {
  botaoPagar.disabled = !event.complete;
});
```

| Evento | Quando | Payload |
|---|---|---|
| `ready` | uma vez por `mount`, com o fragment configurado; não espera as fontes | `{ fragmentType: 'payment' }` |
| `change` | logo depois do `ready` e a cada troca de aba, mudança de campo ou mudança do total | `{ fragmentType, value: { type, save, parts }, complete, empty, error }` |
| `focus` | o foco entra no fragment | `{ fragmentType: 'payment' }` |
| `blur` | o foco sai do fragment | `{ fragmentType: 'payment' }` |
| `loaderror` | o fragment não montou | `{ fragmentType, error: { type, code, message } }` |

No `change`:

- `value.type`: a aba selecionada, `'card'`, `'pix'` ou `'combine'`.
- `value.save`: só com `'card'`; nesta versão, sempre `false`.
- `value.parts`: só com `'combine'` e com o valor da primeira Parte maior que zero e menor que o total, mesmo com o cartão incompleto: `[{ method, amount }, { method, amount }]`.
- `complete`: no cartão, número, validade e CVC completos e válidos; no PIX, sempre `true`; no Combinar, o valor válido e os cartões da combinação completos.
- `empty`: nada digitado; no PIX, sempre `true`.
- `error`: `{ code, message }` do erro que está na tela, quando há.

O `change` nunca traz o que foi digitado no cartão.

## Erros [#erros]

Todo erro tem a forma `{ type, code, message }`; em `createPaymentMethod`, também `part`, o índice da Parte que falhou. A `message` vem do catálogo do SDK, no `locale` da instância (`'pt-BR'` ou `'en'`), nunca do servidor. Os quatro `type` são `validation_error` (o fragment conferiu sem ir à API), `card_error` (a API recusou o cartão), `invalid_request_error` (chamada ou configuração inválida) e `api_error` (a API não respondeu, o prazo acabou ou o SDK não conseguiu seguir).

| `type` | `code` | `message` (pt-BR) |
|---|---|---|
| `validation_error` | `incomplete_number` | `O número do cartão está incompleto.` |
| `validation_error` | `invalid_number` | `O número do cartão é inválido.` |
| `validation_error` | `incomplete_expiry` | `A validade do cartão está incompleta.` |
| `validation_error` | `invalid_expiry_month` | `O mês de validade do cartão é inválido.` |
| `validation_error` | `invalid_expiry_year` | `O ano de validade do cartão é inválido.` |
| `validation_error` | `incomplete_cvc` | `O código de segurança está incompleto.` |
| `validation_error` | `invalid_cvc` | `O código de segurança é inválido.` |
| `validation_error` | `card_brand_not_accepted` | `Esta bandeira não é aceita.` |
| `validation_error` | `incomplete_parts` | `Preencha as duas partes do pagamento.` |
| `validation_error` | `parts_invalid` | `A divisão do pagamento é inválida.` |
| `card_error` | `invalid_number` | `O número do cartão é inválido.` |
| `card_error` | `incorrect_number` | `O número do cartão está incorreto.` |
| `card_error` | `invalid_expiry_month` | `O mês de validade do cartão é inválido.` |
| `card_error` | `invalid_expiry_year` | `O ano de validade do cartão é inválido.` |
| `card_error` | `invalid_cvc` | `O código de segurança é inválido.` |
| `card_error` | `card_not_supported` | `Este cartão não é aceito.` |
| `invalid_request_error` | `payer_field_missing` | `Falta um dado do pagador.` |
| `invalid_request_error` | `email_invalid` | `O e-mail é inválido.` |
| `invalid_request_error` | `method_not_supported` | `Este método de pagamento não é aceito.` |
| `invalid_request_error` | `parameter_invalid` | `Um parâmetro enviado é inválido.` |
| `invalid_request_error` | `api_key_invalid` | `A chave de API é inválida.` |
| `invalid_request_error` | `permission_denied` | `A chave de API não tem permissão para esta chamada.` |
| `invalid_request_error` | `invalid_publishable_key` | `A chave publicável é inválida.` |
| `invalid_request_error` | `insecure_page` | `Uma chave live só funciona em página HTTPS.` |
| `invalid_request_error` | `invalid_client_secret` | `O client_secret é inválido.` |
| `invalid_request_error` | `amount_invalid` | `O valor é inválido.` |
| `invalid_request_error` | `currency_invalid` | `A moeda é inválida.` |
| `invalid_request_error` | `mode_invalid` | `O modo é inválido; use 'payment'.` |
| `invalid_request_error` | `payment_method_types_invalid` | `As opções de pagamento são inválidas.` |
| `invalid_request_error` | `options_invalid` | `As opções do Payment Fragment são inválidas.` |
| `invalid_request_error` | `fragment_type_invalid` | `Tipo de Payment Fragment desconhecido; só 'payment' existe.` |
| `invalid_request_error` | `fragment_already_exists` | `Já existe um Fragments ou Payment Fragment nesta instância.` |
| `invalid_request_error` | `mount_target_not_found` | `O seletor do mount não casa com exatamente um elemento.` |
| `invalid_request_error` | `page_origin_invalid` | `A página precisa de uma origem real para montar o Payment Fragment.` |
| `invalid_request_error` | `fragments_not_ready` | `O Payment Fragment não está montado e pronto.` |
| `invalid_request_error` | `already_mounted` | `O Payment Fragment já está montado.` |
| `invalid_request_error` | `request_in_progress` | `Já existe uma chamada em andamento.` |
| `invalid_request_error` | `client_secret_missing` | `Falta o client_secret da Intenção.` |
| `invalid_request_error` | `payment_intent_not_found` | `A Intenção de pagamento não foi encontrada.` |
| `api_error` | `api_unavailable` | `O serviço está indisponível; tente de novo.` |
| `api_error` | `rate_limited` | `Muitas tentativas; aguarde um pouco.` |
| `api_error` | `request_timeout` | `A chamada demorou demais.` |
| `api_error` | `next_action_unsupported` | `O próximo passo deste pagamento não é suportado.` |
| `api_error` | `overlay_not_ready` | `A janela do pagamento não abriu; tente de novo.` |
| `api_error` | `unknown` | `Ocorreu um erro inesperado.` |

Quando acontece cada um, e como as respostas da API viram estes códigos: [Referência do SDK](/fragments/referencia.md).
