Broadleaf Microservices
  • v1.0.0-latest-prod

Upgrade to 3.0.0

Table of Contents

July 27, 2026

Security

Important

July 27, 2026

This release natively integrates security improvements previously addressed via a standalone hotpatch module. Clients upgrading to version 3.0.0 can safely remove the standalone hotpatch module from their project configuration since these concerns are now supported directly in the framework.

For detailed information regarding this security item, please refer to the Broadleaf Security Advisory. You will need your login credentials originally provided for accessing the Broadleaf nexus.

Requirements

  • Java 17 is required since 2.0.0-GA.

General Upgrade Lifecycle

  • Check out your project(s) for upgrade preparation from your source control.

  • Run the OpenRewrite plugin with Broadleaf’s 3.0.0 upgrade recipe on the project(s) source.

  • Review the changes the plugin makes and attempt compilation.

  • Address any compilation failures.

  • Review your project(s) for possible out-of-scope changes not handled by the automation. See the official Spring upgrade guides mentioned in the sections below.

  • Run your automated test suite and QA test your upgrade.

Migration

Broadleaf 3.0.0 OpenRewrite Recipes

Broadleaf Commerce 3.0.0 includes a suite of automated OpenRewrite Recipes to handle the heavy lifting of the Spring Boot 4.1, Spring Security 7.0, and Hibernate 7 upgrades. The aggregate upgrade script includes a subset of relevant OpenRewrite recipes for the Spring Boot 4 upgrade, as well as custom recipes to update an existing Initializr project to Broadleaf 3.0.

These custom recipes include automation for:

  • Request Matchers Migration: Renames and restructures all AntPathRequestMatcher instantiations, static factory helpers (.antMatcher), and method references (::antMatcher or ::new) to use Broadleaf’s secure smartPathPattern(…​) utility.

  • Spring Security DSL Refactoring: Renames legacy MatcherUtil.mvcMatcher calls to MatcherUtil.mvcRequestMatcher for Spring Security 7 compatibility.

  • Automatic Bean Registration: Automatically registers any custom MetadataMessagesBasename classes as Spring @Component beans (only if they are not already manually registered as @Beans in configuration classes).

  • Hibernate 7 Annotation Changes: Migrates legacy @Where(clause = "…​") entity annotations to the modern @SQLRestriction("…​") format.

  • Apache SolrJ 10 Renaming: Automatically renames Jetty HTTP/2 Solr client classes (e.g. Http2SolrClient to HttpJettySolrClient) and injects the solr-solrj-jetty dependency only where required.

  • Defensive SendGrid Package Updates: Safely renames old com.sendgrid.* package paths to com.sendgrid.helpers.mail.objects.*, executing only if the newer SendGrid library is active on your module’s classpath.

  • Cart Payment Activity Renaming: Performs a highly-scoped rename of validatePaymentsForSubscription(…​) to validatePaymentsForFutureSubscriptionBillingCycles(…​) strictly on classes that extend CartPaymentMethodValidationActivity.

The full list of recipes can be found in the composite upgrade recipe, com.broadleafcommerce.Upgrade_3_0_0.

Configuration

To configure these recipes, you’ll need to add the rewrite-maven-plugin configuration to your project’s root pom.xml and register the Broadleaf Upgrade 3.0.0 recipe.

    <plugin>
      <groupId>org.openrewrite.maven</groupId>
      <artifactId>rewrite-maven-plugin</artifactId>
      <version>6.45.0</version>
      <configuration>
        <activeRecipes>
          <recipe>com.broadleafcommerce.Upgrade_3_0_0</recipe>
        </activeRecipes>
      </configuration>
      <dependencies>
        <dependency>
          <groupId>com.broadleafcommerce.microservices</groupId>
          <artifactId>broadleaf-rewrite</artifactId>
          <version>1.0.0-GA</version>
        </dependency>
      </dependencies>
    </plugin>

Also, add a new repositories element & pluginRepositories element, if one does not already exist, in the same pom.

    <repository>
      <id>codegenome</id>
      <url>https://artifacts.codegenomeproject.org/maven</url>
    </repository>
    <pluginRepository>
      <id>codegenome</id>
      <url>https://artifacts.codegenomeproject.org/maven</url>
    </pluginRepository>

To view the OpenRewrite recipes available, you can run mvn rewrite:discover.

Before modifying any files, you can execute a dry run with mvn rewrite:dryRun. OpenRewrite will scan your project and generate a detailed report showing exactly what changes will be made, along with patch diff files under target/rewrite/.

Once you have reviewed the dry-run diffs and confirmed the planned refactorings are correct, run the active recipe train to modify your source files in-place. To execute the recipes listed in the activeRecipes section of the rewrite plugin, run mvn rewrite:run.

More information on running the Broadleaf external module for Open Rewrite can be found in their documentation.

Spring Boot 4

Background

For Broadleaf’s 3.0.0 release, which upgrades the platform from Spring Boot 3.5.15 to Spring Boot 4.1.0 (along with Spring Security 7 and Hibernate 7), clients should be aware of several important architectural, dependency, and configuration changes.

Migration

Broadleaf provides an automated upgrade script (com.broadleafcommerce.Upgrade_3_0_0) via the rewrite-maven-plugin to automate the heavy lifting. See the migration notes above on setting up the rewrite plugin.

  • Updates for Spring dependencies, such as OAuth2 & Web Starters

    • Spring libraries such as spring-boot-starter-oauth2-client and spring-boot-starter-web have been renamed.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

  • Migrates spring-boot-starter-aop to spring-boot-starter-aspectj

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

  • Updates legacy Mock annotations to the new standard replacements

    • i.e. updates annotations, such as @MockBean → @MockitoBean

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

  • Automatically re-routes relocated core Spring Boot classes to their modern classpath locations. See the below table for a sample list of relocated classes used in the Broadleaf framework.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

Class Original Package New Package

JpaProperties

org.springframework.boot.autoconfigure.orm.jpa

org.springframework.boot.jpa.autoconfigure

HibernateProperties

org.springframework.boot.autoconfigure.orm.jpa

org.springframework.boot.hibernate.autoconfigure

HibernatePropertiesCustomizer

org.springframework.boot.autoconfigure.orm.jpa

org.springframework.boot.hibernate.autoconfigure

HibernateSettings

org.springframework.boot.autoconfigure.orm.jpa

org.springframework.boot.hibernate.autoconfigure

DataSourceAutoConfiguration

org.springframework.boot.autoconfigure.jdbc

org.springframework.boot.jdbc.autoconfigure

EntityManagerFactoryBuilder

org.springframework.boot.orm.jpa

org.springframework.boot.jpa

TransactionManagerCustomizers

org.springframework.boot.autoconfigure.transaction

org.springframework.boot.transaction.autoconfigure

JpaRepositoriesAutoConfiguration

org.springframework.boot.autoconfigure.data.jpa

org.springframework.boot.data.jpa.autoconfigure

SpringBeanContainer

org.springframework.orm.hibernate5

org.springframework.orm.jpa.hibernate

LiquibaseProperties

org.springframework.boot.autoconfigure.liquibase

org.springframework.boot.liquibase.autoconfigure

  • Resolves package-shifting issues for slice-based tests by safely injecting required slice configurations. See the below table for a list of test slices affected.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

Test Slice Original Package New Package

@AutoConfigureMockMvc

org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc.imports

org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc.imports

@AutoConfigureWebMvc

org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureWebMvc.imports

org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureWebMvc.imports

@AutoConfigureDataJpa

org.springframework.boot.test.autoconfigure.orm.jpa.AutoConfigureDataJpa.imports

org.springframework.boot.data.jpa.test.autoconfigure.AutoConfigureDataJpa.imports

Refer to the Spring Boot 4.0 and 4.1 release notes for the complete documentation. Since it was a major upgrade for Spring, the majority of the changes will be found in the 4.0 upgrade guide.

Spring Cloud

Background

Spring Cloud 5.0 (2025.1.2) is included as part of Spring Boot 4.1 dependencies.

Migration

  • The legacy spring-cloud-starter-parent Maven parent pom has been completely removed from the ecosystem. If this artifact was set as a parent, it must migrate to use the standard Spring Boot parent.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

Full release notes for Spring Cloud 2025.1.2 can be found in their Oakwood release blog as well as in their official release notes page.

Spring Security

Background

Spring Security 7.1 is included as part of Spring Boot 4.1 dependencies.

Migration

  • In Spring Security 7.0, the legacy configuration registration method .apply() has been completely removed in favor of the standardized, lambda-friendly .with() builder.

    • Migrate all DSL registrations to use http.with() paired with a customizer:

      • Before: http.apply(resourceSecurity());

      • After: http.with(resourceSecurity(), Customizer.withDefaults());

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

  • The method signatures for AbstractHttpConfigurer initialization have been cleaned up and simplified in Spring Security 7.0.

    • The overridden init(HttpSecurity) and configure(HttpSecurity) methods on classes extending AbstractHttpConfigurer (such as ResourceSecurityDsl or other custom filters) no longer declare throws Exception in their base signatures.

    • Remove the throws Exception clause from your custom configurers to match the base signatures

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

  • Spring Security 7.0 has completely removed MvcRequestMatcher and its dependency on HandlerMappingIntrospector to simplify and speed up servlet-path matching.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

Refer to the Spring Security 7.1 release notes for the complete documentation.

Hibernate

Background

Hibernate 7.4.1.Final is included as part of Spring Boot 4.1 dependencies.

Migration

  • Hibernate 7 enforces strict specification checks on Jakarta Persistence 3.2 cascade and flush cycles.

    • Attempting to persist or merge an entity tree where child collections contain references to unsaved (transient) objects is now strictly blocked and throws TransientObjectException.

    • Broadleaf introduced TransientReferenceResolvingInterceptor and its Customizer in CommonJpaAutoConfiguration to safely intercepts these cascades, resolve transient primary keys, and auto-persist them before Hibernate’s state validation block triggers.

  • Hibernate 7 has modernized its Service Provider Interface (SPI) for registering custom identifier generators.

    • Registering identifier generators (like Broadleaf’s default UlidIdentifierGenerator) via traditional hibernate property mappings is deprecated.

    • UlidIdentifierGenerator and UlidIntegrator are now registered natively using Hibernate 7’s StrategyRegistrationProvider and ServiceContributor SPI files.

  • In Hibernate 7, a number of annotations were removed or migrated. For example, the legacy @org.hibernate.annotations.Where annotation (which had been deprecated) has been completely removed from the library.

    • Any soft-delete or multi-tenant domain models relying on @Where to filter out records (e.g. @Where(clause = "ARCHIVED = 'N'")) will fail to compile. All usages should be replaced with the modern @SQLRestriction annotation. Note that the attribute name has changed from clause to value.

      • Before: @Where(clause = "ARCHIVED = 'N'")

      • After: @SQLRestriction("ARCHIVED = 'N'")

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring for @org.hibernate.annotations.Where globally. The full list of affected annotations can be found in the Hibernate 7.0 migration guide.

Lombok

Background

When upgrading to Spring Boot 4.1, Lombok is upgraded to 1.18.46. This introduces a critical behavioral change that directly affects REST payload serialization/deserialization for custom domain objects, DTOs, and API projections.

Migration

  • Starting in version 1.18.40, Lombok reversed its default behavior and no longer automatically copies Jackson annotations (such as @JsonProperty, @JsonIgnore, @JsonInclude, or custom Jackson validators) from private fields to their generated getter and setter methods. Fields annotated with @JsonIgnore might start appearing in your JSON responses because Jackson reads the unannotated, generated getter method instead of the private field. Custom JSON field mappings (e.g., @JsonProperty("customer_id") on a field customerId) will fail to map during deserialization, resulting in null values because the generated setter lacks the annotation.

    • The previous behavior should be explicitly restored by updating the project’s root configuration to include the following property: lombok.copyJacksonAnnotationsToAccessors = true. If you do not have a lombok.config file in your project root, one should be created and with this line to ensure global compatibility across all your custom modules.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 adds this annotation property to the lombok.config file in your project root (and creates a new lombok.config in your project root if one does not already exist).

  • Lombok has tightened its handling of @Builder and default field values.

    • If you use @Builder on a class with initialized field variables (e.g., private String status = "ACTIVE";), the builder pattern will bypass this default initialization unless explicitly instructed.

    • If your DTOs or domain models rely on default assignments while using @Builder, ensure you annotate those specific fields with @Builder.Default

For more details, see the Lombok changelog.

Jackson2

Background

Migrating from Jackson 2 to Jackson 3 introduces major breaking changes in package names, annotations, and API signatures. Broadleaf’s decision to remain on Jackson 2 instead of migrating to Jackson 3 during the 3.0.0 (Spring Boot 4.1) upgrade is driven by highly strategic engineering, risk-mitigation, and backward-compatibility factors. When the Broadleaf framework migrates to Jackson 3, all custom client projects, extension modules, SDKs, and third-party integrations will have to migrate to Jackson 3 annotations (com.fasterxml.jackson3 namespaces) simultaneously. To reduce the risk for clients adopting the Broadleaf 3.0.0 release, we’ve chosen to remain on Jackson 2 since it is still the industry standard.

Migration

  • Since Spring Jackson properties have changed property pathing, we’ve added an EnvironmentPostProcessor which will bridges standard Spring Boot Jackson properties, JacksonCompatibilityEnvironmentPostProcessor.

    • Dynamically bridges standard Spring Boot {@code spring.jackson.*} properties to {@code spring.jackson2.*} counterparts when Jackson 2 is the preferred JSON mapper.

    • For project hygiene, you can update your spring.jackson.* properties to correct to the new pathing spring.jackson2.*.

Solr

Background

As part of the upgrade to Spring Boot 4.1, Solr (and its core SolrJ dependency) has been upgraded to SolrJ 10. Full migration notes can be found on the Solr Upgrade Guide.

Migration

  • In SolrJ 10, the core solr-solrj dependency has been stripped of its transitive third-party dependencies (like ZooKeeper, Jetty HTTP, and Streaming Expressions) to make the library leaner.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 adds the solr-solrj-jetty dependency only where required.

  • To avoid generic/vague names (like Http2), SolrJ 10 has renamed all client implementations that are backed by the Jetty HTTP Client and moved them to a dedicated .jetty subpackage. As part of this the historical HttpSolrClient based on Apache HttpClient 4.x has been completely removed in SolrJ 10.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 replaces the org.apache.solr.client.solrj.impl.Http2SolrClient with org.apache.solr.client.solrj.jetty.HttpJettySolrClient.

  • Several core SolrJ classes have been relocated to better align with functional package scoping. The central query builder, org.apache.solr.client.solrj.SolrQuery, has moved packages to org.apache.solr.client.solrj.request.SolrQuery.

    • Broadleaf’s automated OpenRewrite recipe for 3.0.0 performs this refactoring globally.

  • The handling of client endpoint URLs has become strictly enforced to prevent routing issues.

  • The extension mechanism for customizing the underlying HTTP client sockets has been reorganized.

Refer to the Solr 10 upgrade notes for the complete documentation.

Apache Camel

Background

As part of the upgrade to Spring Boot 4.1, Apache Camel has been upgraded from 4.14.4 to 4.20.

Migration

  • To prepare for future parser strictness, Camel 4.20 has deprecated and removed several legacy binary operators containing spaces in their names.

  • The declarative handling of specific Data Types has been extracted into its own Enterprise Integration Pattern (EIP).

  • As part of alignment with modern Kubernetes and Service Mesh environments, the legacy camel-cloud module has been completely retired.

  • Camel’s telemetry and microservices tracing modules have been refactored to align with Spring Boot 4’s platform standards.

  • Upgrading Camel to 4.20 brings in the modern Apache Kafka Client 4.2.0 library.

  • The Camel engine has tightened its resource parsing guidelines during application startup.

  • To prevent remote code execution (RCE) vulnerabilities and protect runtime state, the execution component has been secured.

Other

  • You should consider adding the following Kafka logging property to prevent being spammed with benign WARN logs.

    • Set org.apache.kafka.clients.consumer.internals.OffsetFetcherUtils to ERROR.

    • See KAFKA-20449 for the details on why this is benign and suppression is expected.

  • GatewayServerPropertyRelocationApplicationContextInitializer converts a number of properties to their compatible format in the latest gateway version, and are emitted in the gateway logs when this occurs.

    • As part of project hygiene, these properties should be found via the logs & updated in the project. Properties to look for include:

      • spring.cloud.gateway.* configuration namespace has been remapped to spring.cloud.gateway.server.webflux.*, except for properties under spring.cloud.gateway.server.* which are not remapped.

Troubleshooting Guide

If you find that custom Metadata i18n labels are not being loaded, verify that the custom MetadataMessagesBasename classes are either annotated with @Component and scanned, or that they are registered as Spring Beans. The classes should be annotated with @Component by AddSpringComponentToMetadataMessages recipe if you’ve run the Broadleaf 3.0.0 OpenRewrite scripts.

If you notice a number of WARN logs from Kafka with Not updating high watermark for partition {} as it is no longer assigned, make sure to set the logging level of OffsetFetcherUtils to ERROR as noted in the "Other" migration section.

New and Notable Changes

Improved Support for $0 Checkouts and $0 Authorizations

  • Checkout Payment Expectations API: Broadleaf now provides a consolidated expectations endpoint (GET /checkout/{cartId}/payment-expectations) returning both immediate and future checkout payment expectations (paymentRequired and savedPaymentMethodRequired) alongside available payment method options. This guides the frontend on exactly what payment interactions are required to complete checkout, unifying presentations for standard and subscription carts.

  • Support for $0 Checkouts: Broadleaf now better supports zero-dollar checkout scenarios (e.g., promotional carts, free gifts, subscription trials, or postpaid subscriptions) with clear property-driven controls. By default, a $0 cart does not require a payment, unless the broadleaf.cartoperation.service.checkout.payment-required-for-zero-cart-total property is explicitly enabled.

  • $0 Authorizations for Saved Payment Method Registration: When checking out with zero-dollar cart totals (such as free trials) and saving a card for future use, Broadleaf can safely coordinate a $0.00 Authorize (account verification) transaction with the payment gateway to validate the card before tokenization and preservation. On success, the token is stored in the BLC_SAVED_PAYMENT_METHOD wallet table.

Added Support for Adyen Web v6

Added support for the Adyen Web v6 with supporting changes in broadleaf-adyen 2.0.0, broadleaf-payment-transaction-services 3.0.0 and broadleaf-cart-operation-services 3.0.0 services. The React library SDK, @broadleaf/adyen-payment-services-react 1.5.1, contains additional changes to support Adyen Web v6 as well.

Stripe Version Updates

The Stripe Java SDK in broadleaf-stripe 3.0.0 was updated from 29.1.0 to 32.1.0. This requires the Stripe API version 2026-04-22.dahlia

Notable Bug Fixes

Webclient Access Token Resolution for Background and Async Calls
Fixed Spurious Lock-Failure Events from a TOCTOU Race in the ZooKeeper Lock Monitor

Frontend Compatibility and Release Notes

Unified Admin Release Notes for 2.1.0

Important
  • Requires Node ≥22, React ≥19, React Router ≥6

Features & Enhancements

Introduced ThemeProvider and update existing CSS
Tip
This provides Runtime Admin Theming Support.

We introduced a robust, CSS-variable-based runtime theming engine for the Admin application. The foundation is highly extensible, memory-leak safe, and 100% backwards compatible for downstream apps that do not wish to utilize theming.

Key Features
  • Dynamic Theming Engine: Introduced a new <ThemeProvider> that programmatically accepts a nested Theme object and dynamically injects scoped CSS variables (e.g., --tw-colors-primary-500) into the document :root.

  • AdminProvider Integration: Integrated the new <ThemeProvider> directly into <AdminProvider>, allowing the theme to be passed cleanly as a top-level prop without disturbing existing context setups.

  • Tailwind Plugin & Config Upgrades:

    • Updated @broadleaf/admin-tailwindcss to utilize safe CSS variable fallbacks across all branding colors, mapping gray to the secondary scale and red to the danger scale.

    • Added a new adminTheme Tailwind plugin to dynamically generate shell-specific styling utilities (tw-shell-nav- and tw-shell-header-).

  • Shell Chrome Migration: Surgically migrated legacy SCSS from structural components like <Navigation> and <Header> to leverage the new theme-aware Tailwind classes instead of hardcoded background colors.

  • SASS Color Function Removal: Removed legacy SASS functions (like lighten() or color-yiq()) from older components in @broadleaf/admin-style, allowing them to safely compile and consume the new dynamic CSS variable fallbacks seamlessly.

How it works

You can now theme the admin by supplying a theme prop to your <AdminProvider>:

import { AdminProvider, Theme } from '@broadleaf/admin-components';

const brandTheme: Theme = {
  colors: {
    primary: { /* 50-950 scale */ },
    secondary: { /* 50-950 scale (defaults to Tailwind 'gray') */ },
    danger: { /* 50-950 scale (defaults to Tailwind 'red') */ },
    link: '#3b82f6',
    linkHover: '#2563eb'
  },
  shell: {
    nav: { bg: '#111827', text: '#d1d5db', active: '#1f2937' },
    header: { bg: '#1f2937' }
  }
};

const App = () => (
  <AdminProvider theme={brandTheme}>
    {/* Admin Routes */}
  </AdminProvider>
);
Cleaned up BaseLayout to be only concerned with layout

We introduced a significant architectural cleanup to the <BaseLayout> component and its associated global elements. The goal of this refactor is to improve component cohesion, enable granular overrides for downstream consumers, and future-proof the codebase for modern React paradigms like Server Components (RSC).

1. Global Context Provider Extraction

The <BaseLayout> component was previously cluttered with global context providers and structural modals. These have been cleanly extracted: * ToastContainerContextProvider: Extracted the toast state logic into its own dedicated context provider. * ConfirmModalContextProvider: Extracted the confirmation modal state. The actual <ConfirmModal /> element is now rendered natively inside this provider (after {children}) rather than hanging off the <BaseLayout>. * Both of these new providers have been injected into the default array of the <GlobalProviders> component, ensuring they are available across all routes without cluttering the layout tree.

2. Component Overrides

To provide maximum flexibility for downstream applications, the following global UI components have been wrapped in enableOverrides, allowing them to be swapped out completely: * ConfirmModal * ToastContainer * TransitionToasts * Toast

3. Flat Exports for <ScrollView> (Tree-shaking & RSC Support)

The <ScrollView> component was previously exposing its sub-components via dot-notation (e.g., ScrollView.Header). This has been refactored to use named flat exports (ScrollViewHeader, ScrollViewContent, ScrollViewFooter). * Why? Flat exports drastically improve tree-shaking by allowing bundlers to eliminate dead code. Furthermore, in React Server Component (RSC) environments (like Next.js App Router), dot-notation forces the entire object tree to be treated as a Client Component. Flat exports allow for granular server/client boundary definitions. * Backwards Compatibility: The dot-notation accessors have been kept intact on the default export but are marked as @deprecated.

4. Header & AuthService Cohesion

Removed AuthService dependency injection from <BaseLayout>. The <Header> component now directly imports and handles its own logout and changePassword logic, resulting in a much dumber, cleaner layout wrapper.

Deprecations

The following properties/accessors have been marked as @deprecated and should be migrated to their new paradigms: * ScrollView.Header → Use named import ScrollViewHeader * ScrollView.Content → Use named import ScrollViewContent * ScrollView.Footer → Use named import ScrollViewFooter * HeaderProps.onLogoutClick → Handled internally by <Header> * HeaderProps.onChangePasswordClick → Handled internally by <Header>

Enabled decoupling the rendering of Admin NavMenuItems from Routes
Important
Requires Admin Navigation Services 3.0.0, which is part of Release Train 3.0.0 but also compatible with Release Train 2.3.1 if upgrading independently.

We introduced the ability for Menu Items defined in the Admin Navigation Service to be rendered independently of any Metadata Routes. They can now be filtered by their own predicates in a similar fashion to Routes, e.g., only return this item in a Tenant context rather than an Application. They can also define the security scopes for whether the user has access to see them similar to Routes, e.g., a user needs READ_PRODUCT to see this item.

  • Introduced VITE_MAIN_NAVIGATION_BEHAVIOR. To trigger the new behavior set it to ROUTE_INDEPENDENT

  • Introduced new hook, useSecureNavMenuItem to check whether the user has access to a NavMenuItem either using the old behavior based on a matching route or the new behavior checking the scopes of the NavMenuItem directly

  • Added debug logging to help diagnose why menu items appear or don’t appear

    • Enable using VITE_

  • Fixed lack of typing and documentation for various pieces of the UserOperationContext and related reducers to address technical debt in this area.

  • Deprecated useGetMatchingRoutesWithAccess and useHasMatchingRoute. Instead, useSecureNavMenuItem should be used as it returns both whether the module or submodule has access as well as the map of URL/path for each submodule to whether it has access specifically.

  • Removed access check in NavSection as the access checks for each section has already been done by the time the component is rendered. Instead filtration of the submenu items is explicitly done before rendering any NavSections.

Modularized Metadata Route Rendering & Shell Extraction

We decoupled the application routing logic from the structural UI shell. Previously, AdminApp was tightly coupled to both the layout and the React Router definition, making it difficult for downstream consumers to inject custom routing or entirely replace the application shell.

1. Extraction of AdminRoutes

The react-router-dom <Routes> definition and the metadata-driven route grouping logic have been extracted from AdminApp into a new, standalone <AdminRoutes> component. * This component is purely responsible for consuming the ComponentRouterContext and dynamically rendering the ComponentRouteResolver for each path. * New Injection Slots: <AdminRoutes> now accepts beforeRoutes and afterRoutes props (typed as ReactNode). This allows downstream consumers to seamlessly inject custom static routes alongside the dynamically generated metadata routes without interfering with the 404 catch-all logic.

2. Shell Injection via AdminApp (Slot Pattern)

The <AdminApp> component has been refactored to fully support the slotted children pattern. * If children are provided to <AdminApp>, it now acts strictly as a lifecycle/routing wrapper and renders the provided children directly. This explicitly supports downstream architectures (such as Capibara) that need to inject and manage their own UI shell layout. * Backwards Compatibility: If no children are provided, <AdminApp> gracefully falls back to the original Out-Of-The-Box (OOB) behavior, rendering <BaseLayout><AdminRoutes /></BaseLayout>.

3. Routing Context Lifecycle Audit

Verified that the ComponentRouterContext population is strictly decoupled from the UI shell rendering. The ComponentRouter gateway successfully blocks rendering via the GatewayProvider until all metadata routes are fetched and the context is fully hydrated. This guarantees that any custom shell or route definition passed into the application will have immediate, synchronous access to the fully resolved routing state.

Optimized fulfillment inventory data loading
  • Optimize fulfillment inventory loading to reduce redundant requests due to component re-renders

  • Fixed an issue where an incorrect quantity value was used during fulfillment

  • Updated useFulfillmentInventory to replace the skuCodes parameter with fulfillments

  • Separated inventoryQuantities from StatusChangeContext into a new InventoryContext

    • Note: A withInventoryContext provider is available to allow existing components taking in an inventoryQuantities prop to continue working as previously

Enhanced Extensibility of the Application Selector Component

Moved internal logic of <ApplicationSelector> into a useApplicationSelector hook, encapsulating the state, fetch orchestration, authorization logic, and event callbacks for selecting the active application context. This allows easier customization of the <ApplicationSelector> in the nav sidebar.

import useApplicationSelector from '@broadleaf/admin-components/dist/view/components/ApplicationSelector/hooks/useApplicationSelector';

export const CustomApplicationSelector = () => {
  const {
    application,
    applications,
    isLoading,
    formattedMessages,
    handleApplicationChange,
    queueFetchApplications,
    resolveApplicationsWithQuery
  } = useApplicationSelector();

  return <>{/* Custom Selector Component */}</>;
}
Added support for overriding out-of-box Application and Metadata splash screens

These components are rendered while resolving the Application and Metadata Routes or when those requests encounter an error. This covers the following:

  • App-Level Error and Loading Splash (AppSplash)

    • Registered as blAppSplash

    • Subcomponents that support overrides

      • blAppSplashOverlay for AppSplashOverlay

      • blAppSplashBackground for AppSplashBackground

      • blAppSplashLoading for AppSplashLoading

      • blAppSplashError for AppSplashError

      • blAppSplashErrorNotFound for AppSplashErrorNotFound

      • blAppSplashErrorUnauthorized for AppSplashErrorUnauthorized

      • blAppSplashErrorServer for AppSplashErrorServer

      • blAppSplashErrorUnknown for AppSplashErrorUnknown

      • blAppSplashErrorTemplate for AppSplashErrorTemplate

  • Route/Metadata-Level Error and Loading Splash (ComponentRouteSplash)

    • Registered as blComponentRouteSplash

      • Subcomponents that support overrides

  • blTransitionComponentRouteSplashOverlay for TransitionComponentRouteSplashOverlay

  • blComponentRouteSplashOverlay for ComponentRouteSplashOverlay

  • blComponentRouteSplashBackground for ComponentRouteSplashBackground

  • blComponentRouteSplashLoading for ComponentRouteSplashLoading

  • blComponentRouteSplashError for ComponentRouteSplashError

  • blComponentRouteSplashErrorNotFound for ComponentRouteSplashErrorNotFound

  • blComponentRouteSplashErrorUnauthorized for ComponentRouteSplashErrorUnauthorized

  • blComponentRouteSplashErrorServer for ComponentRouteSplashErrorServer

  • blComponentRouteSplashErrorUnknown for ComponentRouteSplashErrorUnknown

  • blComponentRouteSplashErrorTemplate for ComponentRouteSplashErrorTemplate

Modularized the Main Navigation Components

We refactored the monolithic <Navigation> component into a set of highly granular, composable layout building blocks backed by a centralized context provider. These changes enable users to build entirely custom navigation shells, e.g., top-level headers, custom mobile sidebars, off-canvas menus, while preserving the exact out-of-the-box (OOB) Admin navigation layout and DOM structure. Users can now leverage the NavigationContextProvider alongside individual <Nav*> layout components to compose entirely custom sidebar or header shell layouts without needing to manually lift state or drill props. The default <Navigation> component continues to function identically without requiring downstream changes.

Key Changes:
  • State Extraction: Extracted all data fetching logic, cache interactions, and UI state (menuOpen, menuCollapsed, isHovered, filter) from the main component into a new NavigationContextProvider.

  • Granular Layout Building Blocks: Created atomic <Nav*> wrappers for each semantic layout section (<NavWrapper>, <NavMain>, <NavHeader>, <NavMenuWrapper>, <NavErrorSection>, <NavFooter>).

  • Layout V2 Alignment: Removed legacy branching logic from the new navigation wrappers, standardizing entirely on Layout V2 semantic HTML tags (<nav>, <section>, <header>, <aside>, <footer>).

  • OOB Preservation: Rebuilt the default <Navigation> component as a strict composition of these new atomic layout wrappers to ensure no regressions in default styling or DOM behavior.

  • Expanded Exports: Exported all atomic navigation blocks, types, contexts, and hooks to the root index layer for downstream consumption. All new wrappers are fully extensible via enableOverrides.

Example Custom Navigation
import {
  NavWrapper,
  NavMain,
  NavErrorSection,
  NavError,
  NavMenu,
  NavFooter,
  NavCollapser,
  NavMobileToggle,
  NavigationContextProvider,
  useNavigationContext
} from '@broadleaf/admin-components/dist/view';

export const CustomNavigation: React.FC = () => {
  const {
    isError,
    isSuccess,
    menuCollapsed,
    menuOpen,
    isHovered,
    filter,
    setFilter,
    setMenuOpen,
    setMenuCollapsed,
    setIsHovered,
    modules,
    fetchNavigation
  } = useNavigationContext();

  return (
    <NavigationContextProvider>
      <NavWrapper>
        <NavMain>
          {isError && (
            <NavErrorSection>
              <NavError fetchNavigation={fetchNavigation} />
            </NavErrorSection>
          )}

          {isSuccess && (
            <NavMenu
              filter={filter}
              modules={modules}
              setFilter={setFilter}
              setMenuOpen={setMenuOpen}
              menuCollapsed={menuCollapsed && !isHovered && !menuOpen}
            />
          )}
        </NavMain>

        <NavFooter>
          <NavCollapser
            menuCollapsed={menuCollapsed}
            setMenuCollapsed={val => {
              setMenuCollapsed(val);
              setMenuOpen(!val);
              setIsHovered(false);
            }}
          />
        </NavFooter>
      </NavWrapper>

      <NavMobileToggle
        menuOpen={menuOpen}
        setMenuOpen={val => {
          setMenuOpen(val);
          setMenuCollapsed(!val);
        }}
      />
    </NavigationContextProvider>
  );
};
Introduced support for an admin user to see and copy generated API key values
Important
Requires Metadata 2.0.9-GA (Release Trains 2.3.1, 2.2.3, 2.1.7) or 3.0.0-GA (Release Train 3.0.0).

Introduces support for a "One-Time Success Response" feature, providing a highly integrated framework for safely displaying sensitive, transient API response payloads (such as generated API keys) natively within the Admin UI. This feature looks for successResponse handler configurations on metadata actions. Upon successful form submission, it dynamically hooks-into the onSubmit promise resolution to present the read-only payload values in a slide-over modal or inline-replacement view before the transient state is discarded. It also includes setting a string field as "copyable" that triggers a "copy" action to render with the field and makes the field read-only.

While the successResponse handler can be configured for any action, a helper is included for Entity Create and Update views: submitSuccessResponse. This passes the configuration function down to the appropriate submit action in the metadata.

Example Usage of submitSuccessResponse
Views.entityViewCreate()
    .submitSuccessResponse(response -> response
        .title("Key Generated Successfully")
        .message("Copy this secure key now. It will not be shown again.")
        .displayMode(SuccessResponse.DisplayMode.REPLACE_CONTENT)
        // Renders the API response's 'secret' property as a click-to-copy string field
        .addField("secret", Fields.string()
            .copyable()
            .label("API Key")));
LocalStorageCache Enhancements
  • Added an error boundary in LocalStorageCache to gracefully handle failures to persist the in memory cache to local storage.

  • Modified the in-memory cache used by LocalStorageCache to act as an LRU map with additional byte-size-based eviction that can be set with LocalStorageCache#maxByteSize.

New Configuration Options

Introduce environment properties to fine-tune the behavior of different LocalStorageCache instances across the application. This enables configuring time-to-live (TTL), maximum byte size limits, and local/session storage enablement dynamically.

The following configuration properties have been introduced (formatted as VITE_ prefixed, uppercase, snake_case dotenv variables) with their respective default values:

  • Authentication Cache:

    • VITE_AUTH_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_AUTH_CACHE_TTL: Default: 600000, 10 minutes

    • VITE_AUTH_CACHE_LOCAL_ENABLE: Default: true

    • VITE_AUTH_CACHE_SESSION_ENABLE: Default: true

  • Authentication State Cache:

    • VITE_AUTH_STATE_CACHE_MAX_BYTE_SIZE: Default: 0, not restricted as it is not persisted to local or session storage

    • VITE_AUTH_STATE_CACHE_TTL: Default: 0, no TTL

  • Authentication Transaction Storage:

    • VITE_AUTH_TRANSACTION_STORAGE_MAX_BYTE_SIZE: Default: 0, not restricted as it is not persisted to local or session storage

    • VITE_AUTH_TRANSACTION_STORAGE_TTL: Default: 0, no TTL

  • Metadata/Component Router Cache:

    • VITE_METADATA_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_METADATA_CACHE_TTL: Default: 1800000, 30 minutes

    • VITE_METADATA_CACHE_LOCAL_ENABLE: Default: true

    • VITE_METADATA_CACHE_SESSION_ENABLE: Default: true

  • Catalog Cache:

    • VITE_CATALOG_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_CATALOG_CACHE_TTL: Default: 600000, 10 minutes

    • VITE_CATALOG_CACHE_LOCAL_ENABLE: Default: true

    • VITE_CATALOG_CACHE_SESSION_ENABLE: Default: true

  • Customer Context Cache:

    • VITE_CUSTOMERCONTEXT_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_CUSTOMERCONTEXT_CACHE_TTL: Default: 600000, 10 minutes

    • VITE_CUSTOMERCONTEXT_CACHE_LOCAL_ENABLE: Default: true

    • VITE_CUSTOMERCONTEXT_CACHE_SESSION_ENABLE: Default: true

  • Sandbox Cache:

    • VITE_SANDBOX_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_SANDBOX_CACHE_TTL: Default: 600000, 10 minutes

    • VITE_SANDBOX_CACHE_LOCAL_ENABLE: Default: true

    • VITE_SANDBOX_CACHE_SESSION_ENABLE: Default: true

  • Search Settings Cache:

    • VITE_SEARCHSETTINGS_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_SEARCHSETTINGS_CACHE_TTL: Default: 600000, 10 minutes

    • VITE_SEARCHSETTINGS_CACHE_LOCAL_ENABLE: Default: true

    • VITE_SEARCHSETTINGS_CACHE_SESSION_ENABLE: Default: true

  • Tenant Cache:

    • VITE_TENANT_CACHE_MAX_BYTE_SIZE: Default: 1048576, 1MB

    • VITE_TENANT_CACHE_TTL: Default: 600000, 10 minutes

    • VITE_TENANT_CACHE_LOCAL_ENABLE: Default: true

    • VITE_TENANT_CACHE_SESSION_ENABLE: Default: true

Miscellaneous
  • Introduced a dedicated new component specifically designed to handle the toggle states of Placeholder Characteristics, registered as PLACEHOLDER_ENUM.

  • Added support to ActionListGridCreateAction to configure the modal max size with the modalSize attribute.

    • Supported values:

      • sm: 32rem (512 pixels)

      • lg: 48rem (768 pixels)

      • xl: 72rem (1152 pixels)

  • Refactored the ToggleSwitch component for improved keyboard accessibility and WAI-ARIA compliance

    • Addressed:

      • Limited Keyboard Shortcuts: Previously, keyboard support was restricted to a simple spacebar toggle which acted as a circular index cycler. This made navigating back and forth through multiple choices laborious and did not meet standard accessibility expectations.

      • Double Screen-Reader Announcements: Because both the underlying input element and the visible label/button were interactive and present in the DOM tree, screen readers frequently exposed redundant or competing focused states.

      • Improper Aria Roles: The selection element lacked programmatic group context and checked-state markers, making color changes the only indicator of active options.

  • Updated EntityView useContextParams to take the React Router match param into account to share the behavior used by EntityLongView to determine which form within the view is active.

  • Added support for using EntityViews as targets of slide-over form actions (as opposed to modal forms) like ActionListGridUpdateViewAction that can target an existing View to render when invoked.

  • Added support for overriding some subcomponents of ListGrids

    • blListGridHeaders for ListGridHeaders

    • blActionListGridQueryFilter for ActionListGridQueryFilter

  • Ensured that empty EntityView form tabs are hidden automatically

Bug Fixes

  • Fixed Admin Forms Initializing with Unsaved Changes

  • Fixed the ApplicationSelector disappearing when filtering by a query with no matching results

  • Fixed Workflow view not showing error page for invalid workflows (e.g., navigating to a URL for a workflow that doesn’t exist).

  • Fixed possibility of QuotaExceededError for CustomerContextCache used by the Customer Context Selector component in the Tenant-level admin.

    • Only cache the IDs and Names of Applications stored as these are the only ones relevant for admin functionality

  • Fixed Customize Form actions not opening modals to add Field or Group Augmentations when clicking on the button icon rather than the button background. Now clicking on either will open the modal.

  • Fixed management of placeholder enum characteristics to handle and send valid API request/response payloads.

    • Created a PlaceholderEnumField component (and corresponding internationalized messages) specifically designed to manage placeholder enum characteristics. This avoids polluting the general-purpose select elements with complex active/inactive toggling logic.

    • Updated the conditional resolver in Field.tsx to automatically route characteristic placeholders to this new field type.

  • Fixed runtime TypeError, metadata?.actions?.find is not a function, that occurs in the EntityLongView component for auditable entities (set in metadata with .auditable("ENTITY_TYPE").

    • Logic now ensures that standard metadata actions collections always maintain a strict Array structure, preventing downstream components and hooks from crashing on native array method calls.

  • Fixed regression in 2.0.2: Added missing CSS styles resulting from removal of Bootstrap dependency.


Unified Admin Release Notes for 2.0.1

Features & Enhancements

Tip
Security and maintenance release. Includes all changes for 2.0.0.
Support Retail Delivery Fee Refunds
  • Introduced the ability to optionally request the refund of order fees (esp. Retail Delivery Fees) when requesting creating a ReturnAuthorization.

Introduced Badges for Tile and List Grids

Added support for badges in both Tile and List grid views within the Unified Admin interface. This enhancement allows developers to define badges that can be displayed on individual items in these grids, providing additional context or status information at a glance.

Badges are rendered through BadgeColumn and BadgeTileComponent components, which can be configured in the metadata for the respective grid views. This feature enhances the visual representation of data and can be particularly useful for highlighting important attributes or statuses of items in a list or tile format. These badge components are specifically designed to pull from a hydration endpoint based on a field in the entity. The main current use case is to show AdvancedTags associated with a product in product grids.

Introduced a click event handler for links with href attributes matching the pattern modal(modalId). This handler intercepts such clicks and opens a modal dialog with the specified ID, allowing users to interact without leaving the current page. The handler searches both the current metadata and the new global components metadata for the modal definition and opens it if found.

This enhancement’s primary use case is to facilitate the opening of modals from links within a fields' help text, improving user experience by providing context-sensitive information without navigating away from the current view.

Introduced ModalFormView Component

A new ModalFormView component has been added to the Unified Admin interface. This component is designed to render forms within modal dialogs primarily triggered by links in help text or other interactive elements. These modal forms can handle both creation and editing of entities, providing a seamless user experience.

Introduced Divider Form Component

Introduced new Divider form component type to render a horizontal rule between fields. The divider component can be targeted using the .Divider css class. It takes a width attribute, indicating the width of the horizontal rule—default is sm indicating 1px. It also takes a spacing attribute indicating the spacing above and below the rule in rem—default is md indicating 2rem.

Tip
Other values are defined in metadata, see Divider#Width and Divider#Spacing enums.
Introduced CheckboxField Form Component

A new CheckboxField form component type has been added to render a checkbox input. This component is useful for boolean fields, where users want to have a simple checkbox instead of the default toggle switch.

Multi-line Truncation using line-clamp in LargeContentColumn

Multi-line truncation has been made available by including the @tailwindcss/line-clamp dependency in order to access the tw-line-clamp class name. The LargeContentColumn component has been updated to use tw-line-clamp to dictate the number of lines to truncate to for the column display value. The number of lines is expected to be set in the metadata for the colum via the lines attribute, and is set to 1 by default. By default, the available classes are tw-line-clamp-1 to tw-line-clamp-6 (numbers 1 through 6), and tw-line-clamp-none.

Introduced NavMenuFooterMenuItems Sub-Component

An overridable sub-component NavMenuFooterMenuItems has been created for the purpose of containing all menu items and links in the footer of the navigation menu. This sub-component can be overridden by registering a new component in the ComponentRegistrar with the ID blblNavMenuFooterMenuItems. By default, NavMenuFooterMenuItems contains the Developer Settings menu item.

Introduced ModalLinkColumn Component

A new component ModalLinkColumn has been created that renders a column with a link that when clicked, opens a modal containing fields and other components configured through the column metadata. If the ModalLinkColumn metadata has no configured sub-components such fields, groups, or grids, then the link rendered in the column will not open a modal when it is clicked. The modal also displays a Submit button and expects by default that the ModalLinkColumn metadata has a configured SUBMIT endpoint that it will submit the form data to.

This component also supports showing a confirmation message before invoking a "delete" submission action. When a "Delete" action is triggered the first time, this message will be displayed and must be acknowledged before actually invoking the endpoint.

private void example() {
    Externals.grid()
        .addColumn(SHARED_CODE, Columns.modalLink()
            .label("Modal Link Column")
            .addField("field1", Fields.string()
                .label("First Field")
                .order(1000))
            .closeButtonLabel("Close")
            .submitEndpoint(Endpoints.put()
                .uri("/my-endpoint")
                .scope("MY_SCOPE"))
            .submitLabel("Submit")
            .addEndpoint(EndpointTypes.DELETE, Endpoints.delete()
                .uri("/my-endpoint")
                .scope("MY_SCOPE")
                .attribute(ModalFormAction.Attributes.SUBMIT_COLOR,
                        ModalFormAction.SubmitColors.DANGER)
                .attribute(ModalFormAction.Attributes.SUBMIT_LABEL, "Delete")));
}
Introduced FormFieldColumn Grid component
  • Introduced a new FormFieldColumn component that allows for the columns in ActionListGrid to render a form field for inline editing of the row entity.

    • Added a separate form state context to external grids to allow modifying the row’s state inline and submitting updates to a separate endpoint from the main entity.

    • When the user clicks away from a field in the row after making changes, these changes are saved automatically via the update endpoint specified in the grid metadata.

      • Custom column types that seek similar behavior can expect the update submission to be triggered via an onBlur prop passed as a prop

    • Updated the components for the supported field types (BooleanField, DateField, DecimalField, EnumSelectField, IntegerField, LongField, LookupField, MoneyField, PhoneField, and StringField) to support calling the onBlur callback prop when available.

Tip
See the Metadata 2.0.8 release notes for an example of how to configure a grid with this new type of column.

A new click event handler has been added to the admin that listens for anchor tag clicks specifically targeting a href with the pattern href="modal(id)" where id maps to the id of a modal defined in metadata. This allows reusable modals to be defined and triggered from links on multiple pages.

Define a global component ModalView in the metadata
registry.addGlobalComponent("test-modal", Views.modalView()
       .label("Terms and Conditions")
       .addField("testField", Fields.string()
            .label("Test Field")));

...

form.addField("someField", Fields.string()
       .label("some-field.label")
       .hint("some-field.hint-text"));
Add a modal link in a hint text message
some-field.hint-text=Click <a href="modal(test-modal)">here</a> for more info.

The test-modal modal view will be triggered when the link on the hint is clicked.

Support alternative UPDATE endpoint to be configured for EntityView per EntityViewForm

Updated EntityFormView to be able to configure an UPDATE endpoint on the form itself that overrides the parent View’s UPDATE endpoint

Example usage of overriding the UPDATE endpoint per form
UpdateEntityView view = Views.entityViewUpdate()
        .label("Update View")
        .submitUrl("/main-entity", Scopes.SCOPE)
        // ...
        .addForm("separateEntityForm", Views.entityForm()
                .label("Form with External Related Entity")
                .updateUrl("/external-entity", Scopes.EXTERNAL_SCOPE);
Improved support for updating fields within an ExternalFieldGroup

This work was done in support of allowing a fields within an ExternalFieldGroup to be updated using a form’s override, update endpoint where the actual parent entity’s state wasn’t being modified and wouldn’t be returned from the submit endpoint, which was in a different microservice.

  • Added attributes map to Endpoints metadata with relevant helper methods

  • Added Endpoint attribute, responseIsPartialState: Indicates that this endpoint’s response will be a partial state that should be merged with the existing entity state, similar to a PATCH request. This is useful for APIs that do not support PATCH requests directly or that are backing external field groups on a different entity’s form. In those cases, the parent entity state should be merged with the new state returned by the endpoint to maintain the full view state.

  • Fixed Groups not getting their IDs set when using Form#addGroup(String type, Group<?> group)

  • Expanded javadocs on various metadata DSL interfaces

  • Added attribute to ExternalFieldGroup, useParentFormState. Indicates that the state for the fields in this group will be backed by the parent form’s.

  • If false, then a nested form state will be created to hold the field values in isolation from the parent. Note that this currently makes the group effectively read-only.

  • If true, then users should ensure that the field names are prefixed with the Group.getId() since this is used in the admin to ensure that the values do not collide with other fields in the parent form. TransformBody and MappingList can be used to map the form state to the appropriate structure expected by the form or entity submit endpoint (if any) and vice versa.

public void exampleUsage() {
    form.updateEndpoint(Endpoints.post()
            .responseIsPartialState()
            .uri("/dto-endpoint")
            .transformRequest(t -> t.mappings(transformEntityToRequestDto("externalGroupId")))
            .transformResponse(t -> t.mappings(transformEntityFromRequestDto("externalGroupId")))
            .scope("DTO"))
            .addGroup("externalGroupId", getExternalGroup());
}

public static MappingList transformEntityToRequestDto(String groupId) {
    return new MappingList(Arrays.asList(
            Mappings.mapValue("%s.%s".formatted(groupId, "field1"), "field1"),
            Mappings.mapValue("%s.%s".formatted(groupId, "field2"), "field2"),
            Mappings.mapValue("%s.%s".formatted(groupId, "field3"), "field3")));
}

public static MappingList transformEntityFromRequestDto(String groupId) {
    return new MappingList(Arrays.asList(
            Mappings.mapValue("field1", "%s.%s".formatted(groupId, "field1")),
            Mappings.mapValue("field2", "%s.%s".formatted(groupId, "field2")),
            Mappings.mapValue("field3", "%s.%s".formatted(groupId, "field3")));
}
Introduced TenantService to make interactions with Tenant Microservice extensible

This allows the API calls made by the Admin to Tenant Service to be overridden and customized.

  • Refactored code to use new Tenant Service

  • Introduced new TenantServiceContext to allow overrides by passing in custom TenantService object

Example Override:
// 1. Define your custom getApplications function
const customGetApplications = async (
  tenantId: string,
  applicationId: string
) => {
  console.log('Using custom getApplications with customParam!');
  const defaultService = DefaultTenantService;
  const applications = await defaultService.getApplications(
    tenantId,
    applicationId
  );
  // You can modify params or response here, for example:
  // const response = await axios.get(getTenantApplicationsUrl(), {
  //   ...restrictByTenant(tenantId, applicationId),
  //   params: {
  //     active: true,
  //     forward: true,
  //     offset: 0,
  //     size: getTenantApplicationsPageSize(),
  //     customParam: 'my-custom-value' // adding a custom parameter
  //   }
  // });
  return applications;
};

// 2. Create a custom TenantService implementation
const customTenantService: TenantService = {
  ...DefaultTenantService,
  getApplications: customGetApplications
};

// 3. Create a component that uses the TenantService
const MyComponent = () => {
  const tenantService = useTenantService();

  useEffect(() => {
    // Now this will call the custom getApplications function
    tenantService.getApplications('my-tenant-id', 'my-app-id');
  }, [tenantService]);

  return <div>My Component</div>;
};

// 4. Wrap your component with the TenantServiceProvider
const TenantServiceOverride = () => {
  return (
    <TenantServiceProvider service={customTenantService}>
      <MyComponent />
    </TenantServiceProvider>
  );
};

export default TenantServiceOverride;
Removed Bootstrap dependency

The dependency on Bootstrap CSS has been removed from the Unified Admin interface as it is no longer referenced in our styles. If you are relying on Bootstrap for custom code, or this removal results in any styling issues in your implementation, we recommend re-adding it as a dependency and importing it in your custom code to restore the previous styles.

Miscellaneous Improvements
  • Introduced new SimpleComponentTypeRenderer to handle simple components that do not fall into the existing classifiers like Field, Group, External, and View.

  • Allow for top-level menu items (menu items that have a configured URL without child menu items) to be rendered and displayed in the Navigation menu.

    • Additionally, if a parent menu item only has one child menu item visible, the menu items will be flattened so that the child menu item is displayed at the root level and the parent menu item is hidden. This behavior is controlled by the VITE_ENABLE_NAVIGATION_FLATTEN_SINGLE_MENU_ITEM environment variable, which is true by default.

  • Single-value fields were updated to be able to render raw data according to their types. If the field’s metadata has the displayOnly attribute set to true, then the field will be displayed as a plain or raw text representation. There is also a placeholder value to be displayed when there is no value for the field.

    • The following single-value field components can display fields as raw data:

      • BooleanField

      • DateField

      • DecimalField

      • IntegerField

      • LongField

      • MoneyField

      • PhoneField

      • StringField

      • TextAreaField

    • Updated FieldDecorations to not display change highlights for display-only fields

  • Added the ability to "Select All" tiles from the Product Browse Tile Grid similar to how it is supported on the list grid view. Used for bulk operations.

  • Handle pre-selected items in grids by looking for $selected on the row

  • Handle paginated results when using a transformMapper to map data from the response. Previously, it expected the response to be an array. Added support for checking the content if it exists.

  • This has no impact on any grids unless a transformResponse is added to the grids metadata

  • Introduced a MoneyTileComponent for TileGrid to display money fields

  • Introduced a ToggleTileComponent for TileGrid elements to display boolean fields as toggles.

  • Introduced a description prop to CollapsibleGroup components.

  • Introduced a overrideMaxWidth prop to SlideOver components to allow overriding the max width that is calculated by default.

  • Added mobile breakpoints for TileGrid components to allow for better responsiveness on smaller screens

  • Added ModalFormView that handles Update, Create and simple ModalViews

  • Support targeting Auth token claims in field conditionals

    • Auth claims can be targeted by prefixing $authClaims to the name of the claims, e.g., $authClaims.3pidp_client_registration_id

    • Example usage: Make an Admin User’s email read-only if they authenticated with Google.

  • Enhanced display of display-only Rule Builders so that they are more human-readable

    • This allows a FieldArrayBlock to be marked as displayOnly and for that to be inherited by its child field components.

    • This also improves the look of displayOnly RuleBuilderFields.

      Display-Only Rule Builder Example
  • Introduced support for Computed Dates in DateField component.

    • Takes in metadata-provided computedFromDate, computedFromDuration, and computedFromDurationUnit attributes to dynamically compute the value of the read-only date field.

  • Introduced a new DateRangeField component to display dates in a range format, e.g., "Start Date - End Date".

  • Introduced new DurationField and DurationColumn components to display duration values given specific base units, e.g., "2 weeks".

  • Added support for displaying a success toast notification message when a new entity is created. This is driven by the successNotificationOnCreate attribute. Additionally, a custom message can be set via the successNotificationMessage attribute, otherwise it will fall back to a default success message.

  • Updated the ActionListGridSelectAction to display a toast notification to indicate when the sort action is in process, and another to notify that the sort action was applied successfully.

  • Updated components to support displaying metadata-driven icons in action buttons:

    • Updated the ActionListGridAdvancedSearch component to display a button with a filter icon instead of the original "Filters" label. This icon can be modified via the filterIconName metadata attribute.

    • Updated the ActionListGridModalFormButton component to support displaying a button with a custom icon instead of the action definition’s label. This is driven by the iconName metadata attribute, and if it is not set, it will fall back to displaying the label as before.

    • Updated the custom Export and Import views to also display icons in their buttons and allow for customization via the iconName metadata attribute. By default, these views will display an upload icon and download icon, respectively.

  • Updated the BetterSearchInput component to display an explicit search button, instead of relying on the user to press enter after typing in their search query. This also allows for better accessibility.

  • Added a new selectableWhenMutableOnly metadata attribute to the ActionListGrid component. This is helpful to disable row selection and thus prevent the use of actions on entities that are not mutable.

  • Added a new forceCatalogSelector metadata attribute to Entity Forms that forces displaying the Catalog Selector, overriding the default behavior where it is only shown in Create forms.

  • The Rule Builder is now able to render lookups that are dependent upon the values from the parent form. To achieve this, the $parent prop is now being passed to the RuleBuilderQueryBuilder component.

  • The Grid Create Action now supports refetching on action success based on the new readOnSuccess metadata attribute.

  • Introduced components to support the new Interdependent Grid Concept

  • Implemented custom behaviors in the FieldArrayGrid component to support custom messages for Boolean Attribute Choices, defaulting the choice labels to "Yes" and "No".

  • Updated the main navigation to display the Application Logo instead of the Application Portrait and added environment properties to provide better control over this selection:

    • VITE_USE_PORTRAIT_ASSET_IN_MAIN_NAV: reverts to using the Application Portrait in the main navigation when set to true. This property is false by default.

    • VITE_USE_PORTRAIT_ASSET_IN_APPLICATION_SELECTOR: allows using the first letter of the Application Name instead of the portrait asset as the portrait in the application selector when set to false. This property is true by default.

  • Allowed metadata for entity forms to define a isCatalogOverrideFallbackHintField attribute that can be used as a fallback to determine if the current entity is a catalog override in the current context, even if its ContextState data does not indicate that it is an override.

    • The exemplifying use-case here is a Product from parentCatalog which has characteristics defined in childCatalog. If viewing the entity from the childCatalog context, Product.contextState may not have field changes and will not seem like an override, but the new characteristicsOverriddenHint field in CatalogServices will be used as a fallback to ensure the 'undo overrides' button still appears.

  • isProductionCatalogEntity is now exported as a public method out of CatalogUtils to allow its direct invocation in Delete.tsx

Bug Fixes

  • Fixed extra translations being submitted on entity forms where the values were the same as the default, untranslated values.

    • When in translation mode, an extra call will now be made to fetch the entity’s data without the language header in order to maintain in state the untranslated values for comparison when the TranslateModeService builds out the list of translations to submit to the backend APIs.

  • Added support for metadata attribute default_selected_component_id in TargetKeyLookup component.

    • When it is set, that value will be used to find a matching component and use it as a default selection if no component is found, the first component will be used.

  • Fixed typo on showInAdmin for badge columns/tile components (attribute name changed on the metadata)

  • Fixed TimeZoneProvider not making requests with appropriate scope and against the right endpoints.

    • Previously, this would not work for users without ALL_ADMIN_USER permissions.

  • Application selector will be hidden when no applications are available

  • Fixed mistakenly including the sandboxId in external grid requests when the grid isn’t sandboxable

  • Fixed an issue where the list grid rows are not selectable when they are displayed in a modal popup

  • Fixed an issue with validation for fields with multiple conditions

  • Fixed a bug where the 'Undo Overrides' action was being offered to users even when the entity was not specifically overridden in the current catalog context. This enabled the possibility of users making requests to undo overrides inherited from parent catalogs. Going forward, the action will only appear if the current catalog matches the entity’s override catalog.

  • Added handling in the FieldArrayGrid component to prevent modification of attribute choices that are derived from enum characteristics.

  • Fixed a bug where the 'Undo Overrides' action was being offered to users even when the entity was not specifically overridden in the current catalog context. This enabled the possibility of users making requests to undo overrides inherited from parent catalogs. Going forward, the action will only appear if the current catalog matches the entity’s override catalog.

  • Fixed a bug in LookupColumn where it failed to detect that the lookup value should be hydrated and instead displayed the entity id.

  • Added support for handling strings with single quotes in SpEL expressions, which were previously stripped or would result in errors at evaluation.

  • Replaced polling in the useSummaryGrid in favor of a refresh button.

    • This fixes an issue where continuous polling would clear the state and cause selected rows in the change summary grids to become unselected.


Auth JS SDK Release Notes for 1.6.8

Important

Compatibility Warning: If your frontend application is updated to use this version of the SDK but your backend AuthenticationServices is on an older version that does not support public clients calling /oauth2/revoke, browser console errors will appear during logout or session clearing. To prevent these console errors, you should explicitly set enableTokenRevocation: false when instantiating the AuthClient.

Tip

This release is otherwise compatible with Release Trains starting in the 1.8.x line.

Enhancements & Notable Features

Frontend Token Revocation

The frontend Auth Web SDK (@broadleaf/auth-web) now supports automatic token revocation during logout or session clearing.

  • Automatic Revocation: Before the token cache is cleared, the SDK will automatically iterate over the cached tokens, detect if a refresh token is present, and make a request to the /oauth2/revoke endpoint in AuthenticationServices to revoke it.

  • Compatibility Gating Property: To maintain backward compatibility with older versions of AuthenticationServices that do not support public clients calling the token revocation endpoint, this feature can be explicitly disabled.

  • Toggle Option: A new boolean option enableTokenRevocation has been added to AuthClientOptions which defaults to true. If your frontend application is updated to use the new SDK but your AuthenticationServices backend is on an older version that does not support public clients calling /oauth2/revoke, you should set enableTokenRevocation: false when instantiating the AuthClient.

Token Cache API Updates

To support the lookup and revocation of refresh tokens in the cache, the TokenCache interface now exposes a getEntries method:

  • Added getEntries?(): TokenCacheEntry[] to the TokenCache interface.

  • Implemented getEntries(): TokenCacheEntry[] in both InMemoryTokenCache and WebStorageTokenCache implementations.

Bug Fixes

  • Fixed race-condition when using iframe for silent-callback authorization where some token responses were handled out of order


Commerce SDK Release Notes for 1.7.5

Tip

This release is compatible with Release Trains starting in the 2.1.x line.

New Features & Notable Changes

Checkout Payment Expectations Support
  • Support has been introduced to query both paymentRequired and savedPaymentMethodRequired indicators, along with gathering details about available payment method options, in a single aggregate backend request (under the /checkout/payment-expectations endpoint). This simplifies checkout step transitions and payment status evaluation in storefront integrations.


Commerce Quote Microfrontend Release Notes for 1.1.5

Tip

This release is compatible with Release Trains starting in the 2.1.x line.

Important

Minimum React version upgraded to React 18. Minimum Node version upgraded to Node 18.

Features & Enhancements

Tip
Security and maintenance release. Includes all changes for 1.1.4.

Commerce Shared React Microfrontend Release Notes for 1.0.5

Tip

This release is compatible with Release Trains starting in the 2.2.x line.

Important

Minimum React version upgraded to React 18. Minimum Node version upgraded to Node 18.

Features & Enhancements

Tip
Security and maintenance release. Includes all changes for 1.0.4.

Commerce Subscription React Microfrontend Release Notes for 1.0.5

Tip

This release is compatible with Release Trains starting in the 2.2.x line.

Important

Minimum React version upgraded to React 18. Minimum Node version upgraded to Node 18.

Features & Enhancements

Tip
Security and maintenance release. Includes all changes for 1.0.4.

Payment JS SDK Release Notes for 1.4.3

Tip

This release is compatible with Release Trains starting in the 2.1.x line.

Bug Fixes

  • Fixed useListSavedPaymentMethods infinitely sending requests if the "paymentTypes" parameter is provided.


Commerce Next.js Starter Release Notes for 2.1.0

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/commerce-nextjs-starter:2.1.0

Features & Enhancements

Important
Dropped Node 20 support

Bug Fixes

  • Fixed an issue in which the anonymous endpoint was used to read historical carts for an authorized customer, causing an error.

  • Updated the configuration property names for the CSP to fix content loading.

  • Fixed loading issues with Stripe scripts

  • Fixed context issues when using @broadleaf/commerce-quote-react

  • Ignore 404 errors when the customer’s cart hasn’t been created yet

  • Improved the guest cart transfer page loading transition

  • Fixed cart state being lost when rejecting a quote request

  • Fixed resolution of findDOMNode and 409 cart version conflict errors during checkout

  • Fixed default locale not having a default value and being applied inconsistently in the LocaleContext


Commerce Next.js Starter Release Notes for 2.0.4

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/commerce-nextjs-starter:2.0.4

Bug Fixes

  • Fixed an issue in which the anonymous endpoint was used to read historical carts for an authorized customer, causing an error.

  • Updated the configuration property names for the CSP to fix content loading.

  • Fixed loading issues with Stripe scripts

  • Fixed context issues when using @broadleaf/commerce-quote-react

  • Ignore 404 errors when the customer’s cart hasn’t been created yet

  • Improved the guest cart transfer page loading transition

  • Fixed cart state being lost when rejecting a quote request

  • Fixed resolution of findDOMNode and 409 cart version conflict errors during checkout

  • Fixed default locale not having a default value and being applied inconsistently in the LocaleContext


Telco Next.js Starter Release Notes for 1.1.4

Note

Docker URL: repository.broadleafcommerce.com:5001/broadleaf/telco-nextjs-starter:1.1.4

Features & Enhancements

Tip
Security and maintenance release. Includes all changes for 1.1.3.
  • Upgraded to Next 16 and React 19

  • Dropped support for Node 18

  • Verified support for Node 22 and 24

  • Pass product when fetching additional pricing targets

  • Made a PDP specific to products with recurring pricing

    • Handles a new display template name, BLC_SUBSCRIPTION, meant to handle demo subscription products (Office 365 and VPNs)

    • The DefaultRecurringPricingPdp is the exact same copy of DefaultPdp except that DefaultPdp now does NOT use the AdditionalPricingOptions while DefaultRecurringPricingPdp does

      • This is to prevent /price-targets requests from being called unnecessarily on the DefaultPdp

    • Products using the PAY_AS_YOU_GO_PHONE template will use the DefaultPdp as they do not have recurring pricing.

  • Added Fee total to Cart Summary and list fees underneath

  • Added generic recurring pricing template and logic to reprice a cart before submitting checkout for virtual or non-shipping fulfillment groups

    • Added condition for GENERIC_RECURRING_PRICING display template

    • Reprice cart right before checkout for nonShippingFulfillment

      • Since they need the BillingAddress to calculate taxes and everything

    • Now reprices the cart right before checkout for fulfillments that have DefaultFulfillmentType of VIRTUAL or NONE. This is done so that taxes and other pricing mechanisms can be run after the BillingAddress has been populated.taxes and everything === Payment Offers Support

  • Add a useAddAttributeToCart hook to facilitate requests to cartClient#addAttributeToCart.

  • Add a useRemoveAttributeFromCart hook to facilitate requests to cartClient#removeAttributeFromCart.

  • The Passthrough Payment Form now uses the useAddAttributeToCart hook to update the PAYMENTS_LIST when a new payment is submitted or removed.

    • The AddAttributeRequest contains invalidatePricing: true, so that the cart will be repriced once the attribute has been updated.

    • The PAYMENTS_LIST attribute is used by the backend to apply offers based on the type of payment. For more details visit the Cart Operation Service release notes.

Bug Fixes

  • Fixed issues with telco product initial pricing and products with recurring prices added to cart

    • Use frequency when adding to cart, fix term messaging to allow 1-year terms

    • Update to pass frequency when adding to cart with additional pricing options

    • Add logic to reset Product PriceInfo to basePrice before fetching additional pricing options

      • The product at this point is already priced, but we don’t want the already-determined bestPrice to be used as basePrice when pricing these additional targets

    • Make sure to pass frequency for dependent items

  • Fixed an issue in which the anonymous endpoint was used to read historical carts for an authorized customer, causing an error.

  • Improved the guest cart transfer page loading transition

  • Fixed default locale not having a default value and being applied inconsistently in the LocaleContext

Service-level Release Notes

Services

Admin Navigation Release Notes for 3.0.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

New Features & Notable Changes

Introduced caching for NavigableMenu responses from the API.

Introduced caching for MenuItemService#getMenuWithNavigationTree since this is hitting the DB and building a NavigableNavMenuItem for each NavMenuItem. Admin menus are practically static from a data perspective so the expiration is 1-year. Additional eviction based on CRUD usage is included since there is a NavMenuItemEndpoint.

  • Configured adminNavigationMenuTreeCacheStateConfigurer to register our new cache with the global Broadleaf CacheStateManager.

  • Implemented AdminNavigationMenuTreeKeyGenerator to produce a serialized context key incorporating both depthLimit and context information: Tenant, Application, and Locale. Menus are tenant trackable and translatable, but they may also be filtered on application ID or presence using predicates.

  • Cache configuration using the following properties

  • broadleaf.adminnavigation.cache.heapBudget default is 5

  • broadleaf.adminnavigation.cache.offHeapBudget default is 5

  • broadleaf.adminnavigation.cache.menu-navigation-tree default is 1 year in minutes, duration.

  • broadleaf.adminnavigation.cache.sizes.menu-navigation-tree default is 20,480 bytes

  • broadleaf.adminnavigation.cache.weights.menu-navigation-tree default 1.0

Support Defining Standard Admin Navigation Menus with Spring Properties

We introduced support for defining NavMenuItems as Spring Properties (e.g., in YAML) and to use predicates and scopes similar to Metadata Routes that determine whether they should render.

Out of box, Predicates are simple and use a standardized name to drive logic when menu items are retrieved to be applied: APPLICATION_BY_ID, APPLICATION_ONLY, TENANT_BY_ID, TENANT_ONLY. Handling for custom, additional predicates can be added in the DefaultMenuItemService#evaluateCustomNavigationPredicate.

Scopes may be specific and used in the frontend to limit accessibility of menu items without needing to consult a metadata Route. This includes specifying the match behavior: All must match or Any may match. To enable this behavior in the Admin, set VITE_MAIN_NAVIGATION_BEHAVIOR=ROUTE_INDEPENDENT, otherwise only scopes of the matching Route will be used, which is the default behavior.

Example Usage
broadleaf:
  adminnavigation:
    menu:
      items:
        - id: 'TENANT'
          label: 'Tenant Management' // or message key like menu-item.tenant-management
          displayOrder: 1_000
          icon: 'globe'
          predicates:
            - name: 'TENANT_ONLY'
          predicate-match-type: 'ALL'
          submenu:
            - id: 'APPLICATIONS'
              label: 'Applications'
              url: '/applications'
              displayOrder: 1_000
              icon: 'globe'
              scopes: [ 'TENANT' ]
              scope-match-type: 'ANY'
              predicates:
                - name: 'TENANT_ONLY'
              predicate-match-type: 'ALL'
            - id: 'VENDORS'
              label: 'Vendors'
              url: '/vendors'
              displayOrder: 2_000
              icon: 'user-group'
              scopes: [ 'TENANT', 'VENDOR' ]
              scope-match-type: 'ANY'
              predicates:
                - name: 'TENANT_ONLY'
              predicate-match-type: 'ALL'
1.New Classes Created
  • NavigationPredicate.java: Models runtime tenant/application-based menu restrictions (e.g., TENANT_ONLY, APPLICATION_BY_ID) defined in YAML, mirroring the RoutePredicate metadata pattern.

  • NavigationItemLocator.java: Pluggable interface to contribute menus from any in-memory or programmatic source.

  • PropertiesNavigationItemLocator.java: Default implementation mapping Spring properties to the locator.

  • CompositeNavigationItemLocator.java: Composite wrapper collecting and flattening all registered locators.

2. Domain & DTO Extension
  • Updated NavMenuItem.java and NavigableNavMenuItem.java to hold and propagate scopes, scopeMatchType, predicates, and predicateMatchType.

  • Updated the existing AdminNavigationProperties.java to bind the list configuration under broadleaf.adminnavigation.menu.items.

  • Introduced PropertyDrivenNavMenuItem to represent the property version and allow nesting submenu items rather than a purely flat list for convenience.

    • PropertiesNavigationItemLocator will convert these to a flat list of NavMenuItems to fit into the existing business logic seamlessly.


Admin Navigation Release Notes for 3.0.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.


Auth Release Notes for 3.0.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

New Features & Notable Changes

API Key Support

Starting with AuthenticationServices 3.0.0, it is possible to create an AuthorizedClient that authenticates/authorizes with API keys.

Please review API Key Grant Type documentation for more information.

At a high level, this release introduces the following:

  • Comprehensive API Key Management: Introduced the ApiKey domain along with supporting CRUD endpoints and repositories. API keys are generated securely, stored as cryptographic hashes, and mapped to an AuthorizedClient (API Account) to utilize existing scope/permission/tenancy concepts.

  • Custom OAuth2 Grant Type & Authentication Method: Implemented a new custom grant type (urn:broadleaf:params:oauth:grant-type:api-key) as well as a new api_key authentication method.

  • Authentication/Authorization Flows: Added new authentication and authorization flows to support the /token endpoint accepting an api_key form parameter. This flow validates the provided API key against stored values to both authenticate and authorize the client, issuing an OAuth2AccessToken with narrowed scopes upon success.

  • Visibility and Environment Controls: API keys are categorized by visibility (SECRET vs PUBLISHABLE) and environment (e.g., live, staging). PUBLISHABLE keys support configuring whitelisted domains that are automatically injected into the token response envelope.

    Note

    The usage and enforcement of the visibility type, environment separation, and domain whitelisting are left as an implementation exercise for clients, as requirements can vary greatly.

  • Admin UI Integration: Broadleaf Admin metadata has been updated to expose API key management in the AuthorizedClient edit page.

  • Strict Validation & Immutability Guardrails: Added validation rules to prevent the deletion of clients with outstanding API keys and enforce immutability on key visibility and environment settings once keys are generated.

Improved Bean Overridability

Added @ConditionalOnMissingBean annotations to several auto-configured beans in AuthI18nAutoConfiguration and AuthServiceTemplateAutoConfiguration to support easier customization and overriding in client implementations:

  • Internationalization & Mapping:

    • authMessageSourcePostProcessor

    • authTranslationPostMapperMember

  • Thymeleaf & OAuth2 Template Engine / Resolvers:

    • oAuth2ClientIdTemplateEngine

    • viewResolverPostProcessor (updated to return the concrete ViewResolverPostProcessor type, which has been made public)

    • oAuth2DefaultTemplateResolver

    • broadleafTemplateResolver

    • broadleafOAuth2DefaultTemplateResolver

Public Client Access of Token Revocation Endpoint

The OAuth2 Token Revocation Endpoint is now accessible to public clients. Please review OAuth2 Token Revocation endpoint documentation for more details.

  • Introduced a new PublicClientTokenRevocationAuthenticationConverter that specifically targets the token revocation endpoint and establishes a PublicClientTokenRevocationAuthenticationToken authentication

  • Updated PublicRefreshPublicClientAuthenticationProvider to handle PublicClientTokenRevocationAuthenticationToken

Impersonation Security Scope

Add the IMPERSONATE security scope and its root permission-scope mapping to the required starter data. This is the Authentication Services half of the admin change that hides the View Quote Details action from users who lack impersonation permissions — without the scope in place, the action is hidden from every user because there is nothing for the permission to map to.

The changesets are guarded by preconditions and will not insert rows that already exist, so a deployment that added the scope by hand needs no further action.

Session Token Blacklisting Support

Please refer to Session Token Blacklisting documentation for more details on this feature.

Application Domain Environment Field

Expanded the Application and JpaApplication domain models to include an environment property (backed by a new EnvironmentTypeEnum defaulting to PRODUCTION). Accompanying Liquibase changelogs were added for MariaDB, MySQL, Oracle, and PostgreSQL to introduce the environment VARCHAR column to the blc_application table.

Invalid Token Verification Exception Handling

Enhanced token verification within StatelessUtilImpl#verifyToken to explicitly throw a new custom UnverifiedTokenException if a JWT fails parsing or signature validation. The global AuthExceptionAdvisor has been updated to handle this specific exception, returning an HTTP 401 Unauthorized ApiError rather than a generic 500 server error.

API Key Visibility and Environment Usability Updates

Refactored API key environment and visibility options from string constants (DefaultApiKeyEnvironments, DefaultApiKeyVisibilityTypes) into strongly-typed Java Enums (DefaultApiKeyEnvironmentType, DefaultApiKeyVisibilityType). Entities such as AuthorizedClient, ApiKey, and JpaApiKey, along with their generators and validators, have been updated to utilize these enums. This resolves usability issues in the Broadleaf Admin metadata, bridging the gap between uppercase Admin UI select fields and backend representations.

API Key Creation Success Confirmation

Creating an API Key in the Admin now shows a success confirmation with the generated plaintext key, since the key cannot be retrieved again once the confirmation is dismissed.

  • Added a copyable plaintext-key field to the API Key creation success response.

  • Fixed the whitelisted-domains field incorrectly displaying for SECRET visibility keys; it now only displays for PUBLISHABLE keys, which is the only visibility type domain whitelisting applies to.

Starter Data for Pricing Endpoint APIs

Added the PRICE_REQUEST security scope, permission, and default client scope/permission mappings required to authorize the new PricingEndpoint APIs.

Bug Fixes

  • Fixed incorrect default Caffeine and EHCache heap/offheap size properties — they were previously specified in raw megabyte values (e.g. 500) under keys expecting a unit suffix, so the configured sizes were not actually being applied. The defaults now use unit-suffixed values (e.g. 500MB).

Upgrade Guide

Liquibase Change Sets

The database schema has changed as part of this version.

Creates and Updates

Create/update changes (new tables, new columns, etc) are automatically included in the updated *changelog-master.xml after you upgrade to the new Authentication Services JAR. The new changesets inside will run automatically to migrate existing data.

Database Platform Create/Update Changelog File Name

PostgreSQL

db/changelog/auth.postgresql.changelog-master.xml

MariaDB

db/changelog/auth.mariadb.changelog-master.xml

MySQL

db/changelog/auth.mysql.changelog-master.xml

Oracle

db/changelog/auth.oracle.changelog-master.xml and db/changelog/auth.oracle.short.changelog-master.xml

Miscellaneous

  • Failing to verify or parse a login token now throws a custom exception.

    • The custom exception is an UnverifiedTokenException. This allows specific handling based on the context in which the token is being verified.


Billing Release Notes for 2.0.0

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

New Features & Notable Changes

Billing Entity Notification State Suppression

Added @ConditionalOnMissingBean to various JPA ignored notification state suppression beans across Billing, DeadLetter, and Subscription configurations. This allows for easier overriding and customization of notification state suppression in implementations.

Centralized Netty Pool and Metrics Configuration

Refactored BillingWebClientAutoConfiguration to replace manual SSL/connector configuration with the centralized CommonInternalServiceClientConnectorFactory. This consolidates Netty connection pooling and WebClient metrics for better performance tracking across the service.

Added Unpooled WebClient Connector Behavior

Expanded the BillingWebClientAutoConfiguration connector factory usage to optionally construct an unpooled SSL connection factory via CommonInternalServiceClientConnectorFactory.unpooled(sslVerificationProperties). This provides a fallback and alternative configuration for environments avoiding persistent Netty connection pooling.


Bulk Operations Release Notes for 2.0.0

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

New Features & Notable Changes

Centralized Netty Pool and Metrics Configuration

Refactored BulkOperationsServiceAutoConfiguration to replace manual SSL and connector configuration with the centralized CommonInternalServiceClientConnectorFactory. This consolidates Netty connection pooling and WebClient metrics for better performance tracking across the service architecture.

Added Unpooled WebClient Connector Behavior

Expanded the BulkOperationsServiceAutoConfiguration connector factory usage to optionally construct an unpooled SSL connection factory via CommonInternalServiceClientConnectorFactory.unpooled(sslVerificationProperties). This provides a fallback configuration for environments avoiding persistent Netty connection pooling.

Bug Fixes

Fixed Infinite Paging Loop in Bulk Operations
  • Fixed an issue within the paging implementation of the CategoryModifiedEventListener, SingleProductIndexedEventListener, and InitializeBulkOperationItemsListener.

    • Previously, the Pageable request was stuck at page 0 during bulk operations parsing product memberships and auto-included categories, resulting in an infinite loop when the number of items exceeded the initial batch size. The pageable objects are now correctly advanced (e.g., searchPageable = searchPageable.next()) to appropriately traverse search results and auto-included categories. New comprehensive test suites were also added to validate correct paging behaviors.


Admin Metadata Release Notes for 3.0.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

Requirements

  • JDK 17 is required for Broadleaf release trains 2.0.0-GA, and beyond.

Notable Changes

Automatically Add Augmentations for Characteristics That Are Targetable in Rule Builders
Important
Requires Catalog Service 3.0.0 (Release Train 3.0.0)
What Changed

When a Product Characteristic is created, updated, or deleted, the Catalog Service now publishes a CharacteristicModifiedEvent message. The Metadata Service consumes this event and automatically creates, updates, or deletes the corresponding Metadata Augmentations (under the catalog:products:update container) based on whether the characteristic has been flagged as "targetable in rule builders."

Supported characteristic value types (STRING, DECIMAL/NUMBER, INTEGER, ENUM, BOOLEAN) are automatically mapped to their respective Admin UI field types, with ENUM values being dynamically populated as select options.

Benefits
  • Zero-Touch Automation: No manual creation or deployment of Metadata files to expose characteristics in rule-builders (e.g., target item rule builders for pricing, campaigns, or promotions).

  • Frictionless Syncing: Any changes to characteristic names, types, or enumeration values are automatically and dynamically synchronized with their corresponding Metadata Augmentations in near real-time.

  • Reduced Human Error: Eliminates the risk of misconfiguring JSON augmentation payloads or missing manual configuration steps during administrative updates.

How to Use

This feature works out-of-the-box with zero required code changes or manual configurations. * In the Admin Console: Simply enable the Targetable in Rule Builders option when creating or updating any Product Characteristic. The Catalog and Metadata services will handle the rest in the background. * Infrastructure Configuration: If you customize or override default messaging bindings, ensure that the Catalog Service output binding characteristicModifiedOutput and the Metadata Service input binding characteristicModifiedInputMetadata are both connected to the same messaging destination (defaults to characteristicModified).


Cart Operation Services Release Notes for 3.0.0-GA

Tip
The 3.x versions are Spring Boot 4.1+ compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

Requirements

  • JDK 17 is required for Broadleaf release trains 3.0.0-GA, and beyond.

New Features & Notable Changes

Allow Disabling Anonymous Historical Cart Endpoints

Added configuration properties to allow users to disable the anonymous history endpoints in Cart Ops altogether if not in use. Cart Ops was chosen for this as it will short-circuit the requests at the earliest point to improve performance. This is granular in case some endpoints are in use but not all. Additionally, users may have added their own custom endpoints in Cart Ops that use the same endpoints in Cart, making Cart Ops the most straightforward placement for this configuration.

To disable each:

broadleaf:
  cartoperation:
    endpoint:
      cart-history:
        read-anonymous-customer-cart-enabled: false
        read-cart-by-order-number-for-anonymous-customer-enabled: false
        read-cart-by-order-number-for-anonymous-customer-hydrate-payments-enabled: false
Miscellaneous
  • Added CartProvider#retrieveHistoricalCartByOrderNumber for looking up both registered and anonymous historical carts by Order Number only. Additional ownership checks are handled in CartHistoryService to allow a more streamlined experience.

  • Added property to control whether CartProvider#retrieveHistoricalCartForAnonymousCustomer can retrieve only anonymous user carts or both registered and anonymous historical carts by email address and order number.

    • Previously the method returned both despite the name implying only anonymous carts being returned.

    • Recommended: Restrict it to only retrieve anonymous carts by setting the following property: broadleaf.cartoperation.cartprovider.readHistoricalCartForAnonymousCustomerEndpointVersion=2

  • All cart retrieval logic in CartHistoryEndpoint methods has been moved to methods in CartHistoryService to better consolidate business logic.

    • CartHistoryService centralizes handling of user access checks for retrieved all historical carts.

    • This enhances the capabilities of the endpoints so that Account Carts can be retrieved by either the owner or an Account Admin/Approver to streamline the behavior on the order confirmation page.

  • Avoid threadlocal context issues for access tokens on webclient

  • Do not throw an exception if a second request to transfer the guest cart to the customer is sent, but it has already been transferred

Checkout Payment Expectations API
  • Introduced a new REST API endpoint GET /checkout/{cartId}/payment-expectations returning a consolidated CheckoutPaymentExpectations DTO.

  • The expectations payload contains:

    • paymentRequired: Whether a payment is required immediately to successfully process checkout.

    • savedPaymentMethodRequired: Whether a saved payment method is required for future use (esp. for future subscription billing cycles).

    • paymentMethodOptions: Available payment method options for the cart.

  • Centralized checkout payment rules within CheckoutPaymentMethodService so that the expectations API and the CartPaymentMethodValidationActivity in the checkout workflow share identical logic.

  • Deprecated the GET /checkout/{cartId}/payment-method-options endpoint but preserved its execution paths.

Improved Support for $0 Authorizations during Checkout
  • Added improved support for $0.00 authorizations during checkout validation and processing.

  • This enables customers checking out with zero-dollar carts (such as subscription free trials) to securely tokenize and register new saved payment methods for future billing.

  • Coordinates a $0 Authorize (account verification check) transaction with the payment gateway to safely validate the payment details. On success, the token is stored in the BLC_SAVED_PAYMENT_METHOD table.

New PRICING_CHANGE CartAlert
  • The new CartAlert with PRICING_CHANGE type is added to the cart with updated payment information when the Cart price changes during repricing and the payment is updated.

    • This can be used to retrieve the latest payment version if you need to update it after the cart is repriced. If more information is needed, the DefaultCartOperationService#toPaymentUpdate method can be overridden.

CartAlert Example
{
  "type": "PRICING_CHANGE",
  "additionalAttributes": {
    "UPDATED_PAYMENTS": [
      {
        "paymentId": "ID of the updated payment",
        "amount": {
          "amount" : 3.99,
          "currency" : "USD"
        },
        "version": 2
      }
    ]
  }
}
Improved Inventory API Exception and Communication Error Handling
  • Added support for optionally throwing API exceptions from the inventory provider instead of silently catching them and returning false or empty availability results.

  • Added new configuration properties in ExternalInventoryProperties under the broadleaf.cartoperation.inventoryprovider prefix:

    • throw-is-inventory-available-exceptions: If true, exceptions from isInventoryAvailable methods are thrown rather than caught. Default: false.

    • throw-get-inventory-summaries-exceptions: If true, exceptions from getInventorySummaries and related methods are thrown rather than caught. Default: false.

    • message-keys-for-availability-errors: If true, returns message keys instead of human-readable messages for availability check failures. Default: false.

  • Enhanced checkout workflow error handling:

    • Introduced a new checkout failure type INVENTORY_CHECK_ERROR (mapping to the checkout.failure.inventory.error message key) to differentiate actual out-of-stock items (FAILED_INVENTORY_CHECK) from communication/API errors (e.g., connection errors or timeouts).

    • When communication errors occur, the checkout validation workflow captures the root cause (e.g., CONNECT_ERROR, READ_TIMEOUT, WRITE_TIMEOUT) or the inventory service’s response payload and includes it in the exception’s additionalInfo payload.

  • Refactored CartOperationExceptionAdvisor to handle request-level or empty API response errors (e.g., timeout, connection refused) from external service providers gracefully without exposing internal API details, mapping them to PROVIDER_API_ERROR with custom error payloads.

  • Introduced a new WebClientExceptionHelper utility to resolve root causes of connection issues (ConnectException, Netty read/write timeouts).

  • Added missing localizable error messages under messages.properties for cart quantity failures and communication failures.

WebClientRequestException Support for ProviderApiException

Previously, ProviderApiException only handled WebClientResponseException. Now, it also handles WebClientRequestException. These types of exceptions can be encountered in cases such as connection timeouts, connection refused, etc.

To support this, a potential breaking change was made to com.broadleafcommerce.cartoperation.exception.ProviderApiException#getReceivedException. This now returns the more generic WebClientException. This may cause compilation errors with custom exception handlers. A few helper methods were added to ProviderApiException to retrieve appropriate data depending on if the cause is a request or response exception. Refer to CartOperationExceptionAdvisor#handleProviderApiError for an example on how to handle this change.

Payment Lock Tokens When Removing Invalid Offer Codes

Add CartOperationService#removeOfferAndCampaignCodesFromCart(Cart, List, boolean, Map, ContextInfo), which accepts the payment lock tokens held by the calling flow. The checkout offer validation activity now passes its lock tokens through when it strips invalid offer codes, so the reprice that follows can act on payments the flow already holds locks for instead of failing to acquire them.

Stale Dependent Items and Attribute Choices No Longer Block Checkout or Edits

Previously, a cart item whose only config error was a NO_MATCHING_ALLOWED_VALUE mismatch (for example, an attribute value that was removed from the catalog) would hard-fail checkout validation or a cart edit request.

  • CartItemValidationActivity now identifies these invalid or mismatching dependent items as stale during checkout validation instead of mutating cart collections in place, and removes them (along with their nested dependent items and fulfillment items) via StaleCartItemsService rather than blocking the request.

  • When updating a cart item whose only config errors are unmatched-value mismatches and the request isn’t trying to set that attribute, the item is now removed as stale instead of the update hard-failing, keeping fulfillment/fee/COD cleanup consistent.

  • Fixed an issue where CartSubscriptionUtils#identifySubscriptionRootItems mutated the cart’s item collection while an alias to it was in use, which could throw a ConcurrentModificationException or duplicate items.

Bug Fixes

  • Fixed an issue where dependent item validation was not done for nested dependent items.

  • Fixed offer application for nested dependent cart items and optimized the lookup used to apply them.

  • Fixed properties not binding in CartStalePricingValidationActivityProperties because a @Fluent annotation was preventing correct binding, so broadleaf.cartoperation.checkout.activity.validation.cart-stale-pricing.should-reject-lower-price and …​use-real-time-cart-pricing behaved as if left at their defaults regardless of what was configured.

    • The fluent accessors shouldRejectLowerPrice() and useRealTimeCartPricing() are deprecated for removal. Use isShouldRejectLowerPrice()/setShouldRejectLowerPrice(boolean) and isUseRealTimeCartPricing()/setUseRealTimeCartPricing(boolean) instead.

  • Fixed a NullPointerException raised while pricing a cart when a merchandising product resolved to a price with no recurring price.

Extensibility & Deprecations

  • CartPaymentMethodValidationActivity Removals:

    • The protected method validatePayments(CheckoutProcessDto, ContextInfo) was removed. Its logic was split into validateAddToBillPaymentsIfNeeded and other cart payment vs saved payment method checks.

    • The protected method validatePaymentsForSubscription(CheckoutProcessDto, ContextInfo) was removed. Its logic was refactored and other cart payment vs saved payment method checks.

    • The protected method readCustomerActivePaymentMethods(ContextInfo) was removed. Its logic was migrated to DefaultCheckoutPaymentMethodService#readCustomerActivePaymentMethods as part of the centralized payment method logic in CheckoutPaymentMethodService.

  • Deprecated ULIDGuestTokenGenerator: Replaced by default by SecureRandomGuestTokenGenerator that generates a token with 128 bits of randomness to ensure cryptographic level randomness.

    • This is backwards compatible, only affecting the generation of the string and not lookups.

    • To revert to ULIDGuestTokenGenerator temporarily, set broadleaf.cartoperation.service.checkout.guest-token.generator-type=ULID.

  • Inventory Availability and Extensibility Improvements:

    • DefaultInventoryAvailabilityService has been substantially refactored to modularize inventory check workflows and simplify extension.

    • Added new overrideable helper methods in DefaultInventoryAvailabilityService:

    • verifyInventoryAvailability(CartItem, I, CatalogItemList<I>, List<InventoryAvailabilityRequest>, ContextInfo)

    • handleInventoryAvailabilityException(CartItem, I, CatalogItemList<I>, RuntimeException, ContextInfo)

    • findCartItemsWithSameSku(Cart, CartItem)

    • findSerializedItemsWithSameSku(CartItem, String, Cart)

    • findFulfillmentItemRefAndInventoryLocation(Cart, CartItem, String, ContextInfo)

    • Deprecated the method getInventoryLocationReference(String, Cart) in favor of getInventoryLocationReference(Cart, CartItem, String, ContextInfo).

    • Removed Spring @NonNull annotations from parameter definitions in the InventoryProvider interface to rely on standard JVM nullability checks.

Cart Version Validation & Resolution Refactor

This update introduces a unified, request-driven mechanism for validating Cart versions prior to read or write operations. Version validation is now handled directly during the resolution phase inside CartResolverService using ResolveCartRequest configuration, rather than being manually invoked by endpoints or downstream services.

  • Centralized version validation directly within CartResolverService driven by CartVersionValidationStrategy (READ, UPDATE, NONE) on ResolveCartRequest.

  • Deprecated manual CartVersionValidationService calls within DefaultCartResolverService.

  • Added handleCartBeforeProcessCheckout(CheckoutProcessDto, CheckoutProcessRequest, ContextInfo) to CheckoutCartEndpoint as an explicit extensibility hook prior to checkout processing.

Note

Extensibility Signature Change:

In CheckoutCartEndpoint, the legacy resolveCart(String, CustomerRef, ContextInfo) method has been removed. Additionally, CheckoutCartEndpoint#processCheckout now resolves the cart via a dedicated resolveCartForProcess(String, Integer, CustomerRef, ContextInfo) method (disabling automatic repricing on resolve, as pricing is handled within the checkout workflow itself).

Because resolveCartForProcess is a newly introduced method, any custom client subclasses that previously overrode resolveCart or resolveCartForUpdate to customize checkout resolution will compile successfully, but their overrides will go silently unused. You must migrate custom checkout resolution logic by overriding resolveCartForProcess or implementing custom logic inside the handleCartBeforeProcessCheckout hook.


Content Services Release Notes for 3.0.0

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

Bug Fixes

  • Add property spring.cloud.stream.bindings.cloneContentItemOutput.destination: cloneContentItem to correctly bind clone content item notification.

  • Fixed the issue where the active check is not skipped when bulk cloning content items


Credit Account Services Release Notes for 3.0.0

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.


Payment Transaction Services Release Notes for 3.0.0-GA

Tip
The 3.x versions are Spring Boot 4.1+ compatible.

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

Requirements

  • JDK 17 is required for Broadleaf release trains 3.0.0-GA, and beyond.

New Features & Notable Changes

Allow $0 Authorizations during Checkout
  • Updated DefaultTransactionExecutionRequestValidator to allow $0.00 AUTHORIZE requests. This supports the ability to save a payment method for future use via executing a $0 Authorize transaction, such as for a subscription free trial, while ensuring AUTHORIZE_AND_CAPTURE and other transactional amounts remain strictly positive (> 0).

  • Refactored DefaultTransactionSummaryService#buildTransactionSummary to gracefully return an empty TransactionSummary rather than throwing an EntityMissingException when no payment records are found for a given owner type/ID. This changes the REST API response from a 404 Not Found to a 200 OK with an empty TransactionSummary payload, allowing downstream lookups of transaction summaries to proceed without error on zero-dollar checkouts or orders with no payments.

WebClient Access Token Threadlocal Fix
  • Avoid threadlocal context issues for access tokens on webclient builders in Spring Boot 4, backwards compatible with Spring Boot 3.5.

Bug Fixes

  • Fixed Transaction entitySourceIds being null on preexisting transaction

    • Also ensure that PaymentTransactionRef#getSourceEntityId falls back to the singular sourceEntityId when sourceEntityIds is empty like is done on PaymentTransaction#getSourceEntityId

  • Updated DefaultPaymentManagementService to catch InvalidPaymentConfigurationException thrown by PaymentGatewayPaymentModificationService.modifyFullPaymentForCreate, and rethrow as InvalidCreatePaymentRequestException to ensure the API caller receives a 400 response instead of 500

  • Prevent NPE in DefaultTransactionExecutionService#recordPaymentAttributesFromResponse when the external transaction result doesn’t have a transaction type.

  • Increased the column value length for the JpaPaymentTransaction#rawResponse field from MEDIUM_TEXT_LENGTH to LONG_TEXT_LENGTH to avoid truncation errors on longer gateway responses.

  • Updated external provider WebClient calls to merge multi-value HTTP headers from getHeaders(ContextInfo) instead of overwriting same-named headers, fixing compatibility across Spring Boot 3.5 & 4.


Order Operation Services Release Notes for 3.0.0

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all microservices have been upgraded to support Spring Boot 4.1 and Java 25.

New Features & Notable Changes

Warning

Breaking Change: The semantics of ResourceLockProvider.obtainLock() (and its implementation ExternalResourceLockProvider) have changed. It now strictly obtains a permanent lock and no longer behaves as a temporal lock with auto-expiration. Any existing customizations or third-party code relying on obtainLock() for short-lived, temporal concurrency control must be refactored to use obtainTemporalLock() to prevent resources from being indefinitely locked.

Locking Architecture Realignment & Temporal Locks
The Conflation Problem & Solution

Previously, message idempotency checks and resource concurrency guards were conflated under the same API /order-resource-locks which used a temporal lock with auto-expiration. This is incorrect, as for message idempotency, it must use a permanent lock.

To solve this, the locking architecture was decoupled into permanent and short-lived temporal locking behaviors.

Provider Method Semantic Changes (ExternalResourceLockProvider)

Within OrderOperationServices, the client-side provider ResourceLockProvider and its implementation ExternalResourceLockProvider have been modified: * obtainLock() (Semantics Updated): This method now strictly routes requests to the updated /order-resource-locks endpoint to obtain a permanent lock. It is used exclusively for message idempotency and deduplication checks. * obtainTemporalLock() (New Methods): Added default and abstract overloaded methods to obtain a short-lived temporal lock (routing to POST /order-resource-locks/temporal). Callers can optionally pass a custom Duration for lockTtl, or default to the configuration defined under the broadleaf.orderoperation.providers.order.temporal-locks-uri property mapping in orderoperation-defaults.yml.

Listener Component Migration to obtainTemporalLock

All listeners and webhooks guarding resource concurrency have been migrated from obtainLock() to the new obtainTemporalLock() method. This ensures they only obtain temporal, auto-expiring locks rather than permanent ones.

Migrated components include: * Fulfillments: AbstractPaymentReversalFulfillmentStatusChangeListener (guarding ORDER_PAYMENT_MANAGEMENT on order cancellations) FulfillmentCapturingPaymentListener (guarding ORDER_FULFILLMENT_MANAGEMENT during capture) FutureInventoryStockChangeListener (guarding ORDER_FULFILLMENT_MANAGEMENT during inventory allocation) PaymentReversalFulfillmentCancelledListener (guarding ORDER_FULFILLMENT_MANAGEMENT during reversal) SplitFutureInventoryFulfillmentListener (guarding ORDER_FULFILLMENT_MANAGEMENT during splits) * Returns: PaymentRefundReturnConfirmedListener (guarding RETURN_AUTHORIZATION_MANAGEMENT) * Transaction Webhooks: FulfillmentAwaitingRefundResultWebhookListener (guarding ORDER_FULFILLMENT_MANAGEMENT) FulfillmentCaptureWebhookListener (guarding ORDER_FULFILLMENT_MANAGEMENT) ** ReturnConfirmationRefundWebhookListener (guarding RETURN_AUTHORIZATION_MANAGEMENT)

Critical Loop Lock Leak Bug Fix

In SplitFutureInventoryFulfillmentListener, locks were previously obtained inside a loop but lacked reliable release safeguards. This release implements robust try-finally blocks within the processing loop to guarantee that every temporal lock obtained via obtainTemporalLock() is securely released via resourceLockProvider.releaseLock() regardless of whether the processing succeeds or raises an exception.

Zero-Dollar Fulfillments & Empty Payments Support
  • Fulfillment Grand Total Short-Circuit: Updated FulfillmentCapturingPaymentListener to short-circuit execution when the fulfillment grand total is $0.00. It now transitions the fulfillment status directly to PAYMENT_CAPTURED and fires a capture result status of UNNECESSARY instead of attempting to fetch payment summaries.

  • Payment Lookup Graceful 404 Handling: Refactored ExternalPaymentProvider to catch downstream 404 Not Found responses when querying payment or transaction summaries. Instead of throwing EntityMissingException when an order or cart has no payment records, the provider now returns empty collections or an empty TransactionSummary.

  • Refund & Reversal Bypass & Short-Circuit: Updated the refund and reverse authorization execution logic in DefaultPaymentRefundService and DefaultPaymentAuthReversalService to completely bypass interactions with Payment Transaction Services (PTS) and the Payment Service Provider (PSP) when the order has no payments and/or the total transaction amount is $0.00. This prevents useless and invalid zero-dollar transactions from being sent to gateways or locking empty payments unnecessarily.

OAuth2 Client Bean Extensibility Fix
  • Distinguished the oauth2FilterFunctionSupplier bean from the common shared singleton, so it can be overridden independently instead of being tied to a bean instance shared with other consumers. Connection configuration for this bean was also modernized.

  • Avoid threadlocal context issues for access tokens on webclient builders in Spring Boot 4, backwards compatible with Spring Boot 3.5.

Bug Fixes

  • Added missing fulfillmentPendingInventoryOutput message binding

  • Fixed bug where discounts are ignored and non-taxable shipping charge is included in commit tax calculation

    • Fixed commitTaxes to use merchandiseTotal instead of merchandiseSubtotal. Prior to this fix, applied discounts would not be considered in the tax calculation

    • Skip TaxItem for fulfillment charge if it’s not taxable

  • Add property spring.cloud.stream.bindings.fulfillmentPendingInventoryOutput.destination: fulfillmentPendingInventory to correctly bind fulfillment pending inventory notifications.

  • Fixed an issue where @JsonIgnore on FulfillmentStatusChangeEvent#order/#fulfillment prevented the order and fulfillment IDs from being serialized at all.

    • As part of this fix, FulfillmentStatusChangeEvent now carries orderId and fulfillmentId directly instead of the full Order/OrderFulfillment payload.

  • Avalara Tax fixes

    • Set reversalRequestId on ReverseTaxTransactionRequest so unique ID can be passed for reversal transactions

    • Update returned tax items to only include confirmed return items

  • Updated external provider WebClient calls to merge multi-value HTTP headers from getHeaders(ContextInfo) instead of overwriting same-named headers, fixing compatibility across Spring Boot 3.5 & 4.

Method Signature Changes

  • In DefaultTaxRequestService

    • The method findFulfillmentForReturnItem was renamed to findFulfillmentForReturn


Tax Provider Libraries

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Common Libraries

Common Libraries

Audit Common Release Notes

2.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Cart Client Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Customer Client Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Data Tracking Release Notes

Data Tracking Release Notes for 3.0.0-GA

Important Updates

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Notable Changes

Dynamic Notification Suppression Integration
  • Introduced support for initializing tracking/persistence notification states based on their suppressed state within the active SuppressNotificationContext

  • Updated trackable domain mapper member supports (DefaultTrackableDomainMapperMemberSupport and JpaTrackableDomainMapperMemberSupport) to check if the specific notification type is suppressed before mapping trackable entities

  • When executing inside a suppressed context, the stopped field of the corresponding trackable domain NotificationState (userState for ChangeSummaryProducer#TYPE and persistenceState for PersistenceProducer#TYPE) is initialized to true dynamically

  • Updated NotificationStateInitializingDomainMapperMember to dynamically resolve and set the stopped property based on the suppressed context check of SuppressNotificationContext.isSuppressed(state.getName())

Description-Based ModelMapper Cache (Replacement for Kryo mappers.zip)

ModelMapper mapping compilation has been refactored to eliminate live object-graph Kryo serialization (and its fragile dependencies like runtime class file transformers patching JDK lambda metafactories). It is replaced by a declarative, highly-efficient Description-Based ModelMapper Cache.

Mappers are compiled and serialized into lightweight JSON descriptors containing purely declarative structure, which is fully compatible with runtime client extensions and Spring-free build generation.

For more details on how to configure and generate these cached descriptors, see the ModelMapper Descriptor Cache documentation.

Internal Service Calls Share One Observable Connection Pool

The interlink WebClient now builds its connector from Extension Common’s shared internal-service connector factory instead of creating its own client on Reactor Netty’s global HttpResources pool. Outbound interlink traffic is therefore pooled and measured alongside every other internal service-to-service call, under one named pool.

  • No configuration change is required. The pool, its size and its metrics are configured under broadleaf.common.webclient.connection-pool.* — see the Extension Common 3.0.0 release notes.

  • An implementation that supplies its own interlinkClientHttpConnector bean keeps that bean and is unaffected, but stays off the shared pool. To join it, build the connector from the injected CommonInternalServiceClientConnectorFactory.

Bug Fixes

  • Updated DefaultProjectionFactory to assign an explicit parent package to AutoProjection classes

    • Some versions of Apache Fury (now Fory), used for serialization/deserialization in Caffeine and EHCache caching implementations, are susceptible to an internal race condition when dealing with unnamed-package classes. This can result in corrupted output, and cause failures (such as NullPointerException) during cacheable operations. By ensuring AutoProjection classes have an explicit package, Broadleaf reduces exposure to this edge case.

  • Fix an issue where ContextInfo#filterByActiveFlag was lost when a ContextInfo was copied, and where restoring the flag after an active-flag-filtered operation wrote to filterByActiveDates instead. Queries run after such an operation could filter on the wrong flag.

  • Fixed a bug where MappingUtils.setupExtensions did not register mappings from parent classes at a higher precedence than implicit mappings from subclasses. This led to parent-defined mappings being ignored/unused in some cases.

  • Fix an issue where a repeated query returned different results than its first execution, because narrowing mutated the CriteriaQuery cached on the query info in place. Narrowing now works against a defensive copy, and the copy carries the same ordering the identifier selection applied, so a page is returned in the order its rows were chosen.

  • Skipped dormant records (those with no recorded changeTimestamp) when retry handlers query for notification-ready members, so they’re no longer incorrectly picked up for retry.

Export Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Fulfillment Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Extension Common Release Notes

Release Notes for 3.0.0

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

  • This version includes all changes up to 2.0.8

A Shared, Measurable Connection Pool for Internal Service-to-Service Calls

This release introduces a named connection pool for internal service-to-service traffic, as well as a factory that services build their connectors from, so all of that traffic is pooled and reported in one place.

See A Shared, Measurable Connection Pool for Internal Service-to-Service Calls for a comprehensive overview of how this works and how to utilize it in your own code.

  • When upgrading to 3.0.0, if you are currently overriding the internal connector beans (e.g., pricingClientHttpConnector), it is highly recommended to update them to build from the new CommonInternalServiceClientConnectorFactory instead of defining your own HttpClient directly to take advantage of these shared settings.

Internal Service-to-Service Calls Authorize Off the Request Thread

Under Spring Boot 4, a service-to-service call issued from a background worker or an @Async method can fail to obtain its token if the lookup depends on thread-local state that only exists on a request thread.

A client_credentials access token for an internal call can now be resolved without an ambient request or security context.

See Internal Service-to-Service Calls Authorize Off the Request Thread for more details and how to configure this on a WebClient builder.

Spring Boot 4 Jackson Property Bridging

Spring Boot 4 binds Jackson 2 configuration under spring.jackson2., while Broadleaf remains on Jackson 2. An EnvironmentPostProcessor now mirrors every spring.jackson. property to its spring.jackson2.* counterpart whenever Jackson 2 is the preferred JSON mapper, so existing configuration keeps applying.

  • An explicit spring.jackson2.* value always wins over a mirrored one.

  • For project hygiene, move spring.jackson. properties to spring.jackson2.. See Upgrade to 3.0.0 for the platform-wide picture.

Dedicated Connection Pools for External Third-Party Service Calls

Integrations with external, third-party services now utilize a dedicated blc-external-service external pool so they no longer share configurations with internal service calls.

See Dedicated Connection Pools for External Service Calls for a complete explanation of this new pool and examples of how to override or create customized external connection configurations.

  • Third-party services have been updated to utilize a new commonExtlSvcClientConnector.

  • If you have overridden the WebClient beans for third-party modules (like Adyen or PayPal) in the past, please ensure you update your overrides to similarly inject and use the new ClientHttpConnector beans.

Centralized SSL Configuration Utilities

As part of the external connection pool consolidation, we refactored SSL logic into a CommonWebClientSslUtils to centralize and share the configuration of customized and disabled SSL contexts for HttpClient.

See Centralized SSL Configuration Utilities for more information on how to utilize these utilities.

Import Consumer Release Notes

3.0.0

  • This version includes all changes up to 2.0.4

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

JPA Common Release Notes

Version 3.0.0-GA

  • This version includes all changes up to 2.0.6.

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Features & Notable Changes
  • Introduce JpaEntityScan#skipRouteInference to replace JpaEntityScan#includeInAllRoutesIfRoutePackageEmpty, which is deprecated for removal.

    • The two attributes are aliases, so an existing @JpaEntityScan(includeInAllRoutesIfRoutePackageEmpty = true) keeps working unchanged. Prefer skipRouteInference = true for shared utility entities and JPA converters that genuinely belong to every data route.

    • When no routePackage is given and route inference finds no extension route, the fallback to assigning entities to all routes is now logged at WARN rather than INFO, and the message names the two ways to resolve it. A deployment that has been silently falling back will start reporting it at startup.

Metadata Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

  • This version includes all changes up to 2.0.9-GA

Features/Notable Changes
  • Registers custom MetadataMessagesBasename beans found in the application context, in addition to those declared through spring.factories

    • Previously the basenames were resolved only from spring.factories against the class loader of the library that performed the lookup, which frequently did not reach a downstream project’s classpath roots, so custom metadata i18n labels silently failed to load.

    • A MetadataMessagesBasename annotated with @Component and picked up by component scanning, or registered as a @Bean, is now honored. Duplicates between spring.factories and the context are registered once.

    • The Broadleaf 3.0.0 OpenRewrite recipe annotates existing MetadataMessagesBasename classes with @Component for you.

Messaging Common Release Notes

3.0.0

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Notable Changes
  • Introduced dynamic check in SuppressNotificationContext to verify if a given message/notification type is suppressed

    • Added a static isSuppressed(String messageType) utility method to check the current thread-local context

  • Integrated suppressed context check into message dispatching in DefaultNotificationHandler

    • Updated message dispatching so that remote messages are not sent if the associated NotificationState is explicitly stopped (state.isStopped() returns true)

Money Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Notable Changes
  • Ship Jackson 2 property defaults so monetary amounts serialize correctly in an application that does not depend on Extension Common.

    • Broadleaf’s monetary support is built on Jackson 2, while Spring Boot 4 defaults JSON handling to Jackson 3. A new MoneyEnvironmentPostProcessor contributes spring.http.converters.preferred-json-mapper=jackson2, spring.http.codecs.preferred-json-mapper=jackson2 and spring.jackson.use-jackson2-defaults=true as defaults.

Offer Client Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Order Client Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Order Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Payment Gateway Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Pricing Client Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Security Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Tax Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Notable Changes
  • Introduced the reversalRequestId field to the ReverseTaxTransactionRequest representation.

Translation Common Release Notes

3.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.

Workflow Client Release Notes

2.0.0-GA

Spring Boot Upgrade
  • As of Broadleaf Release Train 3.0.0-GA, all common libraries have been upgraded to support Spring Boot 4.1 and Java 25.