403Webshell
Server IP : 202.61.199.114  /  Your IP : 216.73.217.139
Web Server : nginx/1.22.1
System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64
User : web20 ( 1018)
PHP Version : 8.4.23
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : ON
Directory :  /var/www/clients/client2/web20/web/wp-content/plugins/arni-crm-tracking/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/clients/client2/web20/web/wp-content/plugins/arni-crm-tracking//README.md
# ArNi CRM Tracking

DSGVO-bewusstes, **cookieloses First-Party-Tracking** für WordPress – serverseitig **und** per JavaScript – ganz **ohne externe Dienste** (kein Google Analytics, kein Matomo, kein CDN, keine externen Fonts/Libraries). Alle Daten liegen in deiner eigenen Datenbank, sind im WP-Admin sichtbar und über eine **eigene, API-Key-geschützte REST-API** für ein CRM abrufbar.

- **Version:** 1.0.0
- **Autor:** ArNi Solutions · <https://www.arni-solutions.de>
- **PHP:** ≥ 7.3 · **WordPress:** ≥ 5.6
- **Plugin-Ordner:** `wp-content/plugins/arni-crm-tracking/`

---

## Inhalt

1. [Funktionsüberblick](#funktionsüberblick)
2. [Installation & Konfiguration](#installation--konfiguration)
3. [Datenmodell](#datenmodell)
4. [Tracking (Server + JavaScript)](#tracking-server--javascript)
5. [PHP-Helper](#php-helper)
6. [**API-Kommunikation CRM ↔ Plugin**](#api-kommunikation-crm--plugin)
7. [Datenschutz](#datenschutz)
8. [Cron / Aufbewahrung](#cron--aufbewahrung)
9. [Deinstallation](#deinstallation)

---

## Funktionsüberblick

- **Serverseitiges Tracking** bei jedem Frontend-Aufruf (funktioniert auch ohne JavaScript).
- **JavaScript-Events** via `navigator.sendBeacon` (Fallback `fetch`).
- **Conversion-Tracking** server- *und* clientseitig.
- **Admin-Dashboard** mit Kennzahlen, Top-Listen, Geräte-Verteilung, Conversions, CSV-Export.
- **REST-API** für CRM-Anbindung (API-Key).
- IP-Anonymisierung (immer aktiv), cookieloser Tages-Session-Hash, automatische Löschung alter Daten.

---

## Installation & Konfiguration

1. Ordner `arni-crm-tracking/` unter `wp-content/plugins/` ablegen.
2. Im WP-Admin unter **Plugins** aktivieren → legt Tabellen, Salt, API-Key und einen täglichen Cleanup-Cron an.
3. **WP-Admin → ArNi Tracking → Einstellungen** anpassen.

### Einstellungen

| Option | Beschreibung | Default |
|---|---|---|
| Tracking aktiv | Serverseitiges Tracking an/aus | an |
| JavaScript-Tracking aktiv | Frontend-JS einbinden | an |
| Bots ignorieren | Bot-Traffic nicht speichern | an |
| Eingeloggte Admins ignorieren | `manage_options`-Nutzer nicht tracken | an |
| Do-Not-Track respektieren | DNT-Header beachten | an |
| REST-API aktiv | CRM-Endpunkte erreichbar | an |
| Speicherdauer (Tage) | Auto-Löschung; `0` = unbegrenzt | 365 |
| Consent-Modus | `cookieless` oder `consent` vorbereitet | cookieless |
| Daten beim Deinstallieren löschen | Tabellen bei Uninstall droppen | aus |
| IP-Anonymisierung | **immer aktiv, nicht abschaltbar** (IPv4 /24, IPv6 /64) | fix |

Der **API-Key** wird unter *Einstellungen* angezeigt und kann dort neu generiert werden (der alte wird sofort ungültig).

---

## Datenmodell

Drei Tabellen (Präfix `wp_`):

- **`wp_arni_tracking_visits`** – ein Datensatz je Session-Hash (cookielos, täglich rotierend): `session_hash`, `first_seen`, `last_seen`, `landing_page`, `referrer`, `referrer_host`, `utm_*`, `ip_anon`, `user_agent_hash`, `device_type`, `browser`, `os`, `is_bot`, `pageviews`, `total_duration`, `created_at`, `updated_at`.
- **`wp_arni_tracking_events`** – Einzel-Events: `visit_id`, `session_hash`, `event_type`, `page_url`, `page_title`, `event_value`, `event_data` (JSON), `duration_seconds`, `scroll_depth`, `created_at`.
- **`wp_arni_tracking_conversions`** – Conversions: `visit_id`, `session_hash`, `conversion_type`, `reference_id`, `value`, `currency`, `meta` (JSON), `created_at`.

Alle Zeitstempel werden in **UTC** gespeichert.

---

## Tracking (Server + JavaScript)

### Serverseitig
Bei jedem Frontend-GET wird am Request-Ende (`shutdown`, daher kein TTFB-Einfluss) ein `pageview`-Event + Visit-Upsert geschrieben. Funktioniert **auch ohne JavaScript**.

### JavaScript-Events (an `POST /wp-json/arni-tracking/v1/event`)

| `event_type` | Auslöser |
|---|---|
| `pageview_js` | Seitenaufruf mit aktivem JS |
| `page_leave` | Verlassen der Seite (inkl. Verweildauer + max. Scrolltiefe) |
| `scroll_depth` | 25 / 50 / 75 / 100 % |
| `track_click` | Klick auf Element mit `data-track` |
| `button_click` | Klick auf `<button>` |
| `outbound_click` | Klick auf externen Link |
| `file_download` | Klick auf Datei-Link (PDF, ZIP, …) oder `download`-Attribut |
| `form_start` / `form_submit` | Formular-Start / Absenden |
| `conversion` | Conversion (siehe unten) |

**`data-track`-Beispiel:**
```html
<button data-track="cta_hero" data-position="header">Jetzt buchen</button>
```
Outbound-Links, Downloads, Buttons und Formulare werden **automatisch** erfasst – kein Attribut nötig.

---

## PHP-Helper

```php
// Serverseitige Conversion (immer möglich, unabhängig von JavaScript):
arni_track_conversion( 'booking_completed', 129.00, 'ABC123', array( 'room' => 'suite' ), 'EUR' );
arni_track_conversion( 'contact_form_sent' );

// Beliebiges serverseitiges Event:
arni_track_event( 'lead_generated', array( 'event_value' => 'kontaktformular' ) );
```

JavaScript-Pendant:
```js
window.ArNiTracking.trackConversion('booking_completed', { value: 129.00, reference_id: 'ABC123', currency: 'EUR' });
window.ArNiTracking.trackEvent('video_play', { event_value: 'intro' });
```

---

## API-Kommunikation CRM ↔ Plugin

Es gibt **zwei Richtungen**:

1. **Website → Plugin (öffentlicher Ingest):** Das Frontend-JS sendet Events/Conversions an `POST …/event`. Dieser Endpunkt ist **nicht** für das CRM gedacht.
2. **CRM → Plugin (Daten abrufen):** Das CRM **pollt** die `GET`-Endpunkte und authentifiziert sich per **API-Key im HTTP-Header**. Das ist der Hauptweg der CRM-Anbindung.

> Das Plugin pusht **nicht** von sich aus zum CRM – das CRM holt sich die Daten in seinem eigenen Intervall (Pull-Prinzip). Conversions entstehen auf der Website (JS oder PHP-Helper).

### Basis-URL
```
https://DEINE-DOMAIN/wp-json/arni-tracking/v1
```
Namespace: `arni-tracking/v1`.

### Authentifizierung (CRM-Endpunkte)

Jeder `GET`-Aufruf muss den API-Key als Header mitsenden:

```
X-ArNi-Tracking-Key: <API_KEY>
```

| Situation | HTTP-Status | Body (`code`) |
|---|---|---|
| Kein/falscher Key | `401` | `arni_forbidden` |
| REST-API in den Einstellungen deaktiviert | `403` | `arni_rest_disabled` |
| OK | `200` | siehe Endpunkt |

Der Key wird serverseitig per `hash_equals()` (zeitkonstant) geprüft. Den Key findest/erneuerst du unter **ArNi Tracking → Einstellungen**. Niemals im Frontend ausgeben.

### Gemeinsame Query-Parameter (GET-Endpunkte)

| Parameter | Typ | Beschreibung |
|---|---|---|
| `period` | string | `today`, `yesterday`, `24h`, `7d` (Default), `30d`, `90d`, `all` |
| `from` / `to` | `YYYY-MM-DD` | Eigener Zeitraum (überschreibt `period`); in **Site-Zeitzone** interpretiert |
| `limit` | int | Zeilenlimit (max. **1000**, Default 50) |
| `page` | int | Seite für Paginierung (bei `conversions`/`events`) |
| `event_type` | string | Filter (nur `/events`) |
| `conversion_type` | string | Filter (nur `/conversions`) |
| `domain` | string | optional (Single-Site: i. d. R. nicht nötig) |

Antworten enthalten immer ein `range`-Objekt mit den effektiv genutzten **UTC**-Grenzen.

### Endpunkt-Übersicht

| Methode | Pfad | Zweck | Auth |
|---|---|---|---|
| `POST` | `/event` | Event-/Conversion-Ingest (Website-JS) | – (Same-Origin) |
| `GET` | `/stats` | Kennzahlen-Überblick | API-Key |
| `GET` | `/pages` | Top-Seiten | API-Key |
| `GET` | `/referrers` | Top-Referrer (Hosts) | API-Key |
| `GET` | `/campaigns` | Top-Kampagnen (UTM) | API-Key |
| `GET` | `/conversions` | Conversions (Summen, nach Typ, Liste) | API-Key |
| `GET` | `/events` | Einzel-Events (gefiltert) | API-Key |
| `GET` | `/sessions` | Vollständige Session-Journeys (Pfad, Events, Conversions) | API-Key |

---

### `GET /stats`

```bash
curl -s "https://DOMAIN/wp-json/arni-tracking/v1/stats?period=30d" \
  -H "X-ArNi-Tracking-Key: $KEY"
```
**Antwort:**
```json
{
  "range": { "from": "2026-05-13 22:00:00", "to": "2026-06-12 21:59:59" },
  "visitors": 1840,
  "pageviews": 5231,
  "avg_duration": 78.4,
  "conversions": 42,
  "conversion_value": 5418.00,
  "conversion_rate": 2.28,
  "devices": [ { "label": "desktop", "hits": "1100" }, { "label": "mobile", "hits": "640" } ],
  "form_abandonments": 17
}
```

### `GET /pages`
```json
{ "range": {…}, "items": [ { "label": "https://…/preise/", "title": "Preise", "hits": "812" } ] }
```

### `GET /referrers`
```json
{ "range": {…}, "items": [ { "label": "google.com", "hits": "430" } ] }
```

### `GET /campaigns`
```json
{ "range": {…}, "items": [ { "label": "sommer2026", "source": "newsletter", "medium": "email", "hits": "120" } ] }
```

### `GET /conversions`
```bash
curl -s "https://DOMAIN/wp-json/arni-tracking/v1/conversions?period=30d&conversion_type=booking_completed&limit=100&page=1" \
  -H "X-ArNi-Tracking-Key: $KEY"
```
```json
{
  "range": {…},
  "totals": { "count": 42, "value": 5418.0 },
  "by_type": [ { "label": "booking_completed", "hits": "42", "total": "5418.00" } ],
  "items": [
    {
      "id": "128", "visit_id": "934",
      "session_hash": "d7360344a1522bc0…",
      "conversion_type": "booking_completed",
      "reference_id": "ABC123",
      "value": "129.00", "currency": "EUR",
      "meta": "{\"room\":\"suite\"}",
      "created_at": "2026-06-12 09:14:55"
    }
  ]
}
```

### `GET /events`
```bash
curl -s "https://DOMAIN/wp-json/arni-tracking/v1/events?from=2026-06-01&to=2026-06-12&event_type=outbound_click&limit=200&page=1" \
  -H "X-ArNi-Tracking-Key: $KEY"
```
```json
{
  "range": {…},
  "items": [
    {
      "id": "9931", "visit_id": "934", "session_hash": "d7360344…",
      "event_type": "outbound_click", "page_url": "https://…/blog/",
      "page_title": "Blog", "event_value": "https://partner.example/",
      "event_data": "{\"host\":\"partner.example\"}",
      "duration_seconds": "0", "scroll_depth": "0",
      "created_at": "2026-06-12 10:02:11"
    }
  ]
}
```

> Hinweis: Zahlenfelder kommen als String aus MySQL zurück – im CRM ggf. casten. Paginierung über `page` + `limit`; ist `items` kürzer als `limit`, gibt es keine weitere Seite.

### `GET /sessions`

Liefert pro Besuch (Session) die **komplette Customer-Journey**: Stammdaten, Seitenpfad mit Verweildauer, Interaktions-Events und Conversions. Ideal, um im CRM eine Besucher-/Lead-Timeline aufzubauen.

```bash
curl -s "https://DOMAIN/wp-json/arni-tracking/v1/sessions?from=2026-06-01&to=2026-06-12&limit=50&page=1" \
  -H "X-ArNi-Tracking-Key: $KEY"
```
```json
{
  "range": { "from": "2026-05-31 22:00:00", "to": "2026-06-12 21:59:59" },
  "items": [
    {
      "visit_id": 934,
      "session_hash": "d736…",
      "started_at": "2026-06-12 10:01:55",
      "last_seen": "2026-06-12 10:14:55",
      "duration_seconds": 780,
      "country": "DE",
      "device": "desktop",
      "user_agent": "Chrome · Windows · desktop",
      "referrer": "google.com",
      "utm_source": "newsletter", "utm_medium": "email", "utm_campaign": "sommer2026",
      "entry_page": "https://…/",
      "exit_page": "https://…/preise/",
      "pageviews": 6,
      "is_bounce": false,
      "pages": [ { "url": "https://…/", "title": "Start", "ts": "2026-06-12 10:01:55", "dwell": 42 } ],
      "events": [ { "type": "outbound_click", "value": "https://partner…", "ts": "2026-06-12 10:02:25" } ],
      "conversions": [ { "conversion_type": "booking_completed", "value": 129.0, "reference_id": "ABC123", "currency": "EUR" } ]
    }
  ]
}
```

**Feld-Herkunft & Hinweise:**

- `country` – ISO-3166-alpha-2, **ohne externen Dienst** ermittelt (nur das Land, nie die IP). Quellen in Reihenfolge:
  1. **Reverse-Proxy-/CDN-Header:** Cloudflare (`CF-IPCountry`), AWS CloudFront (`CloudFront-Viewer-Country`), Google App Engine (`X-AppEngine-Country`), generische `X-GeoIP-Country-Code` / `X-Geo-Country` / `X-Country-Code`.
  2. **Webserver-GeoIP-Modul:** `$_SERVER['GEOIP_COUNTRY_CODE']` (nginx `ngx_http_geoip`, Apache `mod_geoip`) bzw. `MM_COUNTRY_CODE` (MaxMind).
  3. **Lokale PHP-GeoIP-Erweiterung** `geoip_country_code_by_name()`, falls auf dem Server installiert (lokale DB, kein Netzwerkdienst).
  4. **Filter `arni_crm_tracking_country`** für eigene Auflösung (z. B. eigener MaxMind-Reader). Beispiel:
     ```php
     add_filter( 'arni_crm_tracking_country', function ( $cc ) {
         if ( $cc ) { return $cc; }
         // eigene Logik, z. B. MaxMind-DB-Reader auf Basis von $_SERVER['REMOTE_ADDR']
         return $cc;
     } );
     ```
  Leer (`""`), wenn keine Quelle ein Land liefert. `XX`/`T1` (unbekannt/Tor) werden als leer gewertet.
- `user_agent` – standardmäßig eine **zusammengesetzte** Angabe `"Browser · OS · Gerät"`. Den **rohen** User-Agent gibt es nur, wenn unter *Einstellungen → „Vollständigen User-Agent speichern"* aktiviert (Default **aus**, Privacy-by-Design). Der UA wird unabhängig davon immer **gehasht** gespeichert.
- `referrer` – externer Referrer-**Host** (interne Navigation wird nicht als Referrer gewertet).
- `pages[].dwell` – Verweildauer in Sekunden, berechnet aus dem Abstand aufeinanderfolgender Pageviews; die letzte Seite hat `dwell: 0`.
- `exit_page` – letzte aufgerufene Seite; `is_bounce` – `true` bei nur einem Pageview.
- `events` enthält Interaktionen (Klicks, Scroll, Formular, Verlassen …) **ohne** Pageviews; Pageviews stehen in `pages`.
- **Limit:** wegen der verschachtelten Daten max. **200** Sessions pro Aufruf (Default 25); verschachtelte `pages`/`events` sind je Session auf 300 Einträge begrenzt. Paginierung über `page`.
- Performance: pro Aufruf nur **3 Datenbank-Queries** (Visits, Events, Conversions) – Gruppierung erfolgt in PHP.

---

### `POST /event` (Website-Ingest, kein CRM)

Wird vom Frontend-JS genutzt. **Öffentlich**, aber mit leichtem **Same-Origin-Schutz** (Header `Origin`/`Referer` muss zur Site-Domain passen, sofern gesetzt). Kein API-Key. Die Session wird **serverseitig** aus anonymisierter IP + User-Agent + Tages-Salt berechnet – der Client sendet **keinen** Session-Identifier.

**Einzel-Event:**
```json
{ "event_type": "scroll_depth", "scroll_depth": 75,
  "page_url": "https://DOMAIN/", "page_title": "Start" }
```
**Conversion:**
```json
{ "event_type": "conversion", "conversion_type": "newsletter_signup",
  "value": 0, "reference_id": "NL-9", "currency": "EUR", "meta": { "src": "footer" } }
```
**Batch (max. 25):**
```json
{ "events": [ { "event_type": "page_leave", "duration_seconds": 64, "scroll_depth": 90 } ] }
```
**Antwort:** `{ "ok": true, "saved": 1 }` · bei Same-Origin-Verstoß `403`, bei deaktiviertem/DNT-Tracking `202 { "ok": false, "reason": "…" }`.

---

### CRM-Integrationsbeispiele

**Polling (Bash/cron im CRM):**
```bash
KEY="arni_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
BASE="https://DOMAIN/wp-json/arni-tracking/v1"
curl -s "$BASE/conversions?from=2026-06-01&to=2026-06-12&limit=1000" \
     -H "X-ArNi-Tracking-Key: $KEY" | jq '.items[]'
```

**PHP (CRM-seitig):**
```php
$res = wp_remote_get( 'https://DOMAIN/wp-json/arni-tracking/v1/stats?period=7d', array(
    'headers' => array( 'X-ArNi-Tracking-Key' => ARNI_KEY ),
    'timeout' => 15,
) );
$data = json_decode( wp_remote_retrieve_body( $res ), true );
```

**Python (CRM-seitig):**
```python
import requests
r = requests.get(
    "https://DOMAIN/wp-json/arni-tracking/v1/conversions",
    params={"period": "30d", "limit": 1000, "page": 1},
    headers={"X-ArNi-Tracking-Key": KEY}, timeout=15,
)
for c in r.json()["items"]:
    print(c["conversion_type"], c["reference_id"], c["value"], c["created_at"])
```

**Empfohlener Pull-Workflow fürs CRM:**
1. Periodisch (z. B. alle 15 Min) `/conversions` mit `from = letzter_abruf` ziehen.
2. Über `reference_id` mit eigenen Datensätzen abgleichen (Deduplizierung).
3. `created_at` (UTC) als Hochwasser-Marke für den nächsten Abruf speichern.
4. Bei großen Mengen über `page` + `limit` (max. 1000) blättern.

---

## Datenschutz

- Keine externen Dienste, kein CDN, keine externen Fonts/Libraries.
- **Niemals vollständige IPs** (IPv4 /24, IPv6 /64). Gehashter User-Agent (mit Salt).
- Cookieloser, **täglich rotierender** Session-Hash → keine Langzeit-/Cross-Site-Profile.
- Keine Cookies im Standardmodus. Opt-out per Link: `https://DOMAIN/?arni_optout=1`.
- DNT wird (optional) respektiert. Fertiger **Datenschutz-Textbaustein** unter *ArNi Tracking → Datenschutz*.

---

## Cron / Aufbewahrung

- Täglicher WP-Cron-Hook `arni_crm_tracking_cleanup` löscht Daten älter als die Speicherdauer.
- Ist `DISABLE_WP_CRON` gesetzt, muss `wp-cron.php` per System-Cron laufen, z. B.:
  ```cron
  */5 * * * *  web32  /usr/bin/php8.4 -d max_execution_time=300 /pfad/zu/web/wp-cron.php >/dev/null 2>&1
  ```

---

## Deinstallation

`uninstall.php` entfernt den Cron immer; **Tabellen & Optionen** werden nur gelöscht, wenn unter *Einstellungen* „Daten beim Deinstallieren löschen" aktiviert ist. Andernfalls bleiben die Daten erhalten.

Youez - 2016 - github.com/yon3zu
LinuXploit