WordPress Integration Guide

Plugin, theme, or multi-plugin patterns — dedicated plugin is documented for v1.1.0

🔌
The helios-fingerprint-wp plugin is pre-built and lives in its own repo (current v1.1.0). Collection mode is configurable: All pages (default) enqueues helios-init.js and runs on every page; Events only enqueues helios-events.js and runs on selected actions (login, register, checkout, WC add-to-cart, plus server-side hooks). Optional local event log table (wp_helios_events), webhook forwarding, and Settings → Helios event log are available. Data still flows to WC session, user meta, and order meta via POST /wp-json/helios/v1/identify.
Repository layout
helios-fingerprint-wp/
helios-fingerprint-wp/
├── helios-fingerprint.php        ← v1.1+ bootstrap, migrate, activation, cron, uninstall
├── includes/
│   ├── class-enqueue.php         ← SDK + helios-init.js OR helios-events.js (mode)
│   ├── class-rest-api.php        ← POST /helios/v1/identify (+ log / webhook)
│   ├── class-woocommerce.php     ← order meta; optional add_to_cart log
│   ├── class-event-hooks.php     ← wp_login, user_register (events-only)
│   ├── class-logger.php         ← wp_helios_events + purge
│   ├── class-webhook.php         ← optional outbound JSON + HMAC
│   ├── class-admin-logs.php     ← Settings → Helios event log
│   └── class-settings.php        ← SDK, collection, storage
├── assets/
│   ├── helios-init.js            ← all-pages: page-load collect + sessionStorage TTL
│   └── helios-events.js          ← events-only: form submit + WC add_to_cart
├── .gitignore
├── readme.txt
└── CHANGELOG.md
Entry point — helios-fingerprint.php
helios-fingerprint.php
<?php
/**
 * Plugin Name: Helios Fingerprint
 * Version:     1.1.0
 * Requires at least: 6.4 | Requires PHP: 8.1
 */
defined( 'ABSPATH' ) || exit;

define( 'HELIOS_FP_VERSION', '1.1.0' );
define( 'HELIOS_FP_DIR',  plugin_dir_path( __FILE__ ) );
define( 'HELIOS_FP_URL',  plugin_dir_url( __FILE__ ) );

require_once HELIOS_FP_DIR . 'includes/class-logger.php';
require_once HELIOS_FP_DIR . 'includes/class-webhook.php';
require_once HELIOS_FP_DIR . 'includes/class-enqueue.php';
require_once HELIOS_FP_DIR . 'includes/class-rest-api.php';
require_once HELIOS_FP_DIR . 'includes/class-woocommerce.php';
require_once HELIOS_FP_DIR . 'includes/class-event-hooks.php';
require_once HELIOS_FP_DIR . 'includes/class-admin-logs.php';
require_once HELIOS_FP_DIR . 'includes/class-settings.php';

// helios_fp_default_options() adds helios_public_api_key, helios_collection_mode, helios_local_log_*, helios_webhook_*, track_* …
register_activation_hook( __FILE__, 'helios_fp_activate' );  // options, Logger::install(), daily cron
register_deactivation_hook( __FILE__, 'helios_fp_deactivate' );
register_uninstall_hook( __FILE__, 'helios_fp_uninstall' );  // drops custom table
add_action( 'plugins_loaded', 'helios_fp_migrate', 5 );   // new options + DB on upgrade
add_action( 'helios_fp_daily_purge', [ 'Helios_Fingerprint_Logger', 'purge_old' ] );
add_action( 'plugins_loaded', 'helios_fp_bootstrap' ); // Enqueue, Rest, WC, EventHooks, AdminLogs, Settings
Enqueue — class-enqueue.php (key method)
includes/class-enqueue.php → enqueue()
// Simplified: after checking helios_enabled and non-empty cdn_url …
$cdn_url = (string) get_option( 'helios_cdn_url', 'https://fp.techstudio.live/sdk/latest/helios-fingerprint.umd.js' );
$mode = (string) get_option( 'helios_collection_mode', 'all_pages' );
$init = ( 'events_only' === $mode )
    ? [ 'helios-fp-events', HELIOS_FP_URL . 'assets/helios-events.js' ]
    : [ 'helios-fp-init',   HELIOS_FP_URL . 'assets/helios-init.js'   ];

wp_enqueue_script( 'helios-fp-sdk', $cdn_url, [], null, [ 'strategy' => 'defer', 'in_footer' => true ] );
wp_enqueue_script( $init[0], $init[1], [ 'helios-fp-sdk' ], HELIOS_FP_VERSION, [ 'strategy' => 'defer', 'in_footer' => true ] );

wp_localize_script( $init[0], 'HeliosConfig', [
    'restUrl'       => rest_url( 'helios/v1/identify' ),
    'nonce'         => wp_create_nonce( 'wp_rest' ),
    'userId'        => get_current_user_id(),
    'cacheTtl'      => (int) get_option( 'helios_cache_ttl', '5' ),
    'publicApiKey'  => (string) get_option( 'helios_public_api_key', '' ),
    'mode'          => $mode,
    'trackedEvents' => [ 'login' => true, 'register' => true, 'addToCart' => false, 'placeOrder' => true ],
] ); // trackedEvents values come from get_option( 'helios_track_*' ) in the real plugin
Client script — assets/helios-init.js
assets/helios-init.js
(function () {
    'use strict';
    if (typeof HeliosFingerprint === 'undefined') return;

    var ttl = (window.HeliosConfig && parseInt(window.HeliosConfig.cacheTtl, 10)) || 0;
    var publicApiKey = window.HeliosConfig && window.HeliosConfig.publicApiKey;
    if (ttl > 0) {
        var cachedAt = sessionStorage.getItem('helios_fp_cached_at');
        if (cachedAt && (Date.now() - parseInt(cachedAt, 10)) < ttl * 60000) {
            return;
        }
    }

    HeliosFingerprint.load({ publicApiKey: publicApiKey || undefined })
        .then(function (fp) { return fp.get(); })
        .then(function (result) {
            sessionStorage.setItem('helios_visitor_id',     result.visitorId     || '');
            sessionStorage.setItem('helios_device_core_id', result.deviceCoreId  || '');
            sessionStorage.setItem('helios_browser_scoped', result.browserScopedId || '');
            sessionStorage.setItem('helios_ip',             result.ip            || '');

            if (ttl > 0) {
                sessionStorage.setItem('helios_fp_cached_at', String(Date.now()));
            }

            if (!window.HeliosConfig || !window.HeliosConfig.restUrl) return;

            return fetch(window.HeliosConfig.restUrl, {
                method: 'POST',
                credentials: 'same-origin',
                headers: {
                    'Content-Type': 'application/json',
                    'X-WP-Nonce':   window.HeliosConfig.nonce || '',
                },
                body: JSON.stringify({
                    visitorId:       result.visitorId,
                    deviceCoreId:    result.deviceCoreId,
                    browserScopedId: result.browserScopedId,
                    observationId:   result.observationId,
                    ip:              result.ip,
                    flags:           result.flags || {},
                }),
            }).catch(function () { /* non-fatal */ });
        })
        .catch(function (e) {
            if (typeof console !== 'undefined' && console.warn) {
                console.warn('[Helios] fingerprint failed', e);
            }
        });
}());
Events only — assets/helios-events.js

When helios_collection_mode is events_only, this script loads instead of helios-init.js. It binds to #loginform, #registerform, form.checkout.woocommerce-checkout, and (if jQuery is present) the WooCommerce added_to_cart event. Each trigger runs load().get() and POSTs to the REST API with a matching eventType (login, register, place_order, add_to_cart). Form submits are intercepted once, fingerprint collected, then the form re-submits. Server-side wp_login / user_register in class-event-hooks.php can record the same events from session for logged-in context.

helios-events.js — POST payload shape
fetch(HeliosConfig.restUrl, {
  method: 'POST',
  credentials: 'same-origin',
  headers: {
    'Content-Type': 'application/json',
    'X-WP-Nonce': HeliosConfig.nonce
  },
  body: JSON.stringify({
    visitorId, deviceCoreId, browserScopedId, observationId, ip, flags,
    eventType: 'place_order',  // e.g. login | register | add_to_cart
    eventData: {}              // optional key/value map
  })
});
Local log & webhook (v1.1+)

Under Settings → Helios Fingerprint → Storage, you can enable a MySQL table wp_prefix + helios_events (retention in days, daily helios_fp_daily_purge), and/or a non-blocking POST to your own URL with optional X-Helios-Signature: HMAC-SHA256(body, secret). View recent rows at Settings → Helios event log.

What's already wired (v1.1): WC session, user meta, order meta (when place-order tracking is on), REST POST /wp-json/helios/v1/identify with optional eventType / eventData, optional wp_helios_events + webhook, and Admin UI under Settings → Helios Fingerprint. In events-only mode, pre-auth login / register requests from the browser (guest) skip duplicate log/webhook rows; canonical rows are written on wp_login / user_register.
🎨
The fastest option for a single site. Drop the snippet into your child theme's functions.php and you're done in under a minute. No plugin, no admin UI, no REST endpoint. IDs are available in sessionStorage for any JS on the page.
child-theme/functions.php
functions.php — complete integration (~20 lines)
add_action( 'wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'helios-fp',
        'https://fp.techstudio.live/sdk/latest/helios-fingerprint.umd.js',
        [], null, true
    );

    wp_add_inline_script( 'helios-fp', "
        (function(){
            if(typeof HeliosFingerprint==='undefined') return;
            var _agent=null;
            function runHeliosOnce(){
                var p=_agent?Promise.resolve(_agent):HeliosFingerprint.load({publicApiKey:'YOUR_PUBLIC_API_KEY'}).then(function(f){_agent=f;return f;});
                return p.then(function(f){return f.get();});
            }
            runHeliosOnce().then(function(r){
                    sessionStorage.setItem('helios_visitor_id',    r.visitorId    || '');
                    sessionStorage.setItem('helios_device_core_id', r.deviceCoreId || '');
                    sessionStorage.setItem('helios_browser_scoped', r.browserScopedId || '');
                    sessionStorage.setItem('helios_ip',             r.ip           || '');
                });
        }());
    " );
} );
⚠️
Limitation: this code lives in the theme. If the theme is ever switched, fingerprinting silently stops. For multi-site deployments, client projects, or anything needing WooCommerce order attribution, use the dedicated plugin instead.
Reading the IDs from JavaScript
Any JS on the page — read after the SDK has run
// Safe to read on any user interaction (click, submit, etc.)
const visitorId    = sessionStorage.getItem('helios_visitor_id')     || '';
const deviceCoreId = sessionStorage.getItem('helios_device_core_id') || '';
const ip           = sessionStorage.getItem('helios_ip')             || '';

// Example: attach to a form submission
document.getElementById('checkout-form')
    .addEventListener('submit', function (e) {
        document.getElementById('hidden-visitor-id').value = visitorId;
    });
🔗
You have an existing plugin that talks to a third-party system — login, register, submit transaction, launch game — via its own REST or SDK calls. Choose how the fingerprint gets to that system.
Approach A — Two separate plugins

Run helios-fingerprint-wp alongside your existing plugin. The Helios plugin runs on each page load (subject to the admin cache TTL) and stores IDs in sessionStorage, WC session, and user meta. Your plugin reads those IDs at event time — no duplicate SDK load, clean separation.

Reading IDs client-side at event time (JavaScript)
your-plugin/assets/your-plugin.js
// Helios runs deferred on page load — by the time the user clicks
// anything, sessionStorage is already populated.
document.getElementById('launch-game-btn')
    .addEventListener('click', function () {
        const visitorId    = sessionStorage.getItem('helios_visitor_id')     || '';
        const deviceCoreId = sessionStorage.getItem('helios_device_core_id') || '';

        fetch('/wp-json/myplugin/v1/launch', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': myPlugin.nonce },
            body: JSON.stringify({
                gameId:       myPlugin.gameId,
                visitorId:    visitorId,
                deviceCoreId: deviceCoreId,
            }),
        });
    });
Reading IDs server-side from user meta (PHP)
your-plugin/includes/class-game.php
// Works at any WP hook where the user is known
add_action( 'wp_login', function ( string $username, WP_User $user ) {
    $visitor_id  = (string) get_user_meta( $user->ID, 'helios_visitor_id',    true );
    $device_core = (string) get_user_meta( $user->ID, 'helios_device_core_id', true );

    if ( '' !== $visitor_id ) {
        MyThirdPartySDK::identify( $visitor_id, $device_core );
    }
}, 10, 2 );
Reacting to fingerprint arrival in real-time (action hook)
your-plugin — consume helios_fingerprint_identified
// Fires the moment the browser POSTs a fingerprint to /helios/v1/identify.
// Perfect for syncing to a remote fraud service immediately.
add_action( 'helios_fingerprint_identified', function ( array $data, WP_REST_Request $req ) {
    MyThirdPartySDK::trackVisit([
        'visitor_id'   => $data['visitorId'],
        'device_core'  => $data['deviceCoreId'],
        'ip'           => $data['ip'],
        'is_vpn'       => $data['flags']['isVpn']       ?? '',
        'is_incognito' => $data['flags']['isIncognito'] ?? '',
    ]);
}, 10, 2 );
Approach B — Embed fingerprint in your existing plugin

Your plugin enqueues the SDK itself and chains the third-party call directly inside the then(). Best when events are server-triggered at page load (SSO, auto-login) and you need the fingerprint before the user ever clicks anything.

Enqueue SDK + fire third-party inline (PHP)
your-plugin/includes/class-enqueue.php
add_action( 'wp_enqueue_scripts', function () {
    wp_enqueue_script(
        'helios-fp-sdk',
        'https://fp.techstudio.live/sdk/latest/helios-fingerprint.umd.js',
        [], null, true
    );

    wp_add_inline_script( 'helios-fp-sdk', "
        (function(){
            if(typeof HeliosFingerprint==='undefined') return;
            var _agent=null;
            function runHeliosOnce(){
                var p=_agent?Promise.resolve(_agent):HeliosFingerprint.load({publicApiKey:'YOUR_PUBLIC_API_KEY'}).then(function(f){_agent=f;return f;});
                return p.then(function(f){return f.get();});
            }
            runHeliosOnce().then(function(r){
                    sessionStorage.setItem('helios_visitor_id',    r.visitorId    || '');
                    sessionStorage.setItem('helios_device_core_id', r.deviceCoreId || '');

                    if(typeof MyThirdPartySDK !== 'undefined') {
                        MyThirdPartySDK.identify(r.visitorId, r.deviceCoreId, r.ip);
                    }
                });
        }());
    " );
} );
Hybrid bridge — PHP helper class (works with either approach)

Add this small bridge class to your existing plugin. It reads from WC session or user meta (populated by the Helios plugin) with a graceful fallback when the Helios plugin is absent. Call My_Helios_Bridge::get_visitor_id() anywhere in your plugin — no hard dependency.

your-plugin/includes/class-helios-bridge.php
class My_Helios_Bridge {

    /** Returns visitorId from WC session or user meta. Empty string if unavailable. */
    public static function get_visitor_id(): string {
        return self::get_meta( 'helios_visitor_id' );
    }

    public static function get_device_core_id(): string {
        return self::get_meta( 'helios_device_core_id' );
    }

    public static function get_browser_scoped_id(): string {
        return self::get_meta( 'helios_browser_scoped_id' );
    }

    private static function get_meta( string $key ): string {
        $user_id = get_current_user_id();
        if ( $user_id > 0 ) {
            return (string) get_user_meta( $user_id, $key, true );
        }
        if ( function_exists( 'WC' ) && WC()->session ) {
            return (string) WC()->session->get( $key, '' );
        }
        return '';
    }
}

// ── Usage anywhere in your plugin: ────────────────────────────────────────────
$visitor_id  = My_Helios_Bridge::get_visitor_id();
$device_core = My_Helios_Bridge::get_device_core_id();

MyThirdPartyAPI::submitTransaction( $payload, $visitor_id, $device_core );

Click a scenario that matches your situation to see the recommended approach and reasoning.

1 Quick prototype or single site — I just want to see fingerprints working
Recommended Theme · functions.php

Paste the 20-line snippet into your child theme's functions.php and you're done. No plugin activation, no admin UI. IDs land in sessionStorage within seconds of page load. Upgrade to the dedicated plugin if requirements grow.

2 Multiple WordPress sites, or this is a client project
Recommended Dedicated Plugin

Install the plugin as a zip across sites. The CDN URL, public API key, and enable/disable toggle live in Settings → Helios Fingerprint, so clients can manage it without touching code. It's theme-independent — switching themes never breaks fingerprinting.

3 I need fingerprint IDs visible inside WooCommerce order admin
Recommended Dedicated Plugin

The plugin's class-woocommerce.php hooks into woocommerce_checkout_order_created, copies _helios_visitor_id and _helios_device_core_id from the WC session onto the order, and renders them in the order admin screen after the billing address. Zero extra work required.

4 Existing plugin with third-party API — events are user-triggered (login button, game launch, submit transaction)
Recommended Two Plugins + Bridge class

Run helios-fingerprint-wp alongside your plugin. The SDK fires deferred on page load — by the time the user clicks anything, sessionStorage is already populated. Your plugin reads the IDs client-side from sessionStorage, or server-side via the My_Helios_Bridge helper from user meta. Clean separation: the Helios plugin can be toggled without touching your business logic.

5 Existing plugin with third-party API — events are server-triggered at page load (SSO, auto-login, redirect-based flow)
Recommended Embed Inline

When your third-party call fires immediately on page load (before any user interaction), the deferred Helios plugin will not have run yet. Embed the SDK enqueue inside your existing plugin and chain the third-party call directly inside the .then(). You control the exact timing — the fingerprint is guaranteed to be ready before your API call goes out.

6 The fingerprint needs to reach multiple third-party systems (fraud service, analytics, CRM, game provider)
Recommended Two Plugins — shared storage

The Helios plugin is a single source of truth: on each fresh collection (skipped when the admin cache TTL says the browser copy is still fresh), IDs are written to sessionStorage, WC session, and user meta. Every other plugin on the site reads from those shared stores using the My_Helios_Bridge helper or the helios_fingerprint_identified action hook — no duplication of the SDK, no multiple REST calls. Adding a new consumer plugin costs zero extra JS weight.

⏱ Timing safety — when is the fingerprint ready?

The Helios plugin enqueues the SDK with defer. Deferred scripts run after the HTML is parsed but before DOMContentLoaded — typically within 1–3 seconds of page load. Any event the user triggers by clicking a button (login, register, game launch, checkout) will always fire several seconds after that, so sessionStorage is reliably populated by then.

The only unsafe window is an automatic server-side action that fires on first page load before the deferred script runs (SSO redirect, cookie-based auto-login). For those flows, use Approach B (embed inline) so you control the timing explicitly.