Pular para o conteúdo principal

Guia de Devolução (PIX Refund)

Este guia explica como solicitar a devolução de um pagamento PIX recebido.

Visão Geral

A devolução PIX permite estornar um pagamento recebido, no todo ou em parte. A transação original é sempre identificada pelo E2E (End-to-End ID) — não existe devolução por identifier da cobrança. Se você só tem o identifier, consulte o QR ou o pagamento primeiro para obter o E2E (ver Onde Encontrar o E2E).

Quando Utilizar

  • Pagamento duplicado - Cliente pagou duas vezes
  • Cancelamento de pedido - Pedido cancelado após o pagamento
  • Valor incorreto - Cliente pagou um valor diferente do esperado
  • Erro operacional - Pagamento recebido incorretamente

Prazos

O prazo para devolução PIX é definido pelo BACEN e aplicado pelo liquidante: 90 dias contados do pagamento original, tanto para devolução comum quanto para os casos de fraude.

aviso

Passado esse prazo, o liquidante recusa a devolução — a API repassa a recusa como partner_rejected (422), com o motivo informado no bloco partner. Não há um código de erro específico para "prazo expirado". Nesse cenário, use outros meios de estorno.

Solicitar Devolução por E2E (End-to-End ID)

O E2E é o identificador único da transação no sistema PIX brasileiro. Formato: E{ISPB}{DATE}{SEQUENTIAL}.

Request

curl -X POST "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/out/refund" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: tenant-suaempresa" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: refund-e2e-12345" \
-d '{
"originalEndToEnd": "E36741675202601281435001234567",
"amount": 150.00,
"reason": "user-requested",
"identifier": "refund-order-12345"
}'

Parâmetros do Body

A conta que recebeu o PIX original vem do path ({accountId}) e a moeda é sempre BRL — nenhum dos dois é lido do body.

CampoTipoObrigatórioDescrição
originalEndToEndstringSimID E2E da transação original a ser devolvida
amountnumberSimValor a devolver em BRL — igual ao valor do PIX original (estorno total) ou menor (estorno parcial). Não pode excedê-lo
reasonstringSimMotivo da devolução em kebab-case (ver tabela abaixo)
identifierstringNãoIdentificador da devolução (máx. 38 chars [A-Za-z0-9._-]). Gerado automaticamente se omitido
descriptionstringNãoDescrição livre (máx. 140 caracteres)

Valores de reason

A lista é fechada: qualquer valor fora desta tabela é rejeitado com HTTP 400. Os slugs são repassados literalmente ao parceiro bancário (MT Bank), sem tradução.

SlugQuando usar
user-requestedCliente final solicitou a devolução (caso mais comum)
transaction-errorErro genérico na transação (valor incorreto, dados inconsistentes)
unauthorized-transactionTransação não autorizada pelo titular
fraudFraude comprovada/em investigação
trade-disagreementDesacordo comercial (bem/serviço não entregue)
withdrawal-purchaseOperação envolvendo PIX Saque/Troco
contractual-divergenceDivergência contratual entre as partes
operational-errorErro operacional/processamento bancário
duplicate-paymentPagamento duplicado

Resposta de Sucesso

A devolução usa o mesmo pipeline (e shape) do PIX out. O status HTTP reflete o desfecho: 200 (COMPLETED), 422 (FAILED), 202 (TIMEOUT/PENDING).

{
"paymentId": "pay_2f4a0f88-2147-49f2-a4e2-4f7b9f6c0f7a",
"transactionId": "txn-refund-abc123",
"endToEndId": "D12345678202301011500009876543",
"status": "COMPLETED",
"completedAt": "2026-01-28T15:00:03Z",
"identifier": "refund-order-12345"
}
CampoDescrição
paymentIdID interno da intenção (rastreio/conciliação)
endToEndIdD-code/E2E da devolução (após registrada)
statusCOMPLETED, FAILED, TIMEOUT, PENDING, PROCESSING
errorCode / errorReasonpreenchidos quando FAILED

Não há campo refundId/amount/currency na resposta — use paymentId e acompanhe o desfecho final pelo extrato ou webhook.

Estorno parcial

O campo amount é obrigatório e define quanto volta ao pagador: envie o valor integral do PIX original para estornar tudo, ou um valor menor para estornar em parte. Acima do original, a API responde 400 refund_amount_exceeded e não inicia a devolução.

Um mesmo PIX pode ser devolvido em mais de uma parcela, até somar o valor original. Duas regras importam aqui:

  • Cada devolução precisa de Idempotency-Key e identifier próprios. Repetir um dos dois faz a API tratar a chamada como retentativa da devolução anterior e devolver o resultado dela — a segunda parcela nunca sai.
  • O controle do quanto ainda resta devolvível é do banco liquidante. A CorpX não soma parciais: uma devolução que estoure o saldo devolvível é recusada pelo liquidante e volta como refund_amount_exceeded.

Para saber quanto de um PIX já foi devolvido, consulte o extrato da conta: as linhas de devolução referenciam o E2E da transação original.

Webhook de Devolução

Quando a devolução for processada, você receberá um webhook:

{
"id": "pix-refund-pay_2f4a0f88-2147-49f2-a4e2-4f7b9f6c0f7a",
"type": "pix.refund.completed",
"occurredAt": "2026-01-28T15:00:03.000000000Z",
"schemaVersion": "1.0",
"environment": "production",
"tenantId": "tenant-suaempresa",
"accountId": "{accountId}",
"data": {
"paymentId": "pay_2f4a0f88-2147-49f2-a4e2-4f7b9f6c0f7a",
"tenantId": "tenant-suaempresa",
"accountId": "{accountId}",
"status": "SUCCESS",
"endToEnd": "D36741675202601281500009876543",
"transactionId": "txn-refund-abc123",
"amount": 150.00,
"currency": "BRL",
"identifier": "refund-order-12345",
"description": "",
"originalTransactionId": "E36741675202601281435001234567",
"initiatedAt": "2026-01-28T15:00:01.000000000Z",
"completedAt": "2026-01-28T15:00:03.000000000Z",
"payee": {
"name": "John Smith",
"document": "12345678901"
}
}
}

Uma devolução recusada emite pix.refund.failed com status: "FAILED" e os campos errorCode/errorReason/error no data. Devolução em TIMEOUT não emite webhook — consulte o extrato.

Exemplo Completo: Script de Devolução

#!/bin/bash

# Configuration
API_URL="https://tenant.api.corpx.com"
TOKEN="your_token_here"
TENANT_ID="tenant-suaempresa"
ACCOUNT_ID="your_account"

# Refund by E2E function
refund_by_e2e() {
local e2e=$1
local amount=$2
local reason=$3

echo "Requesting refund..."
echo "E2E: $e2e"
echo "Amount: R$ $amount"
echo "Reason: $reason"
echo ""

IDEMPOTENCY_KEY="refund-$(date +%s)-$RANDOM"

response=$(curl -s -X POST "$API_URL/v1/accounts/$ACCOUNT_ID/pix/out/refund" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-Id: $TENANT_ID" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $IDEMPOTENCY_KEY" \
-d "{
\"originalEndToEnd\": \"$e2e\",
\"amount\": $amount,
\"reason\": \"$reason\"
}")

echo "$response" | jq
}

# Usage
echo "=== PIX REFUND ==="
echo ""
read -p "E2E (End-to-End ID): " e2e
read -p "Amount to refund: " amount
read -p "Reason: " reason

refund_by_e2e "$e2e" "$amount" "$reason"

Onde Encontrar o E2E

O E2E pode ser encontrado em:

  1. Webhook de pagamento recebido
  2. Consulta da cobrança após o pagamento
  3. Extrato da conta (Statement)
  4. Comprovante do pagador

Exemplo: Extrair E2E do Webhook

{
"id": "evt_in_123",
"type": "pix.in.completed",
"occurredAt": "2026-01-28T14:35:00.000Z",
"schemaVersion": "1.0",
"tenantId": "tenant-suaempresa",
"accountId": "{accountId}",
"data": {
"identifier": "cob_abc123def456",
"endToEnd": "E36741675202601281435001234567",
"amount": 150.00
}
}

O campo a guardar é data.endToEnd.

Exemplo: Extrair E2E da Cobrança (QR Code)

curl -X GET "https://tenant.api.corpx.com/v1/accounts/{accountId}/pix/qr-code/lookup?identifier=order-12345" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: tenant-suaempresa"
{
"identifier": "order-12345",
"status": "PAID",
"endToEndId": "E36741675202601281435001234567"
}

A resposta do lookup de QR é um objeto plano (sem envelope data); o campo a guardar é endToEndId.

Erros Comuns

ErroHTTPCausaSolução
missing_fields400originalEndToEnd ou amount ausenteEnvie os dois campos
invalid_payload400reason fora da lista fechada, ou JSON malformadoUse um dos slugs da tabela acima
refund_amount_exceeded400amount maior que o PIX original, ou maior que o saldo ainda devolvívelConfira no extrato quanto já foi devolvido
original_transaction_not_found404E2E não encontrado no liquidanteVerifique o originalEndToEnd
conflict409Transação já devolvida integralmenteVerifique o histórico no extrato
insufficient_funds422Saldo insuficiente para a devoluçãoDeposite fundos na conta
policy_denied422Uma regra de política do tenant/conta recusou a devoluçãoVeja violations no corpo — Políticas e Regras
partner_rejected422Recusa do liquidante (inclui prazo de 90 dias vencido)Veja o bloco partner com o motivo
partner_error502Falha ao consultar a transação original no liquidanteTente novamente; se persistir, acione o suporte

A lista completa está em Erros.

Boas Práticas

  1. Salve o E2E de todas as transações recebidas
  2. Use Idempotency Key para evitar devoluções duplicadas — e uma chave (e identifier) diferente a cada parcial do mesmo PIX
  3. Confira no extrato quanto de um PIX já foi devolvido antes de pedir uma nova parcial
  4. Documente o motivo para fins de auditoria
  5. Configure webhooks para acompanhar o status
  6. Mantenha um histórico de devoluções para conciliação

Próximos Passos