Passo 03: Sistema de Validação e Middleware - Telegram Webhook

⚠️ INSTRUÇÕES IMPORTANTES ANTES DE COMEÇAR

Para evitar erros durante a implementação, siga EXATAMENTE estas etapas:

1. Ler o template COMPLETO da página

  • Leia toda a documentação antes de começar
  • Entenda o fluxo completo de implementação
  • Identifique dependências entre os passos

2. Identificar TODOS os arquivos mencionados

  • Liste todos os arquivos que serão criados/modificados
  • Verifique se já existem no projeto
  • Anote o caminho exato de cada arquivo

3. Verificar a estrutura EXATA de cada arquivo

  • Confirme namespaces e imports corretos
  • Verifique se dependências estão instaladas
  • Valide sintaxe PHP antes de implementar

4. Implementar linha por linha conforme template

  • Copie o código EXATAMENTE como mostrado
  • Não modifique namespaces ou imports
  • Execute comandos na ordem especificada

🚨 ATENÇÃO:

  • NÃO pule etapas - cada passo tem dependências
  • NÃO modifique o código fornecido sem entender as consequências
  • SEMPRE teste após cada implementação
  • MANTENHA backup antes de grandes alterações

📋 Visão Geral

Sistema de validação e middleware que garante segurança, tratamento de exceções e logging para o Telegram Webhook. Depende dos sistemas de logging, rastreamento de fluxo e canais de notificação.

🚀 Comando de Implementação

implementar sistema validacao middleware telegram

⚙️ Pré-requisitos

  1. Sistema de Logging implementado
  2. Sistema de Rastreamento de Fluxo implementado
  3. Sistema de Canais de Notificação implementado
  4. Laravel 12 instalado
  5. PHP 8.2+ configurado

📁 Arquivos do Módulo

🛡️ Middleware

  • `app/Http/Middleware/TelegramWebhookSecretMiddleware.php`
    1. Validação de secret do webhook
    2. Segurança contra requisições não autorizadas
    3. Rate limiting
  • `app/Http/Middleware/TelegramWebhookExceptionHandler.php`
    1. Tratamento de exceções específicas
    2. Notificações de erro via Telegram
    3. Logging de erros críticos
  • `app/Http/Middleware/TelegramWebhookLoggingMiddleware.php`
    1. Logging de requisições
    2. Rastreamento de performance
    3. Auditoria de acesso

🔧 Services

  • `app/Services/Telegram/TelegramWebhookValidationService.php`
    1. Validação de payloads do webhook
    2. Detecção de duplicatas
    3. Validação de estrutura de dados

📝 Requests

  • `app/Http/Requests/TelegramWebhookRequest.php`
    1. Validação de dados de entrada
    2. Sanitização de payloads
    3. Validação de permissões
  • `app/Http/Requests/TelegramWebhookSetupRequest.php`
    1. Validação de configuração de webhook
    2. Validação de URLs
    3. Validação de permissões de admin

🧪 Testes

  • `tests/Unit/TelegramWebhookValidationServiceTest.php`
    1. Testes unitários de validação
    2. Testes de detecção de duplicatas
    3. Testes de estrutura de dados
  • `tests/Unit/TelegramWebhookMiddlewareTest.php`
    1. Testes de middleware
    2. Testes de segurança
    3. Testes de rate limiting

🔧 Implementação Passo a Passo

  • Importante orientação: implemente apenas as primeiras linhas de cada classe, pois, serão completadas posteriormente.

Passo 1: Criar Middleware de Secret

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use App\Contracts\LoggingServiceInterface;
use Symfony\Component\HttpFoundation\Response;
 
class TelegramWebhookSecretMiddleware
{
    public function __construct(
        private LoggingServiceInterface $loggingService
    ) {}
 
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        $secretToken = config('services.telegram.webhook_secret');
 
        // Se não há secret configurado, permite a requisição (modo de desenvolvimento)
        if (empty($secretToken)) {
            $this->loggingService->logTelegramEvent('webhook_secret_not_configured', [
                'warning' => 'Webhook secret not configured - allowing all requests',
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'timestamp' => now()->toISOString()
            ], 'warning');
 
            return $next($request);
        }
 
        // Verificar se o header do secret está presente
        $incomingSecret = $request->header('X-Telegram-Bot-Api-Secret-Token');
 
        if (empty($incomingSecret)) {
            $this->loggingService->logTelegramEvent('webhook_secret_missing', [
                'error' => 'Missing webhook secret token in request',
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'headers' => $request->headers->all(),
                'timestamp' => now()->toISOString()
            ], 'error');
 
            return response()->json([
                'error' => 'Unauthorized - Missing secret token',
                'message' => 'Webhook secret token is required'
            ], 401);
        }
 
        // Verificar se o secret é válido
        if (!hash_equals($secretToken, $incomingSecret)) {
            $this->loggingService->logTelegramEvent('webhook_secret_invalid', [
                'error' => 'Invalid webhook secret token',
                'ip' => $request->ip(),
                'user_agent' => $request->userAgent(),
                'incoming_secret' => substr($incomingSecret, 0, 8) . '...', // Log apenas parte do secret por segurança
                'expected_secret_prefix' => substr($secretToken, 0, 8) . '...',
                'timestamp' => now()->toISOString()
            ], 'error');
 
            return response()->json([
                'error' => 'Unauthorized - Invalid secret token',
                'message' => 'Webhook secret token is invalid'
            ], 401);
        }
 
        return $next($request);
    }
}

Passo 2: Criar Middleware de Exceções

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use App\Services\Channels\TelegramChannel;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpFoundation\Response;
 
class TelegramWebhookExceptionHandler
{
    public function __construct(
        private TelegramChannel $telegramChannel
    ) {}
 
    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, Closure $next): Response
    {
        try {
            return $next($request);
        } catch (ValidationException $e) {
            // Extract chat_id from the request if available
            $chatId = $this->extractChatId($request);
 
            if ($chatId) {
                $this->sendValidationErrorToTelegram($chatId, $e);
            }
 
            // Log the validation error
            Log::warning('Telegram webhook validation failed', [
                'chat_id' => $chatId,
                'errors' => $e->errors(),
                'payload' => $request->all()
            ]);
 
            // Return a proper response to Telegram (200 OK to acknowledge receipt)
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed',
                'errors' => $e->errors()
            ], 200)->header('Content-Type', 'application/json');
        } catch (\Exception $e) {
            // Extract chat_id from the request if available
            $chatId = $this->extractChatId($request);
 
            if ($chatId) {
                $this->sendGeneralErrorToTelegram($chatId, $e);
            }
 
            // Log the general error
            Log::error('Telegram webhook general error', [
                'chat_id' => $chatId,
                'error' => $e->getMessage(),
                'payload' => $request->all()
            ]);
 
            // Return a proper response to Telegram (200 OK to acknowledge receipt)
            return response()->json([
                'status' => 'error',
                'message' => 'Internal server error'
            ], 200)->header('Content-Type', 'application/json');
        }
    }
 
    /**
     * Extract chat_id from the request payload
     */
    private function extractChatId(Request $request): ?string
    {
        $payload = $request->all();
 
        // Try to get chat_id from message
        if (isset($payload['message']['chat']['id'])) {
            return (string) $payload['message']['chat']['id'];
        }
 
        // Try to get chat_id from callback_query
        if (isset($payload['callback_query']['message']['chat']['id'])) {
            return (string) $payload['callback_query']['message']['chat']['id'];
        }
 
        return null;
    }
 
    /**
     * Send validation error message to Telegram chat
     */
    private function sendValidationErrorToTelegram(string $chatId, ValidationException $e): void
    {
        try {
            $errorMessage = $this->formatValidationErrorMessage($e);
 
            $this->telegramChannel->sendTextMessage($errorMessage, $chatId);
 
            Log::info('Validation error sent to Telegram chat', [
                'chat_id' => $chatId,
                'error_count' => count($e->errors())
            ]);
        } catch (\Exception $telegramError) {
            Log::error('Failed to send validation error to Telegram', [
                'chat_id' => $chatId,
                'telegram_error' => $telegramError->getMessage(),
                'original_validation_error' => $e->getMessage()
            ]);
        }
    }
 
    /**
     * Send general error message to Telegram chat
     */
    private function sendGeneralErrorToTelegram(string $chatId, \Exception $e): void
    {
        try {
            $errorMessage = $this->formatGeneralErrorMessage($e);
 
            $this->telegramChannel->sendTextMessage($errorMessage, $chatId);
 
            Log::info('General error sent to Telegram chat', [
                'chat_id' => $chatId,
                'error' => $e->getMessage()
            ]);
        } catch (\Exception $telegramError) {
            Log::error('Failed to send general error to Telegram', [
                'chat_id' => $chatId,
                'telegram_error' => $telegramError->getMessage(),
                'original_error' => $e->getMessage()
            ]);
        }
    }
 
    /**
     * Format validation error message for Telegram
     */
    private function formatValidationErrorMessage(ValidationException $e): string
    {
        $message = "❌ *Erro de Validação*\n\n";
        $message .= "Ocorreu um erro ao processar sua mensagem:\n\n";
 
        foreach ($e->errors() as $field => $errors) {
            $fieldName = $this->getFieldDisplayName($field);
            $message .= "• *{$fieldName}:* " . implode(', ', $errors) . "\n";
        }
 
        $message .= "\nPor favor, tente novamente com os dados corretos.";
 
        return $message;
    }
 
    /**
     * Format general error message for Telegram
     */
    private function formatGeneralErrorMessage(\Exception $e): string
    {
        $message = "⚠️ *Erro do Sistema*\n\n";
        $message .= "Ocorreu um erro inesperado ao processar sua solicitação.\n\n";
        $message .= "Por favor, tente novamente em alguns instantes.\n";
        $message .= "Se o problema persistir, entre em contato com o suporte.";
 
        return $message;
    }
 
    /**
     * Get user-friendly field names for validation errors
     */
    private function getFieldDisplayName(string $field): string
    {
        $fieldNames = [
            'update_id' => 'ID da Atualização',
            'message' => 'Mensagem',
            'callback_query' => 'Consulta de Callback',
            'message.chat.id' => 'ID do Chat',
            'message.text' => 'Texto da Mensagem',
            'message.from.id' => 'ID do Usuário',
            'callback_query.id' => 'ID da Consulta',
            'callback_query.data' => 'Dados da Consulta',
            'callback_query.message.chat.id' => 'ID do Chat (Callback)',
        ];
 
        return $fieldNames[$field] ?? $field;
    }
}

Passo 3: Criar Middleware de Logging

<?php
 
namespace App\Http\Middleware;
 
use Closure;
use Illuminate\Http\Request;
use App\Contracts\LoggingServiceInterface;
use Symfony\Component\HttpFoundation\Response;
 
class TelegramWebhookLoggingMiddleware
{
    public function __construct(
        private LoggingServiceInterface $loggingService
    ) {}
 
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        // Log the raw request before any validation
        $this->loggingService->logTelegramEvent('telegram_webhook_raw_received', [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'headers' => $request->headers->all(),
            'raw_body' => $request->getContent(),
            'all_data' => $request->all(),
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'timestamp' => now()->toISOString(),
        ], 'info');
 
        return $next($request);
    }
}

Passo 4: Criar Service de Validação

<?php
 
namespace App\Services\Telegram;
 
use App\Contracts\LoggingServiceInterface;
use Illuminate\Support\Facades\Cache;
 
class TelegramWebhookValidationService
{
    private const CACHE_PREFIX = 'telegram_update_processed_';
    private const CACHE_TTL = 300; // 5 minutes
 
    public function __construct(
        private LoggingServiceInterface $loggingService
    ) {}
 
    /**
     * Check if webhook is duplicate and mark as processing
     */
    public function validateAndMarkProcessing(?int $updateId, bool $skipDuplicateCheck = false): bool
    {
        if (!$updateId) {
            return true; // No update_id, allow processing
        }
 
        // Skip duplicate check if requested (useful for testing)
        if ($skipDuplicateCheck) {
            $this->markRequestAsProcessing($updateId);
            return true;
        }
 
        // Check if already processed
        if ($this->isDuplicateRequest($updateId)) {
            $this->logDuplicateWebhook($updateId);
            return false;
        }
 
        // Mark as processing to prevent race conditions
        $this->markRequestAsProcessing($updateId);
        return true;
    }
 
    /**
     * Check if request is duplicate (quick cache check)
     */
    private function isDuplicateRequest(int $updateId): bool
    {
        try {
            $cacheKey = $this->getCacheKey($updateId);
            return Cache::has($cacheKey);
        } catch (\Exception $e) {
            // If cache fails, log but don't block processing
            $this->loggingService->logException($e, [
                'operation' => 'check_duplicate_update_id',
                'update_id' => $updateId
            ]);
            return false;
        }
    }
 
    /**
     * Mark request as being processed (to prevent race conditions)
     */
    private function markRequestAsProcessing(int $updateId): void
    {
        try {
            $cacheKey = $this->getCacheKey($updateId);
            Cache::put($cacheKey, true, self::CACHE_TTL);
        } catch (\Exception $e) {
            // If cache fails, log but don't block processing
            $this->loggingService->logException($e, [
                'operation' => 'mark_update_id_processed',
                'update_id' => $updateId
            ]);
        }
    }
 
    /**
     * Get cache key for update_id
     */
    private function getCacheKey(int $updateId): string
    {
        return self::CACHE_PREFIX . $updateId;
    }
 
    /**
     * Log duplicate webhook detection
     */
    private function logDuplicateWebhook(int $updateId): void
    {
        $this->loggingService->logTelegramEvent('duplicate_webhook_ignored_early', [
            'update_id' => $updateId,
            'message' => 'Duplicate webhook detected and ignored before processing'
        ], 'info');
    }
 
    /**
     * Clean up processed update_id (useful for testing or manual cleanup)
     */
    public function cleanupProcessedUpdate(int $updateId): bool
    {
        try {
            $cacheKey = $this->getCacheKey($updateId);
            return Cache::forget($cacheKey);
        } catch (\Exception $e) {
            $this->loggingService->logException($e, [
                'operation' => 'cleanup_processed_update_id',
                'update_id' => $updateId
            ]);
            return false;
        }
    }
 
    /**
     * Get cache statistics for monitoring
     */
    public function getCacheStats(): array
    {
        try {
            // This is a simplified approach - in production you might want more sophisticated stats
            return [
                'cache_driver' => config('cache.default'),
                'cache_prefix' => self::CACHE_PREFIX,
                'cache_ttl' => self::CACHE_TTL,
                'note' => 'Use Redis or Memcached for better performance and monitoring'
            ];
        } catch (\Exception $e) {
            return [
                'error' => 'Failed to get cache stats: ' . $e->getMessage()
            ];
        }
    }
}

Passo 5: Criar Requests

<?php
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
use App\Contracts\LoggingServiceInterface;
 
class TelegramWebhookRequest extends FormRequest
{
    public function __construct(
        private LoggingServiceInterface $loggingService
    ) {
        parent::__construct();
    }
 
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true; // Webhook requests are always authorized
    }
 
    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'update_id' => 'required|integer',
            'message' => 'sometimes|array',
            'callback_query' => 'sometimes|array',
            'message.chat.id' => 'required_with:message|integer',
            'message.from.id' => 'required_with:message|integer',
            // Remove required validation for text since audio/voice messages don't have text
            'message.text' => 'sometimes|string',
            // Add validation for voice messages
            'message.voice' => 'sometimes|array',
            'message.voice.file_id' => 'required_with:message.voice|string',
            'message.voice.duration' => 'sometimes|integer',
            'message.voice.mime_type' => 'sometimes|string',
            // Add validation for audio messages
            'message.audio' => 'sometimes|array',
            'message.audio.file_id' => 'required_with:message.audio|string',
            'message.audio.duration' => 'sometimes|integer',
            'message.audio.title' => 'sometimes|string',
            'message.audio.performer' => 'sometimes|string',
            // Add validation for other message types
            'message.photo' => 'sometimes|array',
            'message.document' => 'sometimes|array',
            'message.video' => 'sometimes|array',
            'message.sticker' => 'sometimes|array',
            'message.location' => 'sometimes|array',
            'message.contact' => 'sometimes|array',
            'callback_query.id' => 'required_with:callback_query|string',
            'callback_query.data' => 'required_with:callback_query|string',
            'callback_query.message.chat.id' => 'required_with:callback_query|integer',
        ];
    }
 
    /**
     * Get custom messages for validator errors.
     */
    public function messages(): array
    {
        return [
            'update_id.required' => 'Update ID is required',
            'update_id.integer' => 'Update ID must be an integer',
            'message.array' => 'Message must be an array',
            'callback_query.array' => 'Callback query must be an array',
            'message.chat.id.required_with' => 'Chat ID is required when message is present',
            'message.chat.id.integer' => 'Chat ID must be an integer',
            'message.from.id.required_with' => 'User ID is required when message is present',
            'message.from.id.integer' => 'User ID must be an integer',
            'message.text.string' => 'Message text must be a string',
            'message.voice.array' => 'Voice message must be an array',
            'message.voice.file_id.required_with' => 'Voice file ID is required when voice message is present',
            'message.voice.file_id.string' => 'Voice file ID must be a string',
            'message.voice.duration.integer' => 'Voice duration must be an integer',
            'message.voice.mime_type.string' => 'Voice MIME type must be a string',
            'message.audio.array' => 'Audio message must be an array',
            'message.audio.file_id.required_with' => 'Audio file ID is required when audio message is present',
            'message.audio.file_id.string' => 'Audio file ID must be a string',
            'message.audio.duration.integer' => 'Audio duration must be an integer',
            'message.audio.title.string' => 'Audio title must be a string',
            'message.audio.performer.string' => 'Audio performer must be a string',
            'callback_query.id.required_with' => 'Callback query ID is required when callback query is present',
            'callback_query.data.required_with' => 'Callback data is required when callback query is present',
            'callback_query.message.chat.id.required_with' => 'Chat ID is required when callback query is present',
        ];
    }
 
    /**
     * Determine if the request expects JSON.
     *
     * @return bool
     */
    public function expectsJson(): bool
    {
        return true; // Always expect JSON for webhook requests
    }
 
    /**
     * Determine if the request is asking for JSON.
     *
     * @return bool
     */
    public function wantsJson(): bool
    {
        return true; // Always want JSON for webhook requests
    }
 
    /**
     * Handle a failed validation attempt.
     * Instead of throwing HTTP exception, we'll let the controller handle it
     * and send a friendly message via Telegram.
     */
    protected function failedValidation(\Illuminate\Contracts\Validation\Validator $validator): void
    {
        // Log detalhes da validação falhada
        $this->loggingService->logTelegramEvent('telegram_webhook_validation_failed', [
            'error' => 'Webhook validation failed',
            'validation_errors' => $validator->errors()->toArray(),
            'request_data' => $this->all(),
            'request_headers' => $this->headers->all(),
            'ip' => $this->ip(),
            'user_agent' => $this->userAgent(),
            'timestamp' => now()->toISOString(),
            'validation_rules' => $this->rules(),
            'failed_fields' => array_keys($validator->errors()->toArray()),
            'request_size' => strlen($this->getContent()),
            'content_type' => $this->header('Content-Type'),
            'telegram_update_id' => $this->input('update_id'),
            'message_type' => $this->getMessageType(),
            'has_message' => $this->has('message'),
            'has_callback_query' => $this->has('callback_query')
        ], 'error');
 
        // Store validation errors in the request for the controller to handle
        $this->merge(['validation_errors' => $validator->errors()->toArray()]);
 
        // Don't throw exception - let controller handle it gracefully
        // parent::failedValidation($validator);
    }
 
    /**
     * Get the message type from the request
     */
    private function getMessageType(): string
    {
        if ($this->has('callback_query')) {
            return 'callback_query';
        }
 
        if ($this->has('message')) {
            $message = $this->input('message', []);
 
            if (isset($message['text'])) {
                return 'text_message';
            }
            if (isset($message['voice'])) {
                return 'voice_message';
            }
            if (isset($message['audio'])) {
                return 'audio_message';
            }
            if (isset($message['photo'])) {
                return 'photo_message';
            }
            if (isset($message['document'])) {
                return 'document_message';
            }
            if (isset($message['video'])) {
                return 'video_message';
            }
            if (isset($message['sticker'])) {
                return 'sticker_message';
            }
            if (isset($message['location'])) {
                return 'location_message';
            }
            if (isset($message['contact'])) {
                return 'contact_message';
            }
 
            return 'unknown_message_type';
        }
 
        return 'no_message';
    }
 
    /**
     * Log validation attempt for debugging
     */
    public function validateResolved(): void
    {
        // Log successful validation
        // $this->loggingService->logTelegramEvent('telegram_webhook_validation_success', [
        //     'success' => 'Webhook validation passed successfully',
        //     'message_type' => $this->getMessageType(),
        //     'telegram_update_id' => $this->input('update_id'),
        //     'has_message' => $this->has('message'),
        //     'has_callback_query' => $this->has('callback_query'),
        //     'message_content_types' => $this->getMessageContentTypes(),
        //     'timestamp' => now()->toISOString()
        // ], 'info');
 
        parent::validateResolved();
    }
 
    /**
     * Get all content types present in the message
     */
    private function getMessageContentTypes(): array
    {
        if (!$this->has('message')) {
            return [];
        }
 
        $message = $this->input('message', []);
        $contentTypes = [];
 
        if (isset($message['text'])) {
            $contentTypes[] = 'text';
        }
        if (isset($message['voice'])) {
            $contentTypes[] = 'voice';
        }
        if (isset($message['audio'])) {
            $contentTypes[] = 'audio';
        }
        if (isset($message['photo'])) {
            $contentTypes[] = 'photo';
        }
        if (isset($message['document'])) {
            $contentTypes[] = 'document';
        }
        if (isset($message['video'])) {
            $contentTypes[] = 'video';
        }
        if (isset($message['sticker'])) {
            $contentTypes[] = 'sticker';
        }
        if (isset($message['location'])) {
            $contentTypes[] = 'location';
        }
        if (isset($message['contact'])) {
            $contentTypes[] = 'contact';
        }
 
        return $contentTypes;
    }
}
<?php
 
namespace App\Http\Requests;
 
use Illuminate\Foundation\Http\FormRequest;
 
class TelegramWebhookSetupRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }
 
    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'webhook_url' => 'required|url|max:255',
        ];
    }
 
    /**
     * Get custom messages for validator errors.
     */
    public function messages(): array
    {
        return [
            'webhook_url.required' => 'Webhook URL is required',
            'webhook_url.url' => 'Webhook URL must be a valid URL',
            'webhook_url.max' => 'Webhook URL cannot exceed 255 characters',
        ];
    }
}

Passo 6: Registrar Middleware

// app/Http/Kernel.php
 
protected $middlewareAliases = [
    // ... outros middleware
    'telegram.webhook.secret' => \App\Http\Middleware\TelegramWebhookSecretMiddleware::class,
    'telegram.webhook.exception' => \App\Http\Middleware\TelegramWebhookExceptionHandler::class,
    'telegram.webhook.logging' => \App\Http\Middleware\TelegramWebhookLoggingMiddleware::class,
];

🧪 Testes

Teste Unitário de Validação

<?php
 
namespace Tests\Unit\Services\Telegram;
 
use App\Services\Telegram\TelegramWebhookValidationService;
use App\Contracts\LoggingServiceInterface;
use Mockery;
use PHPUnit\Framework\TestCase;
 
class TelegramWebhookValidationServiceTest extends TestCase
{
    private TelegramWebhookValidationService $service;
    private LoggingServiceInterface $loggingService;
 
    protected function setUp(): void
    {
        parent::setUp();
 
        $this->loggingService = Mockery::mock(LoggingServiceInterface::class);
        $this->service = new TelegramWebhookValidationService($this->loggingService);
    }
 
    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
 
    public function test_validate_and_mark_processing_without_update_id_returns_true()
    {
        $result = $this->service->validateAndMarkProcessing(null);
 
        $this->assertTrue($result);
    }
 
    public function test_service_can_be_instantiated()
    {
        $this->assertInstanceOf(TelegramWebhookValidationService::class, $this->service);
    }
 
    public function test_service_has_required_methods()
    {
        $this->assertTrue(method_exists($this->service, 'validateAndMarkProcessing'));
        $this->assertTrue(method_exists($this->service, 'cleanupProcessedUpdate'));
        $this->assertTrue(method_exists($this->service, 'getCacheStats'));
    }
}

✅ Validação do Módulo

Checklist de Implementação

  1. [ ] Middleware `TelegramWebhookSecretMiddleware` criado
  2. [ ] Middleware `TelegramWebhookExceptionHandler` criado
  3. [ ] Middleware `TelegramWebhookLoggingMiddleware` criado
  4. [ ] Service `TelegramWebhookValidationService` implementado
  5. [ ] Request `TelegramWebhookRequest` criado
  6. [ ] Request `TelegramWebhookSetupRequest` criado
  7. [ ] Middleware registrado no Kernel
  8. [ ] Testes unitários implementados
  9. [ ] Integração com sistemas anteriores

Comandos de Validação

# Executar testes
php artisan test tests/Unit/TelegramWebhookValidationServiceTest.php
 
# Verificar middleware registrado
php artisan route:list --middleware=telegram.webhook.secret
 
# Testar validação
php artisan tinker
# >>> $service = app(\App\Services\Telegram\TelegramWebhookValidationService::class);
# >>> $service->validateWebhookPayload(['update_id' => 123, 'message' => ['message_id' => 1, 'from' => ['id' => 123], 'chat' => ['id' => 123], 'date' => time()]]);
Imprimir/Exportar
QR Code
QR Code templates_ai:funcionalidades:laravel:telegram_bot:03_validation_middleware (generated for current page)