Skip to content

Commit a6af895

Browse files
committed
only create a new user upon customer.subscription.created webhook
1 parent b5f01d1 commit a6af895

File tree

3 files changed

+90
-13
lines changed

3 files changed

+90
-13
lines changed

app/Jobs/CreateAnystackLicenseJob.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class CreateAnystackLicenseJob implements ShouldQueue
2121
public function __construct(
2222
public User $user,
2323
public Subscription $subscription,
24-
public ?string $subscriptionItemId = null,
24+
public ?int $subscriptionItemId = null,
2525
public ?string $firstName = null,
2626
public ?string $lastName = null,
2727
) {}

app/Listeners/StripeWebhookReceivedListener.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
use Illuminate\Support\Facades\Log;
88
use Laravel\Cashier\Cashier;
99
use Laravel\Cashier\Events\WebhookReceived;
10-
use Stripe\Customer;
1110

1211
class StripeWebhookReceivedListener
1312
{
@@ -16,11 +15,6 @@ public function handle(WebhookReceived $event): void
1615
Log::debug('Webhook received', $event->payload);
1716

1817
match ($event->payload['type']) {
19-
// 'customer.created' must be dispatched sync so the user is
20-
// created before the cashier webhook handling is executed.
21-
'customer.created' => dispatch_sync(new CreateUserFromStripeCustomer(
22-
Customer::constructFrom($event->payload['data']['object'])
23-
)),
2418
'customer.subscription.created' => $this->createUserIfNotExists($event->payload['data']['object']['customer']),
2519
default => null,
2620
};

tests/Feature/StripePurchaseHandlingTest.php

Lines changed: 89 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ protected function setUp(): void
3131
}
3232

3333
#[Test]
34-
public function a_user_is_created_when_a_stripe_customer_is_created()
34+
public function a_user_is_not_created_when_a_stripe_customer_is_created()
3535
{
3636
Bus::fake();
3737

@@ -49,9 +49,89 @@ public function a_user_is_created_when_a_stripe_customer_is_created()
4949

5050
$this->postJson('/stripe/webhook', $payload);
5151

52+
Bus::assertNotDispatched(CreateUserFromStripeCustomer::class);
53+
}
54+
55+
#[Test]
56+
public function a_user_is_created_when_a_stripe_customer_subscription_is_created_and_a_matching_user_doesnt_exist()
57+
{
58+
Bus::fake();
59+
60+
$this->mockStripeClient();
61+
62+
$payload = [
63+
'id' => 'evt_test_webhook',
64+
'type' => 'customer.subscription.created',
65+
'data' => [
66+
'object' => [
67+
'id' => 'sub_test123',
68+
'customer' => 'cus_test123',
69+
'status' => 'active',
70+
'items' => [
71+
'object' => 'list',
72+
'data' => [
73+
[
74+
'id' => 'si_test',
75+
'price' => [
76+
'id' => Subscription::Max->stripePriceId(),
77+
'product' => 'prod_test',
78+
],
79+
'quantity' => 1,
80+
],
81+
],
82+
],
83+
],
84+
],
85+
];
86+
87+
$this->postJson('/stripe/webhook', $payload);
88+
5289
Bus::assertDispatched(CreateUserFromStripeCustomer::class);
5390
}
5491

92+
#[Test]
93+
public function a_user_is_not_created_when_a_stripe_customer_subscription_is_created_if_a_matching_user_already_exists()
94+
{
95+
Bus::fake();
96+
97+
$user = User::factory()->create([
98+
'stripe_id' => 'cus_test123',
99+
'name' => 'John Doe',
100+
'email' => '[email protected]',
101+
]);
102+
103+
$this->mockStripeClient($user);
104+
105+
$payload = [
106+
'id' => 'evt_test_webhook',
107+
'type' => 'customer.subscription.created',
108+
'data' => [
109+
'object' => [
110+
'id' => 'sub_test123',
111+
'customer' => $user->stripe_id,
112+
'status' => 'active',
113+
'items' => [
114+
'object' => 'list',
115+
'data' => [
116+
[
117+
'id' => 'si_test',
118+
'price' => [
119+
'id' => Subscription::Max->stripePriceId(),
120+
'product' => 'prod_test',
121+
],
122+
'quantity' => 1,
123+
],
124+
],
125+
],
126+
],
127+
],
128+
];
129+
130+
$this->postJson('/stripe/webhook', $payload);
131+
132+
Bus::assertNotDispatched(CreateUserFromStripeCustomer::class);
133+
}
134+
55135
#[Test]
56136
public function a_license_is_created_when_a_stripe_subscription_is_created()
57137
{
@@ -95,6 +175,7 @@ public function a_license_is_created_when_a_stripe_subscription_is_created()
95175
Bus::assertDispatched(CreateAnystackLicenseJob::class, function (CreateAnystackLicenseJob $job) {
96176
return $job->user->email === '[email protected]' &&
97177
$job->subscription === Subscription::Max &&
178+
$job->subscriptionItemId === $job->user->subscriptions->first()->items()->first()->id &&
98179
$job->firstName === 'John' &&
99180
$job->lastName === 'Doe';
100181
});
@@ -105,7 +186,7 @@ public function a_license_is_created_when_a_stripe_subscription_is_created()
105186
$this->assertNotEmpty($user->subscriptions->first()->items);
106187
}
107188

108-
protected function mockStripeClient(User $user): void
189+
protected function mockStripeClient(?User $user = null): void
109190
{
110191
$mockStripeClient = $this->createMock(StripeClient::class);
111192
$mockStripeClient->customers = new class($user)
@@ -120,13 +201,15 @@ public function __construct($user)
120201
public function retrieve()
121202
{
122203
return Customer::constructFrom([
123-
'id' => $this->user->stripe_id,
124-
'name' => $this->user->name,
125-
'email' => $this->user->email,
204+
'id' => $this->user?->stripe_id ?: 'cus_test123',
205+
'name' => $this->user?->name ?: 'Test Customer',
206+
'email' => $this->user?->email ?: '[email protected]',
126207
]);
127208
}
128209
};
129210

130-
$this->app->instance(StripeClient::class, $mockStripeClient);
211+
$this->app->bind(StripeClient::class, function ($app, $parameters) use ($mockStripeClient) {
212+
return $mockStripeClient;
213+
});
131214
}
132215
}

0 commit comments

Comments
 (0)