verify($request)) { Log::warning('Paddle webhook signature verification failed'); return response()->json(['status' => 'invalid'], Response::HTTP_BAD_REQUEST); } $payload = $request->json()->all(); if (! is_array($payload)) { return response()->json(['status' => 'ignored'], Response::HTTP_ACCEPTED); } $eventType = $payload['event_type'] ?? null; $handled = false; if ($eventType) { $handled = $this->webhooks->handlePaddleEvent($payload); $handled = $this->addonWebhooks->handle($payload) || $handled; } Log::info('Paddle webhook processed', [ 'event_type' => $eventType, 'handled' => $handled, ]); $statusCode = $handled ? Response::HTTP_OK : Response::HTTP_ACCEPTED; return response()->json([ 'status' => $handled ? 'processed' : 'ignored', ], $statusCode); } protected function verify(Request $request): bool { $secret = config('paddle.webhook_secret'); if (! $secret) { // Allow processing in sandbox or when secret not configured return true; } $signature = (string) $request->headers->get('Paddle-Webhook-Signature', ''); if ($signature === '') { return false; } $payload = $request->getContent(); $expected = hash_hmac('sha256', $payload, $secret); return hash_equals($expected, $signature); } }