diff --git a/app/Livewire/MobilePricing.php b/app/Livewire/MobilePricing.php
new file mode 100644
index 00000000..46ac17a4
--- /dev/null
+++ b/app/Livewire/MobilePricing.php
@@ -0,0 +1,73 @@
+ 'handlePurchaseRequest',
+ ];
+
+ public function handlePurchaseRequest(array $data)
+ {
+ $user = $this->findOrCreateUser($data['email']);
+
+ return $this->createCheckoutSession($data['plan'], $user);
+ }
+
+ public function createCheckoutSession(string $plan, ?User $user = null)
+ {
+ if (! ($user ??= Auth::user())) {
+ return;
+ }
+
+ if (! ($subscription = Subscription::tryFrom($plan))) {
+ return;
+ }
+
+ $user->createOrGetStripeCustomer();
+
+ $checkout = $user
+ ->newSubscription('default', $subscription->stripePriceId())
+ ->allowPromotionCodes()
+ ->checkout([
+ 'success_url' => $this->successUrl(),
+ 'cancel_url' => route('early-adopter'),
+ ]);
+
+ return redirect($checkout->url);
+ }
+
+ private function findOrCreateUser(string $email): User
+ {
+ return User::firstOrCreate([
+ 'email' => $email,
+ ], [
+ 'password' => Hash::make(Str::random(72)),
+ ]);
+ }
+
+ private function successUrl(): string
+ {
+ // This is a hack to get the route() function to work. If you try
+ // to pass {CHECKOUT_SESSION_ID} to the route function, it will
+ // throw an error.
+ return Str::replace(
+ 'abc',
+ '{CHECKOUT_SESSION_ID}',
+ route('order.success', ['checkoutSessionId' => 'abc'])
+ );
+ }
+
+ public function render()
+ {
+ return view('livewire.mobile-pricing');
+ }
+}
diff --git a/app/Livewire/PurchaseModal.php b/app/Livewire/PurchaseModal.php
new file mode 100644
index 00000000..e968ee06
--- /dev/null
+++ b/app/Livewire/PurchaseModal.php
@@ -0,0 +1,51 @@
+ 'required|email',
+ ];
+
+ #[Renderless]
+ public function setPlan(string $plan): void
+ {
+ $this->selectedPlan = $plan;
+ }
+
+ public function closeModal(): void
+ {
+ $this->showModal = false;
+ $this->reset('email', 'selectedPlan');
+ $this->resetValidation();
+ }
+
+ public function submit(): void
+ {
+ $this->validate();
+
+ $this->dispatch('purchase-request-submitted', [
+ 'email' => $this->email,
+ 'plan' => $this->selectedPlan,
+ ]);
+
+ $this->closeModal();
+ }
+
+ public function render()
+ {
+ return view('livewire.purchase-modal');
+ }
+}
diff --git a/resources/views/early-adopter.blade.php b/resources/views/early-adopter.blade.php
index 0963689f..c208f3fa 100644
--- a/resources/views/early-adopter.blade.php
+++ b/resources/views/early-adopter.blade.php
@@ -608,7 +608,7 @@ class="absolute inset-0 -z-10 h-full w-full object-cover"
{{-- Pricing Section --}}
-
+
{{-- Testimonials Section --}}
{{-- --}}
@@ -676,11 +676,14 @@ class="mx-auto flex w-full max-w-2xl flex-col items-center gap-4 pt-10"
-
+
- Yes! Once we've hit sustainability and can afford to continue
- investing in this project indirectly, then a version of it will
- be fully open source and made available for free.
+ Yes! Once we've hit sustainability and can afford to
+ continue investing in this project indirectly, then a
+ version of it will be fully open source and made available
+ for free.
- If you purchased after May 6, 2025, you should get an invoice with your receipt via email.
+ If you purchased after May 6, 2025, you should get an
+ invoice with your receipt via email.
For purchases made before this, you simply need to
follow the instructions here
diff --git a/resources/views/components/mobile-pricing.blade.php b/resources/views/livewire/mobile-pricing.blade.php
similarity index 86%
rename from resources/views/components/mobile-pricing.blade.php
rename to resources/views/livewire/mobile-pricing.blade.php
index 7b050437..587d5276 100644
--- a/resources/views/components/mobile-pricing.blade.php
+++ b/resources/views/livewire/mobile-pricing.blade.php
@@ -118,14 +118,25 @@ class="size-5 shrink-0"
- {{-- Button --}}
-
- Get started
-
+ @auth
+
+ @else
+
+ @endauth
{{-- Features --}}
- {{-- Button --}}
-
- Get started
-
+ @auth
+
+ @else
+
+ @endauth
{{-- Features --}}
- {{-- Button --}}
-
- Get started
-
+ @auth
+
+ @else
+
+ @endauth
{{-- Features --}}