13 KiB
13 KiB
Como Verificar Status do Pagamento PIX
Este guia mostra como implementar a verificação de status do pagamento PIX no frontend, incluindo um botão manual e polling automático.
Endpoint Disponível
GET /mercadopago/payment/by-reference/{external_reference}
Resposta de Exemplo
{
"has_payment": true,
"latest_payment": {
"id": 123456789,
"status": "approved",
"transaction_amount": 0.01,
"payment_method_id": "pix",
"external_reference": "pedido-123"
},
"is_approved": true,
"status": "approved",
"count": 1
}
Implementação no Frontend
Opção 1: Botão Manual para Verificar Status
<!DOCTYPE html>
<html>
<head>
<title>Verificar Pagamento PIX</title>
</head>
<body>
<div id="payment-status">
<h2>Status do Pagamento</h2>
<p id="status-message">Aguardando verificação...</p>
<button id="check-status-btn" onclick="checkPaymentStatus()">
Verificar Status do Pagamento
</button>
</div>
<script>
// Obtenha o external_reference da URL ou de onde você salvou
const externalReference = new URLSearchParams(window.location.search).get('ref') || 'pedido-123';
const apiBaseUrl = 'https://bff.saveinmed.com.br'; // Ajuste para sua URL
async function checkPaymentStatus() {
const btn = document.getElementById('check-status-btn');
const message = document.getElementById('status-message');
btn.disabled = true;
btn.textContent = 'Verificando...';
message.textContent = 'Verificando status do pagamento...';
try {
const response = await fetch(
`${apiBaseUrl}/mercadopago/payment/by-reference/${externalReference}`
);
if (!response.ok) {
throw new Error('Erro ao verificar status');
}
const data = await response.json();
if (!data.has_payment) {
message.textContent = 'Pagamento ainda não foi criado. Aguarde...';
message.style.color = 'orange';
} else {
const status = data.latest_payment.status;
const isApproved = data.is_approved;
if (isApproved) {
message.textContent = '✅ Pagamento aprovado! Redirecionando...';
message.style.color = 'green';
// Redireciona para página de sucesso
setTimeout(() => {
window.location.href = 'https://app.saveinmed.com.br/pagamento/sucesso';
}, 2000);
} else if (status === 'pending') {
message.textContent = '⏳ Pagamento pendente. Aguarde a confirmação...';
message.style.color = 'orange';
} else if (status === 'rejected' || status === 'cancelled') {
message.textContent = '❌ Pagamento recusado ou cancelado.';
message.style.color = 'red';
} else {
message.textContent = `Status: ${status}`;
message.style.color = 'gray';
}
}
} catch (error) {
message.textContent = 'Erro ao verificar status: ' + error.message;
message.style.color = 'red';
} finally {
btn.disabled = false;
btn.textContent = 'Verificar Status do Pagamento';
}
}
// Verificar automaticamente ao carregar a página
window.addEventListener('load', () => {
checkPaymentStatus();
});
</script>
</body>
</html>
Opção 2: Polling Automático (Recomendado)
<!DOCTYPE html>
<html>
<head>
<title>Aguardando Pagamento PIX</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
text-align: center;
}
.status-card {
border: 2px solid #ddd;
border-radius: 10px;
padding: 30px;
margin: 20px 0;
}
.pending {
border-color: #ffa500;
background-color: #fff8e1;
}
.approved {
border-color: #4caf50;
background-color: #e8f5e9;
}
.rejected {
border-color: #f44336;
background-color: #ffebee;
}
button {
background-color: #2196F3;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
margin-top: 20px;
}
button:hover {
background-color: #1976D2;
}
button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #2196F3;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<h1>Verificando Pagamento</h1>
<div id="status-card" class="status-card pending">
<div id="spinner" class="spinner"></div>
<h2 id="status-title">Aguardando confirmação do pagamento...</h2>
<p id="status-message">Por favor, aguarde enquanto verificamos o status do seu pagamento.</p>
<p id="status-details"></p>
</div>
<button id="check-btn" onclick="checkPaymentStatus()">Verificar Agora</button>
<script>
const externalReference = new URLSearchParams(window.location.search).get('ref') || 'pedido-123';
const apiBaseUrl = 'https://bff.saveinmed.com.br'; // Ajuste para sua URL
let pollingInterval = null;
let checkCount = 0;
const maxChecks = 60; // Máximo de 60 verificações (5 minutos se verificar a cada 5 segundos)
async function checkPaymentStatus() {
const card = document.getElementById('status-card');
const title = document.getElementById('status-title');
const message = document.getElementById('status-message');
const details = document.getElementById('status-details');
const spinner = document.getElementById('spinner');
const btn = document.getElementById('check-btn');
try {
const response = await fetch(
`${apiBaseUrl}/mercadopago/payment/by-reference/${externalReference}`
);
if (!response.ok) {
throw new Error('Erro ao verificar status');
}
const data = await response.json();
checkCount++;
if (!data.has_payment) {
title.textContent = 'Aguardando criação do pagamento...';
message.textContent = 'O pagamento ainda não foi registrado. Aguarde alguns instantes.';
details.textContent = `Verificações realizadas: ${checkCount}/${maxChecks}`;
card.className = 'status-card pending';
} else {
const status = data.latest_payment.status;
const isApproved = data.is_approved;
const paymentId = data.latest_payment.id;
const amount = data.latest_payment.transaction_amount;
if (isApproved) {
spinner.style.display = 'none';
title.textContent = '✅ Pagamento Aprovado!';
message.textContent = 'Seu pagamento foi confirmado com sucesso!';
details.textContent = `ID do pagamento: ${paymentId} | Valor: R$ ${amount.toFixed(2)}`;
card.className = 'status-card approved';
btn.style.display = 'none';
// Para o polling
if (pollingInterval) {
clearInterval(pollingInterval);
}
// Redireciona após 3 segundos
setTimeout(() => {
window.location.href = 'https://app.saveinmed.com.br/pagamento/sucesso';
}, 3000);
} else if (status === 'pending') {
title.textContent = '⏳ Pagamento Pendente';
message.textContent = 'Aguardando confirmação do banco. Isso pode levar alguns segundos.';
details.textContent = `ID: ${paymentId} | Verificações: ${checkCount}/${maxChecks}`;
card.className = 'status-card pending';
} else if (status === 'rejected' || status === 'cancelled') {
spinner.style.display = 'none';
title.textContent = '❌ Pagamento Não Aprovado';
message.textContent = `Status: ${status}. Por favor, tente novamente.`;
details.textContent = `ID: ${paymentId}`;
card.className = 'status-card rejected';
btn.style.display = 'none';
// Para o polling
if (pollingInterval) {
clearInterval(pollingInterval);
}
} else {
title.textContent = `Status: ${status}`;
message.textContent = 'Aguardando atualização do status...';
details.textContent = `ID: ${paymentId}`;
}
}
// Para o polling se atingiu o máximo de verificações
if (checkCount >= maxChecks) {
if (pollingInterval) {
clearInterval(pollingInterval);
}
btn.style.display = 'block';
message.textContent += ' Polling automático parado. Use o botão para verificar manualmente.';
}
} catch (error) {
spinner.style.display = 'none';
title.textContent = 'Erro ao Verificar';
message.textContent = 'Ocorreu um erro ao verificar o status: ' + error.message;
details.textContent = '';
card.className = 'status-card rejected';
}
}
// Inicia polling automático a cada 5 segundos
function startPolling() {
checkPaymentStatus(); // Verifica imediatamente
pollingInterval = setInterval(checkPaymentStatus, 5000); // Depois a cada 5 segundos
}
// Para o polling
function stopPolling() {
if (pollingInterval) {
clearInterval(pollingInterval);
pollingInterval = null;
}
}
// Inicia quando a página carrega
window.addEventListener('load', startPolling);
// Para quando a página é fechada
window.addEventListener('beforeunload', stopPolling);
</script>
</body>
</html>
Como Usar
- Após criar a preferência, salve o
external_reference - Redirecione o usuário para o
init_pointdo Mercado Pago - Crie uma página de "Aguardando Pagamento" com o código acima
- Passe o
external_referencecomo parâmetro na URL:?ref=pedido-123 - A página fará polling automático e redirecionará quando o pagamento for aprovado
Exemplo de Fluxo
// 1. Criar preferência
const response = await fetch('/mercadopago/preference', {
method: 'POST',
body: JSON.stringify({
items: [...],
external_reference: 'pedido-123',
back_urls: {
success: 'https://app.saveinmed.com.br/pagamento/sucesso',
pending: 'https://app.saveinmed.com.br/pagamento/aguardando?ref=pedido-123',
failure: 'https://app.saveinmed.com.br/pagamento/erro'
}
})
});
const preference = await response.json();
// 2. Redirecionar para o Mercado Pago
window.location.href = preference.init_point; // ou sandbox_init_point
// 3. Quando o usuário voltar (via back_urls.pending), a página de aguardando
// fará polling automático até detectar que o pagamento foi aprovado
Notas Importantes
- O polling verifica a cada 5 segundos
- Para automaticamente após 60 verificações (5 minutos)
- Redireciona automaticamente quando o pagamento é aprovado
- O usuário pode clicar no botão para verificar manualmente a qualquer momento