Driving Real-Time Pricing Digital Commerce: SAP Commerce with SAP CPS and S/4HANA

Estimated read time 17 min read

Introduction

Businesses demands delivering consistent pricing throughout the customer journey is key to modern digital commerce. By seamlessly connecting SAP Commerce with SAP Commerce Pricing Service (CPS) and deep-linking live S/4HANA business data delivers a modern, scalable solution for omnichannel pricing. Organizations can achieve real-time price calculation with full transparency for every product and transaction step. This blog demystifies the architecture, showcases the end-to-end solution, and explores technical best practices that make this integration foundational to digital commerce success.

Solution Overview

Our integrated solution enables:

The integration between SAP Commerce and SAP CPS enables real-time pricing functionality throughout the digital commerce experience.

Real-time context-aware price fetching for the Product Listing Page (PLP) and Product Detail Page (PDP) for all product types.

Dynamic context-aware cart price simulation and final price calculation during checkout.

Configuration-driven mapping of pricing conditions: total price, subtotal, base price, tax, and promotions.

SAP CPS acts as the orchestrator for pricing logic and maintains synchronization with SAP S/4HANA’s pricing data for end-to-end Enterprise-grade consistency.

System Architecture

 

Key Components:

SAP Commerce Cloud: Handles storefront logic, triggers pricing requests, manages cart/checkout, and exposes APIs (OCC).

SAP Commerce Pricing Service (CPS): Microservice or cloud-based component responsible for price calculation, orchestration, and condition mapping.

SAP S/4HANA: System of record for product master data, pricing procedures, condition records, promotions, and taxes.

Process Flow:

User action (PLP/PDP/cart/checkout) triggers a pricing request in Commerce.

Commerce generates a context-rich pricing API call (including product, customer, sales org, and relevant parameters) to CPS.

CPS retrieves or calculates prices, using mapping logic to apply configured condition types: base, subtotal, total, tax, promotion.

If required, CPS makes a synchronous call to S/4HANA’s pricing engine, drawing on master data and up-to-date pricing procedures.

CPS aggregates the response, applies additional logic (e.g., for promotions), and returns structured pricing details to Commerce.

Commerce returns the final price breakdown to the storefront (via OCC APIs) for user display.

For cart simulation or checkout, a fresh real-time price calculation is performed to ensure all conditions and promotions are current.

How It Works

Custom Configuration are injected at the base store level to select the relevant Pricing conditions for the relevant prices and also to enable the PLP, PDP prices retrieval from CPS.

Also the below classes have been extended to achieve the functionality.

PLP: 

public class CustomSearchResponseResultsPopulator<FACET_SEARCH_CONFIG_TYPE, INDEXED_TYPE_TYPE, INDEXED_PROPERTY_TYPE, INDEXED_TYPE_SORT_TYPE, ITEM> extends SearchResponseResultsPopulator

@Override
protected List<SearchResultValueData> buildResults(final SearchResult searchResult, final SearchQuery searchQuery)
{
final List<SearchResultValueData> result = super.buildResults(searchResult, searchQuery);

String sort = searchQuery.getNamedSort();
if(CollectionUtils.isNotEmpty(result)
&& ( StringUtils.isBlank(sort) || (StringUtils.isNotBlank(sort) && !SORT_KEY_IN.equalsIgnoreCase(sort))))
{
BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore();
if (Objects.nonNull(baseStore) && Objects.nonNull(baseStore.getSAPConfiguration()) && Boolean.TRUE.equals(baseStore.getSAPConfiguration().getPlpCPSPricing()))
{
customCPSPricingService.plpSimulatePricing(result, baseStore);
}
}

return result;
}

PDP:

public class CustomProductDiscountedPricePopulator<SOURCE extends ProductModel, TARGET extends ProductData>
extends AbstractDiscountPopulator<SOURCE, TARGET> {

@Override
public void populate(SOURCE source, TARGET target) throws ConversionException
{

//CPS Synchronous Pricing.
BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore();
if (Objects.nonNull(baseStore) && Objects.nonNull(baseStore.getSAPConfiguration()) && Boolean.TRUE.equals(baseStore.getSAPConfiguration().getPdpCPSPricing()))
{
customCPSPricingService.productSimulatePricing(target, source, baseStore);
}
}
}

CPS Pricing Service and Build the Pricing Document Input

public class CustomCPSPricingServiceImpl{

public void cpsSimulateOrder(AbstractOrderModel cart, BaseStoreModel baseStore)
{
resetCartPrices(cart);
try {
if (CollectionUtils.isNotEmpty(cart.getEntries())) {
LOG.info(“cpsSimulateOrder”);
final PricingDocumentInput pricingDocumentInput = customPricingDocumentInputPopulator.populate(cart);

// Prepare Pricing Result
final PricingDocumentResult pricingDocumentResult = charonPricingFacade.createPricingDocument(pricingDocumentInput);

List<PricingItemResult> pricingItemResultItems = pricingDocumentResult.getItems();

for (AbstractOrderEntryModel entryModel : cart.getEntries()) {
List<String> itemIds = getEntriesItemIds(entryModel, pricingDocumentInput);
List<PricingItemResult> pricingItemResults = pricingItemResultItems.stream().filter(pricingItemResultItem -> itemIds.contains(pricingItemResultItem.getItemId()))
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(pricingItemResults)) {
populateEntryBasePrice(entryModel, pricingItemResults, baseStore);
populateEntryBaseDiscount(entryModel, pricingItemResults, baseStore);
populateEntryEcoTax(entryModel, pricingItemResults, baseStore);
populateEntryDealerMargin(entryModel, pricingItemResults, baseStore);
populateEntryTotalPrice(entryModel, pricingItemResults, baseStore);

entryModel.setCalculated(true);
modelService.save(entryModel);
}
}
populateCartSubTotalPrice(cart, pricingDocumentResult, baseStore);
populateCartEcoTax(cart, pricingDocumentResult, baseStore);
populateCartDealerMargin(cart, pricingDocumentResult, baseStore);
populateCartDiscount(cart, pricingDocumentResult, baseStore);
populateCartTax(cart, pricingDocumentResult, baseStore);
populateCartTotalPrice(cart, pricingDocumentResult, baseStore);

cart.setCalculated(true);
cart.setErrorDuringSimulation(false);
}
} catch (ConfigurationEngineException | PricingEngineException exp) {
LOG.error(String.format(“CPS Order Simulation failed due to %s”, exp.getMessage()));
cart.setErrorDuringSimulation(true);
}
modelService.save(cart);
}

public void productSimulatePricing(ProductData productData, ProductModel productModel, BaseStoreModel baseStore)
{
LOG.info(“productSimulatePricing”);
if(Objects.nonNull(productModel))
{
PricingDocumentResult pricingDocumentResult = null;
productData.setPricingError(Boolean.FALSE);
try
{
// Prepare Pricing Input
final PricingDocumentInput pricingDocumentInput = customProductPricingDocInputPopulator.populate(productModel);
pricingDocumentResult= charonPricingFacade.createPricingDocument(pricingDocumentInput);

} catch (PricingEngineException e) {
LOG.error(“Error while populating Product PDP price from CPS!”, e);
productData.setPricingError(Boolean.TRUE);
}
populateProductSubTotalPrice(productData, pricingDocumentResult, baseStore);
populateProductDiscountLabel(productData, pricingDocumentResult, baseStore);
populateProductTotalPrice(productData, pricingDocumentResult, baseStore);
}
}

public void plpSimulatePricing(List<SearchResultValueData> result, BaseStoreModel baseStore)
{
LOG.info(“plpSimulatePricing”);
resetePLPPrices(result);
try
{
// Prepare Pricing Input
final PricingDocumentInput pricingDocumentInput = customPLPPricingDocInputPopulator.populate(result);
PricingDocumentResult pricingDocumentResult= charonPricingFacade.createPricingDocument(pricingDocumentInput);

for (PricingItemResult pricingItemResult : pricingDocumentResult.getItems())
{
SearchResultValueData data = result.get(Integer.valueOf(pricingItemResult.getItemId()));

populatePLPSubTotalPrice(data, pricingItemResult, baseStore);
populatePLPDiscountLabel(data, pricingItemResult, baseStore);
populatePLPTotalPrice(data, pricingItemResult, baseStore);
}

} catch (PricingEngineException e) {
LOG.error(“Error while populating PLP price from CPS!”, e);
result.forEach(data->data.getValues().put(“pricingError”,Boolean.TRUE));
}
}

}

public class CustomPLPPricingDocumentInputPopulator extends AbstractPricingDocumentInputPopulator
{
public PricingDocumentInput populate(List<SearchResultValueData> result)
{
PricingDocumentInput target = new PricingDocumentInput();
fillCoreAttributes(target);
fillPricingItemsInput(result, target);
return target;
}

protected void fillPricingItemsInput(List<SearchResultValueData> result, final PricingDocumentInput target)
{
target.setItems(new ArrayList<>());

int i=0;
for(SearchResultValueData data:result)
{
populateAsNormalItem(i++, data, target);
}
}

protected void populateAsNormalItem(Integer i, SearchResultValueData data, final PricingDocumentInput target)
{
String code = (String) data.getValues().get(“code”);

CPSQuantity cpsQuantity = new CPSQuantity();
cpsQuantity.setUnit(“PCE”);
cpsQuantity.setValue(1d);

CPSItem source = new CPSItem();
source.setId(i.toString());
source.setQuantity(cpsQuantity);
source.setKey(code);
source.setType(SapproductconfigruntimecpsConstants.ITEM_TYPE_MARA);
source.setVariantConditions(new ArrayList<>());

fillPricingItemInput(source, target.getItems(), null);
}

protected void fillPricingItemInput(final CPSItem item, final List<PricingItemInput> target, final ConfigurationRetrievalOptions context)
{
PricingItemInput pricingItemInput = pricingItemInputConverter.convertWithContext(item, context);
target.add(pricingItemInput);
}

}

public class CustomProductPricingDocumentInputPopulator extends AbstractPricingDocumentInputPopulator
{
public PricingDocumentInput populate(final ProductModel product) {
PricingDocumentInput target = new PricingDocumentInput();
fillCoreAttributes(target);
fillPricingItemsInput(product, target);
return target;
}

protected void fillPricingItemsInput(final ProductModel product, final PricingDocumentInput target) {
target.setItems(new ArrayList<>());
populateAsNormalItem(product, target);
}

protected void populateAsNormalItem(ProductModel product, final PricingDocumentInput target) {

CPSQuantity cpsQuantity = new CPSQuantity();
cpsQuantity.setUnit(“PCE”);
cpsQuantity.setValue(1d);

CPSItem source = new CPSItem();
source.setId(“1”);
source.setQuantity(cpsQuantity);
source.setKey(product.getCode());
source.setType(SapproductconfigruntimecpsConstants.ITEM_TYPE_MARA);
source.setVariantConditions(new ArrayList<>());

fillPricingItemInput(source, target.getItems(), null);
}

protected void fillPricingItemInput(final CPSItem item, final List<PricingItemInput> target,
final ConfigurationRetrievalOptions context)
{
PricingItemInput pricingItemInput = pricingItemInputConverter.convertWithContext(item, context);
target.add(pricingItemInput);
}

}

 

1. Real-Time Price Retrieval

Each time a customer browses a product (PLP/PDP), the Commerce storefront triggers a call to the CPS service. The request includes product ID, customer context, and essential pricing parameters.

For every product displayed, Commerce invokes CPS, passing the product, customer details, and required pricing condition context.

CPS returns itemized pricing: base price, taxes, promotions, mapped from condition types configured in S/4HANA.

2. Pricing Condition Mapping

SAP CPS is configured to map S/4HANA pricing condition types to required display elements (base price, subtotal, total, tax, promotion). For example, PR00 for base price, MWST for tax, or ZPR0 for promotions.

Mapping between SAP S/4HANA pricing conditions (e.g., PR00 for base price, MWST for tax, ZPR0 for promotions) and Commerce pricing roles is handled in CPS.

Price response structure typically includes:

Base Price: Sourced via condition type (e.g., PR00)

Subtotal: Sum prior to tax/promotion (may use calculated subtotal condition)

Tax: Fed from tax condition (e.g., MWST)

Promotion/Discount: Special condition types as assigned in S/4HANA pricing procedures

This mapping is managed in CPS configuration, allowing for rapid change without code updates in Commerce.

3. Cart Simulation and Checkout

When items are added to the cart or quantities changed, Commerce requests a fresh price simulation from CPS, capturing up-to-date promotions and calculating taxes dynamically. During checkout, the system fetches real-time prices from S/4HANA, ensuring all figures are final and compliant.

Each cart event (add/remove/update item) triggers a re-simulation request to CPS.

CPS may aggregate line-item responses, apply global/cart-level promotions, and respect tax logic.

At checkout, a final real-time price retrieval ensures promotions and pricing are current, before order submission.

4. S/4HANA Synchronization

CPS either fetches current prices from S/4HANA (via synchronous API) or synchronizes relevant condition master data. This guarantees every price shown reflects S/4HANA’s latest logic, promoting transparency and auditability.

Master data and pricing condition records are synchronized (batch or real-time) via CPI to keep price calculations accurate.

SAP CPS either performs stateless pricing (reading reference data from S/4HANA) or document pricing (mirroring S/4HANA calculation logic in simulated documents).

Consistency is maintained by aligning product, customer, and pricing data between Commerce, CPS, and S/4HANA to prevent discrepancies and ensure synchronized updates.

Configuration Highlights

Flexible Condition Assignment: Pricing condition codes can be mapped per channel, market, or customer segment in CPS configuration.

Promotion Engine Integration: Cart-level and item-level discounts are handled through mapped pricing condition types, enabling commerce-driven promotion scenarios to flow through SAP’s pricing stack.

Extensibility: The entire architecture supports plug-and-play enhancements, such as new tax rules or advanced promotion logic, without deep code changes.

Business Impact

Customer Trust: Shoppers see consistent, reliable pricing from product discovery to checkout.

Operational Agility: Pricing changes in S/4HANA take effect instantly in Commerce.

Compliance: All tax and price breakdowns are traceable to ERP rules, simplifying audits.

Key Benefits

Unified, accurate pricing across all customer touchpoints.

Seamless integration for complex pricing scenarios, including promotions, tiered pricing, and taxes.

Real-time pricing reflection driven by master data consistency and robust API orchestration.

Extensible architecture supporting further enhancements and new pricing models.

Summary

This architecture achieves the grail of omnichannel commerce: unified, authoritative, and real-time pricing for every product and transaction. The combination of SAP Commerce, CPS, and S/4HANA provides a scalable foundation for global businesses requiring flexibility, transparency, and operational excellence. Integrating SAP Commerce Cloud with SAP CPS and S/4HANA pricing transforms the digital commerce experience. Enterprises gain agility, compliance, and a seamless customer journey, all backed by the power of modern SAP integration best practices.​

 

​ IntroductionBusinesses demands delivering consistent pricing throughout the customer journey is key to modern digital commerce. By seamlessly connecting SAP Commerce with SAP Commerce Pricing Service (CPS) and deep-linking live S/4HANA business data delivers a modern, scalable solution for omnichannel pricing. Organizations can achieve real-time price calculation with full transparency for every product and transaction step. This blog demystifies the architecture, showcases the end-to-end solution, and explores technical best practices that make this integration foundational to digital commerce success.Solution OverviewOur integrated solution enables:The integration between SAP Commerce and SAP CPS enables real-time pricing functionality throughout the digital commerce experience.Real-time context-aware price fetching for the Product Listing Page (PLP) and Product Detail Page (PDP) for all product types.Dynamic context-aware cart price simulation and final price calculation during checkout.Configuration-driven mapping of pricing conditions: total price, subtotal, base price, tax, and promotions.SAP CPS acts as the orchestrator for pricing logic and maintains synchronization with SAP S/4HANA’s pricing data for end-to-end Enterprise-grade consistency.System Architecture Key Components:SAP Commerce Cloud: Handles storefront logic, triggers pricing requests, manages cart/checkout, and exposes APIs (OCC).SAP Commerce Pricing Service (CPS): Microservice or cloud-based component responsible for price calculation, orchestration, and condition mapping.SAP S/4HANA: System of record for product master data, pricing procedures, condition records, promotions, and taxes.Process Flow:User action (PLP/PDP/cart/checkout) triggers a pricing request in Commerce.Commerce generates a context-rich pricing API call (including product, customer, sales org, and relevant parameters) to CPS.CPS retrieves or calculates prices, using mapping logic to apply configured condition types: base, subtotal, total, tax, promotion.If required, CPS makes a synchronous call to S/4HANA’s pricing engine, drawing on master data and up-to-date pricing procedures.CPS aggregates the response, applies additional logic (e.g., for promotions), and returns structured pricing details to Commerce.Commerce returns the final price breakdown to the storefront (via OCC APIs) for user display.For cart simulation or checkout, a fresh real-time price calculation is performed to ensure all conditions and promotions are current.How It WorksCustom Configuration are injected at the base store level to select the relevant Pricing conditions for the relevant prices and also to enable the PLP, PDP prices retrieval from CPS.Also the below classes have been extended to achieve the functionality.PLP: public class CustomSearchResponseResultsPopulator<FACET_SEARCH_CONFIG_TYPE, INDEXED_TYPE_TYPE, INDEXED_PROPERTY_TYPE, INDEXED_TYPE_SORT_TYPE, ITEM> extends SearchResponseResultsPopulator @Override protected List<SearchResultValueData> buildResults(final SearchResult searchResult, final SearchQuery searchQuery) { final List<SearchResultValueData> result = super.buildResults(searchResult, searchQuery); String sort = searchQuery.getNamedSort(); if(CollectionUtils.isNotEmpty(result) && ( StringUtils.isBlank(sort) || (StringUtils.isNotBlank(sort) && !SORT_KEY_IN.equalsIgnoreCase(sort)))) { BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore(); if (Objects.nonNull(baseStore) && Objects.nonNull(baseStore.getSAPConfiguration()) && Boolean.TRUE.equals(baseStore.getSAPConfiguration().getPlpCPSPricing())) { customCPSPricingService.plpSimulatePricing(result, baseStore); } } return result; }PDP:public class CustomProductDiscountedPricePopulator<SOURCE extends ProductModel, TARGET extends ProductData> extends AbstractDiscountPopulator<SOURCE, TARGET> { @Override public void populate(SOURCE source, TARGET target) throws ConversionException { //CPS Synchronous Pricing. BaseStoreModel baseStore = baseStoreService.getCurrentBaseStore(); if (Objects.nonNull(baseStore) && Objects.nonNull(baseStore.getSAPConfiguration()) && Boolean.TRUE.equals(baseStore.getSAPConfiguration().getPdpCPSPricing())) { customCPSPricingService.productSimulatePricing(target, source, baseStore); } }}CPS Pricing Service and Build the Pricing Document Inputpublic class CustomCPSPricingServiceImpl{ public void cpsSimulateOrder(AbstractOrderModel cart, BaseStoreModel baseStore) { resetCartPrices(cart); try { if (CollectionUtils.isNotEmpty(cart.getEntries())) { LOG.info(“cpsSimulateOrder”); final PricingDocumentInput pricingDocumentInput = customPricingDocumentInputPopulator.populate(cart); // Prepare Pricing Result final PricingDocumentResult pricingDocumentResult = charonPricingFacade.createPricingDocument(pricingDocumentInput); List<PricingItemResult> pricingItemResultItems = pricingDocumentResult.getItems(); for (AbstractOrderEntryModel entryModel : cart.getEntries()) { List<String> itemIds = getEntriesItemIds(entryModel, pricingDocumentInput); List<PricingItemResult> pricingItemResults = pricingItemResultItems.stream().filter(pricingItemResultItem -> itemIds.contains(pricingItemResultItem.getItemId())) .collect(Collectors.toList()); if (CollectionUtils.isNotEmpty(pricingItemResults)) { populateEntryBasePrice(entryModel, pricingItemResults, baseStore); populateEntryBaseDiscount(entryModel, pricingItemResults, baseStore); populateEntryEcoTax(entryModel, pricingItemResults, baseStore); populateEntryDealerMargin(entryModel, pricingItemResults, baseStore); populateEntryTotalPrice(entryModel, pricingItemResults, baseStore); entryModel.setCalculated(true); modelService.save(entryModel); } } populateCartSubTotalPrice(cart, pricingDocumentResult, baseStore); populateCartEcoTax(cart, pricingDocumentResult, baseStore); populateCartDealerMargin(cart, pricingDocumentResult, baseStore); populateCartDiscount(cart, pricingDocumentResult, baseStore); populateCartTax(cart, pricingDocumentResult, baseStore); populateCartTotalPrice(cart, pricingDocumentResult, baseStore); cart.setCalculated(true); cart.setErrorDuringSimulation(false); } } catch (ConfigurationEngineException | PricingEngineException exp) { LOG.error(String.format(“CPS Order Simulation failed due to %s”, exp.getMessage())); cart.setErrorDuringSimulation(true); } modelService.save(cart); } public void productSimulatePricing(ProductData productData, ProductModel productModel, BaseStoreModel baseStore) { LOG.info(“productSimulatePricing”); if(Objects.nonNull(productModel)) { PricingDocumentResult pricingDocumentResult = null; productData.setPricingError(Boolean.FALSE); try { // Prepare Pricing Input final PricingDocumentInput pricingDocumentInput = customProductPricingDocInputPopulator.populate(productModel); pricingDocumentResult= charonPricingFacade.createPricingDocument(pricingDocumentInput); } catch (PricingEngineException e) { LOG.error(“Error while populating Product PDP price from CPS!”, e); productData.setPricingError(Boolean.TRUE); } populateProductSubTotalPrice(productData, pricingDocumentResult, baseStore); populateProductDiscountLabel(productData, pricingDocumentResult, baseStore); populateProductTotalPrice(productData, pricingDocumentResult, baseStore); } } public void plpSimulatePricing(List<SearchResultValueData> result, BaseStoreModel baseStore) { LOG.info(“plpSimulatePricing”); resetePLPPrices(result); try { // Prepare Pricing Input final PricingDocumentInput pricingDocumentInput = customPLPPricingDocInputPopulator.populate(result); PricingDocumentResult pricingDocumentResult= charonPricingFacade.createPricingDocument(pricingDocumentInput); for (PricingItemResult pricingItemResult : pricingDocumentResult.getItems()) { SearchResultValueData data = result.get(Integer.valueOf(pricingItemResult.getItemId())); populatePLPSubTotalPrice(data, pricingItemResult, baseStore); populatePLPDiscountLabel(data, pricingItemResult, baseStore); populatePLPTotalPrice(data, pricingItemResult, baseStore); } } catch (PricingEngineException e) { LOG.error(“Error while populating PLP price from CPS!”, e); result.forEach(data->data.getValues().put(“pricingError”,Boolean.TRUE)); } }}public class CustomPLPPricingDocumentInputPopulator extends AbstractPricingDocumentInputPopulator{ public PricingDocumentInput populate(List<SearchResultValueData> result) { PricingDocumentInput target = new PricingDocumentInput(); fillCoreAttributes(target); fillPricingItemsInput(result, target); return target; } protected void fillPricingItemsInput(List<SearchResultValueData> result, final PricingDocumentInput target) { target.setItems(new ArrayList<>()); int i=0; for(SearchResultValueData data:result) { populateAsNormalItem(i++, data, target); } } protected void populateAsNormalItem(Integer i, SearchResultValueData data, final PricingDocumentInput target) { String code = (String) data.getValues().get(“code”); CPSQuantity cpsQuantity = new CPSQuantity(); cpsQuantity.setUnit(“PCE”); cpsQuantity.setValue(1d); CPSItem source = new CPSItem(); source.setId(i.toString()); source.setQuantity(cpsQuantity); source.setKey(code); source.setType(SapproductconfigruntimecpsConstants.ITEM_TYPE_MARA); source.setVariantConditions(new ArrayList<>()); fillPricingItemInput(source, target.getItems(), null); } protected void fillPricingItemInput(final CPSItem item, final List<PricingItemInput> target, final ConfigurationRetrievalOptions context) { PricingItemInput pricingItemInput = pricingItemInputConverter.convertWithContext(item, context); target.add(pricingItemInput); }}public class CustomProductPricingDocumentInputPopulator extends AbstractPricingDocumentInputPopulator{ public PricingDocumentInput populate(final ProductModel product) { PricingDocumentInput target = new PricingDocumentInput(); fillCoreAttributes(target); fillPricingItemsInput(product, target); return target; } protected void fillPricingItemsInput(final ProductModel product, final PricingDocumentInput target) { target.setItems(new ArrayList<>()); populateAsNormalItem(product, target); } protected void populateAsNormalItem(ProductModel product, final PricingDocumentInput target) { CPSQuantity cpsQuantity = new CPSQuantity(); cpsQuantity.setUnit(“PCE”); cpsQuantity.setValue(1d); CPSItem source = new CPSItem(); source.setId(“1”); source.setQuantity(cpsQuantity); source.setKey(product.getCode()); source.setType(SapproductconfigruntimecpsConstants.ITEM_TYPE_MARA); source.setVariantConditions(new ArrayList<>()); fillPricingItemInput(source, target.getItems(), null); } protected void fillPricingItemInput(final CPSItem item, final List<PricingItemInput> target, final ConfigurationRetrievalOptions context) { PricingItemInput pricingItemInput = pricingItemInputConverter.convertWithContext(item, context); target.add(pricingItemInput); }} 1. Real-Time Price RetrievalEach time a customer browses a product (PLP/PDP), the Commerce storefront triggers a call to the CPS service. The request includes product ID, customer context, and essential pricing parameters.For every product displayed, Commerce invokes CPS, passing the product, customer details, and required pricing condition context.CPS returns itemized pricing: base price, taxes, promotions, mapped from condition types configured in S/4HANA.2. Pricing Condition MappingSAP CPS is configured to map S/4HANA pricing condition types to required display elements (base price, subtotal, total, tax, promotion). For example, PR00 for base price, MWST for tax, or ZPR0 for promotions.Mapping between SAP S/4HANA pricing conditions (e.g., PR00 for base price, MWST for tax, ZPR0 for promotions) and Commerce pricing roles is handled in CPS.Price response structure typically includes:Base Price: Sourced via condition type (e.g., PR00)Subtotal: Sum prior to tax/promotion (may use calculated subtotal condition)Tax: Fed from tax condition (e.g., MWST)Promotion/Discount: Special condition types as assigned in S/4HANA pricing proceduresThis mapping is managed in CPS configuration, allowing for rapid change without code updates in Commerce.3. Cart Simulation and CheckoutWhen items are added to the cart or quantities changed, Commerce requests a fresh price simulation from CPS, capturing up-to-date promotions and calculating taxes dynamically. During checkout, the system fetches real-time prices from S/4HANA, ensuring all figures are final and compliant.Each cart event (add/remove/update item) triggers a re-simulation request to CPS.CPS may aggregate line-item responses, apply global/cart-level promotions, and respect tax logic.At checkout, a final real-time price retrieval ensures promotions and pricing are current, before order submission.4. S/4HANA SynchronizationCPS either fetches current prices from S/4HANA (via synchronous API) or synchronizes relevant condition master data. This guarantees every price shown reflects S/4HANA’s latest logic, promoting transparency and auditability.Master data and pricing condition records are synchronized (batch or real-time) via CPI to keep price calculations accurate.SAP CPS either performs stateless pricing (reading reference data from S/4HANA) or document pricing (mirroring S/4HANA calculation logic in simulated documents).Consistency is maintained by aligning product, customer, and pricing data between Commerce, CPS, and S/4HANA to prevent discrepancies and ensure synchronized updates.Configuration HighlightsFlexible Condition Assignment: Pricing condition codes can be mapped per channel, market, or customer segment in CPS configuration.Promotion Engine Integration: Cart-level and item-level discounts are handled through mapped pricing condition types, enabling commerce-driven promotion scenarios to flow through SAP’s pricing stack.Extensibility: The entire architecture supports plug-and-play enhancements, such as new tax rules or advanced promotion logic, without deep code changes.Business ImpactCustomer Trust: Shoppers see consistent, reliable pricing from product discovery to checkout.Operational Agility: Pricing changes in S/4HANA take effect instantly in Commerce.Compliance: All tax and price breakdowns are traceable to ERP rules, simplifying audits.Key BenefitsUnified, accurate pricing across all customer touchpoints.Seamless integration for complex pricing scenarios, including promotions, tiered pricing, and taxes.Real-time pricing reflection driven by master data consistency and robust API orchestration.Extensible architecture supporting further enhancements and new pricing models.​SummaryThis architecture achieves the grail of omnichannel commerce: unified, authoritative, and real-time pricing for every product and transaction. The combination of SAP Commerce, CPS, and S/4HANA provides a scalable foundation for global businesses requiring flexibility, transparency, and operational excellence. Integrating SAP Commerce Cloud with SAP CPS and S/4HANA pricing transforms the digital commerce experience. Enterprises gain agility, compliance, and a seamless customer journey, all backed by the power of modern SAP integration best practices.​   Read More Technology Blog Posts by SAP articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author