Assiny Gateway
Fragments

Integrar o Payment Fragment

Do script na página ao pagamento confirmado, em nove passos no navegador e no seu servidor, com os valores do Modo de teste.

Nove passos, na ordem em que o código roda: cinco no navegador, um no seu servidor e três de volta no navegador. Os trechos usam o Modo de teste (pk_test_... e sk_test_...); no Modo real, as mesmas chamadas com as chaves do Modo real.

A verdade do pagamento é o seu servidor

A sua página nunca vê a chave secreta (sk_): ela mora só no seu servidor. O que o navegador recebe serve para a tela; quem libera o pedido é o seu servidor, pelo webhook ou lendo o pagamento com a sk_.

Navegador

Carregue o script

Na página de pagamento, pela tag, sempre do host do Assiny Gateway:

<script src="https://sdk-gateway-js.assiny.com.br/v1/assiny.js"></script>

O script define a função global Assiny. Ele não usa eval nem nada que exija Trusted Types, então funciona em página com require-trusted-types-for 'script'. Se a sua página tem Content-Security-Policy, libere o script e os quatro iframes do SDK, somando às diretivas que você já tem:

script-src 'self' https://sdk-gateway-js.assiny.com.br;
frame-src https://sdk-gateway-card.assiny.com.br https://sdk-gateway-controller.assiny.com.br https://sdk-gateway-risk.assiny.com.br https://sdk-gateway-overlay.assiny.com.br
Navegador

Crie a instância

const assiny = Assiny('pk_test_...', { locale: 'pt-BR' });

Com a chave publicável: a pk_test_ no Modo de teste, a do Modo real no Modo real. A chave publicável do Modo real só funciona em página https: (fora de localhost e 127.0.0.1). locale é 'pt-BR' (o padrão) ou 'en': o idioma dos textos do fragment e das mensagens de erro. Uma chave sk_ aqui é recusada, com aviso no console.

Navegador

Configure o pagamento

const fragments = assiny.fragments({
  mode: 'payment',
  amount: 12990, // R$ 129,90, em centavos
  currency: 'brl',
  appearance: { theme: 'assiny' },
});

amount é o total em centavos, um inteiro maior que zero. Ele serve para desenhar e dividir o pagamento; o valor que vale é o que o seu servidor manda no passo 6. Se o total mudar antes do clique (cupom, frete), chame fragments.update({ amount }). A appearance entra só aqui: Aparência.

Navegador

Monte o fragment

<div id="pagamento"></div>
const payment = fragments.create('payment', {
  paymentMethodTypes: ['card', 'pix'],
  combine: 'auto',
});
payment.on('loaderror', event => console.error(event.error.code));
payment.mount('#pagamento');

O seletor precisa casar com exatamente um elemento. O fragment ocupa a largura dele e a altura acompanha o conteúdo. Opções e eventos: AssinyGateway Payment Fragment.

Navegador

Crie os métodos de pagamento no clique

pagar.addEventListener('click', async () => {
  const { parts, error } = await assiny.createPaymentMethod({
    fragments,
    params: {
      billing_details: {
        name: 'Maria Souza',
        email: '[email protected]',
        address: { country: 'BR' },
      },
    },
  });

  if (error) {
    // A message já vem no idioma do locale; error.part diz qual Parte falhou.
    mostrarErro(error.message);
    return;
  }

  const resposta = await fetch('/checkout', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ parts }),
  });
  const pagamento = await resposta.json();
  // Passos 7 e 8.
});

O fragment confere os campos e cria um método de pagamento (pm_) por Parte: uma no cartão ou no PIX, duas no Combinar. A promessa resolve { parts: [{ method, amount, payment_method }] }, na ordem das Partes, ou { error }; ela nunca rejeita. Mande parts ao seu servidor (aqui, a rota /checkout é da sua loja).

O que você manda em billing_details vale como veio, para todas as Partes; o que falta entra dos campos Nome no cartão e País do fragment, quando ligados e preenchidos. Chaves aceitas: name, email, phone, tax_id com tax_id_type e address (line1, line2, city, state, postal_code, country); outra chave é descartada com aviso no console. Na política padrão, o pagador precisa de email no cartão e no PIX e de name no cartão; sem eles, a criação do método de pagamento ou o confirm recusa com payer_field_missing.

Servidor

Crie e confirme o pagamento no servidor

Com a chave secreta, dois pedidos à API:

  • POST https://api-gateway.assiny.com.br/v1/payment_intents, com Authorization: Bearer sk_test_..., Content-Type: application/json e o corpo:
    • amount: o total do pedido em centavos, calculado no seu servidor; a soma das Partes tem de ser igual a ele.
    • currency: 'brl'.
    • billing_details: o Comprador (name, email, tax_id com tax_id_type, phone, address).
    • parts: as Partes que vieram do navegador, cada uma { method, amount, payment_method }.
    • combined_payment_methods: { enabled: true } (opcional, recomendado com duas Partes): liga a conferência de que a política da Conta aceita o Combinar na moeda.
    • payment_method_options: { pix: { expires_after_seconds } } (opcional): o prazo do PIX em segundos; no Combinar (cartão + PIX), vale o prazo da política da Conta para o Combinar, não o enviado (PIX e a tela do QR).
    • customer (opcional): o cus_ do Comprador.
  • POST https://api-gateway.assiny.com.br/v1/payment_intents/{id}/confirm, com o id que voltou e sem corpo. O confirm não tem parâmetro: qualquer chave no corpo é 400 parameter_invalid.

Devolva ao navegador só id, status, client_secret e last_payment_error.

# 1. Crie o pagamento com as Partes que o navegador mandou.
curl https://api-gateway.assiny.com.br/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 12990,
    "currency": "brl",
    "billing_details": { "name": "Maria Souza", "email": "[email protected]" },
    "parts": [
      { "method": "card", "amount": 12990, "payment_method": "pm_..." }
    ]
  }'

# 2. Confirme com o id que voltou, sem corpo.
curl -X POST https://api-gateway.assiny.com.br/v1/payment_intents/pi_.../confirm \
  -H "Authorization: Bearer sk_test_..."

No curl, os dois pedidos à mão, com um pm_ e um pi_ de exemplo; nas outras linguagens, uma função checkout que recebe as Partes, faz os dois pedidos e devolve o status HTTP e o corpo para responder ao navegador.

Uma recusa volta com o status HTTP e o corpo { error: { type, code, message, param, request_id } } (param só quando a recusa é de um campo), e os trechos a devolvem como veio: 401 api_key_invalid (chave ausente ou inválida), 403 permission_denied, 400 com o motivo em code, como parts_sum_mismatch (a soma das Partes diferente de amount).

Navegador

Siga o próximo passo

let resultado = pagamento;

if (pagamento.status === 'requires_action' || pagamento.status === 'processing') {
  const { paymentIntent, error } = await assiny.handleNextAction({
    clientSecret: pagamento.client_secret,
  });
  if (error) {
    mostrarErro(error.message);
    return;
  }
  resultado = paymentIntent;
}

Com requires_action (o PIX), a tela do QR do PIX abre em tela cheia sobre a sua página e o SDK lê o pagamento a cada 3 s até o fim; com processing, o SDK só lê, sem desenhar nada. A promessa resolve { paymentIntent } com status, parts e last_payment_error, ou { error }. O detalhe está em PIX e a tela do QR.

Navegador

Trate o resultado

switch (resultado.status) {
  case 'succeeded':
    mostrarConfirmacao();
    break;
  case 'requires_payment_method':
    mostrarErro(resultado.last_payment_error?.message);
    break;
  case 'requires_action':
    // O Comprador fechou a tela do QR; handleNextAction de novo a reabre.
    break;
  default:
    // canceled ou requires_confirmation.
    mostrarErro('Não foi possível concluir o pagamento.');
}
  • succeeded: pago. Mostre a confirmação; o pedido é liberado pelo seu servidor.
  • requires_payment_method: o cartão foi recusado ou o PIX venceu; last_payment_error diz o motivo. Para tentar de novo, repita a partir do passo 5.
  • requires_action depois do handleNextAction: o Comprador fechou a tela do QR (Esc ou Fechar) sem pagar; chamar handleNextAction de novo reabre o mesmo QR.
  • canceled: o pagamento foi cancelado.
  • requires_confirmation: o pagamento foi criado e ainda não confirmado (passo 6).
  • { error } em qualquer passo: error.message no idioma do locale. A lista completa está na Referência do SDK.
Navegador

Teste no Modo de teste

Com as chaves pk_test_... e sk_test_..., nada é cobrado. Use uma validade no futuro e o CVC com o número de dígitos da bandeira (4 na Amex, 3 nas demais).

Cartões aprovados:

NúmeroBandeira
4242 4242 4242 4242Visa
5555 5555 5555 4444Mastercard
4000 0007 6000 0002Visa (Brasil)
3782 822463 10005Amex

Cartões recusados, com o motivo que o Modo de teste simula e o decline_code que volta:

NúmeroMotivodecline_code
4000 0000 0000 0002generic_decline05
4000 0000 0000 9995insufficient_funds51
4000 0000 0000 9987lost_card41
4000 0000 0000 9979stolen_card43
4000 0000 0000 0069expired_card54
4000 0000 0000 0127incorrect_cvc835
4000 0000 0000 0119processing_error96
4000 0000 0000 6975card_velocity_exceeded61

Na recusa, o confirm responde status: 'requires_payment_method' e last_payment_error com type: 'card_error', code: 'card_declined' e o decline_code da tabela; o motivo é só a descrição do que o Modo de teste simula e não volta na resposta. Para forçar a recusa com qualquer cartão, ponha REFUSED<código> no nome do portador (o billing_details.name ou o campo Nome no cartão), como Maria REFUSED51: o código volta em last_payment_error.decline_code; um código que o Modo de teste não conhece é ignorado.

PIX: 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, e a tela do QR mostra o pagamento confirmado.

Nesta página