Web Commerce - Serializers

Location: api/nextango/apps/web_commerce/serializers.py Last Updated: 2026-07-08

Overview

DRF serializers for web commerce API endpoints. Split into:

  • Read-only serializers: Display checkout sessions, carts, and reservations
  • Write-only serializers: Orchestrate checkout flow operations with validation

Read-Only Serializers

CartLineItemSerializer

Source: serializers.py:14-23

Serializer for cart line items stored in Cart's JSONField.

Fields:

FieldTypeDescription
variant_idUUIDProductVariant ID
skustring (read-only)Variant SKU
namestring (read-only)Variant name
product_namestring (read-only)Parent product name
quantityintegerQuantity in cart
unit_pricedecimal (read-only)Price per unit
total_pricedecimal (read-only)quantity × unit_price
image_urlsarray (read-only)Product images

CartSerializer

Source: serializers.py:26-58

Serializer for Cart model with computed fields.

Fields:

FieldTypeDescription
cartIdUUIDCart identifier
statusstringACTIVE, CHECKOUT, COMPLETED, ABANDONED
itemsarrayCartLineItemSerializer data
item_countintegerTotal quantity of all items
subtotalstringFormatted subtotal (e.g., "149.99")
created_atdatetimeCart creation timestamp
updated_atdatetimeLast modification timestamp

Computed Methods:

def get_item_count(self, obj):
    """Total number of items in cart"""
    return sum(item.get('quantity', 0) for item in obj.line_items)

def get_subtotal(self, obj):
    """Calculate cart subtotal"""
    total = sum(float(item.get('total_price', 0)) for item in obj.line_items)
    return f"{total:.2f}"

CheckoutReservationContextSerializer

Source: serializers.py:61-78

Serializer for inventory reservation audit records.

Fields:

FieldTypeDescription
inventory_levelUUIDInventoryLevel FK
skustringDerived from inventory_level.variant_sku
stock_reservedintegerReserved quantity
statusstringactive, released, committed
reserved_atdatetimeReservation timestamp
released_atdatetimeRelease timestamp (if released)
committed_atdatetimeCommit timestamp (if committed)

CheckoutSessionSerializer

Source: serializers.py:81-113

Serializer for CheckoutSession with nested cart and reservations.

Fields:

FieldTypeDescription
session_idUUIDCheckout session ID
statusstringSession status
fulfillment_methodstringship, pickup, digital
is_guestbooleanGuest checkout flag
guest_emailstringGuest email (if applicable)
cartobjectNested CartSerializer
transaction_idUUIDResulting transaction (if completed)
is_expiredbooleanWhether session has expired
expires_atdatetimeExpiration timestamp
time_remainingintegerSeconds until expiration
reservationsarrayNested reservation contexts
initiated_atdatetimeCheckout start timestamp
completed_atdatetimeCompletion timestamp

Computed Methods:

def get_time_remaining(self, obj):
    """Calculate time remaining in seconds"""
    if obj.is_expired():
        return 0
    remaining = (obj.expires_at - timezone.now()).total_seconds()
    return max(0, int(remaining))

Write-Only Serializers

CreateGuestSessionSerializer

Source: serializers.py:118-121

Create guest web session for anonymous checkout.

Fields:

FieldTypeRequiredDescription
emailemailNoOptional guest email
store_idUUIDNoStore context

AddToCartSerializer

Source: serializers.py:124-166

Add item to cart with inventory validation.

Fields:

FieldTypeRequiredDescription
skustringYesProduct variant SKU
quantityintegerYesQuantity to add (min: 1)
store_idUUIDYesStore for inventory check

Validation:

  1. SKU Exists: ProductVariant must exist
  2. Inventory Available: InventoryLevel must exist for store with sufficient stock
def validate(self, data):
    inventory = InventoryLevel.objects.get(
        variant=variant,
        store_id=data['store_id'],
        is_active=True
    )
    available = inventory.stock_on_hand - inventory.stock_reserved
    if available < data['quantity']:
        raise serializers.ValidationError(
            f"Insufficient inventory for '{sku}'. Available: {available}, Requested: {data['quantity']}"
        )

UpdateCartItemSerializer

Source: serializers.py:169-172

Update cart item quantity.

Fields:

FieldTypeRequiredDescription
skustringYesProduct variant SKU
quantityintegerYesNew quantity (0 = remove)

RemoveFromCartSerializer

Source: serializers.py:175-177

Remove item from cart.

Fields:

FieldTypeRequiredDescription
skustringYesProduct variant SKU

InitiateCheckoutSerializer

Source: serializers.py:180-200

Initiate checkout session with cart validation.

Fields:

FieldTypeRequiredDescription
cart_idUUIDYesCart to checkout
store_idUUIDYesStore for inventory
fulfillment_methodstringYesship, pickup, digital
pickup_scheduled_timedatetimeNoScheduled pickup time

Validation:

def validate_cart_id(self, value):
    cart = Cart.objects.get(cartId=value)
    if cart.status != Cart.Status.ACTIVE:
        raise ValidationError(f"Cart is {cart.status}, cannot checkout")
    if not cart.line_items:
        raise ValidationError("Cart is empty")
    return value

CompleteCheckoutSerializer

Source: serializers.py:203-258

Complete checkout with payment validation.

Fields:

FieldTypeRequiredDescription
checkout_session_idUUIDYesCheckout session to complete
payment_method_idUUIDConditionalRequired for immediate payment
payment_dataobjectNoAdditional payment data
stripe_payment_method_tokenstringNoStripe PaymentMethod token (pm_...) from CardElement; distinct from payment_method_id
checkout_form_dataobjectNoCustomer form data

Validation:

  1. Session Valid: Must exist and not be expired
  2. Not Completed: Cannot complete twice
  3. Payment Required: If payment_timing='immediate', payment_method_id required
def validate(self, data):
    checkout_form_data = data.get('checkout_form_data') or {}
    payment_timing = checkout_form_data.get('payment_timing') or checkout.payment_timing

    if payment_timing == 'immediate' and not data.get('payment_method_id'):
        raise ValidationError({
            'payment_method_id': 'Payment method is required for immediate payment'
        })

CancelCheckoutSerializer

Source: serializers.py:261-263

Cancel checkout session.

Fields:

FieldTypeRequiredDescription
checkout_session_idUUIDYesSession to cancel

Usage Examples

Reading Cart

cart = Cart.objects.get(cartId=cart_id)
serializer = CartSerializer(cart)
return Response(serializer.data)

# Response:
{
    "cartId": "abc-123",
    "status": "ACTIVE",
    "items": [
        {
            "variant_id": "var-456",
            "sku": "SKU-001",
            "name": "Size M / Blue",
            "quantity": 2,
            "unit_price": "49.99",
            "total_price": "99.98"
        }
    ],
    "item_count": 2,
    "subtotal": "99.98"
}

Adding to Cart

serializer = AddToCartSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

# Validation ensures inventory is available
# before CartService.add_item() is called

Completing Checkout

serializer = CompleteCheckoutSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

# Validation ensures:
# - Session exists and not expired
# - Payment method provided if immediate payment
# - Session not already completed

  • Views - API endpoints using these serializers
  • Models - Cart and CheckoutSession models
  • Services - Business logic called after validation

Was this page helpful?