Estornos
Um estorno devolve ao comprador todo ou parte do valor de um pagamento confirmado. A OrcaPay aceita três formas de estorno e permite vários estornos parciais no mesmo pagamento, até que o valor pago seja devolvido por completo:
- Total: devolve todo o saldo ainda não estornado.
- Parcial por valor: devolve um valor exato em reais.
- Parcial por percentual: devolve um percentual do valor total do pagamento.
Estornando um pagamento
Seção intitulada “Estornando um pagamento”Envie um POST /payments/{id}/reverse (requer o escopo payments:refund). O corpo é opcional:
| Campo | Tipo | Descrição |
|---|---|---|
amount | número | Valor exato a estornar, em reais. Mínimo 0.01, até 2 casas decimais. |
percentage | número | Percentual do valor total do pagamento, de 0.01 a 100, até 2 casas decimais. |
reason | texto | Motivo do estorno, até 500 caracteres. Fica registrado no histórico do pagamento. |
Informe amount ou percentage, nunca os dois. Sem nenhum dos dois, ou sem corpo, a OrcaPay estorna todo o saldo.
Estorno total
Seção intitulada “Estorno total”curl -X POST https://api.orcapay.com.br/payments/{id}/reverse \ -H "X-Api-Key: op_live_..." \ -H "Idempotency-Key: 7c1f0a52-3d5e-4f0e-9a51-2f6f4d1b8e10"Estorno parcial por valor
Seção intitulada “Estorno parcial por valor”curl -X POST https://api.orcapay.com.br/payments/{id}/reverse \ -H "X-Api-Key: op_live_..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 0b8e7c1d-6f2a-4c55-9e0b-3f1d2a7c9b14" \ -d '{ "amount": 30.00, "reason": "Item devolvido pelo cliente" }'Estorno parcial por percentual
Seção intitulada “Estorno parcial por percentual”curl -X POST https://api.orcapay.com.br/payments/{id}/reverse \ -H "X-Api-Key: op_live_..." \ -H "Content-Type: application/json" \ -H "Idempotency-Key: 4e9d2b71-8a3c-4f6e-b0d5-6c1a9f2e7b38" \ -d '{ "percentage": 25, "reason": "Desconto concedido após a compra" }'Como o valor é calculado
Seção intitulada “Como o valor é calculado”- O percentual incide sobre o valor total do pagamento (
amount), não sobre o saldo. Em um pagamento de R$ 159,90,"percentage": 25estorna R$ 39,97. - O resultado é arredondado para baixo no centavo, para nunca devolver mais do que o pedido:
33.33por cento de R$ 10,00 estorna R$ 3,33. - Em cartão com juros repassados ao comprador, o
amountdo pagamento já inclui os juros, então o percentual também incide sobre eles. - Sem
amounte sempercentage, o valor estornado é o saldo:amountdo pagamento menos o total já estornado.
Estornos parciais sucessivos
Seção intitulada “Estornos parciais sucessivos”Você pode fazer quantos estornos parciais precisar, desde que a soma não ultrapasse o valor do pagamento. Exemplo com um pagamento de R$ 100,00:
| Momento | Requisição | status | refundedAmount | refundableAmount |
|---|---|---|---|---|
| Pagamento confirmado | - | CONFIRMED | 0.00 | 100.00 |
| 1º estorno | { "amount": 30.00 } | CONFIRMED | 30.00 | 70.00 |
| 2º estorno | { "percentage": 50 } | CONFIRMED | 80.00 | 20.00 |
| 3º estorno | sem corpo | REVERSED | 100.00 | 0.00 |
O pagamento continua CONFIRMED enquanto houver saldo e só passa para REVERSED quando o valor total é devolvido. Use refundedAmount para saber se um pagamento confirmado já teve estorno parcial e refundableAmount para saber quanto ainda pode ser estornado.
Repare no 2º estorno: 50 por cento de R$ 100,00 são R$ 50,00, que cabem no saldo de R$ 70,00. Um "percentage": 100 nessa situação seria recusado, porque R$ 100,00 excede o saldo. Para devolver o que sobrou, envie a requisição sem corpo.
Resposta
Seção intitulada “Resposta”A resposta 200 traz o pagamento atualizado (trecho):
{ "id": "9f8a7b6c-5d4e-4a3b-2c1d-0e9f8a7b6c5d", "status": "CONFIRMED", "amount": 100.00, "refundedAmount": 30.00, "refundableAmount": 70.00, "feeAmount": 2.79, "netAmount": 67.21, "reversedAt": null}Os valores financeiros do pagamento são recalculados na proporção do valor que ficou retido. No exemplo, um pagamento de R$ 100,00 com taxa de R$ 3,99 e líquido de R$ 96,01 passa a ter taxa de R$ 2,79 e líquido de R$ 67,21 depois de estornar R$ 30,00. Nas parcelas de cartão (paymentInstallments), valor, taxa e líquido são redistribuídos da mesma forma. Quando o saldo zera, taxa e líquido também ficam zerados. O amount do pagamento nunca muda: ele continua sendo o valor original da venda.
Consultando os estornos
Seção intitulada “Consultando os estornos”GET /payments/{id}/refunds (requer payments:read) lista os estornos do pagamento em ordem cronológica, com a taxa e o líquido antes e depois de cada um:
curl https://api.orcapay.com.br/payments/{id}/refunds \ -H "X-Api-Key: op_live_..."[ { "id": "5b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e", "paymentId": "9f8a7b6c-5d4e-4a3b-2c1d-0e9f8a7b6c5d", "type": "PARTIAL", "typeDescription": "Parcial", "source": "API", "sourceDescription": "API", "amount": 30.00, "percentage": null, "reason": "Item devolvido pelo cliente", "refundedAmountBefore": 0.00, "refundedAmountAfter": 30.00, "feeAmountBefore": 3.99, "feeAmountAfter": 2.79, "netAmountBefore": 96.01, "netAmountAfter": 67.21, "createdAt": "2026-09-12T15:04:11Z", "createdBy": "..." }]| Campo | Significado |
|---|---|
type | PARTIAL quando ainda resta saldo depois do estorno; TOTAL quando o estorno zerou o saldo. |
source | Origem do estorno: API, PANEL (painel do lojista), ADMIN (console da OrcaPay) ou PROVIDER (estorno feito direto na operadora e sincronizado pela OrcaPay). |
percentage | Percentual enviado na requisição, quando o estorno foi por percentual. |
refundedAmountBefore / refundedAmountAfter | Total estornado antes e depois deste estorno. |
feeAmountBefore / feeAmountAfter | Taxa do pagamento antes e depois do recálculo. |
netAmountBefore / netAmountAfter | Líquido do pagamento antes e depois do recálculo. |
createdBy | Identificação de quem solicitou. Vem como system quando o estorno foi sincronizado automaticamente a partir da operadora. |
Validações
Seção intitulada “Validações”| Situação | HTTP | code |
|---|---|---|
amount ou percentage fora do formato (zero, negativo, mais de 2 casas decimais, percentual acima de 100) ou reason com mais de 500 caracteres | 400 | VALIDATION_ERROR |
amount e percentage enviados juntos | 400 | BAD_REQUEST |
| Valor maior que o saldo disponível para estorno. A mensagem informa o saldo e o total já estornado | 400 | BAD_REQUEST |
| Percentual que resulta em um estorno abaixo de R$ 0,01 | 400 | BAD_REQUEST |
| Prazo de estorno da operadora expirado (veja a tabela abaixo) | 400 | BAD_REQUEST |
| Pagamento sem cobrança confirmada na operadora | 400 | BAD_REQUEST |
| A operadora recusou o estorno (por exemplo, valor acima do que ela ainda permite devolver) | 400 | PROVIDER_OPERATION_FAILED |
| Saldo insuficiente na conta do recebedor para devolver o valor | 400 | REFUND_INSUFFICIENT_BALANCE |
Chave sem o escopo payments:refund, ou pagamento de outro estabelecimento | 403 | FORBIDDEN |
| Pagamento não encontrado | 404 | NOT_FOUND |
Pagamento PENDING ou PROCESSING. Para desistir de uma cobrança ainda não paga, use POST /payments/{id}/cancel | 409 | CONFLICT |
Pagamento já REVERSED, CANCELLED, FAILED ou em CHARGEBACK | 409 | CONFLICT |
Mesma Idempotency-Key de uma requisição que ainda está em processamento | 409 | - |
Quando a requisição é recusada, nada é estornado e o pagamento não muda.
Prazos da operadora
Seção intitulada “Prazos da operadora”Os prazos são contados a partir da autorização do pagamento:
| Forma de pagamento | Prazo máximo para estorno |
|---|---|
| Pix | 90 dias |
| Boleto | 90 dias |
| Cartão de crédito | 350 dias |
| Cartão de débito | 350 dias (180 dias para Mastercard) |
Pagamentos com divisão (split)
Seção intitulada “Pagamentos com divisão (split)”O estorno é solicitado à operadora pelo recebedor primário da transação, a conta dona da credencial usada na cobrança. A distribuição do valor estornado entre os recebedores e a verificação de saldo ficam com a operadora. Se a conta não tiver saldo suficiente, a requisição retorna 400 com o código REFUND_INSUFFICIENT_BALANCE e nada é estornado: garanta o saldo e tente de novo.
Retentativas e requisições simultâneas
Seção intitulada “Retentativas e requisições simultâneas”- Envie o header
Idempotency-Keycom um valor único por estorno (um UUID, por exemplo). Se a requisição for repetida com a mesma chave, a OrcaPay devolve a resposta original com o headerIdempotent-Replayed: truee não estorna de novo, inclusive quando a primeira resposta foi um erro4xx. A chave vale por pelo menos 24 horas. - Sem
Idempotency-Key, cada requisição é um estorno novo. Repetir um estorno parcial depois de um timeout pode devolver o valor duas vezes. Na dúvida, consulteGET /payments/{id}/refundsantes de tentar de novo. - Estornos simultâneos no mesmo pagamento são processados um de cada vez: o segundo já enxerga o saldo atualizado pelo primeiro e é recusado se o valor não couber mais.
Webhooks
Seção intitulada “Webhooks”| Evento | Disparado quando |
|---|---|
PAYMENT_PARTIALLY_REVERSED | Um estorno é concluído e o pagamento ainda tem saldo |
PAYMENT_REVERSED | Um estorno zera o saldo do pagamento (estorno total ou último estorno parcial) |
Além dos campos do pagamento, o data dos dois eventos traz os dados do estorno que gerou a notificação (trecho):
{ "eventType": "PAYMENT_PARTIALLY_REVERSED", "data": { "paymentId": "9f8a7b6c-5d4e-4a3b-2c1d-0e9f8a7b6c5d", "externalId": "pedido-1042", "status": "CONFIRMED", "amount": 100.00, "netAmount": 67.21, "refundedAmount": 30.00, "refundableAmount": 70.00, "refundId": "5b1c2d3e-4f5a-4b6c-8d7e-9f0a1b2c3d4e", "refundType": "PARTIAL", "refundSource": "API", "refundAmount": 30.00, "refundPercentage": null, "refundReason": "Item devolvido pelo cliente" }}Estornos feitos direto na operadora, fora da OrcaPay, são identificados pela notificação da própria operadora, registrados com source PROVIDER e disparam os mesmos eventos.
Endpoints de webhook cadastrados antes desta funcionalidade não recebem PAYMENT_PARTIALLY_REVERSED automaticamente: edite o endpoint no console e marque o evento.
Boas práticas
Seção intitulada “Boas práticas”- Envie sempre
Idempotency-Keye guarde orefundIdrecebido no webhook ou emGET /payments/{id}/refunds. - Antes de estornar, use
refundableAmountpara saber quanto ainda pode ser devolvido. - Trate
PAYMENT_PARTIALLY_REVERSEDePAYMENT_REVERSED: um pagamento pode passar por vários estornos parciais antes do total. - Prefira
amountquando o valor a devolver já é conhecido (item devolvido, frete). Usepercentagepara descontos concedidos sobre a venda inteira.