Skip to content

Commit

Permalink
Merge pull request #44 from klaviyo/202409_ECOS-8244_ECOS-8181
Browse files Browse the repository at this point in the history
Bumps API revision, updates klaviyo.js, add top level event fields
  • Loading branch information
cykolln authored Sep 18, 2024
2 parents ad9a91e + bfb02a3 commit 6bbb9e8
Show file tree
Hide file tree
Showing 18 changed files with 202 additions and 155 deletions.
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,16 @@ bumped for multiple releases during one month.
<!-- BEGIN RELEASE NOTES -->
### [Unreleased]

### [24.9.0] - 24-09-17

#### Added
- Added `siteID` key/value to client-side events
- Added `SiteID` key/value to client-side events
- Added the following fields to the top level of Added to Cart events: `masterProductID`, `productID`, `price`, `productName` for use during segmentation.
- Added `value` and `value_currency` to the following events to unblock attribution value tracking in Klaviyo: Started Checkout, Order Confirmation, Viewed Product, Added to Cart

#### Changed
- Updates API revision to latest (2024-07-15)
- Updates klaviyo.js onsite javascript url to new format

#### Fixed
- Updated error handling for consent at checkout when a phone number is invalid
Expand Down Expand Up @@ -79,7 +87,8 @@ bumped for multiple releases during one month.
<!-- END RELEASE NOTES -->

<!-- BEGIN LINKS -->
[Unreleased]: https://github.com/klaviyo/SFCC_Klaviyo/compare/24.1.0...HEAD
[Unreleased]: https://github.com/klaviyo/SFCC_Klaviyo/compare/24.9.0...HEAD
[24.9.0]: https://github.com/klaviyo/SFCC_Klaviyo/compare/24.1.0...24.9.0
[24.1.0]: https://github.com/klaviyo/SFCC_Klaviyo/compare/23.7.0...24.1.0
[23.7.0]: https://github.com/klaviyo/SFCC_Klaviyo/compare/21.10.0...23.7.0
[21.10.0]: https://github.com/klaviyo/SFCC_Klaviyo/compare/21.7.0...21.10.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
</isscript>
<!--- TEMPLATENAME: klaviyoFooter.isml --->
<isif condition="${klaviyoUtils.klaviyoEnabled}">
<script async src="//static.klaviyo.com/onsite/js/klaviyo.js?company_id=${dw.system.Site.getCurrent().preferences.custom.klaviyo_account}"></script>
<script async src="//static.klaviyo.com/onsite/js/${dw.system.Site.getCurrent().preferences.custom.klaviyo_account}/klaviyo.js"></script>
<script>
// klaviyo object loader - provided by klaviyo
!function(){if(!window.klaviyo){window._klOnsite=window._klOnsite||[];try{window.klaviyo=new Proxy({},{get:function(n,i){return"push"===i?function(){var n;(n=window._klOnsite).push.apply(n,arguments)}:function(){for(var n=arguments.length,o=new Array(n),w=0;w<n;w++)o[w]=arguments[w];var t="function"==typeof o[o.length-1]?o.pop():void 0,e=new Promise((function(n){window._klOnsite.push([i].concat(o,[function(i){t&&t(i),n(i)}]))}));return e}}})}catch(n){window.klaviyo=window.klaviyo||[],window.klaviyo.push=function(){var n;(n=window._klOnsite).push.apply(n,arguments)}}}}();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ function getData(basket) {
data['SiteID'] = siteId;
data.event = klaviyoUtils.EVENT_NAMES.addedToCart;
data.basketGross = basket.getTotalGrossPrice().getValue().valueOf();
data.value = basket.getTotalGrossPrice().getValue().valueOf(); // duplicative but we will attempt to pop this out to the top-level value property
data.value_currency = session.getCurrency().getCurrencyCode();
data.itemCount = basketItems.length;
data.lineItems = [];
data.items = [];
Expand Down Expand Up @@ -109,6 +111,12 @@ function getData(basket) {
for (var idx = data.lineItems.length - 1; idx >= 0; idx--) {
if (!data.lineItems[idx]['Is Bonus Product']) {
data.productAddedToCart = data.lineItems[idx];

// Duplicating to top-level for segmentation
data.productID = data.productAddedToCart.productID;
data.masterProductID = data.productAddedToCart.masterProductID;
data.price = data.productAddedToCart.priceValue;
data.productName = data.productAddedToCart.productName;
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ function getData(order) {
data['Item Count'] = itemCount;
data['Item Primary Categories'] = itemPrimaryCategories;
data['Item Categories'] = klaviyoUtils.dedupeArray(itemCategories);
data['$value'] = orderTotal;
data['value'] = orderTotal;
data['value_currency'] = session.getCurrency().getCurrencyCode();
data['$event_id'] = 'orderConfirmation-' + order.orderNo;
data['Tracking Number'] = order.shipments[0].trackingNumber ? order.shipments[0].trackingNumber : '';
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ function getData(currentBasket) {
var basketItems = currentBasket.getProductLineItems().toArray();
var reconstructCartItems = [];
data['Basket Gross Price'] = currentBasket.getTotalGrossPrice().value;
data['value'] = currentBasket.getTotalGrossPrice().value; // duplicative, will be pulled to top-level event field during event creation
data['value_currency'] = session.getCurrency().getCurrencyCode(); // will be pulled to top-level event field during event creation
data['Item Count'] = basketItems.length;

// prepare to add top-level data while iterating through product line items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ function getData(productID) {
data['Original Price'] = prices.originalPrice ? prices.originalPrice : prices.price;
data['Original Price String'] = prices.originalPriceString ? prices.originalPriceString : prices.priceString;
data['Product UPC'] = product.UPC;
data['value'] = prices.price;
data['value_currency'] = session.getCurrency().getCurrencyCode();

if (!product.master && 'masterProduct' in product) {
data['Master Product ID'] = product.masterProduct.ID;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ var KlaviyoEventService = ServiceRegistry.createService('KlaviyoEventService', {
svc.addHeader('Authorization', 'Klaviyo-API-Key ' + key);
svc.addHeader('Content-type', 'application/json');
svc.addHeader('Accept', 'application/json');
svc.addHeader('revision', '2023-02-22');
svc.addHeader('revision', '2024-07-15');
// TODO: dynamically pull extension version
svc.addHeader('X-Klaviyo-User-Agent', 'sfcc-klaviyo/24.1.0');
svc.addHeader('X-Klaviyo-User-Agent', 'sfcc-klaviyo/24.9.0');

return JSON.stringify(args);
},
Expand Down Expand Up @@ -103,9 +103,9 @@ var KlaviyoSubscribeProfilesService = ServiceRegistry.createService('KlaviyoSubs
svc.addHeader('Authorization', 'Klaviyo-API-Key ' + key);
svc.addHeader('Content-type', 'application/json');
svc.addHeader('Accept', 'application/json');
svc.addHeader('revision', '2023-02-22');
svc.addHeader('revision', '2024-07-15');
// TODO: dynamically pull extension version
svc.addHeader('X-Klaviyo-User-Agent', 'sfcc-klaviyo/24.1.0');
svc.addHeader('X-Klaviyo-User-Agent', 'sfcc-klaviyo/24.9.0');

return JSON.stringify(args);
},
Expand Down
128 changes: 103 additions & 25 deletions cartridges/int_klaviyo_core/cartridge/scripts/klaviyo/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,36 +179,70 @@ function trackEvent(exchangeID, data, event, customerEmail) {
return;
}

var metricObj = { name: event };
// METRIC DATA
var metricAttributes = { name: event };
/* IMPORTANT:
If the klaviyo_sendEventsAsSFCC site preference has been set to Yes (true) events will show up in the Klaviyo Dashboard with SFCC as the event provider.
Generally speaking this should only be set to Yes if this is a brand new Klaviyo integration - if there is a previous integration with Klaviyo for
this site that did not label events with SFCC as provider there will be a break in reporting and functionality between past events that were not
labelled with SFCC as provider and the new events that are. If in doubt, leave the site preference set to No and contact Klaviyo technical support.
*/
if (Site.getCurrent().getCustomPreferenceValue('klaviyo_sendEventsAsSFCC')) {
metricObj.service = 'demandware';
metricAttributes.service = 'demandware';
}
var metricObj = {
data: {
type: 'metric',
attributes: metricAttributes
}
};

var profileObj = {};
// PROFILE DATA
var profileDataAttributes = {};
if (!customerEmail) {
profileObj = { $exchange_id: exchangeID };
profileDataAttributes._kx = exchangeID;
} else {
profileObj = { $email: customerEmail };
profileDataAttributes.email = customerEmail;
}

var profileObj = {
data: {
type: 'profile',
attributes: profileDataAttributes
}
};

// Extract value and value_currency as top-level fields
var value;
var valueCurrency;

if (data.value) {
value = data.value;
delete data.value;

if (data.value_currency) {
valueCurrency = data.value_currency;
delete data.value_currency;
}
}

// EVENT DATA
var eventData = {
data: {
type : 'event',
attributes : {
profile : profileObj,
metric : metricObj,
properties : data,
value: value,
value_currency: valueCurrency,
time : (new Date()).toISOString()
}
}
};

logger.info(JSON.stringify(eventData));

var result = klaviyoServices.KlaviyoEventService.call(eventData);

if (result == null) {
Expand Down Expand Up @@ -242,15 +276,36 @@ function subscribeUser(email, phone) {
if (session.custom.KLEmailSubscribe && emailListID) {
data = {
data: {
type : 'profile-subscription-bulk-create-job',
attributes : {
list_id : emailListID,
type: 'profile-subscription-bulk-create-job',
attributes: {
custom_source : 'SFCC Checkout',
subscriptions : [{
channels : { email: ['MARKETING'] },
email : email,
phone_number : phone
}]
profiles: {
data: [
{
type: 'profile',
attributes: {
subscriptions: {
email: {
marketing: {
consent: 'SUBSCRIBED'
}
}
},
email : email,
phone_number : phone
}
}
]
},
historical_import: false
},
relationships: {
list: {
data: {
type: 'list',
id: emailListID
}
}
}
}
};
Expand All @@ -266,7 +321,7 @@ function subscribeUser(email, phone) {
// check to see if the reason the call failed was because of Klaviyo's internal phone number validation. if so, try to resend without phone number
var errObj = JSON.parse(result.errorMessage);
if (result.error == 400 && errObj.errors[0].code == 'invalid' && errObj.errors[0].detail.includes('phone number')) {
data.data.attributes.subscriptions[0].phone_number = null;
data.data.attributes.profiles.data[0].attributes.phone_number = null;
result = klaviyoServices.KlaviyoSubscribeProfilesService.call(data);
if (result == null) {
logger.error('klaviyoServices.KlaviyoSubscribeProfilesService subscribe call for email returned null result on second attempt without phone number');
Expand All @@ -279,18 +334,41 @@ function subscribeUser(email, phone) {
}

if (session.custom.KLSmsSubscribe && smsListID && phone) {
data = { data: {
type : 'profile-subscription-bulk-create-job',
attributes : {
list_id : smsListID,
custom_source : 'SFCC Checkout',
subscriptions : [{
channels : { sms: ['MARKETING'] },
email : email,
phone_number : phone
}]
data = {
data: {
type: 'profile-subscription-bulk-create-job',
attributes: {
custom_source : 'SFCC Checkout',
profiles: {
data: [
{
type: 'profile',
attributes: {
subscriptions: {
sms: {
marketing: {
consent: 'SUBSCRIBED'
}
}
},
email : email,
phone_number : phone
}
}
]
},
historical_import: false
},
relationships: {
list: {
data: {
type: 'list',
id: smsListID
}
}
}
}
} };
};

result = klaviyoServices.KlaviyoSubscribeProfilesService.call(data);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
</iscomment>
<!--- TEMPLATENAME: klaviyoFooter.isml --->
<isif condition="${klaviyoUtils.klaviyoEnabled}">
<script async src="//static.klaviyo.com/onsite/js/klaviyo.js?company_id=${dw.system.Site.getCurrent().preferences.custom.klaviyo_account}"></script>
<script async src="//static.klaviyo.com/onsite/js/${dw.system.Site.getCurrent().preferences.custom.klaviyo_account}/klaviyo.js"></script>
<script>
// klaviyo object loader - provided by klaviyo
!function(){if(!window.klaviyo){window._klOnsite=window._klOnsite||[];try{window.klaviyo=new Proxy({},{get:function(n,i){return"push"===i?function(){var n;(n=window._klOnsite).push.apply(n,arguments)}:function(){for(var n=arguments.length,o=new Array(n),w=0;w<n;w++)o[w]=arguments[w];var t="function"==typeof o[o.length-1]?o.pop():void 0,e=new Promise((function(n){window._klOnsite.push([i].concat(o,[function(i){t&&t(i),n(i)}]))}));return e}}})}catch(n){window.klaviyo=window.klaviyo||[],window.klaviyo.push=function(){var n;(n=window._klOnsite).push.apply(n,arguments)}}}}();
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "int_klaviyo",
"version": "24.1.0",
"version": "24.9.0",
"description": "Klaviyo integration for Commerce Cloud",
"main": "index.js",
"scripts": {
Expand All @@ -21,7 +21,7 @@
"devDependencies": {
"chai": "^4.2.0",
"mocha": "^6.2.2",
"sgmf-scripts": "^2.4.1"
"sgmf-scripts": "^2.4.2"
},
"dependencies": {
"proxyquire": "^2.1.3"
Expand Down
15 changes: 0 additions & 15 deletions test/src/tests/e2e/sfra/product/product-tests.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { test, expect } from '@playwright/test'
import { ProductPage } from '../page-objects/product.js'
import { getLatestMetricData } from '../../../../utils/v3-api-helper.mjs'

let testData = {
firstName: 'Product',
Expand Down Expand Up @@ -43,17 +42,3 @@ test.describe('Test Klaviyo viewed category event', () => {
expect(logData[0].success).toBe(true)
})
})

test.describe('Test successful integration with Klaviyo', () => {
test('Verify Viewed Product metric in Klaviyo sandbox', async ({ page }) => {
email = await productPage.generateEmail()
testData.email = email
await productPage.accountPage.gotoAccountLogin()
await productPage.accountPage.fillRegistrationForm(testData)
expect(await page.innerText('h1.page-title')).toBe('Dashboard')
await productPage.getProduct()
const metrics = await getLatestMetricData()
const metricName = metrics.attributes.name
expect(metricName).toBe('Viewed Product')
})
})
15 changes: 0 additions & 15 deletions test/src/tests/e2e/sitegen/product/product-tests.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { test, expect } from '@playwright/test'
import { ProductPage } from '../page-objects/product.js'
import { getLatestMetricData } from '../../../../utils/v3-api-helper.mjs'

let testData = {
firstName: 'Product',
Expand Down Expand Up @@ -43,17 +42,3 @@ test.describe('Test Klaviyo viewed category event', () => {
expect(logData[0].success).toBe(true)
})
})

test.describe('Test successful integration with Klaviyo', () => {
test('Verify Viewed Product metric in Klaviyo sandbox', async ({ page }) => {
email = await productPage.generateEmail()
testData.email = email
await productPage.accountPage.gotoAccountLogin()
await productPage.accountPage.fillRegistrationForm(testData)
expect(page.locator('#primary > h1')).toContainText('My Account')
await productPage.getProduct()
const metrics = await getLatestMetricData()
const metricName = metrics.attributes.name
expect(metricName).toBe('Viewed Product')
})
})
Loading

0 comments on commit 6bbb9e8

Please sign in to comment.