Web Commerce Domain - Views Documentation
Documentation of Web Commerce domain API endpoints.
Source: api/nextango/apps/web_commerce/views.py
Last Updated: 2026-07-08
Overview
Web Commerce views are plain APIView classes, not a router-registered ViewSet. The app's URLconf mounts at web-commerce/ on the root URLconf (nextango/urls.py:167), so every endpoint below is an absolute path under /web-commerce/. None of the app's views are registered on the root DefaultRouter.
The storefront's Next.js API proxy (storefront/web/app/api/[[...path]]/route.ts) maps its own /api/foo/bar paths onto Django's http://api:8000/foo/bar; that /api/ prefix belongs to the storefront proxy layer, not to Django's URLconf, and is not part of the endpoint path documented here. See the domain README for the proxy.
Every view sets authentication_classes = [] and permission_classes = [AllowAny] (consumer/public posture). Authorization is enforced separately per endpoint, by cart or checkout ownership, not by DRF permission classes. Views delegate business logic to CartService, CheckoutService, and SessionService.
Ownership-based authorization
Cart and checkout endpoints do not trust session['cart_id'] by byte-equality. Authorization is ownership-based:
- Identity resolution (
SessionService.resolve_authorized_web_session(),services/session_service.py:82-108): tries theX-Cart-Session-Tokenheader first (guest-only, SHA-256 hashed againstWebSession.cart_token_hash), falling back to the cookie-resolvedWebSessionfor the current Django session key. The token is primary because in cookie-hostile contexts (embedded webviews, ITP) the session cookie round-trips intermittently while a client-held token does not. - Cart ownership (
_authorize_cart_access(),views.py:59-91): a cart request is authorized only when the cart isACTIVEand itsweb_sessionFK matches the resolved web session. All 5 cart endpoints (CartView,CartAddItemView,CartUpdateItemView,CartRemoveItemView,CartClearView) use this gate. - Checkout ownership (
CheckoutService._owned_checkout(),services/checkout_service.py:87-106): a checkout session is loaded only when itsweb_sessionFK matches the resolved session; a mismatch or missing session raisesCheckoutSession.DoesNotExist, which every caller maps to one generic 403/404 response so existence and ownership are never disclosed. - CheckoutCompleteView additionally enforces ownership before the idempotency lookup (
authorize_before_idempotency(),views.py:927-950), so an unowned request cannot even reach the cached-response short-circuit.
Session Management
GuestSessionCreateView
POST /web-commerce/session/create/
Creates a guest WebSession and an empty Cart for an unauthenticated storefront visitor. No auth required; CSRF-exempt (called before any session exists).
Request body:
{
"email": "[email protected]",
"store_id": "uuid-here"
}
Response (201):
{
"success": true,
"cart_id": "uuid",
"cart_token": "opaque-token-or-null",
"expires_at": "2026-07-08T20:00:00Z"
}
cart_token is a raw, one-time-shown cart-session token minted at session establishment (only for guest sessions; null for a reused non-guest session). Only its SHA-256 hash is persisted server-side, on WebSession.cart_token_hash.
Errors: 400 on invalid body or an unknown store_id; 500 on unexpected failure.
ChangeStoreView
POST /web-commerce/session/store/
Changes the active store for the current session. Because inventory is store-specific, the existing cart is abandoned and a new empty cart is created for the new store. No auth required; CSRF-exempt.
Request body: { "store_id": "uuid" }
Response (200):
{
"success": true,
"cart_id": "uuid",
"cart_token": "opaque-token-or-null",
"message": "Store changed successfully. Your cart has been cleared."
}
Errors: 400 if store_id is missing, malformed, or does not resolve to a Store.
SessionStatusView
GET /web-commerce/auth/session/
Validates the session and returns verified cart/store/user context. Called on every storefront page load. No auth required; CSRF-exempt.
Identity resolves through SessionService.resolve_authorized_web_session(). If no valid WebSession exists, the storefront session keys are cleared and the response reports authenticated: false. Otherwise the view resolves the session's single ACTIVE cart (SessionService.enforce_single_active_cart() abandons any other ACTIVE cart on the same web session), realigns session['cart_id'] to the cart actually returned, and includes store_settings (pickup_enabled, tax_rate) when a store is set.
If the request carries no X-Cart-Session-Token header, the view mints one for a guest session and returns it in cart_token; a non-guest session never receives one.
Response (unauthenticated / no session):
{
"authenticated": false,
"cart_id": null,
"store_id": null,
"web_session_id": null,
"store_settings": null
}
Response (active session):
{
"authenticated": true,
"web_session_id": "uuid",
"cart_id": "uuid",
"store_id": "uuid",
"store_settings": { "pickup_enabled": true, "tax_rate": 0.0725 },
"cart_token": "opaque-token",
"user": {
"name": "John Doe",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"phone": "",
"customer_id": "uuid",
"loyalty_points": 0
}
}
user is present only when web_session.user is set. Any unexpected exception is swallowed and the view returns the unauthenticated shape rather than a 500, so a storefront page load never hard-fails on session resolution.
Cart Operations
All cart endpoints require _authorize_cart_access() to pass; a failed check returns 403 { "success": false, "error": "Cart does not belong to this session" } before any cart lookup happens.
CartView
GET /web-commerce/cart/{cart_id}/
Returns the serialized cart (CartSerializer) enriched with a promotion preview computed directly against the cart's JSON line items (PromotionService.preview_from_cart_items()), not a SaleTransaction. Each line item is annotated with promotion_discount, effective_price, and applied_promotions; the cart payload also carries active_promotions, promotion_savings, and free_shipping_active. Promotion enrichment failures are caught and logged; the cart itself still returns.
Response (200): CartSerializer data plus the promotion annotations above.
Errors: 403 not owned; 404 cart not found.
CartAddItemView
POST /web-commerce/cart/{cart_id}/add/
Adds a product to the cart by SKU via CartService.add_to_cart().
Request body: { "sku": "ABC123", "quantity": 2, "store_id": "uuid" }
Response (200): { "success": true, "message": "...", "cart": { CartSerializer data } }
Errors: 400 on serializer validation failure or a CartService rejection (SKU not found, insufficient inventory); 403 not owned; 500 unexpected.
CartUpdateItemView
PATCH /web-commerce/cart/{cart_id}/update/
Updates a line item's quantity via CartService.update_cart_item(). The SKU is resolved to a ProductVariant id before calling the service; store_id comes from request.session['store_id'], not the request body.
Request body: { "sku": "ABC123", "quantity": 3 }
Response (200): updated cart. Errors: 400 serializer/service failure; 403 not owned; 404 SKU not found; 500 unexpected.
CartRemoveItemView
POST /web-commerce/cart/{cart_id}/remove/
Removes a line item by SKU via CartService.remove_from_cart().
Request body: { "sku": "ABC123" }
Response (200): updated cart without the removed item. Errors: 400 serializer/service failure; 403 not owned; 500 unexpected.
CartClearView
POST /web-commerce/cart/{cart_id}/clear/
Clears all line items via CartService.clear_cart() without deleting the cart record. Also called internally by ChangeStoreView's flow.
Response (200): { "success": true, "message": "Cart cleared" }
Errors: 400 service failure; 403 not owned; 500 unexpected.
Checkout Flow
CheckoutInitiateView
POST /web-commerce/checkout/initiate/
Begins checkout: creates a CheckoutSession in checkout_initiated status with a 30-minute expiry. Identity comes from the resolved session, never a body-supplied web_session_id; the cart must be ACTIVE and owned by that session before the serializer even runs its DB lookup (_cart_owned_and_active(), views.py:43-56). Inventory is not reserved at this step; it is reserved synchronously inside CheckoutCompleteView.
Request body:
{
"cart_id": "uuid",
"store_id": "uuid",
"fulfillment_method": "ship",
"pickup_scheduled_time": "ISO datetime"
}
fulfillment_method is one of ship, pickup, digital. pickup_scheduled_time is required for pickup.
Response (201): { "success": true, "checkout": { CheckoutSessionSerializer data } }
Errors: 403 (generic, no existence disclosure) when the resolved session does not own the cart; 400 serializer/service validation failure; 500 unexpected.
CheckoutCompleteView
POST /web-commerce/checkout/complete/
Completes checkout: processes payment, creates the SaleTransaction, reserves and commits inventory, and creates Fulfillment/Shipment records. Delegates entirely to CheckoutService.complete_checkout() (see Services for the full orchestration sequence).
This view mixes in IdempotencyMixin and enforces checkout ownership in authorize_before_idempotency() before the idempotency-key lookup runs, so an unowned replay cannot receive another session's cached response.
Request body:
{
"checkout_session_id": "uuid",
"payment_method_id": "uuid",
"payment_data": {},
"checkout_form_data": {
"email": "[email protected]",
"shipping_address": { "firstName": "John", "lastName": "Doe", "company": "",
"address": "123 Main St", "apartment": "Apt 4B", "city": "Portland",
"region": "OR", "postalCode": "97201", "country": "USA", "phone": "503-555-1234" },
"billing_address": {},
"billing_same_as_shipping": true,
"delivery_method": { "id": "standard", "title": "Standard Shipping",
"turnaround": "5-7 business days", "price": "$10.00", "priceValue": 10.00 },
"payment_details": { "nameOnCard": "John Doe", "cardNumber": "4111111111111111", "cardType": "visa" },
"shipping_cost": 10.00,
"saved_address_id": "uuid (optional)",
"save_to_profile": false
}
}
Response (200, immediate payment success):
{
"success": true,
"transaction_id": "TXN-20260708-A1B2C3D4",
"checkout_session_id": "uuid",
"total_amount": "129.99",
"receipt_token": "token-or-null"
}
Response (200, 3DS required):
{
"success": true,
"requires_action": true,
"transaction_id": "TXN-20260708-A1B2C3D4",
"payment_intent_id": "pi_...",
"client_secret": "pi_..._secret_..."
}
Errors: 400 serializer validation, payment failure ({ "success": false, "error": "Payment failed", "details": "..." }), or insufficient inventory re-checked at completion time; 403 not authorized for the checkout; 500 unexpected.
CheckoutCancelView
POST /web-commerce/checkout/cancel/
Cancels an active checkout session and releases any reserved inventory via CheckoutService.cancel_checkout(). Identity is resolved but not required to be present; the service fails closed on a non-owning or missing session with one generic response.
Request body: { "checkout_session_id": "uuid" }
Response (200): { "success": true, "message": "Checkout cancelled, inventory released" }
Errors: 400 serializer/service failure (includes "checkout session not found" and "cannot cancel completed checkout"); 500 unexpected.
CheckoutStatusView
GET /web-commerce/checkout/{checkout_session_id}/status/
Polls the current status of a checkout session via CheckoutService.get_checkout_status(), fail-closed to the owning session.
Response (200):
{
"success": true,
"checkout_session": {
"session_id": "uuid",
"status": "checkout_initiated",
"fulfillment_method": "pickup",
"expires_at": "2026-07-08T20:00:00Z",
"is_expired": false,
"transaction_id": null,
"pickup_scheduled_time": "2026-07-09T18:00:00Z",
"pickup_display_time": "Thursday 06:00 PM",
"store_pay_at_store_enabled": true
}
}
pickup_display_time is rendered in the store's local timezone (store.timezone); it falls back to a UTC-formatted string if the timezone conversion fails.
Errors: 404 checkout session not found or not owned; 500 unexpected.
Payment Methods
OnlinePaymentMethodsView
GET /web-commerce/payment-methods/?store_id=<id>
Lists active, online-available payment methods for a store, restricted to the storefront-safe field subset (no employee-only fields). This list is UX only; the authoritative enforcement is PaymentMethodService.validate_method_for_transaction() at checkout completion.
Response (200):
{
"success": true,
"payment_methods": [
{ "id": "uuid", "name": "Credit Card", "method_type": "card",
"is_active": true, "is_online_available": true,
"minimum_amount": "0.00", "maximum_amount": null,
"icon_url": "...", "description": "...", "display_order": 1 }
]
}
Errors: 400 missing store_id; 500 unexpected.
Guest Order Tracking
GuestOrderLookupView
POST /web-commerce/guest-order/
Lets a guest customer look up an order using the guest_access_token returned at checkout completion, without authentication. Throttled with AnonRateThrottle to resist token enumeration.
Request body: { "access_token": "32-character-alphanumeric-token" }
Response (200):
{
"success": true,
"transaction": { "transactionId": "TXN-20260708-A1B2C3D4", "status": "completed", "totalAmount": "129.99", "line_items": [] },
"fulfillment": { "id": "uuid", "status": "shipped", "shipping_city": "Portland", "shipping_region": "OR",
"shipments": [ { "carrier_name": "UPS", "tracking_number": "1Z999AA10123456784", "status": "in_transit", "items": [] } ] }
}
Errors: 400 missing access_token; 404 order not found (a generic message shared by "invalid" and "expired" to prevent enumeration); 429 rate limit exceeded.
Saved Addresses
SavedAddressesView
GET /web-commerce/saved-addresses/
Returns the authenticated user's saved shipping addresses, ordered default-first then most-recently-added. Identity uses SessionService.resolve_web_session() (cookie-only, not the token-primary resolver), so it never leaks another user's saved-address PII through a stale or foreign token. Returns an empty list (not 401) for guests, since checkout must still work without an account.
Response (authenticated):
{
"success": true,
"addresses": [
{ "id": "uuid", "label": "Home", "is_default": true,
"first_name": "John", "last_name": "Doe", "company": "",
"address": "123 Main St", "apartment": "Apt 4B", "city": "Portland",
"region": "OR", "postal_code": "97201", "country": "USA", "phone": "503-555-1234" }
]
}
Response (guest / unauthenticated): { "success": true, "addresses": [] }
Related Documentation
- Models:
CheckoutSession,CheckoutReservationContext - Services:
CartService,CheckoutService,ReservationService,SessionService - Serializers: request/response serialization
- Transactions Views:
FulfillmentViewSet,ShipmentViewSet