---
title: "SFCC"
slug: "sfcc"
updated: 2025-02-24T14:09:04Z
published: 2025-02-24T14:09:04Z
canonical: "docs.newstore.com/sfcc"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://docs.newstore.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SFCC

This document describes how the shopping app integrates with SFCC. It covers the integration on both a high and detailed level and covers the steps required to setup and configure the integration. See the “Detailed API documentation” section for specific details on which end points are used and how they are used.

## SFCC integration overview

The shopping app integrates with SFCC exclusively via the Shop API of the Open Commerce API (OCAPI). Requests are made directly from NewStore’s backend.

All data is pulled directly from SFCC and no data is stored within NewStore. The only exception being product data which is cached for up to 24 hours to limit the number of requests made to SFCC. SFCC is the source of truth in terms of data, e.g. customer accounts, baskets, pricing, etc. For example: if a user creates an account in the app then a request is made directly to SFCC to create the user account within the SFCC database. This means that user accounts on the web and app are shared.

The OCAPI integration layer is part of the NewStore platform. The same integration is used by every SFCC customer that is on the NewStore Shopping App platform.

### Feature overview

The following commerce features are supported within the NewStore - SFCC integration. Items marked with a * are not supported out of the box and require customization to the SFCC response.

- Category Tree - for navigation
- Product Lists
  - Sorting
  - Filtering
- Product Details
  - Name
  - Images
  - Original Price
  - Sale Price
  - Product Promotions - customer specific promotions are not currently supported on the product detail level. customer specific promotions can be supported on the basket level as this data is not cached
  - Description
  - Variation Attributes -eg size / colour
  - Share URL
  - Badges *
  - Size Guide *
  - Delivery Message *
- Baskets
  - Create Basket
  - Add Item to Basket
  - Update Item in Basket
  - Remove item from Basket
  - Basket level promotions - these can be based on customer groups
  - Shipping Address
  - Billing Address
  - Display Shipping Methods
  - Shipping Methods containing sub option, eg pick up points *
  - Display of Payment Methods
  - Coupon Codes
  - Order Creation
- Accounts
  - Account Creation
  - Login
  - Add address to account - Only 1 address is supported
  - User Order History
- Wishlist
  - Add item to wishlist
  - Remove item from wishlist
- Store Locator
  - View list of Stores
  - View Store Details

### Customization

The integration between NewStore and SFCC cannot be customized in the sense that requests cannot be altered, nor can different endpoints be used. However it is possible to customize the response that is returned by SFCC through the use of OCAPI hooks. These can be used to add custom attributes to the response that NewStore can use in an extension layer in order to alter the data shown in the app.

For example:

- An OCAPI hook could be used to alter the price of the product in the basket.
- An OCAPI hook could be used to add an integration with an external system such as a loyalty platform

#### NewStore-specific customizations

You may well have other services that make use of the OCAPI and as such you may wish to only apply some customizations to requests from NewStore. Requests from NewStore can be recognised using the **User-Agent** request header. The value will always be set to **Highstreet Backend**.

The diagram below details this customisation pattern.

![](https://cdn.document360.io/c2d22fcd-a44b-4e1e-bf3a-84f0ffba0dbe/Images/Documentation/image(3).png)

![](https://media-cdn.atlassian.com/file/eab46a0d-c88e-4ed8-a2cb-5178f678b3a4/image/cdn?allowAnimated=true&amp;client=2376c579-7cc3-433e-b234-8ea3e95cae8e&amp;collection=contentId-3681026049&amp;height=616&amp;max-age=2592000&amp;mode=full-fit&amp;source=mediaCard&amp;token=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiIyMzc2YzU3OS03Y2MzLTQzM2UtYjIzNC04ZWEzZTk1Y2FlOGUiLCJhY2Nlc3MiOnsidXJuOmZpbGVzdG9yZTpjb2xsZWN0aW9uOmNvbnRlbnRJZC0zNjgxMDI2MDQ5IjpbInJlYWQiXX0sImV4cCI6MTczMTQyMTE4NSwibmJmIjoxNzMxNDE4MzA1fQ.AgDDpgp7zNtNreJDBStJEtIOoABJ7GS0pFyr1QfqoVo&amp;width=760)

### Integration setup

In order to setup the OCAPI integration within NewStore the following steps will need to be performed by the merchant.

#### OCAPI setup

In order to setup the connection between NewStore and SFCC the following is needed:

1. SFCC Domain of all environments
2. The site ID of all relevant sites
3. The locale of all relevant sites
4. A Client ID (see below)
5. Upload OCAPI settings (see below)

#### Configuring OCAPI settings

In the Account Manager go to **API Client** > **Add API Client**. Provide a name (for example 'NewStore app'), a password and select your organisation. Leave the other settings to their default and click **Save**. Copy the generated Client ID.

In the Business Manager go to **Administration** > Site **Development** > **Open Commerce API Settings**, select Type ‘Shop’ and the context of your site. Copy the contents of the `sample_wapi_shop_config.json` file below. If previous OCAPI settings were present then ensure to merge them. If there are no existing OCAPI settings the file can be used as is. Replace the value of the client_id to the previously copied value. Press **Save** to store the OCAPI settings. Provide the OCAPI Client ID and Client Secret to NewStore.

[](https://cdn.document360.io/c2d22fcd-a44b-4e1e-bf3a-84f0ffba0dbe/Images/Documentation/sample_wapi_shop_config.json)sample_wapi_shop_config15.68 KB[](https://cdn.document360.io/c2d22fcd-a44b-4e1e-bf3a-84f0ffba0dbe/Images/Documentation/sample_wapi_shop_config.json)

#### Business manager

In the majority of cases a user access token is created by the app and used to authenticate against the OCAPI shop API. However sometimes NewStore will make server to server calls directly to the OCAPI which will require Business Manager Credentials.

To configure the Business Manager credentials the following is needed:

1. Configure Client ID and Secret
  1. The Same client ID and Secret that was created in the previous step can be used.
  2. These should be stored in NewStore Secret Manager under the **demandware_business_manager.client_id** and **demandware_business_manager.client_secret** keys
2. Configure username and password
  1. Ask the tenant to create a Business Manager User. The user should have the scopes required to read and write orders.
    1. Use the following email address service-integrations@newstore.com

The above email address should be used for the user creation, all members of the Shopping app services team are part of that group email and can receive or send emails from it.
  2. Once the user has been created you will need to login into the tenant’s business manager and create an access key by doing the following:
    1. Click your username in the upper right corner of Business Manager.
    2. Click Manage Access Keys.

Here you can enable, disable, or delete access keys. If an access key is expired or disabled, you can't use that access key or your password to log in.
    3. Generate an access key.
    4. Select a scope for your key. This determines what you can use the key to log into.
    5. Click Download to copy your access key.

You won’t be able to access this key again after you close this screen. This is your only chance to copy the code. We recommend you keep the code in a safe place for future use. Full SFCC documentation is here: [https://help.salesforce.com/s/articleView?id=cc.b2c_access_keys_for_business_manager.htm&type=5](https://help.salesforce.com/s/articleView?id=cc.b2c_access_keys_for_business_manager.htm&amp;type=5)
  3. Save the username and access key in NewStore Secret Manager under the **demandware_business_manager.username** and **demandware_business_manager.password** keys. Business manager should now be configured.

### Order Flow

When a user places an order in the shopping app Newstore will make a request to the `POST /orders` endpoint to create the order directly in SFCC. Once created the order will have a status of `created` . At this stage there will be no payment details on the order and the order will not be able to be exported. You will have to rely on an incoming webhook from your Payment Service Provider to update the order and add the payment details (eg transaction ID). Newstore does not sit in between this flow. Typically this flow will already be implemented for your web orders.

If using Adyen as your PSP you can find out more about webhooks here: [https://docs.adyen.com/development-resources/webhooks/](https://docs.adyen.com/development-resources/webhooks/)

## Detailed API documentation

This sections goes into detailed specifics of how this integration works on an endpoint level.

### Categories / Navigation

**GET /categories/{id}**

[https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-categories?meta=Get%2Bmultiple%2Bcategories](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-categories?meta=Get%2Bmultiple%2Bcategories)

#### Integration details

The category tree is used to construct the main shopping menu within the app. The tree is fetched using the `GET /categories/{id}` OCAPI endpoint. Categories are fetched in levels of 2. First a request is made to fetch the root category along with 2 levels of sub-categories. For each lowest sub category a subsequent request is made to fetch all of its sub-categories and so on until the full category tree is fetched.

Only categories can be displayed in the category navigation menu. Links to landing pages or any other content is not supported. Similarly redirects are not supported. For example there may be a case on the website that if a user clicks on the category item **women** then they are actually redirected to the subcategory women-view-all. Such behaviour is not supported in the app. Instead the app would show all products that are assigned to the category with ID **women**.

As such it’s very important to note that each category *must* have products assigned to them. See example below for a representation of a basic category tree. Every level of category mentioned must have products assigned to them. It’s the expectation that the top level category would contain all products of the sub categories combined.

```plaintext
women
  - women-bottoms
    - women-bottoms-long
    - women-bottoms-short
  - women-tops
    - women-tops-short-sleeve
    - women-tops-long-sleeve    

men
  - men-bottoms
    - men-bottoms-long
    - men-bottoms-short
  - men-tops
    - men-tops-short-sleeve
    - men-tops-long-sleeve
```

By default SFCC will return all categories, even those that should not be shown. To ensure that the correct categories are hidden from the app’s navigation the custom attribute `c_showInMenu` with a boolean value can be added to the category object. The app will use this to determine wether the category should be shown or not.

The following attributes on the category object are used. All other attributes are ignored.

| Name | Type | Comment |
| --- | --- | --- |
| id | String | The id of the category, this is also the value that will be sent within the cgid parameter when performing a product search request. |
| name | String | The name of the category. This should be localised. |
| c_showInMenu | Boolean | Used to determine wether the category should be shown in the app’s navigation or not. This will require customisation within SFCC. |

### Product lister view / Product search

**GET /product_search**

[https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-product-search?meta=Search%2BProducts](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-product-search?meta=Search%2BProducts)

#### Integration details

Products displayed in the Product Lister View (PLV) and Product Search View of the app are fetched using the `GET /product_search` OCAPI endpoint. This endpoint returns a list of product IDs that should be shown, along with filtering and sorting options. The request is paginated and broken into 20 results at a time.

In the response the products are included in the hits array. If the hit is of type **product** or **slicing_group** then the **hit["product_id"]** attribute is used. If the hit is of type **variation_group** or **master** then the **hit["represented_product"]["id"]** attribute is used. The reason for this is that in both cases the product ID will be that of a variant product. A variant product ID is required as this will ensure that the correct colour of the product is shown on the PLV. If a master ID were to be used then the app would have no way of knowing which is the correct colour to display on the PLV in the case of products with multiple colour variations.

In the case of populating the PLV the `cgid` parameter is set to the selected category ID. In the case of a product search query the `q` parameter will contain the search term entered by the user.

**Barcode scanner**

When a product barcode is scanned within the app a product search request will also be performed. The product’s EAN will be sent as the value for the `q` parameter. For this reason product EANs must be added to the product search index within SFCC, otherwise the barcode scanner will not work within the app.

Example

```json
GET /product_search?currency={{currency}}&start=0&count=2&refine_1=cgid%3D{{category_id}}&refine_2=htype%3Dslicing_group%7Cproduct%7Cvariation_group%7Cmaster&select=%28count%2Chits.%28%2A%2A%29%2Crefinements.%28attribute_id%2Clabel%2Cvalues%5B0%3A1000%5D%29%2Csorting_options%2Ctotal%29&expand=availability&locale={{locale}} 

{
	"_v": "19.10",
	"_type": "product_search_result",
	"count": 1,
	"hits": [{
		"_type": "product_search_hit",
		"hit_type": "master",
		"link": "https://www.brand.com/s/Sites-brand-Site/dw/shop/v19_10/products/V944984264?select=%28count%2Chits.%28**%29%2Crefinements.%28attribute_id%2Clabel%2Cvalues%5B0%3A1000%5D%29%2Csorting_options%2Ctotal%29&refine_2=htype%3Dslicing_group%7Cproduct%7Cvariation_group%7Cmaster&start=0&count=1&currency=USD&refine_1=cgid%3Dwomen-tees&locale=default",
		"orderable": true,
		"product_id": "V944984264",
		"product_name": "Crew Neck T-Shirt",
		"product_type": {
			"_type": "product_type",
			"master": true
		},
		"represented_product": {
			"_type": "product_ref",
			"id": "273963",
			"link": "https://www.brand.com/s/Sites-brand-Site/dw/shop/v19_10/products/273963?select=%28count%2Chits.%28**%29%2Crefinements.%28attribute_id%2Clabel%2Cvalues%5B0%3A1000%5D%29%2Csorting_options%2Ctotal%29&refine_2=htype%3Dslicing_group%7Cproduct%7Cvariation_group%7Cmaster&start=0&count=1&currency=USD&refine_1=cgid%3Dwomen-tees&locale=default"
		}
	}]
  ...
}
```

#### Sorting options

The sorting options returned within the `sorting_options` of the response will be shown unaltered within the app. The **id** and **label** attributes are used. If needed sorting options can be hidden via the configuration layer within the Shopping App.

#### Filters

The filters returned within the `refinements` attribute of the response will be shown unaltered within the app. If needed filters can be hidden via the configuration layer within the Shopping App.

### Product details

**GET /products/{id}**

[https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-products?meta=Get%2Bmultiple%2Bproducts](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-products?meta=Get%2Bmultiple%2Bproducts)

#### Integration details

The details of products are fetched using the `GET /products/{id}` endpoint. See below for specific details on how each attribute is used and how it can be customised. In general it will always be the ID of a product variant that will be used.

If any attributes are added to a product for the purpose of customisation then it’s essential that this attribute is added to every variant product and not just added to the master product.

```json
GET /products/({{product_id}})?expand=availability%2Cimages%2Cprices%2Cpromotions%2Cvariations&currency={{currency}}&locale={{locale}}

{
    "_v": "19.10",
    "_type": "product_result",
    "count": 1,
    "data": [
        {
            "_type": "product",
            "currency": "GBP",
            "ean": "5059786528395",
            "id": "5059786528395",
            "image_groups": [
                {
                    "_type": "image_group",
                    "images": [
                        {
                            "_type": "image",
                            "alt": "State Wool Leather Logo Baseball Cap  large",
                            "dis_base_link": "https://www.allsaints.com/dw/image/v2/BHHD_PRD/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwa37a1215/images/large/MH607Z/7/MH607Z-7-1.jpg",
                            "link": "https://www.allsaints.com/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwa37a1215/images/large/MH607Z/7/MH607Z-7-1.jpg",
                            "title": "State Wool Leather Logo Baseball Cap "
                        },
                        {
                            "_type": "image",
                            "alt": "State Wool Leather Logo Baseball Cap  large",
                            "dis_base_link": "https://www.allsaints.com/dw/image/v2/BHHD_PRD/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dw7a4ab54d/images/large/MH607Z/7/MH607Z-7-2.jpg",
                            "link": "https://www.allsaints.com/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dw7a4ab54d/images/large/MH607Z/7/MH607Z-7-2.jpg",
                            "title": "State Wool Leather Logo Baseball Cap "
                        },
                        {
                            "_type": "image",
                            "alt": "State Wool Leather Logo Baseball Cap  large",
                            "dis_base_link": "https://www.allsaints.com/dw/image/v2/BHHD_PRD/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwe2d208eb/images/large/MH607Z/7/MH607Z-7-3.jpg",
                            "link": "https://www.allsaints.com/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwe2d208eb/images/large/MH607Z/7/MH607Z-7-3.jpg",
                            "title": "State Wool Leather Logo Baseball Cap "
                        },
                        {
                            "_type": "image",
                            "alt": "State Wool Leather Logo Baseball Cap  large",
                            "dis_base_link": "https://www.allsaints.com/dw/image/v2/BHHD_PRD/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwfb42bdd8/images/large/MH607Z/7/MH607Z-7-4.jpg",
                            "link": "https://www.allsaints.com/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwfb42bdd8/images/large/MH607Z/7/MH607Z-7-4.jpg",
                            "title": "State Wool Leather Logo Baseball Cap "
                        },
                        {
                            "_type": "image",
                            "alt": "State Wool Leather Logo Baseball Cap  large",
                            "dis_base_link": "https://www.allsaints.com/dw/image/v2/BHHD_PRD/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwf4450be8/images/large/MH607Z/7/MH607Z-7-5.jpg",
                            "link": "https://www.allsaints.com/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dwf4450be8/images/large/MH607Z/7/MH607Z-7-5.jpg",
                            "title": "State Wool Leather Logo Baseball Cap "
                        }
                    ],
                    "variation_attributes": [
                        {
                            "_type": "variation_attribute",
                            "id": "color",
                            "values": [
                                {
                                    "_type": "variation_attribute_value",
                                    "value": "Grey"
                                }
                            ]
                        }
                    ],
                    "view_type": "large"
                },
                {
                    "_type": "image_group",
                    "images": [
                        {
                            "_type": "image",
                            "alt": "State Wool Leather Logo Baseball Cap  swatch",
                            "dis_base_link": "https://www.allsaints.com/dw/image/v2/BHHD_PRD/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dw22bccd2d/images/swatches/MH607Z-7.jpg",
                            "link": "https://www.allsaints.com/on/demandware.static/-/Sites-allsaints-emea-master-catalog/default/dw22bccd2d/images/swatches/MH607Z-7.jpg",
                            "title": "State Wool Leather Logo Baseball Cap "
                        }
                    ],
                    "variation_attributes": [
                        {
                            "_type": "variation_attribute",
                            "id": "color",
                            "values": [
                                {
                                    "_type": "variation_attribute_value",
                                    "value": "Grey"
                                }
                            ]
                        }
                    ],
                    "view_type": "swatch"
                }
            ],
            "inventory": {
                "_type": "inventory",
                "ats": 425,
                "backorderable": false,
                "id": "inventory-allsaints-uk",
                "orderable": true,
                "preorderable": false,
                "stock_level": 425
            },
            "long_description": "<ul> <li>Head circumference: 58cm</li> <li>Crown height: 160mm, brim width: 70mm</li> <li>Shell: 75% wool, 25% sheep leather</li> <li>Lining: 100% cotton</li> <li>Strap: 100% cow leather</li> <li>Wipe clean only</li> </ul>",
            "master": {
                "_type": "master",
                "link": "https://www.allsaints.com/s/Sites-allsaints-uk-Site/dw/shop/v19_10/products/MH607Z?currency=GBP&locale=en-GB",
                "master_id": "MH607Z",
                "orderable": true,
                "price": 59.00
            },
            "min_order_quantity": 1,
            "name": "State Wool Leather Logo Baseball Cap",
            "price": 59.00,
            "prices": {
                "allsaints-GBP-list": 59.00
            },
            "short_description": "<p>Made from wool and leather, the State Cap is the perfect winter option. The classic shape features our signature embroidered on the front. It's an ideal addition for winter walks on chilly days.</p> <ul> <li>Leather adjustable strap</li> <li>Curved brim</li> <li>Five panel trucker cap</li> <li>AllSaints embroidered logo</li> <li>Corduroy checked fabric</li> <li>Ramskull metal clip on the strap</li> </ul>",
            "step_quantity": 1,
            "type": {
                "_type": "product_type",
                "variant": true
            },
            "unit": "NA",
            "upc": "5059786528395",
            "variants": [
                {
                    "_type": "variant",
                    "link": "https://www.allsaints.com/s/Sites-allsaints-uk-Site/dw/shop/v19_10/products/5059786528395?currency=GBP&locale=en-GB",
                    "orderable": true,
                    "price": 59.00,
                    "product_id": "5059786528395",
                    "variation_values": {
                        "color": "Grey",
                        "size": "One Size"
                    }
                }
            ],
            "variation_attributes": [
                {
                    "_type": "variation_attribute",
                    "id": "color",
                    "name": "Color",
                    "values": [
                        {
                            "_type": "variation_attribute_value",
                            "description": "Grey",
                            "name": "Grey",
                            "orderable": true,
                            "value": "Grey"
                        }
                    ]
                },
                {
                    "_type": "variation_attribute",
                    "id": "size",
                    "name": "UK Size",
                    "values": [
                        {
                            "_type": "variation_attribute_value",
                            "description": "One Size",
                            "name": "One Size",
                            "orderable": true,
                            "value": "One Size"
                        }
                    ]
                }
            ],
            "variation_groups": [
                {
                    "_type": "variation_group",
                    "link": "https://www.allsaints.com/s/Sites-allsaints-uk-Site/dw/shop/v19_10/products/MH607Z-7?currency=GBP&locale=en-GB",
                    "orderable": true,
                    "product_id": "MH607Z-7",
                    "variation_values": {
                        "color": "Grey"
                    }
                }
            ],
            "variation_values": {
                "color": "Grey",
                "size": "One Size"
            },
            "c_color": "Grey",
            "c_exclusivity": "all",
            "c_isBackInStockEnabled": "true",
            "c_isSale": false,
            "c_originCountry": "IN",
            "c_productType": "Hats",
            "c_size": "One Size",
            "c_subtitle": 1.0
        }
    ],
    "total": 1
}
```

#### Name

The attribute `name` is used. The name can be customised within a NewStore extension. Any other attribute that is included in the product response could also be used.

#### Description

The attribute `long_description` is used. If this is not present then the `short_description` attribute is used as a fallback. The description can be customised within a NewStore extension. Any other attribute that is included in the product response could also be used. Basic HTML markup is supported within the product description field.

Example:

```plaintext
<h1>Product Details</h1> 
<p>These are the product details</p> 

<h1>Composition</h1> 
<ul> 
  <li>80% Cotton</li> 
  <li>20% Elastane</li> 
</ul>
```

#### Images

The attribute `image_groups` is used. The image group with the highest `view_type` is selected. The following order is used to determine the highest quality:

- `hi-res`
- `pdp`
- `zoom`
- `extralarge`
- `large`
- `medium`
- `small`

NewStore makes use of an image resizing service and CDN. This service sits in between the app and the image source. This means that the highest resolution is needed as the app will add the necessary resizing parameters to the request url. The resizing service will then resize the image and serve the image via the CDN reducing the load on the original image source. Images should be publicly available and not require authentication.

> [!WARNING]
> If an image is updated within your system but the original url of the image is not changed then the new image will not be visible in the app. This is because it will remain cached by the image CDN. If an image is updated it is essential that the image url is also updated.

#### Videos

Product videos can also be shown within the media gallery. Note that this is an iOS feature only. Only one video per product is supported. The video url must be exposed to the app as a custom attribute `c_videoURL` . This attribute must be set on the variant level. The NewStore middleware will then extract this url and add it to the media gallery within the app.

Alternatively if using vimeo the video ID can be returned as a custom attribute on the variant level `c_vimeoID` With this the NewStore middleware will then make an API request directly to vimeo in order to download the relevant video sizes.

The video must be in `.mp4` format

#### Pricing

The app supports the display of 2 prices for a single product. The original and effective price. To get these prices the following is done:

- IF the attribute `prices` is present then the highest value is taken for the original price and the lowest price is taken for the effective price.
- IF the `prices` attribute is not present then the `price` attribute is used as both the original and effective price.
- IF the attribute `product_promotions` is present AND there are applicable product promotions then the `promotional_price` of the product promotion is used. If there are multiple product promotions then the lowest price is selected.
- IF the promotional price is lower than the previously calculated effective price then the effective price is updated to that of the promotional price.

If the effective price is the same as the original price then only one price is displayed in the app. Otherwise the original price is displayed with a strikethrough next to the effective price.

#### Web / share URI

The web uri is used when sharing a product. It is essentially the url of the product detail page on the website. This is constructed by combining the website domain with the following path segment `/on/demandware.store/#{site_id}/#{locale}/Product-Show` and the query string parameter `pid=#{product_id}`.

The website domain, site ID and locale are part of the configuration layer. The product ID is the ID of a variant product.

Full example: [https://www.example.org/on/demandware.store/Sites-Website-Europe-Site/en_GB/Product-Show?pid=5059786528395](https://www.allsaints.com/state-wool-leather-logo-baseball-cap/5059786528395.html)

#### Size chart

The shopping app can link to a size guide web page on the Product Detail View. The app will open the size guide within a webview and display the contents of the webpage. As such a webpage needs to be created that hosts the contents of the size guide.

The URL to the size guide can be added as the custom attribute `c_AppSizeGuide_URI` on product responses, such that it’s accessible by NewStore. Size Guide URLs can be static or completely dynamic (for example: provide different URLs for jeans and T-shirts). Size Guide URLs can be changed without the need to inform NewStore.

> [!NOTE]
> This attribute will need to be added on the product variant level.

Examples from other clients:

[Size guide | Find the perfect fit | G-STAR®](https://www.g-star.com/en_nl/size-guide?gender=men&amp;guide=tops&amp;unit=cm)

[Size Guide Women Clothing](https://www.allsaints.com/sizechart-women-clothing.html?highstreet=true)

#### Delivery message

A delivery message can be shown on the buy button on the product detail view, within the configuration popover on the product detail view and on a line item in the cart. It can be used to provide the user with an extra message unique to the selected product variant. For example an estimated delivery date, or an “only x left in stock” message.

The message that you wish to display should be added as the custom attribute `c_hsDeliveryMessage` on product responses. The value is expected to be a string.

> [!NOTE]
> This attribute will need to be added on the product variant level

#### Badges

Product badges or labels can be displayed in the app to indicate additional messaging. Some common use cases for products labels are:

- Indicating a product is one sale
- Indicating a product is new
- Indicating a product is eco friendly

This information will need to be exposed as a custom attribute on the product. The value should be a list of badges that should be shown on the specific product.

- The display text, text color and background color can be dynamic.
- A product can have multiple badges.

```json
{
  ...
  "c_newstoreProductLabels": [
    {
      "id": "sale",
      "text": "Sale",
      "text_color": {
        "red": 255,
        "green": 255,
        "blue": 255,
        "alpha": 1
      },
      "background_color": {
        "red": 0,
        "green": 0,
        "blue": 0,
        "alpha": 1
      }
    },
  ]
}
```

| **Name** | **Type** | **Comment** |
| --- | --- | --- |
| id | String | The ID of the label. The ID might be used within the app to determine where the label should be placed, e.g. “sale” label top right, “sustainability” label bottom left. |
| text | String | The text that should be displayed within the label |
| text_color | Object[RGBA] | A RGB color value with opacity component |
| background_color | Object[RGBA] | A RGB color value with opacity component |

An **RGBA** object describes a color, including a opacity component:

| Name | Type | Description |
| --- | --- | --- |
| red | number | A number between 0 and 255 |
| green | number | A number between 0 and 255 |
| blue | number | A number between 0 and 255 |
| alpha | number | A number between 0 and 1 that determines the opacity, where 0 is transparent and 1 is opaque. |

> [!WARNING]
> This attribute will need to be added on the product variant level

#### Inventory

Product inventory is fetched from SFCC using the `GET /products/{id}` endpoint. The `select` parameter is used to fetch only the relevant data. The following attributes are used:

```plaintext
select=(data.(id,inventory,step_quantity,min_order_quantity,c_hsAvailabilityClass))
```

Inventory is fetched on the product variant level and is cached on the CDN level for 60 seconds.

```plaintext
curl --location --request GET '/products/(4067979315715)?expand=availability&select=%28data.%28id%2Cinventory%2Cstep_quantity%2Cmin_order_quantity%29%29&currency=DKK&locale=en-DK' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
```

**Example response:**

```json
{
	"_v": "18.8",
	"_type": "product_result",
	"data": [
	  {
		"_type": "product",
		"id": "4067979315715",
		"inventory": {
			"_type": "inventory",
			"ats": 231,
			"backorderable": false,
			"id": "inventory-eu",
			"orderable": true,
			"preorderable": false,
			"stock_level": 231
		},
		"step_quantity": 1,
		"min_order_quantity": 1
	  }
	]
}
```

The following attributes are used when determining a product’s availability:

| Name | Type | Comment |
| --- | --- | --- |
| id | String | The variant Product ID |
| ats | Number | The available stock |
| orderable | Boolean | Whether the product can be purchased or not |

### Baskets

The following endpoints are used in the cart and checkout views of the app. The data is pulled directly from SFCC. NewStore does not store anything in relation to the basket within an internal database. A user’s access token is always sent within the `Authorization` header for both guest and logged in users. SFCC can use this token to identify the user and apply any promotions to the Basket.

#### SFCC endpoints used:

- POST /baskets
- POST /baskets/{basketID}/items
- PATCH /baskets/{basketID}/items/{itemID}
- DELETE /baskets/{basketID}/items/{itemID}
- POST /baskets/{basketID}/billing_address
- POST /baskets/{basketID}/shipments/me/shipping_address
- POST /baskets/{basketID}/customer
- GET baskets/{basketID}/shipments/me/shipping_methods
- GET baskets/{basketID}/payment_methods
- PATCH /baskets/{basketID}/shipments/me
- POST /baskets/{basketID}/payment_instruments
- POST /baskets/{basketID}/coupons
- DELETE /baskets/{basketID}/coupons/{couponID}
- POST /orders

#### Cart

**Cart creation**

`POST /baskets`

The `POST /baskets` endpoint is used to create an empty cart. This returns a cart ID which is then saved in the app and used in subsequent requests.

**Item operations**

The following endpoints are used to add items to the basket, update them and delete them.

- `POST /baskets/{basketID}/items`
- `PATCH /baskets/{basketID}/items/{itemID}`
- `DELETE /baskets/{basketID}/items/{itemID}`

```json
{
	"_v": "19.10",
	"_type": "basket",
	"_resource_state": "618bc664865e415433eeacd5ba37ac21c30e994d7194fd48fd8788877cf34d1e",
	"_flash": [{
		"_type": "flash",
		"type": "PaymentMethodRequired",
		"message": "No payment method ID was specified. Please provide a valid payment method ID.",
		"path": "$.payment_instruments[0].payment_method_id"
	}, {
		"_type": "flash",
		"type": "BillingAddressRequired",
		"message": "No billing address was specified. Please provide a valid billing address.",
		"path": "$.billing_address"
	}, {
		"_type": "flash",
		"type": "ShippingAddressRequired",
		"message": "No shipping address was specified. Please provide a valid shipping address.",
		"path": "$.shipments[0].shipping_address",
		"details": {
			"shipmentId": "me"
		}
	}],
	"adjusted_merchandize_total_tax": 0.00,
	"adjusted_shipping_total_tax": 0.00,
	"agent_basket": false,
	"basket_id": "12b46b24d02d48905fa534b622",
	"channel_type": "storefront",
	"creation_date": "2024-08-23T13:10:55.310Z",
	"currency": "USD",
	"customer_info": {
		"_type": "customer_info",
		"customer_id": "bd1ja8hb2tdad0pzTSS7IJ4IeS",
		"email": ""
	},
	"last_modified": "2024-08-23T13:10:55.598Z",
	"merchandize_total_tax": 0.00,
	"notes": {
		"_type": "simple_link",
		"link": "https://www.brand.com/s/Sites-EUR-Site/dw/shop/v19_10/baskets/12b46b24d02d48905fa534b622/notes?locale=en-US"
	},
	"order_total": 138.95,
	"product_items": [{
		"_type": "product_item",
		"adjusted_tax": 0.00,
		"base_price": 109.00,
		"bonus_product_line_item": false,
		"gift": false,
		"item_id": "b65d8229c1d0f6deac366c2bf5",
		"item_text": "space odyssey unpadded balcony bra",
		"price": 109.00,
		"price_after_item_discount": 109.00,
		"price_after_order_discount": 109.00,
		"product_id": "8718877376464",
		"product_name": "space odyssey unpadded balcony bra",
		"quantity": 1,
		"shipment_id": "me",
		"tax": 0.00,
		"tax_basis": 109.00,
		"tax_class_id": "DEFAULT",
		"tax_rate": 0.0,
		"c_EAN": "8718877376464"
	}],
	"product_sub_total": 109.00,
	"product_total": 109.00,
	"shipments": [{
		"_type": "shipment",
		"adjusted_merchandize_total_tax": 0.00,
		"adjusted_shipping_total_tax": 0.00,
		"gift": false,
		"merchandize_total_tax": 0.00,
		"product_sub_total": 109.00,
		"product_total": 109.00,
		"shipment_id": "me",
		"shipment_total": 138.95,
		"shipping_method": {
			"_type": "shipping_method",
			"description": "✔ SAFE & HYGIENIC. Highest safety and hygienic standards with contactless delivery",
			"id": "standard-US",
			"name": "USA | Standard with FedEx",
			"price": 29.95,
			"c_deliveryDays": 8,
			"c_shippingDays": [2, 3, 4, 5, 6]
		},
		"shipping_status": "not_shipped",
		"shipping_total": 29.95,
		"shipping_total_tax": 0.00,
		"tax_total": 0.00
	}],
	"shipping_items": [{
		"_type": "shipping_item",
		"adjusted_tax": 0.00,
		"base_price": 29.95,
		"item_id": "f253d7bc8b3980459b9a4f4353",
		"item_text": "Shipping",
		"price": 29.95,
		"price_after_item_discount": 29.95,
		"shipment_id": "me",
		"tax": 0.00,
		"tax_basis": 29.95,
		"tax_class_id": "DEFAULT",
		"tax_rate": 0.0
	}],
	"shipping_total": 29.95,
	"shipping_total_tax": 0.00,
	"taxation": "gross",
	"tax_total": 0.00,
	"c_freeShippingThreshold": 250.0
}
```

**basket**

| Name | Type | Comment |
| --- | --- | --- |
| basket_id | String | The ID of the basket |
| product_items | product_item | The list of Items in the cart |
| product_sub_total | Float | The Sub total |
| tax_total | Float | The Total Tax |
| shipping_total | Float | The total shipping costs |
| order_total | Float | The basket grand total |
| product_total | Float | The basket grand total - only used if order_total is missing |

**product_item**

| Name | Type | Comment |
| --- | --- | --- |
| item_id | String | The ID of the item. |
| product_id | String | The ID of the product |
| quantity | Number | The total number of products in this the line item |
| bonus_product_line_item | Boolean | Whether the item is a promotional item or not. For example a Free bag with orders above $100 |
| base_price | Float | The original price of the item |
| price_after_item_discount | Float | The final sale price of the item |

**Billing / shipping address**

The billing address is set on the basket using the `POST /baskets/{basketID}/billing_address` endpoint

The shipping address is set on the basket using the `POST /baskets/{basketID}/shipments/me/shipping_address` endpoint.

**Customer info**

The only customer info that is set on the basket is the email address. This is set by making a request to the `POST /baskets/{basketID}/customer` endpoint.

**Shipping methods**

Shipping methods are fetched using the `GET baskets/{basketID}/shipments/me/shipping_methods` and are set using the `PATCH /baskets/{basketID}/shipments/me` endpoint.

The standard shipping methods can be extended to support pick up in store and pick up points.

#### Pickup In Store

The list of available stores will be fetched from the `GET /stores` endpoint. Each store will be displayed as a shipping method sub-option.

1. Communicate the shipping method you’d like to use for Pick-Up In Store to NewStore.
2. Add support for the custom attribute `c_newstoreStoreId` as described below.

Once the customer has selected a store the app will communicate this back to SFCC through the `PATCH /baskets/{basketID}/shipments/me` endpoint:

```json
{
  "_resource_state": "...",
  "shipping_method": {
    "id": "PICKUP_IN_STORE",
    "c_newstoreStoreId": "1839"
  },
  "c_newstoreStoreId": "1839",
}
```

The shopping app expects to be able to read back the store ID it communicated on all endpoints returning a Basket object.

```json
{
  "_v": "19.1",
  "_type": "basket",
  "...": "...",
  "shipments": [
    {
      "_type": "shipment",
      "id": "me",
      "shipping_method": {
        "_type": "shipping_method",
        "description": "Pick up your order in one of our stores",
        "id": "PICKUP_IN_STORE",
        "name": "In-Store Pickup",
        "price": 0,
        "c_newstoreStoreId": "1839"
      },
      "shipment_id": "aadknaASga38cd5vaiWbNRZfwa",
    }
  ]
}
```

Just before creating the order through the `POST /orders` endpoint the app will update the Basket’s shipping address to be that of the store referred to in the `c_newstoreStoreId` attribute.

#### Pickup Points

The custom attribute `c_hs_suboptions` can be provided to display a list of pick-up points to the user. The attribute holds an array of objects each describing a pickup point. Example:

```json
{
  "_v": "19.10",
  "_type": "shipping_method_result",
  "applicable_shipping_methods": [
    {
      "_type": "shipping_method",
      "description": "✔ SAFE & HYGIENIC. Highest safety and hygienic standards with contactless delivery",
      "id": "standard-US",
      "name": "USA | Standard with FedEx",
      "price": 29.95,
      "c_hs_suboptions": [
        {
          "type": "pickup_point",
          "id": "161513",
          "price": 0.1,
          "name": "Boston Pick up Point",
          "address1": "First Street 61 A",
          "address2": "",
          "city": "Boston",
          "country_code": "US",
          "latitude": 52.095055611875,
          "longitude": 5.1213805885847,
          "postal_code": "12345",
          "opening_times": [
              { "day": "mon", "open": "08:00", "close": "22:00" },
              { "day": "tue", "open": "08:00", "close": "22:00" },
              { "day": "wed", "open": "08:00", "close": "22:00" },
              { "day": "thu", "open": "08:00", "close": "22:00" },
              { "day": "fri", "open": "08:00", "close": "22:00" },
              { "day": "sat", "open": "08:00", "close": "22:00" },
              { "day": "sun", "open": "08:00", "close": "22:00" }
          ]
        }
      ]
    }
  ],
  "default_shipping_method_id": "standard-US"
}
```

| Name | Type | Comment |
| --- | --- | --- |
| type | string | Must be `"pickup_point"` |
| id | string | **Required**. The pickup point ID. This will be used to communicate the selected pickup point back to SFCC. |
| name | string | **Required**. The pickup point name to display to the user |
| address1 | string | **Required**. The pickup point address (usually street name) |
| city | string | **Required**. The pickup point city |

Once the user has made their selection the sub-option ID will be sent as a custom attribute in the request to set the shipping method. You will have to ensure that the value `c_hs_suboption` is saved correctly and returned whenever the Basket object is requested.

Example request to set the pick up point

```json
PATCH /baskets/{basketID}/shipments/me 

{ 
  "shipping_method": { 
    "id": "standard-US", 
    "c_hs_suboption": "161513" 
  } 
}
```

Example response once the pickup point has been set

```json
PATCH /baskets/{basketID}/shipments/me

{
  "_v": "19.1",
  "_type": "basket",
  "...": "...",
  "shipments": [
    {
      "_type": "shipment",
      "id": "me",
      "shipping_method": {
        "_type": "shipping_method",
        "description": "✔ SAFE & HYGIENIC. Highest safety and hygienic standards with contactless delivery",
        "id": "standard-US",
        "name": "USA | Standard with FedEx",
        "price": 29.95
      },
      "shipment_id": "cd5vaiWbNRZfwaaadknaASga38",
      "c_hs_suboption": "161513"
    }
  ]
}
```

**Payment methods**

The payment methods are fetched using the `GET baskets/{basketID}/payment_methods` endpoint and are set using the `POST /baskets/{basketID}/payment_instruments` endpoint. Actual payment itself will be captured by the payment service provider.

Example response

```json
{
	"_v": "19.10",
	"_type": "payment_method_result",
	"applicable_payment_methods": [{
		"_type": "payment_method",
		"id": "visa",
		"image": "https://www.merchant.com/on/demandware.static/-/Sites/default/dwd3ccf6a6/payment-method-images/icon_visa.png",
		"name": "Visa",
		"payment_processor_id": "Adyen"
	}]
}
```

| Name | Type | Comment |
| --- | --- | --- |
| id | String | The ID of the payment method. Used when setting the payment method |
| image | String | A url to an image representing the payment method. Displayed as an icon next to the payment method name |
| name | String | The display name of the payment method that will be displayed in the app |

**Coupons**

A coupon code is added using the `POST /baskets/{basketID}/coupons` endpoint and removed using the `DELETE /baskets/{basketID}/coupons/{couponID}`.

**Order creation**

Once all information has been added by the user and the user has clicked on the pay button an order will be created using the `POST /orders` endpoint. After this the user will be directed to the payment page of the payment service provider. At this point the order process from the app’s perspective is now complete. A webhook will be sent by the payment service provider to SFCC once the payment has been completed. This will then be used by SFCC to update the order and mark it as paid so that it can be exported to the next step.

> [!WARNING]
> If the user abandons the cart during payment the order will then remain in a created state. You could create a job within SFCC to clean up these orders after a certain amount of time eg 24hours

### Accounts inc loyalty

Users have the option to create an account in the app or to login to the app using credentials of an account that was created on the website. All requests are made directly to SFCC meaning that an account created in the app is created directly within SFCC. No user account data is stored anywhere within NewStore.

**SFCC endpoints used:**

- `POST /customers`
- `GET /customers/{customerID}`
- `PATCH /customers/{customerID}`
- `POST /customers/{customerID}/addresses`
- `PATCH /customers/{customerID}/addresses/{addressID}`
- `POST /customers/auth`
- `POST /customers/password_reset`

#### **Account creation**

`POST /customers`

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Register%2Bcustomer

This endpoint is used to create a customer account via the app.

Core attributes from the response that are used by NewStore:

| Attribute | Type | ID | Description |
| --- | --- | --- | --- |
| first_name | String | first_name | User’s first name |
| last_name | String | last_name | User’s last name |
| customer_id | String | customer_id | User’s ID - This is used in subsequent request to SFCC when requesting a specific user. |
| login | String | login | The user’s login - normally it’s the same as the user’s email |
| email | String | email | The user’s email address |

#### Loyalty

If any integration is needed with external systems such as a loyalty provider then an OCAPI hook can be implemented in order to also register this user with the external service.

It’s possible to customise the response so that the app can display additional data in relation to a loyalty program. Note that these attributes need to be present on all endpoints that return the Customer object.

Possible custom attributes that can be displayed:

| Attribute | Type | ID |
| --- | --- | --- |
| c_pointsBalance | Number | c_pointsBalance |
| c_membershipLevel | String | c_membershipLevel |
| c_customerCard | String | c_customerCard |

Example request

```plaintext
curl --location 'https://domain.com/s/SiteID/dw/shop/v22_6/customers?locale=default' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'Authorization: Bearer GUEST_USER_ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data-raw '{
    "customer": {
        "login": "example@newstore.com",
        "email": "example@newstore.com",
        "first_name": "John",
        "last_name" : "Smith"
    },
    "password" :"Pa$$word123"
}'
```

Example response

Note the addition of the attributes prefixed with c_:

```json
{
    "_v": "22.6",
    "_type": "customer",
    "_resource_state": "0b6cf448d10f71f81106997c8fa450ee74153958f1c1a7fd26aabd4e051be87a",
    "auth_type": "registered",
    "creation_date": "2023-09-13T13:04:40.620Z",
    "customer_id": "abyrQrVZmchUVZReiARRXKcZOo",
    "customer_no": "00066130",
    "email": "80ec0e90-edf8-4eba-8f35-845557fd1b68@newstore.com",
    "enabled": true,
    "first_name": "John",
    "hashed_login": "b628995efa7e245d19ffcdc0dc7b12ef7f807d738e0ecc77e79f2764e44301e8",
    "last_modified": "2023-09-13T13:04:40.631Z",
    "last_name": "Smith",
    "login": "80ec0e90-edf8-4eba-8f35-845557fd1b68@newstore.com",
    "c_pointsBalance": 100,
    "c_membershipLevel": "Gold",
    "c_customerCard": "1234567890",
}
```

#### Fetching a customer

GET /customers/:ID

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Get%2Ba%2BCustomer

Example request:

```bash
curl --location --request GET 'https://domain.com/s/siteID/dw/shop/v19_10/customers/ID&locale=default' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'Authorization: Bearer ACCESS_TOKEN'
```

Example response (note the addition of the attributes prefixed with c_):

```json
{
    "_v": "22.6",
    "_type": "customer",
    "_resource_state": "0b6cf448d10f71f81106997c8fa450ee74153958f1c1a7fd26aabd4e051be87a",
    "auth_type": "registered",
    "creation_date": "2023-09-13T13:04:40.620Z",
    "customer_id": "abyrQrVZmchUVZReiARRXKcZOo",
    "customer_no": "00066130",
    "email": "80ec0e90-edf8-4eba-8f35-845557fd1b68@newstore.com",
    "enabled": true,
    "first_name": "John",
    "hashed_login": "b628995efa7e245d19ffcdc0dc7b12ef7f807d738e0ecc77e79f2764e44301e8",
    "last_modified": "2023-09-13T13:04:40.631Z",
    "last_name": "Smith",
    "login": "80ec0e90-edf8-4eba-8f35-845557fd1b68@newstore.com",
    "c_pointsBalance": 100,
    "c_membershipLevel": "Gold",
    "c_customerCard": "1234567890",
}
```

#### Updating a customer

`PATCH /customers/{customerID}`

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Update%2Ba%2BCustomer

Note that it is only possible to update a user’s first_name, last_name or custom attributes. A user cannot change their email via the shopping app.

Example request:

```bash
curl --location --request PATCH 'https://domain.com/s/siteID/dw/shop/v19_10/customers/:ID?locale=default' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
    "first_name": "johny_1",
    "last_name": "Smithy_1"
}
'
```

Example response:

```json
{
    "_v": "19.10",
    "_type": "customer",
    "_resource_state": "786f9d368d3d1832e4f90d3422d011e484b5b75da933145f1695a181c328e9ec",
    "auth_type": "registered",
    "creation_date": "2023-09-13T13:04:40.620Z",
    "customer_id": "abyrQrVZmchUVZReiARRXKcZOo",
    "customer_no": "00066130",
    "email": "80ec0e90-edf8-4eba-8f35-845557fd1b68@newstore.com",
    "enabled": true,
    "first_name": "johny_1",
    "last_login_time": "2023-09-13T13:33:13.918Z",
    "last_modified": "2023-09-13T13:56:30.767Z",
    "last_name": "Smithy_1",
    "last_visit_time": "2023-09-13T13:33:13.918Z",
    "login": "80ec0e90-edf8-4eba-8f35-845557fd1b68@newstore.com",
    "previous_login_time": "2023-09-13T13:33:13.918Z",
    "previous_visit_time": "2023-09-13T13:33:13.918Z"
}
```

#### Adding an address

`POST /customers/{customerID}/addresses`

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Create%2Bcustomer%2Baddress

The shopping app only supports 1 customer address. When creating an address in the app the address_id will always be set to `”default”`. If a customer has multiple addresses then the address with ID `”default”` will be shown in the app. If there is no address with address ID `”default”` then the first address in the list of addresses will be shown.

Example request:

```plaintext
curl --location 'https://domain.com/s/siteID/dw/shop/v19_10/customers/:ID/addresses?locale=default' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
    "address_id": "default",
    "first_name": "John",
    "last_name": "Smith",
    "company_name": null,
    "postal_code": "123456",
    "city": "city",
    "address1": "address1",
    "address2": "address2",
    "phone": "1234567890",
    "country_code": "US",
    "state_code": "AZ",
    "preferred": true
}
'
```

Example response

```json
{
    "_v": "19.10",
    "_type": "customer_address",
    "_resource_state": "9527edafb4c7855ae7b0f19d72fb9b2f59c60df5c1dec4536f10bcb3ba5f3b11",
    "address1": "address1",
    "address2": "address2",
    "address_id": "default",
    "city": "city",
    "country_code": "US",
    "creation_date": "2023-09-13T13:43:55.142Z",
    "first_name": "John",
    "full_name": "John Smith",
    "last_modified": "2023-09-13T13:52:31.036Z",
    "last_name": "Smith",
    "phone": "1234567890",
    "postal_code": "123456",
    "preferred": true,
    "state_code": "DE"
}
```

#### Updating an address

`PATCH /customers/{customerID}/addresses/{addressID}`

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Update%2Bcustomer%2Baddress

Example request

```plaintext
curl --location --request PATCH 'https://domain.com/s/siteID/dw/shop/v19_10/customers/:ID/addresses/default?locale=default' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
--header 'X-Dw-Http-Method-Override: PUT' \
--header 'Content-Type: application/json' \
--data '{
    "address_id": "default",
    "first_name": "John",
    "last_name": "Smith",
    "company_name": null,
    "postal_code": "123456",
    "city": "city",
    "address1": "address1",
    "address2": "address2",
    "phone": "1234567890",
    "country_code": "US",
    "state_code": "DE",
    "preferred": true
}
'
```

Example response

```json
{
    "_v": "19.10",
    "_type": "customer_address",
    "_resource_state": "9527edafb4c7855ae7b0f19d72fb9b2f59c60df5c1dec4536f10bcb3ba5f3b11",
    "address1": "address1",
    "address2": "address2",
    "address_id": "default",
    "city": "city",
    "country_code": "US",
    "creation_date": "2023-09-13T13:43:55.142Z",
    "first_name": "John",
    "full_name": "John Smith",
    "last_modified": "2023-09-13T13:52:31.036Z",
    "last_name": "Smith",
    "phone": "1234567890",
    "postal_code": "123456",
    "preferred": true,
    "state_code": "DE"
}
```

#### Login

`POST /customers/auth`

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Starts%2Ba%2Bpassword%2Breset%2Bprocess

A user’s user name and password are stored on device within the app’s key chain. If the access token expires then the stored user name and password will be used to log the user in automatically again. Unless a user has changed their credentials the user will always remain logged into the app.

Example request

```plaintext
curl --location 'https://domain.com/s/siteID/dw/shop/v19_10/customers/auth?locale=default' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'User-Agent: Highstreet Backend' \
--header 'Content-Type: application/json' \
--header 'Authorization: Basic USER_CREDENTIALS' \
--data '{
	"type": "credentials"
}'
```

#### Session sharing

`POST /sessions`

[https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/sessionbridge.html?q=session%20bridge](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/sessionbridge.html?q=session+bridge)

This is used to share a user’s session across the app and a webview. It ensures that a logged in user remains logged in when they view web content within the app. For example, the “My Account” page or the external checkout. The JWT access token obtained from the Shop API is sent in the request and the response will contain a list of cookies that need to be set to keep the user logged in on the web. The webview is opened with these cookies added as in the request header for the initial request.

Example request:

```plaintext
curl --location --request POST 'https://domain.com/s/siteID/dw/shop/v19_10/sessions?locale=default' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'User-Agent: Highstreet Backend' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer ACCESS_TOKEN' \
```

#### Reset password

`POST /customers/password_reset`

https://developer.salesforce.com/docs/commerce/b2c-commerce/references/ocapi-shop-api?meta=Starts%2Ba%2Bpassword%2Breset%2Bprocess

This request will trigger the Reset Password hook to be fired within SFCC. You will have to implement the password reset hook to actually make sure that the user receives a password reset email. See [SFCC docs](https://developer.salesforce.com/docs/commerce/b2c-commerce/references/b2c-commerce-ocapi/get-started-with-ocapi.html) for full info.

The link in the email will link to the password reset flow of the website. There is no native password reset view in the app. Once the user has reset their password on the web they will need to open the app again to login.

Example request:

```plaintext
curl --location 'https://domain.com/s/siteID/dw/shop/v19_10/customers/password_reset?locale=default' \
--header 'User-Agent: Highstreet Backend' \
--header 'X-Dw-Client-Id: CLIENT_ID' \
--header 'Content-Type: application/json' \
--data-raw '{
    "type": "login",
    "identification": "example@newstore.com"
}'
```

## Webhooks

If the web checkout is being used then a webhook will need to be sent to NewStore once an order has been paid. This ensures that payment status updates for orders in SFCC are communicated to NewStore. This will enable Newstore to properly mark orders as paid.

### Key Requirements

1. **Trigger for Webhook Request**
  - Once an order is successfully paid in SFCC, a background process must send a POST request to the Newstore webhook endpoint.
  - For every change in an order’s payment status, a new webhook request should be sent. For instance:
    - If the payment status transitions from “failed” to “paid,” a second webhook request must be sent to inform Newstore of this update.
2. **Handling Payment Status Updates**
  - Ideally, SFCC should send **all payment status updates** to Newstore.
  - A delay between the order placement and the webhook being sent is acceptable.

### Webhook Configuration

#### **Endpoint URLs**

The appropriate endpoint depends on the environment:

- **Production:** `https://{tenantID}.api.highstreetapp.com/webhooks/sfcc/events`
- **Staging:** `https://{tenantID}-staging.api.highstreetapp.com/webhooks/sfcc/events`
- **Development:** `https://{tenantID}-development.api.highstreetapp.com/webhooks/sfcc/events`

#### **Request Method**

- HTTP Verb: `POST`

#### **Content Type**

- `application/json`

#### **Request Body**

- The request body must be a valid JSON object containing the following keys:
  - `order_id`: The unique identifier for the order.
  - `cart_id`: The unique identifier for the basket.

Example Request Body:

```plaintext
{
  "order_id": "123",
  "cart_id": "123456"
}
```

### Response Handling

#### **Successful Response**

- Status Code: `200`
- Response Body: Empty

#### **Failure Response**

- If a non-200 status code is received, treat the request as failed.
- Failed requests should be retried to ensure that Newstore is updated correctly.

### Additional Notes

- The endpoint does **not require authentication**.
- Ensure robust error handling and retry mechanisms for failed requests to maintain data consistency between SFCC and NewStore.
