<% if (adData) { %>
<!-- AD Button - dynamically loaded from bidder --><!-- TODO - check if ad for this manufacturer is valid:
* manufacturer S has a brandShop S
* offerList contains offers from S -->
<%
/**
* Mock shop/retailer data.
* In production, this data is provided by the shop/retailer system,
* NOT from the ad server. The ad server only provides tracking (adClick).
*/
const shopData = {
productUrl: 'https://www.idealo.de/relocator/relocate?categoryId=19116&offerKey=2be476c925ea6ce07f2753a0c24c0fa1&pos=31&price=869.00&productid=205675497&sid=316622&type=offer',
shopName: 'Samsung',
shopLogo: 'https://1000logos.net/wp-content/uploads/2017/06/Samsung-Logo-2.png',
price: '€869.00',
currency: 'EUR'
};
%>
<divid="<%= adData.containerId %>"class="offer-button-wrapper offer-button-ad"data-ad-slot="oop-button-ad"><divclass="ad-header"><spanclass="ad-label"><%= adData.label %></span>
<% if (adData.dsaInfo) { %>
<divclass="dsa-info-wrapper"><spanclass="dsa-info-icon"title="Ad transparency info">i</span><divclass="dsa-info-tooltip"><divclass="dsa-info-row"><spanclass="dsa-info-label">Advertiser:</span><spanclass="dsa-info-value"><%= adData.dsaInfo.advertiser || 'N/A' %></span></div><divclass="dsa-info-row"><spanclass="dsa-info-label">Paid by:</span><spanclass="dsa-info-value"><%= adData.dsaInfo.payer || 'N/A' %></span></div></div></div>
<% } %>
</div><!--
Shop data (productUrl, shopLogo, shopName, price) - provided by retailer/shop system
Ad tracking (clickUrl) - provided by ad server (this is the ONLY data from ad server)
--><ahref="<%= shopData.productUrl %>"class="brand-shop-button"target="_blank"rel="noopener nofollow sponsored"data-offer-type="ad"data-ad-id="<%= adData.adId || 'unknown' %>"onclick="dlApi.cmd.push(function() { var url = '<%= adData.clickUrl %>'; console.log('[dlApi] Adclick:', url); new Image().src = url; });">
At <imgsrc="<%= shopData.shopLogo %>"alt="<%= shopData.shopName %>"class="shop-logo"style="height: 16px; vertical-align: middle;">
for <spanclass="button-price"><%= shopData.price %></span></a></div><script>
dlApi.cmd.push(function () {
dlApi.registerBidResponse(<%- JSON.stringify(adData.adm) %>, '<%= adData.containerId %>');
});
</script>
<% } %>
<!-- AD Button - loaded client-side via dlApi --><divid="oop-ad-button"class="offer-button-wrapper offer-button-ad"><divclass="ad-header"><spanclass="ad-label">Ad</span></div><divclass="ad-slot-loading">Loading ad...</div></div><script>// Frontend approach:const divId = 'oop-ad-button';
/**
* Mock function simulating shop/retailer data response.
* In production, this data is provided by the shop/retailer system,
* NOT from the ad server. The ad server only provides tracking (adClick).
*/functiongetMockShopData() {
return {
productUrl: 'https://www.idealo.de/relocator/relocate?categoryId=19116&offerKey=2be476c925ea6ce07f2753a0c24c0fa1&pos=31&price=869.00&productid=205675497&sid=316622&type=offer',
shopName: 'Samsung',
shopLogo: 'https://1000logos.net/wp-content/uploads/2017/06/Samsung-Logo-2.png',
price: '€869.00',
currency: 'EUR'
};
}
/**
* Generate DSA info icon HTML
* @param {Object} dsa - DSA object from ad.dsa with fields: behalf, paid, adrender
*/functiongetDsaInfoHtml(dsa) {
if (!dsa) return'';
return'<div class="dsa-info-wrapper">' +
'<span class="dsa-info-icon" title="Ad transparency info">i</span>' +
'<div class="dsa-info-tooltip">' +
'<div class="dsa-info-row">' +
'<span class="dsa-info-label">Advertiser:</span> ' +
'<span class="dsa-info-value">' + (dsa.behalf || 'N/A') + '</span>' +
'</div>' +
'<div class="dsa-info-row">' +
'<span class="dsa-info-label">Paid by:</span> ' +
'<span class="dsa-info-value">' + (dsa.paid || 'N/A') + '</span>' +
'</div>' +
'</div>' +
'</div>';
}
// Step 1: Wait for SDK to be ready (all dlApi calls must be inside cmd.push)
dlApi.cmd.push(function (dlApi) {
// Step 2: Set targeting key-values (sent to ad server for campaign matching)
dlApi.addKeyValue('manufacturer_id', '12345678'); // Product manufacturer ID// 3. Register template - defines HOW ad is rendered
dlApi.fetchNativeAd({
slot: 'product-button', // constant value for Sponsored Productdiv: divId,
tplCode: '1746213/Banner-Standard', // constant value for Sponsored ProductasyncRender: true// manually count ad impressions if you want to show only active offers
}).then(ad => {
if (!ad) {
console.info('[Frontend Mode] Ad not found!');
return;
}
/** TODO - check if ad for this manufacturer is valid:
* manufacturer S has a brandShop S
* offerList contains offers from S
**/console.log('[Frontend Mode] renderAd called:', ad);
// Use parent document (not iframe's win.document)var container = document.getElementById(divId);
if (!container) {
console.error('[Frontend Mode] Container not found!');
return;
}
// Shop data - provided by retailer/shop system (mocked for demo)var shopData = getMockShopData();
// DSA info from ad response (ad.dsa contains: behalf, paid, adrender)var dsaInfo = ad.dsa;
// Count impressions manually
ad.render();
// Ad tracking URL - provided by ad server (this is the ONLY data from ad server)var clickUrl = ad.meta.adclick + encodeURIComponent(ad.fields.click || '');
// Replace entire container content with proper structure// Note: All product data (URL, logo, price) comes from shop, only adClick tracking comes from ad
container.innerHTML =
'<div class="ad-header">' +
'<span class="ad-label">Ad</span>' +
getDsaInfoHtml(dsaInfo) +
'</div>' +
'<a href="' + shopData.productUrl + '" ' +
'class="brand-shop-button" ' +
'target="_blank" ' +
'rel="noopener nofollow sponsored" ' +
'data-offer-type="ad" ' +
'data-ad-id="' + (ad.meta.adid || 'unknown') + '" ' +
'onclick="(function(){ console.log(\'[dlApi] Adclick:\', \'' + clickUrl + '\'); new Image().src = \'' + clickUrl + '\'; })();">' +
'At <img src="' + shopData.shopLogo + '" ' +
'alt="' + shopData.shopName + '" class="shop-logo" style="height: 16px; vertical-align: middle;"> ' +
'for <span class="button-price">' + shopData.price + '</span>' +
'</a>';
console.log('[Frontend Mode] Button rendered successfully');
}
).catch(err => {
console.error('Ad for rmn-sponsored-product could not be loaded:', err);
});
// 4: Trigger ad request (sends all slots to ad server)
dlApi.fetch();
//// // 3. SECOND WAY//// // Step 3: Define ad slot ('branded-products' = slot name, containerId = container element)// const slot = dlApi.defineSlot('brand-store', divId, { pos: 1 });//// // Step 4: Register custom template to control how ad is rendered// slot.registerTemplate({// tplCode: '1746213/Banner-Standard',// asyncRender: true,// renderAd: function (ad) {// if (!ad) {// console.info('[Frontend Mode] Ad not found!');// return;// }//// /** TODO - check if ad for this manufacturer is valid:// * manufacturer S has a brandShop S// * offerList contains offers from S// **/////// console.log('[Frontend Mode] renderAd called:', ad);// // Use parent document (not iframe's win.document)// var container = document.getElementById(divId);// if (!container) {// console.error('[Frontend Mode] Container not found!');// return;// }//// // Shop data - provided by retailer/shop system (mocked for demo)// var shopData = getMockShopData();//// // DSA info from ad response (ad.dsa contains: behalf, paid, adrender)// var dsaInfo = ad.dsa;//// // Count impressions manually// ad.render();//// // Ad tracking URL - provided by ad server (this is the ONLY data from ad server)// var clickUrl = ad.meta.adclick + encodeURIComponent(ad.fields.click || '');//// // Replace entire container content with proper structure// // Note: All product data (URL, logo, price) comes from shop, only adClick tracking comes from ad// container.innerHTML =// '<div class="ad-header">' +// '<span class="ad-label">Ad</span>' +// getDsaInfoHtml(dsaInfo) +// '</div>' +// '<a href="' + shopData.productUrl + '" ' +// 'class="brand-shop-button" ' +// 'target="_blank" ' +// 'rel="noopener nofollow sponsored" ' +// 'data-offer-type="ad" ' +// 'data-ad-id="' + (ad.meta.adid || 'unknown') + '" ' +// 'onclick="(function(){ console.log(\'[dlApi] Adclick:\', \'' + clickUrl + '\'); new Image().src = \'' + clickUrl + '\'; })();">' +// 'At <img src="' + shopData.shopLogo + '" ' +// 'alt="' + shopData.shopName + '" class="shop-logo" style="height: 16px; vertical-align: middle;"> ' +// 'for <span class="button-price">' + shopData.price + '</span>' +// '</a>';//// console.log('[Frontend Mode] Button rendered successfully');// }// });//// // Step 5: Handle "empty" event (no ad available for this slot)// slot.on('empty', function () {// console.log('[Brand Store] Slot empty - no ad available');// });//// // Step 6: Trigger ad request (sends all slots to ad server)// dlApi.fetch();
});
</script>