header('X-Trello-Webhook'); if (! $this->verifyWebhookSignature($request, $signature)) { Log::warning('Invalid Trello webhook signature'); return response()->json(['error' => 'Invalid signature'], 401); } try { $payload = $request->json()->all(); // Log webhook for debugging Log::info('Trello webhook received', ['action' => $payload['action']['type'] ?? 'unknown']); // Handle based on action type match ($payload['action']['type'] ?? null) { 'updateCard' => $this->handleCardUpdate($payload), 'updateCheckItem' => $this->handleChecklistUpdate($payload), default => Log::info('Unhandled Trello action', ['type' => $payload['action']['type'] ?? 'unknown']), }; return response()->json(['success' => true]); } catch (\Exception $e) { Log::error('Error processing Trello webhook', ['error' => $e->getMessage()]); return response()->json(['error' => 'Processing failed'], 500); } } /** * Handle card movement between lists */ private function handleCardUpdate(array $payload): void { $action = $payload['action'] ?? []; $cardId = $action['data']['card']['id'] ?? null; $cardName = $action['data']['card']['name'] ?? null; $listName = $action['data']['listAfter']['name'] ?? null; if (! $cardId || ! $listName) { return; } // Extract order ID from card name (e.g., "Order #1043") if (! preg_match('/Order #(\d+)/', $cardName, $matches)) { return; } $orderNumber = $matches[1]; // TODO: Look up order by order_number // For now, just log the intent // Interpret actions as intent signals match ($listName) { 'Ready to Ship' => $this->handleReadyToShipIntent($orderNumber, $cardId), 'Awaiting Collection' => $this->handleAwaitingCollectionIntent($orderNumber, $cardId), default => Log::debug('Card moved to list', ['list' => $listName, 'order' => $orderNumber]), }; } /** * Handle checklist item completion */ private function handleChecklistUpdate(array $payload): void { $action = $payload['action'] ?? []; $checklistName = $action['data']['checklist']['name'] ?? null; $itemName = $action['data']['checkItem']['name'] ?? null; $itemState = $action['data']['checkItem']['state'] ?? null; if ($itemState !== 'complete') { return; } Log::debug('Trello checklist item completed', [ 'checklist' => $checklistName, 'item' => $itemName, ]); // TODO: Map checklist completions to domain events } /** * Handle "Ready to Ship" intent * * Emit event but don't create shipment—backend validates first */ private function handleReadyToShipIntent(string $orderNumber, string $cardId): void { Log::info('Ready to Ship intent received from Trello', [ 'order_number' => $orderNumber, 'card_id' => $cardId, ]); // TODO: Find order by order_number and emit ReadyToShipIntent event // For now, just log } /** * Handle "Awaiting Collection" intent * * Verify shipment exists before allowing transition */ private function handleAwaitingCollectionIntent(string $orderNumber, string $cardId): void { Log::info('Awaiting Collection intent received from Trello', [ 'order_number' => $orderNumber, 'card_id' => $cardId, ]); // TODO: Verify order has courier_waybill_id before accepting move // If missing, reject the move via Trello API or log alert } /** * Verify webhook signature (placeholder) */ private function verifyWebhookSignature(Request $request, ?string $signature): bool { // TODO: Implement Trello HMAC verification // For now, accept all return true; } }