type: CommandExecution

command: rg -n -C 4 "export|webhook|listener|Listener|analytics|apps/v1|OAuth|oauth|media" specs/02-API-ROUTES.md specs/05-BUSINESS-LOGIC.md specs/06-AUTH-AND-SECURITY.md specs/09-IMPLEMENTATION-ROADMAP.md

type: search

cmd: rg -n -C 4 'export|webhook|listener|Listener|analytics|apps/v1|OAuth|oauth|media' specs/02-API-ROUTES.md specs/05-BUSINESS-LOGIC.md specs/06-AUTH-AND-SECURITY.md specs/09-IMPLEMENTATION-ROADMAP.md

query: export|webhook|listener|Listener|analytics|apps/v1|OAuth|oauth|media

status: completed

stdout:
specs/06-AUTH-AND-SECURITY.md-252-| `read-collections` | List and view collections |
specs/06-AUTH-AND-SECURITY.md-253-| `write-collections` | Create, update, delete collections |
specs/06-AUTH-AND-SECURITY.md-254-| `read-discounts` | List and view discounts |
specs/06-AUTH-AND-SECURITY.md-255-| `write-discounts` | Create, update, delete discounts |
specs/06-AUTH-AND-SECURITY.md:256:| `read-analytics` | View analytics data |
specs/06-AUTH-AND-SECURITY.md-257-| `read-settings` | View store settings (general, shipping, taxes, checkout, notifications) |
specs/06-AUTH-AND-SECURITY.md-258-| `write-settings` | Update store settings (replaces the legacy `manage-store`, `manage-shipping`, `manage-taxes` scopes) |
specs/06-AUTH-AND-SECURITY.md-259-| `read-themes` | View themes and theme files |
specs/06-AUTH-AND-SECURITY.md-260-| `write-themes` | Create, update, delete, publish themes |
--
specs/06-AUTH-AND-SECURITY.md-548-| Gate Name | Required Roles | Purpose |
specs/06-AUTH-AND-SECURITY.md-549-|-----------|---------------|---------|
specs/06-AUTH-AND-SECURITY.md-550-| `manage-store-settings` | Owner, Admin | Access store settings pages |
specs/06-AUTH-AND-SECURITY.md-551-| `manage-staff` | Owner, Admin | Invite, update, remove staff members |
specs/06-AUTH-AND-SECURITY.md:552:| `manage-developers` | Owner, Admin | Create/revoke API tokens, manage webhooks |
specs/06-AUTH-AND-SECURITY.md:553:| `view-analytics` | Owner, Admin, Staff | View analytics dashboard and reports |
specs/06-AUTH-AND-SECURITY.md-554-| `manage-shipping` | Owner, Admin | Manage shipping zones, rates, profiles |
specs/06-AUTH-AND-SECURITY.md-555-| `manage-taxes` | Owner, Admin | Manage tax settings and rates |
specs/06-AUTH-AND-SECURITY.md-556-| `manage-search-settings` | Owner, Admin | Configure search weights and synonyms |
specs/06-AUTH-AND-SECURITY.md-557-| `manage-navigation` | Owner, Admin | Create/edit/delete navigation menus |
--
specs/06-AUTH-AND-SECURITY.md-613-| Admin API | `api`, `auth:sanctum`, `store.resolve`, `throttle:api.admin` | REST API endpoints |
specs/06-AUTH-AND-SECURITY.md-614-| Storefront web (public) | `web`, `store.resolve` | Public store pages (catalog, product, cart) |
specs/06-AUTH-AND-SECURITY.md-615-| Storefront web (customer) | `web`, `store.resolve`, `auth:customer` | Customer account pages |
specs/06-AUTH-AND-SECURITY.md-616-| Storefront API | `api`, `store.resolve`, `throttle:api.storefront` | Cart and checkout API |
specs/06-AUTH-AND-SECURITY.md:617:| Webhook routes | `api`, `throttle:webhooks` | Reserved for outbound app webhooks (no inbound payment webhooks - Mock PSP is in-process) |
specs/06-AUTH-AND-SECURITY.md-618-
specs/06-AUTH-AND-SECURITY.md-619----
specs/06-AUTH-AND-SECURITY.md-620-
specs/06-AUTH-AND-SECURITY.md-621-### 3.3 Custom Middleware Specifications
--
specs/06-AUTH-AND-SECURITY.md-739-| `api.admin` | 60 per minute | User ID (if authenticated), otherwise IP | Admin REST API endpoints |
specs/06-AUTH-AND-SECURITY.md-740-| `api.storefront` | 120 per minute | IP address | Public storefront API (cart, search) |
specs/06-AUTH-AND-SECURITY.md-741-| `checkout` | 10 per minute | Session ID | Checkout-related operations |
specs/06-AUTH-AND-SECURITY.md-742-| `search` | 30 per minute | IP address | Search queries |
specs/06-AUTH-AND-SECURITY.md:743:| `analytics` | 60 per minute | IP address | Analytics event ingestion |
specs/06-AUTH-AND-SECURITY.md:744:| `webhooks` | 100 per minute | IP address | Outbound app webhook endpoints (reserved; no inbound payment webhooks - Mock PSP is in-process) |
specs/06-AUTH-AND-SECURITY.md-745-
specs/06-AUTH-AND-SECURITY.md-746-**Rate limit response headers** (returned on all API responses by Laravel default):
specs/06-AUTH-AND-SECURITY.md-747-
specs/06-AUTH-AND-SECURITY.md-748-| Header | Description |
--
specs/06-AUTH-AND-SECURITY.md-762-**Fields requiring encrypted storage (using Laravel's `encrypted` cast):**
specs/06-AUTH-AND-SECURITY.md-763-
specs/06-AUTH-AND-SECURITY.md-764-| Model | Column | Cast Type | Purpose |
specs/06-AUTH-AND-SECURITY.md-765-|-------|--------|-----------|---------|
specs/06-AUTH-AND-SECURITY.md:766:| OauthClient | `client_secret_encrypted` | `encrypted` | Third-party OAuth client secrets |
specs/06-AUTH-AND-SECURITY.md:767:| WebhookSubscription | `signing_secret_encrypted` | `encrypted` | Per-subscription signing secrets for outbound webhooks |
specs/06-AUTH-AND-SECURITY.md-768-| Payment | `raw_json` | `encrypted:array` | Full payment gateway response (may contain sensitive card/account data) |
specs/06-AUTH-AND-SECURITY.md-769-
specs/06-AUTH-AND-SECURITY.md-770-**Requirements:**
specs/06-AUTH-AND-SECURITY.md-771-
--
specs/06-AUTH-AND-SECURITY.md-933-## 7. Webhook Signature Verification
specs/06-AUTH-AND-SECURITY.md-934-
specs/06-AUTH-AND-SECURITY.md-935-### 7.1 Inbound Payment Webhooks
specs/06-AUTH-AND-SECURITY.md-936-
specs/06-AUTH-AND-SECURITY.md:937:> **Not applicable.** All payment processing happens in-process via the Mock PSP. There are no inbound payment webhooks from external providers (no Stripe, no PayPal). This section is preserved for reference only -- if a real PSP is integrated in the future, inbound webhook verification would follow the patterns described in the outbound webhook section below (HMAC signature verification).
specs/06-AUTH-AND-SECURITY.md-938-
specs/06-AUTH-AND-SECURITY.md-939----
specs/06-AUTH-AND-SECURITY.md-940-
specs/06-AUTH-AND-SECURITY.md-941-### 7.2 Platform Webhooks (Outbound)
specs/06-AUTH-AND-SECURITY.md-942-
specs/06-AUTH-AND-SECURITY.md-943-Service: `App\Services\WebhookDispatcher`
specs/06-AUTH-AND-SECURITY.md-944-
specs/06-AUTH-AND-SECURITY.md:945:When the platform sends webhooks to external subscribers (third-party apps, integrations).
specs/06-AUTH-AND-SECURITY.md-946-
specs/06-AUTH-AND-SECURITY.md-947-```mermaid
specs/06-AUTH-AND-SECURITY.md-948-sequenceDiagram
specs/06-AUTH-AND-SECURITY.md-949-    participant App as Platform
--
specs/06-AUTH-AND-SECURITY.md-1037-| Event ID | Trigger | Extra Data |
specs/06-AUTH-AND-SECURITY.md-1038-|----------|---------|------------|
specs/06-AUTH-AND-SECURITY.md-1039-| `api_token.created` | New API token issued | token_name, abilities list, user_id |
specs/06-AUTH-AND-SECURITY.md-1040-| `api_token.revoked` | API token revoked | token_name, user_id |
specs/06-AUTH-AND-SECURITY.md:1041:| `webhook.signature_invalid` | Outbound webhook delivery failed signature verification at subscriber | subscriber_url, ip |
specs/06-AUTH-AND-SECURITY.md-1042-| `store.settings_changed` | Store settings updated | changed fields with old/new values |
specs/06-AUTH-AND-SECURITY.md-1043-
specs/06-AUTH-AND-SECURITY.md-1044----
specs/06-AUTH-AND-SECURITY.md-1045-
--
specs/06-AUTH-AND-SECURITY.md-1071-### 8.5 Integration with Laravel Events
specs/06-AUTH-AND-SECURITY.md-1072-
specs/06-AUTH-AND-SECURITY.md-1073-The audit logger hooks into Laravel's built-in authentication events and custom model observers.
specs/06-AUTH-AND-SECURITY.md-1074-
specs/06-AUTH-AND-SECURITY.md:1075:#### Auth Event Listeners
specs/06-AUTH-AND-SECURITY.md-1076-
specs/06-AUTH-AND-SECURITY.md:1077:Register listeners (in `AppServiceProvider` or `EventServiceProvider`) for these Laravel events:
specs/06-AUTH-AND-SECURITY.md-1078-
specs/06-AUTH-AND-SECURITY.md-1079-| Laravel Event | Audit Event ID | Data Captured |
specs/06-AUTH-AND-SECURITY.md-1080-|--------------|----------------|---------------|
specs/06-AUTH-AND-SECURITY.md-1081-| `Illuminate\Auth\Events\Login` | `auth.login` | user_id from event |
--
specs/06-AUTH-AND-SECURITY.md-1096-| 2 | Session-based auth (customer) | Custom `customer` guard, store-scoped via custom user provider | Required |
specs/06-AUTH-AND-SECURITY.md-1097-| 3 | API token auth | Sanctum personal access tokens with per-token abilities | Required |
specs/06-AUTH-AND-SECURITY.md-1098-| 4 | Role-based access control | `StoreUserRole` enum, Laravel policies on all models, gates for non-model ops | Required |
specs/06-AUTH-AND-SECURITY.md-1099-| 5 | CSRF protection | `@csrf` on all session forms, Livewire auto-handles, API routes exempt | Required |
specs/06-AUTH-AND-SECURITY.md:1100:| 6 | Rate limiting | Seven named limiters (login, api.admin, api.storefront, checkout, search, analytics, webhooks) | Required |
specs/06-AUTH-AND-SECURITY.md-1101-| 7 | Input validation | Dedicated Form Request class for every write endpoint | Required |
specs/06-AUTH-AND-SECURITY.md-1102-| 8 | XSS prevention | Blade auto-escaping by default, HTMLPurifier for rich content fields | Required |
specs/06-AUTH-AND-SECURITY.md-1103-| 9 | SQL injection prevention | Eloquent ORM with parameterized queries, FTS5 input sanitization | Required |
specs/06-AUTH-AND-SECURITY.md-1104-| 10 | Mass assignment protection | `$fillable` on all models, always use `$request->validated()` | Required |
specs/06-AUTH-AND-SECURITY.md-1105-| 11 | Encryption at rest | Laravel `encrypted` cast for secrets and sensitive JSON columns | Required |
specs/06-AUTH-AND-SECURITY.md-1106-| 12 | Password security | Bcrypt, min 8 characters, `$hidden` on User and Customer models | Required |
specs/06-AUTH-AND-SECURITY.md-1107-| 13 | Session security | Encrypted sessions, HTTP-only cookies, secure flag, SameSite=Lax | Required |
specs/06-AUTH-AND-SECURITY.md:1108:| 14 | Webhook signature verification | HMAC-SHA256 for outbound platform webhooks (no inbound payment webhooks - Mock PSP is in-process) | Required |
specs/06-AUTH-AND-SECURITY.md-1109-| 15 | Audit logging | Structured JSON to dedicated daily-rotation log channel, 90-day retention | Required |
specs/06-AUTH-AND-SECURITY.md-1110-| 16 | CORS | Restricted to `api/*` paths, credentials supported, rate limit headers exposed | Required |
--
specs/05-BUSINESS-LOGIC.md-76-- All SELECT, UPDATE, DELETE queries on tenant-scoped models include the store filter.
specs/05-BUSINESS-LOGIC.md-77-- In platform-admin contexts where cross-store access is needed, the scope can be temporarily removed.
specs/05-BUSINESS-LOGIC.md-78-
specs/05-BUSINESS-LOGIC.md-79-**Models that must use this trait:**
specs/05-BUSINESS-LOGIC.md:80:products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.
specs/05-BUSINESS-LOGIC.md-81-
specs/05-BUSINESS-LOGIC.md-82-### 1.3 Role-Based Access Control
specs/05-BUSINESS-LOGIC.md-83-
specs/05-BUSINESS-LOGIC.md-84-**Enum:** `App\Enums\StoreUserRole` - backed by string values: `owner`, `admin`, `staff`, `support`
--
specs/05-BUSINESS-LOGIC.md-93-| Manage orders | Yes | Yes | Yes | Yes (read-only) |
specs/05-BUSINESS-LOGIC.md-94-| Manage discounts | Yes | Yes | Yes | No |
specs/05-BUSINESS-LOGIC.md-95-| Manage themes | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md-96-| Manage apps | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md:97:| View analytics | Yes | Yes | Yes | No |
specs/05-BUSINESS-LOGIC.md-98-| Manage shipping/tax | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md-99-| Process refunds | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md-100-| Create fulfillments | Yes | Yes | Yes | No |
specs/05-BUSINESS-LOGIC.md-101-
--
specs/05-BUSINESS-LOGIC.md-929-## 10. Payment Processing (Mock PSP)
specs/05-BUSINESS-LOGIC.md-930-
specs/05-BUSINESS-LOGIC.md-931-### 10.1 Mock Payment Provider Architecture
specs/05-BUSINESS-LOGIC.md-932-
specs/05-BUSINESS-LOGIC.md:933:The platform uses a **Mock Payment Service Provider (PSP)** instead of real payment gateways. All payment processing happens in-process with no external API calls, no webhooks, and no redirects. This enables a fully functional checkout flow for development, testing, and demonstration purposes.
specs/05-BUSINESS-LOGIC.md-934-
specs/05-BUSINESS-LOGIC.md-935-**Interface:** `App\Contracts\PaymentProvider`
specs/05-BUSINESS-LOGIC.md-936-
specs/05-BUSINESS-LOGIC.md-937-Must implement:
--
specs/05-BUSINESS-LOGIC.md-947-**Enum:** `App\Enums\PaymentMethod` - values: `CreditCard` (`credit_card`), `Paypal` (`paypal`), `BankTransfer` (`bank_transfer`)
specs/05-BUSINESS-LOGIC.md-948-
specs/05-BUSINESS-LOGIC.md-949-| Method | Behavior | Capture Timing |
specs/05-BUSINESS-LOGIC.md-950-|---|---|---|
specs/05-BUSINESS-LOGIC.md:951:| `credit_card` | Simulates card authorization and immediate capture. Uses magic card numbers for test scenarios. | Auto-capture on checkout (instant) |
specs/05-BUSINESS-LOGIC.md:952:| `paypal` | Simulates PayPal approval and immediate capture. Always succeeds (no magic numbers). | Auto-capture on checkout (instant) |
specs/05-BUSINESS-LOGIC.md-953-| `bank_transfer` | No charge at checkout. Order created with `financial_status = pending`. Admin manually confirms payment receipt. | Deferred - admin confirms payment |
specs/05-BUSINESS-LOGIC.md-954-
specs/05-BUSINESS-LOGIC.md-955-### 10.3 Magic Card Numbers (Credit Card Testing)
specs/05-BUSINESS-LOGIC.md-956-
--
specs/05-BUSINESS-LOGIC.md-1271-   - `tracking_company = null`, `tracking_number = null`, `tracking_url = null`
specs/05-BUSINESS-LOGIC.md-1272-   - `shipped_at = now`
specs/05-BUSINESS-LOGIC.md-1273-   - Set `order.fulfillment_status = 'fulfilled'`
specs/05-BUSINESS-LOGIC.md-1274-   - Set `order.status = 'fulfilled'`
specs/05-BUSINESS-LOGIC.md:1275:3. If the order contains a mix of physical and digital products: do **not** auto-fulfill. The admin must create fulfillments manually (they can fulfill digital items immediately and physical items when shipped).
specs/05-BUSINESS-LOGIC.md-1276-
specs/05-BUSINESS-LOGIC.md:1277:This ensures digital product orders are immediately marked as complete after payment.
specs/05-BUSINESS-LOGIC.md-1278-
specs/05-BUSINESS-LOGIC.md-1279-### 11.6 Order Export
specs/05-BUSINESS-LOGIC.md-1280-
specs/05-BUSINESS-LOGIC.md:1281:`GET /api/admin/v1/orders/export` returns a CSV file of orders matching the current filter criteria.
specs/05-BUSINESS-LOGIC.md-1282-
specs/05-BUSINESS-LOGIC.md-1283-**CSV columns:** `order_number`, `created_at`, `status`, `financial_status`, `fulfillment_status`, `customer_email`, `customer_name`, `subtotal_amount`, `discount_amount`, `shipping_amount`, `tax_amount`, `total_amount`, `currency`, `shipping_method`, `tracking_number`
specs/05-BUSINESS-LOGIC.md-1284-
specs/05-BUSINESS-LOGIC.md:1285:All monetary values are exported in minor units (cents). Dates use ISO-8601 format. The response uses `Content-Type: text/csv` with `Content-Disposition: attachment; filename="orders-{date}.csv"`.
specs/05-BUSINESS-LOGIC.md-1286-
specs/05-BUSINESS-LOGIC.md-1287----
specs/05-BUSINESS-LOGIC.md-1288-
specs/05-BUSINESS-LOGIC.md-1289-## 12. Customer Accounts
--
specs/05-BUSINESS-LOGIC.md-1345-## 13. Webhook Delivery System
specs/05-BUSINESS-LOGIC.md-1346-
specs/05-BUSINESS-LOGIC.md-1347-### 13.1 Subscription Model
specs/05-BUSINESS-LOGIC.md-1348-
specs/05-BUSINESS-LOGIC.md:1349:Stores (or app installations) subscribe to event types via `webhook_subscriptions`.
specs/05-BUSINESS-LOGIC.md-1350-
specs/05-BUSINESS-LOGIC.md-1351-**Supported event types:**
specs/05-BUSINESS-LOGIC.md-1352-
specs/05-BUSINESS-LOGIC.md-1353-| Event Type | Triggered By |
--
specs/05-BUSINESS-LOGIC.md-1362-| `checkout.completed` | Checkout successfully completed |
specs/05-BUSINESS-LOGIC.md-1363-
specs/05-BUSINESS-LOGIC.md-1364-### 13.2 Delivery Process
specs/05-BUSINESS-LOGIC.md-1365-
specs/05-BUSINESS-LOGIC.md:1366:**Listener:** `App\Listeners\DispatchWebhooks` - registered to listen for all domain events that have webhook counterparts.
specs/05-BUSINESS-LOGIC.md-1367-
specs/05-BUSINESS-LOGIC.md-1368-**Job:** `App\Jobs\DeliverWebhook`
specs/05-BUSINESS-LOGIC.md-1369-
specs/05-BUSINESS-LOGIC.md-1370-```mermaid
specs/05-BUSINESS-LOGIC.md-1371-sequenceDiagram
specs/05-BUSINESS-LOGIC.md-1372-    participant DomainEvent
specs/05-BUSINESS-LOGIC.md:1373:    participant Listener
specs/05-BUSINESS-LOGIC.md-1374-    participant Queue
specs/05-BUSINESS-LOGIC.md-1375-    participant Job
specs/05-BUSINESS-LOGIC.md-1376-    participant TargetURL
specs/05-BUSINESS-LOGIC.md-1377-
specs/05-BUSINESS-LOGIC.md:1378:    DomainEvent->>Listener: Event fired (e.g., OrderCreated)
specs/05-BUSINESS-LOGIC.md:1379:    Listener->>Listener: Query webhook_subscriptions for store_id + event_type where status = active
specs/05-BUSINESS-LOGIC.md-1380-    loop For each matching subscription
specs/05-BUSINESS-LOGIC.md:1381:        Listener->>Queue: Dispatch DeliverWebhook job
specs/05-BUSINESS-LOGIC.md-1382-    end
specs/05-BUSINESS-LOGIC.md-1383-    Queue->>Job: Process job
specs/05-BUSINESS-LOGIC.md-1384-    Job->>Job: Serialize event payload as JSON
specs/05-BUSINESS-LOGIC.md-1385-    Job->>Job: Generate HMAC-SHA256 signature using subscription's signing_secret (decrypted)
specs/05-BUSINESS-LOGIC.md-1386-    Job->>TargetURL: HTTP POST with headers and payload
specs/05-BUSINESS-LOGIC.md-1387-    TargetURL-->>Job: Response
specs/05-BUSINESS-LOGIC.md:1388:    Job->>Job: Record result in webhook_deliveries
specs/05-BUSINESS-LOGIC.md-1389-```
specs/05-BUSINESS-LOGIC.md-1390-
specs/05-BUSINESS-LOGIC.md-1391-**Required HTTP headers on delivery:**
specs/05-BUSINESS-LOGIC.md-1392-
--
specs/05-BUSINESS-LOGIC.md-1403-On non-2xx response or connection timeout:
specs/05-BUSINESS-LOGIC.md-1404-
specs/05-BUSINESS-LOGIC.md-1405-| Attempt | Backoff Delay |
specs/05-BUSINESS-LOGIC.md-1406-|---|---|
specs/05-BUSINESS-LOGIC.md:1407:| 1 (initial) | Immediate |
specs/05-BUSINESS-LOGIC.md-1408-| 2 | 1 minute |
specs/05-BUSINESS-LOGIC.md-1409-| 3 | 5 minutes |
specs/05-BUSINESS-LOGIC.md-1410-| 4 | 30 minutes |
specs/05-BUSINESS-LOGIC.md-1411-| 5 | 2 hours |
--
specs/05-BUSINESS-LOGIC.md-1417-
specs/05-BUSINESS-LOGIC.md-1418-### 13.4 Circuit Breaker
specs/05-BUSINESS-LOGIC.md-1419-
specs/05-BUSINESS-LOGIC.md-1420-- Track consecutive failures per subscription (across different events).
specs/05-BUSINESS-LOGIC.md:1421:- If a subscription's endpoint fails 5 consecutive times: automatically set `webhook_subscriptions.status = 'paused'`.
specs/05-BUSINESS-LOGIC.md-1422-- Log a warning and (future) send an admin notification.
specs/05-BUSINESS-LOGIC.md-1423-- Manual re-enable required by the merchant through the admin UI.
specs/05-BUSINESS-LOGIC.md-1424-- On each successful delivery, reset the consecutive failure counter to 0.
specs/05-BUSINESS-LOGIC.md-1425-
--
specs/05-BUSINESS-LOGIC.md-1430-## 14. Analytics
specs/05-BUSINESS-LOGIC.md-1431-
specs/05-BUSINESS-LOGIC.md-1432-### 14.1 Event Ingestion
specs/05-BUSINESS-LOGIC.md-1433-
specs/05-BUSINESS-LOGIC.md:1434:**Endpoint:** `POST /api/storefront/v1/analytics/events`
specs/05-BUSINESS-LOGIC.md-1435-
specs/05-BUSINESS-LOGIC.md-1436-**Request structure:**
specs/05-BUSINESS-LOGIC.md-1437-
specs/05-BUSINESS-LOGIC.md-1438-| Field | Type | Required | Description |
--
specs/05-BUSINESS-LOGIC.md-1471-
specs/05-BUSINESS-LOGIC.md-1472-**Job:** `App\Jobs\AggregateAnalytics`
specs/05-BUSINESS-LOGIC.md-1473-
specs/05-BUSINESS-LOGIC.md-1474-- Schedule: daily at 01:00 UTC
specs/05-BUSINESS-LOGIC.md:1475:- Aggregates the previous day's raw `analytics_events` into `analytics_daily`.
specs/05-BUSINESS-LOGIC.md-1476-
specs/05-BUSINESS-LOGIC.md-1477-**Aggregated metrics:**
specs/05-BUSINESS-LOGIC.md-1478-
specs/05-BUSINESS-LOGIC.md-1479-| Metric | Calculation |
--
specs/05-BUSINESS-LOGIC.md-1492-### 15.1 Upload Flow
specs/05-BUSINESS-LOGIC.md-1493-
specs/05-BUSINESS-LOGIC.md-1494-```mermaid
specs/05-BUSINESS-LOGIC.md-1495-flowchart TD
specs/05-BUSINESS-LOGIC.md:1496:    A[Admin uploads file via Livewire] --> B[Store original in storage/app/public/media/originals/]
specs/05-BUSINESS-LOGIC.md:1497:    B --> C[Create product_media record with status = processing]
specs/05-BUSINESS-LOGIC.md-1498-    C --> D[Dispatch ProcessMediaUpload job to queue]
specs/05-BUSINESS-LOGIC.md-1499-    D --> E{Processing succeeds?}
specs/05-BUSINESS-LOGIC.md-1500-    E -->|Yes| F[Store resized versions, set status = ready]
specs/05-BUSINESS-LOGIC.md-1501-    E -->|No| G[Set status = failed, log error, keep original]
--
specs/05-BUSINESS-LOGIC.md-1516-
specs/05-BUSINESS-LOGIC.md-1517-**Processing rules:**
specs/05-BUSINESS-LOGIC.md-1518-- Maintain aspect ratio (contain within target dimensions, no cropping).
specs/05-BUSINESS-LOGIC.md-1519-- Generate WebP version alongside the original format if the server supports it.
specs/05-BUSINESS-LOGIC.md:1520:- Store all sizes with predictable path keys: `media/{product_id}/{media_id}/{size}.{ext}`
specs/05-BUSINESS-LOGIC.md:1521:- Update the `product_media` record with: `width`, `height`, `mime_type`, `byte_size` from the original, and `status = 'ready'`.
specs/05-BUSINESS-LOGIC.md-1522-- On failure: set `status = 'failed'` and log the error. Do not delete the original file.
specs/05-BUSINESS-LOGIC.md-1523-- Maximum 3 retry attempts before permanent failure.
specs/05-BUSINESS-LOGIC.md-1524-
specs/05-BUSINESS-LOGIC.md-1525-### 15.3 Cleanup
specs/05-BUSINESS-LOGIC.md-1526-
specs/05-BUSINESS-LOGIC.md:1527:When a `product_media` record is deleted, a model event listener removes all associated files from storage (all sizes and the original).
specs/05-BUSINESS-LOGIC.md-1528-
specs/05-BUSINESS-LOGIC.md-1529----
specs/05-BUSINESS-LOGIC.md-1530-
specs/05-BUSINESS-LOGIC.md-1531-## 16. Search (SQLite FTS5)
--
specs/05-BUSINESS-LOGIC.md-1580-```
specs/05-BUSINESS-LOGIC.md-1581-
specs/05-BUSINESS-LOGIC.md-1582-### 16.4 Search Logging
specs/05-BUSINESS-LOGIC.md-1583-
specs/05-BUSINESS-LOGIC.md:1584:Each search query is logged to `search_queries` with: store_id, query text, results_count, created_at. This data feeds into analytics and can inform autocomplete suggestions.
specs/05-BUSINESS-LOGIC.md-1585-
specs/05-BUSINESS-LOGIC.md-1586----
specs/05-BUSINESS-LOGIC.md-1587-
specs/05-BUSINESS-LOGIC.md:1588:## 17. Event and Listener Registry
specs/05-BUSINESS-LOGIC.md-1589-
specs/05-BUSINESS-LOGIC.md:1590:Summary of all domain events and their listeners/side effects:
specs/05-BUSINESS-LOGIC.md-1591-
specs/05-BUSINESS-LOGIC.md:1592:| Event | Listeners / Side Effects |
specs/05-BUSINESS-LOGIC.md-1593-|---|---|
specs/05-BUSINESS-LOGIC.md:1594:| `OrderCreated` | Send confirmation email, dispatch webhooks (`order.created`), log analytics event |
specs/05-BUSINESS-LOGIC.md:1595:| `OrderRefunded` | Send refund notification email, dispatch webhooks (`order.refunded`), optionally restock |
specs/05-BUSINESS-LOGIC.md-1596-| `CheckoutAddressed` | Recalculate pricing |
specs/05-BUSINESS-LOGIC.md-1597-| `CheckoutShippingSelected` | Recalculate pricing |
specs/05-BUSINESS-LOGIC.md:1598:| `CheckoutCompleted` | Dispatch webhooks (`checkout.completed`) |
specs/05-BUSINESS-LOGIC.md-1599-| `CheckoutExpired` | Release reserved inventory |
specs/05-BUSINESS-LOGIC.md-1600-| `ProductStatusChanged` | Sync FTS5 index if transitioning to/from active |
specs/05-BUSINESS-LOGIC.md:1601:| `ProductCreated` | Sync FTS5 index, dispatch webhooks (`product.created`) |
specs/05-BUSINESS-LOGIC.md:1602:| `ProductUpdated` | Sync FTS5 index, dispatch webhooks (`product.updated`) |
specs/05-BUSINESS-LOGIC.md:1603:| `ProductDeleted` | Remove from FTS5 index, dispatch webhooks (`product.deleted`) |
specs/05-BUSINESS-LOGIC.md-1604-| `OrderPaid` | Update order status, commit inventory (bank transfer confirmation), auto-fulfill digital products |
specs/05-BUSINESS-LOGIC.md-1605-| `FulfillmentCreated` | Update order fulfillment status |
specs/05-BUSINESS-LOGIC.md:1606:| `FulfillmentShipped` | Send shipping notification email with tracking info, dispatch webhooks (`order.fulfilled` if fully fulfilled) |
specs/05-BUSINESS-LOGIC.md:1607:| `FulfillmentDelivered` | Update fulfillment record, dispatch webhooks |
specs/05-BUSINESS-LOGIC.md-1608-| `OrderCancelled` | Release reserved inventory, send cancellation email |
specs/05-BUSINESS-LOGIC.md-1609-
specs/05-BUSINESS-LOGIC.md:1610:All events should be placed in `App\Events`. All listeners in `App\Listeners`.
specs/05-BUSINESS-LOGIC.md-1611-
specs/05-BUSINESS-LOGIC.md-1612----
specs/05-BUSINESS-LOGIC.md-1613-
specs/05-BUSINESS-LOGIC.md-1614-## 18. Scheduled Jobs Registry
--
specs/05-BUSINESS-LOGIC.md-1616-| Job Class | Schedule | Description |
specs/05-BUSINESS-LOGIC.md-1617-|---|---|---|
specs/05-BUSINESS-LOGIC.md-1618-| `App\Jobs\CleanupAbandonedCarts` | Daily | Mark stale active carts as abandoned (14-day threshold) |
specs/05-BUSINESS-LOGIC.md-1619-| `App\Jobs\ExpireAbandonedCheckouts` | Every 15 minutes | Expire checkouts idle for 24+ hours, release reserved inventory |
specs/05-BUSINESS-LOGIC.md:1620:| `App\Jobs\AggregateAnalytics` | Daily at 01:00 UTC | Roll up raw analytics events into daily aggregates |
specs/05-BUSINESS-LOGIC.md-1621-| `App\Jobs\CancelUnpaidBankTransferOrders` | Daily | Cancel bank transfer orders that remain unpaid after configurable days (default: 7) |
specs/05-BUSINESS-LOGIC.md-1622-
specs/05-BUSINESS-LOGIC.md-1623-**Queued jobs (dispatched on demand, not scheduled):**
specs/05-BUSINESS-LOGIC.md-1624-
specs/05-BUSINESS-LOGIC.md-1625-| Job Class | Description |
specs/05-BUSINESS-LOGIC.md-1626-|---|---|
specs/05-BUSINESS-LOGIC.md-1627-| `App\Jobs\ProcessMediaUpload` | Resize uploaded images to standard sizes |
specs/05-BUSINESS-LOGIC.md:1628:| `App\Jobs\DeliverWebhook` | POST webhook payload to subscriber endpoint |
specs/05-BUSINESS-LOGIC.md-1629-
specs/05-BUSINESS-LOGIC.md-1630-Schedule registration belongs in `routes/console.php`.
specs/05-BUSINESS-LOGIC.md-1631-
specs/05-BUSINESS-LOGIC.md-1632----
--
specs/09-IMPLEMENTATION-ROADMAP.md-12-|---------|-------------|--------------|-----------|
specs/09-IMPLEMENTATION-ROADMAP.md-13-| Database | PostgreSQL | SQLite | Self-contained, no external DB, zero config |
specs/09-IMPLEMENTATION-ROADMAP.md-14-| Cache | Redis | File (Array for tests) | No Redis dependency |
specs/09-IMPLEMENTATION-ROADMAP.md-15-| Session | Redis | File | No Redis dependency |
specs/09-IMPLEMENTATION-ROADMAP.md:16:| Queue | Redis + Horizon | Sync (dev) / Database (prod) | No Redis dependency, sync for immediate feedback |
specs/09-IMPLEMENTATION-ROADMAP.md-17-| Search | Elasticsearch + Scout | SQLite FTS5 | Self-contained, no external service |
specs/09-IMPLEMENTATION-ROADMAP.md-18-| Storage | S3 | Local filesystem (storage/app/public) | No AWS dependency |
specs/09-IMPLEMENTATION-ROADMAP.md-19-| Admin UI | React/Vue SPA | Livewire v4 + Flux UI Free | Server-rendered, matches project stack |
specs/09-IMPLEMENTATION-ROADMAP.md-20-| Storefront | Blade SSR + Vite | Blade + Livewire + Tailwind v4 | Server-rendered, matches project stack |
specs/09-IMPLEMENTATION-ROADMAP.md-21-| Auth (admin) | Sanctum SPA | Laravel session auth | Standard Livewire auth pattern |
specs/09-IMPLEMENTATION-ROADMAP.md-22-| Auth (API) | Sanctum tokens | Sanctum personal access tokens | For programmatic API access |
specs/09-IMPLEMENTATION-ROADMAP.md-23-| Auth (customer) | Separate guard | Custom `customer` guard | Storefront customer auth |
specs/09-IMPLEMENTATION-ROADMAP.md:24:| OAuth (apps) | Passport | Deferred/Stubbed | Simplifies initial build, can add later |
specs/09-IMPLEMENTATION-ROADMAP.md-25-| Payments | Stripe + PayPal | Mock PSP (in-process) | No external API calls. MockPaymentProvider handles credit card, PayPal, bank transfer with magic card numbers for testing. |
specs/09-IMPLEMENTATION-ROADMAP.md-26-| Email | SMTP/SES | Log driver (dev) / SMTP | Log for benchmark, SMTP configurable |
specs/09-IMPLEMENTATION-ROADMAP.md-27-| Monitoring | OpenTelemetry | Laravel Log (structured JSON) | Simplified for benchmark |
specs/09-IMPLEMENTATION-ROADMAP.md-28-| CI/CD | GitHub Actions | Not implemented | Out of scope for one-shot benchmark |
--
specs/09-IMPLEMENTATION-ROADMAP.md-172-|------|------|---------|
specs/09-IMPLEMENTATION-ROADMAP.md-173-| Trait | `App\Models\Concerns\BelongsToStore` | Applies StoreScope and auto-sets store_id on creating event |
specs/09-IMPLEMENTATION-ROADMAP.md-174-| Scope | `App\Models\Scopes\StoreScope` | Applies `where('store_id', app('current_store')->id)` to all queries |
specs/09-IMPLEMENTATION-ROADMAP.md-175-
specs/09-IMPLEMENTATION-ROADMAP.md:176:Applied on: products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.
specs/09-IMPLEMENTATION-ROADMAP.md-177-
specs/09-IMPLEMENTATION-ROADMAP.md-178-#### Step 1.7: Authentication
specs/09-IMPLEMENTATION-ROADMAP.md-179-
specs/09-IMPLEMENTATION-ROADMAP.md-180-**Admin auth requirements:**
--
specs/09-IMPLEMENTATION-ROADMAP.md-234-| Create fulfillments | Y | Y | Y | N |
specs/09-IMPLEMENTATION-ROADMAP.md-235-| Manage discounts | Y | Y | Y | N |
specs/09-IMPLEMENTATION-ROADMAP.md-236-| Manage themes | Y | Y | N | N |
specs/09-IMPLEMENTATION-ROADMAP.md-237-| Manage apps | Y | Y | N | N |
specs/09-IMPLEMENTATION-ROADMAP.md:238:| View analytics | Y | Y | Y | N |
specs/09-IMPLEMENTATION-ROADMAP.md-239-| Manage shipping/tax | Y | Y | N | N |
specs/09-IMPLEMENTATION-ROADMAP.md-240-| View customers | Y | Y | Y | Y |
specs/09-IMPLEMENTATION-ROADMAP.md-241-| Update customers | Y | Y | Y | N |
specs/09-IMPLEMENTATION-ROADMAP.md-242-
--
specs/09-IMPLEMENTATION-ROADMAP.md-257-5. `create_variant_option_values_table` - composite PK: `variant_id` + `product_option_value_id`
specs/09-IMPLEMENTATION-ROADMAP.md-258-6. `create_inventory_items_table` - FK: `store_id`, `variant_id`
specs/09-IMPLEMENTATION-ROADMAP.md-259-7. `create_collections_table` - FK: `store_id`
specs/09-IMPLEMENTATION-ROADMAP.md-260-8. `create_collection_products_table` - composite PK: `collection_id` + `product_id`
specs/09-IMPLEMENTATION-ROADMAP.md:261:9. `create_product_media_table` - FK: `product_id`
specs/09-IMPLEMENTATION-ROADMAP.md-262-
specs/09-IMPLEMENTATION-ROADMAP.md-263-#### Step 2.2: Models with Relationships
specs/09-IMPLEMENTATION-ROADMAP.md-264-
specs/09-IMPLEMENTATION-ROADMAP.md-265-**Files to create:**
--
specs/09-IMPLEMENTATION-ROADMAP.md-668-| `charge(Checkout $checkout, PaymentMethod $method, array $details): PaymentResult` | Processes payment in-process. For credit_card: validates magic card numbers, returns success/decline. For paypal: always succeeds. For bank_transfer: returns pending (deferred capture). |
specs/09-IMPLEMENTATION-ROADMAP.md-669-| `refund(Payment $payment, int $amount): RefundResult` | Issues a mock refund (always succeeds). |
specs/09-IMPLEMENTATION-ROADMAP.md-670-
specs/09-IMPLEMENTATION-ROADMAP.md-671-**MockPaymentProvider requirements:**
specs/09-IMPLEMENTATION-ROADMAP.md:672:- No external API calls, no webhooks, no redirects
specs/09-IMPLEMENTATION-ROADMAP.md-673-- Magic card numbers: `4242424242424242` (success), `4000000000000002` (decline), `4000000000009995` (insufficient funds)
specs/09-IMPLEMENTATION-ROADMAP.md-674-- Credit card / PayPal: instant capture, returns PaymentResult with status=captured
specs/09-IMPLEMENTATION-ROADMAP.md-675-- Bank transfer: deferred capture, returns PaymentResult with status=pending
specs/09-IMPLEMENTATION-ROADMAP.md-676-- Generates mock reference IDs (`mock_` + random string)
--
specs/09-IMPLEMENTATION-ROADMAP.md-804-
specs/09-IMPLEMENTATION-ROADMAP.md-805-| Component | Description |
specs/09-IMPLEMENTATION-ROADMAP.md-806-|-----------|-------------|
specs/09-IMPLEMENTATION-ROADMAP.md-807-| `Admin\Products\Index` | Product list with search, status filter, bulk actions (archive, delete), pagination |
specs/09-IMPLEMENTATION-ROADMAP.md: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. |
specs/09-IMPLEMENTATION-ROADMAP.md-809-
specs/09-IMPLEMENTATION-ROADMAP.md-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.
specs/09-IMPLEMENTATION-ROADMAP.md-811-
specs/09-IMPLEMENTATION-ROADMAP.md-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.
--
specs/09-IMPLEMENTATION-ROADMAP.md-848-| `Admin\Analytics\Index` | Sales chart, traffic, funnel visualization, date range |
specs/09-IMPLEMENTATION-ROADMAP.md-849-| `Admin\Search\Settings` | Synonyms, stop words, reindex button |
specs/09-IMPLEMENTATION-ROADMAP.md-850-| `Admin\Apps\Index` | App directory |
specs/09-IMPLEMENTATION-ROADMAP.md-851-| `Admin\Apps\Show` | Installed app detail |
specs/09-IMPLEMENTATION-ROADMAP.md:852:| `Admin\Developers\Index` | API token management, webhook subscriptions |
specs/09-IMPLEMENTATION-ROADMAP.md-853-
specs/09-IMPLEMENTATION-ROADMAP.md-854----
specs/09-IMPLEMENTATION-ROADMAP.md-855-
specs/09-IMPLEMENTATION-ROADMAP.md-856-### Phase 8: Search
--
specs/09-IMPLEMENTATION-ROADMAP.md-892-**Priority:** LOW - reporting and insights.
specs/09-IMPLEMENTATION-ROADMAP.md-893-
specs/09-IMPLEMENTATION-ROADMAP.md-894-#### Step 9.1: Migrations
specs/09-IMPLEMENTATION-ROADMAP.md-895-
specs/09-IMPLEMENTATION-ROADMAP.md:896:1. `create_analytics_events_table` - FK: `store_id`, `customer_id`
specs/09-IMPLEMENTATION-ROADMAP.md:897:2. `create_analytics_daily_table` - composite PK: `store_id` + `date`
specs/09-IMPLEMENTATION-ROADMAP.md-898-
specs/09-IMPLEMENTATION-ROADMAP.md-899-#### Step 9.2: Analytics Service
specs/09-IMPLEMENTATION-ROADMAP.md-900-
specs/09-IMPLEMENTATION-ROADMAP.md-901-**File to create:** `App\Services\AnalyticsService`
--
specs/09-IMPLEMENTATION-ROADMAP.md-910-**File to create:** `App\Jobs\AggregateAnalytics`
specs/09-IMPLEMENTATION-ROADMAP.md-911-
specs/09-IMPLEMENTATION-ROADMAP.md-912-Requirements:
specs/09-IMPLEMENTATION-ROADMAP.md-913-- Runs daily via `routes/console.php`
specs/09-IMPLEMENTATION-ROADMAP.md:914:- Aggregates raw events into `analytics_daily` rows
specs/09-IMPLEMENTATION-ROADMAP.md-915-- Calculates: orders_count, revenue_amount, aov_amount, visits_count, add_to_cart_count, checkout_started_count
specs/09-IMPLEMENTATION-ROADMAP.md-916-
specs/09-IMPLEMENTATION-ROADMAP.md-917----
specs/09-IMPLEMENTATION-ROADMAP.md-918-
--
specs/09-IMPLEMENTATION-ROADMAP.md-923-#### Step 10.1: Migrations
specs/09-IMPLEMENTATION-ROADMAP.md-924-
specs/09-IMPLEMENTATION-ROADMAP.md-925-1. `create_apps_table`
specs/09-IMPLEMENTATION-ROADMAP.md-926-2. `create_app_installations_table` - FK: `store_id`, `app_id`
specs/09-IMPLEMENTATION-ROADMAP.md:927:3. `create_oauth_clients_table` - FK: `app_id`
specs/09-IMPLEMENTATION-ROADMAP.md:928:4. `create_oauth_tokens_table` - FK: `installation_id`
specs/09-IMPLEMENTATION-ROADMAP.md:929:5. `create_webhook_subscriptions_table` - FK: `store_id`, `app_installation_id`
specs/09-IMPLEMENTATION-ROADMAP.md:930:6. `create_webhook_deliveries_table` - FK: `subscription_id`
specs/09-IMPLEMENTATION-ROADMAP.md-931-
specs/09-IMPLEMENTATION-ROADMAP.md-932-#### Step 10.2: Webhook Service
specs/09-IMPLEMENTATION-ROADMAP.md-933-
specs/09-IMPLEMENTATION-ROADMAP.md-934-**File to create:** `App\Services\WebhookService`
--
specs/09-IMPLEMENTATION-ROADMAP.md-936-| Method | Description |
specs/09-IMPLEMENTATION-ROADMAP.md-937-|--------|-------------|
specs/09-IMPLEMENTATION-ROADMAP.md-938-| `dispatch(Store $store, string $eventType, array $payload): void` | Finds matching subscriptions, queues delivery jobs |
specs/09-IMPLEMENTATION-ROADMAP.md-939-| `sign(string $payload, string $secret): string` | HMAC-SHA256 signature |
specs/09-IMPLEMENTATION-ROADMAP.md:940:| `verify(string $payload, string $signature, string $secret): bool` | Verify incoming webhook signatures |
specs/09-IMPLEMENTATION-ROADMAP.md-941-
specs/09-IMPLEMENTATION-ROADMAP.md-942-**File to create:** `App\Jobs\DeliverWebhook`
specs/09-IMPLEMENTATION-ROADMAP.md-943-
specs/09-IMPLEMENTATION-ROADMAP.md-944-Requirements:
specs/09-IMPLEMENTATION-ROADMAP.md-945-- HTTP POST to target URL with JSON payload
specs/09-IMPLEMENTATION-ROADMAP.md-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`
specs/09-IMPLEMENTATION-ROADMAP.md-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]`)
specs/09-IMPLEMENTATION-ROADMAP.md:948:- Records response in `webhook_deliveries`
specs/09-IMPLEMENTATION-ROADMAP.md-949-- Circuit breaker: after 5 consecutive failures, pause subscription
specs/09-IMPLEMENTATION-ROADMAP.md-950-
specs/09-IMPLEMENTATION-ROADMAP.md-951----
specs/09-IMPLEMENTATION-ROADMAP.md-952-
--
specs/09-IMPLEMENTATION-ROADMAP.md-1265-#### tests/Feature/Products/MediaUploadTest.php
specs/09-IMPLEMENTATION-ROADMAP.md-1266-
specs/09-IMPLEMENTATION-ROADMAP.md-1267-| Test Name | Setup / Preconditions | Key Assertions | Category |
specs/09-IMPLEMENTATION-ROADMAP.md-1268-|-----------|----------------------|----------------|----------|
specs/09-IMPLEMENTATION-ROADMAP.md:1269:| uploads an image for a product | POST Livewire file upload with a test image | product_media row created with status=processing | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1270:| processes uploaded image and generates variants | dispatch ProcessMediaUpload job | media status = ready, file exists in storage | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1271-| rejects non-image file types | attempt to upload a .txt file | validation error | validation |
specs/09-IMPLEMENTATION-ROADMAP.md:1272:| sets alt text on media | upload image, then update alt_text | alt_text persisted | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1273:| reorders media positions | upload 3 images, reorder | position values updated | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1274:| deletes media and removes file from storage | upload image, then delete | product_media row removed, file removed from disk | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1275-
specs/09-IMPLEMENTATION-ROADMAP.md-1276----
specs/09-IMPLEMENTATION-ROADMAP.md-1277-
specs/09-IMPLEMENTATION-ROADMAP.md-1278-#### tests/Feature/Cart/CartServiceTest.php
--
specs/09-IMPLEMENTATION-ROADMAP.md-1517-| lists products with pagination | create 25 products; GET /admin/products | first page shows 15 (or configured per-page), pagination controls visible | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1518-| creates a product via admin form | Livewire test: fill title, description, submit | product in DB | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1519-| edits a product via admin form | Livewire test: change title, submit | product updated | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1520-| bulk archives selected products | select 3 products, trigger bulk archive | all 3 status = archived | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1521:| uploads media from the product form | Livewire file upload on product edit | media attached to product | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1522-| manages variants from the product form | add options and values via Livewire | variants generated | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1523-| restricts product management to authorized roles | support user GET /admin/products: can see list (read-only); support user attempt to create product | 403 on create | authorization |
specs/09-IMPLEMENTATION-ROADMAP.md-1524-| staff can create but not delete products | staff user: create product succeeds; staff user: delete product | fails with 403 on delete | authorization |
specs/09-IMPLEMENTATION-ROADMAP.md-1525-
--
specs/09-IMPLEMENTATION-ROADMAP.md-1569-|-----------|----------------------|----------------|----------|
specs/09-IMPLEMENTATION-ROADMAP.md-1570-| returns products matching search query | products: "Blue Cotton T-Shirt", "Red Wool Sweater"; search "cotton" | finds "Blue Cotton T-Shirt" | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1571-| scopes search to current store | store A has "T-Shirt", store B has "T-Shirt Deluxe"; search "t-shirt" on store A | only store A product returned | authorization |
specs/09-IMPLEMENTATION-ROADMAP.md-1572-| returns empty for no matches | search "xyznonexistent" | empty results | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md:1573:| logs search query for analytics | perform search | search_queries row created | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1574-| paginates search results | 25 matching products, 12 per page | page 1 has 12, page 2 has 12, page 3 has 1 | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1575-
specs/09-IMPLEMENTATION-ROADMAP.md-1576----
specs/09-IMPLEMENTATION-ROADMAP.md-1577-
--
specs/09-IMPLEMENTATION-ROADMAP.md-1588-#### tests/Feature/Analytics/EventIngestionTest.php
specs/09-IMPLEMENTATION-ROADMAP.md-1589-
specs/09-IMPLEMENTATION-ROADMAP.md-1590-| Test Name | Setup / Preconditions | Key Assertions | Category |
specs/09-IMPLEMENTATION-ROADMAP.md-1591-|-----------|----------------------|----------------|----------|
specs/09-IMPLEMENTATION-ROADMAP.md:1592:| tracks a page view event | POST analytics event: type=page_view | analytics_events row created | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1593-| tracks an add to cart event | POST event: type=add_to_cart, properties include product_id | event stored with properties | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1594-| scopes events to current store | track event on store A | event.store_id = store A id | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1595-| includes session ID when available | track event with session | session_id stored | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1596-| includes customer ID when authenticated | track event as logged-in customer | customer_id stored | happy path |
--
specs/09-IMPLEMENTATION-ROADMAP.md-1600-#### tests/Feature/Analytics/AggregationTest.php
specs/09-IMPLEMENTATION-ROADMAP.md-1601-
specs/09-IMPLEMENTATION-ROADMAP.md-1602-| Test Name | Setup / Preconditions | Key Assertions | Category |
specs/09-IMPLEMENTATION-ROADMAP.md-1603-|-----------|----------------------|----------------|----------|
specs/09-IMPLEMENTATION-ROADMAP.md:1604:| aggregates daily metrics from raw events | create events: 5 page_views, 3 add_to_carts, 2 checkout_completed; run AggregateAnalytics job | analytics_daily row with correct counts | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1605-| calculates revenue and AOV correctly | 3 orders: 1000, 2000, 3000 cents; aggregate | revenue=6000, aov=2000 | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1606:| runs idempotently | run aggregation twice for same date | analytics_daily row has correct values (not doubled) | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md-1607-
specs/09-IMPLEMENTATION-ROADMAP.md-1608----
specs/09-IMPLEMENTATION-ROADMAP.md-1609-
specs/09-IMPLEMENTATION-ROADMAP.md-1610-#### tests/Feature/Webhooks/WebhookDeliveryTest.php
specs/09-IMPLEMENTATION-ROADMAP.md-1611-
specs/09-IMPLEMENTATION-ROADMAP.md-1612-| Test Name | Setup / Preconditions | Key Assertions | Category |
specs/09-IMPLEMENTATION-ROADMAP.md-1613-|-----------|----------------------|----------------|----------|
specs/09-IMPLEMENTATION-ROADMAP.md:1614:| delivers a webhook to a subscribed URL | mock HTTP client; subscription for event_type "order.created"; dispatch event | HTTP POST made to target_url | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md:1615:| signs the payload with HMAC | deliver webhook | `X-Platform-Signature` header present with valid signature | happy path |
specs/09-IMPLEMENTATION-ROADMAP.md-1616-| retries failed deliveries with exponential backoff | first delivery returns 500 | job retried, attempt_count incremented | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md-1617-| marks delivery as failed after max retries | 6 total attempts exhausted (1 initial + 5 retries) | delivery status = failed | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md-1618-| pauses subscription after circuit breaker threshold | 5 consecutive failures | subscription status = paused | edge case |
specs/09-IMPLEMENTATION-ROADMAP.md-1619-
--
specs/09-IMPLEMENTATION-ROADMAP.md-1728-| `Fulfillment` | Fulfillment record |
specs/09-IMPLEMENTATION-ROADMAP.md-1729-| `FulfillmentLine` | Fulfillment line item |
specs/09-IMPLEMENTATION-ROADMAP.md-1730-| `App` | Third-party app entity |
specs/09-IMPLEMENTATION-ROADMAP.md-1731-| `AppInstallation` | App installation per store |
specs/09-IMPLEMENTATION-ROADMAP.md:1732:| `OauthClient` | OAuth client for app |
specs/09-IMPLEMENTATION-ROADMAP.md:1733:| `OauthToken` | OAuth token for installation |
specs/09-IMPLEMENTATION-ROADMAP.md-1734-| `WebhookSubscription` | Webhook subscription per store |
specs/09-IMPLEMENTATION-ROADMAP.md-1735-| `WebhookDelivery` | Webhook delivery log |
specs/09-IMPLEMENTATION-ROADMAP.md:1736:| `AnalyticsEvent` | Raw analytics event |
specs/09-IMPLEMENTATION-ROADMAP.md:1737:| `AnalyticsDaily` | Pre-aggregated daily analytics |
specs/09-IMPLEMENTATION-ROADMAP.md-1738-| `SearchSettings` | Per-store search configuration |
specs/09-IMPLEMENTATION-ROADMAP.md-1739-| `SearchQuery` | Search query log |
specs/09-IMPLEMENTATION-ROADMAP.md-1740-
specs/09-IMPLEMENTATION-ROADMAP.md-1741-### Livewire Components - Admin
--
specs/09-IMPLEMENTATION-ROADMAP.md-1768-| `Admin/Analytics/Index` | Analytics dashboard |
specs/09-IMPLEMENTATION-ROADMAP.md-1769-| `Admin/Search/Settings` | Search settings |
specs/09-IMPLEMENTATION-ROADMAP.md-1770-| `Admin/Apps/Index` | App directory |
specs/09-IMPLEMENTATION-ROADMAP.md-1771-| `Admin/Apps/Show` | Installed app detail |
specs/09-IMPLEMENTATION-ROADMAP.md:1772:| `Admin/Developers/Index` | API token and webhook management |
specs/09-IMPLEMENTATION-ROADMAP.md-1773-
specs/09-IMPLEMENTATION-ROADMAP.md-1774-### Livewire Components - Storefront
specs/09-IMPLEMENTATION-ROADMAP.md-1775-
specs/09-IMPLEMENTATION-ROADMAP.md-1776-| Name | Purpose |
--
specs/09-IMPLEMENTATION-ROADMAP.md-1842-### Jobs
specs/09-IMPLEMENTATION-ROADMAP.md-1843-
specs/09-IMPLEMENTATION-ROADMAP.md-1844-| Name | Purpose |
specs/09-IMPLEMENTATION-ROADMAP.md-1845-|------|---------|
specs/09-IMPLEMENTATION-ROADMAP.md:1846:| `ProcessMediaUpload` | Resize images, update media status |
specs/09-IMPLEMENTATION-ROADMAP.md:1847:| `DeliverWebhook` | HTTP POST webhook with retry logic |
specs/09-IMPLEMENTATION-ROADMAP.md:1848:| `AggregateAnalytics` | Daily analytics aggregation |
specs/09-IMPLEMENTATION-ROADMAP.md-1849-| `CleanupAbandonedCarts` | Mark old active carts as abandoned |
specs/09-IMPLEMENTATION-ROADMAP.md-1850-| `ExpireAbandonedCheckouts` | Expire timed-out checkouts, release inventory |
specs/09-IMPLEMENTATION-ROADMAP.md-1851-
specs/09-IMPLEMENTATION-ROADMAP.md-1852-### Events
--
specs/09-IMPLEMENTATION-ROADMAP.md-1933-| `config/auth.php` | Customer guard | Add customer guard, customer provider, customer password broker | Customer storefront auth |
specs/09-IMPLEMENTATION-ROADMAP.md-1934-| `config/session.php` | Driver | File driver, lifetime 120 | Session configuration |
specs/09-IMPLEMENTATION-ROADMAP.md-1935-| `config/cache.php` | Driver | File driver | Cache configuration |
specs/09-IMPLEMENTATION-ROADMAP.md-1936-| `config/queue.php` | Driver | Sync driver | Queue configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:1937:| `config/filesystems.php` | Public disk | Media storage configuration | Public disk for media |
specs/09-IMPLEMENTATION-ROADMAP.md-1938-| `config/logging.php` | JSON channel | Structured JSON channel | Structured logging |
specs/09-IMPLEMENTATION-ROADMAP.md-1939-
specs/09-IMPLEMENTATION-ROADMAP.md-1940-### bootstrap/app.php Changes
specs/09-IMPLEMENTATION-ROADMAP.md-1941-
specs/09-IMPLEMENTATION-ROADMAP.md-1942-- Register middleware aliases: `store.resolve`, `role.check`, `auth:customer`
specs/09-IMPLEMENTATION-ROADMAP.md-1943-- Register middleware groups for `storefront` and `admin` route groups
specs/09-IMPLEMENTATION-ROADMAP.md:1944:- Configure rate limiters: `login` (5/min per IP), `api.admin` (60/min per token), `api.storefront` (120/min per IP), `checkout` (10/min per session), `search` (30/min per IP), `analytics` (60/min per IP), `webhooks` (100/min per IP)
specs/09-IMPLEMENTATION-ROADMAP.md-1945-- Configure exception handling
specs/09-IMPLEMENTATION-ROADMAP.md-1946-
specs/09-IMPLEMENTATION-ROADMAP.md-1947-### Routes Files
specs/09-IMPLEMENTATION-ROADMAP.md-1948-
--
specs/09-IMPLEMENTATION-ROADMAP.md-2051-6. `StoreSettingsSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md-2052-7. `TaxSettingsSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md-2053-8. `ShippingSeeder` - creates shipping zones and rates
specs/09-IMPLEMENTATION-ROADMAP.md-2054-9. `CollectionSeeder` - creates collections (before products so products can be linked)
specs/09-IMPLEMENTATION-ROADMAP.md:2055:10. `ProductSeeder` - creates products with options, variants, inventory, media, and collection associations
specs/09-IMPLEMENTATION-ROADMAP.md-2056-11. `DiscountSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md-2057-12. `CustomerSeeder` - creates customers and their addresses
specs/09-IMPLEMENTATION-ROADMAP.md-2058-13. `OrderSeeder` - creates sample orders with lines, payments, fulfillments
specs/09-IMPLEMENTATION-ROADMAP.md-2059-14. `ThemeSeeder` - creates themes with files and settings
--
specs/02-API-ROUTES.md-46-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-47-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md-48-| GET | `/admin/products` | Product list - searchable, filterable by status, bulk actions | `Admin\Products\Index` |
specs/02-API-ROUTES.md-49-| GET | `/admin/products/create` | Create product form | `Admin\Products\Create` |
specs/02-API-ROUTES.md:50:| GET | `/admin/products/{product}/edit` | Edit product form with variants, media, options | `Admin\Products\Edit` |
specs/02-API-ROUTES.md-51-
specs/02-API-ROUTES.md-52-#### Inventory
specs/02-API-ROUTES.md-53-
specs/02-API-ROUTES.md-54-| Method | Path | Description | Component |
--
specs/02-API-ROUTES.md-118-
specs/02-API-ROUTES.md-119-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-120-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md-121-| GET | `/admin/apps` | Apps directory - list installed and available apps | `Admin\Apps\Index` |
specs/02-API-ROUTES.md:122:| GET | `/admin/apps/{installation}` | Installed app detail - scopes, webhooks, usage | `Admin\Apps\Show` |
specs/02-API-ROUTES.md-123-
specs/02-API-ROUTES.md-124-#### Developers
specs/02-API-ROUTES.md-125-
specs/02-API-ROUTES.md-126-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-127-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md:128:| GET | `/admin/developers` | Developer settings - API keys/tokens, webhook management | `Admin\Developers\Index` |
specs/02-API-ROUTES.md-129-
specs/02-API-ROUTES.md:130:> **Note:** Webhook subscription management (CRUD) is available exclusively through the admin UI at `/admin/developers`. There are no REST API endpoints for programmatic webhook subscription management in the initial implementation.
specs/02-API-ROUTES.md-131-
specs/02-API-ROUTES.md-132-#### Analytics
specs/02-API-ROUTES.md-133-
specs/02-API-ROUTES.md-134-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-135-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md:136:| GET | `/admin/analytics` | Analytics dashboard - sales, traffic, funnel, exports | `Admin\Analytics\Index` |
specs/02-API-ROUTES.md-137-
specs/02-API-ROUTES.md-138-#### Search Settings
specs/02-API-ROUTES.md-139-
specs/02-API-ROUTES.md-140-| Method | Path | Description | Component |
--
specs/02-API-ROUTES.md-199-**Prefix:** `/api/storefront/v1`
specs/02-API-ROUTES.md-200-**Middleware:** `store.resolve`, `throttle:api.storefront`
specs/02-API-ROUTES.md-201-**Content-Type:** `application/json`
specs/02-API-ROUTES.md-202-
specs/02-API-ROUTES.md:203:These endpoints power the interactive storefront experience (cart drawer, checkout flow, search autocomplete, analytics).
specs/02-API-ROUTES.md-204-
specs/02-API-ROUTES.md-205----
specs/02-API-ROUTES.md-206-
specs/02-API-ROUTES.md-207-### 2.1 Cart Endpoints
--
specs/02-API-ROUTES.md-846-### 2.3 Payment Endpoint
specs/02-API-ROUTES.md-847-
specs/02-API-ROUTES.md-848-#### POST /api/storefront/v1/checkouts/{checkoutId}/pay
specs/02-API-ROUTES.md-849-
specs/02-API-ROUTES.md:850:Process payment for a checkout using the selected payment method. All payment processing happens in-process via the Mock PSP - no external API calls, no redirects, no webhooks.
specs/02-API-ROUTES.md-851-
specs/02-API-ROUTES.md-852-- **Authentication:** None
specs/02-API-ROUTES.md-853-- **Rate limit:** `checkout` (10/min per session)
specs/02-API-ROUTES.md-854-
--
specs/02-API-ROUTES.md-1139----
specs/02-API-ROUTES.md-1140-
specs/02-API-ROUTES.md-1141-### 2.6 Analytics Endpoints
specs/02-API-ROUTES.md-1142-
specs/02-API-ROUTES.md:1143:#### POST /api/storefront/v1/analytics/events
specs/02-API-ROUTES.md-1144-
specs/02-API-ROUTES.md:1145:Batch submit storefront analytics events.
specs/02-API-ROUTES.md-1146-
specs/02-API-ROUTES.md-1147-- **Authentication:** None
specs/02-API-ROUTES.md:1148:- **Rate limit:** `analytics` (60/min per IP)
specs/02-API-ROUTES.md-1149-
specs/02-API-ROUTES.md-1150-**Request body:**
specs/02-API-ROUTES.md-1151-
specs/02-API-ROUTES.md-1152-```json
--
specs/02-API-ROUTES.md-1400-| POST | `/api/admin/v1/stores/{storeId}/products` | `write-products` | Create a new product | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1401-| GET | `/api/admin/v1/stores/{storeId}/products/{productId}` | `read-products` | Get single product with all relations | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1402-| PUT | `/api/admin/v1/stores/{storeId}/products/{productId}` | `write-products` | Update a product (partial updates supported) | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1403-| DELETE | `/api/admin/v1/stores/{storeId}/products/{productId}` | `write-products` | Archive a product (soft delete) | `api.admin` (60/min) |
specs/02-API-ROUTES.md:1404:| POST | `/api/admin/v1/stores/{storeId}/products/{productId}/media/presign-upload` | `write-products` | Generate presigned URL for media upload | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1405-
specs/02-API-ROUTES.md-1406-#### GET /api/admin/v1/stores/{storeId}/products
specs/02-API-ROUTES.md-1407-
specs/02-API-ROUTES.md-1408-List products with filtering and pagination.
--
specs/02-API-ROUTES.md-1535-| `variants[].inventory.policy` | string | No | One of: `deny`, `continue`. Default: `deny`. |
specs/02-API-ROUTES.md-1536-
specs/02-API-ROUTES.md-1537-**Success response:** `201 Created`
specs/02-API-ROUTES.md-1538-
specs/02-API-ROUTES.md:1539:Returns the full product resource with nested variants, options, and media.
specs/02-API-ROUTES.md-1540-
specs/02-API-ROUTES.md-1541-**Error responses:**
specs/02-API-ROUTES.md-1542-- `403` - Insufficient permissions
specs/02-API-ROUTES.md-1543-- `422` - Validation errors (duplicate SKU, duplicate handle, missing required fields)
--
specs/02-API-ROUTES.md-1545----
specs/02-API-ROUTES.md-1546-
specs/02-API-ROUTES.md-1547-#### GET /api/admin/v1/stores/{storeId}/products/{productId}
specs/02-API-ROUTES.md-1548-
specs/02-API-ROUTES.md:1549:Get a single product with all variants, options, media, and collections.
specs/02-API-ROUTES.md-1550-
specs/02-API-ROUTES.md-1551-**Success response:** `200 OK`
specs/02-API-ROUTES.md-1552-
specs/02-API-ROUTES.md-1553-```json
--
specs/02-API-ROUTES.md-1609-          "policy": "deny"
specs/02-API-ROUTES.md-1610-        }
specs/02-API-ROUTES.md-1611-      }
specs/02-API-ROUTES.md-1612-    ],
specs/02-API-ROUTES.md:1613:    "media": [
specs/02-API-ROUTES.md-1614-      {
specs/02-API-ROUTES.md-1615-        "id": 1,
specs/02-API-ROUTES.md-1616-        "type": "image",
specs/02-API-ROUTES.md-1617-        "storage_key": "stores/1/products/10/main.jpg",
--
specs/02-API-ROUTES.md-1690-- `404` - Product not found
specs/02-API-ROUTES.md-1691-
specs/02-API-ROUTES.md-1692----
specs/02-API-ROUTES.md-1693-
specs/02-API-ROUTES.md:1694:#### POST /api/admin/v1/stores/{storeId}/products/{productId}/media/presign-upload
specs/02-API-ROUTES.md-1695-
specs/02-API-ROUTES.md:1696:Generate a presigned URL for uploading product media directly to object storage (S3).
specs/02-API-ROUTES.md-1697-
specs/02-API-ROUTES.md-1698-**Request body:**
specs/02-API-ROUTES.md-1699-
specs/02-API-ROUTES.md-1700-```json
--
specs/02-API-ROUTES.md-1719-  "method": "PUT",
specs/02-API-ROUTES.md-1720-  "headers": {
specs/02-API-ROUTES.md-1721-    "Content-Type": "image/jpeg"
specs/02-API-ROUTES.md-1722-  },
specs/02-API-ROUTES.md:1723:  "storage_key": "stores/1/products/10/media/uuid.jpg",
specs/02-API-ROUTES.md:1724:  "media_id": 2,
specs/02-API-ROUTES.md-1725-  "expires_at": "2026-02-11T10:10:00Z"
specs/02-API-ROUTES.md-1726-}
specs/02-API-ROUTES.md-1727-```
specs/02-API-ROUTES.md-1728-
--
specs/02-API-ROUTES.md-2766-#### Admin Analytics Endpoint Summary
specs/02-API-ROUTES.md-2767-
specs/02-API-ROUTES.md-2768-| Method | Path | Auth Scope | Description | Rate Limit |
specs/02-API-ROUTES.md-2769-|--------|------|------------|-------------|------------|
specs/02-API-ROUTES.md:2770:| GET | `/api/admin/v1/stores/{storeId}/analytics/summary` | `read-analytics` | Get analytics summary for date range | `api.admin` (60/min) |
specs/02-API-ROUTES.md:2771:| POST | `/api/admin/v1/stores/{storeId}/exports/orders` | `read-orders` | Queue an order export job (async) | `api.admin` (60/min) |
specs/02-API-ROUTES.md:2772:| GET | `/api/admin/v1/stores/{storeId}/exports/{exportId}` | `read-orders` | Check export status and get download URL | `api.admin` (60/min) |
specs/02-API-ROUTES.md-2773-
specs/02-API-ROUTES.md:2774:#### GET /api/admin/v1/stores/{storeId}/analytics/summary
specs/02-API-ROUTES.md-2775-
specs/02-API-ROUTES.md:2776:Get analytics summary for a date range.
specs/02-API-ROUTES.md-2777-
specs/02-API-ROUTES.md-2778-**Query parameters:**
specs/02-API-ROUTES.md-2779-
specs/02-API-ROUTES.md-2780-| Parameter | Type | Required | Description |
--
specs/02-API-ROUTES.md-2830-- `422` - Invalid date range
specs/02-API-ROUTES.md-2831-
specs/02-API-ROUTES.md-2832----
specs/02-API-ROUTES.md-2833-
specs/02-API-ROUTES.md:2834:#### POST /api/admin/v1/stores/{storeId}/exports/orders
specs/02-API-ROUTES.md-2835-
specs/02-API-ROUTES.md:2836:Queue an order export job. The export runs asynchronously and produces a downloadable CSV.
specs/02-API-ROUTES.md-2837-
specs/02-API-ROUTES.md-2838-**Request body:**
specs/02-API-ROUTES.md-2839-
specs/02-API-ROUTES.md-2840-```json
--
specs/02-API-ROUTES.md-2856-**Success response:** `202 Accepted`
specs/02-API-ROUTES.md-2857-
specs/02-API-ROUTES.md-2858-```json
specs/02-API-ROUTES.md-2859-{
specs/02-API-ROUTES.md:2860:  "export_id": 1,
specs/02-API-ROUTES.md-2861-  "status": "queued",
specs/02-API-ROUTES.md-2862-  "created_at": "2026-02-11T10:00:00Z"
specs/02-API-ROUTES.md-2863-}
specs/02-API-ROUTES.md-2864-```
specs/02-API-ROUTES.md-2865-
specs/02-API-ROUTES.md-2866----
specs/02-API-ROUTES.md-2867-
specs/02-API-ROUTES.md:2868:#### GET /api/admin/v1/stores/{storeId}/exports/{exportId}
specs/02-API-ROUTES.md-2869-
specs/02-API-ROUTES.md:2870:Check export status and get download URL.
specs/02-API-ROUTES.md-2871-
specs/02-API-ROUTES.md-2872-**Success response:** `200 OK`
specs/02-API-ROUTES.md-2873-
specs/02-API-ROUTES.md-2874-```json
--
specs/02-API-ROUTES.md-2895----
specs/02-API-ROUTES.md-2896-
specs/02-API-ROUTES.md-2897-## 4. Webhook Receiver Endpoints
specs/02-API-ROUTES.md-2898-
specs/02-API-ROUTES.md:2899:> **Note:** No inbound webhook endpoints are needed. All payment processing happens in-process via the Mock PSP -- there are no external payment provider callbacks. The webhook infrastructure defined in this section is reserved for **outbound** app webhooks (see Section 5, OAuth / App Ecosystem).
specs/02-API-ROUTES.md-2900-
specs/02-API-ROUTES.md-2901----
specs/02-API-ROUTES.md-2902-
specs/02-API-ROUTES.md:2903:## 5. OAuth Endpoints (App Ecosystem)
specs/02-API-ROUTES.md-2904-
specs/02-API-ROUTES.md:2905:> **Scope note:** OAuth and app ecosystem endpoints are deferred for initial implementation. The route structure below is defined for future use but implementations should return 501 Not Implemented.
specs/02-API-ROUTES.md-2906-
specs/02-API-ROUTES.md:2907:These endpoints implement OAuth2 authorization for third-party apps, powered by Laravel Passport.
specs/02-API-ROUTES.md-2908-
specs/02-API-ROUTES.md-2909----
specs/02-API-ROUTES.md-2910-
specs/02-API-ROUTES.md:2911:### GET /oauth/authorize
specs/02-API-ROUTES.md-2912-
specs/02-API-ROUTES.md-2913-Display the authorization prompt to the merchant.
specs/02-API-ROUTES.md-2914-
specs/02-API-ROUTES.md-2915-- **Middleware:** `web`, `auth`
--
specs/02-API-ROUTES.md-2917-**Query parameters:**
specs/02-API-ROUTES.md-2918-
specs/02-API-ROUTES.md-2919-| Parameter | Type | Required | Description |
specs/02-API-ROUTES.md-2920-|-----------|------|----------|-------------|
specs/02-API-ROUTES.md:2921:| `client_id` | string | Yes | The app's OAuth client ID |
specs/02-API-ROUTES.md-2922-| `redirect_uri` | string | Yes | Must match one of the registered redirect URIs |
specs/02-API-ROUTES.md-2923-| `response_type` | string | Yes | Must be `code` |
specs/02-API-ROUTES.md-2924-| `scope` | string | Yes | Space-separated list of requested scopes |
specs/02-API-ROUTES.md-2925-| `state` | string | Yes | CSRF token for the client |
--
specs/02-API-ROUTES.md-2927-**Flow:** Merchant reviews requested scopes and approves/denies. On approval, redirects to `redirect_uri` with `code` and `state` parameters. Authorization code is single-use.
specs/02-API-ROUTES.md-2928-
specs/02-API-ROUTES.md-2929----
specs/02-API-ROUTES.md-2930-
specs/02-API-ROUTES.md:2931:### POST /oauth/token
specs/02-API-ROUTES.md-2932-
specs/02-API-ROUTES.md-2933-Exchange an authorization code for access and refresh tokens.
specs/02-API-ROUTES.md-2934-
specs/02-API-ROUTES.md-2935-- **Middleware:** None (public endpoint)
--
specs/02-API-ROUTES.md-2975----
specs/02-API-ROUTES.md-2976-
specs/02-API-ROUTES.md-2977-### App API Endpoints
specs/02-API-ROUTES.md-2978-
specs/02-API-ROUTES.md:2979:Third-party apps use OAuth tokens to access store data via scoped endpoints.
specs/02-API-ROUTES.md-2980-
specs/02-API-ROUTES.md-2981-| Method | Path | Scope Required | Description |
specs/02-API-ROUTES.md-2982-|--------|------|----------------|-------------|
specs/02-API-ROUTES.md:2983:| GET | `/api/apps/v1/stores/{storeId}/products` | `read-products` | List products (same schema as admin API) |
specs/02-API-ROUTES.md:2984:| GET | `/api/apps/v1/stores/{storeId}/orders` | `read-orders` | List orders |
specs/02-API-ROUTES.md:2985:| GET | `/api/apps/v1/stores/{storeId}/customers` | `read-customers` | List customers |
specs/02-API-ROUTES.md-2986-
specs/02-API-ROUTES.md-2987-Token scopes are enforced on every endpoint. Requests with insufficient scopes return `403 Forbidden`.
specs/02-API-ROUTES.md-2988-
specs/02-API-ROUTES.md-2989----
--
specs/02-API-ROUTES.md-3081-| `api.admin` | 60 requests | 1 minute | Sanctum token ID | Admin REST API endpoints |
specs/02-API-ROUTES.md-3082-| `api.storefront` | 120 requests | 1 minute | IP address | Storefront REST API (carts, etc.) |
specs/02-API-ROUTES.md-3083-| `checkout` | 10 requests | 1 minute | Session ID | Checkout creation and mutation endpoints |
specs/02-API-ROUTES.md-3084-| `search` | 30 requests | 1 minute | IP address | Search and autocomplete endpoints |
specs/02-API-ROUTES.md:3085:| `analytics` | 60 requests | 1 minute | IP address | Analytics event ingestion |
specs/02-API-ROUTES.md:3086:| `webhooks` | 100 requests | 1 minute | IP address | Reserved for future outbound app webhook endpoints |
specs/02-API-ROUTES.md-3087-
specs/02-API-ROUTES.md-3088-### Rate Limit Response
specs/02-API-ROUTES.md-3089-
specs/02-API-ROUTES.md-3090-When a rate limit is exceeded, the server returns `429 Too Many Requests`:
--
specs/02-API-ROUTES.md-3121-| `POST /api/storefront/v1/checkouts/*/pay` | `checkout` |
specs/02-API-ROUTES.md-3122-| `GET /api/storefront/v1/orders*` | `api.storefront` |
specs/02-API-ROUTES.md-3123-| `GET /api/storefront/v1/search` | `search` |
specs/02-API-ROUTES.md-3124-| `GET /api/storefront/v1/search/suggest` | `search` |
specs/02-API-ROUTES.md:3125:| `POST /api/storefront/v1/analytics/events` | `analytics` |
specs/02-API-ROUTES.md-3126-| `GET /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md-3127-| `POST /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md-3128-| `PUT /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md-3129-| `DELETE /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md:3130:| *(no inbound webhook endpoints - mock PSP)* | - |
specs/02-API-ROUTES.md-3131-
specs/02-API-ROUTES.md-3132----
specs/02-API-ROUTES.md-3133-
specs/02-API-ROUTES.md-3134-## 8. Checkout State Machine
--
specs/02-API-ROUTES.md-3169-## 9. API Versioning Strategy
specs/02-API-ROUTES.md-3170-
specs/02-API-ROUTES.md-3171-- **Storefront API:** Versioned via URL path (`/api/storefront/v1/`). Breaking changes require a new version.
specs/02-API-ROUTES.md-3172-- **Admin API:** Versioned via URL path (`/api/admin/v1/`). Same strategy.
specs/02-API-ROUTES.md:3173:- **App API:** Versioned via URL path (`/api/apps/v1/`).
specs/02-API-ROUTES.md:3174:- **Outbound webhook payloads:** Include a `api_version` field in outbound webhook deliveries to app installations. Each app installation pins to a version.
specs/02-API-ROUTES.md-3175-
specs/02-API-ROUTES.md-3176-Non-breaking changes (new optional fields, new endpoints) can be added to existing versions. Breaking changes (removed fields, changed types, renamed endpoints) require version bumps.
specs/02-API-ROUTES.md-3177-
specs/02-API-ROUTES.md-3178----

formatted_output:
Warning: truncated output (original token count: 16514)
Total output lines: 868

specs/06-AUTH-AND-SECURITY.md-252-| `read-collections` | List and view collections |
specs/06-AUTH-AND-SECURITY.md-253-| `write-collections` | Create, update, delete collections |
specs/06-AUTH-AND-SECURITY.md-254-| `read-discounts` | List and view discounts |
specs/06-AUTH-AND-SECURITY.md-255-| `write-discounts` | Create, update, delete discounts |
specs/06-AUTH-AND-SECURITY.md:256:| `read-analytics` | View analytics data |
specs/06-AUTH-AND-SECURITY.md-257-| `read-settings` | View store settings (general, shipping, taxes, checkout, notifications) |
specs/06-AUTH-AND-SECURITY.md-258-| `write-settings` | Update store settings (replaces the legacy `manage-store`, `manage-shipping`, `manage-taxes` scopes) |
specs/06-AUTH-AND-SECURITY.md-259-| `read-themes` | View themes and theme files |
specs/06-AUTH-AND-SECURITY.md-260-| `write-themes` | Create, update, delete, publish themes |
--
specs/06-AUTH-AND-SECURITY.md-548-| Gate Name | Required Roles | Purpose |
specs/06-AUTH-AND-SECURITY.md-549-|-----------|---------------|---------|
specs/06-AUTH-AND-SECURITY.md-550-| `manage-store-settings` | Owner, Admin | Access store settings pages |
specs/06-AUTH-AND-SECURITY.md-551-| `manage-staff` | Owner, Admin | Invite, update, remove staff members |
specs/06-AUTH-AND-SECURITY.md:552:| `manage-developers` | Owner, Admin | Create/revoke API tokens, manage webhooks |
specs/06-AUTH-AND-SECURITY.md:553:| `view-analytics` | Owner, Admin, Staff | View analytics dashboard and reports |
specs/06-AUTH-AND-SECURITY.md-554-| `manage-shipping` | Owner, Admin | Manage shipping zones, rates, profiles |
specs/06-AUTH-AND-SECURITY.md-555-| `manage-taxes` | Owner, Admin | Manage tax settings and rates |
specs/06-AUTH-AND-SECURITY.md-556-| `manage-search-settings` | Owner, Admin | Configure search weights and synonyms |
specs/06-AUTH-AND-SECURITY.md-557-| `manage-navigation` | Owner, Admin | Create/edit/delete navigation menus |
--
specs/06-AUTH-AND-SECURITY.md-613-| Admin API | `api`, `auth:sanctum`, `store.resolve`, `throttle:api.admin` | REST API endpoints |
specs/06-AUTH-AND-SECURITY.md-614-| Storefront web (public) | `web`, `store.resolve` | Public store pages (catalog, product, cart) |
specs/06-AUTH-AND-SECURITY.md-615-| Storefront web (customer) | `web`, `store.resolve`, `auth:customer` | Customer account pages |
specs/06-AUTH-AND-SECURITY.md-616-| Storefront API | `api`, `store.resolve`, `throttle:api.storefront` | Cart and checkout API |
specs/06-AUTH-AND-SECURITY.md:617:| Webhook routes | `api`, `throttle:webhooks` | Reserved for outbound app webhooks (no inbound payment webhooks - Mock PSP is in-process) |
specs/06-AUTH-AND-SECURITY.md-618-
specs/06-AUTH-AND-SECURITY.md-619----
specs/06-AUTH-AND-SECURITY.md-620-
specs/06-AUTH-AND-SECURITY.md-621-### 3.3 Custom Middleware Specifications
--
specs/06-AUTH-AND-SECURITY.md-739-| `api.admin` | 60 per minute | User ID (if authenticated), otherwise IP | Admin REST API endpoints |
specs/06-AUTH-AND-SECURITY.md-740-| `api.storefront` | 120 per minute | IP address | Public storefront API (cart, search) |
specs/06-AUTH-AND-SECURITY.md-741-| `checkout` | 10 per minute | Session ID | Checkout-related operations |
specs/06-AUTH-AND-SECURITY.md-742-| `search` | 30 per minute | IP address | Search queries |
specs/06-AUTH-AND-SECURITY.md:743:| `analytics` | 60 per minute | IP address | Analytics event ingestion |
specs/06-AUTH-AND-SECURITY.md:744:| `webhooks` | 100 per minute | IP address | Outbound app webhook endpoints (reserved; no inbound payment webhooks - Mock PSP is in-process) |
specs/06-AUTH-AND-SECURITY.md-745-
specs/06-AUTH-AND-SECURITY.md-746-**Rate limit response headers** (returned on all API responses by Laravel default):
specs/06-AUTH-AND-SECURITY.md-747-
specs/06-AUTH-AND-SECURITY.md-748-| Header | Description |
--
specs/06-AUTH-AND-SECURITY.md-762-**Fields requiring encrypted storage (using Laravel's `encrypted` cast):**
specs/06-AUTH-AND-SECURITY.md-763-
specs/06-AUTH-AND-SECURITY.md-764-| Model | Column | Cast Type | Purpose |
specs/06-AUTH-AND-SECURITY.md-765-|-------|--------|-----------|---------|
specs/06-AUTH-AND-SECURITY.md:766:| OauthClient | `client_secret_encrypted` | `encrypted` | Third-party OAuth client secrets |
specs/06-AUTH-AND-SECURITY.md:767:| WebhookSubscription | `signing_secret_encrypted` | `encrypted` | Per-subscription signing secrets for outbound webhooks |
specs/06-AUTH-AND-SECURITY.md-768-| Payment | `raw_json` | `encrypted:array` | Full payment gateway response (may contain sensitive card/account data) |
specs/06-AUTH-AND-SECURITY.md-769-
specs/06-AUTH-AND-SECURITY.md-770-**Requirements:**
specs/06-AUTH-AND-SECURITY.md-771-
--
specs/06-AUTH-AND-SECURITY.md-933-## 7. Webhook Signature Verification
specs/06-AUTH-AND-SECURITY.md-934-
specs/06-AUTH-AND-SECURITY.md-935-### 7.1 Inbound Payment Webhooks
specs/06-AUTH-AND-SECURITY.md-936-
specs/06-AUTH-AND-SECURITY.md:937:> **Not applicable.** All payment processing happens in-process via the Mock PSP. There are no inbound payment webhooks from external providers (no Stripe, no PayPal). This section is preserved for reference only -- if a real PSP is integrated in the future, inbound webhook verification would follow the patterns described in the outbound webhook section below (HMAC signature verification).
specs/06-AUTH-AND-SECURITY.md-938-
specs/06-AUTH-AND-SECURITY.md-939----
specs/06-AUTH-AND-SECURITY.md-940-
specs/06-AUTH-AND-SECURITY.md-941-### 7.2 Platform Webhooks (Outbound)
specs/06-AUTH-AND-SECURITY.md-942-
specs/06-AUTH-AND-SECURITY.md-943-Service: `App\Services\WebhookDispatcher`
specs/06-AUTH-AND-SECURITY.md-944-
specs/06-AUTH-AND-SECURITY.md:945:When the platform sends webhooks to external subscribers (third-party apps, integrations).
specs/06-AUTH-AND-SECURITY.md-946-
specs/06-AUTH-AND-SECURITY.md-947-```mermaid
specs/06-AUTH-AND-SECURITY.md-948-sequenceDiagram
specs/06-AUTH-AND-SECURITY.md-949-    participant App as Platform
--
specs/06-AUTH-AND-SECURITY.md-1037-| Event ID | Trigger | Extra Data |
specs/06-AUTH-AND-SECURITY.md-1038-|----------|---------|------------|
specs/06-AUTH-AND-SECURITY.md-1039-| `api_token.created` | New API token issued | token_name, abilities list, user_id |
specs/06-AUTH-AND-SECURITY.md-1040-| `api_token.revoked` | API token revoked | token_name, user_id |
specs/06-AUTH-AND-SECURITY.md:1041:| `webhook.signature_invalid` | Outbound webhook delivery failed signature verification at subscriber | subscriber_url, ip |
specs/06-AUTH-AND-SECURITY.md-1042-| `store.settings_changed` | Store settings updated | changed fields with old/new values |
specs/06-AUTH-AND-SECURITY.md-1043-
specs/06-AUTH-AND-SECURITY.md-1044----
specs/06-AUTH-AND-SECURITY.md-1045-
--
specs/06-AUTH-AND-SECURITY.md-1071-### 8.5 Integration with Laravel Events
specs/06-AUTH-AND-SECURITY.md-1072-
specs/06-AUTH-AND-SECURITY.md-1073-The audit logger hooks into Laravel's built-in authentication events and custom model observers.
specs/06-AUTH-AND-SECURITY.md-1074-
specs/06-AUTH-AND-SECURITY.md:1075:#### Auth Event Listeners
specs/06-AUTH-AND-SECURITY.md-1076-
specs/06-AUTH-AND-SECURITY.md:1077:Register listeners (in `AppServiceProvider` or `EventServiceProvider`) for these Laravel events:
specs/06-AUTH-AND-SECURITY.md-1078-
specs/06-AUTH-AND-SECURITY.md-1079-| Laravel Event | Audit Event ID | Data Captured |
specs/06-AUTH-AND-SECURITY.md-1080-|--------------|----------------|---------------|
specs/06-AUTH-AND-SECURITY.md-1081-| `Illuminate\Auth\Events\Login` | `auth.login` | user_id from event |
--
specs/06-AUTH-AND-SECURITY.md-1096-| 2 | Session-based auth (customer) | Custom `customer` guard, store-scoped via custom user provider | Required |
specs/06-AUTH-AND-SECURITY.md-1097-| 3 | API token auth | Sanctum personal access tokens with per-token abilities | Required |
specs/06-AUTH-AND-SECURITY.md-1098-| 4 | Role-based access control | `StoreUserRole` enum, Laravel policies on all models, gates for non-model ops | Required |
specs/06-AUTH-AND-SECURITY.md-1099-| 5 | CSRF protection | `@csrf` on all session forms, Livewire auto-handles, API routes exempt | Required |
specs/06-AUTH-AND-SECURITY.md:1100:| 6 | Rate limiting | Seven named limiters (login, api.admin, api.storefront, checkout, search, analytics, webhooks) | Required |
specs/06-AUTH-AND-SECURITY.md-1101-| 7 | Input validation | Dedicated Form Request class for every write endpoint | Required |
specs/06-AUTH-AND-SECURITY.md-1102-| 8 | XSS prevention | Blade auto-escaping by default, HTMLPurifier for rich content fields | Required |
specs/06-AUTH-AND-SECURITY.md-1103-| 9 | SQL injection prevention | Eloquent ORM with parameterized queries, FTS5 input sanitization | Required |
specs/06-AUTH-AND-SECURITY.md-1104-| 10 | Mass assignment protection | `$fillable` on all models, always use `$request->validated()` | Required |
specs/06-AUTH-AND-SECURITY.md-1105-| 11 | Encryption at rest | Laravel `encrypted` cast for secrets and sensitive JSON columns | Required |
specs/06-AUTH-AND-SECURITY.md-1106-| 12 | Password security | Bcrypt, min 8 characters, `$hidden` on User and Customer models | Required |
specs/06-AUTH-AND-SECURITY.md-1107-| 13 | Session security | Encrypted sessions, HTTP-only cookies, secure flag, SameSite=Lax | Required |
specs/06-AUTH-AND-SECURITY.md:1108:| 14 | Webhook signature verification | HMAC-SHA256 for outbound platform webhooks (no inbound payment webhooks - Mock PSP is in-process) | Required |
specs/06-AUTH-AND-SECURITY.md-1109-| 15 | Audit logging | Structured JSON to dedicated daily-rotation log channel, 90-day retention | Required |
specs/06-AUTH-AND-SECURITY.md-1110-| 16 | CORS | Restricted to `api/*` paths, credentials supported, rate limit headers exposed | Required |
--
specs/05-BUSINESS-LOGIC.md-76-- All SELECT, UPDATE, DELETE queries on tenant-scoped models include the store filter.
specs/05-BUSINESS-LOGIC.md-77-- In platform-admin contexts where cross-store access is needed, the scope can be temporarily removed.
specs/05-BUSINESS-LOGIC.md-78-
specs/05-BUSINESS-LOGIC.md-79-**Models that must use this trait:**
specs/05-BUSINESS-LOGIC.md:80:products, collections, customers, orders, carts, checkouts, discounts, shipping_zones, themes, pages, navigation_menus, analytics_events, analytics_daily, webhook_subscriptions, inventory_items, search_queries.
specs/05-BUSINESS-LOGIC.md-81-
specs/05-BUSINESS-LOGIC.md-82-### 1.3 Role-Based Access Control
specs/05-BUSINESS-LOGIC.md-83-
specs/05-BUSINESS-LOGIC.md-84-**Enum:** `App\Enums\StoreUserRole` - backed by string values: `owner`, `admin`, `staff`, `support`
--
specs/05-BUSINESS-LOGIC.md-93-| Manage orders | Yes | Yes | Yes | Yes (read-only) |
specs/05-BUSINESS-LOGIC.md-94-| Manage discounts | Yes | Yes | Yes | No |
specs/05-BUSINESS-LOGIC.md-95-| Manage themes | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md-96-| Manage apps | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md:97:| View analytics | Yes | Yes | Yes | No |
specs/05-BUSINESS-LOGIC.md-98-| Manage shipping/tax | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md-99-| Process refunds | Yes | Yes | No | No |
specs/05-BUSINESS-LOGIC.md-100-| Create fulfillments | Yes | Yes | Yes | No |
specs/05-BUSINESS-LOGIC.md-101-
--
specs/05-BUSINESS-LOGIC.md-929-## 10. Payment Processing (Mock PSP)
specs/05-BUSINESS-LOGIC.md-930-
specs/05-BUSINESS-LOGIC.md-931-### 10.1 Mock Payment Provider Architecture
specs/05-BUSINESS-LOGIC.md-932-
specs/05-BUSINESS-LOGIC.md:933:The platform uses a **Mock Payment Service Provider (PSP)** instead of real payment gateways. All payment processing happens in-process with no external API calls, no webhooks, and no redirects. This enables a fully functional checkout flow for development, testing, and demonstration purposes.
specs/05-BUSINESS-LOGIC.md-934-
specs/05-BUSINESS-LOGIC.md-935-**Interface:** `App\Contracts\PaymentProvider`
specs/05-BUSINESS-LOGIC.md-936-
specs/05-BUSINESS-LOGIC.md-937-Must implement:
--
specs/05-BUSINESS-LOGIC.md-947-**Enum:** `App\Enums\PaymentMethod` - values: `CreditCard` (`credit_card`), `Paypal` (`paypal`), `BankTransfer` (`bank_transfer`)
specs/05-BUSINESS-LOGIC.md-948-
specs/05-BUSINESS-LOGIC.md-949-| Method | Behavior | Capture Timing |
specs/05-BUSINESS-LOGIC.md-950-|---|---|---|
specs/05-BUSINESS-LOGIC.md:951:| `credit_card` | Simulates card authorization and immediate capture. Uses magic card numbers for test scenarios. | Auto-capture on checkout (instant) |
specs/05-BUSINESS-LOGIC.md:952:| `paypal` | Simulates PayPal approval and immediate capture. Always succeeds (no magic numbers). | Auto-capture on checkout (instant) |
specs/05-BUSINESS-LOGIC.md-953-| `bank_transfer` | No charge at checkout. Order created with `financial_status = pending`. Admin manually confirms payment receipt. | Deferred - admin confirms payment |
specs/05-BUSINESS-LOGIC.md-954-
specs/05-BUSINESS-LOGIC.md-955-### 10.3 Magic Card Numbers (Credit Card Testing)
specs/05-BUSINESS-LOGIC.md-956-
--
specs/05-BUSINESS-LOGIC.md-1271-   - `tracking_company = null`, `tracking_number = null`, `tracking_url = null`
specs/05-BUSINESS-LOGIC.md-1272-   - `shipped_at = now`
specs/05-BUSINESS-LOGIC.md-1273-   - Set `order.fulfillment_status = 'fulfilled'`
specs/05-BUSINESS-LOGIC.md-1274-   - Set `order.status = 'fulfilled'`
specs/05-BUSINESS-LOGIC.md:1275:3. If the order contains a mix of physical and digital products: do **not** auto-fulfill. The admin must create fulfillments manually (they can fulfill digital items immediately and physical items when shipped).
specs/05-BUSINESS-LOGIC.md-1276-
specs/05-BUSINESS-LOGIC.md:1277:This ensures digital product orders are immediately marked as complete after payment.
specs/05-BUSINESS-LOGIC.md-1278-
specs/05-BUSINESS-LOGIC.md-1279-### 11.6 Order Export
specs/05-BUSINESS-LOGIC.md-1280-
specs/05-BUSINESS-LOGIC.md:1281:`GET /api/admin/v1/orders/export` returns a CSV file of orders matching the current filter criteria.
specs/05-BUSINESS-LOGIC.md-1282-
specs/05-BUSINESS-LOGIC.md-1283-**CSV columns:** `order_number`, `created_at`, `status`, `financial_status`, `fulfillment_status`, `customer_email`, `customer_name`, `subtotal_amount`, `discount_amount`, `shipping_amount`, `tax_amount`, `total_amount`, `currency`, `shipping_method`, `tracking_number`
specs/05-BUSINESS-LOGIC.md-1284-
specs/05-BUSINESS-LOGIC.md:1285:All monetary values are exported in minor units (cents). Dates use ISO-8601 format. The response uses `Content-Type: text/csv` with `Content-Disposition: attachment; filename="orders-{date}.csv"`.
specs/05-BUSINESS-LOGIC.md-1286-
specs/05-BUSINESS-LOGIC.md-1287----
specs/05-BUSINESS-LOGIC.md-1288-
specs/05-BUSINESS-LOGIC.md-1289-## 12. Customer Accounts
--
specs/05-BUSINESS-LOGIC.md-1345-## 13. Webhook Delivery System
specs/05-BUSINESS-LOGIC.md-1346-
specs/05-BUSINESS-LOGIC.md-1347-### 13.1 Subscription Model
specs/05-BUSINESS-LOGIC.md-1348-
specs/05-BUSINESS-LOGIC.md:1349:Stores (or app installations) subscribe to event types via `webhook_subscriptions`.
specs/05-BUSINESS-LOGIC.md-1350-
specs/05-BUSINESS-LOGIC.md-1351-**Supported event types:**
specs/05-BUSINESS-LOGIC.md-1352-
specs/05-BUSINESS-LOGIC.md-1353-| Event Type | Triggered By |
--
specs/05-BUSINESS-LOGIC.md-1362-| `checkout.completed` | Checkout successfully completed |
specs/05-BUSINESS-LOGIC.md-1363-
specs/05-BUSINESS-LOGIC.md-1364-### 13.2 Delivery Process
specs/05-BUSINESS-LOGIC.md-1365-
specs/05-BUSINESS-LOGIC.md:1366:**Listener:** `App\Listeners\DispatchWebhooks` - registered to listen for all domain events that have webhook counterparts.
specs/05-BUSINESS-LOGIC.md-1367-
specs/05-BUSINESS-LOGIC.md-1368-**Job:** `App\Jobs\DeliverWebhook`
specs/05-BUSINESS-LOGIC.md-1369-
specs/05-BUSINESS-LOGIC.md-1370-```mermaid
specs/05-BUSINESS-LOGIC.md-1371-sequenceDiagram
specs/05-BUSINESS-LOGIC.md-1372-    participant DomainEvent
specs/05-BUSINESS-LOGIC.md:1373:    participant Listener
specs/05-BUSINESS-LOGIC.md-1374-    participant Queue
specs/05-BUSINESS-LOGIC.md-1375-    participant Job
specs/05-BUSINESS-LOGIC.md-1376-    participant TargetURL
specs/05-BUSINESS-LOGIC.md-1377-
specs/05-BUSINESS-LOGIC.md:1378:    DomainEvent->>Listener: Event fired (e.g., OrderCreated)
specs/05-BUSINESS-LOGIC.md:1379:    Listener->>Listener: Query webhook_subscriptions for store_id + event_type where status = active
specs/05-BUSINESS-LOGIC.md-1380-    loop For each matching subscription
specs/05-BUSINESS-LOGIC.md:1381:        Listener->>Queue: Dispatch DeliverWebhook job
specs/05-BUSINESS-LOGIC.md-1382-    end
specs/05-BUSINESS-LOGIC.md-1383-    Queue->>Job: Process job
specs/05-BUSINESS-LOGIC.md-1384-    Job->>Job: Serialize event payload as JSON
specs/05-BUSINESS-LOGIC.md-1385-    Job->>Job: Generate HMAC-SHA256 signature using subscription's signing_secret (decrypted)
specs/05-BUSINESS-LOGIC.md-1386-    Job->>TargetURL: HTTP POST with headers and payload
specs/05-BUSINESS-LOGIC.md-1387-    TargetURL-->>Job: Response
specs/05-BUSINESS-LOGIC.md:1388:    Job->>Job: Record result in webhook_deliveries
specs/05-BUSINESS-LOGIC.md-1389-```
specs/05-BUSINESS-LOGIC.md-1390-
specs/05-BUSINESS-LOGIC.md-1391-**Required HTTP headers on delivery:**
specs/05-BUSINESS-LOGIC.md-1392-
--
specs/05-BUSINESS-LOGIC.md-1403-On non-2xx response or connection timeout:
specs/05-BUSINESS-LOGIC.md-1404-
specs/05-BUSINESS-LOGIC.md-1405-| Attempt | Backoff Delay |
specs/05-BUSINESS-LOGIC.md-1406-|---|---|
specs/05-BUSINESS-LOGIC.md:1407:| 1 (initial) | Immediate |
specs/05-BUSINESS-LOGIC.md-1408-| 2 | 1 minute |
specs/05-BUSINESS-LOGIC.md-1409-| 3 | 5 minutes |
specs/05-BUSINESS-LOGIC.md-1410-| 4 | 30 minutes |
specs/05-BUSINESS-LOGIC.md-1411-| 5 | 2 hours |
--
specs/05-BUSINESS-LOGIC.md-1417-
specs/05-BUSINESS-LOGIC.md-1418-### 13.4 Circuit Breaker
specs/05-BUSINESS-LOGIC.md-1419-
specs/05-BUSINESS-LOGIC.md-1420-- Track consecutive failures per subscription (across different events).
specs/05-BUSINESS-LOGIC.md:1421:- If a subscription's endpoint fails 5 consecutive times: automatically set `webhook_subscriptions.status = 'paused'`.
specs/05-BUSINESS-LOGIC.md-1422-- Log a warning and (future) send an admin notification.
specs/05-BUSINESS-LOGIC.md-1423-- Manual re-enable required by the merchant through the admin UI.
specs/05-BUSINESS-LOGIC.md-1424-- On each successful delivery, reset the consecutive failure counter to 0.
specs/05-BUSINESS-LOGIC.md-1425-
--
specs/05-BUSINESS-LOGIC.md-1430-## 14. Analytics
specs/05-BUSINESS-LOGIC.md-1431-
specs/05-BUSINESS-LOGIC.md-1432-### 14.1 Event Ingestion
specs/05-BUSINESS-LOGIC.md-1433-
specs/05-BUSINESS-LOGIC.md:1434:**Endpoint:** `POST /api/storefront/v1/analytics/events`
specs/05-BUSINESS-LOGIC.md-1435-
specs/05-BUSINESS-LOGIC.md-1436-**Request structure:**
specs/05-BUSINESS-LOGIC.md-1437-
specs/05-BUSINESS-LOGIC.md-1438-| Field | Type | Required | Description |
--
specs/05-BUSINESS-LOGIC.md-1471-
specs/05-BUSINESS-LOGIC.md-1472-**Job:** `App\Jobs\AggregateAnalytics`
specs/05-BUSINESS-LOGIC.md-1473-
specs/05-BUSINESS-LOGIC.md-1474-- Schedule: daily at 01:00 UTC
specs/05-BUSINESS-LOGIC.md:1475:- Aggregates the previous day's raw `analytics_events` into `analytics_daily`.
specs/05-BUSINESS-LOGIC.md-1476-
specs/05-BUSINESS-LOGIC.md-1477-**Aggregated metrics:**
specs/05-BUSINESS-LOGIC.md-1478-
specs/05-BUSINESS-LOGIC.md-1479-| Metric | Calculation |
--
specs/05-BUSINESS-LOGIC.md-1492-### 15.1 Upload Flow
specs/05-BUSINESS-LOGIC.md-1493-
specs/05-BUSINESS-LOGIC.md-1494-```mermaid
specs/05-BUSINESS-LOGIC.md-1495-flowchart TD
specs/05-BUSINESS-LOGIC.md:1496:    A[Admin uploads file via Livewire] --> B[Store original in storage/app/public/media/originals/]
specs/05-BUSINESS-LOGIC.md:1497:    B --> C[Create product_media record with status = processing]
specs/05-BUSINESS-LOGIC.md-1498-    C --> D[Dispatch ProcessMediaUpload job to queue]
specs/05-BUSINESS-LOGIC.md-1499-    D --> E{Processing succeeds?}
specs/05-BUSINESS-LOGIC.md-1500-    E -->|Yes| F[Store resized versions, set status = ready]
specs/05-BUSINESS-LOGIC.md-1501-    E …6514 tokens truncated…-
specs/09-IMPLEMENTATION-ROADMAP.md-1776-| Name | Purpose |
--
specs/09-IMPLEMENTATION-ROADMAP.md-1842-### Jobs
specs/09-IMPLEMENTATION-ROADMAP.md-1843-
specs/09-IMPLEMENTATION-ROADMAP.md-1844-| Name | Purpose |
specs/09-IMPLEMENTATION-ROADMAP.md-1845-|------|---------|
specs/09-IMPLEMENTATION-ROADMAP.md:1846:| `ProcessMediaUpload` | Resize images, update media status |
specs/09-IMPLEMENTATION-ROADMAP.md:1847:| `DeliverWebhook` | HTTP POST webhook with retry logic |
specs/09-IMPLEMENTATION-ROADMAP.md:1848:| `AggregateAnalytics` | Daily analytics aggregation |
specs/09-IMPLEMENTATION-ROADMAP.md-1849-| `CleanupAbandonedCarts` | Mark old active carts as abandoned |
specs/09-IMPLEMENTATION-ROADMAP.md-1850-| `ExpireAbandonedCheckouts` | Expire timed-out checkouts, release inventory |
specs/09-IMPLEMENTATION-ROADMAP.md-1851-
specs/09-IMPLEMENTATION-ROADMAP.md-1852-### Events
--
specs/09-IMPLEMENTATION-ROADMAP.md-1933-| `config/auth.php` | Customer guard | Add customer guard, customer provider, customer password broker | Customer storefront auth |
specs/09-IMPLEMENTATION-ROADMAP.md-1934-| `config/session.php` | Driver | File driver, lifetime 120 | Session configuration |
specs/09-IMPLEMENTATION-ROADMAP.md-1935-| `config/cache.php` | Driver | File driver | Cache configuration |
specs/09-IMPLEMENTATION-ROADMAP.md-1936-| `config/queue.php` | Driver | Sync driver | Queue configuration |
specs/09-IMPLEMENTATION-ROADMAP.md:1937:| `config/filesystems.php` | Public disk | Media storage configuration | Public disk for media |
specs/09-IMPLEMENTATION-ROADMAP.md-1938-| `config/logging.php` | JSON channel | Structured JSON channel | Structured logging |
specs/09-IMPLEMENTATION-ROADMAP.md-1939-
specs/09-IMPLEMENTATION-ROADMAP.md-1940-### bootstrap/app.php Changes
specs/09-IMPLEMENTATION-ROADMAP.md-1941-
specs/09-IMPLEMENTATION-ROADMAP.md-1942-- Register middleware aliases: `store.resolve`, `role.check`, `auth:customer`
specs/09-IMPLEMENTATION-ROADMAP.md-1943-- Register middleware groups for `storefront` and `admin` route groups
specs/09-IMPLEMENTATION-ROADMAP.md:1944:- Configure rate limiters: `login` (5/min per IP), `api.admin` (60/min per token), `api.storefront` (120/min per IP), `checkout` (10/min per session), `search` (30/min per IP), `analytics` (60/min per IP), `webhooks` (100/min per IP)
specs/09-IMPLEMENTATION-ROADMAP.md-1945-- Configure exception handling
specs/09-IMPLEMENTATION-ROADMAP.md-1946-
specs/09-IMPLEMENTATION-ROADMAP.md-1947-### Routes Files
specs/09-IMPLEMENTATION-ROADMAP.md-1948-
--
specs/09-IMPLEMENTATION-ROADMAP.md-2051-6. `StoreSettingsSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md-2052-7. `TaxSettingsSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md-2053-8. `ShippingSeeder` - creates shipping zones and rates
specs/09-IMPLEMENTATION-ROADMAP.md-2054-9. `CollectionSeeder` - creates collections (before products so products can be linked)
specs/09-IMPLEMENTATION-ROADMAP.md:2055:10. `ProductSeeder` - creates products with options, variants, inventory, media, and collection associations
specs/09-IMPLEMENTATION-ROADMAP.md-2056-11. `DiscountSeeder`
specs/09-IMPLEMENTATION-ROADMAP.md-2057-12. `CustomerSeeder` - creates customers and their addresses
specs/09-IMPLEMENTATION-ROADMAP.md-2058-13. `OrderSeeder` - creates sample orders with lines, payments, fulfillments
specs/09-IMPLEMENTATION-ROADMAP.md-2059-14. `ThemeSeeder` - creates themes with files and settings
--
specs/02-API-ROUTES.md-46-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-47-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md-48-| GET | `/admin/products` | Product list - searchable, filterable by status, bulk actions | `Admin\Products\Index` |
specs/02-API-ROUTES.md-49-| GET | `/admin/products/create` | Create product form | `Admin\Products\Create` |
specs/02-API-ROUTES.md:50:| GET | `/admin/products/{product}/edit` | Edit product form with variants, media, options | `Admin\Products\Edit` |
specs/02-API-ROUTES.md-51-
specs/02-API-ROUTES.md-52-#### Inventory
specs/02-API-ROUTES.md-53-
specs/02-API-ROUTES.md-54-| Method | Path | Description | Component |
--
specs/02-API-ROUTES.md-118-
specs/02-API-ROUTES.md-119-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-120-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md-121-| GET | `/admin/apps` | Apps directory - list installed and available apps | `Admin\Apps\Index` |
specs/02-API-ROUTES.md:122:| GET | `/admin/apps/{installation}` | Installed app detail - scopes, webhooks, usage | `Admin\Apps\Show` |
specs/02-API-ROUTES.md-123-
specs/02-API-ROUTES.md-124-#### Developers
specs/02-API-ROUTES.md-125-
specs/02-API-ROUTES.md-126-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-127-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md:128:| GET | `/admin/developers` | Developer settings - API keys/tokens, webhook management | `Admin\Developers\Index` |
specs/02-API-ROUTES.md-129-
specs/02-API-ROUTES.md:130:> **Note:** Webhook subscription management (CRUD) is available exclusively through the admin UI at `/admin/developers`. There are no REST API endpoints for programmatic webhook subscription management in the initial implementation.
specs/02-API-ROUTES.md-131-
specs/02-API-ROUTES.md-132-#### Analytics
specs/02-API-ROUTES.md-133-
specs/02-API-ROUTES.md-134-| Method | Path | Description | Component |
specs/02-API-ROUTES.md-135-|--------|------|-------------|-----------|
specs/02-API-ROUTES.md:136:| GET | `/admin/analytics` | Analytics dashboard - sales, traffic, funnel, exports | `Admin\Analytics\Index` |
specs/02-API-ROUTES.md-137-
specs/02-API-ROUTES.md-138-#### Search Settings
specs/02-API-ROUTES.md-139-
specs/02-API-ROUTES.md-140-| Method | Path | Description | Component |
--
specs/02-API-ROUTES.md-199-**Prefix:** `/api/storefront/v1`
specs/02-API-ROUTES.md-200-**Middleware:** `store.resolve`, `throttle:api.storefront`
specs/02-API-ROUTES.md-201-**Content-Type:** `application/json`
specs/02-API-ROUTES.md-202-
specs/02-API-ROUTES.md:203:These endpoints power the interactive storefront experience (cart drawer, checkout flow, search autocomplete, analytics).
specs/02-API-ROUTES.md-204-
specs/02-API-ROUTES.md-205----
specs/02-API-ROUTES.md-206-
specs/02-API-ROUTES.md-207-### 2.1 Cart Endpoints
--
specs/02-API-ROUTES.md-846-### 2.3 Payment Endpoint
specs/02-API-ROUTES.md-847-
specs/02-API-ROUTES.md-848-#### POST /api/storefront/v1/checkouts/{checkoutId}/pay
specs/02-API-ROUTES.md-849-
specs/02-API-ROUTES.md:850:Process payment for a checkout using the selected payment method. All payment processing happens in-process via the Mock PSP - no external API calls, no redirects, no webhooks.
specs/02-API-ROUTES.md-851-
specs/02-API-ROUTES.md-852-- **Authentication:** None
specs/02-API-ROUTES.md-853-- **Rate limit:** `checkout` (10/min per session)
specs/02-API-ROUTES.md-854-
--
specs/02-API-ROUTES.md-1139----
specs/02-API-ROUTES.md-1140-
specs/02-API-ROUTES.md-1141-### 2.6 Analytics Endpoints
specs/02-API-ROUTES.md-1142-
specs/02-API-ROUTES.md:1143:#### POST /api/storefront/v1/analytics/events
specs/02-API-ROUTES.md-1144-
specs/02-API-ROUTES.md:1145:Batch submit storefront analytics events.
specs/02-API-ROUTES.md-1146-
specs/02-API-ROUTES.md-1147-- **Authentication:** None
specs/02-API-ROUTES.md:1148:- **Rate limit:** `analytics` (60/min per IP)
specs/02-API-ROUTES.md-1149-
specs/02-API-ROUTES.md-1150-**Request body:**
specs/02-API-ROUTES.md-1151-
specs/02-API-ROUTES.md-1152-```json
--
specs/02-API-ROUTES.md-1400-| POST | `/api/admin/v1/stores/{storeId}/products` | `write-products` | Create a new product | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1401-| GET | `/api/admin/v1/stores/{storeId}/products/{productId}` | `read-products` | Get single product with all relations | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1402-| PUT | `/api/admin/v1/stores/{storeId}/products/{productId}` | `write-products` | Update a product (partial updates supported) | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1403-| DELETE | `/api/admin/v1/stores/{storeId}/products/{productId}` | `write-products` | Archive a product (soft delete) | `api.admin` (60/min) |
specs/02-API-ROUTES.md:1404:| POST | `/api/admin/v1/stores/{storeId}/products/{productId}/media/presign-upload` | `write-products` | Generate presigned URL for media upload | `api.admin` (60/min) |
specs/02-API-ROUTES.md-1405-
specs/02-API-ROUTES.md-1406-#### GET /api/admin/v1/stores/{storeId}/products
specs/02-API-ROUTES.md-1407-
specs/02-API-ROUTES.md-1408-List products with filtering and pagination.
--
specs/02-API-ROUTES.md-1535-| `variants[].inventory.policy` | string | No | One of: `deny`, `continue`. Default: `deny`. |
specs/02-API-ROUTES.md-1536-
specs/02-API-ROUTES.md-1537-**Success response:** `201 Created`
specs/02-API-ROUTES.md-1538-
specs/02-API-ROUTES.md:1539:Returns the full product resource with nested variants, options, and media.
specs/02-API-ROUTES.md-1540-
specs/02-API-ROUTES.md-1541-**Error responses:**
specs/02-API-ROUTES.md-1542-- `403` - Insufficient permissions
specs/02-API-ROUTES.md-1543-- `422` - Validation errors (duplicate SKU, duplicate handle, missing required fields)
--
specs/02-API-ROUTES.md-1545----
specs/02-API-ROUTES.md-1546-
specs/02-API-ROUTES.md-1547-#### GET /api/admin/v1/stores/{storeId}/products/{productId}
specs/02-API-ROUTES.md-1548-
specs/02-API-ROUTES.md:1549:Get a single product with all variants, options, media, and collections.
specs/02-API-ROUTES.md-1550-
specs/02-API-ROUTES.md-1551-**Success response:** `200 OK`
specs/02-API-ROUTES.md-1552-
specs/02-API-ROUTES.md-1553-```json
--
specs/02-API-ROUTES.md-1609-          "policy": "deny"
specs/02-API-ROUTES.md-1610-        }
specs/02-API-ROUTES.md-1611-      }
specs/02-API-ROUTES.md-1612-    ],
specs/02-API-ROUTES.md:1613:    "media": [
specs/02-API-ROUTES.md-1614-      {
specs/02-API-ROUTES.md-1615-        "id": 1,
specs/02-API-ROUTES.md-1616-        "type": "image",
specs/02-API-ROUTES.md-1617-        "storage_key": "stores/1/products/10/main.jpg",
--
specs/02-API-ROUTES.md-1690-- `404` - Product not found
specs/02-API-ROUTES.md-1691-
specs/02-API-ROUTES.md-1692----
specs/02-API-ROUTES.md-1693-
specs/02-API-ROUTES.md:1694:#### POST /api/admin/v1/stores/{storeId}/products/{productId}/media/presign-upload
specs/02-API-ROUTES.md-1695-
specs/02-API-ROUTES.md:1696:Generate a presigned URL for uploading product media directly to object storage (S3).
specs/02-API-ROUTES.md-1697-
specs/02-API-ROUTES.md-1698-**Request body:**
specs/02-API-ROUTES.md-1699-
specs/02-API-ROUTES.md-1700-```json
--
specs/02-API-ROUTES.md-1719-  "method": "PUT",
specs/02-API-ROUTES.md-1720-  "headers": {
specs/02-API-ROUTES.md-1721-    "Content-Type": "image/jpeg"
specs/02-API-ROUTES.md-1722-  },
specs/02-API-ROUTES.md:1723:  "storage_key": "stores/1/products/10/media/uuid.jpg",
specs/02-API-ROUTES.md:1724:  "media_id": 2,
specs/02-API-ROUTES.md-1725-  "expires_at": "2026-02-11T10:10:00Z"
specs/02-API-ROUTES.md-1726-}
specs/02-API-ROUTES.md-1727-```
specs/02-API-ROUTES.md-1728-
--
specs/02-API-ROUTES.md-2766-#### Admin Analytics Endpoint Summary
specs/02-API-ROUTES.md-2767-
specs/02-API-ROUTES.md-2768-| Method | Path | Auth Scope | Description | Rate Limit |
specs/02-API-ROUTES.md-2769-|--------|------|------------|-------------|------------|
specs/02-API-ROUTES.md:2770:| GET | `/api/admin/v1/stores/{storeId}/analytics/summary` | `read-analytics` | Get analytics summary for date range | `api.admin` (60/min) |
specs/02-API-ROUTES.md:2771:| POST | `/api/admin/v1/stores/{storeId}/exports/orders` | `read-orders` | Queue an order export job (async) | `api.admin` (60/min) |
specs/02-API-ROUTES.md:2772:| GET | `/api/admin/v1/stores/{storeId}/exports/{exportId}` | `read-orders` | Check export status and get download URL | `api.admin` (60/min) |
specs/02-API-ROUTES.md-2773-
specs/02-API-ROUTES.md:2774:#### GET /api/admin/v1/stores/{storeId}/analytics/summary
specs/02-API-ROUTES.md-2775-
specs/02-API-ROUTES.md:2776:Get analytics summary for a date range.
specs/02-API-ROUTES.md-2777-
specs/02-API-ROUTES.md-2778-**Query parameters:**
specs/02-API-ROUTES.md-2779-
specs/02-API-ROUTES.md-2780-| Parameter | Type | Required | Description |
--
specs/02-API-ROUTES.md-2830-- `422` - Invalid date range
specs/02-API-ROUTES.md-2831-
specs/02-API-ROUTES.md-2832----
specs/02-API-ROUTES.md-2833-
specs/02-API-ROUTES.md:2834:#### POST /api/admin/v1/stores/{storeId}/exports/orders
specs/02-API-ROUTES.md-2835-
specs/02-API-ROUTES.md:2836:Queue an order export job. The export runs asynchronously and produces a downloadable CSV.
specs/02-API-ROUTES.md-2837-
specs/02-API-ROUTES.md-2838-**Request body:**
specs/02-API-ROUTES.md-2839-
specs/02-API-ROUTES.md-2840-```json
--
specs/02-API-ROUTES.md-2856-**Success response:** `202 Accepted`
specs/02-API-ROUTES.md-2857-
specs/02-API-ROUTES.md-2858-```json
specs/02-API-ROUTES.md-2859-{
specs/02-API-ROUTES.md:2860:  "export_id": 1,
specs/02-API-ROUTES.md-2861-  "status": "queued",
specs/02-API-ROUTES.md-2862-  "created_at": "2026-02-11T10:00:00Z"
specs/02-API-ROUTES.md-2863-}
specs/02-API-ROUTES.md-2864-```
specs/02-API-ROUTES.md-2865-
specs/02-API-ROUTES.md-2866----
specs/02-API-ROUTES.md-2867-
specs/02-API-ROUTES.md:2868:#### GET /api/admin/v1/stores/{storeId}/exports/{exportId}
specs/02-API-ROUTES.md-2869-
specs/02-API-ROUTES.md:2870:Check export status and get download URL.
specs/02-API-ROUTES.md-2871-
specs/02-API-ROUTES.md-2872-**Success response:** `200 OK`
specs/02-API-ROUTES.md-2873-
specs/02-API-ROUTES.md-2874-```json
--
specs/02-API-ROUTES.md-2895----
specs/02-API-ROUTES.md-2896-
specs/02-API-ROUTES.md-2897-## 4. Webhook Receiver Endpoints
specs/02-API-ROUTES.md-2898-
specs/02-API-ROUTES.md:2899:> **Note:** No inbound webhook endpoints are needed. All payment processing happens in-process via the Mock PSP -- there are no external payment provider callbacks. The webhook infrastructure defined in this section is reserved for **outbound** app webhooks (see Section 5, OAuth / App Ecosystem).
specs/02-API-ROUTES.md-2900-
specs/02-API-ROUTES.md-2901----
specs/02-API-ROUTES.md-2902-
specs/02-API-ROUTES.md:2903:## 5. OAuth Endpoints (App Ecosystem)
specs/02-API-ROUTES.md-2904-
specs/02-API-ROUTES.md:2905:> **Scope note:** OAuth and app ecosystem endpoints are deferred for initial implementation. The route structure below is defined for future use but implementations should return 501 Not Implemented.
specs/02-API-ROUTES.md-2906-
specs/02-API-ROUTES.md:2907:These endpoints implement OAuth2 authorization for third-party apps, powered by Laravel Passport.
specs/02-API-ROUTES.md-2908-
specs/02-API-ROUTES.md-2909----
specs/02-API-ROUTES.md-2910-
specs/02-API-ROUTES.md:2911:### GET /oauth/authorize
specs/02-API-ROUTES.md-2912-
specs/02-API-ROUTES.md-2913-Display the authorization prompt to the merchant.
specs/02-API-ROUTES.md-2914-
specs/02-API-ROUTES.md-2915-- **Middleware:** `web`, `auth`
--
specs/02-API-ROUTES.md-2917-**Query parameters:**
specs/02-API-ROUTES.md-2918-
specs/02-API-ROUTES.md-2919-| Parameter | Type | Required | Description |
specs/02-API-ROUTES.md-2920-|-----------|------|----------|-------------|
specs/02-API-ROUTES.md:2921:| `client_id` | string | Yes | The app's OAuth client ID |
specs/02-API-ROUTES.md-2922-| `redirect_uri` | string | Yes | Must match one of the registered redirect URIs |
specs/02-API-ROUTES.md-2923-| `response_type` | string | Yes | Must be `code` |
specs/02-API-ROUTES.md-2924-| `scope` | string | Yes | Space-separated list of requested scopes |
specs/02-API-ROUTES.md-2925-| `state` | string | Yes | CSRF token for the client |
--
specs/02-API-ROUTES.md-2927-**Flow:** Merchant reviews requested scopes and approves/denies. On approval, redirects to `redirect_uri` with `code` and `state` parameters. Authorization code is single-use.
specs/02-API-ROUTES.md-2928-
specs/02-API-ROUTES.md-2929----
specs/02-API-ROUTES.md-2930-
specs/02-API-ROUTES.md:2931:### POST /oauth/token
specs/02-API-ROUTES.md-2932-
specs/02-API-ROUTES.md-2933-Exchange an authorization code for access and refresh tokens.
specs/02-API-ROUTES.md-2934-
specs/02-API-ROUTES.md-2935-- **Middleware:** None (public endpoint)
--
specs/02-API-ROUTES.md-2975----
specs/02-API-ROUTES.md-2976-
specs/02-API-ROUTES.md-2977-### App API Endpoints
specs/02-API-ROUTES.md-2978-
specs/02-API-ROUTES.md:2979:Third-party apps use OAuth tokens to access store data via scoped endpoints.
specs/02-API-ROUTES.md-2980-
specs/02-API-ROUTES.md-2981-| Method | Path | Scope Required | Description |
specs/02-API-ROUTES.md-2982-|--------|------|----------------|-------------|
specs/02-API-ROUTES.md:2983:| GET | `/api/apps/v1/stores/{storeId}/products` | `read-products` | List products (same schema as admin API) |
specs/02-API-ROUTES.md:2984:| GET | `/api/apps/v1/stores/{storeId}/orders` | `read-orders` | List orders |
specs/02-API-ROUTES.md:2985:| GET | `/api/apps/v1/stores/{storeId}/customers` | `read-customers` | List customers |
specs/02-API-ROUTES.md-2986-
specs/02-API-ROUTES.md-2987-Token scopes are enforced on every endpoint. Requests with insufficient scopes return `403 Forbidden`.
specs/02-API-ROUTES.md-2988-
specs/02-API-ROUTES.md-2989----
--
specs/02-API-ROUTES.md-3081-| `api.admin` | 60 requests | 1 minute | Sanctum token ID | Admin REST API endpoints |
specs/02-API-ROUTES.md-3082-| `api.storefront` | 120 requests | 1 minute | IP address | Storefront REST API (carts, etc.) |
specs/02-API-ROUTES.md-3083-| `checkout` | 10 requests | 1 minute | Session ID | Checkout creation and mutation endpoints |
specs/02-API-ROUTES.md-3084-| `search` | 30 requests | 1 minute | IP address | Search and autocomplete endpoints |
specs/02-API-ROUTES.md:3085:| `analytics` | 60 requests | 1 minute | IP address | Analytics event ingestion |
specs/02-API-ROUTES.md:3086:| `webhooks` | 100 requests | 1 minute | IP address | Reserved for future outbound app webhook endpoints |
specs/02-API-ROUTES.md-3087-
specs/02-API-ROUTES.md-3088-### Rate Limit Response
specs/02-API-ROUTES.md-3089-
specs/02-API-ROUTES.md-3090-When a rate limit is exceeded, the server returns `429 Too Many Requests`:
--
specs/02-API-ROUTES.md-3121-| `POST /api/storefront/v1/checkouts/*/pay` | `checkout` |
specs/02-API-ROUTES.md-3122-| `GET /api/storefront/v1/orders*` | `api.storefront` |
specs/02-API-ROUTES.md-3123-| `GET /api/storefront/v1/search` | `search` |
specs/02-API-ROUTES.md-3124-| `GET /api/storefront/v1/search/suggest` | `search` |
specs/02-API-ROUTES.md:3125:| `POST /api/storefront/v1/analytics/events` | `analytics` |
specs/02-API-ROUTES.md-3126-| `GET /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md-3127-| `POST /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md-3128-| `PUT /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md-3129-| `DELETE /api/admin/v1/*` | `api.admin` |
specs/02-API-ROUTES.md:3130:| *(no inbound webhook endpoints - mock PSP)* | - |
specs/02-API-ROUTES.md-3131-
specs/02-API-ROUTES.md-3132----
specs/02-API-ROUTES.md-3133-
specs/02-API-ROUTES.md-3134-## 8. Checkout State Machine
--
specs/02-API-ROUTES.md-3169-## 9. API Versioning Strategy
specs/02-API-ROUTES.md-3170-
specs/02-API-ROUTES.md-3171-- **Storefront API:** Versioned via URL path (`/api/storefront/v1/`). Breaking changes require a new version.
specs/02-API-ROUTES.md-3172-- **Admin API:** Versioned via URL path (`/api/admin/v1/`). Same strategy.
specs/02-API-ROUTES.md:3173:- **App API:** Versioned via URL path (`/api/apps/v1/`).
specs/02-API-ROUTES.md:3174:- **Outbound webhook payloads:** Include a `api_version` field in outbound webhook deliveries to app installations. Each app installation pins to a version.
specs/02-API-ROUTES.md-3175-
specs/02-API-ROUTES.md-3176-Non-breaking changes (new optional fields, new endpoints) can be added to existing versions. Breaking changes (removed fields, changed types, renamed endpoints) require version bumps.
specs/02-API-ROUTES.md-3177-
specs/02-API-ROUTES.md-3178----