One-click PIX
Cliente já comprou uma vez — próxima cobrança rápida com PaymentMethod salvo (cartão). PIX Automático em roadmap.
Cenário: cliente já comprou uma vez. Próxima compra você quer cobrar com fricção mínima.
A versão definitiva desse fluxo (débito direto via PIX Automático sem QR) está em desenvolvimento — veja PIX Automático. Hoje, o caminho mais próximo de "one-click" é cartão salvo (PaymentMethod reutilizável).
PIX Automático — em desenvolvimento
Débito recorrente direto via PIX Auto (BACEN) ainda não está exposto na API V1. Este recipe cobre o que funciona hoje: cartão salvo via tokenização. Quando PIX Automático entrar, expandimos esta página com o fluxo equivalente sem QR.
Fluxo: cartão salvo (1-clique)
1. Primeira compra — tokenizar e salvar
Cliente preenche o cartão via @zhexio/zhex-js. Você cria o PaymentMethod (pm_*) atrelado ao customer:
import { zhex } from '@/lib/zhex';
app.post('/api/checkout/first', async (req, res) => {
const { email, name, token, amount } = req.body;
// 1) Customer (idempotente por email)
const customer = await zhex.customers.create(
{ email, name },
{ idempotencyKey: `customer:${email}` },
);
// 2) Salvar cartão como PaymentMethod (consome o token)
const pm = await zhex.paymentMethods.create({
type: 'card',
token,
customer: customer.id,
});
await db.users.update({
where: { email },
data: {
zhexCustomerId: customer.id,
defaultZhexPaymentMethod: pm.id,
},
});
// 3) Cobrar a primeira compra com tokenização nova
// (token foi consumido pelo paymentMethods.create acima — para cobrar nesta
// mesma sessão, gere novo token via zhex.js antes deste passo, ou faça a
// cobrança direta SEM salvar o cartão e salve numa próxima compra)
res.json({ ok: true, customerId: customer.id, paymentMethodId: pm.id });
});2. Próxima compra — recobrar via cartão salvo
Cobrança contra um pm_* salvo passa direto, sem nova tokenização — basta enviar o pm_* no confirm com off_session: true:
app.post('/api/checkout/repeat', async (req, res) => {
const { userId, amount, orderId } = req.body;
const user = await db.users.findUnique({ where: { id: userId } });
if (!user?.zhexCustomerId || !user?.defaultZhexPaymentMethod) {
return res.status(400).json({ error: 'no_saved_card' });
}
const intent = await zhex.paymentIntents.create(
{
amount,
currency: 'brl',
customer: user.zhexCustomerId,
payment_method_types: ['card'],
description: `Pedido ${orderId}`,
},
{ idempotencyKey: `order:${orderId}` },
);
const confirmed = await zhex.paymentIntents.confirm(intent.id, {
payment_method: user.defaultZhexPaymentMethod, // pm_*
off_session: true, // cliente não está presente
});
// confirmed.status:
// 'succeeded' — cobrança ok, libera produto
// 'requires_action' — issuer pediu 3DS; envie email de autorização
// 'canceled' — cartão recusado, peça outro
res.json({ status: confirmed.status, paymentIntentId: confirmed.id });
});A Zhex valida que o pm_* pertence ao mesmo customer do intent e à mesma partição (livemode); cross-customer ou cross-mode retornam payment_method_not_found (mesma resposta que id inexistente — sem leak de quais cartões existem).
3. Listar cartões salvos do customer
Para a tela "Seus cartões salvos":
const cards = await zhex.paymentMethods.list({
customer: user.zhexCustomerId,
limit: 10,
});
// cards.data: [{ id, card: { brand, last4, exp_month, exp_year } }, ...]4. Detach (remover cartão)
await zhex.paymentMethods.detach('pm_…');Marca o pm_* como inativo. Histórico de transações que o usaram é preservado.
Quando o cartão expira
Quando uma cobrança recorrente falha porque o cartão expirou, você recebe payment_intent.payment_failed. Trate com email automático "atualize seu cartão" → linka para uma página com @zhexio/zhex-js mountando novo Card Element → tokeniza → cria novo pm_* → marca como default no seu BD.
if (event.type === 'payment_intent.payment_failed') {
const intent = event.data.object;
if (intent.customer) {
await sendUpdateCardEmail(intent.customer, {
reason: intent.last_payment_error?.code ?? 'unknown',
retryUrl: `https://meusite.com/billing/update?intent=${intent.id}`,
});
}
}Quando NÃO usar cartão salvo
- Cliente nunca comprou. Não há
pm_*para reutilizar — use o fluxo padrão de tokenização. - Cliente pediu "comprar como convidado". Não persista o cartão; cobre via
tok_*direto e descarte.
Atualizado em