// ============ CHECKOUT MODAL — Mercado Pago CheckoutBricks ============ // Configurar aqui: const CONFIG = { // Supabase — Project Settings → API SUPABASE_URL: 'https://xemopdhqloicwolqahyf.supabase.co', SUPABASE_ANON_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhlbW9wZGhxbG9pY3dvbHFhaHlmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzcwMjE0NDUsImV4cCI6MjA5MjU5NzQ0NX0.eRbSX3QIFqLdC8w148artJ9OIQzJzoWw8Exs7LHey8Y', // Mercado Pago — Public Key de PRODUÇÃO (pode ficar no frontend) // O Access Token fica APENAS na Edge Function (secret do servidor) MP_PUBLIC_KEY: 'APP_USR-aa8b3a5a-51b2-4c8d-ad70-103269742402', // WhatsApp — apenas números com DDI, ex: '5511999999999' // Deixe como está até ter o número. Aparece só como fallback de erro. WHATSAPP: '5500000000000', }; // Passos: // 1 = formulário de envio // 2 = carregando (criando preferência no MP) // 3 = Payment Brick ativo // 4 = processando pagamento // 5 = aprovado / pendente (pix, boleto) // 6 = erro function CheckoutModal({ open, onClose }) { const [step, setStep] = useState(1); const [form, setForm] = useState({ nome: '', email: '', cpf: '', telefone: '', cep: '', endereco: '', numero: '', complemento: '', bairro: '', cidade: '', uf: '', }); const [err, setErr] = useState(''); const [pedidoId, setPedidoId] = useState(null); const [payStatus, setPayStatus] = useState(null); // 'approved' | 'pending' | 'rejected' const brickControllerRef = useRef(null); // Carrega o SDK do MP quando o modal abre (uma vez) useEffect(() => { if (!open) return; if (document.getElementById('mp-sdk')) return; const s = document.createElement('script'); s.id = 'mp-sdk'; s.src = 'https://sdk.mercadopago.com/js/v2'; document.head.appendChild(s); }, [open]); // Fecha com Escape, trava scroll useEffect(() => { if (!open) return; const onKey = (e) => { if (e.key === 'Escape') onClose(); }; document.body.style.overflow = 'hidden'; window.addEventListener('keydown', onKey); return () => { document.body.style.overflow = ''; window.removeEventListener('keydown', onKey); }; }, [open, onClose]); // Destrói o Brick ao fechar useEffect(() => { if (!open && brickControllerRef.current) { brickControllerRef.current.unmount(); brickControllerRef.current = null; } }, [open]); const setF = (k) => (e) => setForm(s => ({ ...s, [k]: e.target.value })); // Auto-preenche endereço pelo CEP (ViaCEP, API pública) const onCepBlur = async () => { const cep = form.cep.replace(/\D/g, ''); if (cep.length !== 8) return; try { const r = await fetch(`https://viacep.com.br/ws/${cep}/json/`); const d = await r.json(); if (d.erro) return; setForm(s => ({ ...s, endereco: d.logradouro || s.endereco, bairro: d.bairro || s.bairro, cidade: d.localidade || s.cidade, uf: d.uf || s.uf, })); } catch (_) {} }; const validate = () => { const req = ['nome','email','cpf','telefone','cep','endereco','numero','bairro','cidade','uf']; for (const k of req) if (!form[k].trim()) return `Campo obrigatório: ${k}`; if (!/^[^@]+@[^@]+\.[^@]+$/.test(form.email)) return 'E-mail inválido'; return null; }; // Espera o SDK carregar (máx 4s) const waitForSDK = () => new Promise((resolve, reject) => { if (window.MercadoPago) { resolve(); return; } let tries = 0; const t = setInterval(() => { if (window.MercadoPago) { clearInterval(t); resolve(); } else if (++tries > 20) { clearInterval(t); reject(new Error('MP SDK timeout')); } }, 200); }); // Inicializa o Brick com o preferenceId retornado pela Edge Function const initBrick = async (preferenceId, pedId) => { await waitForSDK(); if (brickControllerRef.current) { brickControllerRef.current.unmount(); brickControllerRef.current = null; } const mp = new window.MercadoPago(CONFIG.MP_PUBLIC_KEY, { locale: 'pt-BR' }); const builder = mp.bricks(); setStep(3); const controller = await builder.create('payment', 'mp-brick-container', { initialization: { amount: 119.90, preferenceId, }, customization: { paymentMethods: { creditCard: 'all', debitCard: 'all', ticket: 'all', // boleto bankTransfer: 'all', // pix }, visual: { style: { theme: 'default' }, hideFormTitle: true, }, }, callbacks: { onReady: () => {}, onError: (e) => console.error('[Brick]', e), // Chamado quando o usuário clica "Pagar" no Brick. // Deve retornar uma Promise — resolve = sucesso, reject = falha. onSubmit: ({ formData }) => new Promise(async (resolve, reject) => { setStep(4); try { const res = await fetch( `${CONFIG.SUPABASE_URL}/functions/v1/mp-checkout/pay`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'apikey': CONFIG.SUPABASE_ANON_KEY, 'Authorization': `Bearer ${CONFIG.SUPABASE_ANON_KEY}`, }, body: JSON.stringify({ formData, pedido_id: pedId }), } ); const data = await res.json(); if (data.status === 'approved') { setPayStatus('approved'); setStep(5); resolve(); } else if (data.status === 'pending' || data.status === 'in_process') { setPayStatus('pending'); setStep(5); resolve(); } else { setPayStatus('rejected'); setStep(6); reject(); } } catch (e) { console.error('[pay]', e); setStep(6); reject(); } }), }, }); brickControllerRef.current = controller; }; // Envia formulário → Edge Function → preference_id → Brick const submit = async () => { const v = validate(); if (v) { setErr(v); return; } setErr(''); setStep(2); try { const res = await fetch( `${CONFIG.SUPABASE_URL}/functions/v1/mp-checkout/preference`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'apikey': CONFIG.SUPABASE_ANON_KEY, 'Authorization': `Bearer ${CONFIG.SUPABASE_ANON_KEY}`, }, body: JSON.stringify(form), } ); const data = await res.json(); if (!res.ok || !data.preference_id) throw new Error(data.error || 'Erro ao criar preferência'); setPedidoId(data.pedido_id); await initBrick(data.preference_id, data.pedido_id); } catch (e) { console.error('[preference]', e); setStep(6); } }; const whatsappMsg = encodeURIComponent( `Olá! Quero o Selo dos 7 Arcanjos.\n\nNome: ${form.nome || '...'}\nEmail: ${form.email || '...'}\nEndereço: ${form.endereco}, ${form.numero} — ${form.cidade}/${form.uf}, CEP ${form.cep}` ); const temWhatsapp = CONFIG.WHATSAPP !== '5500000000000'; const stepLabel = { 1:'1', 2:'1', 3:'2', 4:'3', 5:'✓', 6:'!' }[step] ?? step; if (!open) return null; return (
e.stopPropagation()} style={{ background: 'var(--bg)', border: '1px solid var(--line)', width: '100%', maxWidth: step === 3 ? 820 : 720, maxHeight: '92vh', overflow: 'auto', position: 'relative', boxShadow: '0 60px 120px -30px rgba(0,0,0,.5)', animation: 'modalSlide .5s cubic-bezier(.2,.7,.2,1)', transition: 'max-width .4s ease', }}> {/* Fechar */} {/* Header */}
CHECKOUT · ETAPA {stepLabel} DE 3

{step === 1 && 'Seus dados de envio'} {step === 2 && 'Preparando pagamento…'} {step === 3 && 'Como deseja pagar?'} {step === 4 && 'Processando…'} {step === 5 && (payStatus === 'pending' ? 'Pagamento em análise' : 'Pedido confirmado!')} {step === 6 && 'Não foi possível processar'}

Selo dos 7 Arcanjos ·{' '} R$ 119,90{' '} em 3× sem juros · frete até R$ 15 incluso
{/* ── Etapa 1: Formulário ── */} {step === 1 && (
{err && (
{err}
)}
{temWhatsapp && ( Prefiro finalizar pelo WhatsApp )}

Seus dados são armazenados com segurança para emissão do envio.
Pagamento processado via Mercado Pago.

)} {/* ── Etapa 2: Carregando preferência ── */} {step === 2 && } {/* ── Etapa 3: Brick ── */} {step === 3 && (
)} {/* ── Etapa 4: Processando pagamento ── */} {step === 4 && } {/* ── Etapa 5: Sucesso / Pendente ── */} {step === 5 && (
{payStatus === 'approved' && <>

Pagamento confirmado.

Seu Selo será enviado em até 24h úteis. Fique de olho no e-mail {form.email}.

} {payStatus === 'pending' && <>

Pagamento em análise.

Se escolheu Pix ou boleto, a confirmação chega assim que o pagamento for identificado. Avisaremos por e-mail.

}
)} {/* ── Etapa 6: Erro ── */} {step === 6 && (

Não foi possível processar.

Verifique os dados do cartão ou tente outra forma de pagamento. {temWhatsapp && ' Ou finalize pelo WhatsApp.'}

{temWhatsapp && ( Falar no WhatsApp )}
)}
); } function Field({ label, value, onChange, onBlur, type = 'text', col = 1, placeholder, maxLength }) { return ( ); } function CheckoutStatus({ text }) { return (

{text.toUpperCase()}

); } Object.assign(window, { CheckoutModal, CHECKOUT_CONFIG: CONFIG });