saveinmed/saveinmed-bff/docs/verificar_status_pagamento_pix.md
Tiago Yamamoto b39caf0fd0 first commit
2025-12-17 13:58:26 -03:00

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

  1. Após criar a preferência, salve o external_reference
  2. Redirecione o usuário para o init_point do Mercado Pago
  3. Crie uma página de "Aguardando Pagamento" com o código acima
  4. Passe o external_reference como parâmetro na URL: ?ref=pedido-123
  5. 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