---
schema_version: "1"
id: 9104ef70098bfec7d016cc44096ef2e8735681d5cdc406851ecf5817e7479b2f
title: Configure Salesforce Payments for Your B2C Commerce Composable Storefronts
collection: developer
version: current
locale: en-us
portal_url: https://docs.salesforce.com/developer/current/en-us/commerce-cloud/pwa-kit-managed-runtime/guides/salesforce-payments.md
canonical_url: https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/salesforce-payments.html
source_site: developer.salesforce.com
content_type: documentation
content_hash: e66689b9b22b1ffad63a607d5dcd6ebef4471adcb068d3a6698616450327c084
products:
  - Composable Storefront
  - Salesforce APIs
---
# 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](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/upgrade-to-v3.html).
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](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/configure-mrt-routing-bm.html).

   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.

   ```
   // Salesforce Payments configuration
   // To enable Salesforce Payments in a Composable storefront, set Salesforce Payments to true (Salesforce Payments must also be enabled in the B2C Commerce instance).
   // To disable Salesforce Payments in a Composable storefront, set Salesforce Payments to false(Salesforce Payments in the B2C Commerce instance is unaffected).
   // 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.
   //
   //   sdkUrl:      https://[bm_or_vanity_host]/on/demandware.static/Sites-Site/-/-/internal/jscript/sfp/v1/sfp.js
   //   metadataUrl: https://[bm_or_vanity_host]/on/demandware.static/Sites-Site/-/-/internal/metadata/v1.json

   sfPayments: {
     enabled: false,
     sdkUrl: '',
     metadataUrl: ''
   }
   ```

4. To ship to countries outside of the U.S. and Canada, update the constants.js file.

   ```
    // add the local and labels to this array.

    export const SHIPPING_COUNTRY_CODES = [
    {value: 'DE', label: 'Germany'},
    {value: 'GB', label: 'United Kingdom'},
    ]
   ```

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.

   ```
   "start:https": "cross-env NODE_TLS_REJECT_UNAUTHORIZED=0
   NODE_ICU_DATA=node_modules/full-icu DEV_SERVER_PROTOCOL=https
   DEV_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](https://developer.salesforce.com/docs/commerce/pwa-kit-managed-runtime/guide/use-a-slas-private-client.html#configure-the-pwa-kit).
   - 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.

   ```
   'img-src': [
       '.commercecloud.salesforce.com',
       '.demandware.net',
       '*.adyen.com',
       'pay.google.com',
       'www.gstatic.com‘
   ]
   'script-src': [
       'storage.googleapis.com',
       'maps.googleapis.com',
       'places.googleapis.com',
       '*.stripe.com',
       '*.paypal.com',
       '*.adyen.com',
       'pay.google.com',
       'www.gstatic.com',
       '*.demandware.net'
   ]
   'connect-src': [
       'api.cquotient.com',
       '*.c360a.salesforce.com',
       'maps.googleapis.com',
       'places.googleapis.com',
       '*.salesforce-scrt.com',
       '*.demandware.net',
       '*.adyen.com',
       '*.paypal.com',
       'pay.google.com',
       'payments.google.com',
       'google.com/pay',
       'google.com/pay/',
       'www.google.com/pay',
       'www.google.com/pay/'
   ]
   'frame-src': [
       '*.site.com',
       '*.stripe.com',
       '*.paypal.com',
       '*.adyen.com',
       'payments.google.com',
       'pay.google.com'
   ]
   ```

   b. To read metadata from the B2C Commerce instance, add this code to the top of the import before `app.get('*', runtime.render)`.

   ```
   function transformIconPaths(data, ecomServerHost) {
   const baseUrl = `https://${ecomServerHost}/on/demandware.static/Sites-Site/-/-/internal`
   const methodTypes = data?.paymentMethodTypes

   if (methodTypes) {
       for (const method of Object.values(methodTypes)) {
           for (const image of method.images ?? []) {
               if (image.src?.startsWith('/icons/')) {
                   image.src = `${baseUrl}${image.src}`
               }
           }
       }
   }
       return data
   }
   app.get('/api/payment-metadata', async (req, res) => {
   try {
       const response = await fetch(config.app.sfPayments.metadataUrl, {
           headers: {Accept: 'application/json'}
       })
       if (!response.ok) {
           throw new Error(`Metadata request failed with status: ${response.status}`)
       }
       const data = await response.json()
       const transformedData = transformIconPaths(
           data,
           new URL(config.app.sfPayments.metadataUrl).hostname
       )
       res.setHeader('Content-Type', 'application/json')
       res.json(transformedData)
   } catch (error) {
       res.status(500).json({
           error: 'Failed to fetch metadata',
           details: error.message
           })
       }
   })
   ```
