Extending Optimizely Configured Commerce: Passing Dynamic Cart Messages from Backend C# to Spire React

  • Twitter
  • LinkedIn

In Optimizely Configured Commerce, communicating complex backend business logic, such as customer credit holds, restricted partial shipments, or dynamic fulfillment notes, to the frontend user interface requires a clean, decoupled pattern.

In B2B e-commerce environments, dynamic business rules often govern how orders are processed. For example, a customer account might be restricted from receiving partial shipments, requiring all items in an order to ship together. Informing the customer directly on the Cart Page before they reach the checkout funnel helps reduce operational confusion before checkout.

In this technical deep dive, I explore an end-to-end implementation showing how to inject dynamic messages into the cart payload on the backend using C# pipeline handlers and render them in the React/Spire frontend.

Architectural Overview

The customization follows standard Optimizely Configured Commerce extension patterns across both layers:

[ Backend (.NET / C#) ]

1. Intercept 'GetCart' pipeline execution.

2. Evaluate Customer / BillTo custom properties.

3. Add prefixed strings to 'GetCartResult.Messages'.

                         |

                         v  (REST API Payload)

[ Frontend (React / Spire Framework) ]

1. Parse 'cart.messages' array in the CartLines widget.

2. Filter and strip message string prefixes.

3. Render conditionally within 'CartLinesHeader'.

Here is the step-by-step execution flow:

  1. GetCart Pipeline Request: Triggers the pipeline execution.
  2. C# Handler Execution: Evaluates logic and appends custom property messages.
  3. Cart Serialization & REST API: Delivers the updated JSON payload to the client.
  4. Redux Store & Selector Hydration: Hydrates state and passes props down.
  5. Spire React Rendering: Renders localized messages inside the component UI.

 

1. Backend Customization: Extending the GetCart Pipeline

Optimizely Configured Commerce uses a handler pipeline pattern for core service operations. To append business messages to the cart response, I created a custom handler class targeting the GetCart service chain.

Key Architectural Concepts (Backend)

  • Handler Order (Order = 1970): In Optimizely pipelines, handlers execute sequentially based on integer ordering. Running at 1970 is intended to run after the standard cart assembly before inspecting customer properties and attaching messages.
  • Message Localization (MessageProvider): Utilizing MessageProvider.Current.GetMessage avoids hardcoding customer-facing copy. Configured translations and administrative message changes can be reflected in the storefront.
  • Namespaced String Prefixes (MessagePreFixes): By prefixing message strings (e.g., "Default:", "PartialHold:", "PartialHold2:"), the backend establishes a clear contract so the frontend can target and render specific messages in isolated components or locations.

The C# Handler (ValidatePartialHold.cs)

using Insite.Cart.Services.Parameters;

using Insite.Cart.Services.Results;

using Insite.Core.Context;

using Insite.Core.Interfaces.Data;

using Insite.Core.Interfaces.Dependency;

using Insite.Core.Providers;

using Insite.Core.Services;

using Insite.Core.Services.Handlers;

using System.Linq;

namespace Extensions.Base.Modules.Cart.Services.Handlers.GetCartHandler;

[DependencyName(nameof(ValidatePartialHold))]

public sealed class ValidatePartialHold : HandlerBase<GetCartParameter, GetCartResult>

{

    // High execution order ensures standard cart assembly finishes prior to message injection

    public override int Order => 1970;

    public override GetCartResult Execute(

        IUnitOfWork unitOfWork,

        GetCartParameter parameter,

        GetCartResult result

    )

    {

        // 1. Fetch localized site messages dynamically via MessageProvider

        var message1 = MessageProvider.Current.GetMessage(

            "Customer_EstimatedDeliveryMessage",

            "Estimated delivery dates are subject to change depending on quantity selected."

        );

        var message2 = MessageProvider.Current.GetMessage(

            "Customer_PartialHold",

            "Your account does not allow partial shipments, your entire order will be shipped together.",

            "Partial Hold"

        );

        var message3 = MessageProvider.Current.GetMessage(

            "Customer_PartialHold2",

            "Please refer to the checkout page for final estimated delivery date.",

            "Partial Hold on cart page"

        );

        // 2. Evaluate custom properties on the current BillTo customer account

        var partialHold = SiteContext.Current?.BillTo?.CustomProperties

            ?.FirstOrDefault(c => c.Name == "PartialOrderShipments");

        // 3. Inject default localized message with a recognizable prefix tag

        var formatMessage = $"{Consts.Utils.MessagePreFixes.DefaultMessage}{message1}";

        result.Messages.Add(new ResultMessage { Message = formatMessage });

        // 4. Conditional logic for accounts on partial shipment hold

        if (partialHold != null)

        {

            bool.TryParse(partialHold.Value, out var isPartialHold);

            if (isPartialHold)

            {

                result.Messages.Add(new ResultMessage

                {

                    Message = $"{Consts.Utils.MessagePreFixes.PartialHold}{message2}"

                });

                result.Messages.Add(new ResultMessage

                {

                    Message = $"{Consts.Utils.MessagePreFixes.PartialHold2}{message3}"

                });

            }

        }

        // Pass control along to the next pipeline handler in the execution chain

        return NextHandler.Execute(unitOfWork, parameter, result);

    }

}

 

2. Frontend Customization: Parsing and Rendering in Spire React

Once the backend handler adds items to GetCartResult.Messages, they are serialized and delivered to the frontend as cart.messages (an array of strings).

I customized the CartLines React widget (@insite/client-framework extension) to process and display these messages conditionally.

Extracting and Processing Messages

Within the CartLines functional React component, helper logic filters messages by their expected prefix and strips out the metadata key before rendering:

// Helper function to extract and sanitize dynamic backend messages

const getCartMessage = (cartMessages: string[] | null, messagePrefix: string) => {

    const messages = cartMessages?.filter(m => m.startsWith(messagePrefix));

    const message = messages && messages.length > 0

        ? messages[0].replace(messagePrefix, "")

        : "";

    return message;

};

const cartMessages: string[] = [];

// Parse 'Default:' prefixed message

const defaultMessage = getCartMessage(cart.messages, "Default:");

if (defaultMessage) {

    cartMessages.push(defaultMessage);

}

// Parse 'PartialHold:' prefixed message

const partialHoldMessage = getCartMessage(cart.messages, "PartialHold:");

if (partialHoldMessage) {

    cartMessages.push(partialHoldMessage);

}

// Page-context check: render secondary hold message only when on the Cart Page

if (isCartPage) {

    const partialHoldMessage2 = getCartMessage(cart.messages, "PartialHold2:");

    if (partialHoldMessage2) {

        cartMessages.push(partialHoldMessage2);

    }

}

Rendering in the UI (CartLinesHeader)

The extracted cartMessages array is passed as a prop into the CartLinesHeader component and mapped over styled Mobius Typography components:

{cartMessages.map((cartMessage, i) => {

    return (

        <Typography

            key={i}

            {...headerStyles.warningText}

            data-test-selector="cartlineHeader_warningCartMessage"

        >

            {cartMessage}

        </Typography>

    );

})}

Key Architectural Concepts (Frontend)

  • Context Awareness (isCartPage): The logic evaluates Redux store state via getCurrentPage(state).type === "CartPage". This prevents secondary messages (PartialHold2:) from rendering when the widget is executed inside alternate contexts, such as checkout preview drawers.
  • Redux Store Integration (mapStateToProps): The state mapper connects directly to getCurrentCartState(state).value, giving the component reactive updates whenever the user updates quantities or adjusts fulfillment details.
  • Spire CMS Compatibility: Extending the widgetModule definition allows CMS editors to retain full field configuration control (e.g., toggling line notes or custom CSS linting) while incorporating custom messaging functionality.

 

Conclusion

By combining a custom C# GetCart pipeline handler with a tailored Spire React widget, this pattern provides an extensible blueprint for passing dynamic backend alerts directly to the storefront. Using prefixed message contracts keeps frontend parsing simple and ensures business logic remains where it belongs, on the backend.