# WhatsApp Auth Swap Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**Goal:** Remove `phone` from `users`, use `customers.whatsapp_number` / `owners.whatsapp_number` as the contact identifier, and enable registration/login with WhatsApp number while keeping email as the alternative.

**Architecture:** Database migrations drop `users.phone` and make `email` nullable; customer/owner `whatsapp_number` becomes nullable. Auth endpoints switch from `phone` to `whatsapp_number`; employees/admins drop phone entirely and use email only.

**Tech Stack:** Laravel 13, PHP 8.4, Pest, SQLite/MySQL

---

### Task 1: Database migrations

**Files:**
- Create: `database/migrations/2026_07_01_000001_drop_phone_from_users_table.php`
- Create: `database/migrations/2026_07_01_000002_make_customers_whatsapp_number_unique.php`
- Create: `database/migrations/2026_07_01_000003_make_owners_whatsapp_number_unique.php`

- [ ] **Step 1: Drop `phone` from users and make email nullable**

Create `database/migrations/2026_07_01_000001_drop_phone_from_users_table.php`:
```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('phone');
            $table->string('email')->nullable()->change();
        });
    }

    public function down(): void
    {
        Schema::table('users', function (Blueprint $table) {
            $table->string('phone')->unique()->after('email');
            $table->string('email')->nullable(false)->change();
        });
    }
};
```

- [ ] **Step 2: Make customers.whatsapp_number nullable and unique**

Create `database/migrations/2026_07_01_000002_make_customers_whatsapp_number_unique.php`:
```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('customers', function (Blueprint $table) {
            $table->string('whatsapp_number')->nullable()->unique()->change();
        });
    }

    public function down(): void
    {
        Schema::table('customers', function (Blueprint $table) {
            $table->dropUnique(['whatsapp_number']);
            $table->string('whatsapp_number')->nullable(false)->change();
        });
    }
};
```

- [ ] **Step 3: Make owners.whatsapp_number nullable and unique**

Create `database/migrations/2026_07_01_000003_make_owners_whatsapp_number_unique.php`:
```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::table('owners', function (Blueprint $table) {
            $table->string('whatsapp_number')->nullable()->unique()->change();
        });
    }

    public function down(): void
    {
        Schema::table('owners', function (Blueprint $table) {
            $table->dropUnique(['whatsapp_number']);
            $table->string('whatsapp_number')->nullable(false)->change();
        });
    }
};
```

---

### Task 2: User model helper

**Files:**
- Modify: `app/Models/User.php`

- [ ] **Step 1: Add `` helper**

Add after `profile()`:
```php
public function whatsapp_number: ?string
{
    return $this->customer?->whatsapp_number ?? $this->owner?->whatsapp_number;
}
```

---

### Task 3: Auth requests

**Files:**
- Modify: `app/Http/Requests/Auth/LoginRequest.php`
- Modify: `app/Http/Requests/Auth/RegisterCustomerRequest.php`
- Modify: `app/Http/Requests/Auth/RegisterOwnerRequest.php`
- Modify: `app/Http/Requests/Auth/RegisterAdminRequest.php`
- Modify: `app/Http/Requests/Auth/VerifyOtpRequest.php`
- Modify: `app/Http/Requests/Auth/ResendOtpRequest.php`
- Modify: `app/Http/Requests/Auth/ForgotPasswordRequest.php`
- Modify: `app/Http/Requests/Auth/ResetPasswordRequest.php`

- [ ] **Step 1: LoginRequest**

Replace rules with:
```php
return [
    'email' => ['nullable', 'string', 'email', 'required_without:whatsapp_number'],
    'whatsapp_number' => ['nullable', 'string', 'max:255', 'required_without:email', new WhatsAppNumber],
    'password' => ['required', 'string'],
];
```

- [ ] **Step 2: RegisterCustomerRequest**

Replace rules with:
```php
return [
    'name' => ['required', 'string', 'max:255'],
    'email' => ['nullable', 'string', 'email', 'max:255', 'required_without:whatsapp_number', 'unique:users,email'],
    'whatsapp_number' => ['nullable', 'string', 'max:255', 'required_without:email', 'unique:customers,whatsapp_number', new WhatsAppNumber],
    'password' => ['required', 'string', Password::default(), 'confirmed'],
];
```

- [ ] **Step 3: RegisterOwnerRequest**

Same as RegisterCustomerRequest but unique against `owners,whatsapp_number`.

- [ ] **Step 4: RegisterAdminRequest**

Remove `phone` rules; keep email required.

- [ ] **Step 5: VerifyOtpRequest / ResendOtpRequest**

Replace `phone` with `whatsapp_number` (required_without email, XOR).

- [ ] **Step 6: ForgotPasswordRequest / ResetPasswordRequest**

Replace `phone` with `whatsapp_number` (required, WhatsAppNumber rule).

---

### Task 4: Auth service

**Files:**
- Modify: `app/Services/AuthService.php`

- [ ] **Step 1: Update findUserByContact and contact helpers**

```php
public function findUserByContact(?string $whatsappNumber = null, ?string $email = null): User
{
    return User::where(function (Builder $query) use ($whatsappNumber, $email) {
        if ($whatsappNumber) {
            $query->orWhereHas('customer', fn ($q) => $q->where('whatsapp_number', $whatsappNumber))
                  ->orWhereHas('owner', fn ($q) => $q->where('whatsapp_number', $whatsappNumber));
        }

        if ($email) {
            $query->orWhere('email', $email);
        }
    })->firstOrFail();
}

public function contactKey(?string $whatsappNumber = null, ?string $email = null): string
{
    return $this->contactKeyForUser($this->findUserByContact($whatsappNumber, $email));
}

public function contactKeyForUser(User $user): ?string
{
    return $user->whatsapp_number;
}
```

---

### Task 5: Auth controller

**Files:**
- Modify: `app/Http/Controllers/Api/AuthController.php`

- [ ] **Step 1: Update login to use whatsapp_number**

```php
public function login(LoginRequest $request): JsonResponse
{
    $field = $request->has('email') ? 'email' : 'whatsapp_number';
    $value = $request->validated($field);
    $password = $request->validated('password');

    if ($field === 'email') {
        $user = User::where('email', $value)->first();
    } else {
        $user = User::whereHas('customer', fn ($q) => $q->where('whatsapp_number', $value))
                    ->orWhereHas('owner', fn ($q) => $q->where('whatsapp_number', $value))
                    ->first();
    }

    if (! $user || ! Hash::check($password, $user->password) || ! $user->is_verified) {
        return response()->json(['message' => 'Invalid credentials.'], 401);
    }

    return AuthService::issueAuthToken($user)->response();
}
```

- [ ] **Step 2: Update registerOwner/registerCustomer**

Remove `phone` from User creation; make email and whatsapp_number conditional; create profile with nullable whatsapp_number; auto-verify when whatsapp is absent.

- [ ] **Step 3: Update verify/resend/forgot/reset**

Replace `$request->validated('phone')` with `$request->validated('whatsapp_number')`; use `findUserByContact` / `contactKeyForUser`.

---

### Task 6: Profile and employee/admin controllers/requests

**Files:**
- Modify: `app/Http/Controllers/Api/ProfileController.php`
- Modify: `app/Http/Requests/Customer/UpdateProfileRequest.php`
- Modify: `app/Http/Controllers/Api/Owner/EmployeeController.php`
- Modify: `app/Http/Requests/Owner/StoreEmployeeRequest.php`
- Modify: `app/Http/Requests/Owner/UpdateEmployeeRequest.php`
- Modify: `app/Http/Controllers/Api/Admin/AdminController.php`
- Modify: `app/Http/Requests/Auth/RegisterAdminRequest.php`

- [ ] **Step 1: Remove phone from profile update**

UpdateProfileRequest removes `phone`; keeps optional `whatsapp_number`.
ProfileController removes `phone` from `$userData`.

- [ ] **Step 2: Remove phone from employee CRUD**

StoreEmployeeRequest and UpdateEmployeeRequest remove `phone`; controllers remove `phone` from User creation/update.

- [ ] **Step 3: Remove phone from admin creation**

RegisterAdminRequest and AdminController remove `phone`.

---

### Task 7: Pending customer and on-arrival reservation

**Files:**
- Modify: `app/Http/Controllers/Api/PendingCustomerController.php`
- Modify: `app/Http/Controllers/ReservationController.php`
- Modify: `app/Models/PendingCustomer.php`

- [ ] **Step 1: Use whatsapp_number as lookup key**

Replace User lookups by `phone` with lookups through customer relation by `whatsapp_number`; replace `PendingCustomer::byPhone` with `byWhatsappNumber`.

---

### Task 8: Resources, rate limiter, console command, factories

**Files:**
- Modify: `app/Http/Resources/UserResource.php`
- Modify: `app/Http/Resources/Admin/AdminUserResource.php`
- Modify: `app/Providers/AppServiceProvider.php`
- Modify: `app/Console/Commands/CreateSuperAdmin.php`
- Modify: `database/factories/UserFactory.php`

- [ ] **Step 1: Replace phone with whatsapp_number in resources**

`'whatsapp_number' => $this->whatsapp_number`.

- [ ] **Step 2: Update rate limiter key**

Change `$request->input('phone')` to `$request->input('whatsapp_number')`.

- [ ] **Step 3: Remove phone from CreateSuperAdmin**

Remove `--phone` option, validation, and User creation field.

- [ ] **Step 4: Update UserFactory**

Remove `phone` field.

---

### Task 9: Tests

**Files:**
- Modify all test files that pass `phone` to User creation, login, registration, OTP, employee/admin creation.

- [ ] **Step 1: Update auth tests**

Replace `phone` with `whatsapp_number`; create customer/owner records with whatsapp_number when needed.

- [ ] **Step 2: Update validation tests**

Adjust LoginRequest, RegisterOwnerRequest, VerifyOtpRequest expectations from `phone` to `whatsapp_number`.

- [ ] **Step 3: Update console/admin/employee tests**

Remove `phone` from super-admin CLI and admin/employee payloads.

---

### Task 10: Verify

- [ ] **Step 1: Refresh migrations**

Run:
```bash
/c/Users/hamma/.config/herd/bin/php84/php.exe artisan migrate:fresh --seed
```
Expected: migrations complete without errors.

- [ ] **Step 2: Run full test suite**

Run:
```bash
/c/Users/hamma/.config/herd/bin/php84/php.exe artisan test
```
Expected: all tests pass.

---

## Self-Review

1. **Spec coverage:** Migrations, model helper, auth requests/service/controller, profile/employee/admin, pending customer, resources, rate limiter, console command, factories, and tests are all covered.
2. **Placeholder scan:** No TBD/TODO placeholders.
3. **Type consistency:** `whatsapp_number` is consistently used as the contact identifier; `phone` is removed from User contexts.
