797 lines
25 KiB
PHP
797 lines
25 KiB
PHP
<?php
|
|
/**
|
|
* Profice Web API Handler - Centralized Configuration
|
|
* ALL SENSITIVE DATA STORED HERE - NOT VISIBLE TO CLIENT
|
|
* Handles webhooks, API, tokens, cookie consent, tracking
|
|
*/
|
|
|
|
// ==========================================
|
|
// SECURITY HEADERS
|
|
// ==========================================
|
|
header('Content-Type: application/json');
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('X-Frame-Options: DENY');
|
|
header('X-XSS-Protection: 1; mode=block');
|
|
header('Referrer-Policy: strict-origin-when-cross-origin');
|
|
|
|
// CORS - Restrict to your domain in production
|
|
$allowedOrigins = [
|
|
'https://profice.de',
|
|
'https://www.profice.de',
|
|
'http://localhost',
|
|
'http://127.0.0.1',
|
|
'https://staging.profice.de'
|
|
];
|
|
|
|
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
|
|
if (in_array($origin, $allowedOrigins)) {
|
|
header("Access-Control-Allow-Origin: $origin");
|
|
} else {
|
|
header('Access-Control-Allow-Origin: *'); // Development fallback
|
|
}
|
|
|
|
header('Access-Control-Allow-Methods: POST, OPTIONS');
|
|
header('Access-Control-Allow-Headers: Content-Type');
|
|
header('Access-Control-Max-Age: 86400');
|
|
|
|
// Handle preflight OPTIONS request
|
|
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
|
http_response_code(200);
|
|
exit();
|
|
}
|
|
|
|
// ==========================================
|
|
// SENSITIVE CONFIGURATION - HIDDEN FROM CLIENT
|
|
// ==========================================
|
|
|
|
// Environment
|
|
define('USE_PRODUCTION', true); // Use production webhook for live server
|
|
define('DEBUG_MODE', true); // Enable debug temporarily to see errors
|
|
|
|
// N8N Webhooks
|
|
define('WEBHOOK_TEST', 'https://n8n.profice.de/webhook-test/d94ef798-3f43-46dd-8207-1e335e64518f');
|
|
define('WEBHOOK_PROD', 'https://n8n.profice.de/webhook/d94ef798-3f43-46dd-8207-1e335e64518f');
|
|
define('WEBHOOK_URL', USE_PRODUCTION ? WEBHOOK_PROD : WEBHOOK_TEST);
|
|
|
|
// KI Chat Webhook
|
|
define('KI_CHAT_WEBHOOK_TEST', 'https://n8n.profice.de/webhook-test/8a25bce2-ff83-4676-a3a2-a0e1174fcffe');
|
|
define('KI_CHAT_WEBHOOK_PROD', 'https://n8n.profice.de/webhook/8a25bce2-ff83-4676-a3a2-a0e1174fcffe');
|
|
define('KI_CHAT_WEBHOOK_URL', USE_PRODUCTION ? KI_CHAT_WEBHOOK_PROD : KI_CHAT_WEBHOOK_TEST);
|
|
|
|
// Google Analytics
|
|
define('GA_MEASUREMENT_ID', 'G-XXXXXXXXXX'); // Replace with your actual ID
|
|
define('GA_API_SECRET', ''); // For server-side tracking
|
|
|
|
// Google Tag Manager
|
|
define('GTM_CONTAINER_ID', 'GTM-XXXXXXX'); // Replace with your actual ID
|
|
|
|
// Facebook Pixel
|
|
define('FB_PIXEL_ID', ''); // Replace with your actual ID
|
|
define('FB_ACCESS_TOKEN', ''); // For Conversions API
|
|
|
|
// Google Ads
|
|
define('GADS_CONVERSION_ID', 'AW-XXXXXXXXXX');
|
|
define('GADS_CONVERSION_LABEL', '');
|
|
|
|
// LinkedIn Insight
|
|
define('LINKEDIN_PARTNER_ID', '');
|
|
|
|
// API Keys
|
|
define('API_SECRET_KEY', 'your-secret-key-here'); // For API authentication
|
|
|
|
// Rate Limiting
|
|
define('RATE_LIMIT_REQUESTS', 100);
|
|
define('RATE_LIMIT_WINDOW', 3600); // 1 hour
|
|
|
|
// ==========================================
|
|
// HELPER FUNCTIONS
|
|
// ==========================================
|
|
|
|
function sendResponse($success, $message, $data = null, $statusCode = 200) {
|
|
http_response_code($statusCode);
|
|
$response = [
|
|
'success' => $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;
|
|
$jsonData = json_encode($data);
|
|
|
|
// Log the attempt
|
|
error_log("Webhook attempt - URL: " . $url . " - Data length: " . strlen($jsonData));
|
|
|
|
// Try cURL first if available
|
|
if (function_exists('curl_init')) {
|
|
$ch = curl_init();
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_URL => $url,
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => $jsonData,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Content-Type: application/json',
|
|
'Accept: application/json',
|
|
'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
|
|
'Origin: https://staging.profice.de',
|
|
'Referer: https://staging.profice.de/'
|
|
],
|
|
CURLOPT_TIMEOUT => 30,
|
|
CURLOPT_CONNECTTIMEOUT => 10,
|
|
CURLOPT_SSL_VERIFYPEER => false,
|
|
CURLOPT_SSL_VERIFYHOST => 0,
|
|
CURLOPT_FOLLOWLOCATION => true,
|
|
CURLOPT_MAXREDIRS => 3
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$error = curl_error($ch);
|
|
$errno = curl_errno($ch);
|
|
curl_close($ch);
|
|
|
|
error_log("Webhook cURL response - HTTP: $httpCode, Error: $error, Response: " . substr($response, 0, 500));
|
|
|
|
if (!$error && $httpCode > 0) {
|
|
return [
|
|
'success' => $httpCode >= 200 && $httpCode < 300,
|
|
'http_code' => $httpCode,
|
|
'response' => $response,
|
|
'error' => $error,
|
|
'method' => 'curl'
|
|
];
|
|
}
|
|
|
|
error_log("Webhook cURL failed [$errno]: $error - trying file_get_contents fallback");
|
|
}
|
|
|
|
// Fallback to file_get_contents if cURL fails or unavailable
|
|
$context = stream_context_create([
|
|
'http' => [
|
|
'method' => 'POST',
|
|
'header' => "Content-Type: application/json\r\nAccept: application/json\r\nUser-Agent: Profice-Web-API/2.0\r\n",
|
|
'content' => $jsonData,
|
|
'timeout' => 30,
|
|
'ignore_errors' => true
|
|
],
|
|
'ssl' => [
|
|
'verify_peer' => false,
|
|
'verify_peer_name' => false,
|
|
'allow_self_signed' => true
|
|
]
|
|
]);
|
|
|
|
$response = @file_get_contents($url, false, $context);
|
|
|
|
// Get HTTP response code from headers
|
|
$httpCode = 0;
|
|
if (isset($http_response_header) && is_array($http_response_header)) {
|
|
foreach ($http_response_header as $header) {
|
|
if (preg_match('/^HTTP\/\d+\.?\d*\s+(\d+)/', $header, $matches)) {
|
|
$httpCode = (int)$matches[1];
|
|
}
|
|
}
|
|
}
|
|
|
|
error_log("Webhook file_get_contents response - HTTP: $httpCode, Response: " . substr($response ?: '', 0, 500));
|
|
|
|
if ($response !== false) {
|
|
return [
|
|
'success' => $httpCode >= 200 && $httpCode < 300,
|
|
'http_code' => $httpCode,
|
|
'response' => $response,
|
|
'error' => null,
|
|
'method' => 'file_get_contents'
|
|
];
|
|
}
|
|
|
|
$lastError = error_get_last();
|
|
error_log("Webhook all methods failed - Last error: " . json_encode($lastError));
|
|
|
|
return [
|
|
'success' => false,
|
|
'error' => $lastError['message'] ?? 'All HTTP methods failed',
|
|
'errno' => 0,
|
|
'response' => null,
|
|
'method' => 'none'
|
|
];
|
|
}
|
|
|
|
// ==========================================
|
|
// 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 Offer webhook (contact form)
|
|
$webhookResult = sendToWebhook($formData, WEBHOOK_URL);
|
|
|
|
// 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 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 handleChatMessage($data) {
|
|
$message = sanitizeInput($data['message'] ?? '');
|
|
$sessionId = sanitizeInput($data['session_id'] ?? uniqid('chat_'));
|
|
|
|
if (empty($message)) {
|
|
sendResponse(false, 'Nachricht darf nicht leer sein', null, 400);
|
|
}
|
|
|
|
// Format data for n8n webhook - simplified format that n8n expects
|
|
$chatData = [
|
|
'chatInput' => $message, // Main field for n8n AI Agent
|
|
'session_id' => $sessionId,
|
|
'timestamp' => date('c'),
|
|
'source' => 'website_chat'
|
|
];
|
|
|
|
// Log the outgoing request for debugging
|
|
error_log('KI Chat Request - URL: ' . KI_CHAT_WEBHOOK_URL . ' - Data: ' . json_encode($chatData));
|
|
|
|
// Send to KI Chat webhook
|
|
$webhookResult = sendToWebhook($chatData, KI_CHAT_WEBHOOK_URL);
|
|
|
|
// Log the response for debugging
|
|
error_log('KI Chat Response: ' . json_encode($webhookResult));
|
|
|
|
$debugData = DEBUG_MODE ? [
|
|
'session_id' => $sessionId,
|
|
'webhook_result' => $webhookResult,
|
|
'webhook_url' => KI_CHAT_WEBHOOK_URL
|
|
] : null;
|
|
|
|
if ($webhookResult['success']) {
|
|
// Try to parse response from webhook - handle various n8n response formats
|
|
$response = json_decode($webhookResult['response'], true);
|
|
$aiResponse = null;
|
|
|
|
// Check common n8n response field names
|
|
if ($response) {
|
|
$aiResponse = $response['message']
|
|
?? $response['output']
|
|
?? $response['text']
|
|
?? $response['response']
|
|
?? $response['answer']
|
|
?? $response['result']
|
|
?? (is_array($response) && isset($response[0]['output']) ? $response[0]['output'] : null)
|
|
?? (is_array($response) && isset($response[0]['message']) ? $response[0]['message'] : null)
|
|
?? null;
|
|
}
|
|
|
|
// If still no response, use the raw response if it's a string
|
|
if (!$aiResponse && is_string($webhookResult['response']) && !empty($webhookResult['response'])) {
|
|
$aiResponse = $webhookResult['response'];
|
|
}
|
|
|
|
// Fallback message
|
|
if (!$aiResponse) {
|
|
$aiResponse = 'Vielen Dank für Ihre Nachricht. Ich melde mich so schnell wie möglich bei Ihnen.';
|
|
}
|
|
|
|
sendResponse(true, 'Nachricht gesendet', array_merge($debugData ?? [], [
|
|
'session_id' => $sessionId,
|
|
'ai_response' => $aiResponse,
|
|
'timestamp' => date('c')
|
|
]));
|
|
} else {
|
|
// Log detailed error for debugging
|
|
error_log('Chat webhook failed - URL: ' . KI_CHAT_WEBHOOK_URL . ' - Result: ' . json_encode($webhookResult));
|
|
|
|
// Return a friendly message but still allow chat to work with fallback
|
|
$fallbackResponse = 'Vielen Dank für Ihre Nachricht. Unser Team wird sich in Kürze bei Ihnen melden.';
|
|
sendResponse(true, 'Nachricht empfangen', array_merge($debugData ?? [], [
|
|
'session_id' => $sessionId,
|
|
'ai_response' => $fallbackResponse,
|
|
'timestamp' => date('c'),
|
|
'fallback' => true
|
|
]));
|
|
}
|
|
}
|
|
|
|
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';
|
|
|
|
// Debug endpoint to test webhook connectivity
|
|
if ($requestType === 'test_webhook') {
|
|
// Test with simple curl command
|
|
$testData = ['chatInput' => 'Test message', 'timestamp' => date('c')];
|
|
$result = sendToWebhook($testData, KI_CHAT_WEBHOOK_URL);
|
|
|
|
// Also test raw curl
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, KI_CHAT_WEBHOOK_URL);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_POST, true);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($testData));
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
|
|
$rawResponse = curl_exec($ch);
|
|
$rawHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$rawError = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
sendResponse(true, 'Webhook test completed', [
|
|
'webhook_url' => KI_CHAT_WEBHOOK_URL,
|
|
'result' => $result,
|
|
'raw_curl' => [
|
|
'response' => $rawResponse,
|
|
'http_code' => $rawHttpCode,
|
|
'error' => $rawError
|
|
],
|
|
'curl_available' => function_exists('curl_init'),
|
|
'allow_url_fopen' => ini_get('allow_url_fopen')
|
|
]);
|
|
}
|
|
|
|
try {
|
|
switch ($requestType) {
|
|
case 'contact':
|
|
handleContactForm($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;
|
|
case 'chat':
|
|
handleChatMessage($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);
|
|
}
|
|
?>
|