Mes outils

XUNL EXPERIMENTS / JS SDK · WORDPRESS · SERVEUR API

Guide de mise en place des tests A/B

Choisissez une méthode à partir du contenu : ajoutez le SDK JS à la tête de tout site ou installez le plugin WordPress, connectez le consentement, fixez des objectifs et validez les demandes de renseignements de votre serveur avec les exemples.

Guide public · pas d'inscription
Voulez-vous exécuter des tests A/B sur votre propre site Web?

XunL Experiments: éditer visuellement les pages, affecter les visiteurs de manière cohérente, confirmer les demandes acceptées sur le serveur et obtenir des rapports avec des intervalles de confiance et un état de décision clair.

Ouvrir les expériences A/B →

Choisissez comment vous connecter

Les deux utilisent la même connexion de site, des expériences et des rapports; seule l'installation et la confirmation de l'enquête diffèrent.

JS SDKplugin WordPress
Sites WebTout site web: Shopify, Webflow, Wix, Squarespace, gestionnaires d'étiquettes, sites personnalisésWordPress 6.2 ou supérieur
InstallerAjouter une ligne de script à la tête partagéeTéléverser le plugin ZIP et entrer les détails de connexion
Page, cliquez et des objectifs d'événement personnalisésAppuiAppui
Objectif d'enquête acceptéSigné par votre serveur (exemples Node.js, Python et PHP ci-dessous)Automatique pour le formulaire de contact 7, Elementor Pro, WPForms et Gravity Forms

Tout site web : installez le SDK JS

  1. Connectez-vous à XunL, ajoutez le site sous Sites, puis créez une connexion sous Expériences A/B → Sites et installation.
  2. Copiez le script que la console affiche et placez-le dans la tête partagée par chaque page, le plus tôt possible. Remplacer SITE_PUBLIC_KEY dans l'exemple par la clé publique de votre site.
  3. Connectez le consentement du visiteur (ci-dessous), puis sélectionnez Vérifier la connexion dans la console.
HTML
<script defer src="https://www.xunl.net/js/xunl-experiments-sdk.js" data-xunl-key="SITE_PUBLIC_KEY" data-xunl-origin="https://www.xunl.net" data-xunl-consent="pending"></script>

Le script ne lit que la configuration de l'expérience publique; ne jamais mettre le secret de connecteur dans une page Web. Dans les applications à une page, appelez XunLexperiments.refresh() après avoir modifié les itinéraires avec history.pushState.

Où l'ajouter sur chaque plateforme

Magasiner
Boutique en ligne → Thèmes → Modifier le code → layout/theme.liquide, coller juste avant l'étiquette de la tête de fermeture. La checkout ne peut pas être édité sauf sur Shopify Plus, donc mettre des objectifs sur les pages avant la checkout.
Flux Web
Paramètres du site → Code personnalisé → Code de tête, coller et publier. Nécessite un plan de site payant.
Wix
Paramètres → Avancé → Code personnalisé → Ajouter un code personnalisé, choisissez Toutes les pages et Tête. Nécessite un plan premium avec un domaine connecté.
Espace carré
Paramètres → Avancé → Injection de code → En-tête. Nécessite le plan d'affaires ou plus.
Gestionnaire d'étiquettes Google
Créer une balise personnalisée HTML, coller le script et le déclencher sur toutes les pages ou l'initialisation – toutes les pages. Chargement à travers un gestionnaire de tags est plus tard que de l'écrire dans la tête, de sorte que les changements visuels sont plus susceptibles de clignoter.
Sites personnalisés, Réagir, Next.js
Mettez-le dans la tête de gabarit HTML, comme la mise en page racine ou _document dans Next.js, ou index.html dans les projets Vue et React.

Par défaut data-xunl-consentement="pendant": avant consentement le SDK ne touche pas le stockage du navigateur, créer un identifiant de visiteur ou envoyer des événements. Appelez l'un des suivants à partir du callback de votre bannière de cookies.

JavaScript
// After the visitor accepts analytics cookies in your consent banner
XunLExperiments.consent(true);

// If they decline or withdraw consent
XunLExperiments.consent(false);

// Or, without touching the SDK directly (also works before it has started)
window.dispatchEvent(new CustomEvent('xunl:consent', { detail: { granted: true } }));

Si votre site n'a pas besoin d'un consentement là où il fonctionne, définissez data-xunl-consent à accorder dans le script. La question de savoir si le consentement est requis dépend des règles où se trouvent votre site et les visiteurs.

Définir des objectifs d'expérience

Chaque expérience a un but principal, fixé avant le lancement; le verdict n'utilise que le but principal et d'autres buts sont à titre de référence.

ObjectifCompte quandExemple
PagesLe visiteur atteint un URL, comme une page de remerciementhttps://www.ssevening.com/thank-you/
Cliquez surUn clic sur un élément assorti d'un sélecteur CSS#quote-button
Événement personnaliséSignalé à partir de votre code avec la pisteXunLExperiments.track('quote_started')
Enquête acceptéeVotre serveur confirme que l'enquête a été enregistrée ou envoyée; voir la section suivante—

Nombre de demandes acceptées : confirmation du serveur

Sur les sites qui ne sont pas WordPress, compter les demandes acceptées en trois étapes. Envoyer seulement après que l'enquête a vraiment été enregistrée ou envoyée, jamais pour les clics, les échecs de validation ou le spam.

Étape 1: laissez les formes porter le contexte d'expérience

Ajouter data-xunl-forms="all" au script (ou un sélecteur CSS pour ne couvrir que les formulaires de demande). Les formulaires soumis incluent alors un champ caché xunl_context.

HTML
<script defer src="https://www.xunl.net/js/xunl-experiments-sdk.js" data-xunl-key="SITE_PUBLIC_KEY" data-xunl-origin="https://www.xunl.net" data-xunl-consent="pending" data-xunl-forms="all"></script>

Pour les formulaires envoyés avec fetch ou AJAX, ajoutez le contexte à la demande vous-même :

JavaScript
const body = new FormData(form);
const ctx = window.XunLExperiments && XunLExperiments.inquiryContext();
if (ctx) body.set('xunl_context', JSON.stringify(ctx));
await fetch('/api/inquiry', { method: 'POST', body });

Étape 2: signez et envoyez après que votre serveur enregistre l'enquête

Le secret du connecteur est affiché une fois lorsque vous créez la connexion; conservez-le dans la variable d'environnement du serveur XUNL_CONNECTOR_SECRET. Utilisez votre propre numéro d'enquête comme soumissionId; la reprise de la même enquête n'est pas comptée deux fois.

Node.js
// Node 18+. Call after your server has saved or sent the inquiry.
const crypto = require('node:crypto');
const XUNL = 'https://www.xunl.net', KEY = 'SITE_PUBLIC_KEY', SECRET = process.env.XUNL_CONNECTOR_SECRET;

async function confirmInquiry(xunlContext, submissionId) {
  let ctx; try { ctx = JSON.parse(xunlContext || 'null'); } catch { return false; }
  if (!ctx || ctx.preview || !ctx.visitorId || !(ctx.assignments || []).length) return false;
  const body = JSON.stringify({ eventId: crypto.randomUUID(), submissionId, visitorId: ctx.visitorId,
    acceptedAt: new Date().toISOString(), assignments: ctx.assignments, status: 'accepted' });
  const path = '/x/ab/v1/sites/' + KEY + '/conversions';
  for (let attempt = 0; attempt < 3; attempt++) { // retries keep the body, sign again
    const ts = String(Math.floor(Date.now() / 1000)), nonce = crypto.randomUUID();
    const digest = crypto.createHash('sha256').update(body).digest('hex');
    const sig = crypto.createHmac('sha256', SECRET).update(`v1\nPOST\n${path}\n${ts}\n${nonce}\n${digest}`).digest('hex');
    const res = await fetch(XUNL + path, { method: 'POST', body, redirect: 'error', headers: { 'Content-Type': 'application/json',
      'X-XunL-Timestamp': ts, 'X-XunL-Nonce': nonce, 'X-XunL-Signature': 'v1=' + sig } }).catch(() => null);
    if (res && res.ok) return true;
    if (res && res.status !== 409 && res.status < 500) return false;
    await new Promise(r => setTimeout(r, 2000 * (attempt + 1)));
  }
  return false;
}
module.exports = { confirmInquiry };
Python
# Python 3.8+. Call after your server has saved or sent the inquiry.
import hashlib, hmac, json, os, time, uuid, urllib.error, urllib.request
from datetime import datetime, timezone
XUNL, KEY, SECRET = 'https://www.xunl.net', 'SITE_PUBLIC_KEY', os.environ['XUNL_CONNECTOR_SECRET']

def confirm_inquiry(xunl_context, submission_id):
    try:
        ctx = json.loads(xunl_context or 'null')
    except ValueError:
        return False
    if not ctx or ctx.get('preview') or not ctx.get('visitorId') or not ctx.get('assignments'):
        return False
    body = json.dumps({'eventId': str(uuid.uuid4()), 'submissionId': submission_id, 'visitorId': ctx['visitorId'],
        'acceptedAt': datetime.now(timezone.utc).isoformat(timespec='milliseconds').replace('+00:00', 'Z'),
        'assignments': ctx['assignments'], 'status': 'accepted'}).encode()
    path = f'/x/ab/v1/sites/{KEY}/conversions'
    for attempt in range(3):  # retries keep the body, sign again
        ts, nonce = str(int(time.time())), str(uuid.uuid4())
        canonical = f'v1\nPOST\n{path}\n{ts}\n{nonce}\n{hashlib.sha256(body).hexdigest()}'
        sig = hmac.new(SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()
        req = urllib.request.Request(XUNL + path, data=body, method='POST', headers={'Content-Type': 'application/json',
            'X-XunL-Timestamp': ts, 'X-XunL-Nonce': nonce, 'X-XunL-Signature': 'v1=' + sig})
        try:
            with urllib.request.urlopen(req, timeout=10):
                return True
        except urllib.error.HTTPError as e:
            if e.code != 409 and e.code < 500:
                return False
        except OSError:
            pass
        time.sleep(2 * (attempt + 1))
    return False
PHP
<?php // PHP 7.4+ with cURL. Call after your server has saved or sent the inquiry.
function xunl_uuid(): string { $b = random_bytes(16); $b[6] = chr(ord($b[6]) & 0x0f | 0x40); $b[8] = chr(ord($b[8]) & 0x3f | 0x80);
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($b), 4)); }

function xunl_confirm_inquiry(string $xunlContext, string $submissionId): bool {
    $xunl = 'https://www.xunl.net'; $key = 'SITE_PUBLIC_KEY'; $secret = getenv('XUNL_CONNECTOR_SECRET');
    $ctx = json_decode($xunlContext, true);
    if (!is_array($ctx) || !empty($ctx['preview']) || empty($ctx['visitorId']) || empty($ctx['assignments'])) return false;
    $body = json_encode(['eventId' => xunl_uuid(), 'submissionId' => $submissionId, 'visitorId' => $ctx['visitorId'],
        'acceptedAt' => (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s.v\Z'),
        'assignments' => $ctx['assignments'], 'status' => 'accepted'], JSON_UNESCAPED_SLASHES);
    $path = '/x/ab/v1/sites/' . $key . '/conversions';
    for ($attempt = 0; $attempt < 3; $attempt++) { // retries keep the body, sign again
        $ts = (string) time(); $nonce = xunl_uuid();
        $sig = hash_hmac('sha256', "v1\nPOST\n$path\n$ts\n$nonce\n" . hash('sha256', $body), $secret);
        $ch = curl_init($xunl . $path);
        curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_POSTFIELDS => $body, CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 10,
            CURLOPT_HTTPHEADER => ['Content-Type: application/json', "X-XunL-Timestamp: $ts", "X-XunL-Nonce: $nonce", "X-XunL-Signature: v1=$sig"]]);
        curl_exec($ch); $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
        if ($code >= 200 && $code < 300) return true;
        if ($code > 0 && $code !== 409 && $code < 500) return false;
        sleep(2 * ($attempt + 1));
    }
    return false;
}

Étape 3: le contrat API

Adresse
POST https://www.xunl.net/x/ab/v1/sites/{SITE_PUBLIC_KEY}/conversions
En-têtes
Type de contenu: application/json · X-XunL-Timestamp · X-XunL-Nonce · X-XunL-Signature: v1=<hex>
Chaîne à signer
v1\nPOST\n/x/ab/v1/sites/{key}/conversions\n{timestamp}\n{nonce}\n{sha256(body)}
Algorithme de signature
HMAC-SHA256(secret de connexion, chaîne à signer) → hexagone inférieur
Corps
{eventId, submissionId, visitorId, acceptedAt, assignments, status: "accepted"}
Heure et nonce
Unix secondes dans les 300 s du temps du serveur; nonce est un UUID minuscule, nouveau pour chaque requête.
Réponses
200 accepté ou duplicata; 401 mauvaise signature ou heure; 409 l'affectation du navigateur n'est pas encore arrivée, donc resigner le même corps et réessayer plus tard; ne pas réessayer autre 4xx.

plugin WordPress

  1. Télécharger le plugin ZIP sous A/B expérimentations → Sites et installation, puis dans WordPress aller à Plugins → Ajouter Nouveau → Upload Plugin et l'activer.
  2. Dans WordPress → Paramètres → XunL Experiments, entrez l'adresse XunL, la clé publique du site et le secret du connecteur.
  3. Cartez les formulaires d'enquête pour compter comme type:form ID, par exemple cf7:123. Les formulaires non répertoriés ne sont jamais recueillis.
  4. Connectez le consentement du visiteur, puis vérifiez la connexion dans la console. Le plugin inclut déjà le SDK, alors n'ajoutez pas le script à nouveau.

Vérifier la connexion

  1. Accepter les cookies sur votre site, ouvrir la console du développeur de navigateur et exécuter la commande ci-dessous; consentementStatut doit être accordé.
  2. Retour dans la console, sélectionnez Vérifier la connexion et confirmer que les événements et les affectations augmentent.
  3. Ouvrir la page d'expérience plusieurs fois dans des fenêtres privées et confirmer les deux versions afficher et soumettre correctement.
JavaScript
XunLExperiments.diagnostics()
// { version, consentStatus, visitorId, assignments, pendingEvents, lastError, ... }

Foire aux questions

Puis-je l'utiliser sur un site web qui n'est pas WordPress ?

Oui. Ajoutez le script SDK JS à votre tête partagée pour exécuter des expériences visuelles et split-URL et pour mesurer les objectifs de page, de clic et d'événement personnalisé. Les demandes de renseignements acceptées sont signées et envoyées par votre propre serveur; le plugin WordPress fait simplement cette étape pour vous.

Pourquoi les demandes doivent-elles être confirmées par le serveur ?

Le navigateur voit seulement que Submit a été cliqué, ce qui inclut également les échecs de validation, spam et duplicata. Lorsque votre serveur signe l'enquête après qu'elle a vraiment été enregistrée ou envoyée, le rapport correspond à ce que les ventes reçoivent.

Est-ce que ça ralentira la page ou provoquera le scintillement ?

Le script charge asynchronement et conserve la page d'origine si la configuration échoue ou s'éteint. Les modifications visuelles s'appliquent une fois la configuration arrivée, si bien que placer le script tôt et garder les modifications petites réduit le scintillement; pour une refonte complète, utilisez split URL pour envoyer les visiteurs à la nouvelle page.

Est-ce que quelque chose est enregistré avant le consentement des visiteurs?

C'est pas vrai. Par défaut, le SDK attend : jusqu'à ce que XunLexperiments.consent(true) soit appelé ou qu'un événement xunl:consent brûle, il ne touche pas le stockage du navigateur, ne crée pas d'identifiant de visiteur ou n'envoie aucun événement expérimental. Le retrait du consentement efface les dossiers locaux.

Qu'en est-il des applications à une seule page (Réaction, Vue)?

La navigation vers l'arrière et vers l'avant est recoupée automatiquement. Après avoir changé d'itinéraires avec history.pushState, appelez XunLexperiments.refresh() une fois pour réévaluer le SDK quelles expériences s'appliquent au nouveau URL.

ÉTAPE PROCHAINE

Taille de l'échantillon ? Exécutez le test sur votre propre site

Créez un compte d'entreprise et connectez des expériences A/B via le plugin WordPress pour comparer les titres, boutons et formulaires par conversion. Pro lance 5 expériences à la fois pour jusqu'à 50 000 visiteurs par mois.

Les nouvelles entreprises reçoivent un essai Pro de 7 jours, sans carte requise. Les essais excluent la subvention de crédit mensuelle payée.

Vous avez déjà un compte ? Ouvrir votre espace de travail →

Guide pratique et questions communes pour l'installation d'essais A/B

Que fait un guide de configuration de test A/B?

Compare les deux façons de se connecter, montre où chaque constructeur de site prend le script, couvre le consentement des cookies, les quatre types de but et les applications une page, et donne des exemples complets Node.js, Python et PHP avec le contrat signé API pour confirmer les demandes.

C'est pour qui, et quand est-ce utile ?

Propriétaires et développeurs de sites qui veulent des tests A/B sur Shopify, Webflow, Wix, sites personnalisés ou WordPress, avec des demandes acceptées comptées dans les résultats.

Comment puis-je utiliser ce guide de configuration de test A/B?

Choisissez une méthode à partir du contenu : ajoutez le SDK JS à la tête de tout site ou installez le plugin WordPress, connectez le consentement, fixez des objectifs et validez les demandes de renseignements de votre serveur avec les exemples.

Comment interpréter les résultats?

La connexion nécessite un compte XunL et les expériences A/B sont une fonction payante; adapter les exemples à vos formulaires et serveurs, et garder le connecteur secret sur le serveur seulement.

Où le travail est-il effectué et quels services reçoivent les données?

Il s'agit d'un guide public; la lecture et la copie des exemples ne nécessitent pas de connexion, et la page ne recueille rien que vous tapez.