(function() { 'use strict'; const FUNNEL_API_BASE = 'https://funnel.pagamerican.app'; let authToken = null; const PROCESSING_MESSAGE_DEFAULT = 'We are processing your purchase. Please wait...'; let processingOverlay = null; let processingMessageElement = null; let overlayStyleInjected = false; let pendingOverlayMessage = null; function getUrlParameter(name) { const urlParams = new URLSearchParams(window.location.search); return urlParams.get(name); } function clearPageContent() { document.body.innerHTML = ''; } function showModal(title, message) { clearPageContent(); const modal = document.createElement('div'); modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); z-index: 9999; display: flex; align-items: center; justify-content: center; `; const modalContent = document.createElement('div'); modalContent.style.cssText = ` background: white; padding: 30px; border-radius: 12px; max-width: 500px; text-align: center; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); `; const titleEl = document.createElement('h2'); titleEl.style.cssText = ` margin: 0 0 20px 0; color: #333; font-family: Arial, sans-serif; `; titleEl.textContent = title; const messageEl = document.createElement('p'); messageEl.style.cssText = ` margin: 0 0 25px 0; color: #666; font-family: Arial, sans-serif; line-height: 1.5; `; messageEl.textContent = message; modalContent.appendChild(titleEl); modalContent.appendChild(messageEl); modal.appendChild(modalContent); document.body.appendChild(modal); } function showToast(message, type = 'error') { // Remove existing toast const existingToast = document.getElementById('pag-toast'); if (existingToast) { existingToast.remove(); } const toast = document.createElement('div'); toast.id = 'pag-toast'; toast.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${type === 'error' ? '#f44336' : '#4caf50'}; color: white; padding: 15px 20px; border-radius: 4px; font-family: Arial, sans-serif; font-size: 14px; z-index: 9999; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); max-width: 300px; word-wrap: break-word; `; toast.textContent = message; document.body.appendChild(toast); setTimeout(() => { if (toast.parentNode) { toast.remove(); } }, 5000); } function injectOverlayStyles() { if (overlayStyleInjected) { return; } overlayStyleInjected = true; const style = document.createElement('style'); style.id = 'pag-processing-overlay-style'; style.textContent = ` @keyframes pagProcessingSpin { to { transform: rotate(360deg); } } `; document.head.appendChild(style); } function createProcessingOverlay() { if (processingOverlay) { return; } injectOverlayStyles(); processingOverlay = document.createElement('div'); processingOverlay.id = 'pag-processing-overlay'; processingOverlay.style.cssText = ` position: fixed; inset: 0; z-index: 9999; display: none; align-items: center; justify-content: center; background: rgba(255, 255, 255, 0.8); backdrop-filter: blur(6px); pointer-events: all; `; const content = document.createElement('div'); content.style.cssText = ` display: flex; flex-direction: column; align-items: center; gap: 24px; padding: 32px; border-radius: 16px; background: rgba(255, 255, 255, 0.95); box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12); border: 1px solid rgba(226, 232, 240, 0.9); `; const spinner = document.createElement('div'); spinner.style.cssText = ` width: 48px; height: 48px; border-radius: 9999px; border: 4px solid rgba(37, 99, 235, 0.25); border-top-color: #2563eb; animation: pagProcessingSpin 1s linear infinite; `; processingMessageElement = document.createElement('span'); processingMessageElement.style.cssText = ` font-size: 18px; font-weight: 600; text-align: center; color: #334155; max-width: 280px; font-family: 'Inter', 'Segoe UI', system-ui, -apple-system, sans-serif; `; content.appendChild(spinner); content.appendChild(processingMessageElement); processingOverlay.appendChild(content); } function showProcessingOverlay(message) { createProcessingOverlay(); processingMessageElement.textContent = message || PROCESSING_MESSAGE_DEFAULT; if (!processingOverlay.parentNode) { document.body.appendChild(processingOverlay); } processingOverlay.style.display = 'flex'; } function hideProcessingOverlay() { if (!processingOverlay) { return; } processingOverlay.style.display = 'none'; } async function makeFunnelRequest(endpoint, data = null) { const url = FUNNEL_API_BASE + endpoint; let options = { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: data ? JSON.stringify(data) : undefined, }; if (authToken) { options.headers['Authorization'] = `pagamerican ${authToken}`; } const response = await fetch(url, options); if (!response.ok) { const errorText = await response.text(); let errorMessage; try { const errorData = JSON.parse(errorText); errorMessage = errorData.message || errorData.error || errorText; } catch (e) { errorMessage = errorText; } // Handle specific errors switch (errorMessage) { case '_TOKEN_EXPIRED_': showModal( 'Session Expired', 'Your session has expired. You need to return to the checkout page to continue.' ); throw new Error('_TOKEN_EXPIRED_'); case '_INVALID_': showModal( 'Invalid Error', 'You are not allowed to continue. You need to purchase the front offer first.' ); throw new Error(errorMessage); case '_FAILED_TO_RESTORE_SESSION_': showModal( 'Invalid Step', 'You cannot access this step. Please continue following the purchase flow from where you left off.' ); throw new Error(errorMessage); case '_TAX_CALCULATION_FAILED_': showToast('Unable to process payment due to tax calculation error. Please try again.', 'error'); throw new Error(errorMessage); case '_INTERNAL_ERROR_': case '_PAYMENT_REQUIRED_': showToast('Unable to process payment due to payment method error. Please try again.', 'error'); throw new Error(errorMessage); default: showModal( 'Error', `An error occurred. Please try again later.` ); const cleanMessage = errorMessage.replace(/^Error:\s*/i, ''); throw new Error(cleanMessage); } } // Check if response has content const contentType = response.headers.get('content-type'); if (!contentType || !contentType.includes('application/json')) { return {}; } // Check if there's content in the body const text = await response.text(); if (!text || text.trim() === '') { return {}; } // Try to parse JSON try { return JSON.parse(text); } catch (e) { throw new Error(`Invalid JSON response: ${text}`); } } // Function to extract token from URL and remove parameter function extractTokenFromUrl() { const pagG = getUrlParameter('pag_g'); if (!pagG) { showModal('UNAUTHORIZED', 'You are not allowed to continue. You need to purchase the front offer first.'); throw new Error('UNAUTHORIZED'); } return pagG; } // Function to collect all localStorage items function collectLocalStorageData() { const localStorageData = {}; /* for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); localStorageData[key] = value; } */ return localStorageData; } // Function to collect searchParams (except pag_g) function collectSearchParams() { const urlParams = new URLSearchParams(window.location.search); const params = {}; for (const [key, value] of urlParams.entries()) { if (key !== 'pag_g') { params[key] = value; } } return params; } // Function to create and encode payload function createEncodedPayload() { const localStorageData = collectLocalStorageData(); const searchParams = collectSearchParams(); const payload = { localStorage: localStorageData, searchParams: searchParams }; // Encode in base64 (UTF-8 safe) const jsonString = JSON.stringify(payload); const base64Encoded = btoa(unescape(encodeURIComponent(jsonString))); return base64Encoded; } // Flag to prevent multiple initializations let isInitializing = false; let availableOfferCodes = []; let initPayload = null; // Store the _51 parameter from init // Function to initialize the funnel async function initializeFunnel() { // Prevent multiple simultaneous initializations if (isInitializing) { return; } try { // Set flag to prevent multiple initializations isInitializing = true; // Extract token from URL const token = extractTokenFromUrl(); authToken = token; // Create encoded payload with localStorage and searchParams const encodedPayload = createEncodedPayload(); // Store payload for use in confirm calls initPayload = encodedPayload; // Send as _51 parameter const requestData = { _51: encodedPayload }; const response = await makeFunnelRequest('/api/v1/init', requestData); // Store available offer codes from response if (response && Array.isArray(response)) { availableOfferCodes = response; // Trigger event for pages to create dynamic buttons window.dispatchEvent(new CustomEvent('pagOfferCodesReady', { detail: { offerCodes: availableOfferCodes } })); } //showToast(`Funnel initialized with token: ${authToken}`, 'success'); } catch (error) { // Don't block sales on init failure - just log silently console.warn('Init failed but continuing:', error.message); // Reset flag on error after a delay setTimeout(() => { isInitializing = false; }, 3000); } finally { // Always reset flag in finally block isInitializing = false; } } // Flag to prevent multiple requests let isProcessingRequest = false; // Function to choose option (upsell/downsell) async function chooseOption(option, offerCode = null) { // Prevent multiple simultaneous requests if (isProcessingRequest) { return; } const shouldShowOverlay = option === 'upsell'; if (shouldShowOverlay) { const overlayMessage = pendingOverlayMessage || PROCESSING_MESSAGE_DEFAULT; showProcessingOverlay(overlayMessage); pendingOverlayMessage = null; } else { pendingOverlayMessage = null; } try { // Set flag to prevent multiple requests isProcessingRequest = true; // Disable buttons temporarily disableButtons(); const requestData = { option: option }; // Add offerCode if provided if (offerCode) { requestData.offerCode = offerCode; } // Add init payload (_51) if available if (initPayload) { requestData._51 = initPayload; } const response = await makeFunnelRequest('/api/v1/confirm', requestData); const redirectUrl = atob(response._71); //showToast(`Redirecting to: ${redirectUrl}`, 'success'); window.location.href = redirectUrl; } catch (error) { //showToast(`Error: ${error.message}`, 'error'); } finally { // Always reset state in finally block isProcessingRequest = false; enableButtons(); if (shouldShowOverlay) { hideProcessingOverlay(); } } } // Function to disable buttons function disableButtons() { // Disable all buy buttons const buyButtons = document.querySelectorAll('[data-pag-yes]'); buyButtons.forEach(button => { button.disabled = true; button.style.opacity = '0.6'; button.style.cursor = 'not-allowed'; }); // Disable all no-buy buttons const noBuyButtons = document.querySelectorAll('[data-pag-no]'); noBuyButtons.forEach(button => { button.disabled = true; button.style.opacity = '0.6'; button.style.cursor = 'not-allowed'; }); } // Function to enable buttons function enableButtons() { // Enable all buy buttons const buyButtons = document.querySelectorAll('[data-pag-yes]'); buyButtons.forEach(button => { button.disabled = false; button.style.opacity = '1'; button.style.cursor = 'pointer'; }); // Enable all no-buy buttons const noBuyButtons = document.querySelectorAll('[data-pag-no]'); noBuyButtons.forEach(button => { button.disabled = false; button.style.opacity = '1'; button.style.cursor = 'pointer'; }); } // Function to attach click events using event delegation function attachClickEvents() { // Use event delegation to handle dynamically created buttons document.addEventListener('click', function(e) { const target = e.target; if (!(target instanceof Element)) { return; } // Handle buy buttons (data-pag-yes) const buyButton = target.closest('[data-pag-yes]'); if (buyButton) { e.preventDefault(); if (isProcessingRequest) { return; } const offerCode = buyButton.getAttribute('data-pag-offer'); const customMessage = buyButton.getAttribute('data-pag-processing-message'); pendingOverlayMessage = customMessage || PROCESSING_MESSAGE_DEFAULT; chooseOption('upsell', offerCode); return; } // Handle no-buy buttons (data-pag-no) const noBuyButton = target.closest('[data-pag-no]'); if (noBuyButton) { e.preventDefault(); if (isProcessingRequest) { return; } chooseOption('downsell'); return; } }); } async function init() { function runInit() { initializeFunnel(); attachClickEvents(); } if (document.readyState === 'complete') { runInit(); } else if (window.addEventListener) { window.addEventListener('load', runInit); } else if (window.attachEvent) { window.attachEvent('onload', runInit); } else { window.onload = runInit; } } init(); })();