'json', 'monthly_billing' => 'boolean', 'accept_stripe' => 'boolean', 'accept_shoppy' => 'boolean', 'accept_oxapay' => 'boolean', 'is_active' => 'boolean', 'metadata' => 'array', ]; /** * Get the plan tier that this plan belongs to */ public function planTier(): BelongsTo { return $this->belongsTo(PlanTier::class); } /** * Get feature limits for this plan */ public function planFeatureLimits(): HasMany { return $this->hasMany(PlanFeatureLimit::class); } /** * Get permissions for this plan */ public function planPermissions(): HasMany { return $this->hasMany(PlanPermission::class); } /** * Get payment providers for this plan */ public function planProviders(): HasMany { return $this->hasMany(PlanProvider::class); } /** * Get trial configuration for this plan */ public function trialConfiguration(): HasOne { return $this->hasOne(TrialConfiguration::class); } /** * Get subscriptions for this plan */ public function subscriptions(): HasMany { return $this->hasMany(Subscription::class); } /** * Get usage tracking for this plan */ public function planUsages(): HasMany { return $this->hasMany(PlanUsage::class); } /** * Get features associated with this plan (through limits) */ public function features(): BelongsToMany { return $this->belongsToMany(PlanFeature::class, 'plan_feature_limits') ->withPivot(['limit_value', 'is_enabled', 'limit_type', 'applies_during_trial', 'trial_limit_value']) ->withTimestamps(); } /** * Scope: Active plans */ public function scopeActive($query) { return $query->where('is_active', true); } /** * Scope: Ordered by sort order */ public function scopeOrdered($query) { return $query->orderBy('sort_order')->orderBy('name'); } /** * Scope: By tier */ public function scopeByTier($query, $tierId) { return $query->where('plan_tier_id', $tierId); } /** * Check if plan has a specific feature enabled */ public function hasFeature(string $featureName): bool { $featureLimit = $this->planFeatureLimits() ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->first(); return $featureLimit && $featureLimit->isEnabled(); } /** * Check if user can use a feature within limits */ public function canUseFeature(string $featureName, float $currentUsage = 0, bool $isOnTrial = false): bool { $featureLimit = $this->planFeatureLimits() ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->first(); return $featureLimit ? $featureLimit->canUseFeature($currentUsage, $isOnTrial) : false; } /** * Get remaining usage for a feature */ public function getRemainingUsage(string $featureName, float $currentUsage = 0, bool $isOnTrial = false): float { $featureLimit = $this->planFeatureLimits() ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->first(); return $featureLimit ? $featureLimit->getRemainingUsage($currentUsage, $isOnTrial) : 0; } /** * Check if plan has a specific permission */ public function hasPermission(string $featureName, string $permission, bool $isOnTrial = false): bool { $planPermission = $this->planPermissions() ->whereHas('planFeature', function ($query) use ($featureName) { $query->where('name', $featureName); }) ->where('permission', $permission) ->first(); return $planPermission ? $planPermission->isEffectivePermission($isOnTrial) : false; } /** * Get allowed payment providers for this plan */ public function getAllowedProviders(): array { return $this->planProviders() ->enabled() ->orderBy('sort_order') ->pluck('provider') ->toArray(); } /** * Check if plan supports a specific payment provider */ public function supportsProvider(string $provider): bool { return $this->planProviders() ->where('provider', $provider) ->where('is_enabled', true) ->exists(); } /** * Get provider configuration for a specific provider */ public function getProviderConfig(string $provider): ?PlanProvider { return $this->planProviders() ->where('provider', $provider) ->where('is_enabled', true) ->first(); } /** * Check if plan has trial enabled */ public function hasTrial(): bool { return $this->trialConfiguration && $this->trialConfiguration->trial_enabled; } /** * Get trial configuration */ public function getTrialConfig(): ?TrialConfiguration { return $this->trialConfiguration?->trial_enabled ? $this->trialConfiguration : null; } /** * Get billing cycle in human readable format */ public function getBillingCycleDisplay(): string { if ($this->billing_cycle_days) { if ($this->billing_cycle_days == 30) { return 'Monthly'; } if ($this->billing_cycle_days == 90) { return 'Quarterly'; } if ($this->billing_cycle_days == 365) { return 'Yearly'; } return "{$this->billing_cycle_days} days"; } return $this->monthly_billing ? 'Monthly' : 'Yearly'; } /** * Get plan metadata value */ public function getMetadata(?string $key = null, $default = null) { if ($key) { return data_get($this->metadata, $key, $default); } return $this->metadata; } /** * Set plan metadata value */ public function setMetadata(string $key, $value): void { $data = $this->metadata ?? []; data_set($data, $key, $value); $this->metadata = $data; } /** * Get all features with their limits and permissions */ public function getFeaturesWithLimits(): array { return $this->planFeatureLimits() ->with('planFeature') ->get() ->map(function ($limit) { return [ 'feature' => $limit->planFeature, 'limit' => $limit, 'permissions' => $this->planPermissions() ->where('plan_feature_id', $limit->plan_feature_id) ->get(), ]; }) ->toArray(); } /** * Check if this plan is an upgrade from another plan */ public function isUpgradeFrom(Plan $otherPlan): bool { // Simple logic: check if this plan has higher tier or price if ($this->planTier_id && $otherPlan->planTier_id) { return $this->planTier->sort_order > $otherPlan->planTier->sort_order; } return $this->price > $otherPlan->price; } /** * Get upgrade path to this plan */ public function getUpgradePath(): array { // Return plans that can be upgraded to this plan return Plan::where('id', '!=', $this->id) ->where(function ($query) { $query->where('price', '<', $this->price) ->orWhereHas('planTier', function ($q) { $q->where('sort_order', '<', $this->planTier?->sort_order ?? 0); }); }) ->active() ->ordered() ->get() ->toArray(); } }