Note: If your e-commerce platform is not Salesforce Commerce Cloud, use the generic implementation guidelines.
The Dynamic Yield–Salesforce Commerce Cloud cartridge provides a quick and seamless way to implement Dynamic Yield. The cartridge takes care of the basic implementation of Dynamic Yield on the site (script, page context, e-commerce events, and product feed). Part of the setup requires technical knowledge of Salesforce Commerce Cloud.
There are two versions of this article, depending on your Salesforce Commerce Cloud environment. This article is for environments using the Controllers architecture. If your environment is based on SFRA, see Implementing Dynamic Yield on Salesforce Commerce Cloud: SFRA.
The cartridge is self-contained and can integrate into any project. It can be configured in Business Manager and contains all elements necessary to integrate systems into any e-commerce environment.
Prerequisite
This cartridge is designed for Salesforce Commerce Cloud API version 18.10, Compatibility Mode: 18.10. If you're using a different version, manual changes might be required.
Step 1: Create a Dynamic Yield section
Complete in Experience OS.
A section in Dynamic Yield Experience OS represents your store's site in our system. Each section has an associated product feed, which is your product catalog. You'll configure the sync of the catalog/feed in the following steps.
To create the section, go to Experience OS and follow the directions provided in Managing Sections.
Step 2: Install the cartridge in the sandbox
Complete these steps in Salesforce Commerce Cloud:
- Download the cartridge here.
- Open Salesforce Commerce Cloud Studio.
- Import the following downloaded cartridges into your IDE workspace:
int_dynamicyield and bc_dynamicyield
- Link the cartridge to the sandbox as follows:
- Select sandbox connection and select Properties.
- Select Project Reference.
- Check in int_dynamicyield and bc_dynamicyield
For more details, see the Salesforce Commerce Cloud documentation.
- Verify that the cartridge has been installed by going to Business Manager › Merchant Tools › Site Preferences › Custom Site Preference and verifying that the Dynamic Yield folder is there.
Step 3: Set up the Business Manager
Complete these steps in Salesforce Commerce Cloud:
- Go to Business Manager › Administration › Sites › Manage Sites.
- Select your site, and go to the Settings tab.
- In cartridges, add :int_dynamicyield to the end of the line as follows:
- Go to Business Manager › Administration › Sites › Manage Sites.
- Select the site Business Manager, and select the Settings tab.
- In cartridges, add the string “:bc_dynamicyield“
- Go to Business Manager › Site Development › Site Import & Export.
- Upload the file metadata/dynamicyield.zip. This file contains all necessary settings for the site.
- Import the file you just uploaded by selecting it and clicking Import. Click Yes to confirm.
- Verify that the implementation was successful by going to your test StoreFront and checking that the Dynamic Yield icon appears as shown in the following image:
Step 4: Configure the cartridge in Site Preferences
Complete these steps in Salesforce Commerce Cloud:
- Go to Business Manager: Sites › Your Site › Site Preferences › Custom Site Preferences › Dynamic Yield.
- Verify that Enable Dynamic Yield and Enable Catalog Export are set to Yes.
- Enter your Dynamic Yield Site ID.
- Enter the Product Feed’s S3 Access Key ID and Secret Access Key.
- If your product feed has custom columns, in addition to the required columns, add them in the Product Attributes field. You can add up to 30. Once custom columns are added, they can no longer be removed.
These can later be used for targeting (e.g. if you add "color", you can use it to target users who purchased blue items), affinity score (e.g. include color affinity in the affinity recommendation algorithm), define merchandising rules in your recommendation widgets (e.g. never recommend products with a specific brand). - Enter the Dynamic Yield Endpoint for Product Feed uploading (only host part):
-
If you are using Dynamic Yield's US data center:
-
Dynamic Yield Endpoint: com.dynamicyield.feeds
-
-
If you are using Dynamic Yield's EU data center, update the following fields:
-
Dynamic Yield Endpoint: dy-datafeeds-eu
-
Enable Dynamic Yield Europe Account: Yes
-
-
- Dynamic Yield uses a dedicated, blazing-fast CDN. However, if you want to use your own CDN, configure the following settings:
- Enable CDN integration: Yes.
- Custom CDN URL: A link to your CDN in the following format:
<script type="text/javascript" src="//[CUSTOM CDN URL]/api/[SECTION_ID]/api_dynamic.js"></script> <script type="text/javascript" src="//[CUSTOM CDN URL]/api/[SECTION_ID]/api_static.js"></script>
- Enable async integration: This should be set to No in most circumstances. If you configure the scripts to run asynchronously, unexpected behaviors will occur affecting which variations are shown, loading times, flicker, etc. Dynamic Yield provides a high server redundancy rate and lightning-fast loading times so you don’t need to resort to asynchronous loading to speed things up. All other operations after loading the scripts are run asynchronously.
Step 5: Sync your product catalog with Dynamic Yield
Complete the included steps in Experience OS.
A synced product feed enables capabilities like recommendations, segmenting based on product engagement, social proof tactics, and more.
To enable the sync, you must enter the AWS S3 access and secret keys. To get these credentials:
- Go to the Dynamic Yield console.
- Click Assets, and then click Data Feeds.
- Do one of the following:
- If you already have a product feed, go to your feed and click Edit.
- If you don't yet have a product feed, click Add New to create one.
- In the Feed Source, select Sync a file via Amazon S3.
- Do one of the following:
- If you're editing an existing product feed, click Generate Credentials.
- If you're creating a new feed, click Create a bucket.
The Access Key and Secret Key are displayed.
Step 6: Configure the product feed sync schedule
Complete these steps in Salesforce Commerce Cloud:
- Go to Business Manager: Administration › Operations › Job Schedules › Dynamic Yield.
- Go to the Schedule and History tab to define the synchronization schedule.
- Verify that Enabled is checked.
- Configure the Trigger for syncing the product catalog with Dynamic Yield:
- Recurring Intervals (recommended): to automatically sync the feed periodically and keep the Product Feed updated with your product catalog.
- Once: Sync only once. Notice that if your product catalog changes later, it will not update the Product Feed in Dynamic Yield.
- Use the date picker in the From and To fields to define the first time the job will run, and the time in which the job will no longer run. If the To field is blank, the job will continue to run according to the Run Time schedule. We recommend choosing today as the start date and keeping the end date empty.
- The Run Time determines the your sync rate. (minimum: 60 minutes).
- Go to the Step Configurator tab.
- Click Organization.
- Select needed site for this job and click Assign.
Step 7: Configure Events
The cartridge automatically supports the following events:
- Purchase
- Add to cart
- Signup
- Login
- Remove from cart
- Sync cart
- Sort items
- Filter items
- Change attribute
- Promo code entered
- Search
To add additional events:
- Go to int_dynamicyield/cartridge/scripts/helper/dyHelper.js and locate the getDYresponse function. Use the examples of supported events to add custom events.
- Add the events to your JS code wherever you want to trigger them.
- Each event also requires a call from the client JS scripts to the DynamicYield-GetAPIProperties endpoint.
Step 8: Validate the implementation
Complete in Experience OS:
After you implement Dynamic Yield, it's important to validate that your Script, Page Context, Events, and data feed are properly set up. For details, see Validating your Web Implementation.
Step 9: Modify the code base
Make code modifications in the following files. Use the files in the cartridge you downloaded to see examples.
Paste this code snippet from line 382 in cartridge/templates/default/util/modules.isml to the parallel file in your code base.
<ismodule template="dynamicyield/callrender"
name="dynamicyield" attribute="page_type" />
Paste this code snippet from line 202 in cartridge/scripts/util/Resource.ds to the parallel file in your code base.
DYGetAPIProperties:
URLUtils.url('DynamicYield-GetAPIProperties').toString()
Paste this code snippet from line 35 in the cartridge/templates/default/components/footer/footer_UI.js file to the parallel file in your code base.
<script src="${URLUtils.staticURL('/js/dynamicyield.js')}"></script> <script>
$( document ).ready(function() {
$('#email-alert-signup button').on('click',function(e){
e.preventDefault(); var DYform = $(this).closest('form'); var email = $('#email-alert-address').val();
dynamicYield.callEvent('Newsletter Subscription', {email: email, DYform: DYform});
});
});
</script>
<isif condition="${!empty(session.custom.DYnewSession) &&
session.custom.DYnewSession=='true'}">
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Sync cart'); });
</script>
</isif>
Copy the following code to the file as seen in the image below:
var quantity = $qty.val();
var productId = $form.find('input[name="pid"]').val();
dynamicYield.callEvent('setAddedItem', {productId: productId, quantity: quantity});
dynamicYield.callEvent('Add to Cart', {productId : productId, quantity: quantity});
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="OTHER"/>
- Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() { dynamicYield.callEvent('Account');
$('.add-to-cart').click(function(){
var $line = $(this).closest('tr');
$qty = $line.find('input[name="Quantity"]');
if ($qty.length === 0 || isNaN($qty.val()) || parseInt($qty.val(), 10) === 0) { $qty.val('1');
}
var quantity = $qty.val();
var productId = $line.find('.sku .value').text();
dynamicYield.callEvent('setAddedItem', {productId: productId, quantity:
quantity});
});
});
</script>
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="CART"/>
- Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
$("[name$='_deleteProduct']").click(function(e) {
e.preventDefault();
var thisName = $(this).attr('name');
$(this).append('<input type="hidden" value="Remove" name="'+thisName+'">');
var DYform = $(this).closest('form');
var line = $(this).closest( "tr" );
var quantity = line.find('.item-quantity input').val();
var productId = line.find('.sku .value').text();
dynamicYield.callEvent('setRemovedItem', {productId:
productId, quantity: quantity, DYform: DYform});
});
$('.add-to-wishlist').click(function(e){
e.preventDefault();
var line = $(this).closest( "tr" );
var DYhref = $(this).attr("href");
var productId = line.find('.sku .value').text();
dynamicYield.callEvent('Add to Wishlist', {productId: productId, DYhref: DYhref});
});
});
</script>
<isscript>
var couponCatch = '';
try {
couponCatch = newCoupon;
} catch(e){
couponCatch = '';
}
</isscript>
<isif condition="${!empty(newCoupon)}">
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Promo Code Entered', {code: '<isprint value="${newCoupon}" encoding="htmlsinglequote"/>'});
});
</script>
</isif>
<isif condition="${!empty(session.custom.DYaddedItem)}">
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Add to Cart');
});
</script></isif>
<isif condition="${!empty(session.custom.DYremovedItem)}">
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Remove from Cart');
});
</script>
</isif>
- Copy the following code to the file as seen in the image below:
data-swatchtype='{"type": "${attr.displayName}", "value":
"${attrValue.displayValue}"}'
- Copy the following code to the file as seen in the image below:
var swatchtype = $(this).data('swatchtype');
dynamicYield.callEvent('Change Attribute', swatchtype); - Copy the following code to the file as seen in the image below:
dynamicYield.callEvent('Change Attribute', {
type: $(this).parent().parent().find('span').text().trim(),
value: $(this).find(':selected').text().trim() })
Copy the following code to the file as seen in the image below:
var urlParam = $(this).find('option:selected').val();
urlParam = urlParam.substring(urlParam.indexOf('srule=')+6); urlParam = urlParam.substring(0, urlParam.indexOf('&')); var srules = {
'best-matches': {sortBy: 'Best Matches', sortOrder: 'ASC'},
'price-low-to-high': {sortBy: 'Price', sortOrder: 'ASC'},
'price-high-to-low': {sortBy: 'Price', sortOrder: 'DESC'},
'product-name-ascending': {sortBy: 'Product Name', sortOrder: 'ASC'},
'product-name-descending': {sortBy: 'Product Name', sortOrder: 'DESC'},
'brand': {sortBy: 'Brand', sortOrder: 'ASC'},
'most-popular': {sortBy: 'Most Popular', sortOrder: 'ASC'}, 'top-sellers': {sortBy: 'Top Sellers', sortOrder: 'ASC'}
}
var params = srules[urlParam];
dynamicYield.callEvent('Sort Items', params);
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="OTHER"/>
- Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Purchase', {orderId: '<isprint value="${Order.orderNo}" encoding="htmlsinglequote"/>'});
});
</script>
- Copy the following code to the file as seen in the image below:
<isif condition="${(pdict.CurrentHttpParameterMap.cgid.submitted &&
pdict.CurrentHttpParameterMap.cgid.value != '')}">
<isdynamicyield page_type="CATEGORY"/>
<iselse>
<isdynamicyield page_type="OTHER"/> </isif> - Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
$(document).on('click','.refinement:not(.category-refinement) a',function(){
if(!$(this).parent().hasClass('selected')){
var data = $(this).data('filter');
dynamicYield.callEvent('Filter Items', data);
}
});
<isif condition="${!empty(searchPhrase)}">
dynamicYield.callEvent('Keyword Search', {keywords: '<isprint value="${searchPhrase}" encoding="htmlsinglequote"/>'});
</isif>
$(document).on('click','.js-add-to-wishlist',function(e){
e.preventDefault();
var DYhref = $(this).attr("href");
var productId = $(this).data('pid');
dynamicYield.callEvent('Add to Wishlist', {productId: productId, DYhref: DYhref});
});
});
</script>
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="CATEGORY"/>
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="OTHER"/>
- Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
<isif condition="${!empty(pdict.ProductSearchResult.searchPhrase)}">
dynamicYield.callEvent('Keyword Search', {keywords: '<isprint value="${pdict.ProductSearchResult.searchPhrase}" encoding="htmlsinglequote"/>'});
</isif>
});
</script>
Use the example file in the cartridge to update the code in your code base.
- In line 54, replace
with<isdynamicyield page_type="OTHER"/>
<a data-filter='{"type":
"${RefinementValue.getID()}", "value": "${RefinementValue.getValue()}"}'
href="${pdict.ProductSearchResult.urlRefineCategory('Search-Show',
path.get(0).ID)}"> - In lines 105, 111, and 117, add this code for each <a> tag:
For example, in line 105, update this code:data-filter='{"type": "${RefinementValue.getID()}", "value":
"${RefinementValue.getValue()}"}'
to look like this:<a id="${swatchID}"
href="${StringHelpers.unsanitizeOR(pdict.ProductSearchResult.urlRelaxAttributeValue('Search-Sh ow',RefinementValue.getID(),RefinementValue.getValue()))}" title="${Resource.msgf('global.refinement.selected.label','locale',null, RefinementDefinition.displayName, RefinementValue.getDisplayValue())}"><a id="${swatchID}" data-filter='{"type": "${RefinementValue.getID()}", "value":
"${RefinementValue.getValue()}"}'
href="${StringHelpers.unsanitizeOR(pdict.ProductSearchResult.urlRelaxAttributeValue('Search-Sh ow',RefinementValue.getID(),RefinementValue.getValue()))}" title="${Resource.msgf('global.refinement.selected.label','locale',null, RefinementDefinition.displayName, RefinementValue.getDisplayValue())}"> - In lines 173 and 180, add this code snippet to the <a> tag:
For example:data-filter='{"type": "${RefinementDefinition.getDisplayName()}",
"value": "${RefinementValue.getDisplayValue()}"}' code code<a class="refinement-link"
title="${Resource.msg('global.pricerefinement.label.prefix','locale',null)}
${RefinementValue.getDisplayValue()}"
data-filter='{"type":"${RefinementDefinition.getDisplayName()}",
"value": "${RefinementValue.getDisplayValue()}"}'
href="${StringHelpers.unsanitizeOR(pdict.ProductSearchResult.urlRefinePrice('Search-Show',Refi nementValue.getValueFrom(),RefinementValue.getValueTo()))}">
Add the highlighted areas to the file:
<a class="button simple js-add-to-wishlist
" data-action="wishlist" data-pid="${pdict.Product.ID}"
href="${URLUtils.https('Wishlist-Add', 'pid', pdict.Product.ID, 'source', 'productdetail')}"
title="${Resource.msg('global.addtowishlist.label','locale',null)}">${Resource.msg('global.addtowishlist','loc ale',null)}</a>cartridge/templates/default/account/wishlist/pt_wishlist.isml
- Copy the following code to the file as seen in the image below:
<isinclude template="util/modules" />
<isdynamicyield page_type="PRODUCT"/> - Copy the following code to the pageContext call:
For example:productID: (pdict.Product.ID || ''),
var pageContext = {
title: (pdict.Product.name || 'Product Detail'),
productID: (pdict.Product.ID || ''), type: 'product',
ns:'product'
}; - Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
$(document).on('click','.js-add-to-wishlist, a[data-action=wishlist]',function(e){ e.preventDefault();
var DYhref = $(this).attr("href");
var productId = $(this).data('pid') || window.pageContext.productID;
dynamicYield.callEvent('Add to Wishlist', {productId: productId, DYhref: DYhref});
});
});
</script>
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="OTHER"/>
- Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Account');
});
</script>
- Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="OTHER"/>
- Copy the following code to the file as seen in the image below:
<script>
$( document ).ready(function() {
dynamicYield.callEvent('Account');
});
</script>
- Copy the following code to the file as seen in the image below:
<isset name="appliedCoupons"
value="${new dw.util.ArrayList()}" scope="page" /> - Copy the following code to the file as seen in the image below:
<isset name="isAppliedCoupons"
value="${appliedCoupons.add(FormCoupon.object.couponCode)}"
scope="page" /> - Copy the following code to the file as seen in the image below:
<isscript> var DYcoupons = []; var newCoupon = '';
var appliedCoupons = appliedCoupons.toArray(); if(!empty(appliedCoupons)){
// When at least one coupon was used
if(!empty(session.custom.DYcoupons)){
// When at least one coupon was saved to our session
DYcoupons = session.custom.DYcoupons.split('|');
if( DYcoupons.length < appliedCoupons.length ){ // new coupon was used and need to find it for (var i in DYcoupons) { if(DYcoupons[i] != appliedCoupons[i]){ newCoupon = appliedCoupons[i]; break;
}
}
if(newCoupon==''){
i++;
newCoupon = appliedCoupons[i];
}
} // else coupon was removed or not added
} else {
// we didn't call our event yet
newCoupon = appliedCoupons[0];
}
}
session.custom.DYcoupons = appliedCoupons.join("|"); </isscript>
Copy the following code to the file as seen in the image below:
<script> setTimeout(function() {
$(document).ready(function() {
$('select').each(function(i,e) {
$(e).on('change', function(el) {
var $el = $(el.currentTarget).find(':selected'); dynamicYield.callEvent('Change Attribute', {type: $(el.currentTarget).parent().parent().find('span').text(), value: $el.text()})
})
})
})
},100)
</script>
Copy the following code to the file as seen in the image below:
if (data.success) {
dynamicYield.callEvent('Promo Code Entered', {code: code, ajax: true});
if(data.baskettotal === 0){
window.location.assign(Urls.billing);
}}
Copy the following code to the file as seen in the image below:
<isdynamicyield page_type="HOMEPAGE"/>
- cartridge/templates/default/content/pt_customerservice.isml line 18
- cartridge/templates/default/content/content/pt_content.isml line 11
- cartridge/templates/default/account/giftregistry/pt_giftregistry.isml line 24
- cartridge/templates/default/account/orderhistory/orders.isml line 4
<isdynamicyield page_type="OTHER"/>