Start » Blog » Analytics / Measurement Protocol
23.11.2015

Das Measurement Protocol: Transaktionen stornieren per Refund-Event

Wer E-Commerce-Daten in der Webanalyse ernst nimmt, kommt um Stornos nicht herum. Solange nur gebuchte Umsätze in den Reports stehen, lügen die Zahlen - manchmal ein bisschen, manchmal erheblich. Und Entscheidungen auf Basis geschönter Umsatzzahlen pro Kanal sind ungefähr so hilfreich wie eine Wettervorhersage von gestern. Dieser Beitrag zeigt, wie man per GA4 Measurement Protocol Transaktionen sauber storniert - und warum das heute erfreulich wenig Aufwand ist.

Alle Teile dieser Serie

Warum Stornos in die Webanalyse gehören

Nicht jeder gebuchte Umsatz hat Bestand. Retouren, Stornos und Teilrückerstattungen verschieben die Verhältnisse - und zwar nicht gleichmäßig über alle Kanäle. Wenn Käufe aus Social-Kampagnen eine Stornoquote von 40% haben und organische Käufe bei 5% liegen, erzählt ein Report ohne Stornodaten eine komplett falsche Geschichte über die Rentabilität dieser Kampagnen. Der einzig zuverlässige Ort für harte Umsatzzahlen bleibt die eigene Buchhaltung. Aber für die Zuordnung von Umsatz zu Kanälen, Kampagnen und Landingpages braucht man korrigierte Zahlen auch in der Webanalyse.

So funktioniert ein Refund per GA4 Measurement Protocol

GA4 kennt ein dediziertes refund Event. Das macht die Sache angenehm direkt: Ein POST-Request mit JSON-Body an den Measurement Protocol Endpoint - fertig. Kein Jonglieren mit negativen Vorzeichen, keine separaten Hits pro Artikel.

Endpoint und Authentifizierung

Der Request geht an den GA4 Measurement Protocol Endpoint. Measurement ID und API Secret werden als Query-Parameter übergeben:

POST https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXX&api_secret=YOUR_SECRET
Content-Type: application/json

Das API Secret erstellt man in GA4 unter Verwaltung > Datenstreams > [Stream auswählen] > Measurement Protocol API Secrets. Das Secret ist kein Bearer Token im klassischen Sinn - es verhindert nur zufällige Hits, nicht gezielte Manipulation. Für den Anwendungsfall Stornos aus dem eigenen Backend reicht das aber völlig aus.

Vollstorno: Die ganze Transaktion rückgängig machen

Ein Vollstorno storniert die komplette Transaktion inklusive aller Artikel. Der JSON-Body enthält die transaction_id der ursprünglichen Transaktion, den Gesamtwert und alle Positionen:

{
  "client_id": "123456.789012",
  "events": [{
    "name": "refund",
    "params": {
      "currency": "EUR",
      "transaction_id": "TRANS123",
      "value": 300.00,
      "items": [{
        "item_id": "ART321",
        "item_name": "Artikelname",
        "quantity": 1,
        "price": 300.00
      }]
    }
  }]
}

Das war's. Ein Request, ein Event, alles drin. Die transaction_id muss exakt mit der ursprünglichen Transaktion übereinstimmen - darüber findet GA4 die Zuordnung. Und ganz wichtig: Die Werte sind positiv. Keine negativen Beträge, keine negativen Mengen. Der Eventname refund signalisiert GA4, dass es sich um eine Stornierung handelt.

Transaktion mit mehreren Artikeln

Hatte der ursprüngliche Kauf mehrere Positionen, werden beim Vollstorno alle im items-Array aufgeführt:

{
  "client_id": "123456.789012",
  "events": [{
    "name": "refund",
    "params": {
      "currency": "EUR",
      "transaction_id": "TRANS456",
      "value": 478.97,
      "items": [
        {
          "item_id": "SHOE-42",
          "item_name": "Laufschuh Modell X",
          "quantity": 1,
          "price": 129.99
        },
        {
          "item_id": "JACK-M",
          "item_name": "Regenjacke Gr. M",
          "quantity": 2,
          "price": 149.99
        },
        {
          "item_id": "SOCK-3P",
          "item_name": "Laufsocken 3er Pack",
          "quantity": 1,
          "price": 49.00
        }
      ]
    }
  }]
}

Teilstorno: Nur einzelne Positionen stornieren

In der Praxis wird nicht immer die ganze Bestellung zurückgeschickt. Wenn nur ein Artikel retourniert wird, enthält der Refund-Request auch nur diesen Artikel - und der value entspricht dem Wert der stornierten Position:

{
  "client_id": "123456.789012",
  "events": [{
    "name": "refund",
    "params": {
      "currency": "EUR",
      "transaction_id": "TRANS456",
      "value": 149.99,
      "items": [{
        "item_id": "JACK-M",
        "item_name": "Regenjacke Gr. M",
        "quantity": 1,
        "price": 149.99
      }]
    }
  }]
}

Hier wird nur eine der beiden Regenjacken storniert: quantity ist 1, nicht 2. Der value auf Event-Ebene spiegelt den Wert der tatsächlich stornierten Ware wider. Das Prinzip ist simpel: Was storniert wird, kommt in den Request. Was bleibt, bleibt draußen.

Batch-Verarbeitung: Mehrere Stornos auf einmal

Wer Stornos nicht einzeln in Echtzeit sendet, sondern gesammelt aus einem ERP-System oder Stornolog verarbeitet, kann mehrere Events in einem Request bündeln. Das events-Array nimmt bis zu 25 Events pro Request auf:

{
  "client_id": "batch-refund-processor.1",
  "events": [
    {
      "name": "refund",
      "params": {
        "currency": "EUR",
        "transaction_id": "TRANS-2026-0401",
        "value": 59.90,
        "items": [{
          "item_id": "ART-100",
          "item_name": "Artikel A",
          "quantity": 1,
          "price": 59.90
        }]
      }
    },
    {
      "name": "refund",
      "params": {
        "currency": "EUR",
        "transaction_id": "TRANS-2026-0402",
        "value": 199.00,
        "items": [{
          "item_id": "ART-200",
          "item_name": "Artikel B",
          "quantity": 1,
          "price": 199.00
        }]
      }
    }
  ]
}

Bei mehr als 25 Stornos pro Durchlauf einfach mehrere Requests nacheinander senden. Wer das automatisiert, baut sich einen simplen Batch-Prozess, der regelmäßig die offenen Stornos aus der eigenen Datenbank abfragt, in Pakete aufteilt und an GA4 übergibt. Das ist ein überschaubares Script - die eigentliche Arbeit steckt im Datenzugriff auf das ERP, nicht im Analytics-Teil.

Hinweis zur Client ID

Die client_id ist ein Pflichtfeld im GA4 Measurement Protocol. Idealerweise verwendet man die Client ID des ursprünglichen Käufers - falls man sie gespeichert hat. Für Batch-Stornos aus dem Backend, wo diese Information oft nicht mehr vorliegt, kann man eine konsistente synthetische ID verwenden. GA4 ordnet den Refund über die transaction_id zu, nicht über die Client ID. Die Client ID muss nur formal vorhanden und im richtigen Format sein (zwei durch einen Punkt getrennte Zahlengruppen).

Was in den Reports passiert - und wann

Ein paar Dinge, die man wissen sollte, bevor man sich wundert:

Timing: GA4 verarbeitet Measurement Protocol Hits nicht in Echtzeit-Reports. Die Daten tauchen in den Standardberichten mit der üblichen Verzögerung auf - in der Regel innerhalb von 24-48 Stunden. Wer sofort prüfen will, ob der Request angekommen ist, nutzt den GA4 Event Builder oder die Validation-Variante des Endpoints (dazu gleich mehr).

Stornieren ist nicht Löschen: Ein Refund-Event erzeugt einen Gegeneintrag, keine Löschung. Die ursprüngliche Transaktion bleibt in den Rohdaten bestehen. In aggregierten Reports werden Umsatz und Mengen entsprechend verrechnet - aber nur für Zeiträume, die sowohl den Kauf als auch den Storno umfassen. Wer sich den Mai anschaut und der Storno erst im Juni kam, sieht im Mai nach wie vor den vollen Umsatz. Genau wie in der Buchhaltung.

BigQuery-Export: Wer den GA4 BigQuery-Export nutzt, findet dort das refund Event als eigenständigen Eintrag. Das ist praktisch, weil man in SQL beide Seiten - Kauf und Storno - sauber gegeneinander auswerten kann.

Debugging: Validation Server

Bevor man Stornos produktiv feuert, empfiehlt sich ein Test gegen den Validation Endpoint. Statt an /mp/collect sendet man den identischen Request an:

POST https://www.google-analytics.com/debug/mp/collect?measurement_id=G-XXXXXX&api_secret=YOUR_SECRET

Dieser Endpoint verarbeitet keine Daten, sondern gibt eine JSON-Response mit Validierungsergebnissen zurück. Fehlende Pflichtfelder, ungültige Formate, falsche Datentypen - alles wird gemeldet. Der produktive Endpoint gibt dagegen immer nur einen 204 No Content zurück, egal ob der Payload valide war oder nicht. Also: Erst validieren, dann senden.

Alternative: Offline Event Import

Das Measurement Protocol ist nicht der einzige Weg, Refunds nachträglich in GA4 zu bringen. Über den Offline Event Import lassen sich Events - darunter auch refund - per CSV-Upload oder API in GA4 importieren. Das kann die bessere Wahl sein, wenn Stornos nicht in Echtzeit, sondern in größeren Batches verarbeitet werden sollen oder die Integration in ein bestehendes ETL-Setup einfacher ist als ein eigener MP-Client. Die Zuordnung funktioniert analog über die transaction_id.

Historische Fußnote

Wer das Measurement Protocol noch aus der Universal Analytics-Ära kennt, erinnert sich vielleicht an den Aufwand: Separate Hits für die Transaktion und jeden einzelnen Artikel, negative Vorzeichen bei Umsatz und Mengen, und wehe man hat den Preis statt der Menge negiert - dann hatte man plötzlich doppelte Stückzahlen im System. Das GA4 Measurement Protocol macht all das mit einem einzigen refund Event und positiven Werten obsolet. Fortschritt, der den Namen verdient.

War der Beitrag hilfreich?

Dann freue ich mich, wenn er mit anderen geteilt wird!

Ko-fi Einen Tee ausgeben ;)