# Shop API Reference **Last Updated:** 2025-11-22 **API Version:** v1 **Base Path:** `/api/v1/shop` --- ## Overview The Shop API provides customer-facing endpoints for browsing products, managing cart, placing orders, and customer authentication. All endpoints use **middleware-based vendor context** - no vendor ID in URLs! ### Key Features ✅ **Automatic Vendor Detection** - Vendor extracted from Referer header via middleware ✅ **Multi-Tenant** - Each vendor has isolated customer data ✅ **Session-Based Cart** - No authentication required for browsing/cart ✅ **Secure Authentication** - JWT tokens with HTTP-only cookies (path=/shop) ✅ **RESTful Design** - Standard HTTP methods and status codes --- ## How Vendor Context Works All Shop API endpoints automatically receive vendor context from the `VendorContextMiddleware`: 1. **Browser makes API call** from shop page (e.g., `/vendors/wizamart/shop/products`) 2. **Browser automatically sends Referer header**: `http://localhost:8000/vendors/wizamart/shop/products` 3. **Middleware extracts vendor** from Referer path/subdomain/domain 4. **Middleware sets** `request.state.vendor = ` 5. **API endpoint accesses vendor**: `vendor = request.state.vendor` 6. **No vendor_id needed in URL!** ### Supported Vendor Detection Methods - **Path-based**: `/vendors/wizamart/shop/products` → extracts `wizamart` - **Subdomain**: `wizamart.platform.com` → extracts `wizamart` - **Custom domain**: `customshop.com` → looks up vendor by domain --- ## Authentication ### Public Endpoints (No Auth Required) - Product catalog - Product details - Product search - Cart operations - CMS content pages ### Authenticated Endpoints (Customer Token Required) - Place order - Order history - Order details ### Authentication Headers For authenticated requests, include the JWT token: ```http Authorization: Bearer ``` Or use the HTTP-only cookie (automatically sent by browser): ```http Cookie: customer_token= ``` --- ## Products ### Get Product Catalog Get paginated list of products for current vendor. **Endpoint:** `GET /api/v1/shop/products` **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `skip` | integer | 0 | Number of products to skip (pagination) | | `limit` | integer | 100 | Maximum products to return (max 1000) | | `search` | string | null | Search products by name/description | | `is_featured` | boolean | null | Filter by featured products only | **Request Example:** ```http GET /api/v1/shop/products?skip=0&limit=20&is_featured=true Referer: http://localhost:8000/vendors/wizamart/shop/products ``` **Response (200 OK):** ```json { "products": [ { "id": 1, "vendor_id": 1, "product_id": "PROD-001", "price": 29.99, "sale_price": null, "currency": "EUR", "availability": "in stock", "is_featured": true, "is_active": true, "marketplace_product": { "title": "Sample Product", "description": "Product description...", "image_link": "https://example.com/image.jpg", "brand": "Brand Name" } } ], "total": 50, "skip": 0, "limit": 20 } ``` --- ### Get Product Details Get detailed information for a specific product. **Endpoint:** `GET /api/v1/shop/products/{product_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `product_id` | integer | Product ID | **Request Example:** ```http GET /api/v1/shop/products/1 Referer: http://localhost:8000/vendors/wizamart/shop/products ``` **Response (200 OK):** ```json { "id": 1, "vendor_id": 1, "product_id": "PROD-001", "price": 29.99, "sale_price": 24.99, "currency": "EUR", "availability": "in stock", "condition": "new", "is_featured": true, "is_active": true, "min_quantity": 1, "max_quantity": 10, "total_inventory": 100, "available_inventory": 95, "marketplace_product": { "title": "Sample Product", "description": "Full product description...", "image_link": "https://example.com/image.jpg", "brand": "Brand Name", "gtin": "1234567890123" } } ``` **Error Responses:** - `404 Not Found` - Product not found or not active - `404 Not Found` - Vendor not found (missing/invalid Referer) --- ## Shopping Cart Cart operations use session-based tracking (no authentication required). ### Get Cart Retrieve cart contents for a session. **Endpoint:** `GET /api/v1/shop/cart/{session_id}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | **Request Example:** ```http GET /api/v1/shop/cart/session-abc-123 Referer: http://localhost:8000/vendors/wizamart/shop/cart ``` **Response (200 OK):** ```json { "vendor_id": 1, "session_id": "session-abc-123", "items": [ { "product_id": 1, "quantity": 2, "price": 29.99, "subtotal": 59.98, "product": { "id": 1, "product_id": "PROD-001", "title": "Sample Product", "image_link": "https://example.com/image.jpg" } } ], "subtotal": 59.98, "total": 59.98 } ``` --- ### Add to Cart Add a product to the cart. **Endpoint:** `POST /api/v1/shop/cart/{session_id}/items` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `session_id` | string | Unique session identifier | **Request Body:** ```json { "product_id": 1, "quantity": 2 } ``` **Response (201 Created):** ```json { "message": "Item added to cart", "cart": { "vendor_id": 1, "session_id": "session-abc-123", "items": [...], "subtotal": 59.98, "total": 59.98 } } ``` **Error Responses:** - `404 Not Found` - Product not found or not available - `400 Bad Request` - Insufficient inventory --- ### Update Cart Item Update quantity of an item in the cart. **Endpoint:** `PUT /api/v1/shop/cart/{session_id}/items/{product_id}` **Request Body:** ```json { "quantity": 3 } ``` **Response (200 OK):** ```json { "message": "Cart item updated", "cart": {...} } ``` --- ### Remove from Cart Remove an item from the cart. **Endpoint:** `DELETE /api/v1/shop/cart/{session_id}/items/{product_id}` **Response (200 OK):** ```json { "message": "Item removed from cart", "cart": {...} } ``` --- ### Clear Cart Remove all items from the cart. **Endpoint:** `DELETE /api/v1/shop/cart/{session_id}` **Response (200 OK):** ```json { "message": "Cart cleared", "cart": { "vendor_id": 1, "session_id": "session-abc-123", "items": [], "subtotal": 0, "total": 0 } } ``` --- ## Orders Order endpoints require customer authentication. ### Place Order Create a new order (authenticated). **Endpoint:** `POST /api/v1/shop/orders` **Authentication:** Required (customer token) **Request Body:** ```json { "session_id": "session-abc-123", "shipping_address": { "street": "123 Main St", "city": "City", "postal_code": "12345", "country": "US" }, "billing_address": { "street": "123 Main St", "city": "City", "postal_code": "12345", "country": "US" }, "payment_method": "stripe", "notes": "Please deliver before 5pm" } ``` **Response (201 Created):** ```json { "id": 123, "order_number": "ORD-2025-001", "vendor_id": 1, "customer_id": 456, "total": 59.98, "status": "pending", "created_at": "2025-11-22T10:00:00Z", "items": [...], "shipping_address": {...}, "billing_address": {...} } ``` --- ### Get Order History Get list of customer's orders (authenticated). **Endpoint:** `GET /api/v1/shop/orders` **Authentication:** Required **Query Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `skip` | integer | 0 | Pagination offset | | `limit` | integer | 20 | Max orders to return | **Response (200 OK):** ```json { "orders": [ { "id": 123, "order_number": "ORD-2025-001", "total": 59.98, "status": "completed", "created_at": "2025-11-22T10:00:00Z" } ], "total": 5, "skip": 0, "limit": 20 } ``` --- ### Get Order Details Get details of a specific order (authenticated). **Endpoint:** `GET /api/v1/shop/orders/{order_id}` **Authentication:** Required **Response (200 OK):** ```json { "id": 123, "order_number": "ORD-2025-001", "vendor_id": 1, "customer_id": 456, "total": 59.98, "status": "completed", "created_at": "2025-11-22T10:00:00Z", "items": [...], "shipping_address": {...}, "billing_address": {...}, "tracking_number": "TRACK-123" } ``` **Error Responses:** - `404 Not Found` - Order not found or doesn't belong to customer --- ## Authentication ### Register Customer Create a new customer account. **Endpoint:** `POST /api/v1/shop/auth/register` **Request Body:** ```json { "email": "customer@example.com", "password": "SecurePass123!", "first_name": "John", "last_name": "Doe", "phone": "+1234567890" } ``` **Response (201 Created):** ```json { "id": 456, "email": "customer@example.com", "first_name": "John", "last_name": "Doe", "phone": "+1234567890", "is_active": true, "vendor_id": 1 } ``` **Error Responses:** - `400 Bad Request` - Email already registered - `422 Unprocessable Entity` - Validation errors --- ### Customer Login Authenticate a customer and receive JWT token. **Endpoint:** `POST /api/v1/shop/auth/login` **Request Body:** ```json { "email_or_username": "customer@example.com", "password": "SecurePass123!" } ``` **Response (200 OK):** ```json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "token_type": "bearer", "expires_in": 86400, "user": { "id": 456, "email": "customer@example.com", "first_name": "John", "last_name": "Doe" } } ``` **Cookie Set:** The endpoint also sets an HTTP-only cookie: ```http Set-Cookie: customer_token=; Path=/shop; HttpOnly; SameSite=Lax; Secure ``` **Error Responses:** - `401 Unauthorized` - Invalid credentials - `404 Not Found` - Customer not found for this vendor --- ### Customer Logout Clear customer session. **Endpoint:** `POST /api/v1/shop/auth/logout` **Response (200 OK):** ```json { "message": "Logged out successfully" } ``` The endpoint clears the `customer_token` cookie. --- ## Content Pages (CMS) ### Get Navigation Links Get CMS pages for header/footer navigation. **Endpoint:** `GET /api/v1/shop/content-pages/navigation` **Response (200 OK):** ```json [ { "slug": "about", "title": "About Us", "show_in_header": true, "show_in_footer": true, "display_order": 1 }, { "slug": "contact", "title": "Contact Us", "show_in_header": true, "show_in_footer": true, "display_order": 2 } ] ``` --- ### Get Content Page Get full content for a specific page. **Endpoint:** `GET /api/v1/shop/content-pages/{slug}` **Path Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | `slug` | string | Page slug (e.g., "about", "faq") | **Response (200 OK):** ```json { "slug": "about", "title": "About Us", "content": "

Welcome to our shop...

", "meta_description": "Learn about our story", "is_published": true, "show_in_header": true, "show_in_footer": true, "display_order": 1 } ``` **Error Responses:** - `404 Not Found` - Page not found --- ## Error Handling All endpoints follow standard HTTP error responses: ### Common Error Response Format ```json { "error_code": "VALIDATION_ERROR", "message": "Request validation failed", "status_code": 422, "details": { "validation_errors": [ { "loc": ["body", "email"], "msg": "Invalid email format", "type": "value_error.email" } ] } } ``` ### HTTP Status Codes | Code | Meaning | When Used | |------|---------|-----------| | 200 | OK | Successful GET/PUT/DELETE | | 201 | Created | Successful POST (resource created) | | 400 | Bad Request | Invalid request data | | 401 | Unauthorized | Authentication required/failed | | 403 | Forbidden | Insufficient permissions | | 404 | Not Found | Resource not found (product, vendor, order) | | 422 | Unprocessable Entity | Validation errors | | 500 | Internal Server Error | Server error | --- ## Rate Limiting All Shop API endpoints are rate limited: - **Public endpoints**: 100 requests/minute per IP - **Authenticated endpoints**: 200 requests/minute per customer - **Search endpoints**: 20 requests/minute per IP Rate limit headers included in responses: ```http X-RateLimit-Limit: 100 X-RateLimit-Remaining: 95 X-RateLimit-Reset: 1700000000 ``` --- ## Migration from Old API **Old Pattern (Deprecated):** ```http GET /api/v1/public/vendors/{vendor_id}/products POST /api/v1/public/vendors/auth/{vendor_id}/customers/login ``` **New Pattern (Current):** ```http GET /api/v1/shop/products POST /api/v1/shop/auth/login ``` **Key Changes:** - ✅ Removed `{vendor_id}` from URLs - ✅ Vendor extracted from Referer header automatically - ✅ Cleaner URLs (~40% shorter) - ✅ Same functionality, better architecture **See:** [API Migration Status](../architecture/API_MIGRATION_STATUS.md) --- ## Examples ### Complete Add to Cart Flow ```javascript // 1. Get session ID (generate or retrieve from localStorage) const sessionId = localStorage.getItem('session_id') || crypto.randomUUID(); localStorage.setItem('session_id', sessionId); // 2. Add product to cart const response = await fetch(`/api/v1/shop/cart/${sessionId}/items`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ product_id: 1, quantity: 2 }) }); const result = await response.json(); console.log('Cart updated:', result.cart); ``` ### Complete Checkout Flow ```javascript // 1. Customer logs in const loginResponse = await fetch('/api/v1/shop/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email_or_username: 'customer@example.com', password: 'password123' }) }); const { access_token } = await loginResponse.json(); localStorage.setItem('customer_token', access_token); // 2. Place order const orderResponse = await fetch('/api/v1/shop/orders', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${access_token}` }, body: JSON.stringify({ session_id: sessionId, shipping_address: {...}, billing_address: {...}, payment_method: 'stripe' }) }); const order = await orderResponse.json(); console.log('Order created:', order.order_number); ``` --- ## Interactive Documentation For live API testing and exploration: - **Swagger UI**: [http://localhost:8000/docs](http://localhost:8000/docs) - **ReDoc**: [http://localhost:8000/redoc](http://localhost:8000/redoc) - **OpenAPI Spec**: [http://localhost:8000/openapi.json](http://localhost:8000/openapi.json) --- **Questions?** See the [API Migration Status](../architecture/API_MIGRATION_STATUS.md) or [Shop Architecture Guide](../frontend/shop/architecture.md).