Klaviyo API Integration mit Drupal 11 & Drupal Commerce

Steven Schulz
Steven Schulz

Klaviyo API Integration mit Drupal 11

Klaviyo ist der Goldstandard für E-Commerce E-Mail-Marketing. Warenkorbabbrecher-E-Mails, personalisierte Produktempfehlungen, Post-Purchase-Flows – alles basierend auf echtem Kundenverhalten.

In diesem Guide zeige ich, wie Sie Klaviyo über die API v3 in Drupal 11 und Drupal Commerce integrieren.

Warum Klaviyo für E-Commerce?

  1. E-Commerce-DNA: Klaviyo versteht Bestellungen, Produkte, Warenkörbe nativ
  2. Predictive Analytics: Vorhersagen zu Customer Lifetime Value und Churn-Risiko
  3. Segmentierung: Dynamische Segmente basierend auf Kaufverhalten
  4. Flows: Automatisierte Sequenzen für jeden Touchpoint der Customer Journey
  5. SMS-Marketing: E-Mail und SMS aus einer Plattform

Die Architektur

┌─────────────────────┐        ┌─────────────────────┐
│    Drupal 11        │        │      Klaviyo        │
│  Drupal Commerce    │───────▶│   E-Mail & SMS      │
└─────────────────────┘        └─────────────────────┘
         │                              │
         ▼                              ▼
   Events tracken:               Automatisiert:
   - Viewed Product              - Warenkorbabbrecher
   - Added to Cart               - Willkommensserien
   - Started Checkout            - Post-Purchase
   - Placed Order                - Win-Back
   - Fulfilled Order             - Browse Abandonment

1. Klaviyo API Setup

API Key erstellen

  1. In Klaviyo: Account → Settings → API Keys
  2. “Create Private API Key” klicken
  3. Berechtigungen setzen:
    • Profiles: Full Access
    • Events: Full Access
    • Catalogs: Full Access
    • Lists: Full Access

Den Private Key sicher speichern – er wird nur einmal angezeigt.

2. Drupal Custom Module erstellen

Modulstruktur

modules/custom/klaviyo_integration/
├── klaviyo_integration.info.yml
├── klaviyo_integration.module
├── klaviyo_integration.services.yml
├── config/
│   └── install/
│       └── klaviyo_integration.settings.yml
└── src/
    ├── Form/
    │   └── KlaviyoSettingsForm.php
    └── Service/
        └── KlaviyoClient.php

klaviyo_integration.info.yml

name: Klaviyo Integration
type: module
description: 'Klaviyo API Integration für E-Commerce Tracking'
core_version_requirement: ^10 || ^11
package: Custom

dependencies:
  - drupal:commerce_order
  - drupal:commerce_cart

API Client Service

<?php
// src/Service/KlaviyoClient.php

namespace Drupal\klaviyo_integration\Service;

use Drupal\Core\Config\ConfigFactoryInterface;
use GuzzleHttp\ClientInterface;
use Psr\Log\LoggerInterface;

class KlaviyoClient {

  protected string $apiKey;
  protected string $baseUrl = 'https://a.klaviyo.com/api';
  protected ClientInterface $httpClient;
  protected LoggerInterface $logger;

  public function __construct(
    ConfigFactoryInterface $configFactory,
    ClientInterface $httpClient,
    LoggerInterface $logger
  ) {
    $config = $configFactory->get('klaviyo_integration.settings');
    $this->apiKey = $config->get('api_key') ?? '';
    $this->httpClient = $httpClient;
    $this->logger = $logger;
  }

  /**
   * Profil erstellen oder aktualisieren.
   */
  public function upsertProfile(array $profileData): ?array {
    $payload = [
      'data' => [
        'type' => 'profile',
        'attributes' => $profileData,
      ],
    ];

    return $this->request('POST', '/profile-import', $payload);
  }

  /**
   * Event tracken (z.B. Viewed Product, Added to Cart).
   */
  public function trackEvent(string $eventName, array $properties, string $email): ?array {
    $payload = [
      'data' => [
        'type' => 'event',
        'attributes' => [
          'metric' => [
            'data' => [
              'type' => 'metric',
              'attributes' => [
                'name' => $eventName,
              ],
            ],
          ],
          'profile' => [
            'data' => [
              'type' => 'profile',
              'attributes' => [
                'email' => $email,
              ],
            ],
          ],
          'properties' => $properties,
          'time' => date('c'),
        ],
      ],
    ];

    return $this->request('POST', '/events', $payload);
  }

  /**
   * Produkt zum Katalog hinzufügen.
   */
  public function syncProduct(array $productData): ?array {
    $catalogId = 'DRUPAL_COMMERCE'; // Ihr Katalog-ID

    $payload = [
      'data' => [
        'type' => 'catalog-item',
        'attributes' => [
          'external_id' => $productData['sku'],
          'catalog_type' => '$default',
          'title' => $productData['title'],
          'description' => $productData['description'] ?? '',
          'url' => $productData['url'],
          'image_full_url' => $productData['image_url'] ?? '',
          'price' => $productData['price'],
        ],
      ],
    ];

    return $this->request('POST', '/catalog-items', $payload);
  }

  /**
   * HTTP Request an Klaviyo API.
   */
  protected function request(string $method, string $endpoint, array $payload = []): ?array {
    try {
      $response = $this->httpClient->request($method, $this->baseUrl . $endpoint, [
        'headers' => [
          'Authorization' => 'Klaviyo-API-Key ' . $this->apiKey,
          'Accept' => 'application/json',
          'Content-Type' => 'application/json',
          'revision' => '2024-10-15', // API Revision
        ],
        'json' => $payload,
      ]);

      return json_decode($response->getBody()->getContents(), TRUE);
    }
    catch (\Exception $e) {
      $this->logger->error('Klaviyo API error: @message', [
        '@message' => $e->getMessage(),
      ]);
      return NULL;
    }
  }

}

Services registrieren

# klaviyo_integration.services.yml
services:
  klaviyo_integration.client:
    class: Drupal\klaviyo_integration\Service\KlaviyoClient
    arguments:
      - '@config.factory'
      - '@http_client'
      - '@logger.channel.klaviyo_integration'

  logger.channel.klaviyo_integration:
    parent: logger.channel_base
    arguments: ['klaviyo_integration']

3. E-Commerce Events tracken

Event Subscriber für Commerce Events

<?php
// src/EventSubscriber/CommerceEventSubscriber.php

namespace Drupal\klaviyo_integration\EventSubscriber;

use Drupal\commerce_cart\Event\CartEntityAddEvent;
use Drupal\commerce_order\Event\OrderEvent;
use Drupal\klaviyo_integration\Service\KlaviyoClient;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class CommerceEventSubscriber implements EventSubscriberInterface {

  protected KlaviyoClient $klaviyoClient;

  public function __construct(KlaviyoClient $klaviyoClient) {
    $this->klaviyoClient = $klaviyoClient;
  }

  public static function getSubscribedEvents(): array {
    return [
      'commerce_cart.entity.add' => 'onAddToCart',
      'commerce_order.place.post_transition' => 'onOrderPlaced',
    ];
  }

  /**
   * "Added to Cart" Event tracken.
   */
  public function onAddToCart(CartEntityAddEvent $event): void {
    $cart = $event->getCart();
    $orderItem = $event->getOrderItem();
    $purchasedEntity = $orderItem->getPurchasedEntity();

    $email = $cart->getEmail();
    if (empty($email)) {
      return;
    }

    $properties = [
      'ProductName' => $purchasedEntity->getTitle(),
      'ProductID' => $purchasedEntity->getSku(),
      'Quantity' => (int) $orderItem->getQuantity(),
      'Price' => (float) $orderItem->getUnitPrice()->getNumber(),
      'Currency' => $orderItem->getUnitPrice()->getCurrencyCode(),
      'ProductURL' => $purchasedEntity->toUrl('canonical', ['absolute' => TRUE])->toString(),
      'CartTotal' => (float) $cart->getTotalPrice()->getNumber(),
    ];

    $this->klaviyoClient->trackEvent('Added to Cart', $properties, $email);
  }

  /**
   * "Placed Order" Event tracken.
   */
  public function onOrderPlaced(OrderEvent $event): void {
    $order = $event->getOrder();
    $email = $order->getEmail();

    if (empty($email)) {
      return;
    }

    // Bestellte Produkte sammeln
    $items = [];
    foreach ($order->getItems() as $item) {
      $purchasedEntity = $item->getPurchasedEntity();
      $items[] = [
        'ProductID' => $purchasedEntity->getSku(),
        'ProductName' => $purchasedEntity->getTitle(),
        'Quantity' => (int) $item->getQuantity(),
        'Price' => (float) $item->getUnitPrice()->getNumber(),
      ];
    }

    $properties = [
      'OrderID' => $order->getOrderNumber(),
      'OrderTotal' => (float) $order->getTotalPrice()->getNumber(),
      'Currency' => $order->getTotalPrice()->getCurrencyCode(),
      'Items' => $items,
      'ItemCount' => count($items),
      'BillingAddress' => $this->formatAddress($order->getBillingProfile()),
    ];

    $this->klaviyoClient->trackEvent('Placed Order', $properties, $email);

    // Profil mit Kundendaten aktualisieren
    $this->klaviyoClient->upsertProfile([
      'email' => $email,
      'first_name' => $order->getBillingProfile()?->get('address')[0]?->given_name ?? '',
      'last_name' => $order->getBillingProfile()?->get('address')[0]?->family_name ?? '',
      'properties' => [
        'last_order_date' => date('c'),
        'total_orders' => 1, // Sollte inkrementiert werden
      ],
    ]);
  }

  protected function formatAddress($profile): array {
    if (!$profile) {
      return [];
    }

    $address = $profile->get('address')[0] ?? NULL;
    if (!$address) {
      return [];
    }

    return [
      'city' => $address->locality ?? '',
      'country' => $address->country_code ?? '',
      'zip' => $address->postal_code ?? '',
    ];
  }

}

4. JavaScript Tracking für Browse-Verhalten

Viewed Product tracken

<?php
// In einem Preprocess Hook oder Controller

/**
 * Implements hook_preprocess_commerce_product().
 */
function klaviyo_integration_preprocess_commerce_product(&$variables) {
  $product = $variables['product_entity'];
  $variation = $product->getDefaultVariation();

  $variables['#attached']['drupalSettings']['klaviyo'] = [
    'productId' => $variation->getSku(),
    'productName' => $product->getTitle(),
    'price' => (float) $variation->getPrice()->getNumber(),
    'currency' => $variation->getPrice()->getCurrencyCode(),
    'url' => $product->toUrl('canonical', ['absolute' => TRUE])->toString(),
    'imageUrl' => '', // Bild-URL hier einfügen
  ];

  $variables['#attached']['library'][] = 'klaviyo_integration/tracking';
}

JavaScript Library

// js/klaviyo-tracking.js

(function (Drupal, drupalSettings) {
  'use strict';

  Drupal.behaviors.klaviyoTracking = {
    attach: function (context, settings) {
      if (!settings.klaviyo || !window._learnq) {
        return;
      }

      const product = settings.klaviyo;

      // Viewed Product Event
      window._learnq.push(['track', 'Viewed Product', {
        ProductID: product.productId,
        ProductName: product.productName,
        Price: product.price,
        Currency: product.currency,
        ProductURL: product.url,
        ImageURL: product.imageUrl
      }]);
    }
  };

})(Drupal, drupalSettings);

Klaviyo Snippet einbinden

/**
 * Implements hook_page_attachments().
 */
function klaviyo_integration_page_attachments(array &$attachments) {
  $config = \Drupal::config('klaviyo_integration.settings');
  $publicKey = $config->get('public_key');

  if ($publicKey) {
    $attachments['#attached']['html_head'][] = [
      [
        '#type' => 'html_tag',
        '#tag' => 'script',
        '#attributes' => [
          'async' => TRUE,
          'src' => 'https://static.klaviyo.com/onsite/js/klaviyo.js?company_id=' . $publicKey,
        ],
      ],
      'klaviyo_script',
    ];
  }
}

5. Produktkatalog synchronisieren

Für personalisierte Produktempfehlungen braucht Klaviyo Ihren Produktkatalog:

<?php
// Drush Command oder Cron Job

/**
 * Alle Produkte zu Klaviyo synchronisieren.
 */
public function syncAllProducts(): void {
  $productStorage = \Drupal::entityTypeManager()->getStorage('commerce_product');
  $products = $productStorage->loadMultiple();

  foreach ($products as $product) {
    $variation = $product->getDefaultVariation();
    if (!$variation) {
      continue;
    }

    $this->klaviyoClient->syncProduct([
      'sku' => $variation->getSku(),
      'title' => $product->getTitle(),
      'description' => strip_tags($product->get('body')->value ?? ''),
      'url' => $product->toUrl('canonical', ['absolute' => TRUE])->toString(),
      'price' => (float) $variation->getPrice()->getNumber(),
      'image_url' => $this->getProductImageUrl($product),
    ]);
  }
}

6. Flows in Klaviyo einrichten

Nach der Integration können Sie in Klaviyo automatisierte Flows erstellen:

Warenkorbabbrecher Flow

Trigger: "Added to Cart" Event
Warten: 1 Stunde
Bedingung: Hat NICHT "Placed Order" in den letzten 1 Stunde

→ E-Mail 1: "Sie haben etwas vergessen!"
   Warten: 24 Stunden
   Bedingung: Hat immer noch nicht bestellt

→ E-Mail 2: "Noch da? 10% Rabatt für Sie"
   Warten: 48 Stunden

→ E-Mail 3: "Letzte Chance!"

Post-Purchase Flow

Trigger: "Placed Order" Event

→ E-Mail 1 (sofort): Bestellbestätigung
   Warten: 3 Tage

→ E-Mail 2: Versandstatus + Tracking
   Warten: 7 Tage nach Lieferung

→ E-Mail 3: Bewertung anfragen
   Warten: 30 Tage

→ E-Mail 4: Cross-Selling Empfehlungen

Fazit

Die Klaviyo-Integration mit Drupal 11 erfordert etwas Custom Development, aber das Ergebnis ist ein mächtiges E-Commerce Marketing-System:

  • Echtzeit-Tracking aller Kundeninteraktionen
  • Automatisierte Flows für jeden Touchpoint
  • Personalisierung basierend auf Kaufverhalten
  • Messbare ROI durch Conversion-Tracking

Der initiale Aufwand lohnt sich – Klaviyos Warenkorbabbrecher-Flows allein bringen typischerweise 5-15% zusätzlichen Umsatz.

Brauchen Sie Unterstützung bei der Klaviyo-Integration? Kontaktieren Sie mich für ein unverbindliches Gespräch.

Häufig gestellte Fragen (FAQ)

Ist Klaviyo teuer?
Klaviyo bietet ein kostenloses Tier für bis zu 250 Kontakte und 500 E-Mails pro Monat. Danach zahlen Sie basierend auf Kontaktanzahl. Im E-Commerce rechnet sich Klaviyo schnell durch höhere Conversion Rates bei Warenkorbabbrechern.
Gibt es ein fertiges Drupal-Modul für Klaviyo?
Es gibt kein offizielles Klaviyo-Modul für Drupal. Die Integration erfolgt über die Klaviyo API v3, die sehr gut dokumentiert ist. In diesem Artikel zeige ich, wie Sie ein Custom Module dafür erstellen.
Kann Klaviyo auch ohne Drupal Commerce genutzt werden?
Ja, Klaviyo kann auch für Lead-Generierung, Newsletter und Content-Marketing genutzt werden. Die E-Commerce-Features sind aber Klaviyos größte Stärke.

Das könnte Sie auch interessieren

← Zurück zum Blog