SEON Shopify Agent for Custom Storefronts

Updated on 21.10.25
1 minute to read
Copy link

Overview

This guide explains how to integrate the SEON browser agent into custom Shopify storefronts (e.g., Hydrogen, Next.js, Remix, Nuxt) and how it behaves in theme-based storefronts, where it’s injected automatically by the SEON app.

The SEON browser agent gathers device intelligence and produces a Base64-encoded session payload that you can attach to orders, checkouts, or identity events.

  • Theme storefronts (Online Store 2.0 / Liquid)
    The SEON app auto-injects the agent. Nothing to install manually.
  • Custom storefronts (Hydrogen, headless, etc.)
    You must manually embed the agent script. It handles fingerprint generation, cart token tracking, and session payload creation.

 

Integration

Add this script to your storefront (for example in your root layout or app.tsx/index.html).
It will initialize automatically once the DOM is ready.

(function () {
  'use strict';

  // Prevent multiple initializations
  if (window.SEON_AGENT) {
    return;
  }

  // Default configuration
  const defaultConfig = {
    environment: 'production',
    cartEndpoint: '/cart.json',
    debug: false,
    autoInit: true,
    host: 'getdeviceinf.com',
    audioFingerprint: true,
    canvasFingerprint: true,
    webglFingerprint: true,
  };

  // Merge user config with defaults
  const config = { ...defaultConfig, ...(window.SEON_CONFIG || {}) };

  // Storage keys
  const STORAGE_KEYS = {
    VISITOR_ID: 'seon_visitor_id',
    CART_TOKEN: 'seon_cart_token',
  };

  // State management
  let isInitialized = false;
  let isSeonLoaded = false;
  let initializationPromise = null;

  /**
   * Configure SEON with session token
   */
  async function configureSeon(token) {
    if (!window.seon) {
      throw new Error('SEON agent not loaded');
    }

    return new Promise((resolve, reject) => {
      window.seon.config({
        session_id: token,
        host: config.host,
        audio_fingerprint: config.audioFingerprint,
        canvas_fingerprint: config.canvasFingerprint,
        webgl_fingerprint: config.webglFingerprint,
        onSuccess: message => {
          resolve(message);
        },
        onError: message => {
          reject(new Error(message));
        },
      });
    });
  }

  /**
   * Generate and store SEON payload
   */
  async function generatePayload(token) {
    if (!token) {
      throw new Error('Token is required');
    }

    await configureSeon(token);

    return new Promise((resolve, reject) => {
      window.seon.getBase64Session(async data => {
        if (data) {
          localStorage.setItem(STORAGE_KEYS.VISITOR_ID, data);
          resolve(data);
        } else {
          reject(new Error('Failed to retrieve session data'));
        }
      });
    });
  }

  /**
   * Fetch cart token from Shopify
   */
  async function fetchCartToken() {
    try {
      const response = await fetch(config.cartEndpoint, {
        priority: 'low',
        headers: {
          'Accept': 'application/json',
        },
      });

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      const cart = await response.json();
      return cart.token;
    } catch (error) {
      throw error;
    }
  }

  /**
   * Check and update session if needed
   */
  async function checkAndUpdateSession(initialToken = null) {
    let cartToken = initialToken;

    // Try to get cart token from Shopify
    try {
      cartToken = await fetchCartToken();
    } catch (error) {
      // If cart fetch fails and no initial token, generate one
      if (!cartToken) {
        cartToken = crypto.randomUUID();
      }
    }

    const storedToken = localStorage.getItem(STORAGE_KEYS.CART_TOKEN);
    const visitorId = localStorage.getItem(STORAGE_KEYS.VISITOR_ID);

    // Update session if token changed or no visitor ID exists
    if (storedToken !== cartToken || !visitorId) {
      if (storedToken !== cartToken) {
        localStorage.setItem(STORAGE_KEYS.CART_TOKEN, cartToken);
        localStorage.removeItem(STORAGE_KEYS.VISITOR_ID);
      }

      await generatePayload(cartToken);
    }
  }

  /**
   * Load SEON agent script
   */
  function loadSeonAgent() {
    return new Promise((resolve, reject) => {
      if (isSeonLoaded) {
        resolve();
        return;
      }

      const script = document.createElement('script');
      script.type = 'text/javascript';
      script.fetchPriority = 'low';
      script.defer = true;

      script.src = 'https://cdn.getdeviceinf.com/js/v5/agent.js';

      script.onload = () => {
        isSeonLoaded = true;
        resolve();
      };

      script.onerror = () => {
        reject(new Error('Failed to load SEON agent'));
      };

      document.head.appendChild(script);
    });
  }

  /**
   * Initialize SEON fraud detection
   */
  async function initialize() {
    if (isInitialized) {
      return initializationPromise;
    }

    if (initializationPromise) {
      return initializationPromise;
    }

    initializationPromise = (async () => {
      try {
        // Load SEON agent
        await loadSeonAgent();

        // Wait a bit for the agent to be ready
        await new Promise(resolve => setTimeout(resolve, 100));

        // Check and update session
        await checkAndUpdateSession();

        isInitialized = true;
      } catch (error) {
        throw error;
      }
    })();

    return initializationPromise;
  }

  /**
   * Public API
   */
  window.SEON_AGENT = {
    /**
     * Initialize SEON
     */
    init: initialize,

    /**
     * Get current visitor ID
     */
    getVisitorId: () => {
      return localStorage.getItem(STORAGE_KEYS.VISITOR_ID);
    },

    /**
     * Get current cart token
     */
    getCartToken: () => {
      return localStorage.getItem(STORAGE_KEYS.CART_TOKEN);
    },

    /**
     * Manually update session with a new token
     */
    updateSession: async (token) => {
      if (!isInitialized) {
        throw new Error('SEON not initialized. Call init() first.');
      }
      await checkAndUpdateSession(token);
    },

    /**
     * Clear stored session data
     */
    clearSession: () => {
      localStorage.removeItem(STORAGE_KEYS.VISITOR_ID);
      localStorage.removeItem(STORAGE_KEYS.CART_TOKEN);
    },

    /**
     * Check if SEON is initialized
     */
    isInitialized: () => isInitialized,

    /**
     * Get current configuration
     */
    getConfig: () => ({ ...config }),
  };

  // Auto-initialize if configured
  if (config.autoInit) {
    // Wait for DOM to be ready
    if (document.readyState === 'loading') {
      document.addEventListener('DOMContentLoaded', () => {
        setTimeout(initialize, 1000);
      });
    } else {
      setTimeout(initialize, 1000);
    }
  }
})();

 

How It Works

  • Script loading: It lazily loads https://cdn.getdeviceinf.com/js/v5/agent.js and initializes SEON.
  • Session management: It fetches the current Shopify cart token (or generates one if missing) and uses it as the session identifier.
  • Payload storage: A Base64-encoded fingerprint is stored in localStorage under seon_visitor_id.
  • Automatic behavior: If autoInit is true, the agent initializes after DOM load without any manual trigger.