This cartridge integrates Topsort's auction-based sponsored product placement into Salesforce B2C Commerce (SFCC) search results, including both product listings and promotional banners.
This cartridge adds support for the following:
- Sponsored products with custom sponsored flag and event tracking
- One banner on search pages for categories and search with custom placement and event tracking
Future versions will include:
- Support for multiple banners
- Additional preferences for customizing:
- Sponsored product placement
- Number of sponsored products via custom preference
-
Open Import & Export In Business Manager, navigate to Administration > Site Development > Import & Export.
-
Upload Metadata File Under Import & Export Files, upload:
/cartridge/metadata/topsort_sponsored_metadata.xml
-
Select and Import Switch to the MetaData section, select the uploaded
topsort_sponsored_metadata.xml
, and click Import. -
Configure Preferences After importing, go to Merchant Tools > Select Your Site > Site Preferences > Custom Preferences > Topsort to set your API settings.
- SFCC instance (Sandbox or Production) with WebDAV access
- Topsort API key (Bearer token)
- Node.js (optional, for local testing/build scripts)
-
Upload Cartridge Mirror the cartridge directory into your SFCC WebDAV under:
Cartridges/topsort_sponsored
-
Cartridge Path In Business Manager: Administration > Sites > Manage Sites > Manage Sites > Settings > Cartridge Path. Add
topsort_sponsored
(and any dependencies, e.g.,app_storefront_base
) at the front. -
Clear & Rebuild
- Clear code and view caches.
- Restart your sandbox if needed.
In Merchant Tools > Select Your Site > Site Preferences > Custom Preferences > Topsort:
- topsortApiURL — Base URL for Topsort API (e.g.,
https://api.topsort.com
). - topsortApiKey — Your Topsort bearer token.
- topsortCookieMaxAge — Cookie TTL in seconds (default:
86400
).
-
Banner Slots
- File:
/cartridge/scripts/config/topsort_banners.json
- Defines banner slot IDs, slot counts, and types (
search
orcategory
).
[ { "slotId": "banner-1", "slots": 1, "type": "search" }, { "slotId": "cat-banner", "slots": 2, "type": "category" } ]
- File:
-
File:
/cartridges/topsort_auctions/cartridge/controllers/Search.js
-
Flow:
-
Extend base
Search-Show
controller. -
Read
viewData.productSearch.productIds
, extractproductIDs
,searchQuery
(q), andcategoryId
(cgid). -
Manage cookies:
tsuid
: generate/return a UUID cookie for user tracking.topsortLastQuery
: store last search query.
-
Build auctions array:
- Listings auction: type=
listings
, slots = number of products, includesearchQuery
orcategory
. - Banner auctions: iterate
topsort_banners.json
, map each config to an auction object.
- Listings auction: type=
-
Call
TopsortService.runAuction(auctionRequest)
. -
On success:
- Extract
winners
for listings and setisSponsored
andresolvedBidId
on matching entries. - Reorder
productSearch.productIds
to place sponsored items first. - Extract banner winner URL and bid ID into
viewData.featuredContentUrl
andviewData.featuredContentBidId
.
- Extract
-
Populate additional
viewData
:topsortApiKey
,topsortApiURL
,topsortTrackingEnabled
(fromTopsortService.getClientConfig()
).tsuid
for client-side tracking.
-
res.setViewData(viewData)
and callnext()
.
-
-
File:
/cartridge/controllers/Order.js
-
Flow:
- Extend
Order-Confirm
endpoint. - Retrieve order by
req.querystring.ID
. - Read
tsuid
cookie foropaqueUserId
. - Call
TopsortService.sendPurchaseEvent(order, opaqueUserId)
to record conversion. - Log errors if event fails.
- Extend
-
File:
/cartridge/scripts/services/TopsortService.js
-
Responsibilities:
runAuction(request)
: performs HTTP call to Topsort auction API.sendPurchaseEvent(order, userId)
: POSTs purchase data.getClientConfig()
: returns API key, URL, and tracking flag for front-end.
-
File:
/cartridge/templates/default/search/components/productTiles.isml
-
Sponsored Markup:
<div class="featured-item-label">Featured</div> <script> ProductEngagement.setupItemTracking({ productId: '${product.productID}', userId: '${pdict.tsuid}', position: ${loopStatus.index+1}, page: ${pdict.productSearch.page}, pageSize: ${pdict.productSearch.hitsPerPage}, categoryId: '${pdict.request.querystring.cgid}', resolvedBidId: '${product.resolvedBidId}' }); </script>
-
Customize the
.featured-item-label
or reposition as needed.
-
File:
/cartridge/templates/default/search/searchResultsNoDecorator.isml
-
Banner HTML:
<div id="featured-content" class="featured-content-container"> <a href="${pdict.featuredContentUrl}" target="_blank"> <img src="${pdict.featuredContentUrl}" alt="Featured Content" style="width:100%;" /> </a> </div> <script> ProductEngagement.setupContentTracking({ userId: '${pdict.tsuid}', position: 1, page: ${pdict.productSearch.page}, pageSize: ${pdict.productSearch.hitsPerPage}, categoryId: '${pdict.request.querystring.cgid}', resolvedBidId: '${pdict.featuredContentBidId}' }); </script>
-
Feel free to move this block; include the
<script>
to retain tracking.
-
Include:
<script src="${URLUtils.staticURL('/js/product-engagement.js')}"></script> <script> ProductEngagement.init({ apiURL: '${pdict.topsortApiURL}', apiKey: '${pdict.topsortApiKey}', trackingEnabled: ${pdict.topsortTrackingEnabled} }); </script>
-
Ensures event calls from front-end components reach Topsort.
- Search on storefront (
/s?q=example
). - View products; sponsored items appear first with a "Featured" label.
- Banners display above results when configured.
- Impression and click events are tracked automatically.
- No sponsored items: Check
topsortApiKey
, cookie TTL, and that auctions return winners. - No banners: Verify
topsort_banners.json
mapping andfeaturedContentUrl
in view data. - Missing events: Open console to see if
ProductEngagement
calls execute; ensuretrackingEnabled
istrue
. - Server errors: Inspect
logs/SponsoredSearch
andlogs/OrderEvent
for stack traces.
Reach out to your Topsort integration engineer or SFCC support with cartridge logs and configuration screenshots.