License::query()->count(), 'activeCount' => License::query()->whereNull('revoked_at')->where(function ($q): void { $q->whereNull('expires_at')->orWhere('expires_at', '>', now()); })->count(), 'productCount' => LicenseProduct::query()->count(), 'recentLicenses' => License::query()->with('product')->latest()->limit(10)->get(), ]); } public function licenses(): View { return view('licensing::admin.licenses', [ 'licenses' => License::query()->with('product')->latest()->paginate(25), ]); } public function show(License $license): View { return view('licensing::admin.show', [ 'license' => $license->load(['product', 'activations']), ]); } /** Read-only snapshot of existing licensing subscription metadata. */ public function subscriptions(): View { $subscriptions = License::query() ->whereNotNull('external_subscription_id') ->with('product') ->orderByDesc('created_at') ->paginate(25); $total = License::query()->whereNotNull('external_subscription_id')->count(); $active = License::query()->whereNotNull('external_subscription_id') ->whereIn('subscription_status', ['active', 'trialing'])->count(); $cancelScheduled = License::query()->whereNotNull('external_subscription_id') ->whereNotNull('cancel_scheduled_at')->count(); return view('licensing::admin.subscriptions', compact('subscriptions', 'total', 'active', 'cancelScheduled')); } /** Local-only billing integrity audit. Does not contact Paddle or mutate records. */ public function billingAudit(): View { $linked = License::query()->whereNotNull('external_subscription_id'); $duplicateSubscriptions = License::query() ->select('payment_provider', 'external_subscription_id') ->selectRaw('COUNT(*) AS license_count') ->whereNotNull('external_subscription_id') ->groupBy('payment_provider', 'external_subscription_id') ->havingRaw('COUNT(*) > 1') ->orderByDesc('license_count') ->limit(25) ->get(); $duplicateEvents = LicenseWebhookEvent::query() ->select('provider', 'external_event_id') ->selectRaw('COUNT(*) AS event_count') ->whereNotNull('external_event_id') ->groupBy('provider', 'external_event_id') ->havingRaw('COUNT(*) > 1') ->orderByDesc('event_count') ->limit(25) ->get(); return view('licensing::admin.billing-audit', [ 'linkedCount' => (clone $linked)->count(), 'missingProvider' => (clone $linked)->whereNull('payment_provider')->count(), 'missingCustomer' => (clone $linked)->whereNull('external_customer_id')->count(), 'missingStatus' => (clone $linked)->whereNull('subscription_status')->count(), 'eventCount' => LicenseWebhookEvent::query()->count(), 'unprocessedEvents' => LicenseWebhookEvent::query()->whereNull('processed_at')->count(), 'failedEvents' => LicenseWebhookEvent::query()->whereNotNull('processing_error')->count(), 'duplicateSubscriptions' => $duplicateSubscriptions, 'duplicateEvents' => $duplicateEvents, 'recentFailures' => LicenseWebhookEvent::query() ->whereNotNull('processing_error')->latest()->limit(15) ->get(['id', 'provider', 'external_event_id', 'event_type', 'processing_error', 'created_at']), ]); } }