Devoluções

Referência

Devolver Pix e estornar cartão são processos diferentes. A devolução Pix depende do Bacen e costuma ficar pendente até a confirmação assíncrona; o estorno de cartão depende da adquirente e pode confirmar na hora. Este guia explica os dois fluxos, os campos que os diferenciam e o que muda na cobrança original.

Um único endpoint atende os dois fluxos: POST /v1/wallet/refunds e GET /v1/wallet/refunds. Não existe uma rota separada para estorno de cartão. O que você envia no body decide qual caminho a API executa.

Um endpoint, dois fluxos

A API decide o fluxo pelo campo identificador que você envia, não por um parâmetro de tipo:

Você enviaFluxo executado
endToEndIdDevolução Pix
chargeId (de uma cobrança de cartão)Estorno de cartão
chargeId (de uma cobrança Pix)Devolução Pix

Devolução Pix

A devolução Pix parte do endToEndId da transação original e é enviada ao Banco Central via Celcoin. Ela nasce quase sempre como PROCESSING. A confirmação chega depois, de forma assíncrona, pelo webhook.

CampoObrigatórioDescrição
endToEndIdSimEndToEndId da transação Pix original recebida.
amountSimValor a devolver, parcial ou total, em reais (ex.: 10.00).
reasonSimUm de: BANK_ERROR, FRAUD, CUSTOMER_REQUEST, PIX_CHANGE_ERROR.
chargeIdNãoID da cobrança associada, se você já tiver. Acelera a busca.
accountIdNãoNúmero da subconta. Se omitido, opera na conta principal.
{
  "endToEndId": "E003603052026032511186a4f4cdf139",
  "amount": 1.00,
  "reason": "CUSTOMER_REQUEST"
}
{
  "refundId": "ref_1774437918293_jm2hen5y7",
  "status": "PROCESSING",
  "amount": 1,
  "reason": "CUSTOMER_REQUEST",
  "endToEndId": "E003603052026032511186a4f4cdf139",
  "returnIdentification": "D13935893202603251125zbRSLI3lqPH",
  "chargeId": "cha_1774437468463_4hj927ips",
  "createdAt": "2026-03-25T11:25:18.293Z"
}

Se a subconta de origem não tiver saldo suficiente, a ValidaPay adianta o valor da conta principal para a subconta antes de enviar a devolução ao Bacen, e registra a dívida internamente. Na conta principal, saldo insuficiente sem subconta de origem retorna INSUFFICIENT_BALANCE.

Estorno de cartão

O estorno de cartão parte do chargeId de uma cobrança CREDIT_CARD processada por um provedor com estorno suportado (Malga ou Marlim) e cujo status ainda permita estorno (paga, autorizada ou já parcialmente estornada). O provedor pode confirmar na hora, retornando CONFIRMED com success: true, ou deixar PROCESSING para confirmar depois.

CampoObrigatórioDescrição
chargeIdSimID da cobrança de cartão a estornar.
amountSimValor a estornar, parcial ou total, em reais (ex.: 100.00).
reasonSimUm de: BANK_ERROR, FRAUD, CUSTOMER_REQUEST, PIX_CHANGE_ERROR. Na prática é obrigatório: sem ele, a API recusa com INVALID_REASON.
accountIdNãoNúmero da subconta. Se omitido, opera na conta principal.
{
  "chargeId": "cha_1774530966959_frgj3ptax",
  "amount": 100.00,
  "reason": "CUSTOMER_REQUEST"
}
{
  "refundId": "ref_1774531490865_bq4e8v12x",
  "status": "CONFIRMED",
  "success": true,
  "amount": 100,
  "reason": "CUSTOMER_REQUEST",
  "chargeId": "cha_1774530966959_frgj3ptax",
  "providerChargeId": "prov_ch_9f8a7b6c",
  "paymentType": "CREDIT_CARD",
  "createdAt": "2026-03-26T13:24:52.106Z"
}

Campos da resposta

A criação (201) e a consulta (200) devolvem essencialmente o mesmo formato. A consulta apenas acrescenta accountId, splitReversals e updatedAt.

CampoDescrição
refundIdIdentificador único da devolução/estorno. Use para consultar o status depois.
statusPROCESSING, CONFIRMED ou ERROR.
successtrue quando status é CONFIRMED (atalho para não comparar string).
amountValor devolvido/estornado nesta operação.
reasonMotivo enviado na criação.
chargeIdCobrança original associada, quando identificada.
originalEndToEndIdSó na devolução Pix: EndToEndId da transação original.
returnIdentificationSó na devolução Pix: EndToEndId da devolução, gerado pelo banco.
providerChargeIdSó no estorno de cartão: ID da cobrança no provedor (Malga/Marlim).
paymentTypeCREDIT_CARD no estorno de cartão; null na devolução Pix.
splitReversalsRecolhimento proporcional de cada destinatário do split, quando a cobrança original teve split.
errorCódigo de erro do provedor/banco quando status é ERROR (ex.: MD06).

Precedência da consulta por GET: refundId > returnIdentification > endToEndId > chargeId. Sem nenhum identificador, retorna o histórico paginado da conta (com filtros de status e data).

Status da devolução

StatusSignificado
PROCESSINGDevolução/estorno enviado, aguardando confirmação do banco ou do provedor de cartão.
CONFIRMEDValor devolvido/estornado com sucesso. No cartão, pode chegar direto nesse status se o provedor confirmar na hora.
ERRORO banco/provedor recusou a devolução. O campo error traz o código retornado (ex.: MD06).

Valor parcial e total

  • Os dois fluxos aceitam devolução parcial: envie em amount só o quanto quer devolver.
  • Cada devolução soma em refundedAmount na cobrança original; quando o acumulado atinge o valor da cobrança, o status dela vira REFUNDED. Antes disso fica PARTIALLY_REFUNDED.
  • Pedir mais do que ainda resta devolver retorna REFUND_AMOUNT_EXCEEDED.
  • Não há prazo próprio da API para solicitar devolução ou estorno. A única checagem de tempo é a que o emissor do cartão ou o arranjo Pix aplicam do lado deles.

Split e devolução

Quando a cobrança original teve split, a confirmação da devolução Pix recolhe, proporcionalmente, a mesma fração do valor devolvido de cada destinatário do split, na mesma razão entre o valor devolvido e o valor total da cobrança. Se um destinatário não tiver saldo suficiente no momento, o recolhimento dele vira uma dívida (status: "PENDING" com debtId) em vez de ser debitado na hora.

Esse recolhimento aparece no array splitReversals ao consultar a devolução:

{
  "refundId": "ref_1774531490865_bq4e8v12x",
  "accountId": "429131212",
  "status": "CONFIRMED",
  "success": true,
  "amount": 100.00,
  "reason": "CUSTOMER_REQUEST",
  "chargeId": "cha_1774530966959_frgj3ptax",
  "originalEndToEndId": "E0036030520260326132336e2f8787c4",
  "returnIdentification": "D13935893202603261324C5jA3zE3V6J",
  "providerChargeId": null,
  "paymentType": null,
  "splitReversals": [
    { "accountNumber": "459013777", "amount": 30.00, "status": "REVERSED" },
    { "accountNumber": "470084294", "amount": 20.00, "status": "PENDING", "debtId": "debt_1774539002481_ab12cd34e" }
  ],
  "error": null,
  "createdAt": "2026-03-26T13:24:52.106Z",
  "updatedAt": "2026-03-26T13:24:57.553Z"
}

No estorno de cartão não há esse recolhimento automático do split. O valor sai só da cobrança original.

Erros comuns

codeStatusQuando acontece
MISSING_REFUND_IDENTIFIER400Nem endToEndId nem um chargeId de cartão elegível foram enviados.
MISSING_END_TO_END_ID400Faltou endToEndId ao tentar devolução Pix.
MISSING_CHARGE_ID400Faltou chargeId ao tentar estorno de cartão.
REFUND_NOT_SUPPORTED_FOR_CHARGE400chargeId aponta para uma cobrança que não é de cartão suportado nem tem endToEndId informado.
INVALID_CHARGE_TYPE400A cobrança não é CREDIT_CARD de um provedor com estorno suportado (Malga/Marlim).
MISSING_PROVIDER_CHARGE_ID400Cobrança de cartão sem transactionId do provedor: não há o que estornar.
CHARGE_NOT_REFUNDABLE400Status atual da cobrança não permite estorno (ex.: ainda não paga).
CHARGE_NOT_FOUND404chargeId informado não existe.
INVALID_AMOUNT400amount ausente, zero ou negativo.
REFUND_AMOUNT_EXCEEDED400Valor solicitado passa do que ainda pode ser devolvido nessa cobrança.
INVALID_REASON400reason ausente ou fora de BANK_ERROR, FRAUD, CUSTOMER_REQUEST, PIX_CHANGE_ERROR.
INSUFFICIENT_BALANCE400Saldo insuficiente na conta principal para devolver (sem subconta de origem para adiantar).
SUB_ACCOUNT_NOT_FOUND404accountId informado não corresponde a nenhuma subconta.
OWNERSHIP_MISMATCH401A subconta (accountId) ou a cobrança não pertence à conta autenticada.
REFUND_NOT_FOUND404refundId ou returnIdentification informado não existe ou não é legível pela conta autenticada.

Perguntas frequentes

Como a API sabe se é devolução Pix ou estorno de cartão, se a rota é a mesma?
Pelo que você envia no body. Se enviar chargeId de uma cobrança CREDIT_CARD com provedor Malga ou Marlim, é estorno de cartão. Caso contrário, é preciso enviar endToEndId e a API trata como devolução Pix, mesmo que você também envie um chargeId (nesse caso ele só ajuda a localizar a cobrança).
O campo reason é opcional no estorno de cartão?
Não, embora apareça como opcional em alguns exemplos. Nos dois fluxos a API exige um valor de BANK_ERROR, FRAUD, CUSTOMER_REQUEST ou PIX_CHANGE_ERROR, e recusa com INVALID_REASON quando o campo não vem preenchido.
Existe prazo para pedir uma devolução ou estorno?
A API não aplica nenhuma janela de tempo própria: a única checagem de valor é contra o quanto já foi devolvido da cobrança (REFUND_AMOUNT_EXCEEDED). Prazos de contestação do emissor do cartão ou do arranjo Pix, se existirem, são aplicados pelo banco/adquirente, não pela ValidaPay.
Posso devolver só uma parte do valor?
Sim, nos dois fluxos. Cada devolução/estorno soma em refundedAmount na cobrança original; quando o total acumulado atinge o valor da cobrança, o status dela vira REFUNDED. Antes disso, fica PARTIALLY_REFUNDED.
O que acontece com o split quando a cobrança original tinha divisão automática?
Hoje isso só está implementado para devolução Pix: ao confirmar a devolução, cada destinatário do split perde, proporcionalmente, a mesma fração do que recebeu. Se algum destinatário não tiver saldo suficiente no momento, o recolhimento dele vira uma dívida (status PENDING com debtId) em vez de ser recolhida na hora. No estorno de cartão não há esse recolhimento automático do split.
O status 201 da criação já significa que o dinheiro voltou?
Não necessariamente. Na devolução Pix, o retorno quase sempre vem PROCESSING: a confirmação chega depois, via webhook refund.confirmed ou refund.failed. No estorno de cartão, o provedor pode confirmar na hora (CONFIRMED com success: true já no 201) ou deixar PROCESSING para confirmar depois.
Essa página foi útil?