PrestaShop
These instructions cover PrestaShop 1.7 and 8. You will install the snippet site-wide, then record purchases on the order confirmation page.
Install the snippet
Your snippet comes from Project Settings → Snippet in the A vs B dashboard:
<script>window.avsb=window.avsb||{};window.avsb.q=window.avsb.q||[];window.avsb.ready=window.avsb.ready||function(f){window.avsb.q.push(f)};window.avsb.on=window.avsb.on||function(n,f){var t=['on',n,f];window.avsb.q.push(t);return function(){t[0]=null}};window.avsb.consent=window.avsb.consent||{set:function(s){window.avsb.q.push(['consent.set',s])},get:function(){return{}}};window.avsb.track=window.avsb.track||{};['event','segment','purchase','cart'].forEach(function(m){window.avsb.track[m]=window.avsb.track[m]||function(a,b){window.avsb.q.push(['track.'+m,a,b])}});(function(){window.avsb._t0=Date.now();window.avsb._df=fetch('https://cdn.avsb.cloud/YOUR_SNIPPET_KEY/datafile.json').catch(function(){});var d=document,e=d.documentElement,s=d.currentScript,a=s&&s.getAttribute('data-avsb-timeout'),t=a==null?3000:+a;if(!(t>0)||window.avsb.version)return;e.style.opacity='0';e.style.pointerEvents='none';window.avsb._t=setTimeout(function(){e.style.removeProperty('opacity');e.style.removeProperty('pointer-events')},t)})();</script><script src="https://cdn.avsb.cloud/snippet.js?id=YOUR_SNIPPET_KEY" data-avsb="YOUR_SNIPPET_KEY" async></script>The first tag is a stub that makes avsb.ready(...), avsb.on(...) and avsb.consent.set(...) calls safe before the bundle loads. Paste both tags when installing.
Option 1: Theme template (quickest)
Open your theme's head partial
Edit /themes/<your-theme>/templates/_partials/head.tpl (in the classic theme this file renders the head of every page).
Paste the snippet
Add the snippet tag near the top of the file, before other scripts, and save.
Clear the cache
In the back office, go to Advanced Parameters → Performance and click Clear cache so the template change takes effect.
Direct template edits are lost when the theme updates. Use a child theme, or the module approach below, to make the change durable.
Option 2: Module hooked to displayHeader
If you maintain a custom module, register the displayHeader hook and return both tags:
public function hookDisplayHeader($params){ return '<script>' . 'window.avsb=window.avsb||{};window.avsb.q=window.avsb.q||[];' . 'window.avsb.ready=window.avsb.ready||function(f){window.avsb.q.push(f)};' . "window.avsb.on=window.avsb.on||function(n,f){var t=['on',n,f];window.avsb.q.push(t);return function(){t[0]=null}};" . "window.avsb.consent=window.avsb.consent||{set:function(s){window.avsb.q.push(['consent.set',s])},get:function(){return{}}};" . "window.avsb.track=window.avsb.track||{};['event','segment','purchase','cart'].forEach(function(m){window.avsb.track[m]=window.avsb.track[m]||function(a,b){window.avsb.q.push(['track.'+m,a,b])}});" . "(function(){window.avsb._t0=Date.now();window.avsb._df=fetch('https://cdn.avsb.cloud/YOUR_SNIPPET_KEY/datafile.json').catch(function(){});var d=document,e=d.documentElement,s=d.currentScript,a=s&&s.getAttribute('data-avsb-timeout'),t=a==null?3000:+a;if(!(t>0)||window.avsb.version)return;e.style.opacity='0';e.style.pointerEvents='none';window.avsb._t=setTimeout(function(){e.style.removeProperty('opacity');e.style.removeProperty('pointer-events')},t)})();" . '</script>' . '<script src="https://cdn.avsb.cloud/snippet.js?id=YOUR_SNIPPET_KEY" data-avsb="YOUR_SNIPPET_KEY" async></script>';}Some themes and "custom code" modules also offer an admin field that injects into the head: pasting the snippet tag there works the same way.
Record purchases
On the order confirmation page, call avsb.track.purchase. The confirmation template has the order available, so you can hook displayOrderConfirmation from a module:
public function hookDisplayOrderConfirmation($params){ $order = $params['order']; $currency = new Currency((int) $order->id_currency); return '<script> avsb.ready(function () { avsb.track.purchase({ orderId: ' . json_encode($order->reference) . ', total: ' . (float) $order->total_paid . ', currency: ' . json_encode($currency->iso_code) . ', shipping: ' . (float) $order->total_shipping . ' }); }); </script>';}The fields:
orderId: required. Orders are deduplicated by this ID (last write wins), so a refresh of the confirmation page never double-counts.total: required. The order total as a decimal, for example49.99.currency: optional. Defaults to your project's configured currency if omitted.
If a GTM/GA4 module on your store already pushes the GA4 purchase event to window.dataLayer, and your product catalog is connected, you can skip the hook: turn on Google Analytics 4 / dataLayer on your project's Product catalog page and the snippet records those purchases automatically. Running both is harmless: orders are deduplicated by ID.
Verify
Place a test order, then open Commerce → Orders in the A vs B dashboard (see the orders & attribution guide) to confirm the order arrived and was attributed to an experiment exposure.