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