Implementing a Custom Order Rule
You can configure order rules in Liferay for orders that meet a specific condition. The Minimum Order Amount rule is available out-of-the-box. It prevents checkout of orders below a specific value. To add a new order rule, you must implement the COREntryType interface. See Order Rules for more information.
Deploying the Custom Order Rule and Adding Language Keys
Start a new Liferay DXP instance by running
docker run -it -m 8g -p 8080:8080 liferay/dxp:2025.q1.6-lts
Sign in to Liferay at http://localhost:8080 using the email address test@liferay.com and the password test. When prompted, change the password to learn.
Then, follow these steps:
-
Download and unzip the Acme Commerce Order Rule.
curl https://resources.learn.liferay.com/commerce/latest/en/developer-guide/order-management/liferay-x9k1.zip -O unzip liferay-x9k1.zip -
Build and deploy the example.
./gradlew deploy -Ddeploy.docker.container.id=$(docker ps -lq)NoteThis command is the same as copying the deployed jars to
/opt/liferay/osgi/moduleson the Docker container. -
Confirm the deployment in the Docker container console.
STARTED com.acme.x9k1.impl_1.0.0 -
Log in as an administrator, open the Global Menu (
), and click on Control Panel → Language Override. Click the Add button (
) and add the following keys.Language Key Value x9k1-minimum-order-quantity X9K1 Minimum Order Quantity minimum-quantity Minimum Quantity ImportantYou can add language keys in the Language Override tool for Liferay DXP 7.4 U4+ or Liferay Portal 7.4 GA8+. For previous versions, you must add a
Language.propertiesfile under/src/main/resources/content/with the keys before building and deploying. -
Open the Global Menu (
) and click on Commerce → Order Rules. -
Click the Add button (
) and enter the following information:Name: Minimum Order Quantity - 3
Description: Testing minimum order quantity of 3 items
Type: X9K1 Minimum Order Quantity

-
Click Submit.
-
In the Configuration section, set the Minimum Quantity to 3.
-
Enable the new order rule by clicking on the Active toggle.

-
Click Publish.
-
Open the Global Menu (
), click on Control Panel → Sites, and add a new Minium Demo site. -
Log in as a buyer and add items to your cart. Click Submit to check out.
You can see a warning message if the order quantity is less than 3. Checkout is not possible until you meet this condition.

After activating an order rule, it applies to all Accounts, Account Groups, Order Types, and Channels. To control the eligibility, click the order rule’s Eligibility tab and select the appropriate option.
How the Custom Order Rule Works
This example has nine main steps:
- Annotate the Order Rule for OSGi Registration
- Review the
COREntryTypeinterface - Complete the
COREntryTypeimplementation - Add a display context
- Add a utility class to retrieve the minimum quantity value
- Annotate the JSP contributor for OSGi Registration
- Review the
COREntryTypeJSPContributorinterface - Complete the JSP contributor implementation
- Add a JSP to render the configuration of the Order Rule
Annotate the Order Rule for OSGi Registration
@Component(
property = {
"commerce.order.rule.entry.type.key=x9k1-minimum-quantity-order-rule",
"commerce.order.rule.entry.type.order:Integer=1"
},
service = COREntryType.class
)
public class X9K1MinimumQuantityCOREntryTypeImpl implements COREntryType {
You must provide a distinct key for the order rule, so Liferay Commerce can distinguish it from others in the order rule registry. Specifying a key that is already in use overrides the existing associated type. The order determines its sort order in the drop-down. In this case, the order is 1, and it appears as the second item in the drop-down.
Review the COREntryType interface
Implement the following methods.
public boolean evaluate(COREntry corEntry, CommerceOrder commerceOrder) throws PortalException;
This method evaluates the order rule and returns true or false depending on whether the condition is met.
public boolean evaluate(COREntry corEntry, List<COREntryTypeItem> corEntryTypeItems)
This method evaluates multiple items to check against the order rule and returns true or false depending on whether the condition is met. This method is not needed for this sample.
public String getErrorMessage(COREntry corEntry, CommerceOrder commerceOrder, Locale locale) throws PortalException;
If the evaluated method returns false, this method returns a string containing the error message that renders a warning to the user.
public String getKey();
This method returns the unique key of the order rule. Using an existing key overrides that order rule.
public boolean isActive();
This method returns true to specify that the order rule is active.
public String getLabel(Locale locale);
This method returns the name of the order rule as it appears in the UI. This may be a language key or a string.
Complete the COREntryType implementation
@Override
public boolean evaluate(COREntry corEntry, CommerceOrder commerceOrder)
throws PortalException {
if (BigDecimalUtil.gt(
BigDecimal.valueOf(_getMinimumQuantity(corEntry)),
_getOrderQuantity(commerceOrder))) {
return false;
}
return true;
}
@Override
public boolean evaluate(
COREntry corEntry, List<COREntryTypeItem> corEntryTypeItems) {
throw new UnsupportedOperationException();
}
@Override
public String getErrorMessage(
COREntry corEntry, CommerceOrder commerceOrder, Locale locale)
throws PortalException {
StringBundler sb = new StringBundler();
sb.append("Order quantity is less than the minimum quantity ");
Double minimumQuantity = _getMinimumQuantity(corEntry);
sb.append(minimumQuantity);
sb.append(". Add ");
Double delta = BigDecimalUtil.subtract(
BigDecimal.valueOf(minimumQuantity),
_getOrderQuantity(commerceOrder));
sb.append(delta);
sb.append(" more item");
if (delta > 1) {
sb.append("s");
}
sb.append(" to continue.");
return sb.toString();
}
@Override
public String getKey() {
return "x9k1-minimum-quantity-order-rule";
}
@Override
public String getLabel(Locale locale) {
return LanguageUtil.get(locale, "x9k1-minimum-order-quantity");
}
@Override
public boolean isActive() {
return true;
}
To complete the order rule, you must implement the above methods. There are two utility methods added to get the order quantity and the minimum quantity configured in the order rule. The first overridden method is evaluate() and it checks if the current order passes the order rule or not. It returns true if it does and false otherwise.
The second method retrieves the error message for orders that don’t satisfy the order rule. It returns a String converted from a StringBuilder that contains all the terms. The third method returns the unique key, and the last method returns the label that appears on the UI.
There are two additional methods to get the minimum quantity of the order rule and the total order quantity. The first method is present in the utility class X9K1MinimumQuantityUtil. The second method is _getOrderQuantity(CommerceOrder commerceOrder). It returns the total order quantity as a sum of individual product quantities in the order.
Add a display context
public class X9K1MinimumQuantityDisplayContext {
public X9K1MinimumQuantityDisplayContext(COREntry corEntry) {
_corEntry = corEntry;
}
public Double getMinimumQuantity() {
return X9K1MinimumQuantityUtil.getMinimumQuantity(_corEntry);
}
private final COREntry _corEntry;
}
The code retrieves the value for the minimum quantity configured for the order rule from the display context, which contains a single field of type COREntry and it is set using the created order rule. The display context has one method to retrieve the minimum quantity configured for the order rule and it uses the utility class detailed below.
Add a utility class to retrieve the minimum quantity value
public class X9K1MinimumQuantityUtil {
public static Double getMinimumQuantity(COREntry corEntry) {
UnicodeProperties typeSettingsUnicodeProperties =
UnicodePropertiesBuilder.fastLoad(
corEntry.getTypeSettings()
).build();
return GetterUtil.getDouble(
typeSettingsUnicodeProperties.getProperty("minimum-quantity"));
}
}
The X9K1MinimumQuantityUtil class retrieves the minimum quantity configured for the order rule. It retrieves the value using the property’s name as set in the JSPkey.
Annotate the JSP contributor for OSGi Registration
@Component(
property = "commerce.order.rule.entry.type.jsp.contributor.key=x9k1-minimum-quantity-order-rule",
service = COREntryTypeJSPContributor.class
)
public class X9K1MinimumQuantityCOREntryTypeJSPContributor
implements COREntryTypeJSPContributor {
The commerce.order.rule.entry.type.jsp.contributor.key property determines the order rule for which the JSP contributor is implemented.
Review the COREntryTypeJSPContributor interface
public void render(long corEntryId, HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse) throws Exception;
The COREntryTypeJSPContributor interface contains one method that renders a JSP. The method requires the order rule’s id and objects of type HTTPServletRequest and HTTPServletResponse as arguments.
Complete the JSP contributor implementation
@Override
public void render(
long corEntryId, HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse)
throws Exception {
COREntry corEntry = _corEntryLocalService.getCOREntry(corEntryId);
X9K1MinimumQuantityDisplayContext x9k1MinimumQuantityDisplayContext =
new X9K1MinimumQuantityDisplayContext(corEntry);
httpServletRequest.setAttribute(
WebKeys.PORTLET_DISPLAY_CONTEXT, x9k1MinimumQuantityDisplayContext);
_jspRenderer.renderJSP(
_servletContext, httpServletRequest, httpServletResponse,
"/minimum_quantity.jsp");
}
@Reference
private COREntryLocalService _corEntryLocalService;
@Reference
private JSPRenderer _jspRenderer;
@Reference(target = "(osgi.web.symbolicname=com.acme.x9k1.impl)")
private ServletContext _servletContext;
To complete the JSP Contributor, you must implement the render() method. It retrieves the COREntry using the _corEntryLocalService and the corEntryId. Then it creates a new display context of type X9K1MinimumQuantityDisplayContext using the retrieved corEntry. This context is set to the httpServletRequest. The servletContext references the Bundle-Symbolic-Name from the bnd.bnd file. The JSPRenderer renders a JSP file with the renderJSP() method. It accepts the relative path of the JSP, servletContext, httpServletRequest, and httpServletResponse as arguments.
Add a JSP to render the configuration of the Order Rule
<%@ taglib uri="http://liferay.com/tld/aui" prefix="aui" %><%@
taglib uri="http://liferay.com/tld/commerce-ui" prefix="commerce-ui" %>
<%@ page import="com.acme.x9k1.internal.commerce.order.rule.web.display.context.X9K1MinimumQuantityDisplayContext" %>
<%@ page import="com.liferay.portal.kernel.util.WebKeys" %>
<%
X9K1MinimumQuantityDisplayContext x9k1MinimumQuantityDisplayContext = (X9K1MinimumQuantityDisplayContext)request.getAttribute(WebKeys.PORTLET_DISPLAY_CONTEXT);
%>
<div class="row">
<div class="col">
<commerce-ui:panel
bodyClasses="flex-fill"
title="Configuration"
>
<div class="row">
<div class="col">
<aui:input label="minimum-quantity" name="type--settings--minimum-quantity--" required="<%= true %>" type="text" value="<%= x9k1MinimumQuantityDisplayContext.getMinimumQuantity() %>">
<aui:validator name="number" />
</aui:input>
</div>
</div>
</commerce-ui:panel>
</div>
</div>
The JSP contains one input field to accept the minimum quantity for the order rule. It is retrieved through the display context and evaluated inside the custom order rule. The display context uses the utility class and fetches the field using the minimum-quantity name from the type settings configuration. The getMinimumQuantity() method retrieves the existing value if any.
Conclusion
Congratulations! You now know the basics for implementing the COREntryType interface and have added a new order rule to Liferay Commerce.