Skip to main content

Visão Geral

O estorno PIX permite devolver valores pagos via PIX de forma total ou parcial. Este guia explica como implementar estornos PIX em sua aplicação usando a API EconPay.
Importante: Estornos PIX são processados através da Firebank e seguem as regras estabelecidas pelo Banco Central do Brasil.

Quando Usar Estorno PIX

O estorno PIX é ideal para:
  • Cancelamento de Compras: Cliente desiste da compra após pagamento
  • Devolução de Produtos: Produto com defeito ou não entregue
  • Ajuste de Valores: Cobrança incorreta ou desconto posterior
  • Reembolso Parcial: Devolução de apenas parte do valor pago

Requisitos

Para processar um estorno PIX, a transação deve atender aos seguintes requisitos:
A transação deve estar com status PAID (paga). Transações pendentes, canceladas ou já estornadas não podem ser estornadas.
// Verificar status antes de estornar
const transaction = await getTransaction(transactionId);

if (transaction.status !== 'PAID') {
  console.error('Transação não pode ser estornada');
  return;
}
O estorno deve ser solicitado em até 90 dias após o pagamento original.
// Verificar prazo
const paymentDate = new Date(transaction.created_at);
const today = new Date();
const daysDiff = Math.floor((today - paymentDate) / (1000 * 60 * 60 * 24));

if (daysDiff > 90) {
  console.error('Prazo de estorno expirado');
  return;
}
Sua conta deve ter saldo suficiente para processar o estorno.O valor será debitado da sua conta e creditado na conta do pagador.
O valor do estorno (ou soma de estornos parciais) não pode exceder o valor original da transação.
// Validar valor do estorno
if (refundAmount > transaction.amount) {
  console.error('Valor do estorno excede valor original');
  return;
}

Tipos de Estorno

Estorno Total

Devolve 100% do valor da transação PIX.
// Estorno total - não enviar campo amount
const refund = await fetch('https://api.econpay.com.br/payments/pix/refund', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    transaction_id: 123,
    description: 'Cancelamento da compra'
  })
});

const result = await refund.json();
console.log('Estorno total processado:', result);

Estorno Parcial

Devolve apenas parte do valor da transação PIX.
// Estorno parcial - enviar campo amount
const refund = await fetch('https://api.econpay.com.br/payments/pix/refund', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    transaction_id: 124,
    amount: 50.00, // R$ 50,00
    description: 'Devolução parcial - 1 item'
  })
});

const result = await refund.json();
console.log('Estorno parcial processado:', result);
Múltiplos Estornos Parciais: Você pode fazer vários estornos parciais, mas a soma de todos não pode exceder o valor original da transação.

Fluxo Completo

1

Buscar Transação

Primeiro, busque a transação para verificar se ela pode ser estornada.
const transaction = await fetch(
  `https://api.econpay.com.br/transactions/${transactionId}`,
  {
    headers: {
      'Authorization': `Bearer ${token}`
    }
  }
);

const data = await transaction.json();
2

Validar Elegibilidade

Verifique se a transação atende aos requisitos para estorno.
function canRefund(transaction) {
  // Verificar status
  if (transaction.status !== 'PAID') {
    return { can: false, reason: 'Status inválido' };
  }
  
  // Verificar tipo
  if (transaction.type !== 'pix') {
    return { can: false, reason: 'Não é transação PIX' };
  }
  
  // Verificar prazo
  const paymentDate = new Date(transaction.created_at);
  const today = new Date();
  const daysDiff = Math.floor((today - paymentDate) / (1000 * 60 * 60 * 24));
  
  if (daysDiff > 90) {
    return { can: false, reason: 'Prazo expirado' };
  }
  
  return { can: true };
}

const eligibility = canRefund(data.transaction);

if (!eligibility.can) {
  console.error('Não pode estornar:', eligibility.reason);
  return;
}
3

Processar Estorno

Envie a requisição de estorno para a API.
const refund = await fetch('https://api.econpay.com.br/payments/pix/refund', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    transaction_id: transactionId,
    amount: refundAmount, // Opcional
    description: 'Motivo do estorno'
  })
});

const result = await refund.json();
4

Tratar Resposta

Verifique o resultado e atualize sua aplicação.
if (result.success) {
  console.log('Estorno processado com sucesso!');
  console.log('Transaction ID:', result.transaction.id);
  console.log('Valor estornado:', result.transaction.refunded_amount / 100);
  console.log('Data do estorno:', result.transaction.refunded_at);
  
  // Atualizar interface do usuário
  updateUI({
    status: 'REFUNDED',
    message: 'Estorno processado com sucesso'
  });
} else {
  console.error('Erro ao processar estorno:', result.error);
  
  // Mostrar mensagem de erro ao usuário
  showError(result.error);
}

Exemplos Práticos

Exemplo 1: Cancelamento de Compra

async function cancelarCompra(transactionId) {
  try {
    // 1. Buscar transação
    const transactionResponse = await fetch(
      `https://api.econpay.com.br/transactions/${transactionId}`,
      {
        headers: {
          'Authorization': `Bearer ${token}`
        }
      }
    );
    
    const { transaction } = await transactionResponse.json();
    
    // 2. Validar se pode estornar
    if (transaction.status !== 'PAID') {
      throw new Error('Transação não está paga');
    }
    
    if (transaction.type !== 'pix') {
      throw new Error('Transação não é PIX');
    }
    
    // 3. Processar estorno total
    const refundResponse = await fetch(
      'https://api.econpay.com.br/payments/pix/refund',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          transaction_id: transactionId,
          description: 'Cancelamento da compra pelo cliente'
        })
      }
    );
    
    const refund = await refundResponse.json();
    
    if (refund.success) {
      console.log('Compra cancelada com sucesso!');
      console.log('Valor estornado: R$', refund.transaction.refunded_amount / 100);
      return refund;
    } else {
      throw new Error(refund.error);
    }
  } catch (error) {
    console.error('Erro ao cancelar compra:', error.message);
    throw error;
  }
}

// Usar
cancelarCompra(123)
  .then(refund => {
    alert('Compra cancelada! Valor será creditado em instantes.');
  })
  .catch(error => {
    alert('Erro ao cancelar: ' + error.message);
  });

Exemplo 2: Devolução Parcial

async function devolverProduto(transactionId, valorProduto, nomeProduto) {
  try {
    // Processar estorno parcial
    const refundResponse = await fetch(
      'https://api.econpay.com.br/payments/pix/refund',
      {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          transaction_id: transactionId,
          amount: valorProduto,
          description: `Devolução do produto: ${nomeProduto}`
        })
      }
    );
    
    const refund = await refundResponse.json();
    
    if (refund.success) {
      console.log('Produto devolvido com sucesso!');
      console.log('Valor estornado: R$', refund.transaction.refunded_amount / 100);
      
      // Registrar devolução no sistema
      await registrarDevolucao({
        transactionId,
        produto: nomeProduto,
        valor: valorProduto,
        data: refund.transaction.refunded_at
      });
      
      return refund;
    } else {
      throw new Error(refund.error);
    }
  } catch (error) {
    console.error('Erro ao devolver produto:', error.message);
    throw error;
  }
}

// Usar
devolverProduto(124, 50.00, 'Camiseta Azul')
  .then(refund => {
    alert('Produto devolvido! R$ 50,00 será creditado.');
  })
  .catch(error => {
    alert('Erro na devolução: ' + error.message);
  });

Exemplo 3: Múltiplos Estornos Parciais

async function devolverMultiplosProdutos(transactionId, produtos) {
  const resultados = [];
  
  for (const produto of produtos) {
    try {
      const refund = await fetch(
        'https://api.econpay.com.br/payments/pix/refund',
        {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${token}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            transaction_id: transactionId,
            amount: produto.valor,
            description: `Devolução: ${produto.nome}`
          })
        }
      );
      
      const result = await refund.json();
      
      if (result.success) {
        resultados.push({
          produto: produto.nome,
          valor: produto.valor,
          sucesso: true
        });
      } else {
        resultados.push({
          produto: produto.nome,
          valor: produto.valor,
          sucesso: false,
          erro: result.error
        });
      }
    } catch (error) {
      resultados.push({
        produto: produto.nome,
        valor: produto.valor,
        sucesso: false,
        erro: error.message
      });
    }
  }
  
  return resultados;
}

// Usar
const produtos = [
  { nome: 'Camiseta', valor: 30.00 },
  { nome: 'Calça', valor: 50.00 }
];

devolverMultiplosProdutos(125, produtos)
  .then(resultados => {
    const sucesso = resultados.filter(r => r.sucesso);
    const falha = resultados.filter(r => !r.sucesso);
    
    console.log(`${sucesso.length} produtos devolvidos com sucesso`);
    console.log(`${falha.length} produtos com erro`);
    
    if (falha.length > 0) {
      console.error('Erros:', falha);
    }
  });

Tratamento de Erros

Erros Comuns

Erro: TRANSACTION_NOT_FOUNDCausa: ID da transação inválido ou transação não existeSolução:
if (error.code === 'TRANSACTION_NOT_FOUND') {
  console.error('Transação não encontrada. Verifique o ID.');
  // Solicitar ID correto ao usuário
}
Erro: TRANSACTION_NOT_PIXCausa: Tentativa de estornar transação que não é PIXSolução:
if (error.code === 'TRANSACTION_NOT_PIX') {
  console.error('Use /payments/reversal para outros tipos de pagamento');
  // Redirecionar para endpoint correto
}
Erro: REFUND_PERIOD_EXPIREDCausa: Mais de 90 dias desde o pagamentoSolução:
if (error.code === 'REFUND_PERIOD_EXPIRED') {
  console.error('Prazo de estorno expirado (90 dias)');
  // Informar usuário sobre prazo expirado
  // Sugerir contato com suporte para casos especiais
}
Erro: INSUFFICIENT_BALANCECausa: Conta sem saldo para processar estornoSolução:
if (error.code === 'INSUFFICIENT_BALANCE') {
  console.error('Saldo insuficiente para estorno');
  // Solicitar recarga de saldo
  // Ou aguardar entrada de novos pagamentos
}
Erro: REFUND_VALUE_EXCEEDS_ORIGINALCausa: Valor do estorno maior que valor originalSolução:
if (error.code === 'REFUND_VALUE_EXCEEDS_ORIGINAL') {
  console.error('Valor do estorno excede valor original');
  // Ajustar valor do estorno
  // Verificar se já houve estornos parciais anteriores
}

Exemplo de Tratamento Completo

async function processarEstornoComTratamento(transactionId, amount, description) {
  try {
    const response = await fetch('https://api.econpay.com.br/payments/pix/refund', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        transaction_id: transactionId,
        amount,
        description
      })
    });
    
    const result = await response.json();
    
    if (!response.ok) {
      // Tratar erros HTTP
      switch (response.status) {
        case 400:
          throw new Error(`Dados inválidos: ${result.error}`);
        case 404:
          throw new Error('Transação não encontrada');
        case 500:
          throw new Error('Erro no servidor. Tente novamente mais tarde.');
        default:
          throw new Error(`Erro desconhecido: ${result.error}`);
      }
    }
    
    if (!result.success) {
      throw new Error(result.error);
    }
    
    return result;
  } catch (error) {
    console.error('Erro ao processar estorno:', error.message);
    
    // Log para monitoramento
    logError({
      type: 'PIX_REFUND_ERROR',
      transactionId,
      amount,
      error: error.message,
      timestamp: new Date().toISOString()
    });
    
    throw error;
  }
}

Boas Práticas

Validar Antes de Estornar

Sempre valide se a transação pode ser estornada antes de enviar a requisição

Informar o Cliente

Notifique o cliente sobre o estorno e o prazo para crédito

Registrar Motivo

Sempre inclua uma descrição clara do motivo do estorno

Monitorar Erros

Implemente logs para rastrear estornos com erro

Próximos Passos

API Reference

Documentação completa do endpoint

Webhooks

Receba notificações de estornos

Transações

Consulte histórico de estornos

Erros

Lista completa de códigos de erro