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:
parent
06e72831f6
commit
c627c532b3
4 changed files with 10 additions and 130 deletions
|
|
@ -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',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
});
|
||||
Loading…
Reference in a new issue