Key Components:

  1. API Endpoints:
    • POST /cart/add: Adds items to a cart.
    • POST /cart/checkout/initiate: Initiates the checkout process for a cart.
    • POST /checkout/submit : Submits lead to OMS service.
  2. Data Models:
    • AddCartItemRequest: The incoming request model for adding items to the cart.
    • CartItem: Represents an item in the cart.
    • Cart: Represents the entire cart with items and metadata.
    • CartStatus: Enum representing the status of the cart (e.g., ACTIVE, EXPIRED, CHECKOUT_INITIATED).
  3. Services and Processes:
    • Validation: Ensures that incoming requests meet certain criteria.
    • Cart Persistence: Saving and updating cart information in MongoDB.
    • Configurator Service: Fetches vehicle details and pricing summaries.
    • Checkout Lock Mechanism: Uses Redis to lock the cart during checkout initiation to prevent concurrent modifications.
    • Versioning: Manages cart versions to handle deletions, only the customer with most recent cart version will be able to delete the configs.
    • (NOT Implemented) Versioning and Concurrency Control: Manages cart versions to handle concurrent updates, especially when carts are shared via URLs.
  4. Repositories:
    • CartMongoRepository: Interface to interact with MongoDB for cart data.
    • CheckoutRepository: Stores checkout-related data like cartId, requestId, and uuid.

Data Flow Steps

Adding Items to Cart (POST /cart/add):

  • Request Validation:
    • Validate the incoming AddCartItemRequest.
    • Check for null or invalid values (e.g., vehicle quantity, configuration details).
  • Transforming Request:
    • Convert AddCartItemRequest into CartItem for persistence.
    • Perform additional validation on CartItem.
  • Fetching Vehicle Details:
    • Since the frontend doesn’t provide vehicle details and pricing summary, make a call to the Configurator Service to fetch these details based on the vehicle’s configuration.
  • Determine Cart Creation or Update:
    • If cartId is null in the request:
      • Attempt to retrieve an existing cart using the uuid.
      • If a cart exists, check its status:
        • If status is CHECKOUT_INITIATED, verify if the checkout has expired using validateCheckoutTTLExpiredStatus() and revert to ACTIVE if necessary.
        • If the cart is not active or cannot be updated, throw an exception.
      • If no existing cart is found or cannot be used, create a new cart:
        • Generate a new cartId (8 characters long).
        • Set cart status to ACTIVE.
        • Assign a version number (random UUID).
        • Set the cart expiry to 180 days.
        • Save the cart in MongoDB.
    • If cartId is provided and valid:
      • Append items to the existing cart after validating its active status.
  • Version Management:
    • Use version numbers to handle deletions:
      • When carts are shared via URLs, multiple users might attempt to update the same cart.
      • If a cart’s version number has changed due to another user’s update, now since the another user has the most recent version only they’ll be able to delete the configs in cart.
    • (NOT IMPLEMENTED) Use version numbers to handle concurrency:
      • When carts are shared via URLs, multiple users might attempt to update the same cart.
      • If a cart’s version number has changed due to another user’s update, prevent conflicting updates by checking version mismatches.

Initiating Checkout (POST /cart/checkout/initiate):

  • Request Validation
    • Validate the incoming cartCheckoutRequest.
    • Extract cartId and uuid.
  • Cart Retrieval and Validation
    • Retrieve the cart from CartMongoRepository using cartId and uuid.
    • Validate the cart’s status and contents.
  • Lock Acquisition using Redis:
    • Purpose: Prevent multiple checkout initiations on the same cart concurrently.
    • Process:
      • Obtain a Redis template from the cacheFactory.
      • Check if a lock key exists for the cart:
        • If the key exists (non-negative expiry time), the cart is already locked; throw a CartCacheException.
        • If not, set a lock key with an expiry time (e.g., 10 minutes).
      • The lock ensures that the checkout process has exclusive access to the cart for a limited time.
  • Updating Cart Status and Checkout Response:
    • Change the cart status to CHECKOUT_INITIATED.
    • Prepare checkoutResponse with:
      • refreshWindowSize: e.g., 2 minutes.
      • remainingTime: e.g., 10 minutes.
      • requestId: unique identifier for the checkout session.
    • Save checkout details in CheckoutRepository for later validation and cleanup.
    • Return Response:
      • Send the checkoutResponse back to the client, indicating that the checkout process has been initiated successfully.

Checkout Submit (POST /checkout/submit)

Submits all the details to OMS service which returns the orderId.

Sequence Diagrams

ADD API
sequenceDiagram
    participant Client
    participant CartService
    participant ConfiguratorService
    participant MongoDB

    Client ->> CartService: POST /cart/add (AddCartItemRequest)
    Note over CartService: Validate AddCartItemRequest
    CartService ->> CartService: Transform to CartItem
    CartService ->> CartService: Validate CartItem
    CartService ->> ConfiguratorService: Fetch vehicle details & pricing
    ConfiguratorService -->> CartService: VehicleDetails, PriceSummary

    alt cartId is null
        CartService ->> MongoDB: Retrieve cart by uuid
        alt Cart exists
            CartService ->> CartService: checkCartActiveStatus()
            alt Cart status is CHECKOUT_INITIATED
                CartService ->> CartService: validateCheckoutTTLExpiredStatus()
                alt Checkout expired
                    CartService ->> CartService: Revert cart status to ACTIVE
                else Checkout active
                    CartService ->> Client: Throw Exception (Cart Locked)
                    Note over CartService: Operation aborted
                end
            else Cart is ACTIVE
                CartService ->> MongoDB: Append items to cart
                CartService ->> MongoDB: Update cart in database
            end
        else Cart does not exist
            CartService ->> CartService: generateCartId()
            CartService ->> CartService: Set cart status to ACTIVE
            CartService ->> CartService: Set version number
            CartService ->> CartService: Set cart expiry (180 days)
            CartService ->> MongoDB: Save new cart
        end
    else cartId is provided
        CartService ->> MongoDB: Retrieve cart by cartId
        CartService ->> CartService: checkCartActiveStatus()
        alt Cart is ACTIVE
            CartService ->> MongoDB: Append items to cart
            CartService ->> MongoDB: Update cart in database
        else
            CartService ->> Client: Throw Exception (Cart not active)
            Note over CartService: Operation aborted
        end
    end

    CartService ->> Client: Return response (Cart updated)
Checkout Initiate
sequenceDiagram
    participant Client
    participant CartService
    participant MongoDB
    participant Redis
    participant CheckoutRepository

    Client ->> CartService: POST /cart/checkout/initiate (cartCheckoutRequest)
    Note over CartService: Validate cartCheckoutRequest
    CartService ->> MongoDB: Retrieve cart by cartId and uuid
    CartService ->> CartService: Validate cart

    CartService ->> Redis: Check if lock exists for cartId
    alt Lock exists
        Redis -->> CartService: Lock found
        CartService ->> Client: Throw CartCacheException (Cart already in checkout)
        Note over CartService: Checkout initiation failed
    else Lock does not exist
        Redis -->> CartService: No lock found
        CartService ->> Redis: Set lock for cartId with expiry (10 mins)
        CartService ->> CartService: Update cart status to CHECKOUT_INITIATED
        CartService ->> MongoDB: Save cart with updated status
        CartService ->> CheckoutRepository: Save checkout details (cartId, requestId, uuid)
        CartService ->> Client: Return checkoutResponse (remainingTime, refreshWindowSize, requestId)
    end