Pular para o conteúdo

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.

Envie um POST /payments/{id}/reverse (requer o escopo payments:refund). O corpo é opcional:

CampoTipoDescrição
amountnúmeroValor exato a estornar, em reais. Mínimo 0.01, até 2 casas decimais.
percentagenúmeroPercentual do valor total do pagamento, de 0.01 a 100, até 2 casas decimais.
reasontextoMotivo 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.

Terminal window
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"
Terminal window
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" }'
Terminal window
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" }'
  • O percentual incide sobre o valor total do pagamento (amount), não sobre o saldo. Em um pagamento de R$ 159,90, "percentage": 25 estorna R$ 39,97.
  • O resultado é arredondado para baixo no centavo, para nunca devolver mais do que o pedido: 33.33 por cento de R$ 10,00 estorna R$ 3,33.
  • Em cartão com juros repassados ao comprador, o amount do pagamento já inclui os juros, então o percentual também incide sobre eles.
  • Sem amount e sem percentage, o valor estornado é o saldo: amount do pagamento menos o total já estornado.

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:

MomentoRequisiçãostatusrefundedAmountrefundableAmount
Pagamento confirmado-CONFIRMED0.00100.00
1º estorno{ "amount": 30.00 }CONFIRMED30.0070.00
2º estorno{ "percentage": 50 }CONFIRMED80.0020.00
3º estornosem corpoREVERSED100.000.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.

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.

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:

Terminal window
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": "..."
}
]
CampoSignificado
typePARTIAL quando ainda resta saldo depois do estorno; TOTAL quando o estorno zerou o saldo.
sourceOrigem do estorno: API, PANEL (painel do lojista), ADMIN (console da OrcaPay) ou PROVIDER (estorno feito direto na operadora e sincronizado pela OrcaPay).
percentagePercentual enviado na requisição, quando o estorno foi por percentual.
refundedAmountBefore / refundedAmountAfterTotal estornado antes e depois deste estorno.
feeAmountBefore / feeAmountAfterTaxa do pagamento antes e depois do recálculo.
netAmountBefore / netAmountAfterLíquido do pagamento antes e depois do recálculo.
createdByIdentificação de quem solicitou. Vem como system quando o estorno foi sincronizado automaticamente a partir da operadora.
SituaçãoHTTPcode
amount ou percentage fora do formato (zero, negativo, mais de 2 casas decimais, percentual acima de 100) ou reason com mais de 500 caracteres400VALIDATION_ERROR
amount e percentage enviados juntos400BAD_REQUEST
Valor maior que o saldo disponível para estorno. A mensagem informa o saldo e o total já estornado400BAD_REQUEST
Percentual que resulta em um estorno abaixo de R$ 0,01400BAD_REQUEST
Prazo de estorno da operadora expirado (veja a tabela abaixo)400BAD_REQUEST
Pagamento sem cobrança confirmada na operadora400BAD_REQUEST
A operadora recusou o estorno (por exemplo, valor acima do que ela ainda permite devolver)400PROVIDER_OPERATION_FAILED
Saldo insuficiente na conta do recebedor para devolver o valor400REFUND_INSUFFICIENT_BALANCE
Chave sem o escopo payments:refund, ou pagamento de outro estabelecimento403FORBIDDEN
Pagamento não encontrado404NOT_FOUND
Pagamento PENDING ou PROCESSING. Para desistir de uma cobrança ainda não paga, use POST /payments/{id}/cancel409CONFLICT
Pagamento já REVERSED, CANCELLED, FAILED ou em CHARGEBACK409CONFLICT
Mesma Idempotency-Key de uma requisição que ainda está em processamento409-

Quando a requisição é recusada, nada é estornado e o pagamento não muda.

Os prazos são contados a partir da autorização do pagamento:

Forma de pagamentoPrazo máximo para estorno
Pix90 dias
Boleto90 dias
Cartão de crédito350 dias
Cartão de débito350 dias (180 dias para Mastercard)

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.

  • Envie o header Idempotency-Key com 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 header Idempotent-Replayed: true e não estorna de novo, inclusive quando a primeira resposta foi um erro 4xx. 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, consulte GET /payments/{id}/refunds antes 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.
EventoDisparado quando
PAYMENT_PARTIALLY_REVERSEDUm estorno é concluído e o pagamento ainda tem saldo
PAYMENT_REVERSEDUm 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.

  • Envie sempre Idempotency-Key e guarde o refundId recebido no webhook ou em GET /payments/{id}/refunds.
  • Antes de estornar, use refundableAmount para saber quanto ainda pode ser devolvido.
  • Trate PAYMENT_PARTIALLY_REVERSED e PAYMENT_REVERSED: um pagamento pode passar por vários estornos parciais antes do total.
  • Prefira amount quando o valor a devolver já é conhecido (item devolvido, frete). Use percentage para descontos concedidos sobre a venda inteira.