Estornar Pagamento
Estorna (devolve) o valor de um pagamento já confirmado.
IMPORTANTE
O estorno só é possível após o pagamento ser confirmado. Para cancelar antes do pagamento, use Cancelar.
PUT /api/charge/{chargeId}/pix/refund
Estorna um pagamento Pix.
Endpoint
PUT https://api.divipay.com.br/api/charge/{chargeId}/pix/refundHeaders
| Header | Valor | Obrigatório |
|---|---|---|
| Authorization | Bearer | Sim |
Path Parameters
| Parâmetro | Tipo | Descrição |
|---|---|---|
| chargeId | string | ID da cobrança |
Query Parameters
| Parâmetro | Tipo | Descrição | Obrigatório |
|---|---|---|---|
| amount | string | Valor a estornar (parcial ou total) | Não |
| main_refund | string | "true" ou "false" - Enviar para conta principal | Não |
Exemplo de Requisição
bash
curl -X PUT "https://api.divipay.com.br/api/charge/cob_abc123/pix/refund" \
-H "Authorization: Bearer SEU_TOKEN"bash
curl -X PUT "https://api.divipay.com.br/api/charge/cob_abc123/pix/refund?amount=50.00" \
-H "Authorization: Bearer SEU_TOKEN"bash
curl -X PUT "https://api.divipay.com.br/api/charge/cob_abc123/pix/refund?main_refund=true" \
-H "Authorization: Bearer SEU_TOKEN"javascript
// Estorno total
const response = await fetch(
'https://api.divipay.com.br/api/charge/cob_abc123/pix/refund',
{
method: 'PUT',
headers: {
'Authorization': 'Bearer SEU_TOKEN'
}
}
);
// Estorno parcial
const response = await fetch(
'https://api.divipay.com.br/api/charge/cob_abc123/pix/refund?amount=50.00',
{
method: 'PUT',
headers: {
'Authorization': 'Bearer SEU_TOKEN'
}
}
);
const result = await response.json();python
import requests
# Estorno total
response = requests.put(
'https://api.divipay.com.br/api/charge/cob_abc123/pix/refund',
headers={
'Authorization': 'Bearer SEU_TOKEN'
}
)
# Estorno parcial
response = requests.put(
'https://api.divipay.com.br/api/charge/cob_abc123/pix/refund',
params={'amount': '50.00'},
headers={
'Authorization': 'Bearer SEU_TOKEN'
}
)
result = response.json()Resposta de Sucesso
Status: 200 OK
json
{
"id": "cob_abc123",
"status": "REFUNDED",
"refundedAmount": 100.00,
"refundedAt": "2024-11-04T16:00:00.000Z",
"message": "Refund processed successfully"
}PUT /api/charge/{chargeId}/credit-card/refund
Estorna um pagamento de Cartão de Crédito.
Endpoint
PUT https://api.divipay.com.br/api/charge/{chargeId}/credit-card/refundBody Parameters
| Campo | Tipo | Descrição | Obrigatório |
|---|---|---|---|
| amount | number | Valor a estornar | Sim |
Exemplo de Requisição
bash
curl -X PUT https://api.divipay.com.br/api/charge/cc_abc123/credit-card/refund \
-H "Authorization: Bearer SEU_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"amount": 100.00
}'Resposta
json
{
"id": "cc_abc123",
"status": "REFUNDED",
"ok": true,
"message": "Refund processed",
"amountRefunded": 100.00
}Tipos de Estorno
1. Estorno Total
Devolve o valor completo da transação:
javascript
await fetch(
`https://api.divipay.com.br/api/charge/${chargeId}/pix/refund`,
{
method: 'PUT',
headers: { 'Authorization': `Bearer ${token}` }
}
);2. Estorno Parcial
Devolve apenas parte do valor:
javascript
await fetch(
`https://api.divipay.com.br/api/charge/${chargeId}/pix/refund?amount=50.00`,
{
method: 'PUT',
headers: { 'Authorization': `Bearer ${token}` }
}
);3. Estorno para Conta Principal
Envia o valor para a conta principal (taxas aplicadas novamente):
javascript
await fetch(
`https://api.divipay.com.br/api/charge/${chargeId}/pix/refund?main_refund=true`,
{
method: 'PUT',
headers: { 'Authorization': `Bearer ${token}` }
}
);Exemplo de Uso
Estornar Pagamento
javascript
async function refundCharge(chargeId, amount = null) {
try {
let url = `https://api.divipay.com.br/api/charge/${chargeId}/pix/refund`;
if (amount) {
url += `?amount=${amount}`;
}
const response = await fetch(url, {
method: 'PUT',
headers: {
'Authorization': `Bearer ${token}`
}
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
const result = await response.json();
console.log('✅ Estorno processado:', result);
return result;
} catch (error) {
console.error('❌ Erro ao estornar:', error.message);
throw error;
}
}
// Estorno total
await refundCharge('cob_abc123');
// Estorno parcial
await refundCharge('cob_abc123', 50.00);Estornar com Verificação
javascript
async function safeRefund(chargeId, amount = null) {
try {
// 1. Verificar status
const charge = await fetch(
`https://api.divipay.com.br/api/charge/${chargeId}`,
{
headers: { 'Authorization': `Bearer ${token}` }
}
).then(r => r.json());
// 2. Validar se pode estornar
if (charge.status !== 'PAID') {
throw new Error('Apenas cobranças pagas podem ser estornadas');
}
// 3. Validar valor
if (amount && amount > charge.amount) {
throw new Error('Valor do estorno maior que o valor da cobrança');
}
// 4. Processar estorno
const result = await refundCharge(chargeId, amount);
console.log(`✅ Estornado: R$ ${result.refundedAmount}`);
return result;
} catch (error) {
console.error('❌ Erro:', error.message);
throw error;
}
}Estornar Pedido Completo
javascript
async function refundOrder(orderId, reason) {
try {
// 1. Buscar pedido
const order = await db.orders.findUnique({
where: { id: orderId },
include: { charge: true }
});
if (!order.charge) {
throw new Error('Pedido sem cobrança associada');
}
// 2. Estornar na DiviPay
const result = await refundCharge(order.charge.chargeId);
// 3. Atualizar banco de dados
await db.orders.update({
where: { id: orderId },
data: {
status: 'REFUNDED',
refundedAt: new Date(),
refundReason: reason
}
});
// 4. Notificar cliente
await sendRefundEmail(order.customer.email, {
orderId,
amount: result.refundedAmount,
reason
});
console.log(`✅ Pedido ${orderId} estornado`);
return result;
} catch (error) {
console.error('❌ Erro ao estornar pedido:', error);
throw error;
}
}
// Uso
await refundOrder('order-123', 'Produto com defeito');Estorno Parcial Múltiplo
javascript
async function partialRefunds(chargeId, refunds) {
const results = [];
let totalRefunded = 0;
for (const refund of refunds) {
try {
const result = await refundCharge(chargeId, refund.amount);
results.push({
success: true,
amount: refund.amount,
reason: refund.reason
});
totalRefunded += refund.amount;
} catch (error) {
results.push({
success: false,
amount: refund.amount,
error: error.message
});
}
}
return {
totalRefunded,
results
};
}
// Uso - Estornar em partes
const results = await partialRefunds('cob_abc123', [
{ amount: 30.00, reason: 'Item 1 devolvido' },
{ amount: 20.00, reason: 'Item 2 devolvido' }
]);Interface de Estorno
javascript
// React Component
function RefundButton({ chargeId, maxAmount, onRefund }) {
const [amount, setAmount] = useState(maxAmount);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
async function handleRefund() {
const reason = prompt('Motivo do estorno:');
if (!reason) return;
if (!confirm(`Estornar R$ ${amount.toFixed(2)}?`)) {
return;
}
setLoading(true);
setError(null);
try {
await refundCharge(chargeId, amount);
alert('Estorno processado com sucesso!');
onRefund?.();
} catch (err) {
setError(err.message);
alert('Erro ao estornar: ' + err.message);
} finally {
setLoading(false);
}
}
return (
<div>
<label>
Valor do estorno:
<input
type="number"
value={amount}
onChange={(e) => setAmount(parseFloat(e.target.value))}
max={maxAmount}
step="0.01"
disabled={loading}
/>
</label>
<button
onClick={handleRefund}
disabled={loading || amount <= 0 || amount > maxAmount}
style={{
backgroundColor: 'orange',
color: 'white',
padding: '10px 20px',
border: 'none',
borderRadius: '5px',
cursor: loading ? 'not-allowed' : 'pointer'
}}
>
{loading ? 'Processando...' : `Estornar R$ ${amount.toFixed(2)}`}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
}Webhook
Quando um estorno é processado:
json
{
"event": "charge.refunded",
"chargeId": "cob_abc123",
"referenceId": "pedido-123",
"amount": 100.00,
"refundedAmount": 100.00,
"refundedAt": "2024-11-04T16:00:00.000Z",
"status": "REFUNDED"
}Respostas de Erro
412 Precondition Failed
Cobrança não foi paga ainda.
json
{
"statusCode": 412,
"message": "Cannot refund unpaid charge",
"error": "Precondition Failed"
}400 Bad Request
Valor inválido.
json
{
"statusCode": 400,
"message": "Refund amount exceeds charge amount",
"error": "Bad Request"
}404 Not Found
Cobrança não encontrada.
json
{
"statusCode": 404,
"message": "Charge not found",
"error": "Not Found"
}Casos de Uso
1. Produto com Defeito
javascript
async function refundDefectiveProduct(orderId) {
await refundOrder(orderId, 'Produto com defeito');
await db.returns.create({
data: {
orderId,
reason: 'DEFECTIVE',
status: 'APPROVED',
refundedAt: new Date()
}
});
}2. Arrependimento do Cliente
javascript
async function refundCustomerRegret(orderId) {
const order = await db.orders.findUnique({
where: { id: orderId }
});
// Verificar prazo de 7 dias
const daysSincePurchase = Math.floor(
(Date.now() - order.paidAt.getTime()) / (1000 * 60 * 60 * 24)
);
if (daysSincePurchase > 7) {
throw new Error('Prazo de arrependimento expirado');
}
await refundOrder(orderId, 'Direito de arrependimento');
}3. Cobrança Duplicada
javascript
async function refundDuplicate(chargeId) {
await refundCharge(chargeId);
await db.duplicates.create({
data: {
chargeId,
detectedAt: new Date(),
refundedAt: new Date()
}
});
}Boas Práticas
1. Sempre Registrar Motivo
javascript
await db.refunds.create({
data: {
chargeId,
amount: refundedAmount,
reason: 'Produto com defeito',
requestedBy: userId,
processedAt: new Date()
}
});2. Notificar Cliente
javascript
async function refundWithNotification(chargeId, amount, reason) {
const result = await refundCharge(chargeId, amount);
await sendEmail({
to: customer.email,
subject: 'Estorno Processado',
body: `
Seu estorno foi processado com sucesso!
Valor: R$ ${amount.toFixed(2)}
Motivo: ${reason}
O valor será creditado em até 5 dias úteis.
`
});
}3. Validar Permissões
javascript
async function authorizedRefund(chargeId, userId) {
const user = await db.users.findUnique({
where: { id: userId }
});
if (!user.canRefund) {
throw new Error('Usuário sem permissão para estornar');
}
return await refundCharge(chargeId);
}4. Auditoria
javascript
await db.auditLog.create({
data: {
action: 'REFUND',
chargeId,
amount: refundedAmount,
userId,
reason,
timestamp: new Date()
}
});Prazos
- Pix: Estorno imediato
- Cartão de Crédito: 5-10 dias úteis
- Boleto: Não aplicável (use transferência manual)