/** * Serviço para integração com Mercado Pago via API principal * Baseado na API: https://bff-dev.saveinmed.com.br/mercadopago/preference */ import { API_ROOT_URL, API_V1_BASE_URL } from '@/lib/apiBase'; const API_BASE_URL = API_V1_BASE_URL; const WEBHOOK_BASE = API_ROOT_URL; const FRONT_URL = process.env.NEXT_PUBLIC_FRONTEND_URL; /** * Interface para item do Mercado Pago */ export interface MercadoPagoItem { currency_id: string; id: string; quantity: number; title: string; unit_price: number; } /** * Interface para dados do pagador */ export interface MercadoPagoPayer { email: string; name: string; surname: string; } /** * Interface para URLs de retorno */ export interface MercadoPagoBackUrls { failure: string; success: string; pending?: string; } /** * Interface para configurações de pagamento */ export interface MercadoPagoPaymentMethods { excluded_payment_methods?: Array<{ id: string }>; excluded_payment_types?: Array<{ id: string }>; installments?: number; default_installments?: number; } /** * Interface para dados da preferência do Mercado Pago */ export interface MercadoPagoPreferenceData { back_urls: MercadoPagoBackUrls; external_reference: string; items: MercadoPagoItem[]; notification_url: string; payer: MercadoPagoPayer; payment_methods?: MercadoPagoPaymentMethods; } /** * Interface para resposta da API */ export interface MercadoPagoResponse { success: boolean; data?: any; error?: string; message?: string; preference_id?: string; init_point?: string; sandbox_init_point?: string; } /** * Serviço do Mercado Pago */ export const mercadoPagoService = { /** * Obtém o token de autenticação do localStorage */ getAuthToken: (): string | null => { return localStorage.getItem('access_token'); }, /** * Obtém dados do usuário atual */ getUserData: (): any => { try { const userStr = localStorage.getItem('user'); if (!userStr) return null; return JSON.parse(userStr); } catch (error) { console.error('Erro ao obter dados do usuário:', error); return null; } }, /** * Cria uma preferência de pagamento no Mercado Pago */ criarPreferencia: async ( pedidoId: string, itens: Array<{ produto: { id: string; nome?: string; descricao?: string; preco_venda?: number; preco_base?: number; preco_final?: number; }; quantidade: number; }>, dadosComprador?: { nome?: string; email?: string; } ): Promise => { try { const token = mercadoPagoService.getAuthToken(); const userData = mercadoPagoService.getUserData(); // Mapear itens para formato do Mercado Pago const mercadoPagoItems: MercadoPagoItem[] = itens.map((item, index) => { const preco = (item.produto as any).preco_final || 0; const nome = item.produto.nome || item.produto.descricao || `Produto ${index + 1}`; return { currency_id: "BRL", id: item.produto.id || `item-${index}`, quantity: item.quantidade, title: nome.substring(0, 255), // Mercado Pago tem limite de caracteres unit_price: preco }; }); // Dados do comprador const nomeCompleto = dadosComprador?.nome || userData?.nome || userData?.name || 'Cliente'; const email = dadosComprador?.email || userData?.email || 'cliente@example.com'; // Separar nome e sobrenome const partesNome = nomeCompleto.split(' '); const nome = partesNome[0] || 'Cliente'; const sobrenome = partesNome.slice(1).join(' ') || 'Teste'; // Montar dados da preferência const preferenceData: MercadoPagoPreferenceData = { back_urls: { failure: `${FRONT_URL}/pagamento/erro`, success: `${FRONT_URL}/pagamento/sucesso`, pending: `${FRONT_URL}/pagamento/mercadopago/pendente?ref=${pedidoId}` }, external_reference: pedidoId, items: mercadoPagoItems, notification_url: `${WEBHOOK_BASE}/mercadopago/webhook`, payer: { email: email, name: nome, surname: sobrenome }, payment_methods: { installments: 1, // Máximo de 1 parcela (à vista) default_installments: 1, // Padrão 1 parcela (à vista) excluded_payment_types: [ // Excluir boletos { id: "ticket" } // Remove boletos bancários ] } }; // Headers para a requisição const headers: HeadersInit = { 'accept': 'application/json', 'Content-Type': 'application/json', }; // Adicionar token se disponível if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(`${API_ROOT_URL}/mercadopago/preference`, { method: 'POST', headers, body: JSON.stringify(preferenceData), }); const data = await response.json(); if (response.ok) { return { success: true, data, preference_id: data.id, init_point: data.init_point, sandbox_init_point: data.sandbox_init_point }; } else { console.error('❌ Erro ao criar preferência Mercado Pago:', data); return { success: false, error: data.message || data.error || 'Erro ao criar preferência de pagamento' }; } } catch (error) { console.error('💥 Erro na criação da preferência Mercado Pago:', error); return { success: false, error: 'Erro de conexão ao criar preferência de pagamento' }; } }, /** * Verifica status de um pagamento */ verificarStatusPagamento: async (paymentId: string): Promise => { try { const response = await fetch(`/api/mercadopago/verificar-pagamento?id=${paymentId}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (response.ok && data.success) { return { success: true, data: data.data }; } else { console.error('❌ Erro ao verificar status:', data); return { success: false, error: data.error || 'Erro ao verificar status do pagamento' }; } } catch (error) { console.error('💥 Erro ao verificar status:', error); return { success: false, error: 'Erro de conexão ao verificar status' }; } }, /** * Verifica status de um pagamento por external_reference */ verificarStatusPorReferencia: async (externalReference: string): Promise => { try { const response = await fetch(`/api/mercadopago/verificar-pagamento?external_reference=${externalReference}`, { method: 'GET', headers: { 'Content-Type': 'application/json' } }); const data = await response.json(); if (response.ok && data.success) { return { success: true, data: data.data }; } else { console.error('❌ Erro ao verificar status por referência:', data); return { success: false, error: data.error || 'Erro ao verificar status do pagamento' }; } } catch (error) { console.error('💥 Erro ao verificar status por referência:', error); return { success: false, error: error instanceof Error ? error.message : 'Erro de conexão ao verificar status' }; } }, /** * Processa notificação de webhook do Mercado Pago */ processarNotificacao: async (notificationData: any): Promise => { try { const token = mercadoPagoService.getAuthToken(); const headers: HeadersInit = { 'accept': 'application/json', 'Content-Type': 'application/json', }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const response = await fetch(`${API_BASE_URL}/mercadopago/webhook`, { method: 'POST', headers, body: JSON.stringify(notificationData), }); const data = await response.json(); if (response.ok) { return { success: true, data }; } else { console.error('❌ Erro ao processar notificação:', data); return { success: false, error: data.message || 'Erro ao processar notificação' }; } } catch (error) { console.error('💥 Erro ao processar notificação:', error); return { success: false, error: 'Erro de conexão ao processar notificação' }; } }, /** * Calcula valor total dos itens */ calcularValorTotal: (itens: Array<{ produto: { preco_venda?: number; preco_base?: number; preco_final?: number; }; quantidade: number; }>): number => { return itens.reduce((total, item) => { const preco = (item.produto as any).preco_final || 0; return total + (preco * item.quantidade); }, 0); }, /** * Formata preço para exibição */ formatarPreco: (valor: number): string => { return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(valor); }, /** * Valida dados de preferência antes de enviar */ validarDadosPreferencia: ( pedidoId: string, itens: Array ): { valido: boolean; erros: string[] } => { const erros: string[] = []; // Validar pedido ID if (!pedidoId || pedidoId.trim() === '') { erros.push('ID do pedido é obrigatório'); } // Validar itens if (!itens || itens.length === 0) { erros.push('Pelo menos um item é obrigatório'); } else { itens.forEach((item, index) => { if (!item.produto) { erros.push(`Item ${index + 1}: dados do produto são obrigatórios`); } else { if (!item.produto.id) { erros.push(`Item ${index + 1}: ID do produto é obrigatório`); } const preco = (item.produto as any).preco_final; if (!preco || preco <= 0) { erros.push(`Item ${index + 1}: preço final deve ser maior que zero`); } if (!item.quantidade || item.quantidade <= 0) { erros.push(`Item ${index + 1}: quantidade deve ser maior que zero`); } } }); } return { valido: erros.length === 0, erros }; } };