# Free Salesforce B2C Commerce Developer Practice Questions

*Last updated September 2026 · https://www.certifyforce.com/salesforce-b2c-commerce-developer-practice-exam/free-questions*

18 free B2C Commerce Developer practice questions from the Certifyforce bank of 300 reviewed questions, spread across the 4 official exam sections in proportion to their weight. Each has the correct answer, an explanation, and the official documentation it was verified against.

## B2C Commerce Setup (11%)

### Question 1

Cumulus Financial's commerce team is releasing a new storefront build. The release lead asks the developer how code should reach production given the platform's upload restrictions. Which two practices should the developer follow? Choose 2 answers.

- A. Package the whole code version as a .zip and upload it once to production's Code Deployment page
- B. Use Code Replication to transport the active code version from staging to the production instance
- C. Turn off the client certificate requirement on staging so a build server can upload without a second factor
- D. Upload the new cartridges into a new, inactive code version on the staging instance and test it there
- E. Upload the cartridges over WebDAV straight into production's active code version during a quiet period

**Correct answers:** B. Use Code Replication to transport the active code version from staging to the production instance; D. Upload the new cartridges into a new, inactive code version on the staging instance and test it there

**Explanation:** The Code Deployment guide recommends uploading code into an inactive version on staging, testing it, and then using Code Replication, which copies the active code version from staging to production. Production rejects WebDAV uploads into its active version, and Salesforce mandates disallowing direct code uploads to production. Two-factor upload to staging (credentials plus client certificate) is required and should stay on. Uploading a .zip of a code version isn't supported; cartridges are uploaded instead.

**Sources:** [Code Deployment](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-code-deployment.html)

### Question 2

A Universal Containers developer opens Administration > Sites > Manage Sites and clicks New to create a sandbox site for a European storefront. Which three settings does the developer enter on the New Site page? Choose 3 answers.

- A. The default currency for the site
- B. The colon-separated cartridge path for the storefront code
- C. The taxation type, Net or Gross
- D. The site ID, using alphanumeric characters, hyphens and underscores
- E. The storefront catalog and the inventory list that the site will use

**Correct answers:** A. The default currency for the site; C. The taxation type, Net or Gross; D. The site ID, using alphanumeric characters, hyphens and underscores

**Explanation:** The New Site page asks for the ID, the default currency, the taxation type (Net or Gross) and a description. The cartridge path is entered afterward on the site's Settings tab. Catalogs and inventory lists are assigned to the site later from Merchant Tools, because a new site starts with no products, prices or other business data.

**Sources:** [Create and Manage Sites for Development](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-managing-sites-for-dev.html)

## Work With a B2C Site (12%)

### Question 3

A Universal Containers developer deploys a home page template that contains `<isslot id="home-promo" context="global" description="Home page promo"/>`. The storefront renders nothing in that spot, and the log has an entry for the slot ID. What should happen next?

- A. Create a slot configuration for home-promo in the Business Manager content slots module
- B. Wrap the isslot tag in an iscache tag so the slot is rendered on the next request
- C. Add a context-object attribute that points to the current basket so the slot can resolve
- D. Add the cartridge that contains the template to the Business Manager site's cartridge path

**Correct answer:** A. Create a slot configuration for home-promo in the Business Manager content slots module

**Explanation:** When no slot is defined in the database for a slot ID, isslot renders no content and writes a log entry, so the merchandiser or developer must create a slot configuration in Business Manager. Caching changes how often the output is regenerated, not whether a configuration exists. Global slots don't need a context object. The storefront site's cartridge path is already rendering the template, so the Business Manager cartridge path isn't involved.

**Sources:** [isslot Element](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-isslot.html), [Content Slots in B2C Commerce](https://help.salesforce.com/s/articleView?id=cc.b2c_content_slots.htm&type=5)

### Question 4

An Ursa Major Solar developer is checking availability messages for a backorderable solar lantern. Its inventory record has a stock level of 2 and a backorder allocation of 5. Refer to the code below:
```javascript
var ProductMgr = require('dw/catalog/ProductMgr');
var product = ProductMgr.getProduct('UMS-LANTERN');
var levels = product.getAvailabilityModel().getAvailabilityLevels(10);
var summary = levels.inStock.value + '/' + levels.backorder.value + '/' + levels.notAvailable.value;
```
What is the value of `summary`?

- A. `2/5/0`
- B. `10/0/0`
- C. `2/0/8`
- D. `2/5/3`

**Correct answer:** D. `2/5/3`

**Explanation:** `getAvailabilityLevels(quantity)` splits the requested quantity across availability statuses. With 2 in stock and 5 backorderable, a request for 10 returns 2 in stock, 5 on backorder and 3 not available, so `summary` is `2/5/3`. `10/0/0` ignores the stock level entirely. `2/0/8` ignores the backorder allocation. `2/5/0` drops the 3 units that no status can cover. The `inStock`, `backorder` and `notAvailable` properties return Quantity objects, so `.value` reads the number.

**Sources:** [Customize Inventory Management](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-inventory-for-developers.html), [Class ProductAvailabilityModel](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-script-api/dw.catalog.ProductAvailabilityModel.html)

## Data Management Using Business Manager Usage (24%)

### Question 5

A Bloomington Caregivers developer reviews these Shop API settings for an existing realm with OCAPI enabled:
```json
{
    "_v": "23.1",
    "clients": [
        {
            "client_id": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
            "resources": [
                { "resource_id": "/baskets", "methods": ["post"], "read_attributes": "(**)", "write_attributes": "(**)" },
                { "resource_id": "/baskets/*", "methods": ["get"], "read_attributes": "(**)", "write_attributes": "(**)" }
            ]
        }
    ]
}
```
The app creates and reads baskets, but a PATCH request to `/baskets/{basket_id}` that updates the currency is declined. Which change fixes it?

- A. Add `"patch"` to the `methods` array of the `/baskets/*` resource entry
- B. Change `_v` to the OCAPI version the app calls, because `_v` must match the request URL version
- C. Change `write_attributes` on `/baskets/*` to `(currency)` so the property can be included in requests
- D. Add a `/baskets/**` resource entry with `["get"]`, because PATCH uses the subresource path

**Correct answer:** A. Add `"patch"` to the `methods` array of the `/baskets/*` resource entry

**Explanation:** The `methods` property is an HTTP method filter, so a resource entry that lists only `get` declines PATCH; adding `patch` to the `/baskets/*` entry allows the update. `_v` is the version of the configuration file structure and is independent of the OCAPI version in the request. `write_attributes` already allows all properties with `(**)`, and narrowing it would not add the missing method. PATCH on a basket targets `/baskets/{basket_id}`, which `/baskets/*` already matches, and a GET-only entry would not allow PATCH anyway.

**Sources:** [OCAPI Settings](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/ocapisettings.html)

### Question 6

A Cumulus Financial developer must review today's `customerror` files on a staging instance. Which two ways can the developer reach the log files? Choose 2 answers.

- A. Open Administration > Operations > Custom Log Settings and use the Download tab beside each category
- B. Connect an sFTP client to the instance's Logs folder on port 22 using the Business Manager user name and password for that instance
- C. Connect a WebDAV client with Business Manager credentials to `/on/demandware.servlet/webdav/Sites/Logs` on the instance
- D. In Business Manager, open Administration > Site Development > Development Setup and use the Log files link under WebDAV Access
- E. Open Merchant Tools > Reports & Dashboards and export the error log report for the current day as a CSV file

**Correct answers:** C. Connect a WebDAV client with Business Manager credentials to `/on/demandware.servlet/webdav/Sites/Logs` on the instance; D. In Business Manager, open Administration > Site Development > Development Setup and use the Log files link under WebDAV Access

**Explanation:** The Log Files page describes both routes: the Log files link in the WebDAV Access section of Development Setup, and a WebDAV client pointed at `https://<instance>/on/demandware.servlet/webdav/Sites/Logs` with a Business Manager user that has the right access. The same page states sFTP is not supported. Custom Log Settings configures levels and categories but does not serve the files. Reports & Dashboards is a merchant analytics tool, not a log viewer.

**Sources:** [Log Files](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-log-files-overview.html), [Troubleshooting](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-troubleshooting.html)

### Question 7

A Northern Trail Outfitters merchandiser unchecked Enabled on the `nto.http.loyalty.get` service in Business Manager during a vendor outage. Refer to the controller code below:
```javascript
var result = loyaltyService.call({ customerNo: customerNo });
if (!result.ok) {
    Logger.warn('Loyalty status {0}, reason {1}', result.status, result.unavailableReason);
}
```
What is written to the warn log?

- A. `Loyalty status SERVICE_UNAVAILABLE, reason DISABLED`
- B. `Loyalty status ERROR, reason DISABLED`
- C. `Loyalty status ERROR, reason CIRCUIT_BROKEN`
- D. `Loyalty status SERVICE_UNAVAILABLE, reason CONFIG_PROBLEM`

**Correct answer:** A. `Loyalty status SERVICE_UNAVAILABLE, reason DISABLED`

**Explanation:** When a service is disabled, no call is made and the Result has the status `SERVICE_UNAVAILABLE` with the unavailable reason `DISABLED`, which `getUnavailableReason()` returns for that status. `ERROR` is the status for a general service error after a call is attempted. `CONFIG_PROBLEM` means the service was not configured correctly, which differs from being switched off. `CIRCUIT_BROKEN` is the reason when the circuit breaker blocks the call, and it accompanies `SERVICE_UNAVAILABLE`, not `ERROR`.

**Sources:** [Class Result](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-script-api/dw.svc.Result.html), [Web Services](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-webservices.html)

### Question 8

A Universal Containers developer tries to add another custom attribute to the Content system object type and is blocked. The object already has 50 custom attribute definitions, 12 of them localizable. What explains the result?

- A. Content allows at most 50 custom attribute definitions, and localizable definitions count toward it
- B. The Content limit is 500 definitions, so the block comes from the attribute group size instead
- C. Only 25 non-localizable attributes are allowed per object, so the block is from that quota
- D. Localizable attributes count double, so the object has really used 62 of its allowance

**Correct answer:** A. Content allows at most 50 custom attribute definitions, and localizable definitions count toward it

**Explanation:** The business objects page lists a maximum of 50 custom attribute definitions for Content and other extensible system objects, 500 for Product and for site and organization preferences, and states that localizable custom attribute definitions count toward the limit, so 50 definitions hit the cap. Localizable definitions count once, not twice. The 500 limit applies to Product, not Content. There is no separate 25-attribute quota for non-localizable attributes.

**Sources:** [Use Custom Object Definitions](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-business-objects.html)

## Application Development (53%)

### Question 9

The base `Cart-AddProduct` route used by Northern Trail Outfitters already posts each added item to an external inventory reservation service. A developer must change how that step builds the reservation request, and a teammate proposes `server.append` in the overlay cartridge to send a corrected reservation. What should the developer do instead?

- A. Use `server.append` and remove the original reservation step from the chain with `removeListener` before it runs
- B. Append a step that calls the service again and passes a flag so the inventory system ignores the first reservation
- C. Copy the corrected reservation code into `app_storefront_base` so every site on the instance picks up the fix
- D. Replace the route in the overlay cartridge so the original middleware, including its reservation call, doesn't execute

**Correct answer:** D. Replace the route in the overlay cartridge so the original middleware, including its reservation call, doesn't execute

**Explanation:** With `server.append`, the original middleware chain runs before the added step, so a third-party call in the original chain still runs and could run twice. The SFRA guide recommends replacing a route when you need to change individual steps or when the route interacts with a web service. You can't remove a single step with event emitter listeners, because `removeListener` doesn't recognize named emitters. Editing `app_storefront_base` voids its upgrade path and is explicitly discouraged.

**Sources:** [Customize SFRA](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-customizing-sfra.html), [SFRA Features and Components](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-sfra-features-and-comps.html)

### Question 10

A Get Cloudy Consulting developer adds a gift-message form, defined in `forms/default/giftMessage.xml`, to an SFRA route. The route gets the form object with the server module's `getForm` function and then calls `res.render('checkout/giftMessage')` with no data argument. The template reads `pdict.giftForm.message`, but the field never renders. What should the developer change?

- A. Pass the form object to the template in the data argument of `res.render()`, for example under the `giftForm` key
- B. Call `clear()` on the form before rendering it so that the in-memory form object is created again for the request
- C. Move `giftMessage.xml` into the cartridge's `templates/default/forms` folder so that the template can find it
- D. Include the form definition in the template with `<isinclude>` so that the template loads the form fields itself

**Correct answer:** A. Pass the form object to the template in the data argument of `res.render()`, for example under the `giftForm` key

**Explanation:** The Forms guide says form data is read in templates through pdict, but the form is available only if the controller passes the object returned by `getForm` to the template, by adding it to the data passed when rendering. `isinclude` includes templates, not form definitions. Calling `clear()` only resets the values the in-memory form keeps during the session; it doesn't make the form visible to the template. Form definitions belong in the cartridge's `forms` folders (such as `forms/default`), not under `templates`.

**Sources:** [Forms](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-forms.html), [SFRA Features and Components](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-sfra-features-and-comps.html)

### Question 11

An AW Computing developer must integrate a partner's FTP price-list drop and a legacy SOAP order service. Which two statements about the service types' callbacks are correct? Choose 2 answers.

- A. A SOAP service must implement `initServiceClient` to get the WebReference2 port, and it also implements `execute`
- B. For an FTP service, when the script doesn't call `setOperation()`, the `execute` callback runs when the service is called
- C. An HTTP service must implement `initServiceClient` to build its `HTTPClient`, because the framework doesn't create it
- D. The Generic service type wraps `dw.net.HTTPClient`, so its `parseResponse` callback receives the HTTP client object
- E. The HTTP Form type sends the parameters given to `call()` as a JSON body by default, so no `createRequest` is needed

**Correct answers:** A. A SOAP service must implement `initServiceClient` to get the WebReference2 port, and it also implements `execute`; B. For an FTP service, when the script doesn't call `setOperation()`, the `execute` callback runs when the service is called

**Explanation:** The Web Services page says the FTP type has `setOperation()`, and if it isn't called, the `execute` callback defined in the service definition runs when the service is called. It also says SOAP services implement two more callbacks, `initServiceClient` and `execute`, and that `initServiceClient` is required only for SOAP because other client types are created automatically. That is why HTTP services do not need `initServiceClient`. The Generic type doesn't wrap any class, and the HTTP Form type builds a URL-encoded form POST from the call parameters.

**Sources:** [Web Services](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-webservices.html)

### Question 12

AW Computing is launching its storefront in four languages and wants translators to work without touching template logic. Which approach does Salesforce recommend for storefront text?

- A. One template set, with its text externalized into properties files translated per locale
- B. One template set with `<isif>` branches on the locale that hold the text for each language
- C. Content assets for every label, so merchandisers translate the text in Business Manager
- D. A separate copy of every template for each locale, with translated text written into each copy

**Correct answer:** A. One template set, with its text externalized into properties files translated per locale

**Explanation:** Salesforce recommends one template set with localizable strings externalized into resource files, which is the approach SFRA uses: the template resource bundles are translated, not the templates. Separate template sets per locale duplicate display logic and are the less preferred option. Locale branches in templates put translations into logic, and turning every label into a content asset isn't the documented approach for template text.

**Sources:** [Localization](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-localization.html)

### Question 13

An Ursa Major Solar ERP integration, an existing OCAPI integration on a realm where OCAPI is enabled, replaces a customer address through the OCAPI Data API with an HTTP PUT request. It works on a sandbox but is blocked on staging and production. Which two changes let the integration perform the same logical PUT on those instances? Choose 2 answers.

- A. Send the request with POST and the request parameter `method=PUT`
- B. Send the request with PATCH, because PATCH replaces the whole resource like PUT does
- C. Send the request with PUT but add the `Content-Length: 0` header to pass the block
- D. Send the request with GET and the full address document in the query string
- E. Send the request with POST and the header `x-dw-http-method-override: PUT`

**Correct answers:** A. Send the request with POST and the request parameter `method=PUT`; E. Send the request with POST and the header `x-dw-http-method-override: PUT`

**Explanation:** The OCAPI HTTP Methods page says that for security reasons PUT is blocked from making direct calls against production or staging instances, and that you perform a logical PUT through POST by overriding the method. The override can be given as the `method` request or form parameter or as the `x-dw-http-method-override` header, with the value in upper case. PATCH is a partial update that only changes provided properties, so it isn't the same as PUT. GET is a safe method that never changes server state, and the `Content-Length: 0` advice is for requests without a body, not a way around the PUT block.

**Sources:** [OCAPI HTTP Methods](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/httpmethods.html), [Get Started With OCAPI](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/get-started-with-ocapi.html)

### Question 14

An AW Computing developer builds a custom attribute editor that lets marketers pick a store on a map. The component type sets the attribute to `"type": "custom"` with an `editor_definition` that references the editor ID, and the editor files are in `int_awc_editors`, which is on the storefront site's cartridge path. In the visual editor, the attribute shows no map. What should the developer do?

- A. Move the editor's meta definition and script into `experience/components`, next to the component that uses the editor
- B. Load the editor's JavaScript with `assets.addJs` in the storefront page template, so the visual editor can run it
- C. Add `int_awc_editors` to the cartridge path of the Business Manager site, where custom attribute editors are loaded
- D. Change the attribute type to `string`, because editor definitions are applied only to string attributes in the editor

**Correct answer:** C. Add `int_awc_editors` to the cartridge path of the Business Manager site, where custom attribute editors are loaded

**Explanation:** The cartridge that contains the custom attribute editor's meta definition, script and client-side code must be added to the cartridge path for the Business Manager site, because the editor runs in the Business Manager visual editor. A custom attribute editor is used with an attribute of type `custom` and an `editor_definition`, so switching to `string` removes it. Editor files belong under `experience/editors`, not `experience/components`. Storefront assets aren't loaded into the editor, which runs each custom editor in its own iframe.

**Sources:** [Page Designer](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-dev-for-page-designer.html)

### Question 15

Cloud Kicks' SFRA `OnRequest` hook loads the shopper's loyalty profile and recomputes recommended categories on every request. The Code Profiler shows the hook costs about 40 ms per request. What does the documented guidance say?

- A. Expensive custom logic of more than 10 ms in OnSession and OnRequest should be limited or moved
- B. OnRequest logic runs asynchronously after the response is sent, so it doesn't add page time
- C. OnRequest logic is excluded from page caching rules, so the cost only matters on uncached pages
- D. OnRequest logic runs once per session, so the 40 ms is paid only on the first page of each visit

**Correct answer:** A. Expensive custom logic of more than 10 ms in OnSession and OnRequest should be limited or moved

**Explanation:** The Site Performance page's storefront best practices say to limit expensive (more than 10 ms) custom server logic in OnSession and OnRequest, so a 40 ms hook should be reduced or moved. Claims that OnRequest logic runs only once per session, is excluded from caching rules or runs after the response is sent would make the cost irrelevant, and they contradict the documented advice to limit this logic. The Code Profiler lists ONREQUEST calls as a request result type, which is how the 40 ms cost was measured.

**Sources:** [Site Performance](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-site-performance.html)

### Question 16

A Bloomington Caregivers developer writes a chunk-oriented script module for a nightly volunteer-hours export and leaves out every optional function. Which three functions must the module still provide? Choose 3 answers.

- A. `write`, which receives the list of items for the chunk and returns nothing
- B. `beforeStep`, which runs once before any chunk is read, processed or written
- C. `read`, which returns one item or nothing when there are no more items
- D. `process`, which applies business logic to an item and returns one item
- E. `getTotalCount`, which returns the total number of items before chunking starts

**Correct answers:** A. `write`, which receives the list of items for the chunk and returns nothing; C. `read`, which returns one item or nothing when there are no more items; D. `process`, which applies business logic to an item and returns one item

**Explanation:** The Create Custom Job Step Types page marks `read`, `process` and `write` as required for chunk processing: `read` returns one item or nothing, `process` transforms an item and returns one item, and `write` receives the list of items for a chunk. The page marks `getTotalCount` and `beforeStep` as optional; `getTotalCount` is called once before chunk processing to support progress monitoring, and `beforeStep` runs before all items of all chunks are handled. Unless the steptypes file names other functions, the module must export functions named `read`, `process` and `write`.

**Sources:** [Create Custom Job Step Types](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-custom-job-steps.html), [Create Custom Job Steps (Trailhead unit)](https://trailhead.salesforce.com/content/learn/modules/b2c-admin-create-and-manage-jobs/b2c-admin-create-custom-job-steps)

### Question 17

A Get Cloudy Consulting developer creates `common/layout/promoLayout.isml` with a header include and a footer include, and wraps the body of a campaign template like this:
```html
<isdecorate template="common/layout/promoLayout">
    <div class="campaign-body">...</div>
</isdecorate>
```
The storefront shows the header and footer but none of the campaign body. What is the cause?

- A. The `isdecorate` tag needs a `.isml` suffix on the template path, so the body is rendered as plain text
- B. `promoLayout.isml` doesn't contain an `<isreplace/>` tag, so the decorated content is omitted from the output
- C. The campaign body must use `<isinclude template="..."/>` inside `isdecorate`, because inline markup is ignored
- D. Decorator templates must sit in a `decorators` folder, so the body is skipped and only the includes run

**Correct answer:** B. `promoLayout.isml` doesn't contain an `<isreplace/>` tag, so the decorated content is omitted from the output

**Explanation:** A decorator template marks where the wrapped content goes with `<isreplace/>`; if the decorator has no `isreplace` tag, the decorated content is omitted. Template paths don't need the `.isml` suffix, which is appended automatically. There is no required `decorators` folder: SFRA's own decorators live in `common/layout`. The wrapped content can be any markup, so inline HTML inside `isdecorate` is valid.

**Sources:** [isdecorate Element](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-isdecorate.html)

### Question 18

Before moving to a newer compatibility mode, a Universal Containers developer must find every template that still uses `<iscache status="off"/>`, including its code location. Where should the developer look?

- A. The sysevent log, which records template compile warnings when a code version activates
- B. The deprecation log, which records each deprecated iscache usage with its code location
- C. The customwarn log, which is written whenever a template sets a status value on a tag
- D. The quota log, which lists every template that turned off page caching on each server

**Correct answer:** B. The deprecation log, which records each deprecated iscache usage with its code location

**Explanation:** Setting `status="off"` on iscache is deprecated, and the system writes messages to the deprecation log that warn about the usage and show the code location of each occurrence. The quota log covers object and API quotas. The customwarn log holds messages from `Logger.warn` calls in custom code. The sysevent log shows app server registration and cartridge-related events such as code version activation, not deprecated tag usage. To disable caching going forward, omit the iscache element instead.

**Sources:** [iscache Element](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-iscache.html), [Log Files](https://developer.salesforce.com/docs/commerce/b2c-commerce/guide/b2c-log-files-overview.html)

## Related pages

- [Salesforce B2C Commerce Developer practice exam](https://www.certifyforce.com/salesforce-b2c-commerce-developer-practice-exam)
- [Practice exam packs](https://www.certifyforce.com/practice-exams)
- [Official B2C Commerce Developer exam guide](https://help.salesforce.com/s/articleView?id=005298941&language=en_US&type=1)
