Refunds
Reembolso iniciado por você — janelas e tempos de liquidação por método. Hoje single-shot por PaymentIntent.
Refund é o reembolso iniciado por você (merchant). Diferente de chargeback (iniciado pelo emissor), o refund preserva sua reputação com a bandeira e não conta no chargeback ratio.
Criar refund
const refund = await zhex.refunds.create({
payment_intent: 'pi_3MtwBwLkdI',
// omita amount para refund total
reason: 'requested_by_customer',
});
// refund.id → "re_…", refund.status → "succeeded" | "failed"Sem amount, devolve o valor total da transação (incluindo eventuais ajustes de cupom).
Refund parcial
await zhex.refunds.create({
payment_intent: 'pi_3MtwBwLkdI',
amount: 1000, // R$ 10,00 dos R$ 49,90 originais
reason: 'product_unsatisfactory',
});Single-shot por PaymentIntent — hoje
Cada PaymentIntent aceita um único refund (total ou parcial em valor único). Múltiplos refunds parciais sequenciais sobre o mesmo pi_* são roadmap — quando entrar, o Refund ganha tabela própria e cada chamada gera uma row distinta. Hoje, se você precisa devolver R$ 10 + R$ 30 separadamente, faça uma chamada de R$ 40.
Resposta
{
"id": "re_3MtwBwLkdIwHu7ix28a3tqPa",
"object": "refund",
"livemode": false,
"amount": 4990,
"currency": "brl",
"payment_intent": "pi_3MtwBwLkdIwHu7ix28a3tqPa",
"charge": "ch_acquirer_…",
"reason": "requested_by_customer",
"status": "succeeded",
"created": 1714060500
}O id do refund (re_*) é derivado 1:1 do PaymentIntent (pi_* → re_*) — útil em logs e correlação.
Janelas e liquidação
| Método | Janela máxima | Liquidação | Como reverte |
|---|---|---|---|
| PIX | 90 dias | < 1h | BACEN MED |
| PIX Automático | 90 dias | < 1h | BACEN MED |
| Boleto | 60 dias | D+5 | TED reverso |
| Cartão BR | 180 dias | D+30 | Estorno bandeira |
| Cartão internacional | 120 dias (Visa) / 180 dias (Master) | D+30 | Estorno bandeira |
Janela é contada a partir do momento de liquidação da cobrança original. Após esse prazo, refund via API é rejeitado — alternativa é TED manual (suporte).
Razões (reason)
type RefundReason =
| 'duplicate' // cobrança duplicada
| 'fraudulent' // você identificou fraude
| 'requested_by_customer' // cliente pediu
| 'product_unsatisfactory' // qualidade abaixo
| 'service_not_received' // não entregou
| 'expired_uncaptured_charge' // pré-auth expirou
| 'general'; // catch-allreason: 'fraudulent' abre flag interna na sua conta — use só quando tiver certeza. Erro repetido aqui aumenta scoring de risco.
Status do refund
type RefundStatus = 'pending' | 'succeeded' | 'failed' | 'canceled';Em PIX/cartão BR, o status sai direto em succeeded ou failed na criação. Em cartão internacional, pode entrar pending por alguns segundos enquanto o adquirente confirma.
Listar refunds
// todos os refunds
for await (const r of zhex.refunds.list({ limit: 100 }).autoPagingEach()) {
/* … */
}
// refund de um PaymentIntent específico
const refunds = await zhex.refunds.list({
payment_intent: 'pi_…',
limit: 100,
});Idempotência
Sempre use Idempotency-Key no refund — retry de network não duplica:
await zhex.refunds.create(
{ payment_intent: 'pi_…' },
{ idempotencyKey: `refund:${orderId}` },
);Se você não passar, o SDK gera UUIDv4 automaticamente.
Boas práticas
- Refund parcial em assinatura: não cancela a
CustomerSubscription. Para cancelar, usecustomer_subscriptions.cancelseparadamente. - Refund de parcelado: a Zhex devolve o total ao cliente; o emissor cancela as parcelas restantes. Você recebe o estorno em D+30 do liquidação original.
- Refund proativo > chargeback. Se cliente reclamou e você sabe que tem razão, refunde antes que ele abra disputa — chargeback custa valor + taxa + ratio.
- Comunicação com cliente: envie email transacional confirmando o estorno. Reduz suporte e mostra atenção.
Atualizado em