   718→- Restock flag: calls `InventoryService::restock()` for each line
   719→
   720→#### Step 5.6: Fulfillment Service
   721→
   722→**File to create:** `App\Services\FulfillmentService`
   723→
   724→| Method | Description |
   725→|--------|-------------|
   726→| `create(Order $order, array $lines, ?array $tracking): Fulfillment` | Creates fulfillment with specified lines and quantities. **Fulfillment guard:** Throws exception if `financial_status` is not `paid` or `partially_refunded`. |
   727→| `markAsShipped(Fulfillment $fulfillment, ?array $tracking): void` | Sets tracking info, transitions to `shipped`, sets `shipped_at` |
   728→| `markAsDelivered(Fulfillment $fulfillment): void` | Transitions to `delivered`, sets `delivered_at`. Dispatches `FulfillmentDelivered` event. |
   729→
   730→Updates `order.fulfillment_status` based on whether all lines are fulfilled (`fulfilled`) or some (`partial`).
   731→
   732→**Fulfillment guard:** Before creating a fulfillment, the service checks that the order's `financial_status` is `paid` or `partially_refunded`. If the guard fails, a `FulfillmentGuardException` is thrown. This prevents shipping items before payment is confirmed (especially important for bank transfer orders).
   733→
   734→**Events to create:**
   735→
   736→| Type | Name | Purpose |
   737→|------|------|---------|
   738→| Event | `OrderCreated` | Dispatched when order is created from checkout |
   739→| Event | `OrderPaid` | Dispatched when payment is captured |
   740→| Event | `OrderFulfilled` | Dispatched when all order lines are fulfilled |
   741→| Event | `OrderCancelled` | Dispatched when order is cancelled |
   742→| Event | `OrderRefunded` | Dispatched when refund is processed |
   743→
   744→---
   745→
   746→### Phase 6: Customer Accounts
   747→
   748→**Priority:** MEDIUM - enhances the shopping experience.
   749→
   750→#### Step 6.1: Customer Auth
   751→
   752→- `Customer` model with custom `customer` guard (already configured in Phase 1)
   753→- Custom `CustomerUserProvider` - scopes queries by `store_id`
   754→- Customer email unique per store, not globally
   755→
   756→#### Step 6.2: Customer Account Pages (Livewire)
   757→
   758→**Files to create:**
   759→
   760→| Component | Route | Description |
   761→|-----------|-------|-------------|
   762→| `Storefront\Account\Auth\Login` | `GET /account/login` | Login form |
   763→| `Storefront\Account\Auth\Register` | `GET /account/register` | Registration form |
   764→| `Storefront\Account\Dashboard` | `GET /account` | Overview with recent orders |
   765→| `Storefront\Account\Orders\Index` | `GET /account/orders` | Paginated order history |
   766→| `Storefront\Account\Orders\Show` | `GET /account/orders/{orderNumber}` | Order detail with timeline |
   767→| `Storefront\Account\Addresses\Index` | `GET /account/addresses` | Address book CRUD |
   768→
   769→Logout: `POST /account/logout` handled by Livewire action. Invalidates session, regenerates CSRF token, redirects to `/account/login`.
   770→
   771→---
   772→
   773→### Phase 7: Admin Panel
   774→
   775→**Priority:** MEDIUM - merchant management interface.
   776→
   777→#### Step 7.1: Admin Layout
   778→
   779→**File to create:** `resources/views/livewire/admin/layout/app.blade.php`
   780→
   781→**Components to create:**
   782→
   783→| Component | Description |
   784→|-----------|-------------|
   785→| `Admin\Layout\Sidebar` | Fixed left sidebar (256px desktop, overlay mobile), navigation groups, Flux `brand`, `icon`, `separator` |
   786→| `Admin\Layout\TopBar` | Store selector dropdown, user profile, hamburger toggle (mobile) |
   787→| `Admin\Layout\Breadcrumbs` | Dynamic breadcrumb trail |
   788→
   789→Toast notifications via Livewire events.
   790→
   791→#### Step 7.2: Dashboard
   792→
   793→**File to create:** `Admin\Dashboard` Livewire component
   794→
   795→Requirements:
   796→- KPI tiles: total sales (period), order count, average order value, conversion rate
   797→- Orders chart (last 30 days line chart)
   798→- Recent orders table (last 10)
   799→- Date range filtering via Livewire properties
   800→
   801→#### Step 7.3: Product Management
   802→
   803→**Files to create:**
   804→
   805→| Component | Description |
   806→|-----------|-------------|
   807→| `Admin\Products\Index` | Product list with search, status filter, bulk actions (archive, delete), pagination |
   808→| `Admin\Products\Form` | Shared product form component used by both create and edit routes (create vs edit mode). Title, description (rich text), status, vendor, type, tags, variants builder, media upload. |
   809→
   810→> **Shared-form pattern:** Create and Edit routes render the same Livewire form component in create vs edit mode. Shared form components follow the pattern `App\Livewire\Admin\{Resource}\Form`, used for both create and edit routes as defined in Spec 03. This applies to Products, Collections, Discounts, and Pages.
   811→
   812→Variants builder: dynamic form to add options (Size, Color, etc.) and values, auto-generates variant matrix. Each variant row has price, SKU, barcode, weight, inventory quantity fields.
   813→
   814→Media upload: Livewire `WithFileUploads`, drag-and-drop, reorder, alt text editing.
   815→
   816→#### Step 7.4: Order Management
   817→

- Restock flag: calls `InventoryService::restock()` for each line

#### Step 5.6: Fulfillment Service

**File to create:** `App\Services\FulfillmentService`

| Method | Description |
|--------|-------------|
| `create(Order $order, array $lines, ?array $tracking): Fulfillment` | Creates fulfillment with specified lines and quantities. **Fulfillment guard:** Throws exception if `financial_status` is not `paid` or `partially_refunded`. |
| `markAsShipped(Fulfillment $fulfillment, ?array $tracking): void` | Sets tracking info, transitions to `shipped`, sets `shipped_at` |
| `markAsDelivered(Fulfillment $fulfillment): void` | Transitions to `delivered`, sets `delivered_at`. Dispatches `FulfillmentDelivered` event. |

Updates `order.fulfillment_status` based on whether all lines are fulfilled (`fulfilled`) or some (`partial`).

**Fulfillment guard:** Before creating a fulfillment, the service checks that the order's `financial_status` is `paid` or `partially_refunded`. If the guard fails, a `FulfillmentGuardException` is thrown. This prevents shipping items before payment is confirmed (especially important for bank transfer orders).

**Events to create:**

| Type | Name | Purpose |
|------|------|---------|
| Event | `OrderCreated` | Dispatched when order is created from checkout |
| Event | `OrderPaid` | Dispatched when payment is captured |
| Event | `OrderFulfilled` | Dispatched when all order lines are fulfilled |
| Event | `OrderCancelled` | Dispatched when order is cancelled |
| Event | `OrderRefunded` | Dispatched when refund is processed |

---

### Phase 6: Customer Accounts

**Priority:** MEDIUM - enhances the shopping experience.

#### Step 6.1: Customer Auth

- `Customer` model with custom `customer` guard (already configured in Phase 1)
- Custom `CustomerUserProvider` - scopes queries by `store_id`
- Customer email unique per store, not globally

#### Step 6.2: Customer Account Pages (Livewire)

**Files to create:**

| Component | Route | Description |
|-----------|-------|-------------|
| `Storefront\Account\Auth\Login` | `GET /account/login` | Login form |
| `Storefront\Account\Auth\Register` | `GET /account/register` | Registration form |
| `Storefront\Account\Dashboard` | `GET /account` | Overview with recent orders |
| `Storefront\Account\Orders\Index` | `GET /account/orders` | Paginated order history |
| `Storefront\Account\Orders\Show` | `GET /account/orders/{orderNumber}` | Order detail with timeline |
| `Storefront\Account\Addresses\Index` | `GET /account/addresses` | Address book CRUD |

Logout: `POST /account/logout` handled by Livewire action. Invalidates session, regenerates CSRF token, redirects to `/account/login`.

---

### Phase 7: Admin Panel

**Priority:** MEDIUM - merchant management interface.

#### Step 7.1: Admin Layout

**File to create:** `resources/views/livewire/admin/layout/app.blade.php`

**Components to create:**

| Component | Description |
|-----------|-------------|
| `Admin\Layout\Sidebar` | Fixed left sidebar (256px desktop, overlay mobile), navigation groups, Flux `brand`, `icon`, `separator` |
| `Admin\Layout\TopBar` | Store selector dropdown, user profile, hamburger toggle (mobile) |
| `Admin\Layout\Breadcrumbs` | Dynamic breadcrumb trail |

Toast notifications via Livewire events.

#### Step 7.2: Dashboard

**File to create:** `Admin\Dashboard` Livewire component

Requirements:
- KPI tiles: total sales (period), order count, average order value, conversion rate
- Orders chart (last 30 days line chart)
- Recent orders table (last 10)
- Date range filtering via Livewire properties

#### Step 7.3: Product Management

**Files to create:**

| Component | Description |
|-----------|-------------|
| `Admin\Products\Index` | Product list with search, status filter, bulk actions (archive, delete), pagination |
| `Admin\Products\Form` | Shared product form component used by both create and edit routes (create vs edit mode). Title, description (rich text), status, vendor, type, tags, variants builder, media upload. |

> **Shared-form pattern:** Create and Edit routes render the same Livewire form component in create vs edit mode. Shared form components follow the pattern `App\Livewire\Admin\{Resource}\Form`, used for both create and edit routes as defined in Spec 03. This applies to Products, Collections, Discounts, and Pages.

Variants builder: dynamic form to add options (Size, Color, etc.) and values, auto-generates variant matrix. Each variant row has price, SKU, barcode, weight, inventory quantity fields.

Media upload: Livewire `WithFileUploads`, drag-and-drop, reorder, alt text editing.

#### Step 7.4: Order Management


   817→
   818→**Files to create:**
   819→
   820→| Component | Description |
   821→|-----------|-------------|
   822→| `Admin\Orders\Index` | Order list with status filters, search by order number/email, date range |
   823→| `Admin\Orders\Show` | Order detail: timeline, line items, payment info, fulfillment modal, refund modal |
   824→
   825→Fulfillment modal: select lines and quantities, add tracking info, submit.
   826→Refund modal: enter amount (partial or full), reason, restock checkbox, submit.
   827→
   828→#### Step 7.5: Other Admin Sections
   829→
   830→**Files to create:**
   831→
   832→| Component | Description |
   833→|-----------|-------------|
   834→| `Admin\Collections\Index` | Collection list |
   835→| `Admin\Collections\Form` | Shared form component for create/edit collection with product picker, reorder products |
   836→| `Admin\Customers\Index` | Customer list with search |
   837→| `Admin\Customers\Show` | Customer detail: info, orders, addresses |
   838→| `Admin\Discounts\Index` | Discount list with status/type filters |
   839→| `Admin\Discounts\Form` | Shared form component for create/edit discount: code, type, value, dates, rules |
   840→| `Admin\Settings\Index` | Tabbed settings: General, Domains, Shipping, Taxes, Checkout, Notifications |
   841→| `Admin\Settings\Shipping` | Shipping zones CRUD, rates CRUD per zone |
   842→| `Admin\Settings\Taxes` | Tax mode toggle, manual rate configuration |
   843→| `Admin\Themes\Index` | Theme cards with Publish/Duplicate/Delete actions |
   844→| `Admin\Themes\Editor` | Left: sections. Center: preview. Right: settings. |
   845→| `Admin\Pages\Index` | Content pages list |
   846→| `Admin\Pages\Form` | Shared form component for create/edit page with rich text editor |
   847→| `Admin\Navigation\Index` | Menu management with drag-and-drop item ordering |
   848→| `Admin\Analytics\Index` | Sales chart, traffic, funnel visualization, date range |
   849→| `Admin\Search\Settings` | Synonyms, stop words, reindex button |
   850→| `Admin\Apps\Index` | App directory |
   851→| `Admin\Apps\Show` | Installed app detail |
   852→| `Admin\Developers\Index` | API token management, webhook subscriptions |
   853→
   854→---
   855→
   856→### Phase 8: Search
   857→
   858→**Priority:** LOW - enhances product discovery.
   859→
   860→#### Step 8.1: Migrations
   861→
   862→1. `create_search_settings_table` - PK: `store_id`
   863→2. `create_search_queries_table` - FK: `store_id`
   864→3. FTS5 virtual table migration (raw SQL) - create `products_fts` virtual table using FTS5 with columns: title, description, vendor, product_type, tags; content synced from `products` table
   865→
   866→#### Step 8.2: Search Service
   867→
   868→**File to create:** `App\Services\SearchService`
   869→
   870→| Method | Description |
   871→|--------|-------------|
   872→| `search(Store $store, string $query, array $filters, int $perPage): LengthAwarePaginator` | FTS5 query with store scoping |
   873→| `autocomplete(Store $store, string $prefix, int $limit): Collection` | Prefix matching for search-as-you-type |
   874→| `syncProduct(Product $product): void` | Upsert into FTS5 index |
   875→| `removeProduct(int $productId): void` | Delete from FTS5 index |
   876→
   877→**File to create:** `App\Observers\ProductObserver` - calls `SearchService::syncProduct()` on create/update, `removeProduct()` on delete.
   878→
   879→#### Step 8.3: Search UI
   880→
   881→**Files to create:**
   882→
   883→| Component | Description |
   884→|-----------|-------------|
   885→| `Storefront\Search\Modal` | Search-as-you-type modal with autocomplete results |
   886→| `Storefront\Search\Index` | Full search results page with filters (vendor, price range, collection), sort (relevance, price, newest), pagination |
   887→
   888→---
   889→
   890→### Phase 9: Analytics
   891→
   892→**Priority:** LOW - reporting and insights.
   893→
   894→#### Step 9.1: Migrations
   895→
   896→1. `create_analytics_events_table` - FK: `store_id`, `customer_id`
   897→2. `create_analytics_daily_table` - composite PK: `store_id` + `date`
   898→
   899→#### Step 9.2: Analytics Service
   900→
   901→**File to create:** `App\Services\AnalyticsService`
   902→
   903→| Method | Description |
   904→|--------|-------------|
   905→| `track(Store $store, string $type, array $properties, ?string $sessionId, ?int $customerId): void` | Inserts raw event |
   906→| `getDailyMetrics(Store $store, string $startDate, string $endDate): Collection` | Reads pre-aggregated daily data |
   907→
   908→Event types: `page_view`, `product_view`, `add_to_cart`, `remove_from_cart`, `checkout_started`, `checkout_completed`, `search`.
   909→
   910→**File to create:** `App\Jobs\AggregateAnalytics`
   911→
   912→Requirements:
   913→- Runs daily via `routes/console.php`
   914→- Aggregates raw events into `analytics_daily` rows
   915→- Calculates: orders_count, revenue_amount, aov_amount, visits_count, add_to_cart_count, checkout_started_count
   916→
   917→---
   918→
   919→### Phase 10: Apps and Webhooks
   920→
   921→**Priority:** LOW - extensibility.
   922→
   923→#### Step 10.1: Migrations
   924→
   925→1. `create_apps_table`
   926→2. `create_app_installations_table` - FK: `store_id`, `app_id`
   927→3. `create_oauth_clients_table` - FK: `app_id`
   928→4. `create_oauth_tokens_table` - FK: `installation_id`
   929→5. `create_webhook_subscriptions_table` - FK: `store_id`, `app_installation_id`
   930→6. `create_webhook_deliveries_table` - FK: `subscription_id`
   931→
   932→#### Step 10.2: Webhook Service
   933→
   934→**File to create:** `App\Services\WebhookService`
   935→
   936→| Method | Description |
   937→|--------|-------------|
   938→| `dispatch(Store $store, string $eventType, array $payload): void` | Finds matching subscriptions, queues delivery jobs |
   939→| `sign(string $payload, string $secret): string` | HMAC-SHA256 signature |
   940→| `verify(string $payload, string $signature, string $secret): bool` | Verify incoming webhook signatures |
   941→
   942→**File to create:** `App\Jobs\DeliverWebhook`
   943→
   944→Requirements:
   945→- HTTP POST to target URL with JSON payload
   946→- HMAC signature in `X-Platform-Signature` header, event type in `X-Platform-Event`, delivery ID in `X-Platform-Delivery-Id`, timestamp in `X-Platform-Timestamp`
   947→- Retry with exponential backoff: 1 min, 5 min, 30 min, 2 h, 12 h (6 total attempts: 1 initial + 5 retries; Laravel backoff config `[60, 300, 1800, 7200, 43200]`)
   948→- Records response in `webhook_deliveries`
   949→- Circuit breaker: after 5 consecutive failures, pause subscription
   950→
   951→---
   952→
   953→### Phase 11: Polish
   954→
   955→**Priority:** LOW but important for completeness.
   956→
   957→1. Accessibility audit: skip links on all pages, ARIA labels on interactive elements, focus management in modals
   958→2. Responsive testing: all pages render correctly at sm/md/lg/xl breakpoints
   959→3. Dark mode: `dark:` prefix on all storefront views and admin views
   960→4. Error pages: styled 404 and 503 pages matching the storefront theme
   961→5. Structured logging: JSON channel in `config/logging.php`
   962→6. Seed data: comprehensive seeders for demo stores
   963→
   964→---
   965→
   966→### Phase 12: Full Test Suite Execution
   967→
   968→**Priority:** Runs after all phases are complete.
   969→
   970→**Verification steps:**
   971→
   972→1. Run the full unit and feature test suite and confirm all tests pass
   973→2. Run all browser tests and confirm they pass
   974→3. Run the code style formatter/checker and confirm conformance
   975→4. Run a fresh migration with seeding and confirm all migrations and seeders succeed without errors
   976→5. Manual smoke: visit storefront, navigate products, add to cart, checkout
   977→6. Manual smoke: visit admin login, authenticate, manage products and orders
   978→
   979→---
   980→
   981→## 3. Pest Unit/Feature Test Specification
   982→
   983→All tests use Pest syntax. Feature tests use `RefreshDatabase` trait. Unit tests do not hit the database unless factories are involved.
   984→
   985→### Test Directory Structure
   986→
   987→| Directory | Files |
   988→|-----------|-------|
   989→| `tests/Unit/` | `PricingEngineTest.php`, `DiscountCalculatorTest.php`, `TaxCalculatorTest.php`, `ShippingCalculatorTest.php`, `CartVersionTest.php`, `HandleGeneratorTest.php` |
   990→| `tests/Feature/Tenancy/` | `TenantResolutionTest.php`, `StoreIsolationTest.php` |
   991→| `tests/Feature/Auth/` | `AdminAuthTest.php`, `CustomerAuthTest.php`, `SanctumTokenTest.php` |
   992→| `tests/Feature/Products/` | `ProductCrudTest.php`, `VariantTest.php`, `InventoryTest.php`, `CollectionTest.php`, `MediaUploadTest.php` |
   993→| `tests/Feature/Cart/` | `CartServiceTest.php`, `CartApiTest.php` |
   994→| `tests/Feature/Checkout/` | `CheckoutFlowTest.php`, `CheckoutStateTest.php`, `PricingIntegrationTest.php`, `DiscountTest.php`, `ShippingTest.php`, `TaxTest.php` |
   995→| `tests/Feature/Orders/` | `OrderCreationTest.php`, `RefundTest.php`, `FulfillmentTest.php` |
   996→| `tests/Feature/Payments/` | `MockPaymentProviderTest.php`, `PaymentServiceTest.php`, `BankTransferConfirmationTest.php` |
   997→| `tests/Feature/Customers/` | `CustomerAccountTest.php`, `AddressManagementTest.php` |
   998→| `tests/Feature/Admin/` | `DashboardTest.php`, `ProductManagementTest.php`, `OrderManagementTest.php`, `DiscountManagementTest.php`, `SettingsTest.php` |
   999→| `tests/Feature/Search/` | `SearchTest.php`, `AutocompleteTest.php` |
  1000→| `tests/Feature/Analytics/` | `EventIngestionTest.php`, `AggregationTest.php` |
  1001→| `tests/Feature/Webhooks/` | `WebhookDeliveryTest.php`, `WebhookSignatureTest.php` |
  1002→| `tests/Feature/Api/` | `StorefrontCartApiTest.php`, `StorefrontCheckoutApiTest.php`, `AdminProductApiTest.php`, `AdminOrderApiTest.php` |
  1003→| `tests/Browser/` | Covered in `08-PLAYWRIGHT-E2E-PLAN.md` |
  1004→
  1005→---
  1006→
  1007→### Unit Tests
  1008→
  1009→---
  1010→
  1011→#### tests/Unit/PricingEngineTest.php
  1012→
  1013→**Setup:** Instantiate `PricingEngine` directly. Mock dependencies or use simple value objects.
  1014→
  1015→| Test Name | Setup / Preconditions | Key Assertions | Category |
  1016→|-----------|----------------------|----------------|----------|

**Files to create:**

| Component | Description |
|-----------|-------------|
| `Admin\Orders\Index` | Order list with status filters, search by order number/email, date range |
| `Admin\Orders\Show` | Order detail: timeline, line items, payment info, fulfillment modal, refund modal |

Fulfillment modal: select lines and quantities, add tracking info, submit.
Refund modal: enter amount (partial or full), reason, restock checkbox, submit.

#### Step 7.5: Other Admin Sections

**Files to create:**

| Component | Description |
|-----------|-------------|
| `Admin\Collections\Index` | Collection list |
| `Admin\Collections\Form` | Shared form component for create/edit collection with product picker, reorder products |
| `Admin\Customers\Index` | Customer list with search |
| `Admin\Customers\Show` | Customer detail: info, orders, addresses |
| `Admin\Discounts\Index` | Discount list with status/type filters |
| `Admin\Discounts\Form` | Shared form component for create/edit discount: code, type, value, dates, rules |
| `Admin\Settings\Index` | Tabbed settings: General, Domains, Shipping, Taxes, Checkout, Notifications |
| `Admin\Settings\Shipping` | Shipping zones CRUD, rates CRUD per zone |
| `Admin\Settings\Taxes` | Tax mode toggle, manual rate configuration |
| `Admin\Themes\Index` | Theme cards with Publish/Duplicate/Delete actions |
| `Admin\Themes\Editor` | Left: sections. Center: preview. Right: settings. |
| `Admin\Pages\Index` | Content pages list |
| `Admin\Pages\Form` | Shared form component for create/edit page with rich text editor |
| `Admin\Navigation\Index` | Menu management with drag-and-drop item ordering |
| `Admin\Analytics\Index` | Sales chart, traffic, funnel visualization, date range |
| `Admin\Search\Settings` | Synonyms, stop words, reindex button |
| `Admin\Apps\Index` | App directory |
| `Admin\Apps\Show` | Installed app detail |
| `Admin\Developers\Index` | API token management, webhook subscriptions |

---

### Phase 8: Search

**Priority:** LOW - enhances product discovery.

#### Step 8.1: Migrations

1. `create_search_settings_table` - PK: `store_id`
2. `create_search_queries_table` - FK: `store_id`
3. FTS5 virtual table migration (raw SQL) - create `products_fts` virtual table using FTS5 with columns: title, description, vendor, product_type, tags; content synced from `products` table

#### Step 8.2: Search Service

**File to create:** `App\Services\SearchService`

| Method | Description |
|--------|-------------|
| `search(Store $store, string $query, array $filters, int $perPage): LengthAwarePaginator` | FTS5 query with store scoping |
| `autocomplete(Store $store, string $prefix, int $limit): Collection` | Prefix matching for search-as-you-type |
| `syncProduct(Product $product): void` | Upsert into FTS5 index |
| `removeProduct(int $productId): void` | Delete from FTS5 index |

**File to create:** `App\Observers\ProductObserver` - calls `SearchService::syncProduct()` on create/update, `removeProduct()` on delete.

#### Step 8.3: Search UI

**Files to create:**

| Component | Description |
|-----------|-------------|
| `Storefront\Search\Modal` | Search-as-you-type modal with autocomplete results |
| `Storefront\Search\Index` | Full search results page with filters (vendor, price range, collection), sort (relevance, price, newest), pagination |

---

### Phase 9: Analytics

**Priority:** LOW - reporting and insights.

#### Step 9.1: Migrations

1. `create_analytics_events_table` - FK: `store_id`, `customer_id`
2. `create_analytics_daily_table` - composite PK: `store_id` + `date`

#### Step 9.2: Analytics Service

**File to create:** `App\Services\AnalyticsService`

| Method | Description |
|--------|-------------|
| `track(Store $store, string $type, array $properties, ?string $sessionId, ?int $customerId): void` | Inserts raw event |
| `getDailyMetrics(Store $store, string $startDate, string $endDate): Collection` | Reads pre-aggregated daily data |

Event types: `page_view`, `product_view`, `add_to_cart`, `remove_from_cart`, `checkout_started`, `checkout_completed`, `search`.

**File to create:** `App\Jobs\AggregateAnalytics`

Requirements:
- Runs daily via `routes/console.php`
- Aggregates raw events into `analytics_daily` rows
- Calculates: orders_count, revenue_amount, aov_amount, visits_count, add_to_cart_count, checkout_started_count

---

### Phase 10: Apps and Webhooks

**Priority:** LOW - extensibility.

#### Step 10.1: Migrations

1. `create_apps_table`
2. `create_app_installations_table` - FK: `store_id`, `app_id`
3. `create_oauth_clients_table` - FK: `app_id`
4. `create_oauth_tokens_table` - FK: `installation_id`
5. `create_webhook_subscriptions_table` - FK: `store_id`, `app_installation_id`
6. `create_webhook_deliveries_table` - FK: `subscription_id`

#### Step 10.2: Webhook Service

**File to create:** `App\Services\WebhookService`

| Method | Description |
|--------|-------------|
| `dispatch(Store $store, string $eventType, array $payload): void` | Finds matching subscriptions, queues delivery jobs |
| `sign(string $payload, string $secret): string` | HMAC-SHA256 signature |
| `verify(string $payload, string $signature, string $secret): bool` | Verify incoming webhook signatures |

**File to create:** `App\Jobs\DeliverWebhook`

Requirements:
- HTTP POST to target URL with JSON payload
- HMAC signature in `X-Platform-Signature` header, event type in `X-Platform-Event`, delivery ID in `X-Platform-Delivery-Id`, timestamp in `X-Platform-Timestamp`
- Retry with exponential backoff: 1 min, 5 min, 30 min, 2 h, 12 h (6 total attempts: 1 initial + 5 retries; Laravel backoff config `[60, 300, 1800, 7200, 43200]`)
- Records response in `webhook_deliveries`
- Circuit breaker: after 5 consecutive failures, pause subscription

---

### Phase 11: Polish

**Priority:** LOW but important for completeness.

1. Accessibility audit: skip links on all pages, ARIA labels on interactive elements, focus management in modals
2. Responsive testing: all pages render correctly at sm/md/lg/xl breakpoints
3. Dark mode: `dark:` prefix on all storefront views and admin views
4. Error pages: styled 404 and 503 pages matching the storefront theme
5. Structured logging: JSON channel in `config/logging.php`
6. Seed data: comprehensive seeders for demo stores

---

### Phase 12: Full Test Suite Execution

**Priority:** Runs after all phases are complete.

**Verification steps:**

1. Run the full unit and feature test suite and confirm all tests pass
2. Run all browser tests and confirm they pass
3. Run the code style formatter/checker and confirm conformance
4. Run a fresh migration with seeding and confirm all migrations and seeders succeed without errors
5. Manual smoke: visit storefront, navigate products, add to cart, checkout
6. Manual smoke: visit admin login, authenticate, manage products and orders

---

## 3. Pest Unit/Feature Test Specification

All tests use Pest syntax. Feature tests use `RefreshDatabase` trait. Unit tests do not hit the database unless factories are involved.

### Test Directory Structure

| Directory | Files |
|-----------|-------|
| `tests/Unit/` | `PricingEngineTest.php`, `DiscountCalculatorTest.php`, `TaxCalculatorTest.php`, `ShippingCalculatorTest.php`, `CartVersionTest.php`, `HandleGeneratorTest.php` |
| `tests/Feature/Tenancy/` | `TenantResolutionTest.php`, `StoreIsolationTest.php` |
| `tests/Feature/Auth/` | `AdminAuthTest.php`, `CustomerAuthTest.php`, `SanctumTokenTest.php` |
| `tests/Feature/Products/` | `ProductCrudTest.php`, `VariantTest.php`, `InventoryTest.php`, `CollectionTest.php`, `MediaUploadTest.php` |
| `tests/Feature/Cart/` | `CartServiceTest.php`, `CartApiTest.php` |
| `tests/Feature/Checkout/` | `CheckoutFlowTest.php`, `CheckoutStateTest.php`, `PricingIntegrationTest.php`, `DiscountTest.php`, `ShippingTest.php`, `TaxTest.php` |
| `tests/Feature/Orders/` | `OrderCreationTest.php`, `RefundTest.php`, `FulfillmentTest.php` |
| `tests/Feature/Payments/` | `MockPaymentProviderTest.php`, `PaymentServiceTest.php`, `BankTransferConfirmationTest.php` |
| `tests/Feature/Customers/` | `CustomerAccountTest.php`, `AddressManagementTest.php` |
| `tests/Feature/Admin/` | `DashboardTest.php`, `ProductManagementTest.php`, `OrderManagementTest.php`, `DiscountManagementTest.php`, `SettingsTest.php` |
| `tests/Feature/Search/` | `SearchTest.php`, `AutocompleteTest.php` |
| `tests/Feature/Analytics/` | `EventIngestionTest.php`, `AggregationTest.php` |
| `tests/Feature/Webhooks/` | `WebhookDeliveryTest.php`, `WebhookSignatureTest.php` |
| `tests/Feature/Api/` | `StorefrontCartApiTest.php`, `StorefrontCheckoutApiTest.php`, `AdminProductApiTest.php`, `AdminOrderApiTest.php` |
| `tests/Browser/` | Covered in `08-PLAYWRIGHT-E2E-PLAN.md` |

---

### Unit Tests

---

#### tests/Unit/PricingEngineTest.php

**Setup:** Instantiate `PricingEngine` directly. Mock dependencies or use simple value objects.

| Test Name | Setup / Preconditions | Key Assertions | Category |
|-----------|----------------------|----------------|----------|


Good - I have all the information I need for the Phase 5 prompt. Now let me wait for Phase 4 to complete before launching Phase 5 (since Phase 5 depends on Phase 4's models).

Let me also check on the Phase 4 agent to see how it's progressing.


Let me check the Phase 4 agent progress.