Files
orion/docs/api/shop-api-reference.md
Samir Boulahtit 3c7af0ccdf refactor: standardize markdown file naming to kebab-case convention
Renamed all documentation files to follow kebab-case naming standard:
- UPPERCASE files → lowercase (e.g., RBAC.md → rbac.md)
- snake_case files → kebab-case (e.g., icons_guide.md → icons-guide.md)
- SCREAMING_SNAKE_CASE → kebab-case (e.g., DATABASE_SETUP_GUIDE.md → database-setup-guide.md)

Files renamed (15 total):
API Documentation:
  - api/RBAC.md → api/rbac.md

Architecture:
  - architecture/API_CONSOLIDATION_PROPOSAL.md → api-consolidation-proposal.md
  - architecture/API_MIGRATION_STATUS.md → api-migration-status.md

Development:
  - development/AUTH_DEPENDENCIES_GUIDE.md → auth-dependencies-guide.md
  - development/CUSTOMER_AUTHENTICATION_IMPLEMENTATION.md → customer-authentication-implementation.md
  - development/CUSTOMER_AUTH_SUMMARY.md → customer-auth-summary.md
  - development/icons_guide.md → icons-guide.md

Database Seeder:
  - database-seeder/DATABASE_INIT_GUIDE.md → database-init-guide.md
  - database-seeder/DATABASE_QUICK_REFERENCE_GUIDE.md → database-quick-reference-guide.md
  - database-seeder/DATABASE_SEEDER_DOCUMENTATION.md → database-seeder-documentation.md
  - database-seeder/MAKEFILE_DATABASE_SEEDER.md → makefile-database-seeder.md

Error Rendering:
  - error-rendering/ERROR_RENDERING_DEVELOPER_DOCUMENTATION.md → error-rendering-developer-documentation.md
  - error-rendering/HTML_ERROR_RENDERING_FLOW_DIAGRAM.md → html-error-rendering-flow-diagram.md

Getting Started:
  - getting-started/DATABASE_QUICK_REFERENCE.md → database-quick-reference.md
  - getting-started/DATABASE_SETUP_GUIDE.md → database-setup-guide.md

Updates:
- Updated all references in mkdocs.yml
- Updated all cross-references in markdown files
- Verified mkdocs builds without warnings or errors

Standard: Use kebab-case (lowercase-with-hyphens) for all markdown files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-28 07:58:33 +01:00

19 KiB
Raw Blame History

Shop API Reference

Last Updated: 2025-11-23 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 = <Vendor: wizamart>
  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:

Authorization: Bearer <customer_token>

Or use the HTTP-only cookie (automatically sent by browser):

Cookie: customer_token=<jwt_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:

GET /api/v1/shop/products?skip=0&limit=20&is_featured=true
Referer: http://localhost:8000/vendors/wizamart/shop/products

Response (200 OK):

{
  "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:

GET /api/v1/shop/products/1
Referer: http://localhost:8000/vendors/wizamart/shop/products

Response (200 OK):

{
  "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:

GET /api/v1/shop/cart/session-abc-123
Referer: http://localhost:8000/vendors/wizamart/shop/cart

Response (200 OK):

{
  "vendor_id": 1,
  "session_id": "session-abc-123",
  "items": [
    {
      "product_id": 1,
      "product_name": "Sample Product",
      "quantity": 2,
      "price": 29.99,
      "line_total": 59.98,
      "image_url": "https://example.com/image.jpg"
    }
  ],
  "subtotal": 59.98,
  "total": 59.98,
  "item_count": 1
}

Response Schema: CartResponse

Field Type Description
vendor_id integer Vendor ID
session_id string Shopping session ID
items array List of cart items (see CartItemResponse below)
subtotal float Subtotal of all items
total float Total amount (currently same as subtotal)
item_count integer Total number of unique items in cart

CartItemResponse Schema:

Field Type Description
product_id integer Product ID
product_name string Product name
quantity integer Quantity in cart
price float Price per unit (captured when added to cart)
line_total float Total for this line (price × quantity)
image_url string | null Product image URL

Add to Cart

Add a product to the cart. If the product already exists in the cart, the quantity will be incremented.

Endpoint: POST /api/v1/shop/cart/{session_id}/items

Path Parameters:

Parameter Type Description
session_id string Unique session identifier

Request Body Schema: AddToCartRequest

Field Type Required Description
product_id integer Yes Product ID to add (must be > 0)
quantity integer No Quantity to add (default: 1, must be >= 1)

Request Example:

{
  "product_id": 1,
  "quantity": 2
}

Response (200 OK): CartOperationResponse

{
  "message": "Product added to cart",
  "product_id": 1,
  "quantity": 2
}

Response Schema:

Field Type Description
message string Operation result message
product_id integer Product ID affected
quantity integer New quantity in cart

Error Responses:

  • 404 Not Found - Product not found or vendor not found

    {
      "error_code": "PRODUCT_NOT_FOUND",
      "message": "Product with ID '123' not found in vendor 1 catalog",
      "status_code": 404,
      "details": {
        "product_id": 123,
        "vendor_id": 1
      }
    }
    
  • 400 Bad Request - Insufficient inventory

    {
      "error_code": "INSUFFICIENT_INVENTORY_FOR_CART",
      "message": "Insufficient inventory for product 'Sample Product'. Requested: 10, Available: 5",
      "status_code": 400,
      "details": {
        "product_id": 1,
        "product_name": "Sample Product",
        "requested_quantity": 10,
        "available_quantity": 5
      }
    }
    
  • 422 Unprocessable Entity - Validation error

    {
      "error_code": "INVALID_CART_QUANTITY",
      "message": "Quantity must be at least 1",
      "status_code": 422,
      "details": {
        "field": "quantity",
        "quantity": 0,
        "min_quantity": 1
      }
    }
    

Implementation Notes:

  • Cart items are stored in the database with persistent storage
  • Price is captured at time of adding to cart (uses sale_price if available, otherwise price)
  • If product already exists in cart, quantity is incremented (duplicate prevention)
  • Inventory is validated before adding items

Update Cart Item

Update the quantity of an existing item in the cart.

Endpoint: PUT /api/v1/shop/cart/{session_id}/items/{product_id}

Path Parameters:

Parameter Type Description
session_id string Unique session identifier
product_id integer Product ID to update (must be > 0)

Request Body Schema: UpdateCartItemRequest

Field Type Required Description
quantity integer Yes New quantity (must be >= 1)

Request Example:

{
  "quantity": 3
}

Response (200 OK): CartOperationResponse

{
  "message": "Cart updated",
  "product_id": 1,
  "quantity": 3
}

Error Responses:

  • 404 Not Found - Cart item not found

    {
      "error_code": "CART_ITEM_NOT_FOUND",
      "message": "Product 123 not found in cart",
      "status_code": 404,
      "details": {
        "product_id": 123,
        "session_id": "session-abc-123"
      }
    }
    
  • 400 Bad Request - Insufficient inventory for new quantity

  • 422 Unprocessable Entity - Invalid quantity (less than 1)


Remove from Cart

Remove a specific item from the cart.

Endpoint: DELETE /api/v1/shop/cart/{session_id}/items/{product_id}

Path Parameters:

Parameter Type Description
session_id string Unique session identifier
product_id integer Product ID to remove (must be > 0)

Response (200 OK): CartOperationResponse

{
  "message": "Item removed from cart",
  "product_id": 1
}

Error Responses:

  • 404 Not Found - Cart item not found (product not in cart)

Clear Cart

Remove all items from the cart.

Endpoint: DELETE /api/v1/shop/cart/{session_id}

Path Parameters:

Parameter Type Description
session_id string Unique session identifier

Response (200 OK): ClearCartResponse

{
  "message": "Cart cleared",
  "items_removed": 3
}

Response Schema:

Field Type Description
message string Operation result message
items_removed integer Number of items that were removed from cart

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:

{
  "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):

{
  "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):

{
  "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):

{
  "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:

{
  "email": "customer@example.com",
  "password": "SecurePass123!",
  "first_name": "John",
  "last_name": "Doe",
  "phone": "+1234567890"
}

Response (201 Created):

{
  "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:

{
  "email_or_username": "customer@example.com",
  "password": "SecurePass123!"
}

Response (200 OK):

{
  "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:

Set-Cookie: customer_token=<jwt>; 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):

{
  "message": "Logged out successfully"
}

The endpoint clears the customer_token cookie.


Content Pages (CMS)

Get CMS pages for header/footer navigation.

Endpoint: GET /api/v1/shop/content-pages/navigation

Response (200 OK):

[
  {
    "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):

{
  "slug": "about",
  "title": "About Us",
  "content": "<p>Welcome to our shop...</p>",
  "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

{
  "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:

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1700000000

Migration from Old API

Old Pattern (Deprecated):

GET /api/v1/public/vendors/{vendor_id}/products
POST /api/v1/public/vendors/auth/{vendor_id}/customers/login

New Pattern (Current):

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


Examples

Complete Add to Cart Flow

// 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

// 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:


Questions? See the API Migration Status or Shop Architecture Guide.