Hosted Checkout
Sem construir front — gera link pelo dashboard, redireciona, recebe webhook quando paga. White-label com sua marca.
Cenário: você não quer construir tela de checkout — gera um link, redireciona o cliente, recebe webhook quando ele paga.
A Zhex já tem checkout hosted em produção (zhx-checkout) — é a tela onde o cliente vê o produto, escolhe método de pagamento (PIX/cartão/boleto) e finaliza. Você integra fazendo o cliente chegar até esse link.
Hoje via dashboard, API V1 pública em roadmap
A criação programática de Payment Links com shape Stripe-style (POST /v1/payment_links com line_items, after_completion, etc.) é roadmap. Hoje, links de checkout são criados pelo dashboard da Zhex (Produtos → Links de pagamento). Esta página descreve esse fluxo — quando a API V1 entrar, expandimos para cobrir programatic creation.
Fluxo atual
1. Crie o link no dashboard
Em Produtos → escolha o produto → Links de pagamento → Criar:
- Configure métodos aceitos (PIX, cartão, boleto)
- Defina
quantityfixa ou flexível - Ative cupom (
allow_promotion_codes) - Escolha o
redirect_urlpós-pagamento (https://meusite.com/obrigado?intent={PAYMENT_INTENT_ID}) - Copie a URL gerada (algo como
https://buy.zhex.com/c/lnk_…)
A URL do link é estável — você pode reusá-la em campanhas, emails, anúncios, sem expirar até desativar manualmente no dashboard.
2. Redirecione o usuário
// no seu app, quando o cliente clicar em "Comprar":
res.redirect('https://buy.zhex.com/c/lnk_seu_link');A página é hospedada pela Zhex e renderiza o checkout completo: identificação do cliente, escolha de método, tokenização (@zhexio/zhex-js por baixo), confirmação de PIX/3DS/boleto e tela de obrigado.
3. Webhook de retorno
A confirmação de pagamento chega via webhook normal — não há checkout_session.* separado:
import Zhex from '@zhexio/node';
app.post(
'/webhooks/zhex',
express.raw({ type: 'application/json' }),
async (req, res) => {
const event = Zhex.webhooks.constructEvent(
req.body,
req.headers['zhex-signature'] as string,
process.env.ZHEX_WEBHOOK_SECRET!,
);
if (event.type === 'payment_intent.succeeded') {
const intent = event.data.object as { id: string; customer: string | null; amount: number };
// Cliente pagou. Use intent.id para correlacionar com o pedido.
await registrarPedidoPago(intent.id, intent.customer, intent.amount);
}
res.json({ received: true });
},
);O redirect_url que o cliente vê no fim do fluxo recebe o payment_intent_id na query string — útil pra mostrar tela de "obrigado, seu pedido X foi confirmado", mas fonte da verdade ainda é o webhook: nunca confie no redirect (cliente pode fechar o navegador antes de chegar lá).
Branding
Configurável no dashboard em Configurações → Branding:
- Logo (PNG/SVG)
- Cor primária (hex)
- Domínio custom (CNAME apontando para
buy.zhex.com) — disponível em planos avançados
A página segue o seu visual em qualquer link gerado, sem mudança no código.
Modos suportados hoje
| Tipo de produto | Comportamento do checkout |
|---|---|
Produto ONE_TIME (digital ou físico) | Cobrança única; payment_intent.succeeded no fim |
Produto RECURRING (com Subscription template configurado) | Cria CustomerSubscription no sucesso da primeira cobrança |
Produto INSTALLMENT | Cobrança parcelada no cartão; primeira parcela imediata |
Modo "setup" (salvar cartão sem cobrar) é roadmap.
Pitfalls comuns
- Não tratar
payment_intent.payment_failed. Cliente pode tentar 3 vezes no mesmo link, falhar todas e abandonar. Webhook avisa pra você fazer follow-up por email. - Esquecer do
payment_intent_idna URL de retorno. Sem ele, sua tela de "obrigado" não consegue correlacionar com o pedido. - Pré-encher dados do cliente — hoje, o link não aceita pré-fill via query string. Cliente sempre digita os dados na primeira vez. Pré-fill é roadmap.
Eventos a tratar
| Evento | Quando | Ação |
|---|---|---|
payment_intent.succeeded | Pagamento concluído (PIX/cartão/boleto) | Registrar pedido pago, liberar produto |
payment_intent.payment_failed | Última tentativa falhou | Email de follow-up "tente novamente" |
payment_intent.canceled | Link expirou (PIX/boleto) ou cliente cancelou | Marcar pedido como abandonado |
Atualizado em