How to implement anonymous shopping in an e-commerce website built with Sitecore XM Cloud and Sitecore OrderCloud

An anonymous shopping experience is key to any B2C e-commerce website. There are occasional visitors to the e-commerce website who want to purchase items without creating a user account. A seamless shopping experience with anonymous (guest) shopping can significantly increase revenue and help convert a guest user to create an account.

OrderCloud, a headless e-commerce platform, supports anonymous shopping through anonymous user configuration and creation of a cart and order on behalf of an anonymous user.

In this blog, we will discuss how to configure OrderCloud and how to create an anonymous authentication token in XM Cloud Head App based on Next.js and Sitecore Content SDK to implement anonymous shopping.

OrderCloud Configuration for Anonymous Shopping

This article on the OrderCloud knowledge base kind of describes how to configure OrderCloud for generating an anonymous token, but it’s kind of outdated and doesn’t explain things at a deeper level.

API Client

The key to setting up your anonymous user starts with creating the API Client that will be used by the storefront (in OrderCloud terms, buyer site). You need to create a Buyer for storefront shoppers, a Security Profile (permission/roles) for storefront shoppers, and assign the Security Profile to the Buyer. Assign this Buyer to the API Client. Any user who belongs to this Buyer should be able to shop using their authenticated credentials. This takes care of authenticated user shopping.

For an anonymous user shopping, you need to create a user in the Buyer group, which will be used by OrderCloud as the Default Context user for anonymous shoppers who don’t have a user of their own in OrderCloud. Set this user in the API Client as the Default Context user and set “Is Anon Buyer” checked. This completes the OrderCloud configuration for anonymous shopping.

Anonymous Token

An anonymous user needs an anonymous token. How to get that? You can call the token endpoint and pass the above API Client to get the anonymous token. The following screenshot shows how to do it in Postman.

Once you get the token, if you decode it, you will see there is an orderid. This ID will be used when you create an anonymous cart using this anonymous token. This way, anonymous carts can be secured to appropriate guest users.

{
  "jti": "BgLAYhOCBU-XsOoWu3cu3Q",
  "usr": "anonuser",
  "cid": "*************",
  "orderid": "Ac574VWP7EKaTSW9kMHizg",
  "u": "321041",
  "usrtype": "buyer",
  "role": [
    "ProductReader",
    "CategoryReader",
    "MeAddressAdmin"
  ],
  "nbf": 1759885677,
  "exp": 1760491077,
  "iss": "https://useast-sandbox.ordercloud.io",
  "aud": "https://useast-sandbox.ordercloud.io"
}

Generate an Anonymous Token in the XM Cloud Head App

We will not use the REST API to generate the anonymous token. We will be using OrderCloud JavaScript SDK. The best place to generate an anonymous is in the XM Cloud HeadApp middleware. This way, a token can be generated and used both in server-side code and client-side code. We will generate the token in the middleware, set the token as an HttpOnly cookie with appropriate Samesite permission (lax). This will secure the token. The following is the updated middleware code that comes with Sitecore Content SDK. Here we are checking if the token cookie already exists or the token has expired, and generating a new token and saving it in the cookie.

import { type NextRequest, type NextResponse, type NextFetchEvent } from 'next/server';
import {
  defineMiddleware,
  MultisiteMiddleware,
  PersonalizeMiddleware,
  RedirectsMiddleware
} from '@sitecore-content-sdk/nextjs/middleware';
import sites from '.sitecore/sites.json';
import scConfig from 'sitecore.config';
import { ApiRole, Auth, Configuration } from 'ordercloud-javascript-sdk';
import { jwtDecode } from 'jwt-decode'; 

const multisite = new MultisiteMiddleware({
  /**
   * List of sites for site resolver to work with
   */
  sites,
  ...scConfig.api.edge,
  ...scConfig.multisite,
  // This function determines if the middleware should be turned off on per-request basis.
  // Certain paths are ignored by default (e.g. files and Next.js API routes), but you may wish to disable more.
  // This is an important performance consideration since Next.js Edge middleware runs on every request.
  skip: () => false
});
const redirects = new RedirectsMiddleware({
  /**
   * List of sites for site resolver to work with
   */
  sites,
  ...scConfig.api.edge,
  ...scConfig.redirects,
  // This function determines if the middleware should be turned off on per-request basis.
  // Certain paths are ignored by default (e.g. Next.js API routes), but you may wish to disable more.
  // By default it is disabled while in development mode.
  // This is an important performance consideration since Next.js Edge middleware runs on every request.
  skip: () => false
});

const personalize = new PersonalizeMiddleware({
  /**
   * List of sites for site resolver to work with
   */
  sites,
  ...scConfig.api.edge,
  ...scConfig.personalize,
  // This function determines if the middleware should be turned off on per-request basis.
  // Certain paths are ignored by default (e.g. Next.js API routes), but you may wish to disable more.
  // By default it is disabled while in development mode.
  // This is an important performance consideration since Next.js Edge middleware runs on every request.
  skip: () => false
});

async function handleOrderCloudAnonymousAuth(request: NextRequest, response: NextResponse) {
  const token = request.cookies.get('ordercloud.anonymousToken')?.value;

  if (token) {
    try {
      const decodedToken = jwtDecode(token);
      
      // Check if the 'exp' claim exists and if the token is expired.
      // The 'exp' claim is in seconds, so we multiply by 1000 for milliseconds.
      if (decodedToken.exp && decodedToken.exp * 1000 < Date.now()) {
        console.log('OrderCloud token has expired. Fetching a new one.');
        // Token is expired, so we will proceed to fetch a new one below.
      } else {
        // Token exists and is not expired, so we can continue.
        return response;
      }
    } catch (error) {
      console.error('Failed to decode token, fetching a new one.', error);
    }
  }

  try {
    const clientId = process.env.NEXT_PUBLIC_ORDERCLOUD_CLIENT_ID;
    const apiUrl = process.env.NEXT_PUBLIC_ORDERCLOUD_API_URL;

    if (!clientId || !apiUrl) {
      throw new Error("Missing required OrderCloud environment variables.");
    }

    console.log('OrderCloud token not found. Attempting to fetch a new one...');
    Configuration.Set({
      baseApiUrl: apiUrl,
    });

    const scopes: ApiRole[] = ['ProductReader', 'CategoryReader', 'MeAddressAdmin']; 
    const authResponse = await Auth.Anonymous(clientId, scopes);

    if (authResponse?.access_token) {
      console.log('Successfully fetched new OrderCloud token.');
      response.cookies.set('ordercloud.anonymousToken', authResponse.access_token, {
        httpOnly: true,
        secure: process.env.NODE_ENV !== 'development',
        sameSite: 'lax',
        path: '/',
      });
      console.log('OrderCloud token cookie has been set on the response.');
    } else {
      console.warn('OrderCloud auth response did not contain an access_token.');
    }
  } catch (error) {
    console.error("Failed to get anonymous OrderCloud token:", error);
  }

  return response;
}

export async function middleware(req: NextRequest, ev: NextFetchEvent) {
  const sitecoreResponse = await defineMiddleware(multisite, redirects, personalize).exec(req, ev);
  return await handleOrderCloudAnonymousAuth(req, sitecoreResponse);
}



export const config = {
  /*
   * Match all paths except for:
   * 1. /api routes
   * 2. /_next (Next.js internals)
   * 3. /sitecore/api (Sitecore API routes)
   * 4. /- (Sitecore media)
   * 5. /healthz (Health check)
   * 7. all root files inside /public
   */
  matcher: [
    '/',
    '/((?!api/|_next/|locations/|feaas-render|healthz|sitecore/api/|-/|favicon.ico|sc_logo.svg).*)'
  ]
};

Once we have the token, we can use it to create a cart or get the anonymous cart for the token if the cart already exists. OrderCloud will create a cart automatically when a line item is added to the cart if the cart doesn’t exist. This is often a preferred method because it minimizes the number of redundant carts.

In this anonymous shopping workflow, if at any time the user logs in to his/her account, the anonymous cart can be transferred to the logged account. We can do this by using

PUT ‘https://api.ordercloud.io/v1/me/orders?anonUserToken=string&#8217; \ -H ‘Authorization: Bearer <logged_in_user_token>’

Or using an equivalent method in JavaScript SDK or OrderCloud .NET SDK. For more information about how to use the .NET SDK see my previous article.

Posted in Commercce, OrderCloud, Uncategorized | Tagged , , , , , , , , | Leave a comment

How to Securely Access OrderCloud Resources in the Middleware Services on Behalf of Athenticated User

The use case is that the eCommerce website needs to make API calls that access resources from several backend systems, including OrderCloud. The middleware program that implements the APIs needs to access OrderCloud resources on behalf of the user who is already authenticated via the sign-in process and has acquired the OrderCloud authentication token. In this blog, we will discuss how to achieve this utilizing the OrderCloud .NET SDK.

OrderCloud .NET SDK is a Nuget Package that supports practically all .NET versions (.NET Framework 4.5 and .NET Standard 1.3 and 2.0, meaning it’ll run just about everywhere .NET runs, including .NET Core 1.0 and 2.0, Mono, Xamarin (iOS and Android), and UWP 10). The SDK facilitates to use OrderCloud APIs securely and easily without making REST API calls directly.

We will be showing the usage using an Azure Function. To make OrderCloud API calls on behalf of an authenticated user, we will pass the authentication token acquired by the user using the sign-in process on the website and through the Header of the Azure Function API call. Here is a private function that can be used to extract the token from the Header.

    private string GetOCUserTokenFromHeader(HttpRequestData req)
    {
        if (!req.Headers.TryGetValues("Oc-Token", out var tokenValues))
        {
            throw new HttpRequestException("Missing Oc-Token header.", null, HttpStatusCode.BadRequest);
        }

        var token = tokenValues.FirstOrDefault();

        if (string.IsNullOrEmpty(token))
        {
            throw new HttpRequestException("Invalid Oc-Token header.", null, HttpStatusCode.BadRequest);
        }
        return token.ToString();
    }

Once we receive the user’s access token, we need to verify if the token is valid. This is an important step to check because we don’t want the API to use a token with a higher privilege, so it compromises the OrderCloud system. It also checks if the token was generated for the correct ClientID assigned for the users on the website. The following code shows how we can use the SDK’s RequestAuthenticationService to verify the token and return the user token to the calling function.

    private async Task<string?> GetValidatedUserTokenAsync(string token)
    {
        try
        {
            var ocAppClientId = _keyVaultClient.GetSecretAsync("OrderCloudAppClientId") ?? string.Empty;
            var authOptions = new OrderCloudUserAuthOptions
            {
                ValidClientIDs = new List<string> { ocAppClientId }
            };

            var decoded = await _auth.VerifyTokenAsync(token, authOptions);
            return decoded.AccessToken;
        }
        catch
        {
            _logger.LogError("Failed to verify OrderCloud user token.");
            throw;
        }

    }

After verifying the token, we can proceed to use OrderCloudClient to access OrderCloud resources on behalf of the authenticated user. For example, if we want to access a product that the user is allowed to see, we can use the following code.

var usertoken = await GetValidatedUserTokenAsync(GetOCUserTokenFromHeader(req));
var product = await _oc.Products.GetAsync(addCartLineRequest.ProductId, usertoken);

To use RequestAuthenticationService and OrderCloudClient we need to register them in the Program.cs as follows.

        services.AddHttpContextAccessor();
        services.AddSingleton<RequestAuthenticationService>();
        services.AddSingleton<ISimpleCache, LazyCacheService>();
        services.AddSingleton<IOrderCloudClient>(sp =>
        {
            var apiUrl = Environment.GetEnvironmentVariable("OrderCloudAPIUrl");
            return new OrderCloudClient(new OrderCloudClientConfig
            {
                ApiUrl = apiUrl,
                AuthUrl = apiUrl,
                ClientId = _keyVaultClient.GetSecretAsync("OrderCloudServiceClientId") ?? string.Empty,
                ClientSecret = _keyVaultClient.GetSecretAsync("OrderCloudServiceClientSecret") ?? string.Empty
            });
        });

HttpContextAccessor and ISimpleCache are dependencies for RequestAuthenticationService. You may notice that I am using KeyVaultClient for retrieving sensitive information like Client Id and Client Secret. It is extremely important to protect this information in a place like Key Vault because OrderCloudClient runs on a much higher privilege, and much harm can be done if this information falls to a bad actor.

To learn more about the SDK, you can look at the OrderCloud .NET SDK Repository. There is not much documentation available. I hope this blog helps you to start.

Posted in Commercce, OrderCloud, Sitecore | Tagged , , , , , , , , | Leave a comment

Building an AI Shopping Assitant on top of Sitecore OrderCloud

The digital shopping experience built on top of any e-commerce system is significantly different from shopping at physical stores. The interaction with human assistants at physical stores is more, what I should say, human-like. The same experience cannot be presented on a browser-based system, where interaction with the online stores happens through mouse clicks, not via conversation. But this difference can be reduced by using Generative AI because of its ability to process natural language. Can we build a conversational shopping experience for online shopping using an existing e-commerce platform which weren’t built for a conversational shopping experience? I think we can do that with the layered AI architecture on top of the e-commerce platform if the e-commerce platform was built with MACH (Microservices-based, API-first, Cloud-native, and Headless) architecture principles. Sitecoe OrderCloud is suitable for that.

In the first part of this blog series, we will introduce our goal for building an AI Shopping Assistant, A Headless AI-driven architecture, and the Technology Stack.

Our goal is to create an end-to-end conversational buying experience. Imagine a user starting a conversation: “I need some waterproof hiking boots that are good for wide feet.” The AI not only finds the right products but continues the dialogue: “I’ve found a few options. The Merrell Moab 3 is highly rated. Would you like to add a size 10 to your cart?” Our solution aims to unify this journey into a single, fluid conversation, guided by an intelligent agent.

High-Level Architecture

Here is the high-level overview of the components and data flow for our proposed architecture.

Component Breakdown:

  1. Frontend: A modern web application (built with React) containing the chat interface where the user interacts with our AI shopping assistant.
  2. AI Agent (The Brain): This is a Large Language Model (LLM) like Google’s Gemini or OpenAI’s GPT models, orchestrated by a framework like LangChain. Its job is to understand the user’s natural language, determine their intent, and decide which “tool” to use to fulfill the request.
  3. Sitecore OrderCloud MCP Server (The Toolbelt): This is the central orchestration layer we will build. It acts as a standardized bridge, exposing our backend commerce functions (like search_products, add_to_cart) as a suite of tools that the AI Agent can call upon.
  4. Vector Database (Pinecone): This is where the AI’s product knowledge resides. We’ll store numerical representations (vector embeddings) of our product data here. These embeddings capture the meaning of product descriptions, allowing the AI to find conceptually similar items, not just keyword matches.
  5. Headless Commerce Platform (Sitecore OrderCloud): This is our system of record—the source of truth for the entire product catalog, real-time pricing, inventory, and order management.

The Technology Stack

Throughout this series, we will be using the following key technologies to build our proof-of-concept:

  1. Frontend: React / Next.js
  2. Gen AI for Language Processing: A Large Language Model. We will use Gemini.
  3. Orchestration Server: A custom MCP Server built with Python (FastAPI)
  4. AI Orchestration: LangChain or LlamaIndex
  5. Vector Database: Pinecone
  6. Commerce Backend: Sitecore OrderCloud

What Next?

In the next blog, we’ll build the data foundation for our AI assistant, teaching it about the products it can sell by extracting our catalog from OrderCloud and vectorizing it for semantic understanding in Pinecone.

Posted in AI, Commercce, Next.js, OrderCloud, Sitecore | Tagged , , , , , | Leave a comment

Protect Sitecore Forms and the website from Intelligent Bots

As we make progress with technology, the malicious actors take advantage of the same technological advances and come up with new and improved ways to attack websites. What used to be just protecting the forms with reCaptcha is not sufficient anymore. In this blog post, I will discuss how to protect your Sitecore Forms and Sitecore XM Cloud based Nexj.Js Head App from Bots and DDoS attacks. I assume that we will be using Vercel as the hosting platform, but the same concepts can be applied to other hosting services, except that Vercel Service has a built-in Bot protection via BotId.

Sitecore Forms, a part of Sitecore XM Cloud doesn’t come with any in-built protection from Bots except reCAPTCHA. But, in the age of AI, reCAPTCHA is not sufficient to defend against Bots. Sitecore Forms works by enabling us to create a form presentation with its in-built field types, UI design capabilities, and allowing us to integrate the form with an API via webhook functionality. At the application layer, to protect the form, it is the application developers’ responsibility to catch bots in the frontend code or in the API associated with the webhook of the form. We will discuss how we can do that at the application level, as well as layered protection at the network level that vastly cuts down the malicious bot traffic.

Architectural Solution Overview

Our proposed architecture employs a layered defense, with each layer contributing to overall security and efficiency:

Vercel Platform Layer (Edge/CDN)

This layer provides the initial and most performant defense, operating at the edge of the network.

  • DDoS Protection: Vercel’s built-in, automatic DDoS mitigation (Layers 3, 4, and 7) provides the outermost defense, absorbing high-volume attacks before they impact your application.
    DDoS Mitigation
  • Web Application Firewall (WAF): Vercel’s Web Application Firewall applies custom rules to implement business logic and block common web attack vectors, such as credential stuffing, malformed requests, and attacks targeting vulnerable routes.
  • Vercel BotID: This is an invisible, AI-driven bot detection service. It injects obfuscated JavaScript into the client’s browser to silently collect thousands of signals, distinguishing human users from automated bots without requiring user interaction. ​Introducing BotID, invisible bot filtering for critical routes – Vercel
    Basic Mode: Available on all Vercel plans, this mode ensures valid browser sessions are accessing your site.
    Deep Analysis Mode: For Vercel Pro and Enterprise plans, this mode is powered by Kasada and connects thousands of additional client-side signals for enhanced detection against sophisticated, evasive bots. The Best CAPTCHA is No CAPTCHA: Introducing Vercel BotID, Powered by Kasada – Kasada
    The Deep Analysis does have an additional cost in Vercel plans. You need pro plan ($1 per 1000 Deep Analysis) or an Enterprise Plan with Managed Infrastructure pricing.
  • Edge Middleware: This layer acts as a rapid pre-filtering mechanism. It can inspect incoming requests and block or redirect traffic based on simple heuristics, such as suspicious User-Agent strings or known malicious IP addresses. This helps reduce the load on downstream systems and optimizes compute costs by filtering obvious bot traffic early.

Application Layer (Serverless Functions/API Routes)

This layer implements form-specific and application-level bot prevention strategies.

  • Adaptive Rate Limiting: Implemented within API Routes, this controls the volume of requests a single user or IP address can make to an endpoint within a specified timeframe. It prevents server overload, enhances security against brute-force attacks, and helps manage infrastructure costs. Limits can be dynamically adjusted and applied granularly to different API routes based on their sensitivity or resource intensity.
  • Honeypot Fields: This technique involves adding hidden form fields that are invisible to human users but are often automatically filled by bots. Server-side validation checks if these hidden fields contain data; if so, the submission is silently discarded or flagged, effectively catching unsophisticated bots without impacting legitimate user experience.

Sitecore XM Cloud Forms Integration

Sitecore XM Cloud’s role in this architecture focuses on content management and form definition, with bot prevention handled by the Next.js application.

  • Form Definition & Webhook Integration: Sitecore XM Cloud Forms doesn’t come with any built-in Bot protection features. Forms are used to define the structure and fields of web forms. Upon submission, these forms are configured to send data via webhooks to a designated API Route. This API Route serves as the central point where all bot prevention checks are performed. All forms submission webhooks should be integrated with APIs implemented in Next.js, even if the final webhook destination is an API on the backend system. The Next.js APIs will act as a proxy and handle the Bot prevention utilizing the BotId. Only after successful validation by the Next.js application is the form data then forwarded to its final destination within Sitecore XM Cloud or other integrated backend systems. In addition to using BotId, we should add a Honeypot field in the form to capture bots.

Implementation Approach

Implementing this architecture involves configuring each layer to work cohesively:

  • Vercel BotID:
    Client-side: Initialize the BotID client-side script on relevant pages or methods that contain forms or critical user flows.
// pages/_app.tsx (or a relevant client component)
// This component needs to run on the client
'use client';

import { initBotId } from 'botid/client';
import type { AppProps } from 'next/app';

// Initialize BotID for a specific route with basic analysis
// The 'protect' array specifies which paths/methods BotID should monitor.
initBotId({
protect:,
});

function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}

export default MyApp;
  • Server-side (Pages Router API Route – Edge Function): Integrate the checkBotId() function call within your Next.js API Routes that handle form submissions or other sensitive operations. Note that checkBotId() requires the API route to be an Edge Function. Ensure the checkLevel (e.g., 'basic' or 'deepAnalysis') configured on both the client and server sides for a given route is identical to prevent verification failures.
// pages/api/submit-form-with-botid.ts
import { NextRequest, NextResponse } from 'next/server'; // These types are used for Edge Functions
import { checkBotId } from 'botid/server';

// This makes the API route an Edge Function, which is required for checkBotId()
export const runtime = 'edge';

export default async function handler(request: NextRequest) {
if (request.method!== 'POST') {
return NextResponse.json({ message: 'Method Not Allowed' }, { status: 405 });
}

// Perform Vercel BotID verification with basic analysis
const verification = await checkBotId({
advancedOptions: {
checkLevel: 'basic', // Must match client-side configuration [16]
},
});

if (verification.isBot) {
console.warn('Bot detected via Vercel BotID (Basic Analysis).');
// For bots, return a deceptive success to avoid giving feedback,
// or a 403 Forbidden if you prefer an explicit block.
return NextResponse.json({ message: 'Thank you for your submission!' }, { status: 200 });
}

// If not a bot, proceed with your form processing logic
try {
const formData = await request.json();
console.log('Legitimate form data received:', formData);

// Example: Forward to Sitecore XM Cloud webhook
const sitecoreWebhookUrl = process.env.SITECORE_FORM_WEBHOOK_URL;
if (!sitecoreWebhookUrl) {
console.error('SITECORE_FORM_WEBHOOK_URL is not configured.');
return NextResponse.json({ message: 'Server configuration error.' }, { status: 500 });
}

const webhookResponse = await fetch(sitecoreWebhookUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData),
});

if (!webhookResponse.ok) {
console.error(`Webhook submission failed with status: ${webhookResponse.status}`);
return NextResponse.json({ message: 'Failed to submit form data to Sitecore.' }, { status: 500 });
}

return NextResponse.json({ message: 'Form submitted successfully!' }, { status: 200 });
} catch (error) {
console.error('Error processing form submission:', error);
return NextResponse.json({ message: 'Internal server error.' }, { status: 500 });
}
}
  • Edge Middleware:
    – Modify the middleware.ts file at the root of your Next.js project.
    – Implement logic to inspect incoming request headers (e.g., User-Agent) against a configurable list of known bot patterns.
    – Configure the middleware to redirect or block requests identified as bots, while ensuring essential paths (like internal Next.js routes and static assets) are excluded from these checks.
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

// Define paths and file extensions to exclude from bot detection
const EXCLUDED_PATHS = ['/_next/', '/static/', '/api/']; // Exclude Next.js internal paths and API routes
const EXCLUDED_EXTENSIONS = [
'.svg',
'.js',
'.css',
'.ico',
'.png',
'.jpg',
'.jpeg',
'.gif',
'.webp',
'.woff2',
];

// Define common bot user-agent patterns directly in the code
const BOT_PATTERNS = [/bot/i, /crawler/i, /spider/i, /curl/i, /wget/i, /aihttpbot/i, /mj12bot/i]; [6]

export function middleware(request: NextRequest) {
const url = new URL(request.url);
const userAgent = request.headers.get('user-agent') || '';

// 1. Exclude specific paths and file extensions
if (
EXCLUDED_PATHS.some((path) => url.pathname.startsWith(path)) ||
EXCLUDED_EXTENSIONS.some((ext) => url.pathname.endsWith(ext))
) {
return NextResponse.next(); // Allow these requests to proceed without bot checks
}

// 2. Basic User-Agent header check using hardcoded patterns
if (BOT_PATTERNS.some((pattern) => pattern.test(userAgent))) {
console.log(`Bot detected via User-Agent: ${userAgent} - Redirecting to /bot-detected.`);
// Redirect the bot to a specific page or return an access denied response
return NextResponse.redirect(new URL('/bot-detected', request.url));
}

console.log(`Request received: ${request.url} | User-Agent: ${userAgent}`);
return NextResponse.next(); // Allow legitimate requests to proceed
}
// Optional: Configure the matcher to apply middleware to specific paths
// export const config = {
// matcher: [
// /*
// * Match all request paths except for the ones starting with:
// * - _next/ (Next.js internal routes)
// * - api/ (your Next.js API routes, if you have any that need different handling)
// * - static/ (static files)
// * - favicon.ico (favicon file)
// * - any file with an extension (e.g.,.js,.css,.png)
// */
// '/((?!_next|api|static|favicon.ico|.*\\..*).*)',
// ],
// };
  • Adaptive Rate Limiting (Pages Router API Route – Node.js Runtime):
    – Utilize a distributed rate-limiting library (e.g., @upstash/ratelimit with @vercel/kv) within Next.js API Routes.
    – Apply specific rate limits to form submission endpoints and other critical API routes to control request volume and prevent abuse.
// pages/api/rate-limit-example.ts
import { Ratelimit } from '@upstash/ratelimit';
import { kv } from '@vercel/kv';
import type { NextApiRequest, NextApiResponse } from 'next'; // Correct types for Pages Router API routes

// Initialize the rate limiter for 5 requests per minute per IP
const ratelimit = new Ratelimit({
redis: kv,
limiter: Ratelimit.slidingWindow(5, '1m'), // 5 requests per minute
analytics: true, // Optional: enables analytics
});

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method!== 'POST') {
return res.status(405).json({ message: 'Method Not Allowed' });
}

// Get the IP address of the user (or a unique identifier)
// For Node.js runtime, req.socket.remoteAddress or x-forwarded-for header
const ip = req.headers['x-forwarded-for'] as string || req.socket.remoteAddress || '127.0.0.1';
// Check if the user has reached their rate limit
const { success, limit, reset, remaining } = await ratelimit.limit(`form_submit_${ip}`); [9]

if (!success) {
const retryAfter = Math.ceil((reset - Date.now()) / 1000);
return res.status(429).json({
message: `Too many requests. Please try again after ${retryAfter} seconds.`,
limit,
remaining,
reset,
retryAfter,
});
}

// Process the form submission (e.g., forward to Sitecore, save to DB)
try {
const formData = req.body; // Assuming JSON body
console.log('Form data received (rate-limited):', formData);
return res.status(200).json({ message: 'Form submitted successfully (rate-limited)!' });
} catch (error) {
console.error('Form submission error:', error);
return res.status(500).json({ message: 'Error processing form submission.' });
}
}
  • Honeypot Fields:
    – Add a hidden input field to your Sitecore Form, styled to be invisible to human users.
    – Create the hidden field like a normal input field with a proper Id and Name. Avoid names like HoneyPot or Trap etc.
    – Do not use display:none to hide the field; rather, use CSS properties like opacity:0, positioning it off-screen, or shrinking it to zero size to make it harder to guess that it is a trap.
    – In the Next.js API Route that processes the form submission, include a server-side check to detect if this hidden field has been populated. If it has, silently discard the submission or log it as bot activity without providing explicit feedback to the bot.
  • Sitecore XM Cloud Forms:
    – Within the Sitecore XM Cloud Forms builder, configure the submission action for each form to point to the specific Next.js API Route designed to receive and process form data, including all bot prevention checks.

Monitoring and Continuous Improvement

Bot prevention is an ongoing process. Continuous monitoring of traffic patterns, form submission rates, and bot detection logs is essential. Regularly review analytics to identify new attack vectors and adapt the prevention strategies, adjusting rate limits or refining bot patterns as needed. This document Bot Management includes all the verified Bots allowed by Vercel.

Posted in Uncategorized | Tagged , , , | Leave a comment

Selecting the Optimal Sitecore Rendering Host Provider

“Rendering Host” is a term coined by Sitecore, referring to the platform that hosts the website. Similarly, “Editing Host” refers to where you host for editing or building your website. This article will explore the options for hosting websites (Rendering Host) built with Sitecore XM Cloud and Sitecore JSS for the Next.js framework.

Server vs Serverless

At a fundamental level, there are two options for hosting a website built on the Next.js framework: using a Node Server or a Serverless hosting environment. How do you go about hosting your website on a Node Server or in a Serverless environment? The diagram below provides a detailed breakdown of the various hosting options available. However, not all of these options support all Next.js features. It is advisable to avoid those that are not fully compatible with Next.js. For more information on which Next.js features are supported by different hosting providers, refer to Thomas Desmond’s blogs.

Node Server Hosting

A Next.js application can be hosted on any platform that supports Node.js. This is straightforward because Next.js includes a web server that starts serving requests when the next start command is executed, and the production-optimized build output is generated by the next build command. The diagram on the left side illustrates various options for hosting a Next.js application using the Node.js runtime.

Server hosting is not optimized for performance. For example, to cache static pages, you need to place a CDN in front of the hosting. Many optimizations available in serverless hosting options must be configured manually. Among these options, Azure App Service and AWS Elastic Beanstalk are the most advanced in terms of scalability because they are Platform as a Service (PaaS). They offer autoscaling, fault tolerance, and load balancing, making them easy to set up. On-Premise, Cloud VM, and VPS are similar hosting solutions, with Cloud VM being the most scalable as it can be adjusted within the cloud infrastructure, while On-Premise and VPS are limited by their hardware. A Next.js application can also be hosted in a Docker container. Using containers with an orchestrator like Kubernetes can be scalable, but it must be hosted on one of the server hosting options.

Another way to host a Next.js application that doesn’t require a Node.js runtime is through Static Export. Running next build generates static HTML, CSS, and JS that can be hosted on any web server that supports static assets. However, this option does not support many Next.js features such as Server-Side Rendering (SSR) and Middleware.

Serverless Hosting

In Serverless Hosting the hosting provider’s build process breaks the build output into following four categories.

Static Resources

All static pages generated by the build process through Static Site Generation (SSG) and Incremental Static Regeneration (ISR) are deployed to Static Storage. During deployment, assets like unoptimized images, fonts, and JavaScript files are also uploaded to Static Storage. When a request is made for a static page, the hosting service checks if the page is already in the cache. If it is, the page is rendered from the cache; if not, it is fetched from static storage and then copied to the cache for future requests.

For ISR pages, when a request is made, the hosting service verifies if the revalidation period for the page has elapsed. If it has, the stale page is served from the cache while a Serverless Function (Prerender Function) is triggered to regenerate the page. Once the new page is generated, it replaces the stale page in both the cache and Static Storage.

Serverless Function

API Routes and Server-Side Rendering (SSR) pages are converted into serverless functions. When getServerSideProps is used in a Next.js page, it generates a serverless function for that page. This function is responsible for fetching data and rendering the page on each request.

Edge Function

Edge Functions in Next.js enhance performance by deploying code at the edge, closer to the user’s location. They also improve security by using Edge Middleware, which runs before requests are routed to the server. Despite limitations such as code size, memory allocation, and timeout limits, Edge Functions are designed to run lightweight code on the V8 runtime near users. Therefore, if a Next.js application uses middleware or edge functions, that code will be deployed at the edge rather than on the server as serverless functions.

Image Optimization

The Sitecore JSS image component uses next/image to provide image optimization and render images in the Editing Host. During the Next.js build and deployment process, optimized images are created and deployed to the edge if they are part of the Next.js solution. For remote image URLs, Next.js optimizes images at runtime and requires width and height specifications in the code.

The description above explains how a Next.js application is divided into different parts and deployed in a serverless environment. To make this clearer, I have included a diagram from Vercel’s documentation, which best illustrates this process. Other hosting providers may use different approaches.

Source Vercel

Choosing Factors

How to decide which type of hosting option to host Sitecore Websites, Server Hosting or Serverless Hosting. What hosting provider to go with. The answer to this depends on many factors. Let’s discuss them.

Performance

Every company aims for optimal website performance, but performance is a relative concept. Achieving higher performance often involves significant costs. One company might be willing to invest millions to reduce response time by a second, while another might not find such an investment crucial. Therefore, it’s essential to define what level of performance is important for an organization. While a website hosted on a serverless platform will perform better, it will be more expensive than hosting on a Node Server.

A website’s performance depends not only on how requests are processed on the server or serverless platform but also on how those requests are routed from users’ machines to the server. Superior network architecture enhances performance. For instance, Amazon, Azure, and Vercel use Anycast for load balancing, which significantly improves performance and security.

Anycast load balancing is a technique used to distribute incoming network traffic across multiple servers or data centers, ensuring efficient and scalable content delivery. Here’s how anycast load balancing works:

  1. Anycast IP address: A single IP address is announced from multiple locations (edge servers or data centers) worldwide.
  2. Route announcements: Each location announces the same IP address to the internet using BGP (Border Gateway Protocol) routing.
  3. Closest location: When a user sends a request to the anycast IP address, their internet service provider’s (ISP) router directs the request to the closest location (edge server or data center) advertising the same IP address.
  4. Load balancing: The request is then load-balanced across multiple servers within that location, ensuring efficient use of resources and minimizing latency.
  5. Content delivery: The requested content is delivered from the load-balanced server to the user.

Anycast load balancing offers several benefits, including:

  • Reduced latency: Users connect to the closest location, reducing latency and improving performance.
  • Improved scalability: Traffic is distributed across multiple locations, allowing for more efficient handling of high traffic volumes.
  • Increased availability: If one location becomes unavailable, traffic is automatically routed to another location advertising the same anycast IP address.

By leveraging anycast load balancing, content delivery networks (CDNs) and organizations can deliver fast, reliable, and scalable online experiences to users worldwide.

The following picture taken from the Vercel website shows the difference between Geocast (upper) vs Anycast (lower) load balancing. In Anycast you can see that traffic is routed based on the closest distance not based on geo boundary. For some traffic from Europe routed to africa.

Source Vercel

Reliability and Fault Tolerance

The reliability of a website hinges on the hosting architecture’s ability to manage loads and the fault tolerance of the hosting provider. Serverless architecture is superior as it is optimized to handle increased loads effectively. Anycast load balancing enhances protection against DDoS attacks by distributing requests based on server capacity, health, and proximity. This approach eliminates a single point of failure, expands the network surface area, and complicates efforts for hackers to launch DDoS attacks.

Hosting Cost

Serverless hosting is more expensive than Node Server hosting. Hosting websites on Vercel costs significantly more than on Azure App Service, but cost shouldn’t be considered in isolation. App Service hosting necessitates internal DevOps resources for application deployment and site reliability, adding to the overall cost. Additionally, improved website performance leads to a better user experience and higher conversion rates, resulting in increased earnings. These factors should be taken into account when evaluating hosting costs.

Feature Support

Support for Next.js features is a crucial factor when selecting a hosting provider. Whether you choose serverless or Node Server hosting, lacking full support for all Next.js features can limit your ability to create an optimal solution. To find out which hosting providers support Next.js features, refer to Thomas Desmond’s blogs.

Privacy

Sometimes, choosing a hosting option must be based solely on a company’s privacy policy. Some organizations have invested years in building platforms to protect their internal and customer data. In such cases, they are committed to using the system they have developed, and no other factors matter. However, understanding what that system offers for hosting a Sitecore XM Cloud-based website can still help the organization optimize the website’s performance and reliability.

Conclusion

The above is not an exhaustive list for selecting the right hosting provider. Before making a choice, compile a list of requirements based on factors such as performance, security, website uptime, privacy, cost, and any other relevant criteria for your organization. Assess which hosting provider best fits these requirements. Remember, choosing a hosting provider is a long-term decision, so choose wisely.

References

Posted in Next.js, Sitecore | Tagged , , , , , , | Leave a comment

Build Product Recommendations in Sitecore OrderCloud using LLM, and Sitecore CDP


What is happening at OpenAI? Sam Altman, the CEO of OpenAI, was fired on Friday by the board members. Over the weekend, there were discussions about bringing him back. The drama is still unfolding, and we have to wait to see what happens in the coming weeks.

This blog is not about OpenAPI, but it’s hard to start without mentioning it because it’s just such a shocking event for the future of Large Language Model (LLM). In a recent discussion with Cambridge audience, Sam Altman was asked whether another breakthrough is needed to achieve Artificial General Intelligence (AGI)? Sam’s answer was ‘yes, another breakthrough is needed’. Understanding how LLM works, we know that it uses language used by humans to find answers to questions. This is a very good article that explains how ChatGPT works. We express our thoughts using language, but is that enough for AI to create new ideas like humans? I don’t think so. Clearly, Sam thinks there are more than LLM needed to achieve AGI. But, there is no doubt LLM is extremely powerful and it can help us finding solutions for many problems. In this blog I will explore how LLM can be used to build product recommendation in Sitecore OrderCloud utilizing LLM and Sitecore CDP. This discussion will be on a proposed solution at the architecture level. Let’s dive into it!

So, why am considering LLM to build product recommendations in an e-commerce system? I think LLM can remove lots of complexity from the current approach of generating product recommendations which is mainly based on Collaborative Filtering, Content Filtering, and Hybrid Filtering. I discussed this previously in my article The Expanding Universe of Software Development. The filtering approach is complex and takes time to generate data for segmentation. Whereas using LLM will be much more real-time approach because it is based on product content and customer’s interest based on closeness of language.

Sitecore OrderCloud doesn’t have a product recommendation engine built in, but you can combine Sitecore Discover with OrderCloud for product recommendation. Sitecore Discover is part of Sitecore composable stack. It is based on same Collaborative Filtering algorithm. It comes with frontend widgets, a Javascript SDK, admin panel for analytics and product management. It works well and it doesn’t need CDP. What we are trying to do is to create a product recommendation solution based on LLM and Sitecore CDP. Not a product like Discover. It can be designed like a product though.

I will explain the working of this solution with an example. A website selling books wants to recommend books based on users’ browsing behavior and previous purchases.

  • Send book title and author to CDP
  • Read previously browsed and purchased books by the user from CDP
  • Send the books and authors’ names to ChatGPT (LLM) API to find out the genres based on sent data
  • Save the genres against the visitor in CDP
  • Generate a list of books based on the genres provide by ChatGPT
  • Show the list in recommended products

Here is an example

I can even ask for recommended books from ChatGPT but storing the genres in CDP and using the genre to generate the recommendations from OrderCloud is better because I can generate the books that are available in the online store, I can decide what to show based on my requirements, and also I can use the genres when next time same user visit the website even before start browsing books.

This solution works fine with ChatGPT and any kind of LLM services that provides API access to their models, but it will not work so well when the online commerce business is based on specialized products. This especially true in B2B commerce. ChatGPT or other LLM services are trained on scrubbing data from internet. We can’t feed the website products and contents ChatGPT. In this kind of situation we need to train LLM of our own using the products and contents used in the website. There are many open source LLMs available today. Two popular ones are Facebook’s Llama 2 and Claude 2 from Anthropic.

So here is the proposed solution

  • Set up and train the LLM with the products and contents from the website. LLM will need periodic refresh as new products and contents will be updated.
  • Create API interface against the trained LLM to return response to recommendation questions.
  • Set up the website to send visitors’ data to Sitecore CDP using Stream APIs.
  • Use Sitecore CDP Rest APIs to send order data.
  • Use Sitecore CDP Rest APIs to retrieve users’ previously browsed and order products.
  • Send users’ previously browsed and ordered product to LLM to learn users profile.
  • Save this profile in CDP against users.
  • Retrieve user’s profile from CDP using the Rest API.
  • Send user’s profile to LLM for recommended products.

Above is an outline of how we can use LLM to build a Product Recommendation Engine. Actual implementation will require to consider many details. Product recommendation can be further improved with users data like their locations, language they speak, age, sex etc. LLM can also help with inventory management and forecasting, sentiment analysis, search and many other things that helps with e-commerce conversion.

Posted in AI, Commercce, OrderCloud | Tagged , , , , , | Leave a comment

What’s New In Sitecore OrderCloud

It was a great week of learning in Sitecore MVP Summit + Sitecore DX + SUGCON NA in Minneapolis from October 2nd to October 6th. There were no announcements of new acquisitions or new products. It looks like Sitecore is fully embracing the composable architecture approach and is now concentrating on refining its existing products. Sitecore already provides the key components required for a Digital Experience Platform (DXP) implementation. The current emphasis is on ensuring these components work together smoothly without compromising the principles of composable architecture.

I was interested to learn what’s happening with Sitecore OrderCloud. In last year’s Symposium and MVP Summit, we learned that Sitecore would work on Project Affinity. I wrote an article about that. The following slide shows what Sitecore’s plan was to build in Project Affinity.

At this year’s MVP Summit and Sitecore Developer Experience (DX), there was no update on Project Affinity. It’s possible that the project was a bit too bold to pursue. However, it doesn’t seem like Sitecore has completely scrapped the ideas from Project Affinity. Rather, it appears their focus has moved towards offering additional resources and enhanced support for developers. Let’s dive into the latest developments.

OrderCloud is Part of the Sitecore Portal

Until now OrderCloud had its own portal. Sitecore has integrated OrderCloud in the Sitecore Portal. You need to work with partner support to add the OrderCloud Portal to your Sitecore Cloud Portal. It will show up in the Apps section.

If you already have have OrderCloud instance in portal.ordercloud.io, you can keep using that.
With the new portal, you have a more granular way to establish API access, the ability to create custom roles, a much enhanced API Console for filtering and sorting, and Index Tools to rebuild products and orders index.

OrderCloud Javascript SDK

What I am most excited about is the release of OrderCloud Javascript SDK. It’s a daunting task to work with Rest API when building a website using a Frontend Framework. As a developer, I want to focus on the feature development. Working with APIs directly unnecessarily adds repeated code to my projects. An SDK also mandates the development team to follow the same pattern for development. OrderCloud Javascript SDK works both on the browser and node.js. This means I can use the same SDK for Client Side Rendering (CSR) and Server Side Rendering (SSR). This is especially useful for building applications using Next.js. The SDK comes with built-in Typescript support, no additional types package is necessary.

React based Headstart Admin

The Headstart Starter Kit is now available in React. The earlier Headstart was based on Angular and it was not very easy to work with. This version of Headstart uses the OrderCloud Javascript SDK. This is only the starter kit for the admin portal; it does not include a buyer portal. While Project Affinity had the ambition to deliver a Universal Commerce Management Backoffice, which hasn’t materialized, the availability of this starter kit for developing an Admin Portal is a step forward. It’s quite rare for a commerce platform not to offer an out-of-the-box Admin Portal, considering such a feature typically doesn’t vary much from one customer to another.
In future releases, Sitecore is planning to add Headstart Buyer Portal, integration with Sitecore Discover for product search and product recommendations, example solution for connecting Sitecore OrderCloud with Content Hub One and XM Cloud.

Delivery Configuration

Sitecore took the approach of using Pub/Sub pattern for delivering data or messages. In this approach, you define a target and subscribe to get the data delivered to the target. For example, you want the order notification to be sent to Sitecore Send so that email notifications can be sent to customers. Delivery targets can be Sitecore’s internal targets (Send, Discover) or external targets (Kafka, HTTP endpoint, Event Hub). This approach can be used for Product Synchronization, Order Synchronization, and more.

New Features

Last year I wrote about Product Collection and mentioned that it was not complete. Sitecore made significant enhancements to the Product Collection feature this year. It seems to be ready for the mainstream use.
The brand new feature introduced this year is Product Bundles or Kits we say sometimes. Product Bundles are unique SKUs created by combining various products. OrderCloud offers a suite of APIs that enable the creation of these bundles, their assignment to catalogs, the application of promotions to them, and the ability to set pricing either for the bundle as a whole or for individual items within it. Functionally, bundles are treated like individual products, meaning they can be searched for and placed into the shopping cart just like any single product.

Acknowledgments
I would like to thank Ashley Wilson, Commerce Product Manager at Sitecore for sharing the PowerPoint slides of her presentation at the MVP Summit. Some images used in this article are taken from her presentation.

Posted in Commercce, OrderCloud | Tagged , , | Leave a comment

How to troubleshoot “dependency failed to start: container sxastarter-cm-1 is unhealthy”

When you are setting up your Fullstack Sitecore XM Cloud environment in your local machine there is a chance that you may see the below error.

dependency failed to start: container sxastarter-cm-1 is unhealthy
Waiting for CM to become available…
Invoke-RestMethod: .\up.ps1:59
Line |
59 | … $status = Invoke-RestMethod “http://localhost:8079/api/http/routers
| ~~~~~~~~~~~~~
| No connection could be made because the target machine actively refused it.

I am also adding a screenshot of the result after running the .\up.ps1 script to bring up your docker environment.

This error can happen for many reasons. Instead of guessing what went wrong you can troubleshoot the issue using docker commands.

The first thing you should do is inspect the container to see the health check status. Run the below command to do that. Your container name can be different than sxastarter-cm-1.

docker inspect sxastarter-cm-1

Go to the “Health” section of the output and see the messages. In my case, the log showed Internal Server Error.

At this point, you can look at the docker logs by running the below command in the Powershell Window.

docker logs sxastarter-cm-1

In my case log showed the same error as the Health logs above. The health check URL “/healthz/ready” showed HTTP status 500.

To know what exactly caused the 500 error you can browse that URL using the below docker command.

docker exec -it sxastarter-cm-1 curl http://localhost:80/healthz/ready

The above docker command browsed the URL using curl and returned the below result for me.

The output indicates I had a problem with the Sitecore license “Required license is missing: SiteCore.Runtime”. I found that the license I was using had expired.

The above approach can be used to troubleshoot issues with other containers in the XM Cloud.

Funny bit for Sitecore Developers: Error shows SiteCore.Runtime 🙂

So, how did I fix the issue? It was easy for me. The docker inspect command shows me that the license folder for the cm container is mounted to the physical folder of my license file with the mount type “bind”.

That means if I replace my license file with the correct license file, it will be reflected in the container if I restart the container. I replaced the license file, ran .\down.ps1, and then ran .\up.ps1. Voila!

Posted in Debugging, Powershell, Sitecore | Tagged , , , | Leave a comment

What’s Coming to Sitecore OrderCloud

In my previous blog post, I discussed Sitecore OrderCloud’s philosophy of “Flexibility Over Features” and the product’s strategies around this philosophy. After that Sitecore Symposium happened and we had opportunities to hear Sitecore OrderCloud’s roadmaps and directions from the Sitecore leadership team. Based on that I need to amend my previous blog post. I also gathered some information about the Product Collections functionalities roadmap with the Sitecore OrderCloud team. I will discuss that in this article because some issues I raised in my previous article will be addressed by items included in that roadmap.

Project Affinity

Sitecore is not abandoning the “Flexibility Over Features” philosophy. Sitecore OrderCloud will continue to be part of MACH Alliance and enhance the product based on its core philosophy. The new direction is that there are features and integrations that make sense to add as part of the solutions (not necessarily as part of the core product) because those can be used by customers. And, those can be done without compromising the core philosophy. For example, most eCommerce solutions need to calculate tax and creating tax plug-ins to Avalara, Vertex, etc. makes sense as long as Plug-ins can be customizable and configurable. Full details are not available yet, but I envision that we will be able to choose the tax service in the OrderCloud portal and configure that. In the Calculate an Order integration event a call will be made to the tax plug-in to calculate the tax. The plug-ins will have integration options like webhooks so that we can customize them. In addition to providing out-of-the-box integrations, Sitecore has also decided to provide Storefront templates based on XM Cloud and a Commerce Management Portal for business users. This will significantly make it easy for the partners to recommend OrderCloud as an eCommerce platform to their clients. All these are part of Project Affinity which Sitecore announced in the Symposium. The timeline as announced is the end of 2023.

Sitecore Search

Sitecore Discover although treated as a separate service it is becoming a first-class citizen for OrderCloud-based solutions. It will cover both product search and content search for the Storefront. Discover’s AI-based search will provide experience-based search results for eCommerce solutions. OrderCloud does have an in-built search for products, customers, and orders. I think this search will be used by the Commerce Management Backoffice.

Sitecore Connect

Sitecore announced Sitecore Connect, an integration platform that can be used by partners and customers to build Low-Code/No-Code integrations. Sitecore Connect comes with 1000s of pre-built connectors. These connectors can be integrated with Sitecore OrderCloud in an almost drag-and-drop fashion. It makes sense for Sitecore to build some connectors like payment gateways, tax connectors, etc. in the OrderCloud platform itself because of the sensitive nature of the data. For example, payment gateways require the platform to be compliant with PCI compliance. Also, integration between Sitecore products will also be expected for Sitecore to build. One such example is the integration between Sitecore OrderCloud and Content Hub. But, Sitecore Connect opens up immense possibilities for connecting OrderCloud with diverse platforms. In the B2B business, in most cases, sellers use some kind of ERP for order fulfillment. There are many ERP connectors that will be available in Sitecore Connect. Some examples are Infor, Acumatica, Oracle, SAP, etc. I would imagine that when Sitecore Connect will be released, OrderCloud can be integrated with these ERPs.

New Product Collections Enhancements

In my previous blog, I discussed the limitations of the current Product Collections data model and APIs. Particularly I pointed out that Product Collections can be created only for users and we can’t add eXtended Properties at the item level. I took these to the OrderCloud product team so that I can add the feature requests. What I learned they are ahead of me. They shared the below enhancements from their roadmaps. Bolded enhancements are what I wanted to request.

  • ProductCollection entry xp
  • ProductCollection readable by Marketplace Owner (MPO)
  • “Public” ProductCollection  (viewable by any buyer user in your marketplaces)
  • Sharable ProductCollection (viewable by designated buyer users in your marketplace)
  • Properties to enable registry-like functionality (QuantityRequested/QuantityPurchased)

The disclaimer from the OrderCloud team is that currently the above enhancements are being considered, but the plan may change and some of them may not be part of the product.

Final Thoughts

I am excited about the future of OrderCloud and the path Sitecore is taking. Especially, including Sitecore Connect in the architecture shows Sitecore’s commitment to Composable Architecture. According to Gartner one of the pillars of composable DXP is Integration. Sitecore Connect will fill up that gap and help OrderCloud to reach diverse platforms.

Posted in Commercce, OrderCloud, Sitecore | Tagged , , | Leave a comment

Flexibility Over Features Philosophy in Sitecore OrderCloud Architecture

Sitecore OrderCloud is a different kind of eCommerce platform. We know that it is a cloud-native, API-first, headless, SaaS platform, but Sitecore claims that they followed a philosophy when they built the platform or when they are thinking about building new features in the platform. Sitecore calls it “Flexibility Over Features“. You can read about this in this article. One of the main reasons for following this philosophy as explained in the article is that B2B Commerce is too complicated and it is not possible to create features that cover all business cases. Instead, Sitecore decided to create an eCommerce engine that’s flexible enough so that all kinds of business scenarios can be built on that. The claim is that building features will make the architecture rigid. Is this true? In this article, I will examine if it is true and if Sitecore is following up with what they are saying.

This is not an article to establish that Sitecore OrderCloud is a superior or inferior eCommerce platform because it follows this particular philosophy. In fact, there is no platform out there that satisfy all B2B Commerce sellers’ need. The decision to choose an eCommerce platform depends on many factors, including architecture, but often architecture is not the only factor.

Most eCommerce platforms in the market come with an administration portal. Sitecore OrderCloud doesn’t have one. There is Seller Admin Headstart, a starter kit open-source solution built on AngularJS, but it is not a full-featured Admin Portal. Not even close. This comes up often when prospective clients look at the OrderCloud platform. OrderCloud provides a portal where the organization’s marketplaces (businesses) can be managed, but it is not built for business users. Portal provides API Console and some enhanced UIs for creating webhooks, API Clients, etc. and it can be understood by developers. Since Sitecore OrderCloud is providing a commerce engine on which clients can implement their features based on business requirements the Admin Portal features will be different for different clients. This is the line of reasoning behind not having a single Admin Portal for the platform.

I will explain how the Sitecore OrderCloud team approaches Flexibility Over Features using a recent enhancement they have added to the platform. They have added Product Collections feature. A Product Collection is a data model which represents a collection of products that you can create via ProductCollection APIs. You can learn more about Product Collections if you read this article. A Product Collection can be used for different purposes, but let me start with some requirements I have seen before in the B2B Commerce implementation that I was part of.

Most eCommerce sellers want Wish List feature and all most all eCommerce platform comes with Wish List feature. But, since the B2B buying process is bulk buying and repeated buying often, clients need more than just Wish List. Some Clients asked us to implement Frequently Purchased Items List from previous orders. Most B2B users want to work with multiple orders. In the process of creating orders they want to save the order so that they can modify or purchase the order later. This is called Saved Order feature. Saved Order is a very common feature in eCommerce platforms. Ecommerce platforms commonly implement the above-mentioned features as separate features with their own workflows and data models that fit the purpose. You can customize Wish List or Saved Order, but you can’t use them for creating something else like Frequently Purchased Items. The benefit of this approach is that you get Wish List and Saved Orders features out of the box with the eCommerce platform. If that works well for your business it’s great. On one occasion, a client’s requirement was to implement Favorites. Favorites is more of a B2C feature where logged-in users can favorite products so that they can find their favorite products from the list. It is close to Wish List except that when the user is visiting a favorited product, we need to show the favorite icon.

Sitecore OrderCloud doesn’t provide features like Wish List or Saved Orders. The approach in OrderCloud is based on Flexibility Over Features. They see all these features as collection of products. It is kind of a minimalistic approach where OrderCloud says, we will support to give you the ability to create and manage Product Collections, implement features using Product Collections that suits your purpose. So, we can use Product Collections to implement Wish List, Saved Orders, Frequently Purchased Items, Favorites, etc. How to do that? Below I tried to explain with some examples.

We will not go into great detail to implement Wish List or Saved Orders, but talk about simple implementation. All these features are collection of products with additional functionalities. Using Product Collections APIs, I can create Product Collections and identify the type of collection with an XP property “CollectionType”. The XP is helping me to customize Product Collections for my implementation. Here is API request for this.

curl --location --request POST 'https://sandboxapi.ordercloud.io/v1/me/productcollections' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <buyer token value>' \
--data-raw '{
    "Name": "Wish List 1",
    "xp": {
        "CollectionType": "Wish List"
    }
}'

The above code will create Wish List product collection. I can use a similar code to create another Wish List or a different product collection like, “Frequently Purchased Items“. After this, we need to add products to the collection. The below code shows how that can be done.

curl --location --request PUT 'https://sandboxapi.ordercloud.io/v1/me/productcollections/<product collection id>/<product id>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <buyer token value>' \
--data-raw '{
    "xp": {
        "Quantity": 2
    }
}'

The above code adds a product to the product collection, but it doesn’t add the XP. We will talk about the XP part soon when we discuss the current limitation of the Product Collections data model. Using the Product Collections data model and APIs we can implement different kinds of List features as needed. Since OrderCloud doesn’t dictate how Lists should work, it’s completely up to the implementor to decide how to build the features. This is a very powerful way to build solutions, but a lot of restraints require on the platform architecture to stick to this idea and OrderCloud is doing that by considering platform enhancement based Flexibility Over Features philosophy.

I have created a Postman collection for this purpose. You can fork from my collection and work on Product Collections API by visiting the below Postman link.

Run in Postman

Let’s talk about the limitations of the current Product Collections data model and APIs. First, Product Collections works at the ‘Me’ level, which is the current storefront user. This works for the lists on B2C Commerce, but on B2B Commerce, lists are often needed at the Buyer level so that all Buyer users can share lists. The Product Collections documentation mentioned this limitation and talk about expanding the feature to the Buyer level in the future. The other limitation I found is that, when adding products to the collection, I can’t add XP at the item level. I can add only the product id. API doesn’t support this.

This is a limitation because implementation using the Product Collections feature will need to save custom data at the collection item level using XP. For example, if I use Product Collections for implementing Saved Orders, I need to save orderline level data like quantity, line notes, etc. We can get around the limitation by storing data outside of OrderCloud, but that will be a lot more complicated to implement than using XP to store that data.

As an architect, I like the idea of Flexibility Over Features because it gives me lots of freedom to create architectures for implementations and reduce friction. My preference for recommending an eCommerce platform for our clients doesn’t depend on just one notion of the architecture. In spite of that, I like the way Sitecore OrderCloud approaches building the platform around this concept. It fits well in Cloud based SaaS architecture.

Posted in Commercce, OrderCloud, Sitecore, Uncategorized | Tagged , , , , , | 1 Comment