SelectwinSelectwin

Cartões - Tokenização no navegador

Visão Geral

Duas formas de aceitar cartão na sua própria página sem que o número passe pelo seu servidor:

ModeloPor onde passa o número do cartão
Checkout hospedado (Sessions)só a página da Selectwin
Selectwin Elements (esta página, §A) — os campos do cartão num iframe da Selectwin dentro da sua páginasó o iframe da Selectwin
Integração direta (§B) — o seu JavaScript envia o cartão à Selectwino navegador (o seu JS)
Cartão completo pela API (Criar Cartão com numbering)o seu servidor

Nos dois modelos desta página a Selectwin tokeniza o cartão, e o que chega ao seu servidor, e depois à nossa API, é um token de uso único (uma hora).

A. Selectwin Elements — recomendado

É o modelo do Stripe Elements: você carrega um script da Selectwin, monta um elemento de cartão num div seu, e os campos (número, nome, validade, CVV) aparecem dentro de um iframe servido pela Selectwin. A sua página não tem o cartão no DOM; o createToken devolve o token e os dados mascarados.

Texto
<div id="card-element"></div>
<button id="pay">Pagar</button>

<script src="https://pay.selectwin.io/elements/v1/elements.js"></script>
<script>
  var selectwin = Selectwin('pk_test_…', { locale: 'pt-BR' });
  var card = selectwin.elements().create('card', {
    style: { base: { color: '#1f2933', fontSize: '16px', fontFamily: 'Inter, sans-serif' }, invalid: { color: '#dc2626' } }
  });
  card.mount('#card-element');

  var complete = false;
  card.on('change', function (ev) { complete = ev.complete; /* ev.brand, ev.error, ev.empty */ });

  document.getElementById('pay').addEventListener('click', function () {
    if (!complete) return;
    selectwin.createToken(card).then(function (r) {
      if (r.error) { alert(r.error.message); return; }   // já em pt-BR
      // r.token → envie ao seu servidor; r.card = { brand, brandType, firstDigits, lastDigits, expirationMonth, expirationYear, holderName }
    });
  });
</script>
ChamadaO que faz
Selectwin(pk, { locale })Cria a instância com a sua chave publicável. locale: pt-BR (padrão), en, es
elements().create('card', { style, placeholders, title })O elemento de cartão. style.base aceita color, fontFamily, fontSize, fontWeight, backgroundColor, borderColor, borderRadius, focusBorderColor, '::placeholder': { color }; style.invalid.color
card.mount(seletor | elemento) · unmount() · destroy()Coloca/retira o iframe. O iframe ajusta a própria altura
card.on('ready' | 'change' | 'focus' | 'blur', fn)change traz { complete, empty, brand, error: { code, message } }
card.focus() · card.clear() · card.update({ style })
selectwin.createToken(card)Promessa que nunca rejeita: { token, card } ou { error: { code, message } }

Se a sua página tiver CSP, permita frame-src https://pay.selectwin.io; script-src https://pay.selectwin.io. Nada mais: é o iframe que fala com a API.

O token entra no seu servidor como qualquer campo do formulário e vai à API em payment.card.token (Criar Transação), em Criar Assinatura ou em POST /v1/cards { customerId, token } (Criar Cartão). A Selectwin associa o token ao cliente na hora: o cartão já pode ser cobrado. Como foi o comprador quem digitou o cartão, a API devolve associated: null até a 1ª cobrança aprovada dele (o mesmo que acontece com o cartão do checkout hospedado).

B. Integração direta

Para quem não pode usar um iframe (apps nativos, WebViews restritas): o seu próprio código envia o cartão à Selectwin, que o tokeniza. O número passa pelo seu JavaScript, mas nunca pelo seu servidor.

Texto
navegador ──(1) GET /v1/tokenization/config?pk=pk_…──▶ Selectwin
navegador ◀── onde enviar o cartão + nomes dos campos ───┘
navegador ──(2) POST /v1/tokenization/tokens?pk=pk_… {holderName, numbering, …}──▶ Selectwin
navegador ◀── { token: "…" } ──────────────────────────────────────────────────────┘
navegador ──(3) token ──▶ seu servidor ──(4) POST /v1/transactions { payment.card.token } ──▶ Selectwin

B.1 Configuração de tokenização

HTTP
GET /v1/tokenization/config?pk=pk_test_…

Sem autenticação além da chave publicável (ou, do servidor, a SelectKey sem pk). A resposta pode ser lida de qualquer origem (Access-Control-Allow-Origin: *) e não deve ser guardada em cache.

JSON
{
  "environment": "sandbox",
  "publicKey": "pk_test_…",
  "tokenization": {
    "method": "POST",
    "url": "https://api.selectwin.io/v1/tokenization/tokens?pk=pk_test_…",
    "headers": { "Content-Type": "application/json", "Accept": "application/json" },
    "fields": {
      "holderName": "holderName",
      "number": "numbering",
      "expirationMonth": "expirationMonth",
      "expirationYear": "expirationYear",
      "securityCode": "securityCode"
    },
    "tokenField": "token",
    "ttlSeconds": 3600
  },
  "acceptedBrands": ["visa", "mastercard", "elo", "amex", "hipercard"],
  "elements": {
    "scriptUrl": "https://pay.selectwin.io/elements/v1/elements.js",
    "fieldsUrl": "https://pay.selectwin.io/elements/v1/fields?pk=pk_test_…"
  }
}
CampoDescrição
environmentO namespace da chave: sandbox gera tokens de teste; live, de produção
tokenization.url / headersPara onde e com que cabeçalhos o navegador envia o cartão. Envie exatamente como veio
elements.scriptUrl / elements.fieldsUrlO SDK e o iframe do Selectwin Elements (§A)
tokenization.fieldsNome lógico → nome do campo no corpo. Mês com 2 dígitos, ano com 4
tokenization.tokenFieldOnde está o token na resposta (token)
tokenization.ttlSecondsValidade do token (3600 s)
acceptedBrandsBandeiras que a sua conta aceita — recuse na página antes de enviar

Erros

HTTPcodeQuando
401publicKeyInvalidpk ausente, desconhecida, revogada ou expirada (a chave de API revogada leva a publicável junto)
422publicKeyMissingA SelectKey usada é antiga e não tem chave publicável — crie uma chave de API nova
503tokenizationNotConfiguredA tokenização no navegador não está disponível no ambiente

B.2 No navegador

Texto
<script>
async function tokenizeCard(pk, card) {
  const cfg = await fetch(`https://api.selectwin.io/v1/tokenization/config?pk=${encodeURIComponent(pk)}`)
    .then((r) => r.json());
  const f = cfg.tokenization.fields;
  const body = {
    [f.holderName]: card.holderName,
    [f.number]: card.number.replace(/\D/g, ''),
    [f.expirationMonth]: String(card.expirationMonth).padStart(2, '0'),
    [f.expirationYear]: String(card.expirationYear).length === 2 ? `20${card.expirationYear}` : String(card.expirationYear),
    [f.securityCode]: card.securityCode,
  };
  const res = await fetch(cfg.tokenization.url, {
    method: cfg.tokenization.method,
    headers: cfg.tokenization.headers,
    body: JSON.stringify(body),
  });
  const json = await res.json();
  if (!res.ok) throw new Error(json?.error?.details || json?.error?.message || 'card refused');
  return json[cfg.tokenization.tokenField]; // o token — envie ao seu servidor
}
</script>
HTTP
POST /v1/tokenization/tokens?pk=pk_test_…
CampoTipoDescrição
holderNamestringNome impresso no cartão (3 a 255)
numberingstringNúmero do cartão (validado por Luhn)
expirationMonthinteger1 a 12
expirationYearinteger4 dígitos (ou 2 → 20xx). Não pode estar no passado
securityCodestringCVV (3 ou 4 dígitos)

Resposta 201:

JSON
{
  "token": "a1b2c3d4…",
  "ttlSeconds": 3600,
  "card": { "brand": "Visa", "brandType": "visa", "firstDigits": "4111", "lastDigits": "1111",
            "expirationMonth": "12", "expirationYear": "2030", "holderName": "Ana Lima" }
}
HTTPcodeQuando
400(validação) · cardBrandNotSupportedCampo inválido, Luhn, cartão vencido; bandeira que a sua conta não aceita
401publicKeyInvalidpk desconhecida, revogada ou expirada
422invalidCardNumber · invalidSecurityCode · invalidExpiryMonth · invalidExpiryYear · expiredCardError · invalidPaymentInformationO cartão foi recusado na tokenização
429tokenizationRateLimitedTeto por minuto: 15 por IP do comprador (na borda) e 120 por chave publicável
503cardTokenizationUnavailableA tokenização não respondeu a tempo. Tente de novo

Regras que valem a pena seguir na página:

  • Nunca envie numbering/securityCode ao seu servidor — só o token.
  • Valide o Luhn e a bandeira (acceptedBrands) antes de enviar: uma recusa local é mais rápida.
  • Guarde a configuração só pelo tempo da página; peça de novo a cada carregamento (Cache-Control: no-store).
  • Com formulário público, considere um desafio anti-bot (o plugin do WooCommerce integra o Cloudflare Turnstile) para que a sua página não vire ferramenta de teste de cartões — além do teto por chave que a API aplica.

No seu servidor (A e B)

O token entra onde o cartão completo entraria:

JSON
POST /v1/transactions
{
  "amount": 15990,
  "payment": {
    "method": "credit",
    "installments": 3,
    "card": { "token": "a1b2c3…" }
  },
  "customer": { "id": "cus_…" }
}
JSON
POST /v1/cards
{ "customerId": "cus_…", "token": "a1b2c3…", "primary": true }

A resposta é o cartão de sempre (Criar Cartão), com firstDigits de 4 dígitos e associated: null até a 1ª cobrança aprovada (o cartão foi digitado pelo comprador; ele já pode ser cobrado).

Erros do token (além dos erros de cartão de Criar Cartão):

HTTPcodeQuando
422cardTokenInvalidToken desconhecido, expirado (1 h) ou já usado. Gere outro no navegador
422cardDeclinedO cartão foi recusado na validação (zero dollar)
422cardTokenCustomerUnavailableO cliente não pôde ser cadastrado para receber o cartão (dados incompletos/recusados)
503cardTokenAssociationFailedA validação do cartão não respondeu a tempo. Nada foi cobrado; tente de novo com um token novo
503tokenizationNotConfiguredAmbiente sem tokenização no navegador

O cadastro por token não responde 429: o token foi cunhado no navegador de um comprador, então conta na janela de cada comprador (o cliente e o endereço de onde o token foi gerado) e num teto da loja para os cartões dos compradores — nunca no teto da conta. Acima desse teto, ou com a janela do endereço fechada por outros compradores da mesma rede, o cartão é criado do mesmo jeito (201, associated: null) e validado logo em seguida, antes da 1ª cobrança. Só fica sem validar o cartão cujo próprio cliente já esgotou a janela dele (cartões demais recusados na hora) ou cujo endereço também esgotou as validações da hora: o token expira em 1 h e a 1ª cobrança com ele é recusada — peça o cartão de novo.

Sandbox

Com uma pk_test_… a tokenização acontece no ambiente de testes, e os cartões de teste valem como sempre. Um token de teste nunca cobra em produção: a chave de API sk_test_… só enxerga o namespace sandbox.

Atualizado em 17 de outubro de 2026

Preferências de cookies

Hoje o site usa só cookies essenciais: nenhuma ferramenta de medição ou de anúncios está ligada, e nada é compartilhado com terceiros. Se isso mudar, você decide aqui antes de qualquer coisa ser carregada.

  • Mantêm o site funcionando: segurança, a sua escolha sobre cookies e preferências como a versão da documentação. Não dependem de consentimento.

    Sempre ativos