From c0d5d46328a2fa69c46946c5c9cfcc3f55a3edb1 Mon Sep 17 00:00:00 2001 From: Maarten Paauw Date: Sun, 24 Nov 2024 22:04:54 +0100 Subject: [PATCH] feat: multiple plan support --- src/Stripe/BillingProvider.php | 7 ++-- src/Stripe/RedirectIfUserNotSubscribed.php | 42 +++++++++++++++------- tests/BillingProviderTest.php | 12 +++++++ tests/RedirectIfUserNotSubscribedTest.php | 11 ++++++ 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/src/Stripe/BillingProvider.php b/src/Stripe/BillingProvider.php index 7391f47..e6062a0 100644 --- a/src/Stripe/BillingProvider.php +++ b/src/Stripe/BillingProvider.php @@ -13,8 +13,11 @@ final class BillingProvider implements Provider { + /** + * @param string|BackedEnum|array $plans + */ public function __construct( - private readonly string|BackedEnum $plan = 'default', + private readonly string|BackedEnum|array $plans = 'default', ) {} public function getRouteAction(): string|Closure|array @@ -32,6 +35,6 @@ public function getRouteAction(): string|Closure|array public function getSubscribedMiddleware(): string { - return RedirectIfUserNotSubscribed::plan($this->plan); + return RedirectIfUserNotSubscribed::plan($this->plans); } } diff --git a/src/Stripe/RedirectIfUserNotSubscribed.php b/src/Stripe/RedirectIfUserNotSubscribed.php index 77aa910..e8f9200 100644 --- a/src/Stripe/RedirectIfUserNotSubscribed.php +++ b/src/Stripe/RedirectIfUserNotSubscribed.php @@ -10,6 +10,8 @@ use Filament\Pages\Dashboard; use Illuminate\Config\Repository; use Illuminate\Http\Request; +use Illuminate\Support\Arr; +use Illuminate\Support\Collection; use Laravel\Cashier\SubscriptionBuilder; use Maartenpaauw\Filament\Cashier\Plan; use Maartenpaauw\Filament\Cashier\TenantRepository; @@ -26,19 +28,26 @@ public function __construct( * * @throws Exception */ - public function handle(Request $request, Closure $next, string $plan = 'default'): Response + public function handle(Request $request, Closure $next, string ...$plans): Response { $tenant = TenantRepository::make()->current(); - $plan = new Plan($this->repository, $plan); - if ($plan->hasGenericTrial() && $tenant->onGenericTrial()) { - return $next($request); - } + /** @var array $instances */ + $instances = Arr::map($plans, fn (string $plan): Plan => new Plan($this->repository, $plan)); + + foreach ($instances as $plan) { + if ($plan->hasGenericTrial() && $tenant->onGenericTrial()) { + return $next($request); + } - if ($tenant->subscribedToProduct($plan->productId(), $plan->type())) { - return $next($request); + if ($tenant->subscribedToProduct($plan->productId(), $plan->type())) { + return $next($request); + } } + /** @var Plan $plan */ + $plan = Arr::first($instances); + return $tenant ->newSubscription($plan->type(), $plan->isMeteredPrice() ? [] : $plan->priceId()) ->when( @@ -64,11 +73,20 @@ public function handle(Request $request, Closure $next, string $plan = 'default' ->redirect(); } - public static function plan(string|BackedEnum $plan = 'default'): string + /** + * @param string|BackedEnum|array $plans + */ + public static function plan(string|BackedEnum|array $plans = 'default'): string { - return sprintf('%s:%s', self::class, match (true) { - $plan instanceof BackedEnum => strval($plan->value), - default => $plan, - }); + return sprintf( + '%s:%s', + self::class, + Collection::wrap($plans) + ->map(static fn (string|BackedEnum $plan): string => match (true) { + $plan instanceof BackedEnum => strval($plan->value), + default => $plan, + }) + ->join(','), + ); } } diff --git a/tests/BillingProviderTest.php b/tests/BillingProviderTest.php index 04fd5ae..1e3fee8 100644 --- a/tests/BillingProviderTest.php +++ b/tests/BillingProviderTest.php @@ -26,3 +26,15 @@ [Plan::Advanced, 'Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:advanced'], [Plan::Premium, 'Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:premium'], ]); + +it('should use the defined string plans', function (): void { + expect(new BillingProvider('basic,advanced,premium')) + ->getSubscribedMiddleware() + ->toEqual('Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:basic,advanced,premium'); +}); + +it('should use the defined enum plans', function (): void { + expect(new BillingProvider(Plan::cases())) + ->getSubscribedMiddleware() + ->toEqual('Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:basic,advanced,premium'); +}); diff --git a/tests/RedirectIfUserNotSubscribedTest.php b/tests/RedirectIfUserNotSubscribedTest.php index a3fdb86..00b0dff 100644 --- a/tests/RedirectIfUserNotSubscribedTest.php +++ b/tests/RedirectIfUserNotSubscribedTest.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed; +use Workbench\App\enums\Plan; it('should add the plan as parameter', function () { expect(RedirectIfUserNotSubscribed::plan('basic')) @@ -13,3 +14,13 @@ expect(RedirectIfUserNotSubscribed::plan()) ->toEqual('Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:default'); }); + +it('should add the string plans as parameter', function () { + expect(RedirectIfUserNotSubscribed::plan('basic,advanced,premium')) + ->toEqual('Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:basic,advanced,premium'); +}); + +it('should add the enum plans as parameter', function () { + expect(RedirectIfUserNotSubscribed::plan(Plan::cases())) + ->toEqual('Maartenpaauw\Filament\Cashier\Stripe\RedirectIfUserNotSubscribed:basic,advanced,premium'); +});