sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } if (empty($input['url']) || empty($input['events'])) { $this->sendError('URL and events are required', 400); } if (!filter_var($input['url'], FILTER_VALIDATE_URL)) { $this->sendError('Invalid URL', 400); } try { // Webhook erstellen $stmt = $this->conn->prepare(' INSERT INTO ws_webhook (url, events, secret, active, created_at) VALUES (?, ?, ?, 1, NOW()) '); $secret = bin2hex(random_bytes(32)); $stmt->execute([ $input['url'], json_encode($input['events']), $secret ]); $webhookId = $this->conn->lastInsertId(); $this->sendResponse([ 'success' => true, 'data' => [ 'webhook_id' => $webhookId, 'secret' => $secret ], 'message' => 'Webhook registered successfully' ]); } catch (Exception $e) { $this->sendError('Failed to register webhook: ' . $e->getMessage(), 500); } } /** * Webhook-Liste abrufen */ public function getWebhooks() { try { $stmt = $this->conn->prepare(' SELECT id, url, events, active, created_at, last_triggered FROM ws_webhook ORDER BY created_at DESC '); $stmt->execute(); $webhooks = $stmt->fetchAllAssociative(); $this->sendResponse([ 'success' => true, 'data' => $webhooks ]); } catch (Exception $e) { $this->sendError('Failed to fetch webhooks: ' . $e->getMessage(), 500); } } /** * Webhook aktualisieren */ public function updateWebhook($webhookId) { if ($_SERVER['REQUEST_METHOD'] !== 'PUT') { $this->sendError('Method not allowed', 405); } $input = json_decode(file_get_contents('php://input'), true); if (!$input) { $this->sendError('Invalid JSON input', 400); } try { $updateFields = []; $params = []; if (isset($input['url'])) { if (!filter_var($input['url'], FILTER_VALIDATE_URL)) { $this->sendError('Invalid URL', 400); } $updateFields[] = 'url = ?'; $params[] = $input['url']; } if (isset($input['events'])) { $updateFields[] = 'events = ?'; $params[] = json_encode($input['events']); } if (isset($input['active'])) { $updateFields[] = 'active = ?'; $params[] = $input['active'] ? 1 : 0; } if (empty($updateFields)) { $this->sendError('No fields to update', 400); } $updateFields[] = 'updated_at = NOW()'; $params[] = $webhookId; $sql = 'UPDATE ws_webhook SET ' . implode(', ', $updateFields) . ' WHERE id = ?'; $stmt = $this->conn->prepare($sql); $stmt->execute($params); if ($stmt->rowCount() === 0) { $this->sendError('Webhook not found', 404); } $this->sendResponse([ 'success' => true, 'message' => 'Webhook updated successfully' ]); } catch (Exception $e) { $this->sendError('Failed to update webhook: ' . $e->getMessage(), 500); } } /** * Webhook löschen */ public function deleteWebhook($webhookId) { if ($_SERVER['REQUEST_METHOD'] !== 'DELETE') { $this->sendError('Method not allowed', 405); } try { $stmt = $this->conn->prepare('DELETE FROM ws_webhook WHERE id = ?'); $stmt->execute([$webhookId]); if ($stmt->rowCount() === 0) { $this->sendError('Webhook not found', 404); } $this->sendResponse([ 'success' => true, 'message' => 'Webhook deleted successfully' ]); } catch (Exception $e) { $this->sendError('Failed to delete webhook: ' . $e->getMessage(), 500); } } /** * Event auslösen */ public function triggerEvent($event, $data = []) { try { // Aktive Webhooks für dieses Event abrufen $stmt = $this->conn->prepare(' SELECT id, url, secret, events FROM ws_webhook WHERE active = 1 '); $stmt->execute(); $webhooks = $stmt->fetchAllAssociative(); $triggeredCount = 0; $failedCount = 0; foreach ($webhooks as $webhook) { $events = json_decode($webhook['events'], true) ?: []; if (in_array($event, $events) || in_array('*', $events)) { try { $this->sendWebhook($webhook, $event, $data); $triggeredCount++; // Last triggered aktualisieren $stmt = $this->conn->prepare(' UPDATE ws_webhook SET last_triggered = NOW() WHERE id = ? '); $stmt->execute([$webhook['id']]); } catch (Exception $e) { $failedCount++; $this->logWebhookError($webhook['id'], $event, $e->getMessage()); } } } return [ 'triggered' => $triggeredCount, 'failed' => $failedCount ]; } catch (Exception $e) { throw new Exception('Failed to trigger event: ' . $e->getMessage()); } } /** * Webhook senden */ private function sendWebhook($webhook, $event, $data) { $payload = [ 'event' => $event, 'timestamp' => time(), 'data' => $data ]; $jsonPayload = json_encode($payload); $signature = hash_hmac('sha256', $jsonPayload, $webhook['secret']); $headers = [ 'Content-Type: application/json', 'Content-Length: ' . strlen($jsonPayload), 'X-Webhook-Signature: ' . $signature, 'User-Agent: Webshop-System/1.0' ]; $context = stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => implode("\r\n", $headers), 'content' => $jsonPayload, 'timeout' => 30 ] ]); $response = file_get_contents($webhook['url'], false, $context); if ($response === false) { throw new Exception('Failed to send webhook to ' . $webhook['url']); } $httpCode = $this->getHttpResponseCode($http_response_header); if ($httpCode < 200 || $httpCode >= 300) { throw new Exception('Webhook returned HTTP ' . $httpCode); } } /** * HTTP-Response-Code extrahieren */ private function getHttpResponseCode($headers) { if (empty($headers)) { return 0; } $statusLine = $headers[0]; preg_match('/HTTP\/\d\.\d\s+(\d+)/', $statusLine, $matches); return isset($matches[1]) ? intval($matches[1]) : 0; } /** * Webhook-Fehler loggen */ private function logWebhookError($webhookId, $event, $error) { try { $stmt = $this->conn->prepare(' INSERT INTO ws_webhook_log (webhook_id, event, error, created_at) VALUES (?, ?, ?, NOW()) '); $stmt->execute([$webhookId, $event, $error]); } catch (Exception $e) { // Ignore logging errors } } /** * Webhook-Logs abrufen */ public function getWebhookLogs($webhookId = null) { try { if ($webhookId) { $stmt = $this->conn->prepare(' SELECT * FROM ws_webhook_log WHERE webhook_id = ? ORDER BY created_at DESC LIMIT 100 '); $stmt->execute([$webhookId]); } else { $stmt = $this->conn->prepare(' SELECT wl.*, w.url FROM ws_webhook_log wl LEFT JOIN ws_webhook w ON wl.webhook_id = w.id ORDER BY wl.created_at DESC LIMIT 100 '); $stmt->execute(); } $logs = $stmt->fetchAllAssociative(); $this->sendResponse([ 'success' => true, 'data' => $logs ]); } catch (Exception $e) { $this->sendError('Failed to fetch webhook logs: ' . $e->getMessage(), 500); } } /** * Event-Test */ public function testWebhook($webhookId) { if ($_SERVER['REQUEST_METHOD'] !== 'POST') { $this->sendError('Method not allowed', 405); } try { $stmt = $this->conn->prepare(' SELECT * FROM ws_webhook WHERE id = ? AND active = 1 '); $stmt->execute([$webhookId]); $webhook = $stmt->fetchAssociative(); if (!$webhook) { $this->sendError('Webhook not found', 404); } $testData = [ 'test' => true, 'message' => 'This is a test webhook', 'timestamp' => date('Y-m-d H:i:s') ]; $this->sendWebhook($webhook, 'test', $testData); $this->sendResponse([ 'success' => true, 'message' => 'Test webhook sent successfully' ]); } catch (Exception $e) { $this->sendError('Failed to send test webhook: ' . $e->getMessage(), 500); } } /** * Verfügbare Events abrufen */ public function getAvailableEvents() { $events = [ 'order.created' => 'Bestellung erstellt', 'order.updated' => 'Bestellung aktualisiert', 'order.cancelled' => 'Bestellung storniert', 'order.shipped' => 'Bestellung versendet', 'order.delivered' => 'Bestellung geliefert', 'customer.registered' => 'Kunde registriert', 'customer.updated' => 'Kunde aktualisiert', 'product.created' => 'Produkt erstellt', 'product.updated' => 'Produkt aktualisiert', 'product.deleted' => 'Produkt gelöscht', 'review.created' => 'Bewertung erstellt', 'review.updated' => 'Bewertung aktualisiert', 'newsletter.sent' => 'Newsletter gesendet', 'stock.low' => 'Lagerbestand niedrig', 'stock.out' => 'Lagerbestand aufgebraucht', 'payment.received' => 'Zahlung erhalten', 'payment.failed' => 'Zahlung fehlgeschlagen', 'test' => 'Test-Event' ]; $this->sendResponse([ 'success' => true, 'data' => $events ]); } /** * Webhook-Statistiken */ public function getWebhookStats() { try { // Webhook-Statistiken $stmt = $this->conn->prepare(' SELECT COUNT(*) as total_webhooks, COUNT(CASE WHEN active = 1 THEN 1 END) as active_webhooks, COUNT(CASE WHEN active = 0 THEN 1 END) as inactive_webhooks FROM ws_webhook '); $stmt->execute(); $webhookStats = $stmt->fetchAssociative(); // Log-Statistiken $stmt = $this->conn->prepare(' SELECT COUNT(*) as total_logs, COUNT(CASE WHEN error IS NOT NULL THEN 1 END) as error_logs, COUNT(CASE WHEN error IS NULL THEN 1 END) as success_logs FROM ws_webhook_log '); $stmt->execute(); $logStats = $stmt->fetchAssociative(); // Event-Statistiken $stmt = $this->conn->prepare(' SELECT event, COUNT(*) as count FROM ws_webhook_log GROUP BY event ORDER BY count DESC LIMIT 10 '); $stmt->execute(); $eventStats = $stmt->fetchAllAssociative(); $this->sendResponse([ 'success' => true, 'data' => [ 'webhooks' => $webhookStats, 'logs' => $logStats, 'events' => $eventStats ] ]); } catch (Exception $e) { $this->sendError('Failed to get webhook stats: ' . $e->getMessage(), 500); } } }