---
schema_version: "1"
id: 01665990e0bccfdf4a7409c4a8efdd583d537db83bb4f7678c9fa8eed1d7c170
title: Web Services
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/commerce-cloud/b2c-commerce/guides/web-services/b2c-webservices.md
canonical_url: https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/b2c-webservices.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: 2b8e8919e69e69c319e27c8668253336fc9200afe38eb01fb3a0c63cbcd292ba
products:
  - Composable Storefront
  - Salesforce APIs
---
# Web Services

Salesforce B2C Commerce provides a web service framework, which helps you manage calls to web services and analyze service performance. The framework enforces limits that you configure ― limits on the overall number of calls allowed in a time interval, and limits on the number of failed calls allowed in a time interval. It also collects analytics on your web service calls.

## Overview

For each web service, the framework requires a service configuration, a service profile configuration, and a service credential configuration. You create these configurations in Business Manager.

The service configuration generates a `ServiceConfig` object. The service credential is used for basic authentication. The service profile determines how B2C Commerce manages timeouts and rate limits for the web service.

![Web service overview](https://developer.salesforce.com/media/b2c-commerce/b2c_web_services_web_services_ovr.gif)

The Web Service Script

A web service script obtains a `Service` object by calling the `LocalServiceRegistry.createService` method. The method takes two arguments: the name of a service configuration, and a script object that contains callback code that you provide.

Your script then invokes the service by calling `Service.call`. When you call this method, the framework checks if the configured rate limit or circuit breaker limit has been exceeded. If not, the framework executes the callbacks you implemented in the Service object.

When the call is complete, your script either passes the result of the successful call to the pipeline dictionary or throws an error.

**note:** If your storefront uses pipelines to call web services, the web service script is invoked by a script node within the pipeline. If your storefront uses SFRA controllers to call web services, the web service script doesn’t have to be a separate script file: it can be embedded in the controller itself.

## Creating a New Web Service

To create a web service:

1.  [Configure Web Services in Business Manager](#configuring-web-services-in-business-manager)
2.  [Code Your Web Service](#code-the-web-service-call).
3.  [Set Up Logging and Troubleshoot Your Web Service.](#web-service-logging-and-troubleshooting)
4.  Get the web service ready to go live.

## Getting the Web Service Ready to Go Live

1.  Configure credentials, private keys and certificates on the Development and Production instance. These can't be imported or exported and must be manually created on each instance.
2.  Use code replication to push your code to the Development and Production instance.
3.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Import & Export** > **Service**. Select the **Service** option to export your service, profile, and credential configurations and import them into the Development and Production instances.

## Web Service Performance and Incident Analysis

If you experience reports of problems with a specific web service on your site, in addition to logging, investigate the incident using the analytics for registered web services.

The analytics are available at Business Manager: Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Service Status**. ![Service Status](https://developer.salesforce.com/media/b2c-commerce/b2c_web_services_service_status.gif)

Click any web service name to drill down to see information about the performance of that web service. Data for a web service is available in near real time. Data is stored for up to 10 days.

![Real-Time Trend](https://developer.salesforce.com/media/b2c-commerce/b2c_web_services_service_status_trends.gif)

## Configuring Web Services in Business Manager

Use Business Manager to configure a web service.

### Create a Web Service Profile

The web service profile defines how B2C Commerce detects and manages high numbers of requests to and responses from the service, as well as its availability. Different services with similar requirements can all use the same service profile.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Services**.

2.  On the Services page, click the **Profiles** tab.

3.  On the Service Profiles page, click **New**.

4.  On the New Service Profile page, enter a descriptive name for the profile.

5.  Enter the number of milliseconds for the client connection timeout. B2C Commerce waits this number of milliseconds for a response from the web service before throwing an error.

6.  Check the **Enable Circuit Breaker** box if you want to enable the circuit breaker to identify when a service is unavailable and stop making calls to the web service.

    By default, the Enable Circuit Breaker box is checked.

    We recommend that you always enable the circuit breaker, but keep the number of calls set to zero (or blank). This enables the system-level circuit breaker.

7.  (Optional) Enter the number of calls that must fail to trigger the circuit breaker into the Max Circuit Breaker Calls field.

    If you set a value for this field, you must also set a value for the Circuit Breaker Interval (ms) field. Otherwise, leave both fields empty.

8.  (Optional) Enter the number of milliseconds in which the maximum number of calls can fail into the Circuit Breaker Interval (ms) field.

    If you set a value for this field, you must also set a value for the Max Circuit Breaker Calls field. Otherwise, leave both fields empty.

9.  Check the **Enable Rate Limit** box if you want to limit the number of outgoing calls within a specific interval.

10. Enter the maximum number of calls that B2C Commerce makes for the rate limit interval into the Max Rate Limit Calls field.

11. Enter the number of milliseconds in which B2C Commerce can make the maximum number of calls to the web service into the Rate Limit Interval (ms) field.

### Create a Web Service Credential

The web service credentials are used for basic authentication.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Services**.

2.  On the Services page, click the **Credentials** tab.

3.  On the Service Credentials page, click **New**.

4.  On the New Service Credential page, enter a name for the credential. This name can't contain spaces. Use a descriptive name, but don’t include user or password information. For example: `http.mysite.myservice.cred`.

5.  Enter the URL to the service, including the protocol. For example: `ftp://51.134.145.10`

    To use one service configuration for several URIs, extend or alter this URL using the getURL callback in the service registry definition.

6.  Enter the username for the credential.

7.  Enter the password. The password is masked after you type it and can't be retrieved from the Business Manager, so be sure to store the value securely elsewhere.

### Create a Web Service Configuration

The web service configuration specifies the credentials and web service profile that B2C Commerce uses when calling a web service.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Services**.

2.  On the Services page, click **New**.

3.  On the New Service page, enter a name for the service.

    We recommend the naming pattern `{cartridge}.{protocol}.{service}.{operation}`.

    For example: `mycartridge.http.payment.get` or `mycartridge.ftp.partner.getPriceList`.

    If service names contain periods, the period-delimited name segments become part of the logging hierarchy. Including the cartridge in the name is useful when you have multiple web service integrations, because it groups logging information and ensures unique service names.

4.  Select the service type to use for your web service. The type determines the underlying class used to call your web service and adds methods to the methods inherited from the Service class.

5.  If the web service is available and you want to make calls to it, select **Enabled**. It isn't enabled by default.

6.  In the Service Mode list, to make calls to a live web service, select **Live**. To use the mocked call configured in your service registry to simulate the expected response from the web service, select **Mocked**.

7.  Enter the name of a prefix for the log file for this service.

    If you don't enter a prefix, the log name defaults to `main`. All logs are written in the shared log directory to a file named `service-prefix-internalID-date.log`. If you don’t enter a prefix, the file is named `service-main-serviceID-date`.

8.  If you want data in the request and response of the web service call to be logged, select **Communication Log Enabled** .

    If you enable the communications log, Salesforce strongly recommends using the log filtering callback methods to remove private or sensitive data from the log messages.

9.  If you want to prevent unfiltered communication logs from being written on non-production instances, select **Force PRD behavior in non-PRD environments**.

    If you enable this setting on a non-production instance, communication logs are disallowed unless a filter is configured. If you disable this setting on a non-production instance, communication logs are allowed regardless of whether a filter is configured. This setting has no effect on production instances.

10. Select one of the service profiles.

11. Select one of the service credentials.

### Timeout for Web Services

Specify the timeout in milliseconds. This value is used for both the connection and socket timeout of any internal connection. If B2C Commerce receives no response within this time, it throws an error.

The total run time of a service can be longer than the timeout, as long as individual responses don't exceed it. For example, consider a 5000-ms timeout. A 4-second connection attempt, followed by reading two chunks of data at 4 seconds apiece, takes about 13 seconds total. Because none of the individual communications exceeded 5 seconds, the process doesn’t time out.

### Create Custom Attributes for Web Services

Customize web service configurations, credentials, and profiles by adding custom attributes to their system objects. When added to an attribute group, these custom attributes can be edited in Business Manager.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Site Development** > **System Object Types**.

2.  On the System Object Type List page, select **ServiceConfig**, **ServiceCredential**, or **ServiceProfile.**

3.  On the System Object Type List page, click the **Attribute Definitions** tab.

    If you’ve already created a custom attribute, find and add it, otherwise, proceed to the next step.

4.  On the System Object Type List page Attribute Definitions tab, click **New**.

5.  Enter the properties for the custom attribute and click **Apply**.

    These attributes are created identically to any other custom attributes.

6.  Click **\<\< Back**.

7.  Click the **Attribute Grouping** tab.

8.  On the System Object Type List page Attribute Groupings tab, click **Edit** for the section on the tab where you want to add your attributes.

    **note:** You must add the attribute to a grouping to see it in Business Manager.

9.  Click ![Browse button](https://developer.salesforce.com/media/b2c-commerce/b2c_active_merchandising_elipse_btn.gif) to browse for the custom attribute you created, check its checkbox, and click **Select**.

### Service Types

When creating a web service configuration in Business Manager, you must select a service type. The type determines the underlying class used to call your web service and adds methods to those inherited from the Service class.

- HTTP - uses `dw.net.HTTPClient`.

  Has additional methods to set authentication, encoding, output file, request method (defaults to POST), an HTTP header, and additional parameters for the URI.

  The `parseResponse` method gives the `HTTPClient` as its extra argument.

- HTTP Form - uses `dw.net.HTTPClient`.

  The createRequest method takes the parameters given to call() and construct a URL encoded form POST by default.

  Has the same additional methods as the HTTP type, allowing you to set authentication, encoding, output file, request method (defaults to POST), an HTTP header, and additional parameters for the URI.

  As with the HTTP type, the `parseResponse` method gives the `HTTPClient` as its extra argument.

- FTP - uses `dw.net.FTPClient`.

  Has a `setOperation(String, Object...)` method to set the operation to perform when executing the call to the web service. If the `setOperation` method isn't called, the execute callback defined in the web service registry is executed when the web service is called. The callback can define any number of operations to perform.

  The `parseResponse` method gives the output of the method call specified by `setOperation`, or whatever object was returned by the custom execute callback.

- SFTP - uses `dw.net.SFTPClient`. It’s otherwise identical to the `FTP` type.

- SOAP - uses `dw.ws.webReference2`.

  Requires additional methods to get the port

- Generic - Doesn’t wrap any class. Used to define custom calls.

### Web Service Rate Limiter and Circuit Breaker

The rate limiter and circuit breaker determine how many calls can be made to a web service.

#### Rate Limiter

The rate limiter allows a maximum number of calls to a web service in a specified time interval. The rate limit is checked prior to every invocation of the web service. If the rate limit is reached, a `ServiceUnavailableException` is thrown.

The timeout behavior for the rate limiter is similar to the circuit breaker timeout behavior.

#### Circuit Breaker

The circuit breaker suspends calls to a web service if a certain number of calls fail within a specified time interval.

Enable a circuit breaker for any service. The circuit breaker checks whether the limit of failed calls is reached prior to every invocation of the web service. If the limit is reached, B2C Commerce throws a `ServiceUnavailableException` rather than execute the service. Otherwise, B2C Commerce calls the web service as usual.

If the web service fails due to something detected as a misconfiguration or remote problem, then the circuit breaker is notified. These problems include:

- Issues at initial connection:
  - Unknown host
  - Connection timeout
  - Connection refused
- Protocol-specific errors, such as HTTP 500 - internal server error or HTTP 503 - service unavailable.
- Exceptions explicitly thrown as part of the service call

**note:** This list doesn't include HTTP 4xx errors. These are used to indicate the client is doing something wrong, but the remote server itself is fine. Assuming whatever underlying issue is causing problems is addressed, service calls return to normal as the circuit breaker timeouts expire.

Circuit Breaker Timeout Behavior

The timeout caused when the circuit breaker reaches its limit is calculated dynamically. Each time a registered web service wrapper is called, the circuit breaker is checked. If the circuit breaker is triggered, then the call isn't made. In the example below, a web service is triggered every millisecond. In this example, the circuit breaker is configured with a maximum of three failed calls per 6 milliseconds.

![Circuit breaker timeout behavior](https://developer.salesforce.com/media/b2c-commerce/b2c_web_services_web_services.gif)

## Create a Service Instance

A service instance is a single-use object used to call a remote web service.

Use the `dw.svc.LocalServiceRegistry.createService()` method to create a service instance. This method takes two arguments:

- The ID of a service configured in Business Manager
- A configuration object with callback handlers that you implement

The callbacks contain code that is executed at appropriate times by the service framework. For example, an HTTP or FTP service instance typically implements three callbacks: `createRequest`, `parseResponse`, and (for testing) `mockCall`.

A SOAP service implements two more callbacks: `initServiceClient` and `execute`.

## Example: Create an FTP Service Instance

The following FTP service instance executes a single FTP operation (list):

```javascript
//LocalServiceRegistry.createService creates an instance of MyFTPService

var myFTPService = LocalServiceRegistry.createService("MyFTPService", {
  // Three callback handlers are invoked by the Service Framework
  // for FTP services at appropriate times

  // Callback handler to construct request

  createRequest: function (svc: FTPService, params) {
    svc.setOperation("list", "/");
  },

  // Callback handler to parse response

  parseResponse: function (svc: FTPService, params) {
    var x: Array = [];
    var resp: Array = params;
    for (var i = 0; i < resp.length; i++) {
      var f = resp[i];
      x.push({ name: f["name"], timestamp: f["timestamp"] });
    }
    return x;
  },

  // Callback handler to test the service using a mock response

  mockCall: function (svc: FTPService, params) {
    return [
      { name: "file1", timestamp: new Date(2011, 02, 21) },
      { name: "file2", timestamp: new Date(2012, 02, 21) },
      { name: "file3", timestamp: new Date(2013, 02, 21) },
    ];
  },
});
```

## Creating Service Callbacks

When you use `dw.svc.Service.call(Object...)` to invoke a web service, the service framework checks whether the call exceeds either the rate limiter or circuit breaker thresholds and, if not, makes the call. When making a call, the framework invokes the callbacks you implemented in your service instance.

Each callback corresponds to one of the callback methods defined by the `dw.svc.ServiceCallback` class. These methods are called in the following order:

1.  `initServiceClient(Service)` ― Creates the underlying client used to make the call. Required only for SOAP Services. Other client types are created automatically.
2.  `createRequest(Service, Object...)` ― Given arguments to the `Service.call(Object...)`, configure the actual service request. This may include setting request headers, defining the message body, and so on.
3.  `execute(Service, Object)` ― Perform the actual request. At this point the client has been configured with the relevant credentials, so the call should be made. This is required for SOAP services.
4.  `parseResponse(Service, Object)` ― Convert the result of the call into an object to be returned from the `Service.call(Object...)` method.

Some callback methods may be optional, depending on the type of service.

Every callback method requires a `Service` object (for a generic service) or one of its subclasses (`FTPService`, `HTTPFormService`, `HTTPService`, `or SOAPService`).

### HTTP and HTTP Form Callbacks

For an HTTP service, pass in `HTTPService` objects. For an HTTP form, pass in `HTTPFormService` objects.

Recommended callbacks for HTTP services:

- `createRequest`: Required to create a `requestData` object. If you need additional processing, pass the `requestData` object to the `execute` callback. Otherwise, the `requestData` object is used to call the web service when the `Service` object is invoked.
- `parseResponse`: Gives the `HTTPClient` as its extra argument. Use to parse the response contained in the `Service` object.

### FTP and SFTP Callbacks

For an FTP or SFTP service, pass in `FTPService` objects.

Recommended callbacks

- `createRequest`: Required to create a `requestData` object. If you need additional processing, pass the `requestData` object to the execute callback. Otherwise, the `requestData` object is used to call the web service when the `Service` object is called.
- `parseResponse`: Gives the `FTPClient` as its extra argument. Use to parse the response contained in the `Service` object.
- `execute`: If the `setOperation` method isn't called in the web service script, operations defined in the execute callback are executed when the web service is invoked. Use the execute method to perform multiple operations in sequence.

### SOAP Callbacks

For any SOAP service, pass in a `SOAPService` object.

Recommended callbacks

- `initServiceClient`: Implement to return a `dw.ws.port` and `webreferences2` object.
- `createRequest`: Required to create a `requestData` object. This object must be passed to the execute method.
- `execute`: Specifies additional processing for the web service request.

  **note:** If you get your port in this step, instead of in the `initServiceClient` callback, any timeouts you set will override those set in the service configuration. This isn't recommended.

- `parseResponse`: Use this method to parse the response in the `Service` object.

### Generic Callbacks

Doesn’t wrap any class. Used to define custom calls.

- `createRequest:` Required to create a `requestData` object. If you need additional processing, pass the `requestData` object to the execute callback. Otherwise, the `requestData` object is used to call the web service when the `Service` object is called.
- parseResponse: Use to parse the response contained in the `Service` object.

**note:** You must pass a plain Service object, rather than a subclass, for this callback method.

### Mock Callbacks

Mock web service responses in two ways.

First, configure the web service to use mock responses:

1.  In Business Manager, click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration > Operations > Services**. For the Service Mode, select **Mocked**.
2.  Provide a `mockCall` callback handler. For example, in your web service script:

```javascript
mockCall: function(svc:HTTPService, client:HTTPClient){
    return {
        statusCode: 200,
        statusMessage: "Success",
        text: "MOCK RESPONSE (" + svc.url + ")"
        };
    }
```

Force a mock call, regardless of configuration. Add a mock method to your service definition and then call it explicitly in your web service script. Remove this method from your script when testing live web service calls.

```javascript
result = service.setMock().call();
```

## Code the Web Service Call

The general approach to coding web services in Salesforce B2C Commerce involves writing a script for a RESTful web service and storing credentials or certificates in the Business Manager.

The web service script:

- gets `Service` objects from the registry
- provides input parameters for the `createRequest` callback used to construct the URL to call the web service
- provides error handling based on the `Result` of the web service call
- writes output variables to the pipeline dictionary to be used by the rendering template

If you’re implementing a SOAP web service or require WS-Security features, write a script that uses the `dw.ws` package to implement web services. In this case, certificates must be stored in your cartridge.

### Creating a Controller for Web Services

When creating a controller for web services, you usually must include error handling and render the results of the web service call.

### Understanding Pipelines for Web Services in Legacy Implementations

If you’re maintaining an older cartridge that uses pipelines, web services are implemented in a pipeline with a script node for the web service script. The script node must have an error transition for error handling and a transition to a rendering template if you must render the results of the web service call.

### Creating a Web Service Script

Your web service script must import the `dw.svc`package.

Use `LocalServiceRegistry.createService` to create a `Service` object. Your script can modify or extend the `Service` object. Also include business logic to control the parameters used to invoke the web service.

Use `Service.call` to invoke the web service.

When the service is invoked, the service framework checks the circuit breaker and rate limiter. If either the circuit breaker or rate limiter are triggered, then no call is made and the appropriate errors are logged. If neither are triggered, then the callback methods in the service instance are executed, and the `dw.svc.Result` object is stored in the `Service`.

Example 1: Simple FTP Service Call

In this example, the operation invoked for the web service is determined by an input variable passed from the pipeline. In the dictionary bindings for the script node, the `numCalls` variable is bound to `CurrentHttpParameterMap.numCalls.stringValue` and the `testType` variable is bound to `CurrentHttpParameterMap.testType.stringValue`.

```javascript
/**
 *  testFTPClient.ds
 *

 *   @input testType : String
 *   @input numCalls : String
 *   @output svcConfig : dw.svc.Service
 *   @output ftpResult : dw.svc.Result
 *
 */
importPackage(dw.util);
importPackage(dw.svc);
importPackage(dw.net);
importPackage(dw.io);

function execute(args: PipelineDictionary): Number {
  var service: Service;
  var result: Result;
  var counter = args.numCalls;
  var mockCall = false;
  var pipelineError = false;

  var callTestFTP = LocalServiceRegistry.createService("test.ftp", {
    mockCall: function (svc: FTPService, params) {
      return [
        {
          name: "testfile1",
          timestamp: new Date(2011, 02, 21),
        },
        {
          name: "testfile2",
          timestamp: new Date(2012, 02, 21),
        },
        {
          name: "testfile3",
          timestamp: new Date(2013, 02, 21),
        },
      ];
    },
    createRequest: function (svc: FTPService, args) {
      return svc;
    },
    parseResponse: function (svc: FTPService, result: Array) {
      var ret: Array = [];
      for (var i = 0; i < result.length; i++) {
        ret.push(result[i].name);
      }
      ret.sort();
      return ret;
    },
  });

  // Execute the request on the service configuration
  function makeCall(svcConfig: Service, params: Object) {
    if (counter == null) {
      counter = 1;
    }

    while (counter != 0) {
      if (mockCall) {
        result = service.setMock().call(params);
      } else if (pipelineError) {
        result = service.setThrowOnError().call(params);
      } else {
        result = service.call(params);
      }
      counter--;
    }

    // Set pdict out values
    args.svcConfig = service;
    args.ftpResult = result;
  }

  switch (args.testType) {
    case "LIST":
      service = callTestFTP;
      service.setOperation("list");
      break;
    case "CD":
      service = callTestFTP;
      service.setOperation("cd", "/");
      break;
    case "MKDIR":
      service = callTestFTP;
      service.setOperation("mkdir", "test");
      break;
    case "DELETE":
      service = callTestFTP;
      service.setOperation("del", "test");
      break;
  }

  makeCall(service);

  if (result == null || service == null) {
    return PIPELET_ERROR;
  }

  return PIPELET_NEXT;
}
```

Example 2: Modifying the Service Object for an HTTP Service

This example demonstrates how to use the methods on the `Service` object to modify the `Service` object before invoking the service. The ENCODING case in this example adds a header, URL parameters, and changes the encoding before invoking the service.

```javascript
/**
 * testHTTPClient.ds
 *
 *   @input testType : String the type of test to run.
 *	 @input numCalls : String the number of calls to make.
 *   @input returnCode : String the simulated http return code.
 *	 @output svcConfig : dw.svc.Service the service configuration object.
 *   @output httpResult : dw.svc.Result the http result object.
 *
 */
importPackage(dw.system);
importPackage(dw.svc);
importPackage(dw.net);
importPackage(dw.io);

function execute(args: PipelineDictionary): Number {
  var service: Service;
  var result: Result;
  var counter = args.numCalls;
  var mockCall = false;
  var pipelineError = false;
  var returnCode = args.returnCode;
  var requestBody = {
    testString: "foo",
    testNum: 5,
    testBool: true,
  };

  var callTestGet = LocalServiceRegistry.createService("test.http.get", {
    createRequest: function (svc: HTTPService, args) {
      svc.setRequestMethod("GET");
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
    mockCall: function (svc: HTTPService, client: HTTPClient) {
      return {
        statusCode: 200,
        statusMessage: "Success",
        text: "MOCK RESPONSE (" + svc.URL + ")",
      };
    },
    filterLogMessage: function (msg: String) {
      return msg.replace("headers", "OFFWITHTHEHEADERS");
    },
  });

  var callTestPost = LocalServiceRegistry.createService("test.http.post", {
    createRequest: function (svc: HTTPService, args) {
      // Default request method is post
      // No need to setRequestMethod
      if (args) {
        svc.addHeader("Content-Type", "text/json");
        return JSON.stringify(args);
      } else {
        return null;
      }
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
  });

  var callTestPut = LocalServiceRegistry.createService("test.http.put", {
    createRequest: function (svc: HTTPService, args) {
      svc.setRequestMethod("PUT");
      if (args) {
        svc.addHeader("Content-Type", "text/json");
        return JSON.stringify(args);
      } else {
        return null;
      }
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
  });

  var callTestDelete = LocalServiceRegistry.createService("test.http.delete", {
    createRequest: function (svc: HTTPService, args) {
      svc.setRequestMethod("DELETE");
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
  });

  var callTestBasicAuth = LocalServiceRegistry.createService("test.http.basicauth", {
    createRequest: function (svc: HTTPService, args) {
      svc.setRequestMethod("GET");
      svc.setAuthentication("BASIC");
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
  });

  var callTestTimeout = LocalServiceRegistry.createService("test.http.timeout", {
    createRequest: function (svc: HTTPService, args) {
      svc.setRequestMethod("GET");
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
  });

  var callTestStatus = LocalServiceRegistry.createService("test.http.status", {
    createRequest: function (svc: HTTPService, args) {
      svc.setRequestMethod("GET");
    },
    parseResponse: function (svc: HTTPService, client: HTTPClient) {
      return client.text;
    },
  });

  // Execute the request on the service configuration
  function makeCall(svcConfig: Service, params: Object) {
    if (counter == null) {
      counter = 1;
    }

    while (counter != 0) {
      if (mockCall) {
        result = service.setMock().call(params);
      } else if (pipelineError) {
        result = service.setThrowOnError().call(params);
      } else {
        result = service.call(params);
      }
      counter--;
    }

    // Set pdict out values
    args.svcConfig = svcConfig;
    args.httpResult = result;
  }

  switch (args.testType) {
    // make a GET request with additional headers, query param, json payload
    case "GET":
      service = callTestGet;
      service.URL += "/get";
      service.addHeader("testHeader", "testHeaderValue").addParam("filter", true);
      break;

    // make a POST request with additional headers, query param, json payload
    case "POST":
      service = callTestPost;
      service.URL += "/post";
      service.addHeader("testHeader", "testHeaderValue");
      break;

    // make a POST request with additional headers, query param, json payload, using UTF-16 encoding
    case "ENCODING":
      service = callTestPost;
      service.setEncoding("UTF-16");
      break;

    // make a PUT request with additional headers, query param concatenation, json payload
    case "PUT":
      service = callTestPut;
      service.URL += "/put";
      service.addParam("filter", true).addParam("secondParamAmpersand", 1);
      break;

    // make a DELETE request with additional headers, query param, json payload
    case "DELETE":
      service = callTestDelete;
      service.URL += "/delete";
      service.addHeader("testHeader", "testHeaderValue").addParam("filter", true);
      break;

    // make an HTTP post with BASIC auth.
    case "BASICAUTH":
      service = callTestBasicAuth;
      service.URL += "/basic-auth/user/passwd";
      break;

    // make a GET request with timeout
    case "TIMEOUT":
      service = callTestTimeout;
      service.URL += "/delay/6";
      break;

    // make a GET request and write result object to file
    case "OUTFILE":
      service = callTestGet;
      service.setOutFile(File("TEMP/" + Math.random()));
      break;

    // make a GET request in explicit mock mode
    case "MOCKED":
      service = ServiceRegistry.get("test.http.get");
      mockCall = true;
      break;

    // make a GET request and test ratelimit
    case "RATELIMIT":
      service = callTestGet;
      service.URL += "/get";
      break;

    // make a GET request with status code return as specified by returnCode pipeline query param
    case "STATUS":
      service = callTestStatus;
      if (returnCode == null) {
        trace("No return code provided");
      }
      service.URL += "/status/" + returnCode;
      break;

    // make a GET request and throwOnError() to have PIPELINE handle response error.
    case "THROWONERROR":
      service = callTestStatus;
      service.URL += "/status/" + 500;
      counter = 20;
      pipelineError = true;
      break;
  }

  // Make the service call here
  makeCall(service, requestBody);

  if (result == null || service == null) {
    return PIPELET_ERROR;
  }

  return PIPELET_NEXT;
}
```

### Error Handling

Information on the service status is stored in the Result object. Use methods on the object to determine the status of the service and any error it has returned:

- `result.error`- service-type specific error code, such as a 404 for an HTTP service.
- `result.errorMessage` - error message information, such as a Java exception

Call .setThrowOnError when calling the service to throw a JavaScript error if the result status isn’t OK:

```javascript
result = service.setThrowOnError().call();
```

## Configure Underlying Clients

The service framework wraps most functionality of the underlying clients (FTPClient, HTTPClient, and so on), but directly configure the underlying clients when needed.

For example, if you want to cache web service calls for `HTTPService`, in your `createRequest` callback, use the `getClient` method to get the underlying `HTTPClient` object for the service and use the `HTTPClient` class `enableCaching` method to cache the request.

```javascript
LocalServiceRegistry.createService("yourService", {
     createRequest: function(svc:HTTPService, args) {
          svc.client.enableCaching(1000);
[...]
```

**note:** Make sure to use getClient and any HTTPClient methods in the createRequest callback or later callbacks, not when you get the service object from the registry. This is because the underlying HTTPclient object isn't initialized until you call the service using the createRequest object. Before calling the service, using getClient only returns null.

Cached requests are seen in the dashboard and expect lower average execution times after caching is enabled. Cached requests are counted toward rate limits, circuit breakers, and statistics, because all of these are measured at the service level and are triggered by calls to the service.

## Web Service Logging and Troubleshooting

View generated logs by navigating in Business Manager to **Administration** > **Site Development** and clicking the link for Log files.

The log file name for a web service always starts with "service" then the log prefix set in the web service profile, and then internal information. For example: `service-logprefix-blade2-4-appserver-20150206.log.`

Salesforce B2C Commerce logs messages to a custom log category with the following format:

```javascript
service.serviceName.type;
```

Type is one of the following:

- head: After every service call, logs a message indicating success or failure.
- comm: After every service call, logs data passed to the underlying transport and the response received from it. Communication logs are disabled by default and log at the INFO level when enabled. This means in order to see communication logs, a service must have its **Communication Log Enabled** box checked and its log level must be `info` or `debug`.

  The comm logging is filtered to prevent sensitive data from being accidentally logged on a Production instance. The logging of filtered data is possible, but only by enabling communication logging for the service and also applying the `filterLogMessage` function or applying both the `getRequestLogMessage` and `getResponseLogMessage` functions in the service initialization. Importing a service definition with communication logging enabled on a Production system doesn't enable this logging and throws a warning message in the import log indicating that communication logging is disabled. This is a fail-safe to prevent accidentally enabling communication logging that could contain sensitive data. Force non-production instances to behave like production instances by checking the **Force PRD behavior in non-PRD environments** box.

- log: Logs miscellaneous messages, such as service initialization and error traces

For example, given three services: `mycartridge.http.payment.list`, `mycartridge.http.payment.get`, and `mycartridge.ftp.getPriceList`; this creates the following logging structure:

```txt
service
  .mycartridge
    .ftp
      .getPriceList
          .comm
          .head
          .log
   .http
      .payment
          .get
             .comm
             .head
             .log
          .list
             .comm
             .head
             .log
```

**note:** There are two custom log quotas, one for size (10MB) and one for log file prefixes (40). Although service logs are created and managed in the same place, unlike custom logs, service logs only count toward the size quota (10MB).

Setting Log Levels

Log levels are adjusted by clicking App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then selecting **Administration** > **Operations** > **Custom Log Settings**.

**note:** If a logging category doesn't have a log level specified, it inherits its parent log levels. This allows groups of services to have their logging enabled/disabled at the same time.

For example: for the services described above, you might create the following log categories.

Warn

| Enable | Log Category            |
| ------ | ----------------------- |
|        | services                |
| ✔      | mycartridge.service.ftp |

Info

| Enable | Log Category                          |
| ------ | ------------------------------------- |
|        | mycartridge.service.http.payment      |
| ✔      | mycartridge.service.http.payment.list |

Debug

| Enable | Log Category                              |
| ------ | ----------------------------------------- |
| ✔      | mycartridge.service.http.payment.get      |
| ✔      | mycartridge.service.http.payment.list.log |

In this example, everything in the `services.ftp` category is set to the `warning` log level, so `comm` logs aren't generated for the `mycartridge.ftp.getPriceList` service. The `mycartridge.http.payment.get` category generates `head`, `comm`, and `log` logs with `debug` level logging. The `mycartridge.http.payment.list` category generates `head` and `comm` logs with `info` level logging and a `log` log with `debug` level logging.

Filtering Logs

You might need to filter sensitive or private data, such as credit card numbers, so that it's not logged. Do this in two ways:

Example 1: Filter a System-Provided Log Message

This example shows how to add a `filterLogMessage` callback to the service definition to filter log messages.

```javascript
ServiceRegistry.configure("my.service", {
    createRequest: ...,
    parseResponse: ...,
    filterLogMessage: function(msg:String): {
        return msg.replace(/CreditCardNo\: \".*?\"/, "CreditCardNo:********");
    }
});
```

Example 2: let the `ServiceCallback` provide the log message

This example shows how to capture log messages using `getRequestLogMessage`, convert them to log messages and filter or edit them.

```javascript
ServiceRegistry.configure("my.service", {
    createRequest: ...,
    parseResponse: ...,
    getRequestLogMessage: function(reqObj:Object): {
        // Convert to a String here, doing any filtering...
        return msg;
    },
    getResponseLogMessage: function(respObj:Object): {
        // Convert to a String here, doing any filtering...
        return msg;
    }
});
```

Web service response Examples

This section shows typical responses from a simple web service.

Using HTTPResult as an example (all other result objects for other service types follow the same schema):

for a Simple HTTP GET Request That Was Successful, You Get Back:

```txt
[ error=0, errorMessage=null, mockResult=false, msg=OK, object=<html>…</html>, ok=true, status=OK ]
```

for a Simple HTTP GET Request That Failed, You Get Back:

```txt
[ error=500, errorMessage=null, mockResult=false, msg=INTERNAL SERVER ERROR, object=null, ok=false, status=ERROR ]
```

for a Simple HTTP GET Request That Failed Due to 404, You Get Back:

```txt
[ error=404, errorMessage=null, mockResult=false, msg=NOT FOUND, object=null, ok=false, status=ERROR ]
```

When You Hit the Rate Limit, You Get Back:

```txt
[ error=0, errorMessage=Rate Limit Exceeded, mockResult=false, msg=null, object=null, ok=false, status=SERVICE_UNAVAILABLE ]
```

When You Hit the Circuit Breaker, You Get Back:

```txt
[ error=0, errorMessage=Failure Count Exceeded, mockResult=false, msg=null, object=null, ok=false, status=SERVICE_UNAVAILABLE ]
```

When You Hit the Timeout, You Get Back:

```txt
[ error=0, errorMessage=SocketTimeoutException:Read timed out, mockResult=false, msg=Read timed out, object=null, ok=false, status=SERVICE_UNAVAILABLE ]
```

### Troubleshooting Web Services

In Business Manager:

- make sure the cartridge containing your script is in the cartridge path.
- make sure that the web service is enabled.
- make sure that the web service is set to **Live** and not **Mocked**.

In UX Studio:

- make sure that you point to your initialization script in your package.json.
- make sure that your initialization script has a service definition for your web service.
- make sure that your service definition includes callback objects that handle the web service response.
- make sure that you’ve written a script that calls your web service
- Make sure that your web service is available.

## SOAP Web Services

When you upload and run a pipeline with a script node that uses a `WebReference2` object to invoke a WSDL file, Salesforce B2C Commerce automatically generates classes from the WSDL using Apache CXF.

The generated classes are automatically added to the `webreferences2` package. You use the generated classes in a B2C Commerce script to call the web service operations and process any response from the web service.

B2C Commerce implements SOAP web services through the `dw.ws` package `port`, `webreference2`, and `WSUtil` classes.

Overview

The diagram below assumes you’re using the `dw.ws` package to implement web services.

![Web Services SOAP Overview](https://developer.salesforce.com/media/b2c-commerce/b2c_web_services_soap_ovr.gif)

For additional information about transport-layer and application-layer security, see [Web Service Security](#web-service-security.

B2C Commerce also maintains a legacy implementation that uses the `webreferences` object and Axis 1.4 to generate classes from the WSDL. This legacy implementation is deprecated and will be removed in a future release.

### Supported Standards

- SOAP 1.1 and SOAP 1.2
- WSDL 1.1
- Apache CXF
- JAX-WS for all features except WS-addressing, attachments, or asynchronous calls.
- https 1.0 and 1.1

**note:** RPC/Encoded WSDLs aren’t supported. However, we do support RPC/Literal, Document/Encoded, and Document/Literal WSDLs.

### SOAP Web Service Implementation

Integrating a web service into your storefront is relatively straightforward.

1.  Create the following files:

    - _filename_.wsdl - the WSDL file for the web service.
    - _filename_.jks - (optional) if you’re using a java keystore file for WS-Security, it must have the same filename as the WSDL.
    - _filename_.wsdl.properties - (optional) if you’re using the properties file to generate fully qualified class names to avoid class name collisions.See [Resolving Namespace Collisions for WSDLs and Associated Files](#resolving-namespace-collisions-for-wsdls-and-associated-files) below.

    **note:** The _filename_ for all three files must be identical for the files to be used by B2C Commerce. For example, if your WSDL file is named `HelloWorld.wsdl`, then the other files in the directory must be named `HelloWorld.wsdl.properties`, and `HelloWorld.jks`.

    **important:** If you have elements in a WSDL schema that contain the underscore character, Webreferences2 can encounter problems during code generation. To resolve this problem, use the `underscoreBinding=asCharInWord` property to control code generation. To direct Webreferences2 to use the `underscoreBinding` property, create a properties file in the same directory as the WSDL file. The name of the property file is `<wsdl_name>.wsdl.properties`, where `<wsdl_name>` is the name of the WSDL file. Also, if you’re migrating from Webreferences to Webreferences2, expedite the process by directing Webreferences2 to generate arrays instead of lists. To specify that you want to use arrays instead of lists, set `collectionType=indexed` in the `<wsdl_name>.wsdl.properties` properties file.

2.  Place the files in the `webreferences2` folder in your cartridge.
3.  Add a service definition to your service registry script for the SOAP web service.

    Example; Simple SOAP ServiceDefinition

    ```javascript
    {
        initServiceClient: function() {
            // Storing this in a custom attribute so it can be used later
            this.webReference = webreferences.LuhnChecker;
            // The return here is the 'svc.serviceClient' in subsequent calls
            return webReference.getDefaultService();
        }
        createRequest: function(svc:SOAPService, param1) {
            var requestObject = new this.webReference.CheckCC();
            requestObject.setCardNumber(param1);
            return requestObject;
        },
        execute: function(svc:SOAPService, requestObject) {
            return svc.serviceClient.checkCC(requestObject);
        },
        parseResponse: function(svc:SOAPService, responseObject) {
            var responseWrapper = {};
            responseWrapper.type = responseObject.checkCCResult.cardType;
            responseWrapper.valid = responseObject.checkCCResult.cardValid;
            return responseWrapper;
        }

    }
    ```

    The ServiceDefinition must specify:

    - initServiceClient callback to get the following using the `dw.ws` package:

      - a `WebReference2` object
      - a service port
      - WSDL request parameters
        This callback must also set the serviceClient property for the Service.

      The webreference2 object is required to generate the classes for the WSDL, which you must use to call the web service. When you’ve created this callback call the pipeline to generate the API for the WSDL. You might want to skip to the steps where you develop the pipeline and run it, so that you have the WSDL API available, and then finish the task of creating the service definition.

    - createRequest callback to set the WebReferences2 method to call and return a requestData object. If you’re using WS-Security, this is where you’ll construct the security hashmap.

      1.  To implement transport layer security for TLS/SSL, add the certificates that you want to use for your SSL connection, to your instance using Business Manager. See [Importing Certificates for an Instance.](#import-3rd-party-private-keys-and-certificates-for-an-instance)
      2.  To implement WS-Security to sign, encrypt or decrypt soap messages:

          - for a JAX-WS web service, using the `dw.ws` package:

            1.  Get a certificate from a known certificate authority or trusted provider to sign the SOAP message to the web service. You must provide a keystore for the WS-Security actions: Signature, Encryption, and Decryption.
            2.  Place the keystore in the webreferences2 folder in the same cartridge as the WSDL file.

                **note:** The name of the keystore must be the same as the WSDL file and the file extension must be that of the keystore type (jks or pkcs12). For example, for the `CheckFraud.WSDL` service and a pkcs12 keystore type, the keystore must be named `CheckFraud.pkcs12`.

            3.  In your B2C Commerce script that invokes the web service, create a request and response security configuration. The security configuration is a HashMap, whose first element defines the actions you want B2C Commerce to take: whether to add a timestamp, encrypt the message, sign the message, or other actions. The other elements in the HashMap assign a value to constants defined for the WSUtil class.

                Create a separate security configuration for the request messages and the response messages and pass them both into the `setWSSecurityConfig(port : Object, requestConfigMap : Map, responseConfigMap : Map)` class to set the request and response security configuration for the web service.

                Example:

                ```javascript
                // define a map with all the secrets
                var secretsMap: Map = new HashMap();
                secretsMap.put("myclientkey", "ckpass");
                secretsMap.put("myservicekey", "ckpass");
                secretsMap.put("username", "password");

                var requestCfg: Map = new HashMap();

                // define the ws actions to be performed - in this case add a username token, timestamp,
                // sign and encrypt the message
                requestCfg.put(
                  WSUtil.WS_ACTION,
                  WSUtil.WS_USERNAME_TOKEN +
                    " " +
                    WSUtil.WS_TIMESTAMP +
                    " " +
                    WSUtil.WS_SIGNATURE +
                    " " +
                    WSUtil.WS_ENCRYPT,
                );
                requestCfg.put(WSUtil.WS_USER, "username");
                requestCfg.put(WSUtil.WS_PASSWORD_TYPE, WSUtil.WS_PW_DIGEST);
                requestCfg.put(
                  WSUtil.WS_SIG_DIGEST_ALGO,
                  "http://www.w3.org/2001/04/xmlenc#sha256",
                );

                // define signature properties
                // the keystore file has the basename of the WSDL file and the
                // file extension based on the keystore type (for example, HelloWorld.jks).
                // The keystore file has to be placed beside the WSDL file.
                requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks");
                requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass");
                requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myclientkey");

                requestCfg.put(WSUtil.WS_SIGNATURE_USER, "myclientkey");

                // define enrcryption properties
                requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks");
                requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass");
                requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myservicekey");

                requestCfg.put(WSUtil.WS_ENCRYPTION_USER, "myservicekey");
                requestCfg.put(
                  WSUtil.WS_SIGNATURE_PARTS,
                  "{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body",
                );
                requestCfg.put(
                  WSUtil.WS_ENCRYPTION_PARTS,
                  "{Element}{" +
                    WSU_NS +
                    "}Timestamp;" +
                    "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body",
                );

                // set the secrets for the callback
                requestCfg.put(WSUtil.WS_SECRETS_MAP, secretsMap);

                var responseCfg: Map = new HashMap();

                // define the ws actions to be performed for the response
                responseCfg.put(
                  WSUtil.WS_ACTION,
                  WSUtil.WS_TIMESTAMP + " " + WSUtil.WS_SIGNATURE + " " + WSUtil.WS_ENCRYPT,
                );

                // define signature properties
                responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks");
                responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass");
                responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myservicekey");

                responseCfg.put(WSUtil.WS_SIGNATURE_USER, "myservicekey");

                // define decryption properties
                responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks");
                responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass");
                responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myclientkey");

                responseCfg.put(WSUtil.WS_ENCRYPTION_USER, "myclientkey");

                // set the secrets for the callback
                responseCfg.put(WSUtil.WS_SECRETS_MAP, secretsMap);

                // set the security
                WSUtil.setWSSecurityConfig(port, requestCfg, responseCfg);
                ```

          **note:** You must create a pipeline, even if you intend to call the script using a hook for an OCAPI application, because you must run the pipeline to generate the classes for the WSDL. To generate the classes, the script must get a `webreferences2` object. When the classes are generated, however, call the script using an OCAPI hook. However, if the WSDL changes and you need to generate a new version of the classes, you must run the pipeline again.

    - execute callback to make the service call.
    - parseResponse to parse the result of the call.

4.  Make sure your cartridge is in the path for your instance and upload the cartridge to the server. This initializes the Service objects, so that they can be accessed when calling web services.
5.  Develop a pipeline to call the web service and create a script node that points to your web service script.
6.  Execute the pipeline in the storefront, either by navigating to a page that calls the pipeline or by calling the pipeline manually, by adding the pipeline to the end of the URL after the site name. For example: `www.mycompany.com/default/Pipeline-Start`
7.  Download the classes generated by B2C Commerce in Studio. Click ![Site Development DW Server Connection](https://developer.salesforce.com/media/b2c-commerce/b2c_site_development_dw_server_connection.gif), select your server connection, select **CommerceCloudServer**, and then **Download Web Service API**.

    **note:** You must execute the pipeline at least once to generate the classes.

    **tip:** You must create a pipeline, even if you intend to call the script using a hook for an OCAPI application, because you must run the pipeline to generate the classes for the WSDL. To generate the classes, the script must get a `webreferences2` object. When the classes are generated, however, call the script using an OCAPI hook. However, if the WSDL changes and you must generate a new version of the classes, you must run the pipeline again.

    **important:** The `WebReference2` script class generation process uses the name of the WSDL service and port elements to create a class representing the service or port. If the service or port name contains an underscore character, the generated class name might or might not contain the underscore character based on naming rules used in the code generation process. Regardless of this, calling `WebReferences2.getDefaultService()` or `WebReferences2.getService(String, String)` resolves the service and port element names to the corresponding script classes.

8.  Create an ISML template to show the results of the web service and trigger a page refresh when the information shown changes.
9.  Troubleshoot any errors using the request log and customerror development logs. See [Troubleshooting Web Services](#troubleshooting-web-services) below.

See [Web Service Security](#web-service-security).

### Application-Layer Security with X509 Certificates for Signing and Encryption

Some web service integrations require the use of public key infrastructure (PKI) for application-layer web security, in which any messages exchanged are signed or encrypted to make sure that messages aren’t tampered with. A web service might also require secure communication with certificates issued by a known certificate authority, such as VeriSign, GoDaddy, or Comodo.

**note:** X509 certificates are used for application layer security. See [Web Service Security](#web-service-security).

### Resolving Namespace Collisions for WSDLs and Associated Files

When calling a web service from a script file, B2C Commerce generates `dw.ws.Port` and supporting classes, using the default namespace `webreferences2.<wsdl_file_name>.`

If your web service WSDL has many different types with the same name, compilation errors can occur because the type classes are all put in the same namespace package. To resolve this issue, specify that you want B2C Commerce to generate a namespace-aware `Port` and supporting classes, by including a properties file in the same location as your WSDL file. In this properties file, you specify the property as follows:

```javascript
namespace = true;
```

For B2C Commerce to apply namespace support, the properties file name must be specified as follows:

```txt
{wsdl_file_name}.wsdl.properties
```

For example, if your WSDL file is `HelloWorld.wsdl`, the properties file must be `HelloWorld.wsdl.properties` and it must be placed in the same `webreferences2` directory as the WSDL file itself.

In your B2C Commerce script file, create objects using the WebReference2 class. For example, if the namespace for `HelloWorld.wsdl` is `com.test.wsdl`, creating objects for the namespace requires the qualified name to be:

```javascript
var webRef : WebReferences2 = webreference2.HelloWorld;
var request : new webRef.com.test.wsdl.HelloRequest();
var svc = webRef.getDefaultService();
var response = webRef.hello(request);
```

### Troubleshooting Web Services

Q: I’m seeing an error that references security headers and is similar to the following: "com.ibm.wsspi.wssecurity.SoapSecurityException: WSEC....."

A: The request requires a username and password token in the request header. Make sure when constructing this request that the correct credentials (as provided by the web service being connected to) are included.

Q: I’m seeing an error that references 'FaultMessage', however, everything looks syntactically correct. What is wrong and how can I fix it?

A: When the Java files get created, the classes get created with an '`_Elemen`t' suffix. What this means is that, for a WSDL element '`fmt_FaultMessage`' there’s a class with the name '`Fmt_FaultMessage_Element.java`' created (when executing wsdl2java). The WSDL and code to reference this must be adjusted accordingly. To do this, modify the WSDL to change all references to (in this example) `'fmt_FaultMessage`':

```xml
<xsd:element name="fmt_FaultMessage"> <wsdl:message name="fmt_FaultMessage">
```

Q: I’m seeing an error that the WSDL file can't be located in B2C Commerce, but I know the WSDL file is there. The error is: "Script exception in line 42:org.mozilla.javascript.EcmaError: ReferenceError: WSDL file for webreference2 'example' doesn't exist"

A: Make sure that the WSDL file is in the cartridge’s `webreferences2` directory and make sure the pipeline executing/calling it's in the same cartridge (as the WSDL file).

Q: I’m having difficulty invoking a method that expects an ArrayOf type as a parameter, how can I get this to work?

A: To pass an ArrayOf structure as a parameter to a method in a script, one should follow the following conventions (keeping in mind that the exact implementation will vary depending on your specific code/logic): `request.setSomething([theArray]);` or `request.setSomething(new Array(x))`;

Q: I’m trying to integrate with a web service that has fields defined as dateTime and keep getting this error: "Cannot convert [Calendar id=19206138] to java.util.Calendar "

A: All variables have setter methods. Salesforce recommends always using setter methods. However, when using primitive types it isn't necessary, but by using setters you’ll always be safe.

Q: I’m running into some data-type specific issues. Which data-types aren’t supported?

A: The following data types aren’t supported:

- NonNegativeInteger
- NonPositiveInteger
- UnsignedByte
- UnsignedInt
- UnsignedLong
- UnsignedShort

### Tip for Accessing Elements with the Name 'Return'

The following is an example element in a WSDL document with a name of 'return':

```xml
<element name="NewOrderResponse">
   <complexType>
    <sequence>
    <element name="return" type="ns:NewOrderResponseElement" minOccurs="1" maxOccurs="1"/>
    </sequence>
   </complexType>
  </element>
```

In order to access an element with the name "return" you must add an underscore to the name.

```javascript
response = service.newOrder(newOrder);
var result = response.['_return'];
```

With an underscore in the name, access all the properties in the object.

Errors shown for names without an Underscore

Attempting to access the element causes some of the following errors:

Example 1:

Using the following pseudocode:

```javascript
response = service.newOrder(newOrder);
var result = response.return;
```

Throws the following error:

```txt
org.mozilla.javascript.EvaluatorException: missing
```

Example 2:

Using the following pseudocode:

```javascript
response = service.newOrder(newOrder);
```

Throws the following error:

```txt
var result = response.['return']; Unknown dynamic property 'return' for class
```

## Web Service Timeouts

Carefully consider a timeout strategy for web services.

### SOAP Timeout for Unresponsive Server

SOAP web service connections use a default timeout of 2 minutes for storefront and B2C Commerce tool requests and 15 minutes for jobs. If the timeout of the calling script is less than the default, the script timeout is used.

**tip:** To maintain the site's responsiveness, configure a low timeout.

### HTTPClient Timeout

The HTTPClient method times out at 2 minutes for storefront and B2C Commerce tool requests and 15 minutes for jobs.

### Setting Timeouts

Salesforce recommends setting timeout values through the service configuration.

The timeout you configure for a service is a connection and read timeout. If each connection/read duration is just below the timeout duration, the total time of the service might be much larger than the timeout value. For example, with a 5-second timeout, with a 4-second connection attempt, followed by reading two chunks of data at 4 seconds apiece, takes about 13 seconds total, but doesn’t time out.

If you don't configure the service timeout values, it’s also possible to set port timeouts using API methods, but these aren’t monitored or included in the service status analytics. However, information about these timeouts is included below.

Port Timeouts for SOAP Web Services

Use the `dw.ws.WSUtil` class to set read/connection timeouts for ports using the `setConnectionTimeout(timeout, port)` and `setRequestTimeout(timeout, port)` methods. The minimum connection timeout is 100 miliseconds and the maximum connection timeout is 15 seconds.

## Web Service Security

There are two layers of security to configure for communication between your storefront and an external web service. At the transport layer, use certificates to identify both parties of a transaction. At the application layer, encrypt or sign content with another certificate (X.509) to make sure the contents of your messages aren’t tampered with.

### Overview of Web Service Security

The diagram below shows the communication between a Salesforce B2C Commerce pipeline and an external web service. This diagram assumes you’ve created a custom pipeline and script that invokes the web service, creates SOAP messages, and signs or encrypts messages to the web service.

When the web service is invoked via https, B2C Commerce automatically uses the client private key that you’ve stored in your instance via Business Manager, using the host name you’ve provided for that key. Import SSL certificates used for two-factor authentication into your instance. TLS certificates are used for communication with web services or when using `dw.net.HTTPClient`.

When the transport layer negotiation and handshake is complete, your B2C Commerce script sends a SOAP message to the web service.

If you use the B2C Commerce implementation of web services in the `dw.ws` package, then If the web service requires encryption or signing of SOAP messages, B2C Commerce uses the X509 certificate stored in the instance keystore with the WSDL in the `webreference2` folder in your cartridge. In this case, the private key is usually stored in Business Manager in a site preference or custom attribute and referenced in the script, so that the private key doesn't have to be included directly in the script.

Use `dw.net.httpClient` and the crypto package to send and sign a SOAP message if you can't use the B2C Commerce implementation of web services. This isn't recommended and only necessary if you require that the web service X509 certificate be stored in the Business Manager instance.

![Web Service Security Architecture](https://developer.salesforce.com/media/b2c-commerce/b2c_web_service_arch_144.gif)

### Storing Certificates

For both transport layer and application layer security, you import private keys with certificates and trusted certificates into the Business Manager instance keystore. However, if you want to use WS-Security to automatically encrypt and decrypt SOAP messages, these certificates must be stored in a cartridge.

When you import certificates into Business Manager, you also configure additional information required when using the keys and certificates, such as an alias or a service provider host name. The alias indicates to B2C Commerce which URL requires the certificate.

Private keys and certificates are stored per instance and can be used across all sites and organizations for that instance. Store up to 50 entries (keys or certificates) in an instance key store. Each entry in the key store must have an alias. Once given, the alias can't be changed. To change an alias, you must remove the certificate from the keystore and reimport it.

Each private key can be associated with up to five host names. Import the following private key file types: .pfx or .p12. Import the following types of trusted certificates: .pem,.cer,.crt,.der. Import X509 certificates as well.

Once imported, the certificates are used whenever communicating with a web service via https.

**note:** In releases before B2C Commerce 14.4, X509 certificates were stored with the .wsdl file in a keystore in the `webreferences2` folder. It's still possible to do this, but it isn't recommended if you’re only signing messages and not encrypting them, as storing certificates in the instance keystore is more secure and makes managing certificate expiration easier.

Script methods used for X509 certificates stored in the instance Keystore

To perform signing and verification using certificates stored in the instance keystore, you must use methods that include KeyRef parameter in the method signature.

These methods include:

- `dw.crypto.Signature.sign(String message, KeyRef privateKey, String digestAlgorithm)`
- `dw.crypto.Signature.signBytes(Bytes contentToSign, KeyRef privateKey, String digestAlgorithm)`
- `dw.crypto.Signature.verifySignature(String signature, String contentToVerify, CertificateRef certificate, String digestAlgorithm)`
- `dw.crypto.Signature.verifyBytesSignature(Bytes signature, Bytes contentToVerify, CertificateRef certificate, String digestAlgorithm)`
- `dw.crypto.KeyRef(String aliasS)`
- `dw.crypto.KeyRef(String alias, String password)`

Use the helper classes `KeyRef` and `CertificateRef` as references to keys in the keystore. They have a constructor that takes a string that is an alias of a private key `KeyRef` or a trusted certificate `CertificateRef` in the keystore.

### Web Service Security FAQ

Q: What features of WS-Security does B2C Commerce Support?

A: B2C Commerce supports all features of the WS-Security standard except addressing.

Q: What is a key store?

A: A keystore is a storage facility for cryptographic keys and certificates.

Q: What's the difference between a certificate and a trusted certificate?

A: A certificate is private; a trusted certificate is a public certificate issued from a trusted certificate authority.

Q: Why can't I see the Certificates & Private Keys module?

A: You must have the required permissions to see the module. Request permission for the Private Keys and Certificates Business Manager module from your administrator.

Q: How many TLS certificates can be imported per instance?

A: Up to 50 key entries can be stored per B2C Commerce instance and organization. Key entries include both private keys and certificates.

Q: How many host names can a private key be associated with in Business Manager?

A: Each private key can be associated with up to five host names.

Q: If there’s an error when using a certificate, where is it logged?

A: See the error log files under **Administration > Site Development > Development Setup > Log Files.**

Q: How do I scrub logs for sensitive information?

A: When URLs written to the service framework's communication logs must be scrubbed for sensitive information, use `dw.svc.ServiceCallback.filterLogMessage` to filter the request URL as well as the request and response bodies.

Q: Can I use the signatureKeyIdentifier and encryptionKeyIdentifier?

A: Use the WebReferences2 integration with WS-Security to set values for `signatureKeyIdentifier` and `encryptionKeyIdentifier` using constants in the `WSUtil` class. For `signatureKeyIdentifier`, use the `WS_SIG_KEY_ID` and for `encryptionKeyIdentifer`, use `WS_ENC_KEY_ID`. s

**note:** See the B2C Commerce Script API documentation for the default values that `WebReferences2` uses and its permissible options.

Q: Is sensitive data included in web service logging?

A: By default, logging data is filtered to prevent logging sensitive data. It's possible to enable logging this information if it's required. For more information, see the description of the comm logging level in .

### Import 3rd Party Private Keys and Certificates for an Instance

Import Private Keys and SSL certificates used for two-factor authentication into your instance. TLS certificates are used for communication with web services, file transfer over WebDAVClient, or when using `dw.net.HTTPClient` or any of the `dw.crypto` classes that use a `KeyRef` parameter. Salesforce B2C Commerce uses an internal password to encrypt the instance keystore.

If you have an integration to an external server using SSL/TLS, the SSL certificate that is installed at the external server must be issued by a Certification Authority (CA) that the Commerce Cloud B2C Commerce server recognizes. By default, B2C Commerce only recognizes the root CAs that ship with Oracle's Java platform. However, add additional CA certificates to the customer keystore. Certificates added to the customer keystore are checked when establishing outbound SSL connections.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration > Operations > Private Keys and Certificates.**

2.  On the Private Keys and Certificates page, search for a certificate or navigate the grid to select one.

    Select 10, 25, 50, or 100 items to appear on a page.

3.  If you’re importing a new version of an existing certificate and want to use the same alias, you must first delete the existing certificate. Aliases for all entries in the keystore must be unique.

4.  To add a new certificate or private key:

    1.  Click **Import** or **Upload a new private key or certificate.**

    2.  In the Import Private Key or Certificate window, enter a certificate or private key file name, or click **Select...** to open the File Upload window and select the file to import.

        Only the following file types are allowed:

        - Trusted certificates: .crt .pem .cer .der
        - Private Keys: .p12 .pfx
          The certificate appears in the certificate list..

5.  To add host names to a private key:

    1.  Click the dropdown to the right of a private key and select **Manage Hosts**.

    2.  On the Manage Hostnames window, enter the host name, for example, `www.sitegenesis.com`, and click **Add**.

    3.  Click **Save**.

6.  If you’re importing:

    - A trusted certificate (\*.crt): enter the following:
      - Alias: Enter an alias used to refer to this certificate when using any of the B2C Commerce script dw.crypto package methods that specify an alias. The alias shouldn't contain spaces or special characters in B2C Commerce script. B2C Commerce tells you if the alias isn't unique for your instance.
    - A private key (\*.p12): enter the following: - Source Password: Enter a password for the encryption of the key file entered in the instance keystore. - Alias: enter an alias used to refer to this certificate when using any of the B2C Commerce script dw.crypto package methods that specify an alias. The alias shouldn't contain spaces or special characters in B2C Commerce script. B2C Commerce tells you if the alias isn't unique for your instance. You might want to include the web service name or service provider name. - Host Names: If you’re using the key or certificate for transport layer security, enter the DNS name of the server as reached by the client. For example: `www.paypal.com`. B2C Commerce uses the host name to determine what certificate to use. Add two or more host names, separated by a comma. If you importing a certificate specified by one of the `dw.crypto` package methods that takes a KeyRef argument, you don't need to enter a hostname.
      If you’ve more than one private key in your keystore, the host name must be set to the target host you call via `dw.net.HTTPClient` or `dw.ws.WSUtil`. Otherwise, B2C Commerce can't guarantee that the correct client certificate is used for the call.

### Delete Certificates

If you’re importing a new version of an existing certificate and want to use the same alias, you must first delete the existing certificate. Aliases for all entries in the keystore must be unique.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Private Keys and Certificates**.

2.  On the Private Keys and Certificates page, search for a certificate or navigate the grid to select one.

3.  To delete a certificate (alias or host name):

    - Click the **Alias** checkbox (to select all certificates) or select one or more certificates, and then click **Delete**.
    - Click the ![Action Select Icon](https://developer.salesforce.com/media/b2c-commerce/b2c_action_select_icon.gif) icon to the right of a certificate and select **Delete**.

### Monitor Credit Card Encryption Keys

Credit card encryption is performed internally on Salesforce B2C Commerce for secure storage of sensitive information. Credit card encryption, key generation, and re-encryption is automatic. B2C Commerce provides the ability to monitor this process for customers undergoing a PCI audit, to demonstrate the current state of the system.

1.  Click App Launcher ![App Launcher](https://developer.salesforce.com/media/shared/appLauncher.png) and then select **Administration** > **Operations** > **Payment Instrument Encryption Keys**.

    The Payment Instrument Encryption Keys Page appears.

2.  Use the page to determine the status of your encryption keys and the dates when keys are generated.

### Using X509 Certificates for Signing or Encryption

Some web service integrations require the use of public key infrastructure for application layer web security, in which any messages exchanged are signed or encrypted to ensure that messages aren’t tampered with. A web service might also require secure communication with certificates issued by a known certificate authority, such as VeriSign, GoDaddy, or Comodo.

#### Overview

**note:** X509 certificates are used for application layer security.

![Web Service Overview](https://developer.salesforce.com/media/b2c-commerce/b2c_web_service_ovr_144.gif)

1.  Get a certificate from a known certificate authority or trusted provider to sign the SOAP message to the web service. You must provide a keystore for the WS-Security actions: Signature, Encryption, and Decryption.
2.  Place the keystore in the webreferences2 folder in the same cartridge as the WSDL file.

    **note:** The name of the keystore must be the same as the WSDL file and the file extension must be that of the keystore type (jks or pkcs12). For example, for the `CheckFraud.WSDL` service and a pkcs12 keystore type, the keystore must be named `CheckFraud.pkcs12`.

3.  If you have additional certificates that you want to use for your SSL connection, add them to your instance using Business Manager.
4.  Create a DWScript that does the following:
    - Defines a HashMap and put your client key and password, service key and password, username and password into it.
    - Defines a HashMap for the web service request that configures the actions of the interceptor actions when a call is made to the web service. Interceptor actions are defined through methods in the WSUtil class.
    - Defines a HashMap for the web service response that configures the interceptor actions for the return message.
    - Uses the `WSUtil.setWSSecurityConfig` method to configure the actions on the server
    - Calls the web service
5.  Create a custom pipeline with a script node and assign it to the script you’ve created.

#### WS-Security Web Service Template

This template shows how to use the WSUtil class to configure WS-Security for a Web service call in Salesforce B2C Commerce. However, it doesn't use sample data and can't be used to run an actual web service.

```javascript
importPackage(dw.system);
importPackage(dw.util);
importPackage(dw.ws);

var WSU_NS: String =
  "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";

function execute(args: PipelineDictionary): Number {
  // define a map with all the secrets
  var secretsMap: Map = new HashMap();
  secretsMap.put("myclientkey", "ckpass");
  secretsMap.put("myservicekey", "ckpass");
  secretsMap.put("username", "password");

  var requestCfg: Map = new HashMap();

  // define the ws actions to be performed
  requestCfg.put(
    WSUtil.WS_ACTION,
    WSUtil.WS_USERNAME_TOKEN +
      " " +
      WSUtil.WS_TIMESTAMP +
      " " +
      WSUtil.WS_SIGNATURE +
      " " +
      WSUtil.WS_ENCRYPT,
  );

  requestCfg.put(WSUtil.WS_USER, "username");
  requestCfg.put(WSUtil.WS_PASSWORD_TYPE, WSUtil.WS_PW_DIGEST);
  requestCfg.put(WSUtil.WS_SIG_DIGEST_ALGO, "http://www.w3.org/2001/04/xmlenc#sha256");

  // define signature properties
  // the keystore file has the basename of the WSDL file and the
  // file extension based on the keystore type (for example, HelloWorld.jks).
  // The keystore file has to be placed beside the WSDL file.
  requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks");
  requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass");
  requestCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myclientkey");

  requestCfg.put(WSUtil.WS_SIGNATURE_USER, "myclientkey");

  // define enrcryption properties
  requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks");
  requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass");
  requestCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myservicekey");

  requestCfg.put(WSUtil.WS_ENCRYPTION_USER, "myservicekey");
  requestCfg.put(
    WSUtil.WS_SIGNATURE_PARTS,
    "{Element}{http://schemas.xmlsoap.org/soap/envelope/}Body",
  );
  requestCfg.put(
    WSUtil.WS_ENCRYPTION_PARTS,
    "{Element}{" +
      WSU_NS +
      "}Timestamp;" +
      "{Content}{http://schemas.xmlsoap.org/soap/envelope/}Body",
  );

  // set the secrets for the callback
  requestCfg.put(WSUtil.WS_SECRETS_MAP, secretsMap);

  var responseCfg: Map = new HashMap();

  // define the ws actions to be performed for the response
  responseCfg.put(
    WSUtil.WS_ACTION,
    WSUtil.WS_TIMESTAMP + " " + WSUtil.WS_SIGNATURE + " " + WSUtil.WS_ENCRYPT,
  );

  // define signature properties
  responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_TYPE, "jks");
  responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_PW, "cspass");
  responseCfg.put(WSUtil.WS_SIG_PROP_KEYSTORE_ALIAS, "myservicekey");

  responseCfg.put(WSUtil.WS_SIGNATURE_USER, "myservicekey");

  // define decryption properties
  responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_TYPE, "jks");
  responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_PW, "cspass");
  responseCfg.put(WSUtil.WS_ENC_PROP_KEYSTORE_ALIAS, "myclientkey");

  responseCfg.put(WSUtil.WS_ENCRYPTION_USER, "myclientkey");

  // set the secrets for the callback
  responseCfg.put(WSUtil.WS_SECRETS_MAP, secretsMap);

  // do the service call
  var helloWorldService: WebReference = webreferences.HelloWorld;
  var helloSvc: Stub = helloWorldService.getDefaultService();

  WSUtil.setWSSecurityConfig(helloSvc, requestCfg, responseCfg);

  var parameter = new helloWorldService.Hello2();
  parameter.setName("Send Text from client...");
  var result = helloSvc.hello2(parameter);
  var responseMessage = result.getHello2Return();

  return PIPELET_NEXT;
}
```

## Working with JSON

JSON (JavaScript Object Notation) is a human-readable, easily parsed or generated data-interchange language. Salesforce B2C Commerce provides a top level JSON class to make it simple to exchange objects between your server and client. The B2C Commerce implementation is based on the json2.js implementation of JSON.

JSON objects are useful if you need to create a complex data object of an arbitrary depth. For example, if you have a main product with options that depend on other attributes, such as gender or size.

B2C Commerce provides a parsing method that can be used to construct a JSON object from a JSON string used as the value of an attribute field. Salesforce recommends using the `parse(json : String, reviver : Function) : Object` function for this purpose.

The object can then be further manipulated using any ECMA-compliant language, such as JavaScript . The object can then be stringified using the B2C Commerce-provided stringify() method and transferred to the client.

Alternatively, a JSON string used as the value of an attribute field can be transferred to the client and then parsed to create an object for further operations.

JSON objects aren't recommended for general use, because JSON doesn't handle recursive structures effectively.

## Working with XML Data

Salesforce B2C Commerce JavaScript supports the E4X extension to JavaScript. This extension supports the direct manipulation of XML data in scripts, without the need to work DOM or other intermediate structures. It's standardized as ECMA-357.

- To create an XML object

1.  Specify the following:

    ```javascript
    var order = <order><item
    quantity="1">Apple<item><item
    quantity="2">Peach<item></order>
    ```

2.  Read values from an XML object the same way you would from standard ECMAScript objects:

    ```javascript
    var items = order.item;
    var item = order.item(0);
    var quantity = order.item(0).@quantity;
    var singleItem = order.item.(@quantity == 1);
    ```

3.  Directly modify an XML object:

    ```javascript
    var order.item += <item
    quantity="1">Orange</item>;var order.item += <item
    quantity={quantity}>Orange</item>;delete order.item(2);
    ```

### Calling an Amazon.Com Service

As a working example, the next section shows how to call an amazon.com service. Amazon supports Web Service-based calls and REST calls. REST services can be called as a straight URL and return XML. The XML language support is used here to parse the returned result from the amazon.com service.

See the _Amazon Simple Services Developer's Guide_ \(www.docs.amazonwebservices.com\) for more information on developing Web Services for Amazon.

- To call the amazon.com REST service, specify

```javascript
// send the http request to amazon.com
var httpSvc = new HTTPClient();
httpSvc.open(
  "GET",
  "http://webservices.amazon.com/onca/xml?Service=AWSECommerceService&" +
    "SubscriptionId=YOUR-SUBSCRIPTION-ID&Operation=ItemSearch&" +
    "SearchIndex=Video&Keywords=potter%20harry&ResponseGroup=Images",
  false,
  null,
  null,
);
httpSvc.send();
// convert the result into an XML object
var result = new XML(httpSvc.text);
// print some values from the result.
// We must use namespace qualified access to read the values.
var ns = new Namespace("http://webservices.amazon.com/AWSECommerceService/2005-07-26");
trace(result.ns::Items.ns::Request.ns::IsValid);
trace(result.ns::Items.ns::TotalResults);
trace(result.ns::Items.ns::Item[0].ns::ASIN);
```

If you don't see a result, validate that amazon.com still returns data with the same XML format, particularly with the same namespace identifier.
