XUNL EXPERIMENTS / JS SDK · WORDPRESS · SERVER API

A/B-Test-Setup-Anleitung

A/B-Tests auf jeder Website, wobei Anfragen korrekt gezählt werden.

Öffentlicher Reiseführer · keine Anmeldung
Möchten Sie A/B-Tests auf Ihrer eigenen Website durchführen?

XunL-Experimente: Seiten visuell bearbeiten, Besucher konsistent zuordnen, akzeptierte Anfragen auf dem Server bestätigen und Berichte mit Konfidenzintervallen und einem klaren Entscheidungsstatus erhalten.

A/B-Experimente öffnen → (English)

Wählen Sie, wie Sie eine Verbindung herstellen möchten

Beide nutzen die gleiche Site-Verbindung, dieselben Experimente und Berichte; Lediglich Installation und Anfragebestätigung unterscheiden sich.

JS SDKWordPress-Plugin
WebsitesJede Website: Shopify, Webflow, Wix, Squarespace, Tag-Manager, benutzerdefinierte WebsitesWordPress 6.2 oder höher
InstallierenFügen Sie eine Skriptzeile zum gemeinsamen Kopf hinzuLaden Sie die Plugin-ZIP-Datei hoch und geben Sie die Verbindungsdetails ein
Seiten-, Klick- und benutzerdefinierte EreigniszieleUnterstützungUnterstützung
Akzeptiertes AnfragezielVon Ihrem Server signiert (Node.js-, Python- und PHP-Beispiele unten)Automatisch für Contact Form 7, Elementor Pro, WPForms und Gravity Forms

Jede Website: Installieren Sie das JS SDK

  1. Melden Sie sich bei XunL an, fügen Sie die Website unter „Sites“ hinzu und erstellen Sie dann eine Verbindung unter „A/B-Experimente → Sites & Installation“.
  2. Kopieren Sie das Skript, das die Konsole anzeigt, und platzieren Sie es so früh wie möglich im Header, der von jeder Seite gemeinsam genutzt wird. Ersetzen Sie SITE_PUBLIC_KEY im Beispiel durch den öffentlichen Schlüssel Ihrer Site.
  3. Verbinden Sie die Besuchereinwilligung (unten) und wählen Sie dann in der Konsole Verbindung prüfen aus.
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>

Das Skript liest nur die öffentliche Experimentkonfiguration; Geben Sie das Connector-Geheimnis niemals in eine Webseite ein. Rufen Sie in Single-Page-Apps XunLExperiments.refresh() auf, nachdem Sie Routen mit History.pushState geändert haben.

Wo kann es auf jeder Plattform hinzugefügt werden?

Shopify
Online-Shop → Themes → Code bearbeiten → layout/theme.liquid, direkt vor dem schließenden Head-Tag einfügen. Der Checkout kann nur bei Shopify Plus bearbeitet werden. Legen Sie daher vor dem Checkout Ziele auf den Seiten fest.
Webflow
Site-Einstellungen → Benutzerdefinierter Code → Kopfcode, einfügen und veröffentlichen. Erfordert einen kostenpflichtigen Lageplan.
Wix
Einstellungen → Erweitert → Benutzerdefinierter Code → Benutzerdefinierten Code hinzufügen, wählen Sie „Alle Seiten“ und „Kopf“. Erfordert einen Premium-Plan mit einer verbundenen Domain.
Squarespace
Einstellungen → Erweitert → Code-Injection → Header. Erfordert den Businessplan oder höher.
Google Tag Manager
Erstellen Sie ein benutzerdefiniertes HTML-Tag, fügen Sie das Skript ein und lösen Sie es auf „Alle Seiten“ oder „Initialisierung – Alle Seiten“ aus. Das Laden über einen Tag-Manager erfolgt später als das Schreiben in den Kopf, sodass visuelle Änderungen eher flackern.
Benutzerdefinierte Websites, React, Next.js
Fügen Sie es in den HTML-Vorlagenkopf ein, z. B. das Root-Layout oder _document in Next.js oder index.html in Vue- und React-Projekten.

Standardmäßig data-xunl-consent="pending": Vor der Einwilligung greift das SDK nicht in den Browserspeicher ein, erstellt keine Besucher-ID und sendet keine Ereignisse. Rufen Sie über den Rückruf Ihres Cookie-Banners einen der folgenden Schritte auf:

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 } }));

Wenn Ihre Website dort, wo sie betrieben wird, keine Einwilligung benötigt, setzen Sie „data-xunl-consent“ im Skript auf „Gewährt“. Ob eine Einwilligung erforderlich ist, hängt von den Regeln ab, wo sich Ihre Website und Besucher aufhalten.

Legen Sie Experimentziele fest

Jedes Experiment hat ein Hauptziel, das vor dem Start festgelegt wird; Das Urteil verwendet nur das Hauptziel und andere Ziele dienen als Referenz.

ZielCounts whenBeispiel
SeitenDer Besucher erreicht eine URL, beispielsweise eine Dankesseitehttps://www.ssevening.com/thank-you/
Klicken SieEin Klick auf ein Element, das mit einem CSS-Selektor übereinstimmt#quote-button
Benutzerdefiniertes EreignisVon Ihrem Code mit Track gemeldetXunLExperiments.track('quote_started')
Akzeptierte AnfrageIhr Server bestätigt, dass die Anfrage gespeichert oder gesendet wurde; siehe nächsten Abschnitt—

Anzahl akzeptierter Anfragen: Serverbestätigung

Auf Websites, die nicht WordPress sind, zählen Sie akzeptierte Anfragen in drei Schritten. Senden Sie nur, nachdem die Anfrage tatsächlich gespeichert oder gesendet wurde, niemals bei Klicks, Validierungsfehlern oder Spam.

Schritt 1: Lassen Sie Formulare den Experimentkontext tragen

Fügen Sie data-xunl-forms="all" zum Skript hinzu (oder einen CSS-Selektor, um nur Anfrageformulare abzudecken). Eingereichte Formulare enthalten dann ein verstecktes xunl_context-Feld.

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>

Fügen Sie bei Formularen, die mit Fetch oder AJAX gesendet werden, den Kontext selbst zur Anfrage hinzu:

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 });

Schritt 2: Unterschreiben und senden, nachdem Ihr Server die Anfrage gespeichert hat

Das Connector-Geheimnis wird einmalig angezeigt, wenn Sie die Verbindung erstellen; Behalten Sie es in der Serverumgebungsvariablen XUNL_CONNECTOR_SECRET. Verwenden Sie Ihre eigene Anfragenummer als SubmissionId; Das erneute Absenden derselben Anfrage wird nicht zweimal gezählt.

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;
}

Schritt 3: der API-Vertrag

Adresse
POST https://www.xunl.net/x/ab/v1/sites/{SITE_PUBLIC_KEY}/conversions
Kopfzeilen
Inhaltstyp: application/json · X-XunL-Timestamp · X-XunL-Nonce · X-XunL-Signature: v1=<hex>
Zeichenfolge zum Signieren
v1\nPOST\n/x/ab/v1/sites/{key}/conversions\n{timestamp}\n{nonce}\n{sha256(body)}
Algorithmus
HMAC-SHA256 (Connector-Geheimnis, zu signierende Zeichenfolge) → Kleinbuchstabe hexadezimal
Körper
{EventId, SubmissionId, VisitorId, AcceptedAt, Assignments, Status: „Accepted“}
Zeit und Nonce
Unix-Sekunden innerhalb von 300 s Serverzeit; Nonce ist eine UUID in Kleinbuchstaben, die für jede Anfrage neu ist.
Antworten
200 akzeptiert oder dupliziert; 401 schlechte Signatur oder Zeit; 409 Die Browserzuweisung ist noch nicht eingetroffen. Signieren Sie daher denselben Text erneut und versuchen Sie es später noch einmal. Versuchen Sie es nicht erneut mit anderen 4xx.

WordPress-Plugin

  1. Laden Sie das Plugin ZIP unter A/B-Experimente → Websites & Installation herunter, gehen Sie dann in WordPress zu Plugins → Neu hinzufügen → Plugin hochladen und aktivieren Sie es.
  2. Geben Sie in WordPress → Einstellungen → XunL-Experimente die XunL-Adresse, den öffentlichen Schlüssel der Site und das Connector-Geheimnis ein.
  3. Ordnen Sie die Anfrageformulare so zu, dass sie als Typ:Formular-ID gelten, zum Beispiel cf7:123. Nicht aufgeführte Formulare werden niemals gesammelt.
  4. Verbinden Sie die Besuchereinwilligung und überprüfen Sie dann die Verbindung in der Konsole. Das Plugin enthält bereits das SDK, also fügen Sie das Skript nicht erneut hinzu.

Überprüfen Sie die Verbindung

  1. Akzeptieren Sie Cookies auf Ihrer Website, öffnen Sie die Browser-Entwicklerkonsole und führen Sie den folgenden Befehl aus; „consentStatus“ sollte gewährt werden.
  2. Zurück in der Konsole wählen Sie Verbindung prüfen und bestätigen, dass Ereignisse und Zuweisungen zunehmen.
  3. Öffnen Sie die Experimentseite mehrmals in privaten Fenstern und bestätigen Sie, dass beide Versionen korrekt angezeigt und übermittelt werden.
JavaScript
XunLExperiments.diagnostics()
// { version, consentStatus, visitorId, assignments, pendingEvents, lastError, ... }

Häufig gestellte Fragen

Kann ich es auf einer Website verwenden, die nicht WordPress ist?

Ja. Fügen Sie das JS SDK-Skript zu Ihrem Shared Head hinzu, um visuelle und Split-URL-Experimente durchzuführen und Seiten-, Klick- und benutzerdefinierte Ereignisziele zu messen. Akzeptierte Anfragen werden von Ihrem eigenen Server signiert und versendet; Das WordPress-Plugin erledigt diesen Schritt einfach für Sie.

Warum müssen Anfragen vom Server bestätigt werden?

Der Browser sieht nur, dass auf „Senden“ geklickt wurde, was auch Validierungsfehler, Spam und Duplikate einschließt. Wenn Ihr Server die Anfrage signiert, nachdem sie tatsächlich gespeichert oder gesendet wurde, stimmt der Bericht mit dem überein, was die Verkäufe erhalten.

Verlangsamt es die Seite oder verursacht es Flimmern?

Das Skript wird asynchron geladen und behält die Originalseite bei, wenn die Konfiguration fehlschlägt oder eine Zeitüberschreitung auftritt. Visuelle Änderungen werden wirksam, sobald die Konfiguration eintrifft. Wenn Sie das Skript also frühzeitig platzieren und die Änderungen gering halten, wird das Flimmern reduziert. Für eine vollständige Neugestaltung verwenden Sie eine geteilte URL, um Besucher auf die neue Seite zu leiten.

Wird vor der Zustimmung der Besucher etwas aufgezeichnet?

Nein. Standardmäßig wartet das SDK: bis XunLExperiments.consent(true) aufgerufen wird oder ein xunl:consent-Ereignis ausgelöst wird, greift es nicht auf den Browserspeicher zu, erstellt keine Besucher-ID und sendet kein Experimentereignis. Durch den Widerruf der Einwilligung werden die lokalen Aufzeichnungen gelöscht.

Was ist mit Single-Page-Apps (React, Vue)?

Die Vorwärts- und Vorwärtsnavigation wird automatisch wieder abgeglichen. Nachdem Sie die Routen mit „history.pushState“ geändert haben, rufen Sie XunLExperiments.refresh() einmal auf, damit das SDK neu bewertet, welche Experimente für die neue URL gelten.

IHR NÄCHSTER SCHRITT

Verwandeln Sie eine Website-Überprüfung in eine Überwachungsroutine

Erstellen Sie ein Unternehmenskonto, um Websites und Benachrichtigungen zu verwalten. Pro unterstützt 5 Standorte und eine 5-minütige Verfügbarkeitsüberwachung, wobei je nach Plan und Berechtigungen umfassende Diagnosen verfügbar sind.

Neue Unternehmen erhalten eine 14-tägige Pro-Testversion, für die keine Karte erforderlich ist. Bei Testversionen ist der bezahlte monatliche Kreditzuschuss nicht inbegriffen.

Sie haben bereits ein Konto? Öffnen Sie Ihren Arbeitsbereich → (English)

A/B-Test-Setup-Leitfaden, praktischer Leitfaden und häufig gestellte Fragen

Was macht ein A/B-Test-Setup-Leitfaden?

Vergleicht die beiden Verbindungsarten, zeigt, woher jeder Site-Builder das Skript nimmt, deckt die Cookie-Zustimmung, die vier Zieltypen und Single-Page-Apps ab und liefert vollständige Node.js-, Python- und PHP-Beispiele mit dem unterzeichneten API-Vertrag zur Bestätigung von Anfragen.

Für wen ist das gedacht und wann ist es nützlich?

Websitebesitzer und Entwickler, die A/B-Tests auf Shopify, Webflow, Wix, benutzerdefinierten Websites oder WordPress wünschen, wobei akzeptierte Anfragen in den Ergebnissen gezählt werden.

Wie verwende ich diesen A/B-Test-Setup-Leitfaden?

Wählen Sie eine Methode aus dem Inhalt aus: Fügen Sie das JS SDK im Header einer beliebigen Site hinzu oder installieren Sie das WordPress-Plugin, verknüpfen Sie die Zustimmung, legen Sie Ziele fest und bestätigen Sie Anfragen von Ihrem Server anhand der Beispiele.

Wie soll ich die Ergebnisse interpretieren?

Für die Verbindung ist ein XunL-Konto erforderlich und A/B-Experimente sind eine kostenpflichtige Funktion; Passen Sie die Beispiele an Ihre Formulare und Ihren Server an und halten Sie den Connector nur auf dem Server geheim.

Wo wird die Arbeit ausgeführt und welche Dienste erhalten Daten?

Dies ist ein öffentlicher Leitfaden; Für das Lesen und Kopieren der Beispiele ist keine Anmeldung erforderlich, und die Seite sammelt nichts, was Sie eingeben.