Align commerce module with the monorepo module structure by updating all namespaces to use the Core\Mod\Commerce convention. This change supports the recent monorepo separation and ensures consistency with other modules. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
194 lines
5.5 KiB
PHP
194 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace Core\Mod\Commerce\View\Modal\Web;
|
|
|
|
use Illuminate\Auth\Events\Registered;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Hash;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Attributes\Validate;
|
|
use Livewire\Component;
|
|
use Core\Mod\Commerce\Models\Order;
|
|
use Core\Mod\Tenant\Models\User;
|
|
use Core\Mod\Tenant\Models\Workspace;
|
|
|
|
#[Layout('shared::layouts.checkout')]
|
|
class CheckoutSuccess extends Component
|
|
{
|
|
public ?Order $order = null;
|
|
|
|
public string $orderNumber = '';
|
|
|
|
public bool $isPending = false;
|
|
|
|
public bool $needsAccount = false;
|
|
|
|
public string $guestEmail = '';
|
|
|
|
// Registration form fields
|
|
#[Validate('required|string|max:255')]
|
|
public string $name = '';
|
|
|
|
#[Validate('required|string|min:8|confirmed')]
|
|
public string $password = '';
|
|
|
|
public string $password_confirmation = '';
|
|
|
|
public function mount(?string $order = null): void
|
|
{
|
|
if (! $order) {
|
|
return;
|
|
}
|
|
|
|
$this->orderNumber = $order;
|
|
$foundOrder = Order::where('order_number', $order)->first();
|
|
|
|
if (! $foundOrder) {
|
|
return;
|
|
}
|
|
|
|
// Check if this is a guest checkout that needs account creation
|
|
if (! Auth::check()) {
|
|
// Check if order's workspace is a temporary guest workspace
|
|
$workspace = $foundOrder->workspace;
|
|
if ($workspace && str_starts_with($workspace->slug, 'checkout-')) {
|
|
$this->needsAccount = true;
|
|
$this->guestEmail = $workspace->billing_email ?? '';
|
|
$this->order = $foundOrder;
|
|
$this->isPending = $this->order->isPending() || $this->order->isProcessing();
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Verify ownership: user must own the workspace that placed this order
|
|
if ($this->authorizeOrder($foundOrder)) {
|
|
$this->order = $foundOrder;
|
|
$this->isPending = $this->order->isPending() || $this->order->isProcessing();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify the current user is authorised to view this order.
|
|
*/
|
|
protected function authorizeOrder(Order $order): bool
|
|
{
|
|
$user = Auth::user();
|
|
|
|
// If not logged in, don't show order details (just generic success)
|
|
if (! $user instanceof User) {
|
|
return false;
|
|
}
|
|
|
|
// Check if order belongs to user's workspace
|
|
$workspace = $user->defaultHostWorkspace();
|
|
|
|
if (! $workspace) {
|
|
return false;
|
|
}
|
|
|
|
return $order->workspace_id === $workspace->id;
|
|
}
|
|
|
|
/**
|
|
* Create account for guest checkout user and claim their workspace.
|
|
*/
|
|
public function createAccount(): void
|
|
{
|
|
$this->validate();
|
|
|
|
// Check email isn't already taken
|
|
if (User::where('email', $this->guestEmail)->exists()) {
|
|
$this->addError('email', 'An account with this email already exists. Please log in instead.');
|
|
|
|
return;
|
|
}
|
|
|
|
try {
|
|
$user = DB::transaction(function () {
|
|
// Create the user
|
|
$user = User::create([
|
|
'name' => $this->name,
|
|
'email' => $this->guestEmail,
|
|
'password' => Hash::make($this->password),
|
|
]);
|
|
|
|
// Update the guest workspace to be a proper workspace
|
|
$workspace = $this->order->workspace;
|
|
if ($workspace) {
|
|
$workspace->update([
|
|
'name' => $this->name ?: 'My Workspace',
|
|
'slug' => $this->generateUniqueSlug($this->name ?: $this->guestEmail),
|
|
'is_active' => true,
|
|
]);
|
|
|
|
// Attach user to workspace as owner
|
|
$user->hostWorkspaces()->attach($workspace->id, [
|
|
'role' => 'owner',
|
|
'is_default' => true,
|
|
]);
|
|
}
|
|
|
|
return $user;
|
|
});
|
|
|
|
// Fire registered event
|
|
event(new Registered($user));
|
|
|
|
// Log them in
|
|
Auth::login($user);
|
|
|
|
// Clear the needs account flag
|
|
$this->needsAccount = false;
|
|
|
|
// Refresh authorization
|
|
$this->order->refresh();
|
|
|
|
} catch (\Exception $e) {
|
|
report($e);
|
|
$this->addError('email', 'Something went wrong. Please try again.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate a unique workspace slug.
|
|
*/
|
|
protected function generateUniqueSlug(string $name): string
|
|
{
|
|
$baseSlug = \Illuminate\Support\Str::slug($name);
|
|
if (str_contains($baseSlug, '@')) {
|
|
$baseSlug = \Illuminate\Support\Str::slug(\Illuminate\Support\Str::before($name, '@'));
|
|
}
|
|
|
|
$slug = $baseSlug;
|
|
$counter = 1;
|
|
|
|
while (Workspace::where('slug', $slug)->where('id', '!=', $this->order->workspace_id)->exists()) {
|
|
$slug = $baseSlug.'-'.$counter;
|
|
$counter++;
|
|
}
|
|
|
|
return $slug;
|
|
}
|
|
|
|
public function checkStatus(): void
|
|
{
|
|
if (! $this->order) {
|
|
return;
|
|
}
|
|
|
|
$this->order->refresh();
|
|
$this->isPending = $this->order->isPending() || $this->order->isProcessing();
|
|
|
|
// If paid, we can stop polling
|
|
if ($this->order->isPaid()) {
|
|
$this->isPending = false;
|
|
}
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('commerce::web.checkout.checkout-success');
|
|
}
|
|
}
|