$success, 'message' => $message, 'timestamp' => date('c') ]; // Only include data if not null and not in production (security) if ($data !== null && (!USE_PRODUCTION || DEBUG_MODE)) { $response['data'] = $data; } elseif ($data !== null && $success) { // In production, only return safe data $response['data'] = filterSafeData($data); } echo json_encode($response); exit(); } function filterSafeData($data) { // Remove sensitive fields from response $sensitiveFields = ['webhook_url', 'ip_address', 'user_agent', 'http_code', 'error']; if (is_array($data)) { foreach ($sensitiveFields as $field) { unset($data[$field]); } } return $data; } function getClientIP() { $ipKeys = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR']; foreach ($ipKeys as $key) { if (array_key_exists($key, $_SERVER)) { foreach (explode(',', $_SERVER[$key]) as $ip) { $ip = trim($ip); if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) { return $ip; } } } } return $_SERVER['REMOTE_ADDR'] ?? 'unknown'; } function sanitizeInput($input) { if (is_array($input)) { return array_map('sanitizeInput', $input); } return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8'); } function validateCSRFToken($token) { // Implement CSRF validation if needed return true; } function checkRateLimit($ip) { $rateLimitFile = __DIR__ . '/rate_limits.json'; $limits = []; if (file_exists($rateLimitFile)) { $limits = json_decode(file_get_contents($rateLimitFile), true) ?: []; } $now = time(); $windowStart = $now - RATE_LIMIT_WINDOW; // Clean old entries $limits = array_filter($limits, function($entry) use ($windowStart) { return $entry['time'] > $windowStart; }); // Count requests from this IP $ipRequests = array_filter($limits, function($entry) use ($ip) { return $entry['ip'] === $ip; }); if (count($ipRequests) >= RATE_LIMIT_REQUESTS) { return false; } // Add new request $limits[] = ['ip' => $ip, 'time' => $now]; file_put_contents($rateLimitFile, json_encode($limits)); return true; } // ========================================== // WEBHOOK FUNCTIONS // ========================================== function sendToWebhook($data, $webhookUrl = null) { $url = $webhookUrl ?? WEBHOOK_URL; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($data), CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'User-Agent: Profice-Web-API/2.0' ], CURLOPT_TIMEOUT => 30, CURLOPT_SSL_VERIFYPEER => !DEBUG_MODE, // Disable for debugging CURLOPT_FOLLOWLOCATION => true ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { error_log("Webhook Error: " . $error); return ['success' => false, 'error' => $error, 'response' => $response]; } return [ 'success' => $httpCode >= 200 && $httpCode < 300, 'http_code' => $httpCode, 'response' => $response, 'error' => $error ]; } // ========================================== // TRACKING FUNCTIONS - SERVER SIDE // ========================================== function getTrackingConfig() { // Return tracking configuration for client // IDs are loaded from server, not exposed in JS return [ 'analytics_enabled' => !empty(GA_MEASUREMENT_ID) && GA_MEASUREMENT_ID !== 'G-XXXXXXXXXX', 'gtm_enabled' => !empty(GTM_CONTAINER_ID) && GTM_CONTAINER_ID !== 'GTM-XXXXXXX', 'fb_enabled' => !empty(FB_PIXEL_ID), 'gads_enabled' => !empty(GADS_CONVERSION_ID) && GADS_CONVERSION_ID !== 'AW-XXXXXXXXXX', 'linkedin_enabled' => !empty(LINKEDIN_PARTNER_ID) ]; } function loadTrackingScripts($preferences) { $scripts = []; // Google Analytics if ($preferences['analytics'] && !empty(GA_MEASUREMENT_ID) && GA_MEASUREMENT_ID !== 'G-XXXXXXXXXX') { $scripts['ga'] = [ 'type' => 'analytics', 'src' => 'https://www.googletagmanager.com/gtag/js?id=' . GA_MEASUREMENT_ID, 'config' => [ 'id' => GA_MEASUREMENT_ID ] ]; } // Google Tag Manager if ($preferences['analytics'] && !empty(GTM_CONTAINER_ID) && GTM_CONTAINER_ID !== 'GTM-XXXXXXX') { $scripts['gtm'] = [ 'type' => 'gtm', 'src' => 'https://www.googletagmanager.com/gtm.js?id=' . GTM_CONTAINER_ID, 'config' => [ 'id' => GTM_CONTAINER_ID ] ]; } // Facebook Pixel if ($preferences['marketing'] && !empty(FB_PIXEL_ID)) { $scripts['fb'] = [ 'type' => 'pixel', 'src' => 'https://connect.facebook.net/en_US/fbevents.js', 'config' => [ 'pixel_id' => FB_PIXEL_ID ] ]; } // Google Ads if ($preferences['marketing'] && !empty(GADS_CONVERSION_ID) && GADS_CONVERSION_ID !== 'AW-XXXXXXXXXX') { $scripts['gads'] = [ 'type' => 'conversion', 'src' => 'https://www.googletagmanager.com/gtag/js?id=' . GADS_CONVERSION_ID, 'config' => [ 'conversion_id' => GADS_CONVERSION_ID, 'conversion_label' => GADS_CONVERSION_LABEL ] ]; } // LinkedIn if ($preferences['marketing'] && !empty(LINKEDIN_PARTNER_ID)) { $scripts['linkedin'] = [ 'type' => 'insight', 'src' => 'https://snap.licdn.com/li.lms-analytics/insight.min.js', 'config' => [ 'partner_id' => LINKEDIN_PARTNER_ID ] ]; } return $scripts; } function sendEventToGA($data) { if (empty(GA_MEASUREMENT_ID) || GA_MEASUREMENT_ID === 'G-XXXXXXXXXX') { return false; } $postData = [ 'client_id' => $data['client_id'] ?? uniqid(), 'user_id' => $data['user_id'] ?? null, 'events' => [ [ 'name' => $data['event_name'], 'params' => $data['params'] ?? [] ] ] ]; $url = 'https://www.google-analytics.com/mp/collect'; $url .= '?measurement_id=' . GA_MEASUREMENT_ID; $url .= '&api_secret=' . GA_API_SECRET; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($postData), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_TIMEOUT => 10 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode >= 200 && $httpCode < 300; } function sendEventToFB($data) { if (empty(FB_PIXEL_ID) || empty(FB_ACCESS_TOKEN)) { return false; } $postData = [ 'data' => [ [ 'event_name' => $data['event_name'], 'event_time' => time(), 'action_source' => 'website', 'user_data' => $data['user_data'] ?? [], 'custom_data' => $data['custom_data'] ?? [] ] ] ]; $url = 'https://graph.facebook.com/v18.0/' . FB_PIXEL_ID . '/events'; $url .= '?access_token=' . FB_ACCESS_TOKEN; $ch = curl_init(); curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => json_encode($postData), CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_TIMEOUT => 10 ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); return $httpCode >= 200 && $httpCode < 300; } // ========================================== // STORAGE FUNCTIONS // ========================================== function storeLead($data) { $leadFile = __DIR__ . '/data/leads.json'; $dir = dirname($leadFile); if (!is_dir($dir)) { mkdir($dir, 0755, true); } $leads = []; if (file_exists($leadFile)) { $leads = json_decode(file_get_contents($leadFile), true) ?: []; } $lead = [ 'id' => uniqid('lead_', true), 'datum' => date('d.m.Y'), 'dienstleistung' => $data['service'] ?? 'Allgemein', 'status' => 'open', 'statusText' => 'Offen', 'description' => $data['description'] ?? '', 'name' => $data['name'] ?? '', 'contact' => $data['contact'] ?? '', 'organisation' => $data['organisation'] ?? '', 'timestamp' => date('c'), 'ip_address' => getClientIP() ]; array_unshift($leads, $lead); $leads = array_slice($leads, 0, 100); file_put_contents($leadFile, json_encode($leads, JSON_PRETTY_PRINT)); return $lead['id']; } function storeCookieConsent($data) { $consentFile = __DIR__ . '/data/cookie_consent.json'; $dir = dirname($consentFile); if (!is_dir($dir)) { mkdir($dir, 0755, true); } $consents = []; if (file_exists($consentFile)) { $consents = json_decode(file_get_contents($consentFile), true) ?: []; } $consent = [ 'id' => uniqid('consent_', true), 'timestamp' => date('c'), 'preferences' => $data['preferences'] ?? [], 'ip_address' => getClientIP(), 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '' ]; array_unshift($consents, $consent); $consents = array_slice($consents, 0, 1000); file_put_contents($consentFile, json_encode($consents, JSON_PRETTY_PRINT)); return $consent['id']; } function getCookieConsent() { $consentFile = __DIR__ . '/data/cookie_consent.json'; if (!file_exists($consentFile)) { return null; } $consents = json_decode(file_get_contents($consentFile), true) ?: []; return $consents[0] ?? null; } // ========================================== // REQUEST HANDLERS // ========================================== function handleContactForm($data) { $required = ['name', 'contact']; foreach ($required as $field) { if (empty($data[$field])) { sendResponse(false, "Pflichtfeld fehlt: {$field}", null, 400); } } $formData = [ 'name' => sanitizeInput($data['name']), 'organisation' => sanitizeInput($data['organisation'] ?? ''), 'contact' => sanitizeInput($data['contact']), 'service' => sanitizeInput($data['service'] ?? ''), 'budget' => sanitizeInput($data['budget'] ?? ''), 'description' => sanitizeInput($data['description'] ?? ''), 'timestamp' => date('c'), 'source' => 'contact_form', 'form_type' => 'offer', 'ip_address' => getClientIP() ]; // Store lead locally first $leadId = storeLead($formData); // Send to webhook $webhookResult = sendToWebhook($formData); // Always return success to user, but include webhook info in debug mode $debugData = DEBUG_MODE ? [ 'lead_id' => $leadId, 'webhook_result' => $webhookResult, 'webhook_url' => WEBHOOK_URL, 'form_data' => $formData ] : null; if ($webhookResult['success']) { sendResponse(true, 'Formular erfolgreich gesendet', $debugData); } else { // Still return success to user but log the webhook error error_log('Webhook failed but form was stored locally: ' . json_encode($webhookResult)); sendResponse(true, 'Formular erfolgreich gesendet (Webhook-Fehler protokolliert)', $debugData); } } function handleLoginForm($data) { $required = ['email', 'password']; foreach ($required as $field) { if (empty($data[$field])) { sendResponse(false, "Pflichtfeld fehlt: {$field}", null, 400); } } // Add your login logic here sendResponse(true, 'Login erfolgreich'); } function handleRegisterForm($data) { $required = ['name', 'email', 'password']; foreach ($required as $field) { if (empty($data[$field])) { sendResponse(false, "Pflichtfeld fehlt: {$field}", null, 400); } } // Add your registration logic here sendResponse(true, 'Registrierung erfolgreich'); } function handleLeadForm($data) { // Handle lead form submissions $leadId = storeLead($data); sendResponse(true, 'Lead gespeichert', ['lead_id' => $leadId]); } function handleCookieConsent($data) { $preferences = $data['preferences'] ?? []; $consentId = storeCookieConsent(['preferences' => $preferences]); // Load tracking scripts based on preferences $scripts = loadTrackingScripts($preferences); sendResponse(true, 'Cookie-Einstellungen gespeichert', [ 'consent_id' => $consentId, 'scripts' => $scripts ]); } function handleGetCookieConsent($data) { $consent = getCookieConsent(); if ($consent) { sendResponse(true, 'Cookie-Einstellungen gefunden', $consent); } else { sendResponse(false, 'Keine Cookie-Einstellungen gefunden', null, 404); } } function handleGetTrackingConfig($data) { $config = getTrackingConfig(); sendResponse(true, 'Tracking-Konfiguration', $config); } function handleTrackEvent($data) { $eventName = $data['event_name'] ?? ''; $eventData = $data['event_data'] ?? []; // Send to Google Analytics $gaSuccess = sendEventToGA([ 'event_name' => $eventName, 'params' => $eventData ]); // Send to Facebook $fbSuccess = sendEventToFB([ 'event_name' => $eventName, 'custom_data' => $eventData ]); sendResponse(true, 'Event gesendet', [ 'ga_success' => $gaSuccess, 'fb_success' => $fbSuccess ]); } // ========================================== // MAIN REQUEST HANDLER // ========================================== // Validate request method if ($_SERVER['REQUEST_METHOD'] !== 'POST') { sendResponse(false, 'Nur POST-Anfragen erlaubt', null, 405); } // Rate limiting $clientIP = getClientIP(); if (!checkRateLimit($clientIP)) { sendResponse(false, 'Zu viele Anfragen. Bitte später erneut versuchen.', null, 429); } // Get JSON input $jsonInput = file_get_contents('php://input'); $data = json_decode($jsonInput, true); if (!$data) { $data = $_POST; } if (!$data || empty($data)) { sendResponse(false, 'Keine Daten empfangen', null, 400); } $requestType = $data['type'] ?? 'contact'; try { switch ($requestType) { case 'contact': handleContactForm($data); break; case 'login': handleLoginForm($data); break; case 'register': handleRegisterForm($data); break; case 'lead': handleLeadForm($data); break; case 'cookie_consent': handleCookieConsent($data); break; case 'get_cookie_consent': handleGetCookieConsent($data); break; case 'get_tracking_config': handleGetTrackingConfig($data); break; case 'track_event': handleTrackEvent($data); break; default: sendResponse(false, 'Ungültiger Anfragetyp', null, 400); } } catch (Exception $e) { error_log('API Error: ' . $e->getMessage()); error_log('Stack trace: ' . $e->getTraceAsString()); sendResponse(false, 'Interner Serverfehler', null, 500); } ?>