*/ public static function stateLabels(): array { return [ WorkspaceSubscription::STATE_TRIAL => 'Trial', WorkspaceSubscription::STATE_ACTIVE => 'Active', WorkspaceSubscription::STATE_PAST_DUE => 'Past due', WorkspaceSubscription::STATE_CANCEL_AT_PERIOD_END => 'Cancellation pending', WorkspaceSubscription::STATE_ENDED => 'Ended', ]; } public function __construct( private readonly SettingsResolver $settingsResolver, ) {} /** * @return array{ * workspace_id: int, * subscription_present: bool, * state: string|null, * label: string|null, * billing_reference: string|null, * status_reason: string|null, * key_date_label: string|null, * key_date: CarbonInterface|null, * needs_review: bool, * source: string, * fallback_status: bool, * derived_lifecycle_state: string * } */ public function summary(Workspace $workspace): array { $workspace->loadMissing('subscription'); $subscription = $workspace->subscription; if ($subscription instanceof WorkspaceSubscription) { return $this->subscriptionSummary($workspace, $subscription); } return $this->fallbackSummary($workspace); } /** * @return array{ * workspace_id: int, * subscription_present: bool, * state: string, * label: string, * billing_reference: string|null, * status_reason: string, * key_date_label: string|null, * key_date: CarbonInterface|null, * needs_review: bool, * source: string, * fallback_status: bool, * derived_lifecycle_state: string * } */ private function subscriptionSummary(Workspace $workspace, WorkspaceSubscription $subscription): array { $state = in_array($subscription->state, WorkspaceSubscription::stateIds(), true) ? $subscription->state : WorkspaceSubscription::STATE_ACTIVE; $keyDate = $this->keyDateForSubscription($subscription, $state); return [ 'workspace_id' => (int) $workspace->getKey(), 'subscription_present' => true, 'state' => $state, 'label' => self::stateLabels()[$state], 'billing_reference' => $subscription->billing_reference, 'status_reason' => $subscription->status_reason, 'key_date_label' => $this->keyDateLabel($state), 'key_date' => $keyDate, 'needs_review' => $this->needsReview($state, $keyDate), 'source' => self::SOURCE_WORKSPACE_SUBSCRIPTION, 'fallback_status' => false, 'derived_lifecycle_state' => $this->derivedLifecycleState($state), ]; } /** * @return array{ * workspace_id: int, * subscription_present: bool, * state: null, * label: null, * billing_reference: null, * status_reason: string|null, * key_date_label: null, * key_date: null, * needs_review: bool, * source: string, * fallback_status: bool, * derived_lifecycle_state: string * } */ private function fallbackSummary(Workspace $workspace): array { $stateSetting = $this->settingsResolver->resolveDetailed( workspace: $workspace, domain: WorkspaceCommercialLifecycleResolver::SETTING_DOMAIN, key: WorkspaceCommercialLifecycleResolver::SETTING_COMMERCIAL_LIFECYCLE_STATE, ); $rawState = is_string($stateSetting['value'] ?? null) ? strtolower(trim((string) $stateSetting['value'])) : null; $derivedLifecycleState = in_array($rawState, WorkspaceCommercialLifecycleResolver::stateIds(), true) ? $rawState : WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID; $source = ($stateSetting['source'] ?? null) === 'workspace_override' && $rawState !== null ? WorkspaceCommercialLifecycleResolver::SOURCE_WORKSPACE_SETTING : WorkspaceCommercialLifecycleResolver::SOURCE_DEFAULT_ACTIVE_PAID; $statusReason = $this->settingsResolver->resolveValue( workspace: $workspace, domain: WorkspaceCommercialLifecycleResolver::SETTING_DOMAIN, key: WorkspaceCommercialLifecycleResolver::SETTING_COMMERCIAL_LIFECYCLE_REASON, ); return [ 'workspace_id' => (int) $workspace->getKey(), 'subscription_present' => false, 'state' => null, 'label' => null, 'billing_reference' => null, 'status_reason' => is_string($statusReason) && trim($statusReason) !== '' ? trim($statusReason) : null, 'key_date_label' => null, 'key_date' => null, 'needs_review' => false, 'source' => $source, 'fallback_status' => true, 'derived_lifecycle_state' => $derivedLifecycleState, ]; } private function derivedLifecycleState(string $state): string { return match ($state) { WorkspaceSubscription::STATE_TRIAL => WorkspaceCommercialLifecycleResolver::STATE_TRIAL, WorkspaceSubscription::STATE_ACTIVE => WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID, WorkspaceSubscription::STATE_PAST_DUE => WorkspaceCommercialLifecycleResolver::STATE_GRACE, WorkspaceSubscription::STATE_CANCEL_AT_PERIOD_END => WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID, WorkspaceSubscription::STATE_ENDED => WorkspaceCommercialLifecycleResolver::STATE_SUSPENDED_READ_ONLY, default => WorkspaceCommercialLifecycleResolver::STATE_ACTIVE_PAID, }; } private function keyDateLabel(string $state): ?string { return match ($state) { WorkspaceSubscription::STATE_TRIAL => 'Trial ends', WorkspaceSubscription::STATE_ACTIVE, WorkspaceSubscription::STATE_PAST_DUE, WorkspaceSubscription::STATE_CANCEL_AT_PERIOD_END, WorkspaceSubscription::STATE_ENDED => 'Current period ends', default => null, }; } private function keyDateForSubscription(WorkspaceSubscription $subscription, string $state): ?CarbonInterface { return match ($state) { WorkspaceSubscription::STATE_TRIAL => $subscription->trial_ends_at, WorkspaceSubscription::STATE_ACTIVE, WorkspaceSubscription::STATE_PAST_DUE, WorkspaceSubscription::STATE_CANCEL_AT_PERIOD_END, WorkspaceSubscription::STATE_ENDED => $subscription->current_period_ends_at, default => null, }; } private function needsReview(string $state, ?CarbonInterface $keyDate): bool { if (! in_array($state, [WorkspaceSubscription::STATE_TRIAL, WorkspaceSubscription::STATE_CANCEL_AT_PERIOD_END], true)) { return false; } return $keyDate instanceof CarbonInterface && $keyDate->isPast(); } }