Skip to main content
Quando um pagamento já foi concluído, cancelar a compra no seu sistema não move o dinheiro de volta. Para devolver o valor ao comprador, crie um refund: um objeto financeiro próprio, ligado à cobrança original e com um status que informa se a devolução ainda está em processamento, foi concluída ou falhou. Este guia mostra como decidir entre cancelamento e devolução, criar um refund total ou parcial, acompanhar o resultado e reconciliar o dinheiro em cartão e PIX.
A regra principal é simples: se o payment_intent está succeeded, devolva o dinheiro com POST /v1/refunds. Se ele ainda não recebeu o dinheiro, cancele a tentativa com POST /v1/payment-intents/{id}/cancel.

Primeiro: cancelar ou devolver?

Olhe o status atual do payment_intent antes de escolher a operação.

O que acontece com o dinheiro

A chamada para criar um refund inicia um fluxo financeiro; ela não é apenas uma mudança de status no seu pedido.
  1. A Chargefy valida se a charge foi paga, capturada e ainda tem valor disponível para devolução.
  2. O valor solicitado fica logicamente comprometido com aquele refund. Isso impede que dois refunds concorrentes devolvam mais do que foi capturado.
  3. A devolução é enviada pela mesma transação financeira usada no pagamento.
  4. O objeto refund informa o resultado em status.
  5. Somente status: "succeeded" confirma que a devolução foi processada.

Cartão de crédito

O crédito volta para o mesmo cartão usado na compra. O comprador não informa outro cartão e sua integração não envia dados de destino. Quando o refund chega a succeeded, a devolução foi processada. O tempo para o crédito aparecer na fatura ou no limite disponível ainda depende do emissor do cartão e do fechamento da fatura. Por isso, não prometa que o comprador verá o crédito imediatamente na tela do banco.

PIX

O valor volta pelo fluxo da transação PIX original. Sua integração não envia uma nova chave PIX e não escolhe uma conta de destino. Refunds de PIX costumam concluir rapidamente, mas a regra de integração é a mesma do cartão: espere refund.status: "succeeded" antes de marcar a devolução como concluída.
As taxas cobradas pela Chargefy não são devolvidas quando um refund é criado. O amount do refund representa o valor devolvido ao comprador, não uma reversão das taxas da transação.

Antes de começar

Você precisa de:
  • uma API key com permissão de escrita;
  • o ID do payment_intent (pi_*) ou da charge (ch_*);
  • uma cobrança paga e capturada;
  • o valor a devolver, quando a devolução for parcial;
  • uma chave de idempotência única para essa operação.
Para plataformas, envie também o header Organization com a organização conectada dona do pagamento.

Passo a passo

1

Confirme que o pagamento foi concluído

Consulte o payment_intent e verifique o status. Um refund só faz sentido quando existe uma charge paga e capturada.
Se o retorno estiver em requires_capture, o cartão foi autorizado, mas o dinheiro ainda não foi capturado. Nesse caso, cancele o Payment Intent em vez de criar um refund.
2

Escolha a referência da devolução

O endpoint aceita exatamente um destes campos:
  • payment_intent: caminho recomendado quando o seu sistema acompanha a cobrança pelo processo completo. A Chargefy encontra a charge capturada correspondente.
  • charge: use quando você precisa devolver uma tentativa específica, por exemplo, ao reconciliar duas cobranças duplicadas.
Não envie os dois campos no mesmo request.
3

Escolha entre devolução total e parcial

Para devolver todo o saldo ainda disponível, omita amount. Para devolver apenas uma parte, envie amount como inteiro em centavos.Por exemplo, 5000 significa R$ 50,00. Nunca envie 50.00, 50,00 ou uma string formatada.
4

Gere uma chave de idempotência

Use uma chave única por devolução lógica e envie-a somente no header Idempotency-Key. Se a resposta se perder, repita o mesmo request com a mesma chave.Não gere uma chave nova só porque ocorreu timeout: isso pode criar uma segunda devolução.
5

Crie o refund

Faça POST /v1/refunds usando uma das variantes abaixo. A resposta é o objeto refund completo.
6

Decida pelo status do refund

Trate succeeded como conclusão. Para pending ou requires_action, mantenha a operação em andamento. Para failed ou canceled, não informe ao comprador que o dinheiro foi devolvido.
7

Acompanhe os webhooks

Persista o refund.id, processe os eventos de forma idempotente e atualize o seu pedido usando sempre o estado completo de data.object.

Payloads de criação

(a) Devolução total pelo Payment Intent

É o caminho mais simples quando o seu sistema já guarda o pi_*. A ausência de amount significa: devolva todo o valor capturado que ainda está disponível.

(b) Devolução total por uma charge específica

Use esta forma quando houver mais de uma tentativa relacionada ao mesmo pedido e você souber exatamente qual charge precisa ser devolvida.

(c) Devolução parcial

Envie amount quando somente parte da compra deve voltar ao comprador.
Os motivos aceitos são: reason é opcional. Use metadata para guardar referências livres do seu sistema, como o ID do pedido ou do atendimento. Não coloque regra de negócio essencial apenas em metadata.

Exemplo de resposta

O endpoint retorna 200 OK com o objeto refund completo. Neste exemplo, uma devolução parcial de R$ 50,00 já foi processada:

Como interpretar cada status

refund.created confirma que a solicitação entrou no ciclo de processamento; não confirma que o comprador recebeu o valor. A confirmação financeira é data.object.status: "succeeded" na resposta, em refund.updated ou numa consulta posterior.

Webhooks que sua integração deve tratar

Exemplo resumido de um refund que saiu de pending para succeeded:
Seu receiver deve:
  1. validar a assinatura do webhook;
  2. deduplicar pelo event.id;
  3. localizar a devolução por data.object.id;
  4. substituir o estado local pelo objeto completo de data.object;
  5. confirmar a devolução ao comprador somente quando status for succeeded.

Cenários comuns

O comprador desistiu depois de pagar com cartão

O pagamento já está succeeded. Crie um refund total pelo payment_intent e use reason: "requested_by_customer". Informe que o crédito pode levar algum tempo para aparecer na fatura, mesmo depois de o refund ficar succeeded.

Houve duas cobranças para o mesmo pedido

Identifique a charge que deve ser revertida e crie o refund por ela com reason: "duplicate". Não devolva pelo pedido de forma genérica: duas charges distintas exigem reconciliação explícita para evitar devolver a cobrança correta por engano.

Apenas um item do pedido foi devolvido

Some o valor daquele item e envie amount em centavos. O refund será parcial e o restante da charge continuará pago. Você pode criar outros refunds parciais depois, até consumir todo o valor capturado disponível.

Um PIX foi pago depois de o pedido ser cancelado

Se a confirmação financeira chegou, existe uma charge paga mesmo que o seu pedido já esteja cancelado. Crie um refund usando essa charge ou o payment_intent relacionado. Não tente “cancelar o PIX pago”: depois da liquidação, o caminho correto é a devolução.

A API retornou timeout ou erro 5xx

Não conclua que nada aconteceu. Consulte o refund pelo ID conhecido ou liste os refunds filtrando por charge ou payment_intent. Depois, repita a chamada com o mesmo Idempotency-Key e o mesmo corpo. Uma chave nova representa uma nova operação e pode causar devolução duplicada.

O refund falhou por saldo insuficiente

O objeto pode chegar a failed com failure_reason: "insufficient_funds". Mantenha a devolução como pendente de resolução no seu backoffice; não informe ao comprador que o dinheiro voltou. Consulte o refund e acione o suporte se a causa não puder ser resolvida operacionalmente.

Erros comuns da API

O que não fazer

  • Não use o status do seu pedido como prova de que o dinheiro voltou.
  • Não trate refund.created como confirmação financeira.
  • Não tente cancelar um Payment Intent que já está succeeded.
  • Não crie uma nova chave de idempotência para repetir a mesma devolução.
  • Não use apenas charge.status para reconciliar; guarde o objeto refund e acompanhe o seu status.
  • Não prometa prazo exato de visualização no cartão depois de succeeded.
  • Não tente desfazer um refund concluído. Para cobrar novamente, crie uma nova tentativa de pagamento autorizada pelo comprador.

Checklist para produção

  • Guardar payment_intent, charge e refund no pedido.
  • Enviar valores monetários como inteiros em centavos.
  • Usar Idempotency-Key em toda criação de refund.
  • Processar refund.created, refund.updated e refund.failed.
  • Deduplicar webhooks por event.id.
  • Tratar somente refund.status: "succeeded" como conclusão.
  • Separar “pedido cancelado”, “refund solicitado” e “dinheiro devolvido” no seu modelo de status.
  • Testar devolução total, parcial, retry e falha antes de operar em produção.

Próximos passos

Criar um refund

Contrato completo do POST /v1/refunds, incluindo campos e erros.

Objeto refund

Consulte todos os campos, status e motivos de falha.

Idempotência

Saiba como repetir mutações sem executar a mesma operação duas vezes.

Cancelar uma tentativa

Use este fluxo quando o dinheiro ainda não foi capturado.