Reembolsar uma transação
Devolve ao cliente o valor de uma transação já aprovada, total ou parcialmente, respeitando os prazos e limites do meio de pagamento. Use quando precisar estornar uma compra — por desistência, produto
Devolve ao cliente o valor de uma transação já aprovada, total ou parcialmente, respeitando os prazos e limites do meio de pagamento. Use quando precisar estornar uma compra — por desistência, produto indisponível, cobrança incorreta ou acordo comercial.
POST /v1/transactions/{transactionId}/refund
Como funciona
Um reembolso (em inglês, refund) é a operação que devolve dinheiro ao cliente de uma transação que já foi paga (status approved). É diferente de cancelar uma transação ainda não capturada: aqui o valor já saiu do cliente e entrou no seu saldo, então o reembolso debita o seu saldo para devolver ao portador.
Quando você chama este endpoint, o servidor:
- Valida que o
transactionIdexiste e está em estado elegível (approved). - Registra o reembolso, gera um comprovante (
receiptcomtype: "refund") e dispara o estorno no adquirente / meio de pagamento. - Atualiza a transação para
status: "refunded"e marca os recebíveis afetados comorefunded(preenchendorefundedAt). - Devolve a transação completa, já com o novo reembolso dentro do array
refunds[].
A devolução ao cliente não é instantânea: o adquirente processa o estorno em segundo plano. Em cartão de crédito, o valor pode levar de 1 a 2 faturas para aparecer; em PIX/boleto, o fluxo de devolução é diferente e pode exigir dados bancários do cliente. A resposta 200 confirma que o reembolso foi aceito e registrado, não que o dinheiro já chegou ao cliente.
Importante
O reembolso é irreversível. Uma vez que a transação entra em refunded, não há operação para "desfazer" o estorno — seria necessário uma nova cobrança. Confirme o valor e o motivo antes de enviar.
Acompanhe pelo webhook, não por polling
Como o estorno é assíncrono, não fique consultando a transação em laço para saber se o reembolso concluiu. Assine os eventos de webhook da transação (ex.: transaction.refunded) e reaja à notificação. Veja Proibição de polling e Webhook Events.
Quando usar (e quando não usar)
Use o reembolso para:
- Desistência da compra após o pagamento ter sido aprovado.
- Produto indisponível / esgotado depois de confirmado o pedido.
- Produto defeituoso devolvido pelo cliente.
- Cobrança incorreta — estorno de valor cobrado a mais.
- Acordo comercial ou resolução amigável de uma reclamação.
Quando não usar:
| Situação | Operação correta |
|---|---|
| Transação ainda pré-autorizada (não capturada) | Não capture — veja Capture. Sem captura, não há dinheiro a devolver. |
| Cliente abriu contestação no banco (chargeback) | Trate pela Dispute; reembolsar por fora pode gerar devolução em dobro. |
| Quer apenas consultar o estado | Use Read ou List. |
Atenção
Se já existe uma disputa em andamento para a transação, o reembolso pode ser bloqueado com transactionDisputeInProcess. Resolva a disputa primeiro — reembolsar e perder o chargeback ao mesmo tempo significa pagar duas vezes.
Requisição
Headers
| Cabeçalho | Obrigatório | Descrição |
|---|---|---|
SelectKey | Sim | Sua chave de API. Em produção começa com sl_live_; em sandbox, sl_test_. Veja Autenticação. |
Content-Type: application/json | Sim (com corpo) | Indica que o corpo é JSON. |
X-Idempotency-Key | Recomendado | Garante que repetir a mesma chamada não gere dois reembolsos. Veja Idempotência. |
Dica
Reembolso é uma operação de escrita que mexe em dinheiro: sempre envie um X-Idempotency-Key. Se a rede cair depois de o servidor já ter processado, repetir com a mesma chave devolve o mesmo resultado em vez de estornar de novo.
Parâmetros de caminho
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
transactionId | string | Sim | ID da transação a reembolsar, com prefixo tra_ (ex.: tra_01hqzvabc). Trate como opaco — veja Convenções. |
Corpo da requisição
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
reason | string | Não | Motivo do reembolso, registrado para auditoria. Recomendado por boa prática, mesmo sendo opcional. |
O corpo é opcional: para um reembolso total, você pode enviar
{}ou nem incluir corpo. Incluareasonpara deixar rastro do porquê do estorno.
Exemplo curl
curl -X POST https://api.selectwin.io/v1/transactions/tra_01hqzvabc/refund \
-H "SelectKey: sl_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: refund-pedido-1042-001" \
-d '{
"reason": "Cliente desistiu da compra e solicitou reembolso"
}'Resposta
200 OK — o reembolso foi aceito e registrado. O corpo é a transação completa já atualizada para status: "refunded", acompanhada de um receipt (comprovante de reembolso) e do novo item em refunds[].
{
"receipt": {
"id": "rpt_01hqzvabc",
"type": "refund",
"amount": 5000,
"description": "Comprovante de reembolso",
"merchant": {
"id": "bus_1234567890",
"name": "Seller Name",
"statement": "SELLERNAME",
"document": "12345678000190",
"documentType": "cnpj",
"naturalness": "pt-BR"
},
"payment": {
"id": "tra_01hqzvabc",
"customId": "PEDIDO-1042",
"externalId": "order-9981",
"status": "refunded",
"amount": 5000,
"currency": "BRL",
"method": "credit",
"card": {
"brand": "visa",
"holder": "JOAO SILVA",
"first4Digits": "411111",
"last4Digits": "1111"
},
"installments": 3,
"transactionNumber": "A1B2C3D4E5",
"approvedAt": "2026-04-12T17:56:33.000Z",
"refundedAt": "2026-04-13T10:00:00.000Z",
"canceledAt": null,
"reversedAt": null,
"chargedbackAt": null
},
"customer": {
"id": "cus_01hqzvabc",
"firstName": "João",
"lastName": "Silva",
"email": "[email protected]",
"document": "12345678901",
"documentType": "cpf",
"telephone": "5511999999999"
},
"updatedAt": "2026-04-13T10:00:00.000Z",
"createdAt": "2026-04-13T10:00:00.000Z"
},
"id": "tra_01hqzvabc",
"customId": "PEDIDO-1042",
"amount": 5000,
"originalAmount": 5000,
"status": "refunded",
"method": "credit",
"currency": "BRL",
"payment": {
"provider": "selectwin",
"version": "1.1",
"acquirerTransactionNumber": "A1B2C3D4E5",
"cardFirstDigits": "411111",
"cardLastDigits": "1111",
"cardBrand": "visa",
"installments": 3,
"paidAt": "2026-04-12T17:56:33.000Z",
"invoiceLink": "https://selectwin.io/invoices/tra_01hqzvabc"
},
"discount": null,
"customer": {
"id": "cus_01hqzvabc",
"firstName": "João",
"lastName": "Silva",
"email": "[email protected]"
}
}Campos-chave da resposta
| Campo | Tipo | Descrição |
|---|---|---|
status | string | Passa a refunded quando a transação é estornada. |
receipt | object | Comprovante gerado para esta operação. |
receipt.type | string | Vale refund neste endpoint — identifica o comprovante como de reembolso. |
receipt.amount | integer | Valor reembolsado, em centavos (5000 = R$ 50,00). |
receipt.payment.status | string | Status do pagamento dentro do comprovante (refunded). |
receipt.payment.refundedAt | string (ISO 8601 UTC) | Momento em que o reembolso foi registrado. |
amount / originalAmount | integer | Valor atual e valor original da transação, em centavos. |
A transação completa traz muitos outros campos (
payment,customer,billing,receivables,splits,refunds,timeline,_links, …) — os mesmos descritos em Read. Acima mostramos o recorte essencial para o reembolso. Valores monetários sempre em centavos e datas em ISO 8601 UTC, conforme Convenções.
Erros
A API sinaliza falhas pelo envelope de erro padrão. Trate sempre pelo error.code, não pela mensagem — veja Tratamento de Erros e o Catálogo de Códigos de Erro.
error.code | HTTP | Quando ocorre |
|---|---|---|
transactionIdIsInvalid | 400 | O transactionId é malformado ou não corresponde a uma transação válida. |
invalidParameters | 400 | Falha de validação no corpo (ex.: reason excede o tamanho permitido). Veja error.params. |
unauthorized | 401 | SelectKey ausente, inválida ou revogada. |
insufficientFundsError | 402 | Saldo insuficiente para cobrir o reembolso no momento da operação. |
forbidden | 403 | Autenticado, mas sem permissão para reembolsar (ou limite atingido). |
transactionNotFound | 404 | Não existe transação com esse ID. |
transactionRefundNotProcessable | 409 | A transação não está em estado que permita reembolso (ex.: não está approved, já reembolsada). |
transactionDisputeInProcess | 409 | Há uma disputa em andamento — resolva-a antes de reembolsar. |
unprocessableEntity | 422 | A regra de negócio impede processar o reembolso. |
tooManyRequests | 429 | Limite de requisições excedido. Respeite error.retryAfterMinutes e use backoff. |
serverError | 500 | Erro interno. Repita com backoff; se persistir, contate o suporte. |
Exemplo de erro quando a transação não está em estado reembolsável:
{
"error": {
"code": "transactionRefundNotProcessable",
"statusCode": 409,
"category": "client",
"message": "Transaction refund not processable"
}
}Boas práticas
- Sempre envie
X-Idempotency-Key. Reembolso mexe em dinheiro; a chave evita estornos duplicados se a requisição for reenviada após timeout de rede. - Confirme o status antes de reembolsar. Faça um Read e verifique
status: "approved"para falhar cedo e com mensagem clara, em vez de tomar umtransactionRefundNotProcessable. - Preencha
reasonsempre. Embora opcional, o motivo vira trilha de auditoria e ajuda a conciliar estornos depois. - Reaja por webhook, não por polling. Use os eventos da transação para saber quando o estorno concluiu de fato; veja Proibição de polling.
- Verifique disputas antes. Se há
transactionDisputeInProcess, conduza a Dispute primeiro para não pagar em dobro. - Comunique o prazo ao cliente. Em cartão de crédito, o valor pode levar 1 a 2 faturas para aparecer; em PIX/boleto, pode ser necessário coletar dados bancários para a devolução.
- Considere o impacto no saldo. O valor reembolsado debita seu saldo na hora — para estornos de alto valor, garanta saldo disponível para evitar
insufficientFundsError.
Veja também
- Overview — o objeto Transação e seu ciclo de vida.
- Create — criar uma transação.
- Capture — capturar uma pré-autorização (antes de poder reembolsar).
- Read / List — consultar transações.
- Dispute — tratar contestações (chargebacks).
- Idempotência · Convenções · Tratamento de Erros
How is this guide?