From 5a6967dc018928998c61344f6be04836cd80d56a Mon Sep 17 00:00:00 2001 From: Ovidiu U Date: Thu, 11 Jun 2026 10:56:48 +0100 Subject: [PATCH] Guard HandleStripeWebhook plan-cache bust against non-taggable cache stores bustPlanCache() called Cache::tags() unconditionally, which throws on the `database`/`file` cache drivers. Mirror the Cache::supportsTags() idiom used in Plan.php so Stripe webhooks work regardless of the configured cache store. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/Listeners/HandleStripeWebhook.php | 7 ++++--- .../Payments/HandleStripeWebhookTest.php | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app/Listeners/HandleStripeWebhook.php b/app/Listeners/HandleStripeWebhook.php index 00ec40a..2f205a4 100644 --- a/app/Listeners/HandleStripeWebhook.php +++ b/app/Listeners/HandleStripeWebhook.php @@ -129,8 +129,9 @@ final class HandleStripeWebhook private function bustPlanCache(User $user): void { - $tag = Cache::tags(['plans']); - $tag->forget("plan_for_user_{$user->id}"); - $tag->forget("plan_cadence_for_user_{$user->id}"); + $cache = Cache::supportsTags() ? Cache::tags(['plans']) : Cache::store(); + + $cache->forget("plan_for_user_{$user->id}"); + $cache->forget("plan_cadence_for_user_{$user->id}"); } } diff --git a/tests/Feature/Payments/HandleStripeWebhookTest.php b/tests/Feature/Payments/HandleStripeWebhookTest.php index 6c89f4c..09b6bc0 100644 --- a/tests/Feature/Payments/HandleStripeWebhookTest.php +++ b/tests/Feature/Payments/HandleStripeWebhookTest.php @@ -24,6 +24,24 @@ it('busts the plan cache on customer.subscription.created', function (): void { expect(Cache::tags(['plans'])->get("plan_for_user_{$user->id}"))->toBeNull(); }); +it('busts the plan cache without error on a cache store that does not support tags', function (): void { + // The `file` driver is not taggable — calling Cache::tags() on it throws. + // This guards against a regression where bustPlanCache assumed a taggable store. + config(['cache.default' => 'file']); + Cache::store('file')->flush(); + expect(Cache::supportsTags())->toBeFalse(); + + $user = User::factory()->create(['stripe_id' => 'cus_notags_1']); + Cache::put("plan_for_user_{$user->id}", 'stale', 3600); + + (new HandleStripeWebhook)->handle(new WebhookReceived([ + 'type' => 'customer.subscription.created', + 'data' => ['object' => ['customer' => 'cus_notags_1']], + ])); + + expect(Cache::get("plan_for_user_{$user->id}"))->toBeNull(); +}); + it('ignores subscription.created when the user is not found', function (): void { (new HandleStripeWebhook)->handle(new WebhookReceived([ 'type' => 'customer.subscription.created',