scapi – The Rhino Inquisitor https://www.rhino-inquisitor.com Get your insights on Salesforce Commerce Cloud B2C development! Tue, 29 Jul 2025 12:47:51 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.2 https://www.rhino-inquisitor.com/wp-content/uploads/2022/02/logo-wp-inquisitor.svg scapi – The Rhino Inquisitor https://www.rhino-inquisitor.com 32 32 What is new in the 23.8 Commerce Cloud release? https://www.rhino-inquisitor.com/what-is-new-in-the-23-8-commerce-cloud-release/ Thu, 27 Jul 2023 13:28:13 +0000 https://www.rhino-inquisitor.com/?p=9700 Everyone knows that getting ready for the holiday season starts in the summer in the Commerce Cloud world! And looking at the 23.8 release notes, this is apparent.  Are you interested in last month’s release notes? Click here! Infrastructure and Scaling Those who have reviewed the release notes may not notice many new additions to explore […]

The post What is new in the 23.8 Commerce Cloud release? appeared first on The Rhino Inquisitor.

]]>

Everyone knows that getting ready for the holiday season starts in the summer in the Commerce Cloud world! And looking at the 23.8 release notes, this is apparent. 

Are you interested in last month’s release notes? Click here!

Infrastructure and Scaling

Those who have reviewed the release notes may not notice many new additions to explore in the 23.8 release. However, upon closer inspection, it becomes apparent that the emphasis has been placed on improving infrastructure and scalability, which could account for the absence of fresh features.

Let’s take a look!

PWA Kit (3.1.0)

The latest release of the PWA Kit now allows for the conditional enabling of cookies within Managed Runtime. Previously, cookies were not allowed on SSR requests, optimising performance but limited use cases where cookie data was required.

With this new functionality, you can enable cookies per environment and modify their application logic and response via ssr.js. This means that after a customer logs in, the experience can be altered by setting the cache control header to private and leveraging the cookie on the response.

To enable cookies, set the allow_cookies attribute on an environment with the projects_target_partial_update endpoint and use PWA Kit version 3.1.0 or greater.

With this new update, we get more flexibility to support use cases! It is crucial, however, to think about performance, as this can potentially lower your cache-hit ratio!

OCAPI & SCAPI

Get Accurate Allocation Amounts with a Null Allocation Payload

PATCH and PUT /product_inventory_records/{product_id} now supports a null allocation payload while keeping accurate account of product allocation amounts and allocation reset date. Previously, sending a Patch or Put call to set a custom inventory record attribute using a null allocation payload resulted in an invalid reset date.

Salesforce has implemented a bugfix in the Inventory API to ensure that the allocation reset date remains in a valid state and does not become invalid. There is not much else to report about this update.

SCAPI

  • Optional query parameter locale, is now supported for mergeBasket, transferBasket and all delete endpoints in Shopper Orders and Shopper Baskets, with the exception of deleteBasket.
  • PaymentCardSpec includes a new field securityCodeLength. This is available in the response for Shopper baskets - getPaymentMethodsForBasket and Shopper Orders- getPaymentMethodsForOrder endpoints.
  • Coming soon: mergeBasket and transferBasket response will no longer include the property notes. Previously, this property was sent with an empty value in the response.
  • Fix coming soon: mergeBasket will return an HTTP 409 error response no-source-basket-exception if the guest's basket has already been ordered. Previously, the ordered guest basket was merged with the new basket.
  • Shopper Search productSearch now correctly handles storefront search queries with the & character and considers all terms before and after the &. Previously, the search query was incorrectly truncated before the & character and subsequent terms were missing in the query.

More support for the “locale” parameter should make many people happy! 

Nothing else to report except future bugfix statements and actual bug fixes. And, of course: scalability and infrastructure improvements.

SLAS

  • SLAS Infrastructure and scale improvements.
  • SLAS Admin UI improvements related to user search and get user statistics.
  • Fixed logout implementation. SLAS to OCAPI calls no longer fail throwing (ClientAccessForbiddenException)
  • As part of our efforts to scale the SLAS service for the upcoming holiday volume, temporarily, starting the week of August 7th, registered shopper sessions are valid for only 45 days. This applies to shopping apps integrated with SLAS, and to shoppers who have not returned to the shopping app at least once in the last 45 days need to relogin. This temporary state will conclude until September 15. After September 15, registered shopper login sessions will resume their full 90 day, standard duration. Shopper Guest sessions and B2C Commerce basket retention is not affected in any way.

As I previously stated, the emphasis is on enhancing infrastructure and expanding scalability. However, it is essential to note that SLAS will now only maintain active sessions for 45 days instead of the previous 90 days until September 15th. If you are using SLAS in your projects, please keep in mind this temporary change.

Account Manager

  • Security Fixes
  • Bug Fixes
  • Infrastructure Updates

A bit of familiar maintenance has happened on the Account Manager. But not to worry: there is more!

UUID API Access Tokens Deprecated

I have written extensively on this topic before, so it’s not something new. The due date has already passed and your only recourse now is to use JWT.

Specify Enhanced Domain Names In Account Manager

A screenshot showing how Salesforce Identity allows you to log in with other services, which is part of the updates in 23.8

You can now configure Identity Federation with Salesforce Identity in Account Manager using supported enhanced domain names. You can specify the organization MyDomain subdomain name in Salesforce Core. The default domain suffix is my.salesforce.com. If the identity federation is allowed or enforced, you can change the value. For example, if you use a Salesforce Core sandbox, you can use sandbox.my.salesforce.com as the domain suffix.

This is a great new feature if you’re using or planning to use Salesforce Identity.

If you are unfamiliar with it, you can have a single login for multiple Salesforce products. So depending on your situation, this is one to look into!

Stricter rules for API Client Passwords

For stronger passwords and better security, new password requirements are now enforced for Account Manager API Clients.

To ensure better security, there are new password requirements in place. Passwords must be at least 12 characters long and have a minimum complexity that includes three out of four – numbers, symbols, lower case, and upper case.

Passwords cannot include any part of your name, username, or UUID.

Auto Disable Inactive Users

Compliance with PCI DSS 8.1.4 requires that Account Manager user accounts are disabled when their accounts are inactive for 90 days. To support compliance with PCI DSS 8.1.4, Account Manager administrators can now set the number of inactive days before an account is disabled. When the setting is enabled, users with inactive accounts receive an email notification 10 days and 1 day before their account is deactivated. To keep an account active, users can log in to any Commerce Cloud application. To enable the setting, in Account Manager > Organization details, activate or deactivate the setting. The setting is disabled by default.

One of the benefits of using Salesforce Commerce Cloud is that users don’t have to concern themselves with infrastructure or compliance with various regulations, in this case: PCI DSS 8.1.4.

Remember that with this change, inactive accounts will be automatically disabled after 90 days of inactivity, but not deleted!

Updated Cartridges & Tools

resource manager (v2.0.1)

This cartridge contains a Business Manager module that allows editing, translating and publishing of resource bundles.

  • Version 2.0.1 is here! This version contains a slew of updates and improvements.
  • Added OpenAI translation support – you can now translate your keys and bundles using ChatGPT
  • Updated Webpack and added development config
  • Added ‘address’ sample bundle from SFRA
  • Refactoring of client-side code
  • Various UI improvements
  • Upgrade to the latest compatibility mode (22.7) to support template literals
  • Update README.md with the latest changes, recent screenshots and OpenAI setup instructions
  • Feature/translation support by @sfelius in #17

The post What is new in the 23.8 Commerce Cloud release? appeared first on The Rhino Inquisitor.

]]>
scapi Archives - The Rhino Inquisitor nonadult
Salesforce B2C Commerce Cloud 23.2 https://www.rhino-inquisitor.com/salesforce-b2c-commerce-cloud-23-2/ Wed, 18 Jan 2023 19:00:06 +0000 https://www.rhino-inquisitor.com/?p=6792 February is almost here, and so is the February 2023 (23.2) release! If you are an avid user of Page Designer, this is the update you have been waiting for – and Salesforce, keep ’em comin’! Are you interested in last month’s release notes? Click here! Page Designer Improvements Improved Page Designer Structure and Usability Old […]

The post Salesforce B2C Commerce Cloud 23.2 appeared first on The Rhino Inquisitor.

]]>

February is almost here, and so is the February 2023 (23.2) release! If you are an avid user of Page Designer, this is the update you have been waiting for – and Salesforce, keep ’em comin’!

Are you interested in last month’s release notes? Click here!

Page Designer Improvements

Improved Page Designer Structure and Usability

page designer structure oldv2
Old Page Designer Structure
New Page Designer Structure
New Page Designer Structure

The Page Designer page structure in the canvas editor has been redesigned. The asset hierarchy outlines all the assets on your page. The visual connection between what is selected in the canvas and its location in the page structure is also improved.

The redesign of the Salesforce Page Designer will be a good improvement. The new asset hierarchy and visual connection between what is selected in the canvas and its location in the page structure will make it much easier to navigate and edit pages. 

Overall, this will be a great step forward in enhancing the user experience.

Move Components in Page Designer More Easily

page designer move
Old Moving Component
New Moving Component
New Moving Component

Dragging components in Page Designer is now more intuitive. Know exactly where you can place a component. And after you drop a component and refresh the page, the component remains in focus and highlighted until another selection is made in the canvas.

How: Improvements to the drag functionality improve the Page Designer workflow.

  • Minimal changes in page structure during the drag function. When You initiate the drag process, you don’t lose the orientation to your starting location and desired drop location.

  • Clearly identifiable drop zones identify where you can drop a component. After you drop a component and refresh the page, the component remains in focus and highlighted.

  • After you drop a component and refresh the page, the component remains in focus and highlighted.

This update will make Page Designer more user-friendly. The ability to place components with minimal changes to the page structure, clear drop zones, and the component remaining in focus after refreshing the page are all features that will enhance the user’s experience with the designer.

UX seems to be “the” recurring theme in this release, which I believe is a welcome focus.

Save and Return to the Same Canvas Location in Page Designer

When you save a canvas action in Page Designer, you’re now returned to the same canvas location after the page reloads. Previously, you were returned to the top of the canvas.

This feature will likely be met with great relief, particularly for those who have experienced frustration while building large pages with many components.

Platform

Guest Basket Lifetime Limit Is Increased

The lifetime limit for a guest customer basket is now the lesser of 30 days, and the registered customer basket lifetime. Previously, it was the lesser of 7 days and the registered customer basket lifetime. This limit applies to input validation in the BM Basket Preferences UI and Basket Preferences Import. It also affects resolving the guest basket lifetime if it isn’t set, for example, for the baskets cleanup job.

This update increases the lifetime limit from 7 days to 30 days, meaning that guest customers can now have access to their baskets for much longer. This is a huge advantage as it allows anonymous shoppers more time to review their items, make changes, and complete their purchases.

Overall, this update brings many benefits and will significantly enhance the user experience.

Reports and Dashboards for PWA Kit

Starting now, Commerce Cloud Reports and Dashboards work for the Composable Storefront!

Business Manager

Do More When Calculating VAT

You can now calculate tax and use rounding based on tax groups, which is a legal requirement for Japanese taxation. The calculation method uses a new Tax Rounding Level site preference called Group. The tax groups are exposed via OCAPI and Script API to support customisation.

How: To enable rounding per grouped tax rate, in Business Manager Site Preferences, set the Order Item Price Rounding Policy to GROUP.

As I am not familiar with Japanese tax law, so it is not something I can go into. But for anyone who has a project active in Japan, I am sure it is a needed improvement.

Fine-Tune Your Site’s Regional Settings

You now have full control in Business Manager over all parameters used for converting numbers and dates into strings. Use the new Format Symbols tab and regional parameters to add Locale details.

Where: In Business Manager, select Administration | Locales | local ID

  • The Format Symbols tab is added for Locale details.
  • The Numeral System settings are removed on the Locale details General Tab.
  • New Regional setting parameters are added to Export and Import of locales.

A nice improvement for locale management because it gives users complete control over all parameters used for converting numbers and dates into strings with configuration. This is beneficial because it provides more flexibility and precision in formatting numbers and dates according to regional and language-specific standards.

OCAPI & SCAPI

Access Page Designer with SCAPI

You can now use the Shopper Experience SCAPI API to add content created in Page Designer to your B2C Commerce headless implementation. Use the SCAPI Shopper Experience API to look up page information for pages created in Page Designer. The Shopper Experience API responses include:

  • The entire component hierarchy of the page at design time.
  • All merchant data provided at design time.
  • Server-side scripting data provided at run time.

The new “Shopper Experience API” is a significant improvement for both Headless and Composable Storefront projects. In the past, solutions required complex workarounds to enable personalisation with session bridging. But this update eliminates the need for those workarounds.

This change is highly anticipated, and I am sure it will be warmly welcomed by many. It brings more efficiency and ease of use to projects in need of the API.

Other SCAPI Updates

  • Rate limit increase for GET /customers/*(Shopper-Customers), see Rate Limits.
  • Security update for SCAPI platform environment.
  • Maintenance and stability updates for SCAPI platform environment.
  • Update TrustedAgentOnBehalf support for Shopper Token policy.
  • Prepare SCAPI platform for future feature enablement.

SLAS Updates

Support Forgerock IDP

ForgeRock Identity Platform is an open-source identity management platform that provides authentication, authorisation, and identity management solutions for organisations. It is also a popular choice among financial institutions, healthcare providers, and government agencies to help them comply with industry regulations and standards.

This is also the system behind Account Manager.

Enhanced BOT mitigation strategy within SLAS.

The release log does not provide much more information on what specific actions have been taken to improve security. However, any improvement in security measures is generally welcome in order to better protect against malicious bots.

/jwks endpoint now returns 3 key IDs

JWKS (JSON Web Key Set) provides a set of keys that includes the current, past, and future public keys. This set of keys allows clients to check the authenticity of a token, called JWT, SLAS issues that for the shopper.

Other SLAS updates

  • Trusted Agent On Behalf (TAOB) now supports Private ClientID flow, and TAOB JWT token expiry changed from 30 to 15 minutes for PCI compliance.
  • Reduced the Passwordless OTP – token length from 20 to 8 characters.
  • Fixed inconsistencies related to failed tokens.
  • Session Bridge: Improved error messaging & guest support.
  • SLAS no longer calls ecom, when a shopper account is locked.
  • User cache refinements & Fixed cache inconsistencies after tenant key rotation.
  • Addressed login ID inconsistencies for passwordless login.
  • Fixed AppleIDP issue related to middle name.
  • Security library updates.

Development

Set Up Payments for Immediate or Future Payment Capture

You can use the SalesforcePaymentRequest script API to set immediate or future payment captures. Use this method to override the site-level payment settings and gain greater flexibility over how payments are captured.

New functions have been exposed on the SalesforcePaymentRequest:

				
					salesforcePaymentRequest.getCardCaptureAutomatic();
salesforcePaymentRequest.setCardCaptureAutomatic(Boolean);
				
			

Do More When Calculating VAT

New functions are now exposed to work with the new rounding options per group (see the item about Japanese Taxation):

				
					lineItemCtnr.getTaxTotalsPerTaxRate();
lineItemCtnr.isTaxRoundedAtGroup();
basket.isTaxRoundedAtGroup();
order.isTaxRoundedAtGroup()	

				
			

PWA Kit v2.5.0

A new preview release of the commerce-sdk-react library is now available on npm and includes ready-made React hooks for fetching data from B2C Commerce.

The release also includes updates to pwa-kit-create-app, pwa-kit-dev, pwa-kit-react-sdk, pwa-kit-runtime, and template-retail-react-app. Some of the changes include adding instanceType to Einstein activity body, logging cid from res header instead of req in local development, and handling variants with Einstein.

View the full changelog.

Bugfixes

The developers of the current release have been unable to catch a bug, much like trying to catch a fly with chopsticks, resulting in no bug fixes included in this version. But we trust they will keep trying to improve the product.

Updated Cartridges & Tools

Composable Storefront Toolkit

This repo is a composable storefront implementation with various proof of concepts baked in. It otherwise closely tracks pwa-kit.

A new cartridge has appeared, containing some goodies for the Composable Storefront! As all of these are POCs (Proof of Concept), understand that they still need some polishing.

b2c-tools (v0.15.0)

b2c-tools is a CLI tool and library for data migrations, import/export, scripting and other tasks with SFCC B2C instances and administrative APIs (SCAPI, ODS, etc). It is intended to be complimentary to other tools such as sfcc-ci for development and CI/CD scenarios.

  • adds custom preference support to instance info by @clavery in #95
  • intellij sfcc 2022.3 compatibility by @clavery in #97

Salesforce B2C Commerce / Customer 360 Platform Integration (v2.0.0)

Salesforce B2C Commerce / CRM Sync is an enablement solution designed by Salesforce Architects to teach Salesforce's B2C Customer Data Strategy for multi-cloud use-cases. The solution demonstrates a contemporary approach to the integration between Salesforce B2C Commerce and the Cloud products running on the Salesforce Customer 360 Platform.

The post Salesforce B2C Commerce Cloud 23.2 appeared first on The Rhino Inquisitor.

]]>
How to set up the eCDN for Staging in Salesforce B2C Commerce Cloud https://www.rhino-inquisitor.com/how-to-set-up-the-ecdn-in-sfcc-staging/ https://www.rhino-inquisitor.com/how-to-set-up-the-ecdn-in-sfcc-staging/#respond Mon, 07 Nov 2022 10:29:05 +0000 https://www.rhino-inquisitor.com/?p=5618 Deprecated Article Since the writing of this article, the eCDN business manager module has been updated to allow configuration of vanity domains on Staging. Therefore, there is no need to use API calls as described in this article (unless you really, really want to…). Read all about it here. Using the Business Manager module, setting […]

The post How to set up the eCDN for Staging in Salesforce B2C Commerce Cloud appeared first on The Rhino Inquisitor.

]]>

Using the Business Manager module, setting up custom vanity domains and uploading certificates on the production instance is easy. But how about staging?

Until recently, we needed to contact support to set up custom vanity domains with a valid certificate on the staging instances. Luckily that has changed, and now we can fully control the domains and certificates for staging “just like production”.

Yes, it has been put between quotes. Let us find out why!

API First

In an API-first manner, REST APIs are available to manage the eCDN (Cloudflare) for all our environments. But unlike production, there is yet to be a Business Manager module available to do this on staging.

Step 1: Create an API Client

To connect to the SCAPI, we need to create an API client with the correct scopes:

  • sfcc.cdn-zones
  • sfcc.cdn-zones.rw

Salesforce has written a guide on the developer support site to create an API Client for this use case.

Step 2: Get the staging credentials

Since the “CDN Zones” API is part of the SCAPI, we need to get our environment-specific credentials from the business manager. In this case, that is our Staging instance.

We get these settings here:

“Administration > Site Development > Salesforce Commerce API Settings”

Step 3: Get an access token

To communicate with the Zones API, we need a bearer token. This is fetched using the following API call to the Account Manager

				
					curl -i -k \
--data 'grant_type=client_credentials&scope=SALESFORCE_COMMERCE_API:<tenantID> sfcc.cdn-zones sfcc.cdn-zones.rw' \
--user '<client-id>:<client-secret>' \
-X POST 'https://account.demandware.com/dwsso/oauth2/access_token'
				
			

If all goes well, a response similar to the one below appears.

				
					{
    "access_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiJEMWhPUDdEODN4TjBqZWlqaTI3WWFvZFRjL0E9IiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiIxZDc2MzI2MS02NTIyLTQ5MTMtOWQ1Mi01ZDk0N2QzYjk0YzQiLCJjdHMiOiJPQVVUSDJfU1RBVEVMRVNTX0dSQU5UIiwiYXVkaXRUcmFja2luZ0lkIjoiZDBkZDk4MjItMmI3ZC00MThiLTkzZTktYzg2YjgxYjZjZGFhLTE2OTY2NTI3MSIsInN1Ym5hbWUiOiIxZDc2MzI2MS02NTIyLTQ5MTMtOWQ1Mi01ZDk0N2QzYjk0YzQiLCJpc3MiOiJodHRwczovL2FjY291bnQuZGVtYW5kd2FyZS5jb206NDQzL2R3c3NvL29hdXRoMiIsInRva2VuTmFtZSI6ImFjY2Vzc190b2tlbiIsInRva2VuX3R5cGUiOiJCZWFyZXIiLCJhdXRoR3JhbnRJZCI6IjcyWTZ0Slk1bm9vdEhMX2YzLWN5SzZrajVsOCIsImF1ZCI6IjFkNzYzMjYxLTY1MjItNDkxMy05ZDUyLTVkOTQ3ZDNiOTRjNCIsIm5iZiI6MTY1MTYyMjUzOCwiZ3JhbnRfdHlwZSI6ImNsaWVudF9jcmVkZW50aWFscyIsInNjb3BlIjpbIlNBTEVTRk9SQ0VfQ09NTUVSQ0VfQVBJOnp6dGVfMDUzIiwic2ZjYy5jYXRhbG9ncyIsIm1haWwiLCJ0ZW5hbnRGaWx0ZXIiLCJvcGVuSWQiLCJyb2xlcyJdLCJhdXRoX3RpbWUiOjE2NTE2MjI1MzgsInJlYWxtIjoiLyIsImV4cCI6MTY1MTYyNDMzOCwiaWF0IjoxNjUxNjIyNTM4LCJleHBpcmVzX2luIjoxODAwLCJqdGkiOiJlaXdlanNyWmxRdEpmMXhPZ0lJaVQ3REo2LTgiLCJjbGllbnRfaWQiOiIxZDc2MzI2MS02NTIyLTQ5MTMtOWQ1Mi01ZDk0N2QzYjk0YzQiLCJ0ZW5hbnRGaWx0ZXIiOiJTQUxFU0ZPUkNFX0NPTU1FUkNFX0FQSTp6enRlXzA1MyIsInJvbGVzIjpbIlNBTEVTRk9SQ0VfQ09NTUVSQ0VfQVBJIl19.N_D2gZuJfQcIo-X42O4i-hz1j4_KxYzaqYb4CqVSf96Zt9w5-WmDPP_swuIz2eCivxwrs0hyfKmDTS7mQG_fLXiuAr6FT0bMVYndfmSbngJl24eCXu2U6b5-cMlUmwAG7mO7Uji4_cXtayUCA9XGUSVXxu1HuiFzANws_D-cCWlgoaWpEvPmkhq3o_ICJvhcqaZTYDaoQ62hDToMqdojbRQFe6s2kDLiqOIi6Ey_VYev8bRTu4RtXmJ6Pfj_xp0mgc4ak8zaxCVcykZ-ziEE9TqO-tN1U6n0QnuTh-t3wz2iSyEcfJ3fOtv9v9zz1BYpe1qMCYDTkKnMq_alERtKZg",
    "scope": "SALESFORCE_COMMERCE_API:zzxx_001 sfcc.cdn-zones sfcc.cdn-zones.rw",
    "token_type": "Bearer",
    "expires_in": 1799
}
				
			

Step 4: Get all zones

First, check that we can use our newly fetched “access_token” to call the Zones API. This information can be obtained by making a GET call to the “Get zones info” endpoint.

				
					https://{shortCode}.api.commercecloud.salesforce.com/cdn/zones/v1/organizations/{organizationId}/zones/info

				
			

If all the steps have been adhered to above, a response like the one below will magically appear!

				
					[{
  "zoneId": "example1-zone-Id",
  "name": "example1.com",
  "status": "pending"
}]
				
			

Step 5: Create a zone (register domain)

Now that it is confirmed that API calls can be made, the first step in creating a custom domain for the Staging environment can be done: “Creating a zone in Cloudflare”.

To achieve this, the following API call must be made to the “Create storefront zone” endpoint:

				
					curl "https://{shortCode}.api.commercecloud.salesforce.com/cdn/zones/v1/organizations/{organizationId}/storefront-zones" \
  -X POST \
  -d "{\n  \"domainName\": \"cc-merchant.com\"\n}"
				
			

If the zone did not exist already and is created successfully, the following response is given:

				
					{
  "data": {
    "zoneId": "023e105f4ecef8ad9ca31a8372d0c353",
    "zoneName": "stg-zzzz-cc-merchant-com.cc-ecdn.net",
    "createdOn": "2022-01-01T05:20:00.12345Z",
    "status": "active"
  }
}
				
			

Step 6: Upload the certificate

Finally, we get to the “goal”: Uploading the certificate. To do that, an API call is made to the “Add certificate for zone” endpoint.

				
					https://{shortCode}.api.commercecloud.salesforce.com/cdn/zones/v1/organizations/{organizationId}/zones/{zoneId}/certificates
				
			

This is a POST call with the following body:

				
					{
  "hostname": "cc-merchant.com",
  "certificate": "-----BEGIN CERTIFICATE-----\\nMIIDtTCCAp2gAwIBAgIJAMHAwfXZ5/PWMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\\nBAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX\\naWRnaXRzIFB0eSBMdGQwHhcNMTYwODI0MTY0MzAxWhcNMTYxMTIyMTY0MzAxWjBF\\nMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50\\nZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\\nCgKCAQEAwQHoetcl9+5ikGzV6cMzWtWPJHqXT3wpbEkRU9Yz7lgvddmGdtcGbg/1\\nCGZu0jJGkMoppoUo4c3dts3iwqRYmBikUP77wwY2QGmDZw2FvkJCJlKnabIRuGvB\\nKwzESIXgKk2016aTP6/dAjEHyo6SeoK8lkIySUvK0fyOVlsiEsCmOpidtnKX/a+5\\n0GjB79CJH4ER2lLVZnhePFR/zUOyPxZQQ4naHf7yu/b5jhO0f8fwt+pyFxIXjbEI\\ndZliWRkRMtzrHOJIhrmJ2A1J7iOrirbbwillwjjNVUWPf3IJ3M12S9pEewooaeO2\\nizNTERcG9HzAacbVRn2Y2SWIyT/18QIDAQABo4GnMIGkMB0GA1UdDgQWBBT/LbE4\\n9rWf288N6sJA5BRb6FJIGDB1BgNVHSMEbjBsgBT/LbE49rWf288N6sJA5BRb6FJI\\nGKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV\\nBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAMHAwfXZ5/PWMAwGA1UdEwQF\\nMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAHHFwl0tH0quUYZYO0dZYt4R7SJ0pCm2\\n2satiyzHl4OnXcHDpekAo7/a09c6Lz6AU83cKy/+x3/djYHXWba7HpEu0dR3ugQP\\nMlr4zrhd9xKZ0KZKiYmtJH+ak4OM4L3FbT0owUZPyjLSlhMtJVcoRp5CJsjAMBUG\\nSvD8RX+T01wzox/Qb+lnnNnOlaWpqu8eoOenybxKp1a9ULzIVvN/LAcc+14vioFq\\n2swRWtmocBAs8QR9n4uvbpiYvS8eYueDCWMM4fvFfBhaDZ3N9IbtySh3SpFdQDhw\\nYbjM2rxXiyLGxB4Bol7QTv4zHif7Zt89FReT/NBy4rzaskDJY5L6xmY=\\n-----END CERTIFICATE-----\\n",
  "privateKey": "-----BEGIN RSA PRIVATE KEY-----\\nMIIEowIBAAKCAQEAwQHoetcl9+5ikGzV6cMzWtWPJHqXT3wpbEkRU9Yz7lgvddmG\\ndtcGbg/1CGZu0jJGkMoppoUo4c3dts3iwqRYmBikUP77wwY2QGmDZw2FvkJCJlKn\\nabIRuGvBKwzESIXgKk2016aTP6/dAjEHyo6SeoK8lkIySUvK0fyOVlsiEsCmOpid\\ntnKX/a+50GjB79CJH4ER2lLVZnhePFR/zUOyPxZQQ4naHf7yu/b5jhO0f8fwt+py\\nFxIXjbEIdZliWRkRMtzrHOJIhrmJ2A1J7iOrirbbwillwjjNVUWPf3IJ3M12S9pE\\newooaeO2izNTERcG9HzAacbVRn2Y2SWIyT/18QIDAQABAoIBACbhTYXBZYKmYPCb\\nHBR1IBlCQA2nLGf0qRuJNJZg5iEzXows/6tc8YymZkQE7nolapWsQ+upk2y5Xdp/\\naxiuprIs9JzkYK8Ox0r+dlwCG1kSW+UAbX0bQ/qUqlsTvU6muVuMP8vZYHxJ3wmb\\n+ufRBKztPTQ/rYWaYQcgC0RWI20HTFBMxlTAyNxYNWzX7RKFkGVVyB9RsAtmcc8g\\n+j4OdosbfNoJPS0HeIfNpAznDfHKdxDk2Yc1tV6RHBrC1ynyLE9+TaflIAdo2MVv\\nKLMLq51GqYKtgJFIlBRPQqKoyXdz3fGvXrTkf/WY9QNq0J1Vk5ERePZ54mN8iZB7\\n9lwy/AkCgYEA6FXzosxswaJ2wQLeoYc7ceaweX/SwTvxHgXzRyJIIT0eJWgx13Wo\\n/WA3Iziimsjf6qE+SI/8laxPp2A86VMaIt3Z3mJN/CqSVGw8LK2AQst+OwdPyDMu\\niacE8lj/IFGC8mwNUAb9CzGU3JpU4PxxGFjS/eMtGeRXCWkK4NE+G08CgYEA1Kp9\\nN2JrVlqUz+gAX+LPmE9OEMAS9WQSQsfCHGogIFDGGcNf7+uwBM7GAaSJIP01zcoe\\nVAgWdzXCv3FLhsaZoJ6RyLOLay5phbu1iaTr4UNYm5WtYTzMzqh8l1+MFFDl9xDB\\nvULuCIIrglM5MeS/qnSg1uMoH2oVPj9TVst/ir8CgYEAxrI7Ws9Zc4Bt70N1As+U\\nlySjaEVZCMkqvHJ6TCuVZFfQoE0r0whdLdRLU2PsLFP+q7qaeZQqgBaNSKeVcDYR\\n9B+nY/jOmQoPewPVsp/vQTCnE/R81spu0mp0YI6cIheT1Z9zAy322svcc43JaWB7\\nmEbeqyLOP4Z4qSOcmghZBSECgYACvR9Xs0DGn+wCsW4vze/2ei77MD4OQvepPIFX\\ndFZtlBy5ADcgE9z0cuVB6CiL8DbdK5kwY9pGNr8HUCI03iHkW6Zs+0L0YmihfEVe\\nPG19PSzK9CaDdhD9KFZSbLyVFmWfxOt50H7YRTTiPMgjyFpfi5j2q348yVT0tEQS\\nfhRqaQKBgAcWPokmJ7EbYQGeMbS7HC8eWO/RyamlnSffdCdSc7ue3zdVJxpAkQ8W\\nqu80pEIF6raIQfAf8MXiiZ7auFOSnHQTXUbhCpvDLKi0Mwq3G8Pl07l+2s6dQG6T\\nlv6XTQaMyf6n1yjzL+fzDrH3qXMxHMO/b13EePXpDMpY7HQpoLDi\\n-----END RSA PRIVATE KEY-----\\n"
}

				
			

When the request succeeds, and the certificate is checked to be valid, the information needed for the next step is in the response.

				
					{
  "certificateId": "3822ff90-ea29-44df-9e55-21300bb9419b",
  "status": "EXPIRED",
  "hosts": [
    "example.com",
    "www.example.com"
  ],
  "expiresOn": "2021-01-01T05:20:00Z",
  "uploadedOn": "2016-01-01T05:20:00Z",
  "issuer": "DigiCert",
  "signature": "SHA256WithRSA",
  "customHostnameVerificationTXTName": "_example.com",
  "customHostnameVerificationTXTValue": "4c9c3f4f-2e91-4c5d-a902-f12f9c285b9e",
  "customHostnameId": "354a48f6-3d98-4c15-9312-211984ee8518",
  "customHostname": "cc-merchant.com",
  "customHostnameStatus": "PENDING"
}
				
			

Encoding the certificate and key

Since JSON is used, the body of our request needs to be safe to use in that manner.

Certificates and Private Keys tend to have several new line characters incompatible with JSON.

These need to be “escaped”. Luckily, many online tools can help you with this chore.

Step 7: Validate ownership of the domain

For this step, we need the person who manages the DNS records of the domain in question. To prove we own this domain to Cloudflare, a txt record must be added to its configuration.

The data of this TXT record was in the response of the previous step:

				
					{
  ...
  "customHostnameVerificationTXTName": "_example.com",
  "customHostnameVerificationTXTValue": "4c9c3f4f-2e91-4c5d-a902-f12f9c285b9e",
  ...
}
				
			

Once this record is added, the “Get certificates” endpoint is available to track the status. As soon as it changes to “ACTIVE”, you can go to the next step!

Step 8: Update the DNS records

The final step is to set the CNAME record in the DNS for the domain.

This is the combination of “commcloud.<zone_name>”.

The “zone_name” was retrieved in step 4 when the zone was created. It is always possible to do the “Get zones info” API call to get this information.

An example:

				
					commcloud.stg-zzzz-cc-merchant-com.cc-ecdn.net
				
			

Step 9: Business Manager (Optional)

If you make use of vanity domains in the business manager, you will have to contact Support in order to manage this “zone” through the API.

Post-migration tasks validate your eCDN traffic flow and setup. To complete the post-migration process, coordinate with Commerce Cloud Engineering.Commerce Cloud Engineering creates and activates a staging Business Manager zone to handle your Business Manager traffic that goes through only your Business Manager host name, for example, staging-<realm>-<customer>.demandware.net. Business Manager doesn’t have an eCDN management page for staging instances.

Commerce Cloud Engineering services include the demandware.net lockdown protections that protect your production and development instances.

Post-migration tasks:

1. Revert to the *.demandware.net certificate at the origin: This step applies to custom certificates installed to the POD for the staging instance. Commerce Cloud Engineering validates that traffic is flowing through eCDN for the configured host names. After validation, the certificate at the origin level is reverted back to the standard *.demandware.net certificate. The Salesforce Commerce API (SCAPI) can then connect to your staging instance.

2. Create and activate a staging Business Manager zone: Commerce Cloud Engineering creates and activates the Business Manager zone for your implementation.

If you use the self-service steps to create an eCDN zone and certificate, contact SFCC Support. They contact Commerce Cloud Engineering to create and activate a staging Business Manager zone.

If you implement an existing custom certificate for your staging instance, Commerce Cloud Engineering creates and activates the staging Business Manager zone without a GUS request. You can request to opt out of this component.

All done!

After all of these steps are complete, the domain can be used to reach the staging environment with a valid certificate!

The post How to set up the eCDN for Staging in Salesforce B2C Commerce Cloud appeared first on The Rhino Inquisitor.

]]>
https://www.rhino-inquisitor.com/how-to-set-up-the-ecdn-in-sfcc-staging/feed/ 0
The OCAPI/SCAPI Hooks Playbook: A Deep Dive into Salesforce B2C Commerce Best Practices https://www.rhino-inquisitor.com/how-to-use-ocapi-scapi-hooks/ https://www.rhino-inquisitor.com/how-to-use-ocapi-scapi-hooks/#comments Mon, 31 Oct 2022 13:03:53 +0000 https://www.rhino-inquisitor.com/?p=2265 Info This article was updated with the latest and most important feature information as of 26 July 2025. So, you need to add a custom attribute to the basket response, or maybe validate an order against a third-party fraud service before it’s created. Your first thought? A SCAPI hook. You’re not wrong, but you’re only […]

The post The OCAPI/SCAPI Hooks Playbook: A Deep Dive into Salesforce B2C Commerce Best Practices appeared first on The Rhino Inquisitor.

]]>

So, you need to add a custom attribute to the basket response, or maybe validate an order against a third-party fraud service before it’s created. Your first thought? A SCAPI hook. You’re not wrong, but you’re only seeing the tip of the iceberg.

Salesforce Commerce API (SCAPI) and OCAPI (Open Commerce API) hooks are one of the most powerful tools in our arsenal for extending the platform’s headless capabilities. They allow us to inject custom logic directly into the API lifecycle, tailoring the out-of-the-box behaviour to meet unique business requirements. But let’s be clear: with great power comes great responsibility. 

The official documentation provides the “what” and the “how,” but it’s in the wild, under a production load, where the real lessons are learned. These powerful tools, if used improperly, can be extremely hazardous, potentially introducing security vulnerabilities, performance bottlenecks, and maintenance issues.  

This isn’t just a rehash of the official docs. This is a field guide, a playbook forged from experience. We’re going to dive deep into the critical areas that documentation often glosses over: security hardening, performance tuning, bulletproof error handling, and avoiding the architectural traps that lead to what architects call a “Big Ball of Mud”. We’ll cover everything from the fundamental anatomy of a hook to advanced strategies like idempotency and circuit breakers.  

Our roadmap is clear: we’ll start with the fundamentals, then navigate the security gauntlet, tackle the need for speed, prepare for when things go wrong, and finally, tour the hall of shame of common anti-patterns. 

Let’s get started.

The Anatomy of a "Hook"

Before we can master SCAPI and OCAPI hooks, we need to understand them from the ground up. This means not only knowing what they are but also what they are not, and how they fit into the broader SFCC extensibility landscape.

What Are Hooks, Really?

At their core, hooks are a mechanism for altering and extending the behaviour of existing API resources using server-side B2C Commerce Script API logic. They are not standalone endpoints; rather, they are extension points that allow you to inject your custom code into the platform’s standard API request lifecycle. 

This brings us to a critical architectural crossroads. The platform provides two primary methods for adding custom server-side logic to SCAPI and OCAPI: hooks (SCAPI / OCAPI) and Custom APIs (SCAPI only). The choice you make here will define the maintainability and scalability of your solution.

Hooks are fundamentally tethered to an existing Salesforce endpoint, like /baskets or /orders. They can only react to calls made to that endpoint. Their purpose is to augment an existing process. For example:

  • Adding a custom attribute to the basket response.

  • Validating a shipping address against a third-party service.

  • Calculating a complex, custom surcharge on an order.

Custom APIs, on the other hand, allow you to create and expose entirely new, net-new REST endpoints under the SCAPI framework. If your goal is to introduce a new capability that doesn’t logically fit within an existing API’s model, a Custom API is the correct strategic choice:

  • A /loyalty-info endpoint to fetch a customer’s points balance.

  • A /pickup-point-locator endpoint to find nearby physical stores.

  • An endpoint to handle a custom newsletter signup form.

The introduction of Custom APIs, especially with the 23.9 release, was a game-changer, moving us beyond the old workarounds of trying to tweak existing endpoints to serve entirely new purposes. 

Choosing the wrong tool leads to technical debt. 

Trying to shoehorn new functionality into an existing hook results in convoluted, hard-to-maintain code that compromises the original intent of the endpoint. Make the right architectural choice before you write a single line of code.

The Three Musketeers: before, after, and modifyResponse

SCAPI and OCAPI hooks come in three main flavours, each with a distinct role in the request lifecycle. Understanding their specific purpose and limitations is crucial to using them correctly. 

  • before<HTTP_Method>: This hook executes before the server performs its main processing. Its primary role is to validate input and preprocess the incoming request document. This is your first line of defence, where you can perform status checks, apply additional filtering logic, or validate data before it ever touches the core system objects.

  • after<HTTP_Method>: This hook executes after the server’s main logic has completed but before the final response document is created. It operates on the modified Script API object (e.g., the Basket or Order object). This is the place for side effects and integrations, such as sending a newly created order to an external ERP, triggering a basket recalculation (dw.order.calculate), or performing change tracking.

  • modify<HTTP_Method>Response: This is the final step in the chain. It executes after the platform has already created the response document from the Script API object. Its sole purpose is to make final modifications to the response document, such as adding or removing custom attributes (c_fields) or cleaning up data before it’s sent to the client. A critical point: this hook is not transactional. Attempting to modify a persistent Script API object here will result in an ORMTransactionException and an HTTP 500 fault

Not all APIs are made equal

Before starting this journey together, the most important thing to understand is that not all endpoints support hooks. An overview for both types is available:

Feature switch for SCAPI

As it stands, Salesforce B2C Commerce Cloud disables hooks by default for the SCAPI. To enable hook support, a feature switch needs to be enabled in the Business Manager:

“Administration > Global Preferences > Feature Switches”

feature switch scapi hooks

Step 1: Register your customizations

The first step in writing hooks for our APIs is registering them with the server. To do this, you need to do the following steps.

Create a “hooks.json” file

We need to create a JSON file that describes which endpoints we want to customise called “hooks.json.” This file can be put anywhere in a cartridge. But in this case, we will put it in the root  ( e.g. “my_project/cartridges/my_cartridge/hooks.json ) as an example.

				
					{
    "hooks": [
        {
            "name": "dw.ocapi.shop.basket.beforePATCH",
            "script": "./cartridge/scripts/hooks/basketHooks.js"
        },
        {
            "name": "dw.ocapi.shop.customers.password_reset.afterPOST",
            "script": "./cartridge/scripts/hooks/passwordHooks.js"
        }
    ]
}
				
			

In this file, we “hook” the customisation script to the REST endpoint we want to extend.

We can define as many as we want within the file! But make sure every “name” is unique. If it is not, there might be some unexpected behaviour.

Update the “package.json” cartridge file

The next step is to create or edit your cartridge’s “package.json” file.

The file should be in the root folder of your cartridge. (e.g. “my_project/cartridges/my_cartridge/package.json”)

				
					{
  "hooks": "./hooks.json",
  ...
}
				
			

Salesforce B2C Commerce Cloud parses this file to enable specific customisations, including hooks.

Step 2: Build your customisations

You probably noticed that we need to define a “script” file for each hook we register. But we have not created those files until now, so let us do that!

Look up the endpoint documentation

Before we start writing the code, we need to know what function to export in our script for the system to pick up our customisation. This information can be found in the Infocenter.

First, we locate the endpoint we want to override. The documentation will show us more information about the function behind it.

In this case, we need to export the function “beforePATCH” with the parameters “basket” and “basketInput.”

Put that documentation to work

Now that we know what function to use, we can start writing some code.

				
					/**
 * This can be used to update the basket server side, if for instance we need to call a tax service or sync the basket.
 * The client app can retrieve this updated basket by doing a PATCH request.
 */
exports.beforePATCH = function (basket, basketInput) {
    var productLineItems = basket.getProductLineItems();

    /** pass on something to ensure hooks are executed */
    for (var i = 0; i < productLineItems.length; i += 1) {
        productLineItems[i].setLineItemText('PRODUCT ' + productLineItems[i].getLineItemText());
    }
};
				
			

Detecting OCAPI vs SCAPI

We may have a scenario where the OCAPI and the SCAPI use the same endpoint and have their unique customisations. To detect SCAPI calls, the request object/class has recently received a helper function:

				
					request.isSCAPI()
				
			

Step 3: Test if it works!

That wasn’t so much work now, was it? All that is left is to test that our custom code is executed correctly! I recommend Postman to do so.

Maybe a list of things to keep in mind:

  • Don’t forget to upload your cartridge!
  • Don’t forget to add the cartridge to the correct cartridge path!
  • Call the correct endpoint!
  • Call the correct environment!

     

Some of these might seem obvious, but it is easy to get mixed up when working with tools such as Postman.

The Security Gauntlet: Fortifying Your Hooks

We must build our customisations to be robust, as an exception can cause the entire API to fail!

Be sure to catch exceptions and log them appropriately so we can monitor and fix any exceptions that might occur.

Now that we know how to build a hook, let’s discuss how to create a secure one. This is not optional. A poorly secured hook can expose your application to significant risk.

Salesforce has provided a list of constraints and best practices on the documentation site!

The Shared Responsibility Pact

Let’s be crystal clear on the security context. Salesforce is responsible for securing the API endpoints, the underlying platform, and the infrastructure on which it runs. This includes authentication at the API gateway and authorisation based on scopes. However, under this shared responsibility model, you are responsible for the security of any custom code you write, and that absolutely includes hooks.

When a SCAPI request arrives, it’s first authenticated and authorised by the gateway based on the client’s Shopper Login and API Access Service (SLAS) token, along with its associated scopes. Only then is the request passed on for processing, which is where your hook executes. The hook script runs with powerful server-side permissions, and this is where the danger lies. A developer, focused on a simple task like adding a custom field, might implicitly trust that the initial gateway authorisation is sufficient. This is a critical mistake. 

Your hook script has direct access to sensitive Script API objects, such as Order, Customer, and Basket. Without its own internal checks, it can be manipulated. For example, a PATCH request  /orders/{order_id} might be authorised by the gateway for the orders scope, but the gateway doesn’t know if the authenticated user actually owns that specific order_id. It’s the hook’s job to verify ownership. A hook that blindly trusts the data it receives creates a massive security hole. It can function as a “confused deputy,” where an unprivileged user can make privileged calls through your code. The mantra must be: re-authenticate and re-authorise within the hook.

Never Trust, Always Verify: Authentication & Authorization in Hooks

This principle must be applied rigorously. When your hook code deals with sensitive objects, you must always use the secure Script API methods that require a secondary token or secret that only the legitimate owner would possess. 

For instance, the dw.order.OrderMgr class provides two ways to retrieve an order. One is dangerously insecure in this context; the other is the correct choice.

				
					var OrderMgr = require('dw/order/OrderMgr');

// INSECURE: AVOID in hooks where ownership is not yet verified.
// An attacker could pass any valid order number.
var order = OrderMgr.getOrder(orderNumber);

// SECURE: PREFERRED in hooks.
// The orderToken is a secret known only to the user who placed the order.
// This token should be passed in the request from the client.
var order = OrderMgr.getOrder(orderNumber, orderToken);
				
			

This pattern extends to other sensitive objects. Always perform additional checks to confirm the user’s authority to perform the requested action. 

For guest shoppers, where you don’t have an authenticated session, this is even more critical. You should consider prohibiting guest shoppers from changing existing orders or requiring them to provide a combination of secrets from the order (e.g., order number and the email address used) before allowing any modification.

Sanitise Everything: The Gospel of Input Validation

Your before hooks are the primary gatekeepers for all incoming data. They must be ruthless in their validation. Failing to validate and sanitise user-provided input opens the door to a host of attacks, including Cross-Site Scripting (XSS) and various injection attacks that could compromise your server or database. 

The best practice here is to adopt a whitelisting (or allowlisting) approach. Instead of trying to block known bad inputs (blacklisting), you should define exactly what is permitted and reject everything else. This is a far more secure posture, as attackers continually find new ways to circumvent blacklists. 

Your validation logic should check for type, length, format, and range on every single field you process.

				
					// Example validation in a beforePUT hook for a customer address
exports.beforePUT = function (customer, addressId, addressDoc) {
    var Status = require('dw/system/Status');
    var status; // This will hold the Status.ERROR object if any validation fails.

    /**
     * A helper function to initialize the error Status object on the first validation failure.
     * This prevents creating the object unnecessarily and keeps the code clean.
     * @returns {dw.system.Status} The status object, initialized to ERROR if it wasn't already.
     */
    function getErrorStatus() {
        if (!status) {
            // Create a single ERROR status with a custom code the client can use.
            status = new Status(Status.ERROR, 'AddressValidationError');
        }
        return status;
    }

    // Example: Validate postal code format for a specific country
    if (addressDoc.country_code === 'US' && !/^\d{5}(-\d{4})?$/.test(addressDoc.postal_code)) {
        // Add a machine-readable key and a human-readable message for this specific error.
        getErrorStatus().addDetail('INVALID_POSTAL_CODE', 'Invalid US postal code format.');
    }

    // Example: Prevent overly long city names
    if (addressDoc.city && addressDoc.city.length > 50) {
        getErrorStatus().addDetail('CITY_NAME_TOO_LONG', 'City name exceeds maximum length of 50 characters.');
    }

    // Example: Ensure required fields are present
    if (!addressDoc.first_name || !addressDoc.last_name) {
        getErrorStatus().addDetail('MISSING_REQUIRED_FIELDS', 'First and last name are required.');
    }

    // If 'status' was created, an error occurred, so return it.
    // Otherwise, all checks passed, so return an OK status.
    return status || new Status(Status.OK);
};
				
			

Implement the same hook in multiple cartridges

In a single hooks.json file, you can register multiple modules to call for an extension point. However, you can't control the order in which the modules are called. If you call multiple modules, only the last hook returns a value. All modules are called, regardless of whether any of them return a value.

At run time, B2C Commerce runs all hooks registered for an extension point in all cartridges in your cartridge path. Hooks are executed in the order their cartridges appear on the path. Each cartridge can register a module for the same hook. Modules are called in cartridge-path order for all cartridges in which they are registered.

The text above has been taken from the Salesforce B2C Commerce Cloud Infocenter and turns out not to be correct (at least for SCAPI/OCAPI hooks.

This does have a slight nuance: It is not the case for all endpoints. Luckily this is documented for every hook!

hook return behaviour
Read the documentation carefully for each hook!

So basically: never return the following code in your hooks when your custom code completes successfully (if the endpoint supports it):

				
					return new Status(Status.OK);
				
			

Sometimes, your linter will complain about not returning a value in all branches. But you must ignore that warning to avoid breaking another cartridge hook. (Unless you want to break the chain!)

An example where a linter will complain:

				
					exports.beforePOST = function beforePOST(registration) {
    var Status = require('dw/system/Status');

    var verificationResult = validate(registration.customer);

    if (!verificationResult) {
        return new Status(Status.ERROR, 'ERR-TS-02', Resource.msg('turnstile.errors.ERR-TS-02', 'turnstile', null));
    }
    
    // Your linter will want a return statement here
};

				
			

The Need for Speed: Performance Tuning Your Hooks

A functional hook is different from a performant hook. Every line of code added to a hook increases the ‘overhead tax’ on the API’s response time.

The Overhead Tax: The Cost of Customization

Let’s be blunt: hooks are inherently slower than the out-of-the-box APIs. This is because your custom script execution is layered on top of the platform’s own code. This is a trade-off you make for the sake of flexibility. 

The performance shared responsibility model is clear: Salesforce is responsible for the performance of its base API code. You, the developer, are responsible for the performance of your catalog structure, the parameters you send in requests, and every single line of your hook script. A slow hook can bring a snappy API to its knees. 

Your Best Friend, The Code Profiler

You can’t optimise what you can’t measure. The B2C Commerce Code Profiler is the essential tool for diagnosing performance issues in your custom code. It allows you to see exactly how much time is being spent in different parts of the application flow.

The profiler has several modes, each with a different level of detail and performance impact :

  • Production Mode: Measures a subset of requests with minimal performance impact. Good for getting an aggregated view on a live system.

  • Development Mode: Measures all requests with more detail. This is the default for sandboxes and has some runtime overhead.

  • Extended Script Development Mode: Provides deep insight into script execution, down to the line level. It has a severe performance impact and should be used with extreme caution, especially on production instances.

To zero in on your hook’s performance, open the Code Profiler (Administration > Operations > Code Profiler). Select the appropriate mode, and look in the results for the SCRIPT_HOOK result type. This displays the execution times for your hooks, allowing you to quickly identify bottlenecks.

Optimization Tactics for High-Performance Hooks

Once you’ve identified a slow hook, here are the primary tactics for speeding it up:

Minimize External Service Calls

This is, without a doubt, the most common and most severe performance killer. A hook that makes a synchronous call to a slow third-party service will hold up the entire API response. If you absolutely must call an external service, you must use the B2C Commerce Service Framework. This framework is designed for this purpose and provides critical features, such as configurable timeouts and a circuit breaker, which can prevent a failing external service from cascading into a full-blown site outage. 

Strategic Caching

Caching is a powerful tool, but with hooks, it’s a double-edged sword. A modifyGETResponse. For example, the hook is only executed if the cache for that API response is empty or stale. If your hook injects highly dynamic or user-specific data into a response (e.g., “Welcome back, John!”), You have effectively made that response uncacheable for anyone else. What was once a fast, globally cached response from the eCDN now requires a full server-side execution for every single request. This can dramatically increase server load and latency.

Therefore, you must be acutely aware of the “cacheability” of the data you inject. Use modifyResponse hooks on GET requests with extreme caution. If possible, load highly personalised data via a separate, non-cached API call from the client after the main, cacheable content has loaded. 

For expensive, repeatable operations within a hook (like a complex data transformation), you can leverage B2C Commerce Custom Caches to store the result, but be mindful of their size limits (20MB total, 128KB per entry)

Efficient Data Handling

Don’t process more data than necessary. Before your hook even runs, the initial API call from the client should use the select and expand parameters to request only the necessary data fields. 

If your modifyResponse hook on the product details page only needs to add a custom warranty field, the client shouldn’t be asking for all image groups, all variation attributes, and all set products. The less data the platform has to retrieve and your hook has to parse, the faster it will be.

When Things Go Wrong: Error Handling & Idempotency

A hook that works perfectly on a sunny day is easy to write. A truly robust hook is one that behaves predictably and safely when things go wrong.

Building Resilient Hooks: Beyond the try-catch

Any unhandled exception thrown from within your hook script will cause the entire database transaction to roll back, resulting in an HTTP 500 Internal Server Error being returned to the client. This is a jarring experience for the user and can mask the root cause of the problem. 

Therefore, every hook function you write should be wrapped in a comprehensive try-catch block. When an error is caught, you must log it with enough context to be useful for debugging. Use the standard B2C Commerce logging framework (dw.system.Logger) to write detailed messages to a custom log category in the Log Center. 

Include identifiers such as the basket UUID or customer ID to facilitate easier troubleshooting.

The Circuit Breaker Pattern: The Platform's Self-Defence

Your error handling strategy isn’t just about logging, it’s about platform stability. B2C Commerce has a built-in self-defence mechanism called the Hook Circuit Breaker. If a specific hook extension point fails more than 50 times in its last 100 calls, the circuit “opens.” For the next 60 seconds, all calls to that failing extension point will be immediately rejected with an HTTP 503 Service Unavailable and a HookCircuitBreakerException, without ever executing your code.

Think about the implications. A buggy hook doesn’t just return an error for one user; it can render an entire API endpoint, such as adding a payment to the basket, completely unavailable for all users for a full minute. A transient issue, like a third-party payment gateway being temporarily down, could cause a cascade of hook failures, tripping the breaker and turning a minor hiccup into a major outage. This elevates your try-catch block from a simple best practice to a mission-critical component. 

Gracefully catching external failures and returning a non-error status (if the failure is not critical to the core transaction) is essential to prevent your hook from taking down a piece of your storefront.

The Hall of Shame: SCAPI Hook Anti-Patterns

To wrap up, let’s tour the gallery of common mistakes and anti-patterns. Avoid these, and you’ll be well on your way to writing clean, maintainable, and robust hooks.

  • The “God” Hook: A single, monolithic script file (hooks.js) that contains the logic for dozens of different extension points. This violates the Single-Responsibility Principle, resulting in a tangled mess that is difficult to read, debug, or maintain. 

  • The “Chain Breaker”: A hook that incorrectly returns new Status(Status.OK) when it should simply allow processing to continue to other hooks later in the cartridge path. Unless you explicitly intend to short-circuit the execution chain, a successful pass-through hook should often have no return statement at all. Returning a status can prematurely stop the chain and silently disable functionality from base cartridges or other customisations.

  • The “Silent Failure”: A hook that swallows exceptions in an empty catch {} block or logs a useless message like “error occurred.” This makes troubleshooting a nightmare and can conceal critical system failures until they result in major data corruption.   

  • The “Leaky” Hook: A modifyResponse hook that adds internal-only data, debugging information, or sensitive PII to an API response, which is then exposed directly to the client browser.

  • The “Chatty” Hook: A hook that makes multiple, inefficient, synchronous calls to external systems within a single execution instead of designing a more efficient bulk or batch data-fetching strategy.

  • The “Trusting Fool”: The most dangerous of all. A hook that blindly accepts and uses input from the request document without performing its own rigorous validation and authorisation checks, as detailed in our security section.

The post The OCAPI/SCAPI Hooks Playbook: A Deep Dive into Salesforce B2C Commerce Best Practices appeared first on The Rhino Inquisitor.

]]>
https://www.rhino-inquisitor.com/how-to-use-ocapi-scapi-hooks/feed/ 3