Skip to content

Commit 421bed6

Browse files
committed
collect email for guest users and create acct before purchase
1 parent 94d0e5c commit 421bed6

File tree

7 files changed

+601
-25
lines changed

7 files changed

+601
-25
lines changed

app/Livewire/MobilePricing.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace App\Livewire;
4+
5+
use App\Enums\Subscription;
6+
use App\Models\User;
7+
use Illuminate\Support\Facades\Auth;
8+
use Illuminate\Support\Facades\Hash;
9+
use Illuminate\Support\Str;
10+
use Livewire\Component;
11+
12+
class MobilePricing extends Component
13+
{
14+
protected $listeners = [
15+
'email-submitted' => 'handleEmailSubmitted',
16+
];
17+
18+
public function handleEmailSubmitted(array $data)
19+
{
20+
$user = $this->findOrCreateUser($data['email']);
21+
22+
return $this->createCheckoutSession($data['plan'], $user);
23+
}
24+
25+
public function createCheckoutSession(string $plan, ?User $user = null)
26+
{
27+
$user ??= Auth::user();
28+
29+
if (! $user) {
30+
return;
31+
}
32+
33+
$subscription = Subscription::tryFrom($plan);
34+
35+
if (! $subscription) {
36+
return;
37+
}
38+
39+
$user->createOrGetStripeCustomer();
40+
41+
$checkout = $user
42+
->newSubscription('default', $subscription->stripePriceId())
43+
->allowPromotionCodes()
44+
->checkout([
45+
'success_url' => $this->successUrl(),
46+
'cancel_url' => route('early-adopter'),
47+
]);
48+
49+
return redirect($checkout->url);
50+
}
51+
52+
private function findOrCreateUser(string $email): User
53+
{
54+
return User::firstOrCreate([
55+
'email' => $email,
56+
], [
57+
'password' => Hash::make(Str::random(72)),
58+
]);
59+
}
60+
61+
private function successUrl(): string
62+
{
63+
return Str::replace('abc', '{CHECKOUT_SESSION_ID}', route('order.success', ['checkoutSessionId' => 'abc']));
64+
}
65+
66+
public function render()
67+
{
68+
return view('livewire.mobile-pricing');
69+
}
70+
}

app/Livewire/PurchaseModal.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace App\Livewire;
4+
5+
use Livewire\Attributes\Validate;
6+
use Livewire\Component;
7+
8+
class PurchaseModal extends Component
9+
{
10+
public bool $showModal = false;
11+
12+
#[Validate]
13+
public string $email = '';
14+
15+
public ?string $selectedPlan = null;
16+
17+
protected $rules = [
18+
'email' => 'required|email',
19+
];
20+
21+
public function openModal($plan): void
22+
{
23+
$this->selectedPlan = $plan;
24+
$this->showModal = true;
25+
}
26+
27+
public function closeModal(): void
28+
{
29+
$this->showModal = false;
30+
$this->reset('email', 'selectedPlan');
31+
$this->resetValidation();
32+
}
33+
34+
public function emitEmail()
35+
{
36+
$this->validate();
37+
38+
$this->dispatch('email-submitted', [
39+
'email' => $this->email,
40+
'plan' => $this->selectedPlan,
41+
]);
42+
43+
$this->closeModal();
44+
}
45+
46+
public function render()
47+
{
48+
return view('livewire.purchase-modal');
49+
}
50+
}

resources/views/early-adopter.blade.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,7 @@ class="absolute inset-0 -z-10 h-full w-full object-cover"
608608
</section>
609609

610610
{{-- Pricing Section --}}
611-
<x-mobile-pricing />
611+
<livewire:mobile-pricing />
612612

613613
{{-- Testimonials Section --}}
614614
{{-- <x-testimonials /> --}}
@@ -843,4 +843,5 @@ class="dark:text-white"
843843
</p>
844844
</article>
845845
</section>
846+
<!-- Purchase Modal Component is now included in the mobile-pricing component -->
846847
</x-layout>

resources/views/components/mobile-pricing.blade.php renamed to resources/views/livewire/mobile-pricing.blade.php

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -118,14 +118,25 @@ class="size-5 shrink-0"
118118
</p>
119119
</div>
120120

121-
{{-- Button --}}
122-
<a
123-
href="{{ \App\Enums\Subscription::Mini->stripePaymentLink() }}"
124-
class="my-5 block w-full rounded-2xl bg-zinc-200 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
125-
aria-label="Get started with Mini plan"
126-
>
127-
Get started
128-
</a>
121+
@auth
122+
<button
123+
type="button"
124+
wire:click="createCheckoutSession('mini')"
125+
class="my-5 block w-full rounded-2xl bg-zinc-200 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
126+
aria-label="Get started with Mini plan"
127+
>
128+
Get started
129+
</button>
130+
@else
131+
<button
132+
type="button"
133+
@click="$dispatch('open-purchase-modal', { plan: 'mini' })"
134+
class="my-5 block w-full rounded-2xl bg-zinc-200 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
135+
aria-label="Get started with Mini plan"
136+
>
137+
Get started
138+
</button>
139+
@endauth
129140

130141
{{-- Features --}}
131142
<div
@@ -281,14 +292,25 @@ class="size-5 shrink-0"
281292
</p>
282293
</div>
283294

284-
{{-- Button --}}
285-
<a
286-
href="{{ \App\Enums\Subscription::Pro->stripePaymentLink() }}"
287-
class="my-5 block w-full rounded-2xl bg-zinc-200 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
288-
aria-label="Get started with Pro plan"
289-
>
290-
Get started
291-
</a>
295+
@auth
296+
<button
297+
type="button"
298+
wire:click="createCheckoutSession('pro')"
299+
class="my-5 block w-full rounded-2xl bg-zinc-200 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
300+
aria-label="Get started with Pro plan"
301+
>
302+
Get started
303+
</button>
304+
@else
305+
<button
306+
type="button"
307+
@click="$dispatch('open-purchase-modal', { plan: 'pro' })"
308+
class="my-5 block w-full rounded-2xl bg-zinc-200 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
309+
aria-label="Get started with Pro plan"
310+
>
311+
Get started
312+
</button>
313+
@endauth
292314

293315
{{-- Features --}}
294316
<div
@@ -452,14 +474,25 @@ class="size-5 shrink-0"
452474
</p>
453475
</div>
454476

455-
{{-- Button --}}
456-
<a
457-
href="{{ \App\Enums\Subscription::Max->stripePaymentLink() }}"
458-
class="my-5 block w-full rounded-2xl bg-zinc-800 py-4 text-center text-sm font-medium text-white transition duration-200 ease-in-out hover:bg-zinc-900 dark:bg-[#d68ffe] dark:text-black dark:hover:bg-[#e1acff]"
459-
aria-label="Get started with Max plan"
460-
>
461-
Get started
462-
</a>
477+
@auth
478+
<button
479+
type="button"
480+
wire:click="createCheckoutSession('max')"
481+
class="my-5 block w-full rounded-2xl bg-zinc-800 py-4 text-center text-sm font-medium text-white transition duration-200 ease-in-out hover:bg-zinc-900 dark:bg-[#d68ffe] dark:text-black dark:hover:bg-[#e1acff]"
482+
aria-label="Get started with Max plan"
483+
>
484+
Get started
485+
</button>
486+
@else
487+
<button
488+
type="button"
489+
@click="$dispatch('open-purchase-modal', { plan: 'max' })"
490+
class="my-5 block w-full rounded-2xl bg-zinc-800 py-4 text-center text-sm font-medium text-white transition duration-200 ease-in-out hover:bg-zinc-900 dark:bg-[#d68ffe] dark:text-black dark:hover:bg-[#e1acff]"
491+
aria-label="Get started with Max plan"
492+
>
493+
Get started
494+
</button>
495+
@endauth
463496

464497
{{-- Features --}}
465498
<div
@@ -569,4 +602,7 @@ class="grid size-7 shrink-0 place-items-center rounded-xl bg-[#D4FD7D] dark:bg-[
569602
</div>
570603
</div>
571604
</div>
605+
@guest
606+
<livewire:purchase-modal />
607+
@endguest
572608
</section>
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<div>
2+
<div
3+
x-data="{
4+
open: @entangle('showModal'),
5+
selectedPlan: @entangle('selectedPlan'),
6+
}"
7+
@open-purchase-modal.window="
8+
open = true;
9+
selectedPlan = $event.detail.plan;
10+
$wire.openModal($event.detail.plan);
11+
"
12+
>
13+
<!-- Modal Backdrop -->
14+
<div
15+
x-show="open"
16+
x-transition:enter="transition duration-300 ease-out"
17+
x-transition:enter-start="opacity-0"
18+
x-transition:enter-end="opacity-100"
19+
x-transition:leave="transition duration-200 ease-in"
20+
x-transition:leave-start="opacity-100"
21+
x-transition:leave-end="opacity-0"
22+
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
23+
x-cloak
24+
></div>
25+
26+
<!-- Modal Content -->
27+
<div
28+
x-show="open"
29+
x-transition:enter="transition duration-300 ease-out"
30+
x-transition:enter-start="scale-95 opacity-0"
31+
x-transition:enter-end="scale-100 opacity-100"
32+
x-transition:leave="transition duration-200 ease-in"
33+
x-transition:leave-start="scale-100 opacity-100"
34+
x-transition:leave-end="scale-95 opacity-0"
35+
class="fixed inset-0 z-50 flex items-center justify-center p-4"
36+
x-cloak
37+
>
38+
<div
39+
@click.away="open = false"
40+
class="w-full max-w-md rounded-2xl bg-white p-8 shadow-xl dark:bg-mirage"
41+
>
42+
<div class="mb-6 text-center">
43+
<h3 class="text-xl font-semibold dark:text-white">
44+
Get Started with NativePHP
45+
</h3>
46+
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
47+
Enter your email to continue to checkout
48+
</p>
49+
</div>
50+
51+
<form
52+
wire:submit.prevent="emitEmail"
53+
class="space-y-6"
54+
>
55+
<div>
56+
<label
57+
for="email"
58+
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
59+
>
60+
Email Address
61+
</label>
62+
<input
63+
type="email"
64+
id="email"
65+
wire:model.blur="email"
66+
class="w-full rounded-lg border border-gray-300 px-4 py-2.5 focus:border-purple-500 focus:outline-none focus:ring-2 focus:ring-purple-500/50 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:focus:border-purple-400"
67+
placeholder="[email protected]"
68+
required
69+
/>
70+
@error('email')
71+
<p class="mt-1 text-sm text-red-600">
72+
{{ $message }}
73+
</p>
74+
@enderror
75+
</div>
76+
77+
<div class="flex items-center justify-between gap-3">
78+
<button
79+
type="button"
80+
wire:click="closeModal"
81+
class="rounded-xl bg-zinc-200 px-8 py-4 text-center text-sm font-medium transition duration-200 ease-in-out hover:bg-zinc-800 hover:text-white dark:bg-slate-700/30 dark:hover:bg-slate-700/40"
82+
>
83+
Cancel
84+
</button>
85+
<button
86+
type="submit"
87+
class="rounded-xl bg-zinc-800 px-8 py-4 text-center text-sm font-medium text-white transition duration-200 ease-in-out hover:bg-zinc-900 dark:bg-[#d68ffe] dark:text-black dark:hover:bg-[#e1acff]"
88+
>
89+
Next
90+
</button>
91+
</div>
92+
</form>
93+
</div>
94+
</div>
95+
</div>
96+
</div>

0 commit comments

Comments
 (0)