PageSourceSearch

https://bambuser.com/docs/assets/js/73b425b2.1cc09755.js

js bambuser.com collected 2026-09-24 08:28:23 UTC 83,282 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkbambuser_docs||=[]).push([[2072],{44361(e,n,t){t.r(n),t.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>g,frontMatter:()=>d,metadata:()=>o,toc:()=>h});var r=t.cw(function(e,n){const r=t.p+"assets/images/cart-display-cart-4287a8f1b91be8602de367eb573f1f33.png";t.d(n,["A",0,r])});const o=JSON.parse('{"id":"shopifyGuide","title":"Shopify + Video Consultation","description":"Add the Calls Widget to Shopify: sample wiring Overlay Widget, provide-product-data, provide-search-data, AJAX cart sync; CSP frame-ancestors limits.","source":"@site/video-consultation/shopifyGuide.mdx","sourceDirName":".","slug":"/shopify-guide","permalink":"/docs/video-consultation/shopify-guide","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"shopifyGuide","title":"Shopify + Video Consultation","description":"Add the Calls Widget to Shopify: sample wiring Overlay Widget, provide-product-data, provide-search-data, AJAX cart sync; CSP frame-ancestors limits.","sidebar_label":"Shopify","slug":"shopify-guide"},"sidebar":"someSidebars","previous":{"title":"Custom SAML","permalink":"/docs/video-consultation/sso-custom-saml"}}');var a=t(74848),i=t(28453),s=t(4865),c=t(19365),u=t(86025);const d={id:"shopifyGuide",title:"Shopify + Video Consultation",description:"Add the Calls Widget to Shopify: sample wiring Overlay Widget, provide-product-data, provide-search-data, AJAX cart sync; CSP frame-ancestors limits.",sidebar_label:"Shopify",slug:"shopify-guide"},l=void 0,p={},h=[{value:"Integration",id:"integration",level:2},{value:"Sample integration code",id:"sample-integration-code",level:3},{value:"1. Organization ID (mandatory)",id:"1-organization-id-mandatory",level:4},{value:"2. Queue ID (optional)",id:"2-queue-id-optional",level:4},{value:"3. Embed Source URL (optional)",id:"3-embed-source-url-optional",level:4},{value:"Adding integration code to your Shopify",id:"adding-integration-code-to-your-shopify",level:3},{value:"The result",id:"the-result",level:3},{value:"Shopper Events Tracking",id:"shopper-events-tracking",level:2},{value:"Prerequisites",id:"prerequisites",level:3},{value:"Implement via Shopify Custom Pixel",id:"implement-via-shopify-custom-pixel",level:3},{value:"Pixel tracking coverage",id:"pixel-tracking-coverage",level:3},{value:"Shopify Pixel Privacy and Attribution",id:"shopify-pixel-privacy-and-attribution",level:3},{value:"Verification",id:"verification",level:3},{value:"Troubleshooting",id:"troubleshooting",level:3},{value:"Enable Debug Mode",id:"enable-debug-mode",level:4},{value:"Test using Shopify Pixel Helper",id:"test-using-shopify-pixel-helper",level:4},{value:"Potential Tracking Issues",id:"potential-tracking-issues",level:4}];function m(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",h3:"h3",h4:"h4",hr:"hr",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components},{Details:o,Head:d}=n;return o||b("Details",!0),d||b("Head",!0),(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(d,{children:[(0,a.jsx)("meta",{name:"generator",content:"tech-documentation-engine"}),(0,a.jsx)("meta",{name:"description",content:"Some description"})]}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"This is our recommended approach for integrating to Shopify. You may of course modify the code based on your wishes following the general documentation."})}),"\n",(0,a.jsx)(n.hr,{}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.img,{alt:"img",src:t(35112).A+"",width:"215",height:"61"})}),"\n",(0,a.jsx)(n.p,{children:"In order to integrate the Bambuser Calls Widget into a Shopify store, you will need to add Bambuser related integration code to your Shopify website. The integration code can be added by adding script tags to your page content or theme codes."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Steps:"})}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Integrate Bambuser Calls Widget and Overlay Widget ",(0,a.jsx)(n.a,{href:"/video-consultation/initial-setup/#initiate-the-calls-widget",children:"\ud83d\udd17"})]}),"\n",(0,a.jsxs)(n.li,{children:["Add Conversion Tracking ",(0,a.jsx)(n.a,{href:"#shopper-events-tracking",children:"\ud83d\udd17"})]}),"\n"]}),"\n",(0,a.jsx)(n.admonition,{title:"Compatibility",type:"warning",children:(0,a.jsxs)(n.p,{children:["Due to Shopify's technical limitations, the ",(0,a.jsx)(n.a,{href:"/video-consultation/cobrowsing",children:"Cobrowsing"}),", ",(0,a.jsx)(n.a,{href:"/video-consultation/surf-while-in-queue",children:"Surf While in Queue"})," and ",(0,a.jsx)(n.a,{href:"/video-consultation/floating-player",children:"Miniplayer"})," features are ",(0,a.jsx)(n.strong,{children:"not"})," compatible with Shopify stores. You can read more about the compatibility ",(0,a.jsx)(n.a,{href:"/video-consultation/miniplayer-compatibility",children:"here"}),"."]})}),"\n",(0,a.jsxs)(n.admonition,{title:"Enabling Co-browsing, Miniplayer and related features on Shopify: request a CSP change from Shopify",type:"info",children:[(0,a.jsxs)(n.p,{children:["By default, Shopify stores often send a security header ",(0,a.jsx)(n.strong,{children:"Content-Security-Policy: frame-ancestors 'none'"}),', which tells the browser the site must never be displayed inside an iframe. Bambuser\'s advanced interactive features (Co-browsing "Bring to Me", Miniplayer, Surf While in Queue, minimized calls widget, in-call navigation) work by layering your site within the call interface, so this 
1setting blocks them.']}),(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"You can enable these features"})," by asking Shopify to update this header. As a Shopify merchant, you (or your development team) can request the change from ",(0,a.jsx)(n.strong,{children:"Shopify Plus Support"})," or your ",(0,a.jsx)(n.strong,{children:"Shopify Account Manager"}),". See the ",(0,a.jsx)(n.a,{href:"/live/shopify-csp-request",children:"email template for Shopify Support"})," you can send. If Shopify approves the change, let us know so we can enable Co-browsing for your account."]})]}),"\n",(0,a.jsx)(n.h2,{id:"integration",children:"Integration"}),"\n",(0,a.jsx)(n.h3,{id:"sample-integration-code",children:"Sample integration code"}),"\n",(0,a.jsx)(n.p,{children:"Below you find a sample integration code that implements:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.strong,{children:"Overlay Widget"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.strong,{children:"Provide Product Data"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.strong,{children:"Provide Search Data"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.strong,{children:"Cart Integration"})}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"This sample code works on almost all Shopify stores that have standard product models without any changes required. However, if you have a special product setup, you may need to modify the code to justify that with your case."}),"\n",(0,a.jsx)(n.p,{children:"Note that before you will have to modify your embed code a bit in order to make it work as you want"}),"\n",(0,a.jsxs)("blockquote",{children:[(0,a.jsx)(n.h4,{id:"1-organization-id-mandatory",children:"1. Organization ID (mandatory)"}),(0,a.jsxs)(n.p,{children:["For the Calls Widget initiation to work, you will need to enter your own organization ID (",(0,a.jsx)(n.code,{children:"orgId"}),"). That means that you need to have access to Bambuser Workspace."]}),(0,a.jsxs)(n.p,{children:["Your orgId can be found in Bambuser workspace URL (",(0,a.jsx)(n.code,{children:"https://lcx.bambuser.com/o/[orgId]/..."}),")"]}),(0,a.jsx)(n.hr,{}),(0,a.jsx)(n.h4,{id:"2-queue-id-optional",children:"2. Queue ID (optional)"}),(0,a.jsx)(n.p,{children:"Bambuser Video Consultation product comes with a built-in queueing system. Workspace admins can configure different queues in Bambuser workspace. Each queue will get its own unique ID when created."}),(0,a.jsxs)(n.p,{children:["Each queue will have a ",(0,a.jsx)(n.code,{children:"Queue ID"}),", which Video Consultation Admin can find in Queue Settings within Bambuser Workspace."]}),(0,a.jsx)(n.p,{children:"If you want to redirect your end customers to a different queue that the default one, you will need to additionaliy specify that in the below code sample."}),(0,a.jsx)(n.hr,{}),(0,a.jsx)(n.h4,{id:"3-embed-source-url-optional",children:"3. Embed Source URL (optional)"}),(0,a.jsx)(n.p,{children:"Embed source URL is different depending on location of servers that your workspace is set up on. Note that below sample code is assuming your workspace is setup on Global Servers."}),(0,a.jsxs)(s.A,{defaultValue:"global-servers",values:[{label:"Global Servers",value:"global-servers"},{label:"EU Servers",value:"eu-servers"}],children:[(0,a.jsx)(c.A,{value:"global-servers",children:(0,a.jsxs)(n.p,{children:["Use Global servers URL ",(0,a.jsx)("code",{children:(0,a.jsx)(n.a,{href:"https://one-to-one.bambuser.com/embed.js",children:"https://one-to-one.bambuser.com/embed.js"})})," if the login to your Bambuser workspace is on following link: ",(0,a.jsx)("a",{href:"https://lcx.bambuser.com/",children:(0,a.jsx)(n.a,{href:"https://lcx.bambuser.com/",children:"https://lcx.bambuser.com/"})}),". If that is the case, you do not need to make those changes in the code below."]})}),(0,a.jsx)(c.A,{value:"eu-servers",children:(0,a.jsxs)(n.p,{children:["Use European servers URL ",(0,a.jsx)("code",{children:(0,a.jsx)(n.a,{href:"https://one-to-one.bambuser.com/eu/embed.js",children:"https://one-to-one.bambuser.com/eu/embed.js"})})," if the login to your Bambuser workspace is on following link: ",(0,a.jsx)("a",{href:"https://lcx-eu.bambuser.com/",children:(0,a.jsx)(n.a,{href:"https://lcx-eu.bambuser.com/",children:"https://lcx-eu.bambuser.com/"})}),". If this is the case, make sure to modify this in the code below."]})})]})]}),"\n",(0,a.jsxs)(o,{style:{cursor:"pointer"},children:[(0,a.jsx)("summary",{children:(0,a.jsx)("b",{children:"SAMPLE INTEGRATION CODE"})}),(0,a.jsxs)(s.A,{defaultValue:"sourceCode",values:[{label:"Cart integration code",value:"sourceCode"},{label:"Sample product data",value:"productData"}],children:[(0,a.jsx)(c.A,{value:"sourceCode",children:(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-html",metastring:"{123,124,291}",children:"<script>\n// ====================================== Define constants ========================================\n// Reduces server calls if a product has a crazy number of images.\nconst MAX_IMAGES_COUNT_ = 20;\n\n// Extracts product handle (and variantID) from the product URL\n// Group[1] => product handle  (Group[3] => variant ID)\nconst SHOPIFY_PRODUCT_URL_HANDLE_REGEX_ = /\\/products\\/(.[\\w\\d-+]+)/;\n\n// ====================================== General helper methods for Shopify  ========================================\n// Sometimes image URLs miss the protocol at the beginning\n/
1/ E.g. '//cdn.shopify.com/s/files/.../image.jpg'\nwindow.urlSanitizer_ = (url) => {\n  if (typeof url === 'string') {\n    if (url.startsWith('//')) return `https:${url}`;\n    else if (url.toLocaleLowerCase().startsWith('http')) return url;\n    else console.log(`Not a valid URL: ${url}`);\n  } else console.log(`Not a valid URL: ${url}`);\n  return null;\n};\n\nwindow.getActiveCurrency = () => {\n  // 1. From the Shopify object\n  const getCurrencyFromContext = Shopify?.currency?.active;\n  if (getCurrencyFromContext) return getCurrencyFromContext;\n\n  // 2. From the 'cart_currency' cookie\n  const currencyFromCookies = document.cookie\n    .split('; ')\n    .find((row) => row.startsWith('cart_currency'))\n    ?.split('=')[1];\n  if (currencyFromCookies) return currencyFromCookies;\n\n  // If no currency found\n  return null;\n};\n\n// ====================================== Shopify Ajax API Helper methods ========================================\nconst oneToOneStoreApi = {};\n\noneToOneStoreApi.getProductByUrl = (url) => {\n  const handle = SHOPIFY_PRODUCT_URL_HANDLE_REGEX_.exec(url);\n  return fetch('/products/' + handle[1] + '.js', {\n    method: 'GET',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n  }).then((resp) => resp.json());\n};\n\noneToOneStoreApi.getProductByHandle = (handle) => {\n  return fetch('/products/' + handle + '.js', {\n    method: 'GET',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n  }).then((resp) => resp.json());\n};\n\noneToOneStoreApi.getRelatedProducts = (productId) => {\n  return fetch(\n    window.Shopify.routes.root +\n      'recommendations/products.json?product_id=' +\n      [productId] +\n      '&limit=5'\n  ).then((response) => response.json());\n};\n\noneToOneStoreApi.getProduct = (ref) => {\n  if (SHOPIFY_PRODUCT_URL_HANDLE_REGEX_.test(ref)) {\n    return oneToOneStoreApi.getProductByUrl(ref).then((productObject) => {\n      return oneToOneStoreApi\n        .getRelatedProducts(productObject.id)\n        .then((result) => {\n          productObject['relatedProducts'] = result.products;\n          return productObject;\n        });\n    });\n  } else {\n    return oneToOneStoreApi\n      .getProductByHandle(ref.split(':')[1])\n      .then((productObject) => {\n        return oneToOneStoreApi\n          .getRelatedProducts(productObject.id)\n          .then((result) => {\n            productObject['relatedProducts'] = result.products;\n            return productObject;\n          });\n      });\n  }\n};\n\noneToOneStoreApi.predictiveSearch = (query) =>\n  fetch(\n    '/search/suggest.json?q=' +\n      query +\n      '&resources[type]=product&resources[options][fields]=title,product_type,variants.title,variants.sku,variants.barcode,vendor',\n    {\n      method: 'GET',\n      headers: {\n        'Content-Type': 'application/json',\n      },\n    }\n  ).then((res) => res.json());\n\noneToOneStoreApi.addToCart = (itemId) =>\n  fetch('/cart/add.js', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      items: [\n        {\n          quantity: 1,\n          id: itemId.split(':')[0],\n        },\n      ],\n    }),\n  }).then((resp) => resp.json());\n\noneToOneStoreApi.updateItemInCart = (itemId, quantity) =>\n  fetch('/cart/update.js', {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      updates: {\n        [itemId.split(':')[0]]: quantity,\n      },\n    }),\n  }).then((resp) => resp.json());\n\noneToOneStoreApi.getCartState = () =>\n  fetch('/cart.js', {\n    method: 'GET',\n    headers: {\n      'Content-Type': 'application/json',\n    },\n  }).then((resp) => resp.json());\n\n// ===================================== Bambuser OneToOneReady Handler ==========================================\nfunction onBambuserOneToOneReady() {\n  // This method will be invoked when embed.js has loaded and Bambuser\n  // Calls API is available.\n  // Creating an instance directly will allow to detect connect links\n  // that will automatically open the Live Meeting overlay on page load.\n  let oneToOneEmbed = new BambuserOneToOneEmbed({\n    orgId: '[ORG_ID]',\n    // queue: '[QUEUE_ID_HERE]', // if not specified, default queue configured from the workspace will get applied\n    triggers: [\n      'smart', // activate the overlay widget\n      'connect-link', // activate the connect link - Unrelated to OLW, yet mandatory\n    ],\n    smartVariantOverride: 'Video', // force variant 'Video'. Other options are 'Avatar' and 'Side dock'\n    popupTimeoutSeconds: '5', // after how many seconds OLW appears on the first time page load (default = 60)\n  });\n\n  //------------------------------------ Provide Product Data ----------------------------------\n  oneToOneEmbed.on('provide-product-data', (event) =>
1 {\n    event.products.forEach(({ ref, id }) => {\n      oneToOneStoreApi.getProduct(ref).then((item) => {\n        oneToOneEmbed.updateProduct(id, (productFactory) =>\n          productFactory\n            .currency(getActiveCurrency() || 'USD')\n            .locale('en-US')\n            .product((detailFactory) =>\n              detailFactory\n                .name(item.title)\n                .description(item.description)\n                .sku(item.id + ':' + item.handle)\n                .attributes((attribute) => {\n                  if (item.variants && item.variants.length > 1) {\n                    return item.options.map((attr) =>\n                      attribute(attr.name)\n                        .name(attr.name)\n                        .options((option) =>\n                          attr.values.map((optionName) =>\n                            option(optionName).name(optionName)\n                          )\n                        )\n                    );\n                  } else {\n                    return [];\n                  }\n                })\n                .defaultVariationIndex()\n                .variations((variationFactory) => {\n                  return item.variants.map((variation) =>\n                    variationFactory()\n                      // If there is more than one variation, append variation title to the item title\n                      .name(\n                        variation.title === 'Default Title'\n                          ? item.title\n                          : item.title + ` (${variation.title})`\n                      )\n                      .sku(variation.id + ':' + item.handle)\n                      .inStock(variation.available)\n                      .imageUrls([\n                        // Adding the featured image of the chosen variation\n                        // (if existed) at the beginning of the images array\n                        ...(variation.featured_image\n                          ? [variation.featured_image.src]\n                          : []),\n                        // Adding product images\n                        ...item.images\n                          .slice(0, MAX_IMAGES_COUNT_ - 1)\n                          .map((url) => urlSanitizer_(url))\n                          .filter((url) => typeof url === 'string')\n                          .filter(\n                            (image) =>\n                              image !==\n                              (variation.featured_image !== null\n                                ? variation.featured_image.src\n                                : null)\n                          ),\n                      ])\n                      .price((priceFactory) =>\n                        priceFactory\n                          .original(variation.compare_at_price / 100)\n                          .current(variation.price / 100)\n                      )\n                      .attributes((attribute) => {\n                        if (item.variants && item.variants.length > 1) {\n                          return item.options.map((attr) =>\n                            attribute(\n                              attr.name,\n                              variation['option' + attr.position]\n                            )\n                          );\n                        } else {\n                          return [];\n                        }\n                      })\n                      .relatedProducts((relatedProductFactory) =>\n                        item.relatedProducts.map((relatProd) =>\n                          relatedProductFactory()\n                            .title(relatProd.title)\n                            .sku(relatProd.id + ':' + relatProd.handle)\n                            .imageUrl(urlSanitizer_(relatProd.featured_image))\n                            .price((priceFactory) =>\n                              priceFactory\n                                .original(relatProd.compare_at_price / 100)\n                                .current(relatProd.price / 100)\n                            )\n                        )\n                      )\n                  );\n                })\n            )\n        );\n      });\n    });\n  });\n\n  // --------------------------------- Cart Integrations --------------------------------------\n  oneToOneEmbed.on('should-add-item-to-cart', (a
1ddedItem, callback) => {\n    oneToOneStoreApi\n      .addToCart(addedItem.sku)\n      .then((res) => {\n        if (res.items) {\n          callback(true);\n          console.log('Item added successfully!');\n        } else if (res.description && res.description.includes('sold out')) {\n          callback({\n            success: false,\n            reason: 'out-of-stock',\n          });\n        } else callback(false);\n      })\n      .catch((error) => {\n        callback(false);\n        console.error('Add to cart error! ', error);\n      });\n  });\n\n  oneToOneEmbed.on('should-update-item-in-cart', (updatedItem, callback) => {\n    oneToOneStoreApi\n      .updateItemInCart(updatedItem.sku, updatedItem.quantity)\n      .then((res) => {\n        if (res.items) {\n          callback(true);\n          console.log('Item updated successfully!');\n        } else callback(false);\n      })\n      .catch((error) => {\n        callback(false);\n        console.error('Error on updating item! ', error);\n      });\n  });\n\n  oneToOneEmbed.on('goto-checkout', () => {\n    window.open(window.location.origin + '/cart', '_blank');\n  });\n\n  // --------------------------------- Provide Search Data -------------------------------------\n  oneToOneEmbed.on('provide-search-data', (searchRequest, searchResponse) => {\n    const { term, page } = searchRequest;\n\n    // ==== 1. If search by inserting a product URL (check using regex) ====\n    const matchGroups = SHOPIFY_PRODUCT_URL_HANDLE_REGEX_.exec(term);\n    // If term contains a URL matches[1] holds the product handle.\n    if (matchGroups && typeof matchGroups[1] == 'string' && matchGroups[1].length > 0) {\n      const productHandle = matchGroups[1];\n      oneToOneStoreApi\n        .getProductByHandle(productHandle)\n        .then((productData) => {\n          searchResponse((responseFactory) => {\n            return responseFactory\n              .products((productFactory) => {\n                return [\n                  productFactory()\n                    .name(productData.title)\n                    .imageUrl(urlSanitizer_(productData.featured_image))\n                    .sku(productData.id + ':' + productData.handle)\n                    .price((priceFactory) =>\n                      priceFactory\n                        .current(productData.price / 100)\n                        .original(productData.compare_at_price_max / 100)\n                    ),\n                ];\n              })\n              .currency(getActiveCurrency())\n              .locale('en-US');\n          });\n        });\n      return;\n    }\n\n    // ==== 2. If searched by a title, product_type, variants.title, variants.sku, variants.barcode, or vendor ====\n    oneToOneStoreApi.predictiveSearch(term).then((data) => {\n      if (data.status && data.status === 422) throw new Error(data.message);\n      searchResponse((responseFactory) => {\n        return responseFactory\n          .products((productFactory) => {\n            const results = data.resources.results.products.map(\n              (productData) => {\n                return productFactory()\n                  .name(productData.title)\n                  .imageUrl(productData.image)\n                  .sku(productData.id + ':' + productData.handle)\n                  .price((priceFactory) =>\n                    priceFactory\n                      .current(parseInt(productData.price))\n                      .original(parseInt(productData.compare_at_price_max))\n                  );\n              }\n            );\n            return results;\n          })\n          .currency(getActiveCurrency())\n          .locale('en-US');\n      });\n    });\n  });\n}\n<\/script>\n<script id=\"bambuser-one-to-one\" async src=\"https://one-to-one.bambuser.com/embed.js\"><\/script>\n"})})}),(0,a.jsx)(c.A,{value:"productData",children:(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-json",metastring:'title="https://shopify.bambuser.shop/products/bambuser-hoodie.js"',children:'{\n"id":3780507959318,\n"title":"Stratus Backpack",\n"handle":"stratus-backpack",\n"description":"\\u003cmeta charset=\\"utf-8\\"\\u003e\\n\\u003cp\\u003e\\u003ci\\u003eThis is a demonstration store. You can purchase products like this from\\u003cspan\\u003e \\u003ca href=\\"https://caraasport.com\\" title=\\"Caraa\\"\\u003eCaraa\\u003c/a\\u003e\\u003c/span\\u003e.\\u003c/i\\u003e\\u003c/p\\u003e\\n\\u003cp\\u003e\\u003cspan\\u003eLight as air. The Stratus is encased in cloud-like waterproof nylon and features exterior + interior pockets for your water bottle, umbrella, and laptop. Reach around to the side for on-the-go access to the main compartment. Collapse it for easy travel. Your backpack just got an upgrade.\\u003c/span\\u003e\\u003c/p\\u003e",\n"published_at":"2020-03-09T16:13:44-04:00",\n"created_at":"2020-03-09T16:13:50-04:00",\n"vendor":"debut-rich-media-demo",\n"type":"",\n"tags":[],\n"price":17500,\n"price_min":17500,\n"price_max":19500,\n"available":true,\n"price_varies":true,\n"compare_at_price":null,\n"compare_at_price_min":0,\n"compare_at_price_max":0,\n"compare_at_price_varies":false,\n"variants":[\n  {\n    "id":29013951414294,\n    "title":"Navy",\n    "option1":"Navy",\n    "option2":null,\n    "option3":null,\n    "sku":"",\n    "requires_shipping":true,\n    "taxable":true,\n    "featured_image":{\n      "id":4721544527894,\n      "product_id":3780507959318,\n      "position":1,\n      "created_at":"2020-03-09T16:13:51-04:00",\n      "updated_at":"2020-03-09T16:13:51-04:00",\n      "alt":null,\n      "width":1058,\n      "height":1039,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/main_backpack.png?v=1583784831",\n      "variant_ids":[\n        29013951414294\n      ]\n    },\n    "available":true,\n    "name":"Stratus Backpack - Navy",\n    "public_title":"Navy",\n    "options":[\n      "Navy"\n    ],\n    "price":17500,\n    "weight":0,\n    "compare_at_price":null,\n    "inventory_management":"shopify",\n    "barcode":"",\n    "featured_media":{\n      "alt":null,\n      "id":1459887833110,\n      "position":1,\n      "preview_image":{\n        "aspect_ratio":1.018,\n        "height":1039,\n        "width":1058,\n        "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/main_backpack.png?v=1583784831"\n      }\n    }\n  },\n  {\n    "id":29013951447062,\n    "title":"Black",\n    "option1":"Black",\n    "option2":null,\n    "option3":null,\n    "sku":"",\n    "requires_shipping":true,\n    "taxable":true,\n    "featured_image":{\n      "id":4721544658966,\n      "product_id":3780507959318,\n      "position":4,\n      "created_at":"2020-03-09T16:13:51-04:00",\n      "updated_at":"2020-03-11T10:45:10-04:00",\n      "alt":null,\n      "width":1058,\n      "height":1039,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_03.png?v=1583937910",\n      "variant_ids":[\n        29013951447062\n      ]\n    },\n    "available":true,\n    "name":"Stratus Backpack - Black",\n    "public_title":"Black",\n    "options":[\n      "Black"\n    ],\n    "price":19500,\n    "weight":0,\n    "compare_at_price":null,\n    "inventory_management":"shopify",\n    "barcode":"",\n    "featured_media":{\n      "alt":null,\n      "id":1459887964182,\n      "position":5,\n      "preview_image":{\n        "aspect_ratio":1.018,\n        "height":1039,\n        "width":1058,\n        "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_03.png?v=1583784831"\n      }\n    }\n  },\n  {\n    "id":29013951479830,\n    "title":"Red",\n    "option1":"Red",\n    "option2":null,\n    "option3":null,\n    "sku":"",\n    "requires_shipping":true,\n    "taxable":true,\n    "featured_image":null,\n    "available":false,\n    "name":"Stratus Backpack - Red",\n    "public_title":"Red",\n    "options":[\n      "Red"\n    ],\n    "price":17500,\n    "weight":0,\n    "compare_at_price":null,\n    "inventory_management":"shopify",\n    "barcode":""\n  }\n],\n"images":[\n  "//cdn.shopify.com/s/files/1/0260/1061/5830/products/main_backpack.png?v=1583784831",\n  "//cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_01.png?v=1583937910",\n  "//cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_02.png?v=1583937910",\n  "//cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_03.png?v=1583937910"\n],\n"featured_image":"//cdn.shopify.com/s/files/1/0260/1061/5830/products/main_backpack.png?v=1583784831",\n"options":[\n  {\n    "name":"Color",\n    "position":1,\n    "values":[\n      "Navy",\n      "Black",\n      "Red"\n    ]\n  }\n],\n"url":"/products/stratus-backpack",\n"media":[\n  {\n    "alt":null,\n    "id":1459887833110,\n    "position":1,\n    "preview_image":{\n      "aspect_ratio":1.018,\n      "height":1039,\n      "width":1058,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/main_backpack.png?v=1583784831"\n    },\n    "aspect_ratio":1.018,\n    "height":1039,\n    "media_type":"image",\n    "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/main_backpack.png?v=1583784831",\n    "width":1058\n  },\n  {\n    "alt":null,\n    "id":1461301641238,\n    "position":2,\n    "preview_image":{\n      "aspect_ratio":1.0,\n      "height":1024,\n      "width":1024,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/StratusBag.
1jpg?v=1583937876"\n    },\n    "media_type":"model",\n    "sources":[\n      {\n        "format":"glb",\n        "mime_type":"model/gltf-binary",\n        "url":"https://model3d.shopifycdn.com/models/o/5f491746c036baf6/StratusBag.glb"\n      },\n      {\n        "format":"usdz",\n        "mime_type":"model/vnd.usdz+zip",\n        "url":"https://model3d.shopifycdn.com/models/o/c3b4ad9cbc191499/StratusBag.usdz"\n      }\n    ]\n  },\n  {\n    "alt":null,\n    "id":1459887865878,\n    "position":3,\n    "preview_image":{\n      "aspect_ratio":1.018,\n      "height":1039,\n      "width":1058,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_01.png?v=1583784831"\n    },\n    "aspect_ratio":1.018,\n    "height":1039,\n    "media_type":"image",\n    "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_01.png?v=1583784831",\n    "width":1058\n  },\n  {\n    "alt":null,\n    "id":1459887931414,\n    "position":4,\n    "preview_image":{\n      "aspect_ratio":1.018,\n      "height":1039,\n      "width":1058,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_02.png?v=1583784831"\n    },\n    "aspect_ratio":1.018,\n    "height":1039,\n    "media_type":"image",\n    "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_02.png?v=1583784831",\n    "width":1058\n  },\n  {\n    "alt":null,\n    "id":1459887964182,\n    "position":5,\n    "preview_image":{\n      "aspect_ratio":1.018,\n      "height":1039,\n      "width":1058,\n      "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_03.png?v=1583784831"\n    },\n    "aspect_ratio":1.018,\n    "height":1039,\n    "media_type":"image",\n    "src":"https://cdn.shopify.com/s/files/1/0260/1061/5830/products/stratus_03.png?v=1583784831",\n    "width":1058\n  }\n]\n}\n'})})})]})]}),"\n",(0,a.jsx)(n.hr,{}),"\n",(0,a.jsx)(n.admonition,{title:"Further customization",type:"note",children:(0,a.jsxs)(n.p,{children:["To learn more about different ways to initiate the Calls Widget or different embedding options, read the ",(0,a.jsx)(n.a,{href:"/video-consultation/initial-setup",children:"Initial Setup"})," guide."]})}),"\n",(0,a.jsx)(n.p,{children:"In the next section, we explain how you should add the integration code into your Shopify store."}),"\n",(0,a.jsx)(n.h3,{id:"adding-integration-code-to-your-shopify",children:"Adding integration code to your Shopify"}),"\n",(0,a.jsx)(n.p,{children:"There are different ways to add the integration code to the Shopify store. You need to make sure that your integration code is included in all pages that you want have the Calls Widget embedded on."}),"\n",(0,a.jsxs)(s.A,{children:[(0,a.jsxs)(c.A,{value:"all_pages",label:"All pages (Recommended)",default:!0,children:[(0,a.jsx)("b",{children:"Steps:"}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:[" In your Shopify theme codes, create a snippet and add the ",(0,a.jsx)("a",{href:"#sample-integration-code",children:"sample integration code there."})]}),(0,a.jsxs)("li",{children:["Assuming that you named the snippet ",(0,a.jsx)("code",{children:"bambuser-call-widget.liquid"}),", add this snippet to the ",(0,a.jsx)("code",{children:"theme.liquid"})," layout as below:"]})]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-jsx",metastring:'title="theme.liquid"',children:"  {% render 'bambuser-call-widget'%}\n"})})]}),(0,a.jsxs)(c.A,{value:"specific_pages",label:"Specific page/s",children:[(0,a.jsx)("b",{children:"Steps:"}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:[" In your Shopify theme codes, create a snippet and add the ",(0,a.jsx)("a",{href:"#sample-integration-code",children:"sample integration code there."})]}),(0,a.jsxs)("li",{children:["Assuming that you named the snippet ",(0,a.jsx)("code",{children:"bambuser-call-widget.liquid"}),", add this snippet to the ",(0,a.jsx)("code",{children:"theme.liquid"})," layout as below:"]})]}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-jsx",metastring:'title="page.yourTemplateName.liquid"',children:"  {% render 'bambuser-call-widget'%}\n"})})]})]}),"\n",(0,a.jsx)(n.h3,{id:"the-result",children:"The result"}),"\n",(0,a.jsx)(n.p,{children:"Once you added the code snippet to your page/s, you should be seeing the following behavior:"}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsx)(n.li,{children:"Overlay Widget triggers on page load and can initialize Calls Widget"}),"\n",(0,a.jsx)(n.li,{children:"Calls Widget appears in the middle of the screen"}),"\n",(0,a.jsx)(n.li,{children:"You can call to the queue"}),"\n",(0,a.jsx)(n.li,{children:"Agent can pickup the call from Bambuser Agent Tool (Desktop/App)"}),"\n",(0,a.jsx)(n.li,{children:"Agent can search for products in the call"}),"\n",(0,a.jsx)(n.li,{children:"Agent can pull the products to the call"}),"\n",(0,a.jsx)(n.li,{children:"Agent can compare the products"}),"\n",(0,a.jsx)(n.li,{children:"Agent can add product to the caller's cart"}),"\n",(0,a.jsx)(n.li,{children:"Both Agent and Caller can modify cart quantity in the call"}),"\n",(0,a.jsx)(n.li,{children:"Caller can go to checkout, which will open in the new tab"}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.img,{alt:"img",src:r().A+"",width:"1758",height:"1102"})}),"\n",(0,a.jsx)(n.hr,{}),"\n",(0,a.jsx)(n.h2,{id:"shopper-events-tracking",children:"Shopper Events Tracking"}),"\n",(0,a.jsxs)(n.p,{children:["Track shopper behavior and attribute sales to Bambuser Calls by implementing ",(0,a.jsx)(n.a,{href:"/video-consultation/shopper-events-tracking/",children:"Bambuser Shopper Events Tracking"})," solution as a ",(0,a.jsx)(n.a,{href:"https://help.shopify.com/en/manual/promoting-marketing/pixels/custom-pixels",children:"Shopify Custom Pixel"}),"."]}),"\n",(0,a.jsx)(n.h3,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"A Bambuser product (Live Shopping, Shoppable Video, or Video Consultation) integrated on your website"}),"\n",(0,a.jsxs)(n.li,{children:["Ensure your website's cookie consent mechanism allows ",(0,a.jsx)(n.a,{href:"/video-consultation/shopper-events-tracking/#cookies",children:"Bambuser cookies"})," ( ",(0,a.jsx)(n.code,{children:"_bamls_usid"}),", ",(0,a.jsx)(n.code,{children:"_bamls_seid"}),", ",(0,a.jsx)(n.code,{children:"_bamls_lcte"}),")"]}),"\n"]}),"\n",(0,a.jsx)(n.h3,{id:"implement-via-shopify-custom-pixel",children:"Implement via Shopify Custom Pixel"}),"\n",(0,a.jsx)(n.p,{children:"Here are the steps to implement Bambuser Shopper Events Tracking in your Shopify store using a Shopify Custom Pixel."}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsx)(n.li,{children:"Log in to your Shopify Admin workspace"}),"\n",(0,a.jsxs)(n.li,{children:["From the bottom-left select ",(0,a.jsx)(n.code,{children:"Settings"})]}),"\n",(0,a.jsxs)(n.li,{children:["Choose ",(0,a.jsx)(n.code,{children:"Customer events"})," from the left menu"]}),"\n",(0,a.jsxs)(n.li,{children:["From the top-right, click on  ",(0,a.jsx)(n.code,{children:"Add custom pixel"})]}),"\n",(0,a.jsxs)(n.li,{children:["Give it a name and click ",(0,a.jsx)(n.code,{children:"Add Pixel"})]}),"\n",(0,a.jsx)(n.li,{children:"You should see a code box. If there are some codes already in the code box please remove them."}),"\n",(0,a.jsxs)(n.li,{children:["Copy the code below ",(0,a.jsx)("sup",{children:"(Select Global/EU servers based on your account's region!)"}),", and paste it on the Custom Pixel code box."]}),"\n",(0,a.jsx)(n.li,{children:"Press Save and then press Connect"}),"\n"]}),"\n",(0,a.jsxs)(s.A,{defaultValue:"global-servers",values:[{label:"Global Servers",value:"global-servers"},{label:"EU Specific Servers",value:"eu-servers"}],children:[(0,a.jsxs)(c.A,{value:"global-servers",children:[(0,a.jsx)(n.admonition,{title:"Global Servers",type:"note",children:(0,a.jsxs)(n.p,{children:["Tracking library URL is different depending on location of servers that your workspace is set up on. Use Global servers URL ",(0,a.jsx)(n.code,{children:"https://cdn.liveshopping.bambuser.com/metrics/bambuser.min.js"})," if the login to your Bambuser workspace is on following link: ",(0,a.jsx)(n.a,{href:"https://lcx.bambuser.com/",children:"https://lcx.bambuser.com/"}),"."]})}),(0,a.jsxs)(o,{children:[(0,a.jsx)("summary",{children:(0,a.jsx)("b",{children:"Pixel Code"})}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-js",children:"// ---------------------------------------------------------\n// Bambuser Shopper Event Tracking - Shopify Pixels Implementation.\n// Shopify Admin -> Settings -> Customer events\n// Last updated: April 2026\n// ---------------------------------------------------------\n\n/**\n * ============================================\n * ========= Define C
1onstants =================\n * ============================================\n */\n\n// Change the value to true only if your Bambuser workspace's base URL matches lcx-eu.bambuser.com\nconst USE_EU_SERVER = false;\n\n// Debugging configuration\nconst ENABLE_DEBUGGING = false; // Set to true to enable debug logs\n\n// --- Bambuser Tracking Library ---\nconst BAMBUSER_SCRIPT_URL = {\n  EU: 'https://cdn.liveshopping.bambuser.com/metrics/bambuser-eu.min.js',\n  GLOBAL: 'https://cdn.liveshopping.bambuser.com/metrics/bambuser.min.js'\n};\nconst bambuserScriptSrc = USE_EU_SERVER ? BAMBUSER_SCRIPT_URL.EU : BAMBUSER_SCRIPT_URL.GLOBAL;\n\n/**\n * ============================================\n * ========= Define Helper Functions ==========\n * ============================================\n */\n\n// Debug logger helper\nfunction debugLog(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.log('\ud83d\udd35 [Bambuser Pixel]', ...args);\n  }\n}\n\n// Warning logger helper\nfunction debugWarn(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.warn('\u26a0\ufe0f [Bambuser Pixel]', ...args);\n  }\n}\n\n// Error logger helper\nfunction debugError(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.error('\u274c [Bambuser Pixel] ERROR:', ...args);\n  }\n}\n\n// Success logger helper\nfunction debugSuccess(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.log('\u2705 [Bambuser Pixel]', ...args);\n  }\n}\n\n\nlet bambuserScriptPromise = null;\n\n/**\n * Injects the Bambuser tracking script into the DOM.\n * @returns {Promise<void>} Resolves when the script has loaded, rejects on load error.\n */\nfunction loadBambuserScript() {\n  if (bambuserScriptPromise) {\n    return bambuserScriptPromise;\n  }\n\n  bambuserScriptPromise = new Promise((resolve, reject) => {\n    debugLog('\u23f3 Loading Bambuser tracking script...');\n    const bamSrcElm = document.createElement(\"script\");\n    bamSrcElm.src = bambuserScriptSrc;\n    bamSrcElm.onload = () => {\n      debugSuccess('\u2705 Bambuser script loaded successfully!');\n      resolve();\n    };\n    bamSrcElm.onerror = (error) => {\n      // Reset so a future call can retry the load\n      bambuserScriptPromise = null;\n      reject(error);\n    };\n    document.head.appendChild(bamSrcElm);\n  });\n\n  return bambuserScriptPromise;\n}\n\n/**\n * Tracks an event with Bambuser, loading the tracking script first if needed.\n * @param {string} eventType The name of the event (e.g., 'purchase').\n * @param {object} eventData The data payload for the event.\n * @returns {Promise<boolean>} Resolves to true if the event was tracked successfully, false otherwise.\n */\nasync function trackBambuserEvent(eventType, eventData) {\n  debugLog(`\ud83d\udd0d Attempting to track event: ${eventType}`, eventData);\n\n  if (!window._bambuser || typeof window._bambuser.track !== 'function') {\n    try {\n      await loadBambuserScript();\n    } catch (error) {\n      debugError('\u274c Failed to load Bambuser script:', error);\n      return false;\n    }\n  }\n\n  try {\n    debugLog(`\ud83d\udce4 Sending '${eventType}' event to Bambuser...`);\n    const result = await window._bambuser.track(eventType, eventData);\n\n    if (result?.success) {\n      debugSuccess(`\u2705 Successfully tracked event: ${eventType} - ${result.message}`);\n      return true;\n    } else {\n      debugError(`\u274c Failed to track event: ${eventType} - ${result?.message || 'Failed without error message'}`);\n      return false;\n    }\n  } catch (error) {\n    debugError(`\u274c Error tracking event '${eventType}':`, error);\n    return false;\n  }\n}\n\n/**\n * ============================================\n * ===== Track Bambuser Shopper events ========\n * ============================================\n * Tracked Bambuser Shopper events: \n *  - Purchase event\n *  - Add to cart event\n *  - Remove from cart event\n *  - Product viewed event\n * \n * Other available Bambuser Shopper Events could be tracked by the merchant separately. \n * Currently Shopify's Web Pixels API has no standard tracking event for:\n *  - Add to Wishlist\n *  - Remove from Wishlist\n *  - Refund\n * \n * More info: \n *  - https://bambuser.com/docs/live/shopper-events-tracking/\n */\n\n// ----------- 1. Purchase event -----------\n// Subscribe to the 'checkout_completed' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"checkout_completed\", async (event) => {\n  const checkout = event.data.checkout;\n\n  debugLog('\ud83d\uded2 Received checkout_completed event');\n\n  if (!checkout) {\n    debugError('Checkout data is missing in the event payload');\n    return;\n  }\n\n  if (!checkout.order) {\n    debugError('Order data is missing in the checkout object');\n    return;\n  }\n\n  debugLog('\ud83d\udce6 Processing order:', checkout.order.id);\n\n  const couponCodes = (checkout.discountApplications || [])\n    .map(discount => {\n      const code = discount?.title || 'unknown';\n      debugLog('\ud83c\udff7\ufe0f  Found discount application:', code);\n      return code;\n    })\n    .filter(Boolean)\n    .join(', ');\n\n  debugLog('Extracted coupon codes:', couponCodes || 'none');\n\n  debugLog(`\ud83d\udccb Processing ${checkout.lineItems?.length || 0} line items`);\n\n  // Construct the products array\n  const products = (checkout.lineItems || []).map((item, index) =>
1 {\n    debugLog(`   ${index + 1}.`, item?.title || 'unknown');\n    // Safely calculate line item discount total\n    const lineItemDiscountTotal = (item.discountAllocations || []).reduce((total, alloc, i) => {\n      const amount = parseFloat(alloc?.amount?.amount) || 0;\n      if (amount > 0) {\n        debugLog(`     \ud83d\udcb0 Discount ${i + 1}:`, `${amount} ${alloc?.amount?.currencyCode || ''}`);\n      }\n      return total + amount;\n    }, 0);\n\n    const pricePerUnit = parseFloat(item?.variant?.price?.amount) || 0;\n    const discountPerUnit = item.quantity > 0 ? (lineItemDiscountTotal / item.quantity) : 0;\n    const discountApp = item.discountAllocations?.discountApplication;\n    const isDiscountCode = discountApp?.type === \"DISCOUNT_CODE\";\n    const coupon = isDiscountCode ? discountApp.title : '';\n\n    debugLog(`     \ud83d\udcb5 Price: ${pricePerUnit} ${item?.variant?.price?.currencyCode || ''}`);\n    if (discountPerUnit > 0) {\n      debugLog(`     \ud83c\udff7\ufe0f  Discount: ${discountPerUnit} per unit`);\n    }\n    if (coupon) {\n      debugLog(`     \ud83c\udfab Coupon: ${coupon}`);\n    }\n\n    return {\n      id: item?.variant?.sku || item?.variant?.id || item?.variant?.product?.id || '',\n      name: item.title || '',\n      image: item?.variant?.image?.src || '',\n      price: pricePerUnit,\n      currency: item?.variant?.price?.currencyCode || '',\n      discount: discountPerUnit > 0 ? discountPerUnit : 0,\n      coupon: coupon,\n      quantity: item.quantity || 1,\n      brand: item?.variant?.product?.vendor || '',\n      category: item?.variant?.product?.type || '',\n      location: item?.variant?.product?.url || '',\n    };\n  });\n\n  // Construct transaction object\n  const transaction = {\n    id: checkout.order.id,\n    subtotal: parseFloat(checkout.subtotalPrice?.amount) || 0,\n    currency: checkout.currencyCode || '',\n    coupon: couponCodes,\n    discount: checkout.discountsAmount ? parseFloat(checkout.discountsAmount.amount) : 0,\n    total: parseFloat(checkout.totalPrice?.amount) || 0,\n    tax: parseFloat(checkout.totalTax?.amount) || 0,\n    shippingCost: parseFloat(checkout.shippingLine?.price?.amount) || 0,\n    shippingMethod: checkout.shippingLine?.title || '',\n  };\n\n  debugLog('\ud83d\udcb3 Transaction Summary:');\n  debugLog(`   \ud83c\udd94 Order ID: ${transaction.id}`);\n  debugLog(`   \ud83d\udcb0 Subtotal: ${transaction.subtotal} ${transaction.currency}`);\n  if (transaction.discount > 0) {\n    debugLog(`   \ud83c\udff7\ufe0f  Discount: -${transaction.discount} ${transaction.currency}`);\n  }\n  if (transaction.tax > 0) {\n    debugLog(`   \ud83d\udcdd Tax: ${transaction.tax} ${transaction.currency}`);\n  }\n  if (transaction.shippingCost > 0) {\n    debugLog(`   \ud83d\ude9a Shipping (${transaction.shippingMethod || 'Standard'}): ${transaction.shippingCost} ${transaction.currency}`);\n  }\n  debugLog(`   \ud83d\udcb5 Total: ${transaction.total} ${transaction.currency}`);\n\n  // Construct the final data payload for the Bambuser 'purchase' event.\n  const purchaseData = {\n    transaction: transaction,\n    products: products,\n  };\n\n  debugLog('\ud83d\ude80 Sending purchase event to Bambuser');\n  debugLog('\ud83d\udce4 Purchase data:', JSON.stringify(purchaseData, null, 2));\n\n  // Call the tracking function with the event name and the constructed data.\n  const success = await trackBambuserEvent('purchase', purchaseData);
1\n\n  if (success) {\n    debugSuccess('\u2705 Purchase event processed successfully! \ud83c\udf89');\n  } else {\n    debugError('\u274c Failed to process purchase event');\n  }\n});\n\n// ----------- 2. Add to cart event -----------\n// Subscribe to the 'product_added_to_cart' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"product_added_to_cart\", async (event) => {\n  const cartLine = event.data.cartLine;\n\n  debugLog('\ud83d\uded2 Received product_added_to_cart event');\n\n  if (!cartLine) {\n    debugError('Cart line data is missing in the event payload');\n    return;\n  }\n\n  const productVariant = cartLine.merchandise;\n  const product = productVariant?.product;\n\n  if (!product) {\n    debugError('Product data is missing in the cart line');\n    return;\n  }\n\n  debugLog(`\ud83d\udce6 Processing product: ${product.title || 'unknown'}`);\n\n  // Construct the product data in Bambuser format\n  const productData = {\n    id: productVariant?.sku || productVariant?.id || product?.id || '',\n    quantity: cartLine.quantity || 1,\n    name: productVariant?.title || product?.title || '',\n    image: productVariant?.image?.src || '',\n    price: parseFloat(productVariant?.price?.amount) || 0,\n    currency: productVariant?.price?.currencyCode || '',\n    brand: product?.vendor || '',\n    category: product?.type || '',\n    location: product?.url || ''\n  };\n\n  debugLog('\ud83d\udcca Product details:', {\n    id: productData.id,\n    name: productData.name,\n    quantity: productData.quantity,\n    price: `${productData.price} ${productData.currency}`,\n  });\n\n  // Call the tracking function with the event name and the constructed data.\n  const success = await trackBambuserEvent('add-to-cart', {\n    products: [productData]\n  });
1\n\n  if (success) {\n    debugSuccess('\u2705 Add to cart event processed successfully! \ud83c\udf89');\n  } else {\n    debugError('\u274c Failed to process add to cart event');\n  }\n});\n\n// ----------- 3. Update cart event -----------\n// Subscribe to the 'product_removed_from_cart' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"product_removed_from_cart\", async (event) => {\n  const cartLine = event.data.cartLine;\n\n  debugLog('\ud83d\uded2 Received product_removed_from_cart event');\n\n  if (!cartLine) {\n    debugError('Cart line data is missing in the event payload');\n    return;\n  }\n\n  const productVariant = cartLine.merchandise;\n  const product = productVariant?.product;\n\n  if (!product) {\n    debugError('Product data is missing in the cart line');\n    return;\n  }\n\n  debugLog(`\ud83d\udce6 Processing removed product: ${product.title || 'unknown'}`);\n\n  // Construct the product data in Bambuser format with quantity 0 to indicate removal\n  const productData = {\n    id: productVariant?.sku || productVariant?.id || product?.id || '',\n    // Better practice is to pass the current cart quantity of the product after removal but it is currently not provided in Shopify's Web Pixels API\n    quantity: 0, // Set quantity to 0 to indicate removal\n    name: productVariant?.title || product?.title || '',\n    image: productVariant?.image?.src || '',\n    price: parseFloat(productVariant?.price?.amount) || 0,\n    currency: productVariant?.price?.currencyCode || '',\n    brand: product?.vendor || '',\n    category: product?.type || '',\n    location: product?.url || ''\n  };\n\n  debugLog('\ud83d\udcca Removed product details:', {\n    id: productData.id,\n    name: productData.name,\n    price: `${productData.price} ${productData.currency}`,\n  });\n\n  // Call the tracking function with the update-cart event\n  const success = await trackBambuserEvent('update-cart', {\n    products: [productData]\n  });
1\n\n  if (success) {\n    debugSuccess('\u2705 Update cart (removal) event processed successfully! \ud83c\udf89');\n  } else {\n    debugError('\u274c Failed to process update cart (removal) event');\n  }\n});\n\n// ----------- 4. Product viewed event -----------\n// Subscribe to the 'product_viewed' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"product_viewed\", async (event) => {\n  const productVariant = event.data.productVariant;\n\n  debugLog('\ud83d\udc41\ufe0f  Received product_viewed event');\n\n  if (!productVariant) {\n    debugError('Product variant data is missing in the event payload');\n    return;\n  }\n\n  const product = productVariant?.product;\n\n  if (!product) {\n    debugError('Product data is missing in the product variant');\n    return;\n  }\n\n  debugLog(`\ud83d\udce6 Processing product view: ${product.title || 'unknown'}`);\n\n  // Construct the product data in Bambuser format\n  const productData = {\n    id: productVariant?.sku || productVariant?.id || product?.id || '',\n    quantity: 1, // As this is a required field, we pass 1\n    name: productVariant?.title || product?.title || '',\n    image: productVariant?.image?.src || '',\n    price: parseFloat(productVariant?.price?.amount) || 0,\n    currency: productVariant?.price?.currencyCode || '',\n    brand: product?.vendor || '',\n    category: product?.type || '',\n    location: product?.url || ''\n  };\n\n  debugLog('\ud83d\udcca Product view details:', {\n    id: productData.id,\n    name: productData.name,\n    price: `${productData.price} ${productData.currency}`,\n  });\n\n  // Call the tracking function with the product-view event\n  
1const success = await trackBambuserEvent('product-view', {\n    products: [productData]\n  });\n\n  if (success) {\n    debugSuccess('\u2705 Product view event processed successfully! \ud83d\udc41\ufe0f');\n  } else {\n    debugError('\u274c Failed to process product view event');\n  }\n});\n\n"})})]})]}),(0,a.jsxs)(c.A,{value:"eu-servers",children:[(0,a.jsx)(n.admonition,{title:"European Servers",type:"note",children:(0,a.jsxs)(n.p,{children:["Tracking library URL is different depending on location of servers that your workspace is set up on. Use European servers URL ",(0,a.jsx)(n.code,{children:"https://cdn.liveshopping.bambuser.com/metrics/bambuser-eu.min.js"})," if the login to your Bambuser workspace is on following link: ",(0,a.jsx)(n.a,{href:"https://lcx-eu.bambuser.com/",children:"https://lcx-eu.bambuser.com/"}),"."]})}),(0,a.jsxs)(o,{children:[(0,a.jsx)("summary",{children:(0,a.jsx)("b",{children:"Pixel Code"})}),(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-js",children:"// ---------------------------------------------------------\n// Bambuser Shopper Event Tracking - Shopify Pixels Implementation.\n// Shopify Admin -> Settings -> Customer events\n// Last updated: April 2026\n// ---------------------------------------------------------\n\n/**\n * ============================================\n * ========= Define C
1onstants =================\n * ============================================\n */\n\n// Change the value to true only if your Bambuser workspace's base URL matches lcx-eu.bambuser.com\nconst USE_EU_SERVER = true;\n\n// Debugging configuration\nconst ENABLE_DEBUGGING = false; // Set to true to enable debug logs\n\n// --- Bambuser Tracking Library ---\nconst BAMBUSER_SCRIPT_URL = {\n  EU: 'https://cdn.liveshopping.bambuser.com/metrics/bambuser-eu.min.js',\n  GLOBAL: 'https://cdn.liveshopping.bambuser.com/metrics/bambuser.min.js'\n};\nconst bambuserScriptSrc = USE_EU_SERVER ? BAMBUSER_SCRIPT_URL.EU : BAMBUSER_SCRIPT_URL.GLOBAL;\n\n/**\n * ============================================\n * ========= Define Helper Functions ==========\n * ============================================\n */\n\n// Debug logger helper\nfunction debugLog(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.log('\ud83d\udd35 [Bambuser Pixel]', ...args);\n  }\n}\n\n// Warning logger helper\nfunction debugWarn(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.warn('\u26a0\ufe0f [Bambuser Pixel]', ...args);\n  }\n}\n\n// Error logger helper\nfunction debugError(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.error('\u274c [Bambuser Pixel] ERROR:', ...args);\n  }\n}\n\n// Success logger helper\nfunction debugSuccess(...args) {\n  if (ENABLE_DEBUGGING) {\n    console.log('\u2705 [Bambuser Pixel]', ...args);\n  }\n}\n\n\nlet bambuserScriptPromise = null;\n\n/**\n * Injects the Bambuser tracking script into the DOM.\n * @returns {Promise<void>} Resolves when the script has loaded, rejects on load error.\n */\nfunction loadBambuserScript() {\n  if (bambuserScriptPromise) {\n    return bambuserScriptPromise;\n  }\n\n  bambuserScriptPromise = new Promise((resolve, reject) => {\n    debugLog('\u23f3 Loading Bambuser tracking script...');\n    const bamSrcElm = document.createElement(\"script\");\n    bamSrcElm.src = bambuserScriptSrc;\n    bamSrcElm.onload = () => {\n      debugSuccess('\u2705 Bambuser script loaded successfully!');\n      resolve();\n    };\n    bamSrcElm.onerror = (error) => {\n      // Reset so a future call can retry the load\n      bambuserScriptPromise = null;\n      reject(error);\n    };\n    document.head.appendChild(bamSrcElm);\n  });\n\n  return bambuserScriptPromise;\n}\n\n/**\n * Tracks an event with Bambuser, loading the tracking script first if needed.\n * @param {string} eventType The name of the event (e.g., 'purchase').\n * @param {object} eventData The data payload for the event.\n * @returns {Promise<boolean>} Resolves to true if the event was tracked successfully, false otherwise.\n */\nasync function trackBambuserEvent(eventType, eventData) {\n  debugLog(`\ud83d\udd0d Attempting to track event: ${eventType}`, eventData);\n\n  if (!window._bambuser || typeof window._bambuser.track !== 'function') {\n    try {\n      await loadBambuserScript();\n    } catch (error) {\n      debugError('\u274c Failed to load Bambuser script:', error);\n      return false;\n    }\n  }\n\n  try {\n    debugLog(`\ud83d\udce4 Sending '${eventType}' event to Bambuser...`);\n    const result = await window._bambuser.track(eventType, eventData);\n\n    if (result?.success) {\n      debugSuccess(`\u2705 Successfully tracked event: ${eventType} - ${result.message}`);\n      return true;\n    } else {\n      debugError(`\u274c Failed to track event: ${eventType} - ${result?.message || 'Failed without error message'}`);\n      return false;\n    }\n  } catch (error) {\n    debugError(`\u274c Error tracking event '${eventType}':`, error);\n    return false;\n  }\n}\n\n/**\n * ============================================\n * ===== Track Bambuser Shopper events ========\n * ============================================\n * Tracked Bambuser Shopper events: \n *  - Purchase event\n *  - Add to cart event\n *  - Remove from cart event\n *  - Product viewed event\n * \n * Other available Bambuser Shopper Events could be tracked by the merchant separately. \n * Currently Shopify's Web Pixels API has no standard tracking event for:\n *  - Add to Wishlist\n *  - Remove from Wishlist\n *  - Refund\n * \n * More info: \n *  - https://bambuser.com/docs/live/shopper-events-tracking/\n */\n\n// ----------- 1. Purchase event -----------\n// Subscribe to the 'checkout_completed' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"checkout_completed\", async (event) => {\n  const checkout = event.data.checkout;\n\n  debugLog('\ud83d\uded2 Received checkout_completed event');\n\n  if (!checkout) {\n    debugError('Checkout data is missing in the event payload');\n    return;\n  }\n\n  if (!checkout.order) {\n    debugError('Order data is missing in the checkout object');\n    return;\n  }\n\n  debugLog('\ud83d\udce6 Processing order:', checkout.order.id);\n\n  const couponCodes = (checkout.discountApplications || [])\n    .map(discount => {\n      const code = discount?.title || 'unknown';\n      debugLog('\ud83c\udff7\ufe0f  Found discount application:', code);\n      return code;\n    })\n    .filter(Boolean)\n    .join(', ');\n\n  debugLog('Extracted coupon codes:', couponCodes || 'none');\n\n  debugLog(`\ud83d\udccb Processing ${checkout.lineItems?.length || 0} line items`);\n\n  // Construct the products array\n  const products = (checkout.lineItems || []).map((item, index) =>
1 {\n    debugLog(`   ${index + 1}.`, item?.title || 'unknown');\n    // Safely calculate line item discount total\n    const lineItemDiscountTotal = (item.discountAllocations || []).reduce((total, alloc, i) => {\n      const amount = parseFloat(alloc?.amount?.amount) || 0;\n      if (amount > 0) {\n        debugLog(`     \ud83d\udcb0 Discount ${i + 1}:`, `${amount} ${alloc?.amount?.currencyCode || ''}`);\n      }\n      return total + amount;\n    }, 0);\n\n    const pricePerUnit = parseFloat(item?.variant?.price?.amount) || 0;\n    const discountPerUnit = item.quantity > 0 ? (lineItemDiscountTotal / item.quantity) : 0;\n    const discountApp = item.discountAllocations?.discountApplication;\n    const isDiscountCode = discountApp?.type === \"DISCOUNT_CODE\";\n    const coupon = isDiscountCode ? discountApp.title : '';\n\n    debugLog(`     \ud83d\udcb5 Price: ${pricePerUnit} ${item?.variant?.price?.currencyCode || ''}`);\n    if (discountPerUnit > 0) {\n      debugLog(`     \ud83c\udff7\ufe0f  Discount: ${discountPerUnit} per unit`);\n    }\n    if (coupon) {\n      debugLog(`     \ud83c\udfab Coupon: ${coupon}`);\n    }\n\n    return {\n      id: item?.variant?.sku || item?.variant?.id || item?.variant?.product?.id || '',\n      name: item.title || '',\n      image: item?.variant?.image?.src || '',\n      price: pricePerUnit,\n      currency: item?.variant?.price?.currencyCode || '',\n      discount: discountPerUnit > 0 ? discountPerUnit : 0,\n      coupon: coupon,\n      quantity: item.quantity || 1,\n      brand: item?.variant?.product?.vendor || '',\n      category: item?.variant?.product?.type || '',\n      location: item?.variant?.product?.url || '',\n    };\n  });\n\n  // Construct transaction object\n  const transaction = {\n    id: checkout.order.id,\n    subtotal: parseFloat(checkout.subtotalPrice?.amount) || 0,\n    currency: checkout.currencyCode || '',\n    coupon: couponCodes,\n    discount: checkout.discountsAmount ? parseFloat(checkout.discountsAmount.amount) : 0,\n    total: parseFloat(checkout.totalPrice?.amount) || 0,\n    tax: parseFloat(checkout.totalTax?.amount) || 0,\n    shippingCost: parseFloat(checkout.shippingLine?.price?.amount) || 0,\n    shippingMethod: checkout.shippingLine?.title || '',\n  };\n\n  debugLog('\ud83d\udcb3 Transaction Summary:');\n  debugLog(`   \ud83c\udd94 Order ID: ${transaction.id}`);\n  debugLog(`   \ud83d\udcb0 Subtotal: ${transaction.subtotal} ${transaction.currency}`);\n  if (transaction.discount > 0) {\n    debugLog(`   \ud83c\udff7\ufe0f  Discount: -${transaction.discount} ${transaction.currency}`);\n  }\n  if (transaction.tax > 0) {\n    debugLog(`   \ud83d\udcdd Tax: ${transaction.tax} ${transaction.currency}`);\n  }\n  if (transaction.shippingCost > 0) {\n    debugLog(`   \ud83d\ude9a Shipping (${transaction.shippingMethod || 'Standard'}): ${transaction.shippingCost} ${transaction.currency}`);\n  }\n  debugLog(`   \ud83d\udcb5 Total: ${transaction.total} ${transaction.currency}`);\n\n  // Construct the final data payload for the Bambuser 'purchase' event.\n  const purchaseData = {\n    transaction: transaction,\n    products: products,\n  };\n\n  debugLog('\ud83d\ude80 Sending purchase event to Bambuser');\n  debugLog('\ud83d\udce4 Purchase data:', JSON.stringify(purchaseData, null, 2));\n\n  // Call the tracking function with the event name and the constructed data.\n  const success = await trackBambuserEvent('purchase', purchaseData);
1\n\n  if (success) {\n    debugSuccess('\u2705 Purchase event processed successfully! \ud83c\udf89');\n  } else {\n    debugError('\u274c Failed to process purchase event');\n  }\n});\n\n// ----------- 2. Add to cart event -----------\n// Subscribe to the 'product_added_to_cart' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"product_added_to_cart\", async (event) => {\n  const cartLine = event.data.cartLine;\n\n  debugLog('\ud83d\uded2 Received product_added_to_cart event');\n\n  if (!cartLine) {\n    debugError('Cart line data is missing in the event payload');\n    return;\n  }\n\n  const productVariant = cartLine.merchandise;\n  const product = productVariant?.product;\n\n  if (!product) {\n    debugError('Product data is missing in the cart line');\n    return;\n  }\n\n  debugLog(`\ud83d\udce6 Processing product: ${product.title || 'unknown'}`);\n\n  // Construct the product data in Bambuser format\n  const productData = {\n    id: productVariant?.sku || productVariant?.id || product?.id || '',\n    quantity: cartLine.quantity || 1,\n    name: productVariant?.title || product?.title || '',\n    image: productVariant?.image?.src || '',\n    price: parseFloat(productVariant?.price?.amount) || 0,\n    currency: productVariant?.price?.currencyCode || '',\n    brand: product?.vendor || '',\n    category: product?.type || '',\n    location: product?.url || ''\n  };\n\n  debugLog('\ud83d\udcca Product details:', {\n    id: productData.id,\n    name: productData.name,\n    quantity: productData.quantity,\n    price: `${productData.price} ${productData.currency}`,\n  });\n\n  // Call the tracking function with the event name and the constructed data.\n  const success = await trackBambuserEvent('add-to-cart', {\n    products: [productData]\n  });
1\n\n  if (success) {\n    debugSuccess('\u2705 Add to cart event processed successfully! \ud83c\udf89');\n  } else {\n    debugError('\u274c Failed to process add to cart event');\n  }\n});\n\n// ----------- 3. Update cart event -----------\n// Subscribe to the 'product_removed_from_cart' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"product_removed_from_cart\", async (event) => {\n  const cartLine = event.data.cartLine;\n\n  debugLog('\ud83d\uded2 Received product_removed_from_cart event');\n\n  if (!cartLine) {\n    debugError('Cart line data is missing in the event payload');\n    return;\n  }\n\n  const productVariant = cartLine.merchandise;\n  const product = productVariant?.product;\n\n  if (!product) {\n    debugError('Product data is missing in the cart line');\n    return;\n  }\n\n  debugLog(`\ud83d\udce6 Processing removed product: ${product.title || 'unknown'}`);\n\n  // Construct the product data in Bambuser format with quantity 0 to indicate removal\n  const productData = {\n    id: productVariant?.sku || productVariant?.id || product?.id || '',\n    // Better practice is to pass the current cart quantity of the product after removal but it is currently not provided in Shopify's Web Pixels API\n    quantity: 0, // Set quantity to 0 to indicate removal\n    name: productVariant?.title || product?.title || '',\n    image: productVariant?.image?.src || '',\n    price: parseFloat(productVariant?.price?.amount) || 0,\n    currency: productVariant?.price?.currencyCode || '',\n    brand: product?.vendor || '',\n    category: product?.type || '',\n    location: product?.url || ''\n  };\n\n  debugLog('\ud83d\udcca Removed product details:', {\n    id: productData.id,\n    name: productData.name,\n    price: `${productData.price} ${productData.currency}`,\n  });\n\n  // Call the tracking function with the update-cart event\n  const success = await trackBambuserEvent('update-cart', {\n    products: [productData]\n  });
1\n\n  if (success) {\n    debugSuccess('\u2705 Update cart (removal) event processed successfully! \ud83c\udf89');\n  } else {\n    debugError('\u274c Failed to process update cart (removal) event');\n  }\n});\n\n// ----------- 4. Product viewed event -----------\n// Subscribe to the 'product_viewed' event from Shopify's Web Pixels API.\nanalytics.subscribe(\"product_viewed\", async (event) => {\n  const productVariant = event.data.productVariant;\n\n  debugLog('\ud83d\udc41\ufe0f  Received product_viewed event');\n\n  if (!productVariant) {\n    debugError('Product variant data is missing in the event payload');\n    return;\n  }\n\n  const product = productVariant?.product;\n\n  if (!product) {\n    debugError('Product data is missing in the product variant');\n    return;\n  }\n\n  debugLog(`\ud83d\udce6 Processing product view: ${product.title || 'unknown'}`);\n\n  // Construct the product data in Bambuser format\n  const productData = {\n    id: productVariant?.sku || productVariant?.id || product?.id || '',\n    quantity: 1, // As this is a required field, we pass 1\n    name: productVariant?.title || product?.title || '',\n    image: productVariant?.image?.src || '',\n    price: parseFloat(productVariant?.price?.amount) || 0,\n    currency: productVariant?.price?.currencyCode || '',\n    brand: product?.vendor || '',\n    category: product?.type || '',\n    location: product?.url || ''\n  };\n\n  debugLog('\ud83d\udcca Product view details:', {\n    id: productData.id,\n    name: productData.name,\n    price: `${productData.price} ${productData.currency}`,\n  });\n\n  // Call the tracking function with the product-view event\n  
1const success = await trackBambuserEvent('product-view', {\n    products: [productData]\n  });\n\n  if (success) {\n    debugSuccess('\u2705 Product view event processed successfully! \ud83d\udc41\ufe0f');\n  } else {\n    debugError('\u274c Failed to process product view event');\n  }\n});\n\n"})})]})]})]}),"\n",(0,a.jsx)(n.h3,{id:"pixel-tracking-coverage",children:"Pixel tracking coverage"}),"\n",(0,a.jsx)(n.p,{children:"Not all supported Bambuser Shopper Events are feasible to track dues to limitation in Shopify Web Pixels API."}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"\u2705 Tracked Bambuser Shopper events:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Purchase event"}),"\n",(0,a.jsx)(n.li,{children:"Add to cart event"}),"\n",(0,a.jsx)(n.li,{children:"Remove from cart event"}),"\n",(0,a.jsx)(n.li,{children:"Product viewed event"}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"\u274c Events not feasible to track through standard Shopify Web Pixels API:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Add to Wishlist"}),"\n",(0,a.jsx)(n.li,{children:"Remove from Wishlist"}),"\n",(0,a.jsx)(n.li,{children:"Refund"}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(n.h3,{id:"shopify-pixel-privacy-and-attribution",children:"Shopify Pixel Privacy and Attribution"}),"\n",(0,a.jsx)(n.p,{children:"Shopify custom pixel privacy settings can affect attribution coverage."}),"\n",(0,a.jsxs)("div",{style:{display:"flex",flexDirection:"row",gap:"16px",flexWrap:"wrap",alignItems:"flex-start"},children:[(0,a.jsx)("img",{src:(0,u.Ay)("/img/ShopifyCookieBanner.png"),alt:"Shopify cookie and privacy preferences banner",style:{width:"100%",flex:"1 1 420px",maxWidth:"calc(50% - 8px)"}}),(0,a.jsx)("img",{src:(0,u.Ay)("/img/ShopifyPrivacySetting.png"),alt:"Shopify custom pixel privacy settings",style:{width:"100%",flex:"1 1 420px",maxWidth:"calc(50% - 8px)"}})]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["If ",(0,a.jsx)(n.strong,{children:"Permission"})," is set to ",(0,a.jsx)(n.strong,{children:"Required"}),", the Bambuser pixel only fires for visitors who explicitly grant the configured consent purposes. For example, if the pixel requires ",(0,a.jsx)(n.strong,{children:"Marketing"})," and ",(0,a.jsx)(n.strong,{children:"Analytics"}),", the visitor must accept both of those categories in the cookie banner for the pixel to fire."]}),"\n",(0,a.jsxs)(n.li,{children:["If ",(0,a.jsx)(n.strong,{children:"Data sale"})," is set to ",(0,a.jsx)(n.strong,{children:"Data collected qualifies as data sale"}),", the pixel can also be suppressed for visitors who opt out via regional privacy rights flows or browser privacy signals (for example GPC)."]}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"If the pixel does not run, Bambuser Shopper Events (including the purchase event) are not sent to Bambuser."}),"\n",(0,a.jsx)(n.p,{children:"Relevant Shopify references:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://changelog.shopify.com/posts/manage-customer-privacy-settings-for-custom-pixels",children:"Manage customer privacy settings for custom pixels"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://changelog.shopify.com/posts/new-default-setting-for-pixel-data-sharing",children:"New default setting for pixel data sharing"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"https://shopify.dev/docs/api/web-pixels-api/pixel-privacy",children:"Pixel Privacy"})}),"\n"]}),"\n",(0,a.jsx)(n.h3,{id:"verification",children:"Verification"}),"\n",(0,a.jsxs)(n.p,{children:["Follow the Verification steps in the ",(0,a.jsx)(n.a,{href:"/video-consultation/shopper-events-tracking/#verification",children:"Shopper Events Tracking Verification"})," to verify that the events are being tracked correctly."]}),"\n",(0,a.jsx)(n.h3,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,a.jsx)(n.h4,{id:"enable-debug-mode",children:"Enable Debug Mode"}),"\n",(0,a.jsxs)(n.p,{children:["For detailed logging in the browser's console, enable debug mode by setting the following variable to ",(0,a.jsx)(n.code,{children:"true"})," in your custom pixel code:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"// Enable detailed logging for troubleshooting\nconst ENABLE_DEBUGGING = true;  // Set to false in production\n"})}),"\n",(0,a.jsx)(n.h4,{id:"test-using-shopify-pixel-helper",children:"Test using Shopify Pixel Helper"}),"\n",(0,a.jsxs)(n.p,{children:["Learn more about how to ",(0,a.jsx)(n.a,{href:"https://help.shopify.com/en/manual/promoting-marketing/pixels/custom-pixels/testing#shopify-pixel-helper",children:"Test a custom pixel using the Shopify Pixel Helper"}),"."]}),"\n",(0,a.jsx)(n.h4,{id:"potential-tracking-issues",children:"Potential Tracking Issues"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.a,{href:"/video-consultation/troubleshooting/tracker-no-data/",children:"We integrated Shopper Events Tracker and placed a test order, but cannot see the data\n"})}),"\n"]})]})}function g(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(m,{...e})}):m(e)}function b(e,n){throw new Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}},19365(e,n,t){t.d(n,{A:()=>c});t(96540);var r=t(18215),o=t(47751);const a="tabItem_Ymn6";var i=t(74848);function s({children:e,className:n,hidden:t}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,r.A)(a,n),hidden:t,children:e})}function c({children:e,className:n,value:t}){const{selectedValue:r,lazy:a}=(0,o.uc)(),c=t===r;return!c&&a?null:(0,i.jsx)(s,{className:n,hidden:!c,children:e})}},4865(e,n,t){t.d(n,{A:()=>m});t(96540);
1var r=t(18215),o=t(17559),a=t(47751),i=t(23104),s=t(92303);const c="tabList__CuJ",u="tabItem_LNqP";var d=t(74848);function l({className:e}){const{selectedValue:n,selectValue:t,tabValues:o,block:s}=(0,a.uc)(),c=[],{blockElementScrollPositionUntilNextRender:l}=(0,i.a_)(),p=e=>{const r=e.currentTarget,a=c.indexOf(r),i=o[a].value;i!==n&&(l(r),t(i))},h=e=>{let n=null;switch(e.key){case"Enter":p(e);break;case"ArrowRight":{const t=c.indexOf(e.currentTarget)+1;n=c[t]??c[0];break}case"ArrowLeft":{const t=c.indexOf(e.currentTarget)-1;n=c[t]??c[c.length-1];break}}n?.focus()};return(0,d.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.A)("tabs",{"tabs--block":s},e),children:o.map(({value:e,label:t,attributes:o})=>(0,d.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{c.push(e)},onKeyDown:h,onClick:p,...o,className:(0,r.A)("tabs__item",u,o?.className,{"tabs__item--active":n===e}),children:t??e},e))})}function p({children:e}){return(0,d.jsx)("div",{className:"margin-top--md",children:e})}function h({className:e,children:n}){return(0,d.jsxs)("div",{className:(0,r.A)(o.G.tabs.container,"tabs-container",c),children:[(0,d.jsx)(l,{className:e}),(0,d.jsx)(p,{children:n})]})}function m(e){const n=(0,s.A)(),t=(0,a.OC)(e);return(0,d.jsx)(a.O_,{value:t,children:(0,d.jsx)(h,{className:e.className,children:(0,a.vT)(e.children)})},String(n))}},47751(e,n,t){t.d(n,{OC:()=>m,O_:()=>f,uc:()=>b,vT:()=>d});var r=t(96540),o=t(90612),a=t(205),i=t(57485),s=t(70679),c=t(31682),u=t(74848);function d(e){return r.Children.toArray(e).filter(e=>"\n"!==e)}function l(e){const{values:n,children:t}=e;return(0,r.useMemo)(()=>{const e=n??function(e){return r.Children.toArray(e).flatMap(e=>{if(!e)return[];if((0,r.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)}).map(({props:{value:e,label:n,attributes:t,default:r}})=>({value:e,label:n,attributes:t,default:r}))}(t);return function(e){const n=(0,c.XI)(e,(e,n)=>e.value===n.value);if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map(e=>`'${e.value}'`).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e},[n,t])}function p({value:e,tabValues:n}){return n.some(n=>n.value===e)}function h({queryString:e=!1,groupId:n}){const t=(0,o.W6)(),a=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,i.aZ)(a),(0,r.useCallback)(e=>{if(!a)return;const n=new URLSearchParams(t.location.search);n.set(a,e),t.replace({...t.location,search:n.toString()})},[a,t])]}function m(e){const{defaultValue:n,queryString:t=!1,groupId:o}=e,i=l(e),[c,u]=(0,r.useState)(()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!p({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map(e=>e.value).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find(e=>e.default)??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i})),[d,m]=h({queryString:t,groupId:o}),[g,b]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,o]=(0,s.Dv)(n);return[t,(0,r.useCallback)(e=>{n&&o.set(e)},[n,o])]}({groupId:o}),f=(()=>{const e=d??g;return p({value:e,tabValues:i})?e:null})();(0,a.A)(()=>{f&&u(f)},[f]);return{selectedValue:c,selectValue:(0,r.useCallback)(e=>{if(!p({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);u(e),m(e),b(e)},[m,b,i]),tabValues:i,lazy:e.lazy??!1,block:e.block??!1}}const g=(0,r.createContext)(null);function b(){const e=r.useContext(g);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function f(e){return(0,u.jsx)(g.Provider,{value:e.value,children:e.children})}},35112(e,n,t){t.d(n,["A",0,"data:image/png;base64,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"])},28453(e,n,t){t.d(n,{R:()=>i,x:()=>s});var r=t(96540);const o={},a=r.createContext(o);function i(e){const n=r.useContext(a);return r.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:i(e.components),r.createElement(a.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.