diff --git a/config/cloudplans.php b/config/cloudplans.php index 6bf2f7d..fe1aeb9 100644 --- a/config/cloudplans.php +++ b/config/cloudplans.php @@ -69,6 +69,7 @@ 'free' => [ 'id' => 'free', 'name' => 'Free', + 'token_allowance' => (int) env('CLOUD_PLAN_FREE_TOKEN_ALLOWANCE', 0), 'interval' => 'lifetime', 'features' => [ 'Up to 3 wallets', @@ -83,6 +84,7 @@ 'monthly' => [ 'id' => 'monthly', 'name' => 'Monthly', + 'token_allowance' => (int) env('CLOUD_PLAN_MONTHLY_TOKEN_ALLOWANCE', 50000), 'interval' => 'month', 'features' => [ 'Unlimited categories and wallets', @@ -97,6 +99,7 @@ ], 'yearly' => [ 'id' => 'yearly', + 'token_allowance' => (int) env('CLOUD_PLAN_YEARLY_TOKEN_ALLOWANCE', 50000), 'name' => 'Yearly', 'interval' => 'year', 'features' => [ diff --git a/src/CloudServiceProvider.php b/src/CloudServiceProvider.php index 7dd18ff..f28899d 100644 --- a/src/CloudServiceProvider.php +++ b/src/CloudServiceProvider.php @@ -5,6 +5,7 @@ use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; use Trakli\Cloud\Console\SyncPlansCommand; +use Whilesmart\Entitlements\Contracts\Entitlements; class CloudServiceProvider extends ServiceProvider { @@ -31,7 +32,7 @@ public function boot(): void if (blank(config('entitlements-cashier.default_plan'))) { config(['entitlements-cashier.default_plan' => 'free']); } - + $this->app->singleton(Entitlements::class, \Trakli\Cloud\Support\CloudEntitlements::class); if ($this->app->runningInConsole()) { $this->commands([SyncPlansCommand::class]); } diff --git a/src/Support/CloudEntitlements.php b/src/Support/CloudEntitlements.php new file mode 100644 index 0000000..86e44ae --- /dev/null +++ b/src/Support/CloudEntitlements.php @@ -0,0 +1,133 @@ +getPlanCode($owner); + + $plan = config("cloudplans.plans.{$planCode}"); + if (!$plan) { + return 0; + } + + $allowance = (int)($plan['token_allowance'] ?? 0); + if ($allowance <= 0) { + return 0; + } + + $periodStart = Carbon::now()->startOfMonth(); + $used = method_exists($owner, 'tokensUsed') ? (int)$owner->tokensUsed($periodStart) : 0; + + return max(0, $allowance - $used); + } + + /** + * Consume a given amount of tokens for the current period. + */ + public function consume(?Model $owner, string $meter, int $amount): void + { + // Handled in trakli core via whilesmart/eloquent-agent-metrics + } + + private function subscriptionModel(): string + { + return config('entitlements.models.subscription', Subscription::class); + } + + private function activeSubscriptionFor(Model $owner): ?Subscription + { + return $this->subscriptionModel()::query() + ->where('owner_type', $owner->getMorphClass()) + ->where('owner_id', $owner->getKey()) + ->active() + ->latest('id') + ->first(); + } + + /** + * Get the active plan code for the owner. + * + * The entitlement_plans table keys are suffixed with the region + * (e.g. monthly-us), while cloudplans.php is keyed by base plan + * (free, monthly, yearly). Strip the suffix before config lookup. + */ + private function getPlanCode(Model $owner): string + { + $subscription = $this->activeSubscriptionFor($owner); + if ($subscription && $subscription->plan) { + return explode('-', $subscription->plan->key)[0]; // gets monthly from monthly-eu + } + + // Assume user is on the free plan + return 'free'; + } + + public function check(?Model $owner, string $feature): AccessResult + { + // If in freemode or feature is unconditionally allowed + if (config('cloudplans.freemode_enabled', false)) { + return AccessResult::allow($feature); + } + + if (! $owner) { + return AccessResult::deny($feature, 'no_owner'); + } + + $planCode = $this->getPlanCode($owner); + $plan = config("cloudplans.plans.{$planCode}"); + + // Check if the feature is listed in the plan's features or permissions + if ($plan && in_array($feature, $plan['features'] ?? [], true)) { + return AccessResult::allow($feature); + } + + return AccessResult::deny($feature, 'not_in_plan'); + } +} + + + +