*/ use HasFactory, HasUuid; protected $fillable = ['user_id', 'name', 'email', 'locale', 'stripe_customer_id', 'status']; public function user(): BelongsTo { return $this->belongsTo(User::class); } public function orders(): HasMany { return $this->hasMany(Order::class); } public function instances(): HasMany { return $this->hasMany(Instance::class); } /** * Find or create the portal login account for this customer and link it. * Race-safe against the unique email index, and never links an operator * account — that would carry admin authorization into an impersonated * "customer" session. */ public function ensureUser(): User { if ($this->user) { return $this->user; } if (($existing = User::query()->where('email', $this->email)->first()) !== null) { $this->assertNotAdmin($existing); $this->update(['user_id' => $existing->id]); return $existing; } try { $user = User::query()->create([ 'email' => $this->email, 'name' => $this->name, 'password' => Hash::make(Str::random(40)), 'is_admin' => false, ]); } catch (UniqueConstraintViolationException) { // Concurrent first-time creation — re-fetch and link the winner. $user = User::query()->where('email', $this->email)->firstOrFail(); $this->assertNotAdmin($user); } $this->update(['user_id' => $user->id]); return $user->refresh(); } private function assertNotAdmin(User $user): void { if ($user->is_admin) { throw new RuntimeException( "Refusing to link admin account {$user->email} as a portal login for customer {$this->id}.", ); } } }