TenantAtlas/apps/platform/app/Services/Entitlements/WorkspaceSubscriptionResolver.php
Ahmed Darrazi b7a587495f
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 17m36s
chore: commit all local changes
2026-05-04 23:02:19 +02:00

205 lines
7.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\Entitlements;
use App\Models\Workspace;
use App\Models\WorkspaceSubscription;
use App\Services\Settings\SettingsResolver;
use Carbon\CarbonInterface;
final class WorkspaceSubscriptionResolver
{
public const SOURCE_WORKSPACE_SUBSCRIPTION = 'workspace_subscription';
/**
* @return array<string, string>
*/
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();
}
}