Configure Salesforce Payments for Your B2C Commerce Composable Storefronts

Salesforce Payments streamlines the checkout experience for shoppers in your B2C Commerce Composable Storefronts. Salesforce Payments is a native solution that supports multiple payment methods and payment gateways such as Adyen, Stripe, and PayPal.

Set up Salesforce Payments for B2C Commerce Composable Storefronts.

  1. If your PWA Kit isn’t version 3.17 or later, Upgrade to v3.

  2. If you configured a vanity domain within an eCDN zone, redirect the PWA Kit endpoint (/api/metadata) to a B2C Commerce instance static asset that’s respected by an eCDN.

    See Configure MRT Routing Rules in Business Manager.

    a. In Business Manager, click App Launcher, and then select Administration | Sites | Embedded CDN Settings.

    b. Locate the zone that you want to view.

    c. Click on the caret dropdown to the right of the zone.

    d. Select Configure Routing Rules.

    e. Locate the rule that you want to edit.

    f. Click Edit.

    g. To allow the CDN to route the vanity domain to specific instances, in the Rule Expression section, add /api* to the routes.

  3. In your default.js file, update the Salesforce Payments configuration.

    a. Set Salesforce Payments to true.

    b. Set the sdkUrl and metadataUrl values to point to your B2C Commerce instance host by replacing the [bm_or_vanity_host] placeholder with your Business Manager or vanity URL host.

    1// Salesforce Payments configuration
    2// To enable Salesforce Payments in a Composable storefront, set Salesforce Payments to true (Salesforce Payments must also be enabled in the B2C Commerce instance).
    3// To disable Salesforce Payments in a Composable storefront, set Salesforce Payments to false(Salesforce Payments in the B2C Commerce instance is unaffected).
    4// Set the sdkUrl and metadataUrl values to point to your B2C Commerce instance host by replacing the [bm_or_vanity_host] placeholder with your Business Manager or vanity URL host name.
    5//
    6//   sdkUrl:      https://[bm_or_vanity_host]/on/demandware.static/Sites-Site/-/-/internal/jscript/sfp/v1/sfp.js
    7//   metadataUrl: https://[bm_or_vanity_host]/on/demandware.static/Sites-Site/-/-/internal/metadata/v1.json
    8
    9sfPayments: {
    10  enabled: false,
    11  sdkUrl: '',
    12  metadataUrl: ''
    13}
  4. To ship to countries outside of the U.S. and Canada, update the constants.js file.

    1// add the local and labels to this array.
    2
    3 export const SHIPPING_COUNTRY_CODES = [
    4 {value: 'DE', label: 'Germany'},
    5 {value: 'GB', label: 'United Kingdom'},
    6 ]
  5. To test Salesforce Payments for Composable Storefronts in a development instance before setting it up in your Managed Runtime (MRT) project, set up the development instance.

    When you run a Composable storefront in a development instance, only HTTP is supported. Salesforce Payments requires HTTPs.

    a. Create a self-signed certificate (for example, localhost.pem) and place it in the project root.

    b. In the package.json file, add a development script.

    1"start:https": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0
    2NODE_ICU_DATA=node_modules/full-icu DEV_SERVER_PROTOCOL=https
    3DEV_SERVER_SSL_FILE_PATH=localhost.pem pwa-kit-dev start"

    c. To start the Composable Storefront in a developer instance using HTTPS, use npm run start:https to run the application.

    This setup is required for some payment methods.

  6. Set up Apple Pay methods.

    a. For Apple Pay to work with Stripe, register MRT domains on the Stripe Dashboard.

    b. For Apple Pay to work with Adyen, register MRT domains.

  7. To set up Salesforce Payments for new Composable Storefronts, complete these steps.

    New storefronts have the latest Content Security Policy (CSP) configuration.

    a. To configure Salesforce Payments with a SLAS private client, in your SSR.js file, set these values.

    You can configure Salesforce Payments with a public or a private client. By default, Salesforce Payments is configured with a public client. For more information, see Use a SLAS Private Client.

    • In packages/template-retail-react-app/app/components/_app-config/index.jsx, set enablePWAKitPrivateClient={true}.

    • In packages/template-retail-react-app/ssr.js:

      • Set useSLASPrivateClient:true.
      • Uncomment applySLASPrivateClientToEndpoints.
  8. To set up Salesforce Payments for existing Composable Storefronts, complete these steps.

    a. In your ssr.js file, update the content security policy with these directories.

    Google Pay may require additional endpoints depending on the integration. You can adjust the values as needed for your instance.

    1'img-src': [
    2    '.commercecloud.salesforce.com',
    3    '.demandware.net',
    4    '*.adyen.com',
    5    'pay.google.com',
    6    'www.gstatic.com‘
    7]
    8'script-src': [
    9    'storage.googleapis.com',
    10    'maps.googleapis.com',
    11    'places.googleapis.com',
    12    '*.stripe.com',
    13    '*.paypal.com',
    14    '*.adyen.com',
    15    'pay.google.com',
    16    'www.gstatic.com',
    17    '*.demandware.net'
    18]
    19'connect-src': [
    20    'api.cquotient.com',
    21    '*.c360a.salesforce.com',
    22    'maps.googleapis.com',
    23    'places.googleapis.com',
    24    '*.salesforce-scrt.com',
    25    '*.demandware.net',
    26    '*.adyen.com',
    27    '*.paypal.com',
    28    'pay.google.com',
    29    'payments.google.com',
    30    'google.com/pay',
    31    'google.com/pay/',
    32    'www.google.com/pay',
    33    'www.google.com/pay/'
    34]
    35'frame-src': [
    36    '*.site.com',
    37    '*.stripe.com',
    38    '*.paypal.com',
    39    '*.adyen.com',
    40    'payments.google.com',
    41    'pay.google.com'
    42]

    b. To read metadata from the B2C Commerce instance, add this code to the top of the import before app.get('*', runtime.render).

    1function transformIconPaths(data, ecomServerHost) {
    2const baseUrl = `https://${ecomServerHost}/on/demandware.static/Sites-Site/-/-/internal`
    3const methodTypes = data?.paymentMethodTypes
    4
    5if (methodTypes) {
    6    for (const method of Object.values(methodTypes)) {
    7        for (const image of method.images ?? []) {
    8            if (image.src?.startsWith('/icons/')) {
    9                image.src = `${baseUrl}${image.src}`
    10            }
    11        }
    12    }
    13}
    14    return data
    15}
    16app.get('/api/payment-metadata', async (req, res) => {
    17try {
    18    const response = await fetch(config.app.sfPayments.metadataUrl, {
    19        headers: {Accept: 'application/json'}
    20    })
    21    if (!response.ok) {
    22        throw new Error(`Metadata request failed with status: ${response.status}`)
    23    }
    24    const data = await response.json()
    25    const transformedData = transformIconPaths(
    26        data,
    27        new URL(config.app.sfPayments.metadataUrl).hostname
    28    )
    29    res.setHeader('Content-Type', 'application/json')
    30    res.json(transformedData)
    31} catch (error) {
    32    res.status(500).json({
    33        error: 'Failed to fetch metadata',
    34        details: error.message
    35        })
    36    }
    37})