*/ use Billable, HasFactory, HasUsage, HasUuids; public const SUBSCRIPTION_NAME = 'default'; /** * Redis/cache key for aggregated post counts across the account's workspaces. * Invalidated by the PostHog usage sync job before re-reading aggregates for analytics. */ public static function postsCountCacheKey(string $accountId): string { return "account:{$accountId}:posts_count"; } protected $fillable = [ 'owner_id', 'name', 'billing_email', 'plan_id', 'trial_ends_at', ]; protected $casts = [ 'trial_ends_at' => 'datetime', ]; public function forgetPlanFeatureCache(): void { Feature::for($this)->forget([ WorkspaceLimit::class, SocialAccountLimit::class, MemberLimit::class, MonthlyCreditsLimit::class, ]); } public function owner(): BelongsTo { return $this->belongsTo(User::class, 'owner_id'); } public function plan(): BelongsTo { return $this->belongsTo(Plan::class); } public function users(): HasMany { return $this->hasMany(User::class); } public function workspaces(): HasMany { return $this->hasMany(Workspace::class); } public function invites(): HasMany { return $this->hasMany(Invite::class); } public function hasActiveSubscription(): bool { if (config('trypost.self_hosted')) { return true; } return $this->subscribed(self::SUBSCRIPTION_NAME); } public function isOnTrial(): bool { if (! (bool) config('trypost.billing.require_card_for_trial', true) && $this->onGenericTrial()) { return true; } return (bool) $this->subscription(self::SUBSCRIPTION_NAME)?->onTrial(); } public function activeTrialEndsAt(): ?CarbonInterface { $subscription = $this->subscription(self::SUBSCRIPTION_NAME); if (! $subscription?->onTrial()) { if (! (bool) config('trypost.billing.require_card_for_trial', true) && $this->onGenericTrial()) { return $this->trial_ends_at; } return null; } return $subscription->trial_ends_at; } /** * Returns the displayable card for the billing UI. Falls back to the first * attached payment method when the customer has no `invoice_settings.default_payment_method` * (Stripe Checkout trials anchor the card to the subscription, not the customer). * * @return array{brand: string, last4: string, exp_month: int, exp_year: int}|null */ public function displayablePaymentMethod(): ?array { $paymentMethod = $this->defaultPaymentMethod() ?? $this->paymentMethods()->first(); $card = $paymentMethod?->card; if (! $card) { return null; } return [ 'brand' => $card->brand, 'last4' => $card->last4, 'exp_month' => $card->exp_month, 'exp_year' => $card->exp_year, ]; } public function stripeEmail(): string { return $this->billing_email ?? $this->owner?->email ?? ''; } public function stripeName(): string { return $this->name; } }