'array', 'expires_at' => 'datetime', ]; /** * Scope to get only security events */ public function scopeSecurity(Builder $query): Builder { return $query->where('event_type', 'like', 'security_%'); } /** * Scope to get only compliance events */ public function scopeCompliance(Builder $query): Builder { return $query->where('event_type', 'like', 'compliance_%'); } /** * Scope to get only webhook events */ public function scopeWebhooks(Builder $query): Builder { return $query->where('event_type', 'like', 'webhook_%'); } /** * Scope to get only error events */ public function scopeErrors(Builder $query): Builder { return $query->where('level', 'error'); } /** * Scope to get events for a specific provider */ public function scopeForProvider(Builder $query, string $provider): Builder { return $query->whereJsonContains('data->provider', $provider); } /** * Scope to get events for a specific subscription */ public function scopeForSubscription(Builder $query, int $subscriptionId): Builder { return $query->whereJsonContains('data->subscription_id', $subscriptionId); } /** * Scope to get events that require review */ public function scopeRequiresReview(Builder $query): Builder { return $query->whereJsonContains('data->requires_review', true); } /** * Scope to get events that haven't expired */ public function scopeNotExpired(Builder $query): Builder { return $query->where(function ($q) { $q->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }); } /** * Scope to get expired events (for cleanup) */ public function scopeExpired(Builder $query): Builder { return $query->where('expires_at', '<', now()); } /** * Get the user relationship */ public function user() { return $this->morphTo(); } /** * Check if event is security-related */ public function isSecurityEvent(): bool { return str_starts_with($this->event_type, 'security_'); } /** * Check if event is compliance-related */ public function isComplianceEvent(): bool { return str_starts_with($this->event_type, 'compliance_'); } /** * Check if event is webhook-related */ public function isWebhookEvent(): bool { return str_starts_with($this->event_type, 'webhook_'); } /** * Check if event requires review */ public function requiresReview(): bool { return ($this->data['requires_review'] ?? false) || $this->isSecurityEvent() || $this->level === 'error'; } /** * Get the provider from event data */ public function getProvider(): ?string { return $this->data['provider'] ?? null; } /** * Get the subscription ID from event data */ public function getSubscriptionId(): ?int { return $this->data['subscription_id'] ?? null; } /** * Get the action from event data */ public function getAction(): ?string { return $this->data['action'] ?? null; } /** * Check if event contains sensitive data */ public function containsSensitiveData(): bool { $sensitiveKeys = ['payment_method', 'card_number', 'bank_account', 'ssn', 'full_credit_card']; foreach ($sensitiveKeys as $key) { if (isset($this->data[$key])) { return true; } } return false; } /** * Get sanitized data for display (removes sensitive information) */ public function getSanitizedData(): array { $data = $this->data; // Remove or mask sensitive fields $sensitivePatterns = [ '/payment_method.*?number/i', '/card_?number/i', '/cvv/i', '/cvc/i', '/ssn/i', '/bank_?account/i', '/routing_?number/i', ]; foreach ($sensitivePatterns as $pattern) { $data = array_map(function ($value) use ($pattern) { if (is_string($value) && preg_match($pattern, $value)) { return str_repeat('*', strlen($value) - 4).substr($value, -4); } return $value; }, $data); } return $data; } /** * Export event for compliance reporting */ public function toComplianceArray(): array { return [ 'id' => $this->id, 'event_type' => $this->event_type, 'level' => $this->level, 'created_at' => $this->created_at->toISOString(), 'user_id' => $this->user_id, 'user_type' => $this->user_type, 'request_id' => $this->request_id, 'ip_address' => $this->ip_address, 'provider' => $this->getProvider(), 'subscription_id' => $this->getSubscriptionId(), 'action' => $this->getAction(), 'requires_review' => $this->requiresReview(), 'is_security_event' => $this->isSecurityEvent(), 'is_compliance_event' => $this->isComplianceEvent(), 'contains_sensitive_data' => $this->containsSensitiveData(), ]; } /** * Clean up old events based on retention policy */ public static function cleanup(): array { $results = []; // Clean up expired webhook payloads $webhookCleanup = static::webhooks() ->expired() ->delete(); $results['webhook_payloads'] = $webhookCleanup; // Clean up old debug events $debugCleanup = static::where('level', 'debug') ->where('created_at', '<', now()->subDays(90)) ->delete(); $results['debug_events'] = $debugCleanup; // Clean up old info events (keep for 1 year) $infoCleanup = static::where('level', 'info') ->where('created_at', '<', now()->subYear()) ->whereNot(function ($query) { $query->security() ->compliance(); }) ->delete(); $results['info_events'] = $infoCleanup; return $results; } /** * Get events by date range for reporting */ public static function getByDateRange(\DateTime $start, \DateTime $end, array $filters = []): Builder { $query = static::whereBetween('created_at', [$start, $end]); if (! empty($filters['event_types'])) { $query->whereIn('event_type', $filters['event_types']); } if (! empty($filters['levels'])) { $query->whereIn('level', $filters['levels']); } if (! empty($filters['user_id'])) { $query->where('user_id', $filters['user_id']); } if (! empty($filters['provider'])) { $query->forProvider($filters['provider']); } return $query; } }