Plugin, theme, or multi-plugin patterns — dedicated plugin is documented for v1.1.0
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.
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
<?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
// 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
(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); } }); }());
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.
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
})
});
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.
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.
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.
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 || ''); }); }()); " ); } );
// 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; });
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.
// 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, }), }); });
// 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 );
// 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 );
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.
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); } }); }()); " ); } );
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.
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.
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.
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.
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.
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.
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.
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.
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.