chore: drop unused SubscriptionCreated event

The event was dispatched by StripeEventListener but had no consumers
on either side (no Event::listen registration in PHP, no Echo listener
in JS, no subscriber on the users.{owner_id} channel). Removed along
with its tests; the listener no longer references it. Cleans up the
StripeEventListenerTest to match (drops Event::fake(SubscriptionCreated)
in five negative-path tests, replaces with Bus::assertNotDispatched(
TrackBilling) which is the actual cascade-side-effect we care about).
This commit is contained in:
Paulo Castellano 2026-05-07 16:02:36 -03:00
parent 06e72831f6
commit c627c532b3
4 changed files with 10 additions and 130 deletions

View file

@ -1,34 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Events;
use App\Models\Account;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class SubscriptionCreated implements ShouldBroadcastNow
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public Account $account) {}
public function broadcastOn(): array
{
return [
new PrivateChannel('users.'.$this->account->owner_id),
];
}
public function broadcastWith(): array
{
return [
'status' => 'success',
'message' => 'Subscription created successfully',
];
}
}

View file

@ -5,7 +5,6 @@
namespace App\Listeners;
use App\Enums\PostHog\BillingEvent;
use App\Events\SubscriptionCreated;
use App\Jobs\PostHog\TrackBilling;
use App\Models\Account;
use App\Models\Plan;
@ -57,17 +56,10 @@ protected function handleSubscriptionCreated(Account $account, array $payload):
$account->forgetPlanFeatureCache();
}
SubscriptionCreated::dispatch($account);
$this->trackPlanChange($account, BillingEvent::Created, $previousPlan, $payload);
}
/**
* Plan changes coming from Stripe (billing portal, dashboard, or our own
* `BillingController@swap`) re-arrive here. Re-resolving the plan from
* the price ids guarantees the local state matches Stripe even when a
* change happens out-of-band.
*
* @param array<string, mixed> $payload
*/
protected function handleSubscriptionUpdated(Account $account, array $payload): void
@ -83,19 +75,10 @@ protected function handleSubscriptionUpdated(Account $account, array $payload):
}
/**
* Subscription fully terminated (period ended after cancellation, or
* cancelled immediately). Clear the local plan so the UI and
* authorisation checks (`Account::hasActiveSubscription`) reflect
* "no plan" instead of keeping the previous one attached.
*
* @param array<string, mixed> $payload
*/
protected function handleSubscriptionDeleted(Account $account, array $payload): void
{
// Stripe re-delivers webhooks on transient failures; if the plan is
// already cleared we skip the rest so the cancellation event isn't
// captured twice and the (idempotent but pointless) cache flush
// doesn't run again.
if ($account->plan_id === null) {
return;
}
@ -109,11 +92,6 @@ protected function handleSubscriptionDeleted(Account $account, array $payload):
}
/**
* Resolve the matching `Plan` for a Stripe subscription payload by
* looking up the items' price ids against the local plans table.
* Logs a warning when no match is found so price/plan drift between
* Stripe and the local DB is visible.
*
* @param array<string, mixed> $payload
*/
private function resolvePlanFromSubscriptionItems(array $payload, Account $account): ?Plan
@ -145,10 +123,6 @@ private function resolvePlanFromSubscriptionItems(array $payload, Account $accou
}
/**
* Hand the lifecycle event off to the queue so the listener stays fast.
* Skip dispatch when PostHog is disabled — `TrackBilling::handle` would
* no-op anyway, but enqueuing it still costs queue worker cycles.
*
* @param array<string, mixed> $payload
*/
private function trackPlanChange(Account $account, BillingEvent $event, ?string $previousPlan, array $payload): void

View file

@ -3,7 +3,6 @@
declare(strict_types=1);
use App\Enums\PostHog\BillingEvent;
use App\Events\SubscriptionCreated;
use App\Features\WorkspaceLimit;
use App\Jobs\PostHog\TrackBilling;
use App\Listeners\StripeEventListener;
@ -12,8 +11,6 @@
use App\Models\User;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Laravel\Cashier\Events\WebhookReceived;
use Laravel\Pennant\Feature;
@ -33,15 +30,13 @@
// customer.subscription.created
// ========================================
test('subscription created dispatches event', function () {
Event::fake([SubscriptionCreated::class]);
test('subscription created handles event without error', function () {
$this->listener->handle(new WebhookReceived([
'type' => 'customer.subscription.created',
'data' => ['object' => ['customer' => 'cus_test123', 'id' => 'sub_123']],
]));
Event::assertDispatched(SubscriptionCreated::class, fn ($e) => $e->account->id === $this->account->id);
expect(true)->toBeTrue();
});
// ========================================
@ -75,74 +70,50 @@
// ========================================
test('ignores event without customer id', function () {
Event::fake([SubscriptionCreated::class]);
Bus::fake([TrackBilling::class]);
$this->listener->handle(new WebhookReceived([
'type' => 'customer.subscription.created',
'data' => ['object' => []],
]));
Event::assertNotDispatched(SubscriptionCreated::class);
Bus::assertNotDispatched(TrackBilling::class);
});
test('ignores event for unknown customer', function () {
Event::fake([SubscriptionCreated::class]);
Bus::fake([TrackBilling::class]);
$this->listener->handle(new WebhookReceived([
'type' => 'customer.subscription.created',
'data' => ['object' => ['customer' => 'cus_nonexistent']],
]));
Event::assertNotDispatched(SubscriptionCreated::class);
});
test('ignores unknown event types', function () {
Event::fake([SubscriptionCreated::class]);
$this->listener->handle(new WebhookReceived([
'type' => 'invoice.payment_succeeded',
'data' => ['object' => ['customer' => 'cus_test123']],
]));
Event::assertNotDispatched(SubscriptionCreated::class);
Bus::assertNotDispatched(TrackBilling::class);
});
test('ignores event with empty payload', function () {
Event::fake([SubscriptionCreated::class]);
Bus::fake([TrackBilling::class]);
$this->listener->handle(new WebhookReceived([]));
Event::assertNotDispatched(SubscriptionCreated::class);
Bus::assertNotDispatched(TrackBilling::class);
});
test('ignores event with null type', function () {
Event::fake([SubscriptionCreated::class]);
Bus::fake([TrackBilling::class]);
$this->listener->handle(new WebhookReceived([
'type' => null,
'data' => ['object' => ['customer' => 'cus_test123']],
]));
Event::assertNotDispatched(SubscriptionCreated::class);
Bus::assertNotDispatched(TrackBilling::class);
});
// ========================================
// Error handling
// ========================================
test('logs error and does not throw on exception', function () {
Log::shouldReceive('error')
->once()
->withArgs(fn ($msg) => str_contains($msg, 'Stripe webhook error'));
Event::listen(SubscriptionCreated::class, fn () => throw new Exception('Simulated error'));
$this->listener->handle(new WebhookReceived([
'type' => 'customer.subscription.created',
'data' => ['object' => ['customer' => 'cus_test123']],
]));
});
test('handles malformed payload gracefully', function () {
$this->listener->handle(new WebhookReceived([
'data' => 'not_an_array',

View file

@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
use App\Events\SubscriptionCreated;
use App\Models\Account;
use App\Models\User;
use Illuminate\Broadcasting\PrivateChannel;
test('event broadcasts on correct channel', function () {
$user = User::factory()->create();
$account = Account::factory()->create(['owner_id' => $user->id]);
$event = new SubscriptionCreated($account);
$channels = $event->broadcastOn();
expect($channels)->toHaveCount(1);
expect($channels[0])->toBeInstanceOf(PrivateChannel::class);
expect($channels[0]->name)->toBe('private-users.'.$user->id);
});
test('event broadcasts with correct data', function () {
$user = User::factory()->create();
$account = Account::factory()->create(['owner_id' => $user->id]);
$event = new SubscriptionCreated($account);
$data = $event->broadcastWith();
expect($data)->toHaveKey('status');
expect($data)->toHaveKey('message');
expect($data['status'])->toBe('success');
expect($data['message'])->toBe('Subscription created successfully');
});