docs

Cartão

Cartão com Zhex Elements (hosted fields), 3DS automático, parcelamento BR e BIN routing inteligente.

A integração de cartão sempre passa por zhex.js (hosted fields). Seu servidor recebe tok_*, nunca PAN/CVV — escopo PCI-DSS SAQ-A.

1. Renderizar Elements

Instale o pacote no front e monte o iframe — não há bundle global via <script> hoje (a versão CDN entra numa minor próxima):

npm install @zhexio/zhex-js
import { Zhex } from '@zhexio/zhex-js';

const zhex = Zhex('zk_live_...');
const elements = zhex.elements();
const card = elements.create('card');
card.mount('#card-element');

document
  .getElementById('payment-form')!
  .addEventListener('submit', async (e) => {
    e.preventDefault();

    const result = await zhex.createToken(card);
    if ('error' in result) {
      alert(result.error.message);
      return;
    }

    // tok_* vai pro seu servidor por fetch normal
    const res = await fetch('/api/charge', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ token: result.token.id }),
    });
    const intent = await res.json();
    // intent.status: 'succeeded' | 'requires_action' | 'processing' | 'canceled'
  });

A zk_live_* (publishable) pode aparecer no front — só consegue criar Token. Não roda charge, não vê dados sensíveis, não lista clientes.

2. Confirmar no servidor

A confirmação na API V1 é dois passos: cria o intent, depois confirma com o token.

import { zhex } from '@/lib/zhex';

const intent = await zhex.paymentIntents.create({
  amount: 4990,
  currency: 'brl',
  customer: 'cus_…',                    // crie um Customer antes
  payment_method_types: ['card'],
  description: 'Curso Node Avançado',
});

const confirmed = await zhex.paymentIntents.confirm(intent.id, {
  payment_method: tokenId,              // tok_* recebido do front
});
// confirmed.status: 'succeeded' | 'requires_action' | 'processing' | 'canceled'

3DS (3D Secure)

3DS reduz chargeback transferindo a responsabilidade para o emissor (liability shift). A Zhex aplica automaticamente quando o adquirente sinaliza risco ou o emissor exige.

Quando o cliente precisa autenticar, o PaymentIntent volta em requires_action com next_action:

{
  "id": "pi_…",
  "status": "requires_action",
  "next_action": {
    "type": "redirect_to_url",
    "redirect_to_url": {
      "url": "https://3ds.acquirer.com/challenge/...",
      "return_url": "https://meusite.com/checkout/success"
    }
  },
  "last_payment_error": null
}

No front, redirecione o cliente para next_action.redirect_to_url.url. Após o challenge, o cliente volta ao seu return_url (configurado pelo adquirente) — escute o webhook payment_intent.succeeded ou payment_intent.payment_failed como fonte da verdade do desfecho.

const confirmed = await zhex.paymentIntents.confirm(intent.id, {
  payment_method: tokenId,
});

if (confirmed.status === 'requires_action' && confirmed.next_action?.type === 'redirect_to_url') {
  // Cliente vai pro app do banco / página 3DS
  return res.redirect(confirmed.next_action.redirect_to_url.url);
}

Parcelamento (installments)

Hoje o parcelamento é configurado a nível de produto no dashboard (Produtos → Pagamento → Parcelas e juros). A API V1 não aceita ainda payment_method_options.card.installments no body do PaymentIntent — o intent herda a configuração do produto associado.

Modelos disponíveis no dashboard:

  • Sem juros (merchant) — você assume o juros. A taxa de antecipação é descontada no payout.
  • Com juros (consumer) — emissor cobra juros do cliente. Você recebe à vista.

Em parcelamento ≥ 4x, a maioria dos emissores força challenge 3DS independente da configuração — regra anti-fraude da bandeira.

BIN routing

A Zhex usa smart routing baseado no BIN do cartão (primeiros 6 dígitos):

  • Cartão internacional → adquirente cross-border (cost-optimized)
  • Cartão BR premium → adquirente com maior approval para esse BIN
  • Cartão BR pré-pago → roteamento que aceita pré-pago (alguns adquirentes recusam)

Você não configura nada — é automático. O detalhe de qual adquirente atendeu cada transação fica visível no dashboard (Transações → detalhe → Roteamento).

Cartões de teste

Em test mode (zsk_test_*):

PANComportamento
4242 4242 4242 4242Sucesso
4000 0000 0000 0002card_declined
4000 0000 0000 9995insufficient_funds
4000 0000 0000 0069expired_card
4000 0000 0000 0127incorrect_cvc
4000 0000 0000 0119processing_error (retryable)
4000 0000 0000 3220Força requires_action (3DS)
4000 0000 0000 9987lost_card decline
4000 0000 0000 9979stolen_card decline
4000 0000 0000 0259Sucesso + simula charge.dispute.created
4000 0000 0000 1976Sucesso + simula chargeback product_not_received

CVV qualquer (3 ou 4 dígitos), validade futura. Mais cenários em Test mode.

Boas práticas

  • Sempre 3DS em ticket alto (> R$ 200 BR ou > $ 50 internacional) — chargeback ratio cai de 0,3% para < 0,05%.
  • Não armazene tok_* — single-use, 15min TTL. Para reutilizar, suba para PaymentMethod (pm_*) via tokenização.
  • statement_descriptor alinhado com a descrição do produto — cliente reconhece a cobrança 30 dias depois e não abre chargeback por desconhecimento.
Esta página foi útil?

Atualizado em

Nesta página