Shopify Automatisierung: 10 Prozesse, die kein Plugin automatisieren kann
25 min Lesezeit

Shopify Automatisierung: 10 Prozesse, die kein Plugin automatisieren kann

#Shopify#Automatisierung#API#Custom Apps#E-Commerce

Sie haben Shopify Flow eingerichtet, drei Automatisierungs-Apps installiert und Zapier mit zehn Zaps verbunden. Trotzdem sitzen Ihre Mitarbeiter jeden Morgen zwei Stunden an manuellen Aufgaben, die "eigentlich automatisch laufen sollten". Kommt Ihnen das bekannt vor?

Das Problem ist nicht, dass Sie die falschen Plugins gewählt haben. Das Problem ist, dass Standard-Plugins für Standard-Probleme gebaut werden — und Ihr Business längst über den Standard hinausgewachsen ist.

In diesem Artikel zeige ich Ihnen 10 konkrete Prozesse, bei denen Plugins an ihre Grenzen stoßen, und wie Custom-Automatisierung diese Lücken schließt. Mit echten Code-Beispielen, realistischen Kostenvergleichen und klaren Entscheidungshilfen.

Warum Standard-Plugins nicht ausreichen

Bevor wir in die 10 Prozesse einsteigen: Verstehen Sie, warum Plugins strukturell limitiert sind.

Die drei fundamentalen Plugin-Limitierungen

LimitierungErklärungKonsequenz
One-size-fits-allPlugins müssen für tausende Shops funktionierenIhre spezifische Logik passt nicht rein
API-Rate-LimitsJede App teilt sich ein KontingentBei vielen Apps: Konflikte und Fehler
Keine System-übergreifende LogikPlugins kennen nur ShopifyERP, PIM, WMS bleiben außen vor

Die versteckten Kosten von Plugin-Stacking

Ein typisches Szenario, das ich bei Shopify-Händlern sehe:

Bestandsmanagement-App:     $49/Monat
Pricing-App:                $79/Monat
Fulfillment-App:            $39/Monat
Returns-App:                $59/Monat
B2B-App:                    $99/Monat
Sync-App:                   $29/Monat
Automatisierungs-App:       $49/Monat
─────────────────────────────────────
Gesamt:                    $403/Monat = $4.836/Jahr

Das sind fast 5.000 $ pro Jahr — und trotzdem bleiben Lücken. Jede App hat ihr eigenes Dashboard, ihre eigene Logik, ihre eigenen Bugs. Und wenn zwei Apps denselben Webhook abfangen, beginnt das Chaos.

Die Faustregel: Wenn Sie mehr als 4 Apps für zusammenhängende Workflows einsetzen, ist eine Custom-Lösung oft günstiger und zuverlässiger.


Prozess 1: Multi-Warehouse Bestandsabgleich mit unterschiedlichen ERP-Systemen

Das Problem

Sie betreiben drei Lager: eines in Deutschland (angebunden an SAP), eines in Polen (mit eigenem WMS) und ein Dropshipping-Lager beim Lieferanten (CSV-Export per E-Mail). Jedes System spricht eine andere Sprache, hat andere Update-Zyklen und andere Datenformate.

Warum Plugins hier scheitern

Standard-Inventory-Apps wie Stocky oder Katana unterstützen eine Datenquelle. Multi-ERP-Setups mit verschiedenen Protokollen (REST, SOAP, SFTP, E-Mail-Anhänge) sind schlicht nicht vorgesehen. Außerdem kennen diese Apps keine Priorisierungslogik — also welches Lager bei einem Bestelleingang zuerst bedient werden soll.

Die Custom-Lösung

Ein zentraler Sync-Service, der als Middleware zwischen allen Systemen vermittelt:

// Zentraler Bestandsabgleich-Service
async function syncInventoryFromAllSources() {
  // 1. SAP-Bestand abrufen (REST API)
  const sapStock = await fetchSAPInventory({
    endpoint: process.env.SAP_API_URL,
    credentials: process.env.SAP_CREDENTIALS,
    warehouse: 'DE-MAIN'
  });

  // 2. WMS Polen (SOAP)
  const wmsStock = await fetchWMSInventory({
    wsdl: process.env.WMS_WSDL_URL,
    warehouse: 'PL-FULFILLMENT'
  });

  // 3. Dropshipper (CSV per SFTP)
  const dropshipStock = await fetchDropshipCSV({
    host: process.env.SFTP_HOST,
    path: '/exports/stock_daily.csv',
    mapping: { sku: 'Artikelnummer', qty: 'Verfuegbar' }
  });

  // 4. Bestände zusammenführen mit Priorisierung
  const mergedInventory = mergeInventory([
    { source: sapStock, priority: 1, locationId: 'gid://shopify/Location/1001' },
    { source: wmsStock, priority: 2, locationId: 'gid://shopify/Location/1002' },
    { source: dropshipStock, priority: 3, locationId: 'gid://shopify/Location/1003' }
  ]);

  // 5. Shopify Inventory Levels aktualisieren
  for (const item of mergedInventory) {
    await shopify.rest.InventoryLevel.set({
      session,
      inventory_item_id: item.inventoryItemId,
      location_id: item.locationId,
      available: item.available
    });
  }
}

Business Impact

  • Vorher: 3 Stunden tägliche manuelle Bestandspflege, regelmäßige Überverkäufe
  • Nachher: Echtzeit-Sync alle 15 Minuten, Überverkäufe um 94% reduziert
  • ROI: Amortisierung der Entwicklungskosten innerhalb von 8 Wochen

Prozess 2: Automatische Preisanpassung basierend auf Wettbewerber-Daten

Das Problem

Sie verkaufen 2.000 Produkte und möchten bei den Top-100-Artikeln immer 2–5% unter dem günstigsten Wettbewerber liegen — aber nur, wenn Ihre Marge dabei über 15% bleibt. Außerdem sollen bestimmte Marken von der Anpassung ausgenommen werden.

Warum Plugins hier scheitern

Repricing-Apps wie Prisync oder Competera arbeiten mit festen Regeln. Mehrstufige Bedingungslogik (Marge + Wettbewerberpreis + Markenausnahmen + Tageszeit + Lagerbestand) übersteigt deren Regelwerk. Und eine direkte Anbindung an Ihre spezifische Margen-Kalkulation aus dem ERP? Fehlanzeige.

Die Custom-Lösung

// Preisanpassungslogik mit mehrstufigen Bedingungen
async function adjustPrices() {
  const products = await getCompetitiveProducts(); // Top-100 Artikel
  const competitorPrices = await scrapeCompetitorPrices(); // Datenquelle
  const marginData = await getMarginDataFromERP(); // Einkaufspreise

  for (const product of products) {
    const competitor = competitorPrices[product.sku];
    const margin = marginData[product.sku];

    if (!competitor || !margin) continue;

    // Ausnahme: Premium-Marken nicht anpassen
    if (product.vendor === 'PremiumBrand') continue;

    // Zielpreis: 3% unter günstigstem Wettbewerber
    const targetPrice = competitor.lowestPrice * 0.97;

    // Marge prüfen: Mindestens 15%
    const effectiveMargin = (targetPrice - margin.purchasePrice) / targetPrice;

    if (effectiveMargin < 0.15) {
      // Mindestmarge einhalten → Preis auf Mindestmarge setzen
      const minPrice = margin.purchasePrice / 0.85;
      await updateShopifyPrice(product.id, roundToNine(minPrice));
    } else {
      await updateShopifyPrice(product.id, roundToNine(targetPrice));
    }
  }
}

// Shopify Admin API: Preis aktualisieren
async function updateShopifyPrice(productId, newPrice) {
  const mutation = `
    mutation productVariantUpdate($input: ProductVariantInput!) {
      productVariantUpdate(input: $input) {
        productVariant {
          id
          price
        }
        userErrors { field, message }
      }
    }
  `;

  await shopifyGraphQL(mutation, {
    input: { id: productId, price: String(newPrice) }
  });
}

function roundToNine(price) {
  return (Math.floor(price) + 0.99).toFixed(2);
}

Business Impact

  • Vorher: Wöchentliche manuelle Preisprüfung für 100 Artikel (4h/Woche)
  • Nachher: Tägliche automatische Anpassung, 12% höhere Conversion bei preissensiblen Artikeln
  • ROI: +3,2% Gesamtumsatz bei gleichbleibender Marge

Prozess 3: Custom Fulfillment-Routing nach Produkttyp, Standort und Gewicht

Das Problem

Ihre Bestellung enthält drei Zeilen: ein Sperrgut-Produkt (Speditionsversand), zwei Normalpakete (DHL) und ein digitales Produkt (sofortiger Download). Die Bestellung muss automatisch in drei Fulfillment-Aufträge aufgeteilt, an verschiedene Carrier übergeben und dem Kunden als eine einheitliche Sendungsverfolgung präsentiert werden.

Warum Plugins hier scheitern

Shipping-Apps wie ShipStation oder Sendcloud können Multi-Carrier-Versand. Aber die automatische Aufteilung einer Bestellung nach Produkteigenschaften (Typ, Gewicht, Gefahrgut-Status, Lager-Zuweisung) in verschiedene Fulfillments — inklusive Speditionslogik — liegt außerhalb ihres Funktionsumfangs.

Die Custom-Lösung

// Fulfillment-Routing bei neuer Bestellung
async function routeOrderFulfillment(order) {
  const lineItems = order.line_items;

  // Routing-Regeln definieren
  const routes = {
    SPERRGUT: { carrier: 'spedition_api', locationId: 'LOC_WAREHOUSE_DE' },
    STANDARD: { carrier: 'dhl_api', locationId: 'LOC_WAREHOUSE_DE' },
    DIGITAL:  { carrier: null, locationId: null }, // Sofort-Fulfillment
    DROPSHIP: { carrier: 'supplier_api', locationId: 'LOC_SUPPLIER' }
  };

  // Line-Items klassifizieren
  const grouped = {};
  for (const item of lineItems) {
    const category = classifyProduct(item);
    if (!grouped[category]) grouped[category] = [];
    grouped[category].push(item);
  }

  // Fulfillment-Orders erstellen
  for (const [category, items] of Object.entries(grouped)) {
    const route = routes[category];

    if (category === 'DIGITAL') {
      // Digitale Produkte: Sofort als erfüllt markieren + Download-Link senden
      await fulfillDigitalItems(order.id, items);
      continue;
    }

    // Fulfillment-Request an entsprechenden Carrier
    const fulfillment = await createFulfillmentOrder({
      orderId: order.id,
      lineItems: items.map(i => ({
        id: i.id,
        quantity: i.quantity
      })),
      locationId: route.locationId
    });

    // Carrier-spezifische Übergabe
    await route.carrier && submitToCarrier(route.carrier, fulfillment);
  }
}

function classifyProduct(lineItem) {
  if (lineItem.requires_shipping === false) return 'DIGITAL';
  if (lineItem.grams > 31500) return 'SPERRGUT'; // > 31,5 kg
  if (lineItem.vendor === 'DropshipSupplier') return 'DROPSHIP';
  return 'STANDARD';
}

Business Impact

  • Vorher: Manuelle Fulfillment-Aufteilung, durchschnittlich 8 Minuten pro Mixed-Bestellung
  • Nachher: Vollautomatische Aufteilung in < 2 Sekunden
  • ROI: Bei 50 Mixed-Bestellungen/Tag = 6,6 Stunden Zeitersparnis täglich

Prozess 4: Automatische Metafield-Generierung aus externen Datenquellen

Das Problem

Ihre Produktdaten leben in einem PIM-System (z.B. Akeneo, Pimcore). Dort sind detaillierte technische Spezifikationen hinterlegt: Materialzusammensetzungen, Pflegehinweise, Zertifizierungen, CE-Kennzeichnungen. Diese Daten müssen als Metafields in Shopify landen — strukturiert, validiert und in der richtigen Sprache.

Warum Plugins hier scheitern

PIM-Integrationen wie Akeneo Connector for Shopify synchronisieren Basisdaten. Aber komplexe Mapping-Logik (ein PIM-Feld → mehrere Shopify-Metafields mit Transformation), bedingte Generierung (Metafield X nur wenn Produkttyp = Y) und Validierung gegen externe Schemas gehen weit über Plugin-Fähigkeiten hinaus.

Die Custom-Lösung

// PIM-zu-Shopify Metafield-Sync mit Transformation
async function syncMetafieldsFromPIM() {
  const pimProducts = await fetchPIMProducts();

  for (const pimProduct of pimProducts) {
    const shopifyProductId = await findShopifyProduct(pimProduct.sku);
    if (!shopifyProductId) continue;

    // Metafield-Definitionen basierend auf Produkttyp
    const metafields = generateMetafields(pimProduct);

    // Batch-Update über GraphQL
    const mutation = `
      mutation metafieldsSet($metafields: [MetafieldsSetInput!]!) {
        metafieldsSet(metafields: $metafields) {
          metafields { key, namespace, value }
          userErrors { field, message }
        }
      }
    `;

    await shopifyGraphQL(mutation, { metafields });
  }
}

function generateMetafields(pimProduct) {
  const metafields = [];

  // Basis-Metafields für alle Produkte
  metafields.push({
    ownerId: pimProduct.shopifyGid,
    namespace: 'specs',
    key: 'material',
    type: 'single_line_text_field',
    value: pimProduct.attributes.material?.de || ''
  });

  // Bedingte Metafields: Nur für Textilien
  if (pimProduct.category === 'textiles') {
    metafields.push({
      ownerId: pimProduct.shopifyGid,
      namespace: 'specs',
      key: 'care_instructions',
      type: 'json',
      value: JSON.stringify(
        transformCareSymbols(pimProduct.attributes.care)
      )
    });

    metafields.push({
      ownerId: pimProduct.shopifyGid,
      namespace: 'compliance',
      key: 'textile_composition',
      type: 'single_line_text_field',
      value: formatTextileComposition(pimProduct.attributes.composition)
    });
  }

  // Zertifizierungen als Rich-Text
  if (pimProduct.certifications?.length > 0) {
    metafields.push({
      ownerId: pimProduct.shopifyGid,
      namespace: 'compliance',
      key: 'certifications',
      type: 'list.single_line_text_field',
      value: JSON.stringify(pimProduct.certifications.map(c => c.name))
    });
  }

  return metafields;
}

Tipp: Wenn Sie allgemein mehr über Custom Shopify Apps erfahren möchten — inklusive OAuth, Webhooks und Hosting — lesen Sie meinen ausführlichen Guide: Shopify App entwickeln: Eine ehrliche Anleitung.

Business Impact

  • Vorher: Manuelle Metafield-Pflege in Shopify, 2 Mitarbeiter × 3 Stunden/Woche
  • Nachher: Automatischer Sync alle 4 Stunden, Fehlerrate von 12% auf 0,3% gesenkt
  • ROI: 312 Arbeitsstunden/Jahr eingespart

Prozess 5: Cross-Store Synchronisation (Multi-Brand Betrieb)

Das Problem

Sie betreiben drei Shopify-Stores für verschiedene Marken. Einige Produkte existieren in allen Stores (mit unterschiedlichen Preisen und Beschreibungen), der Gesamtbestand wird aber zentral verwaltet. Bestellungen aus allen Stores sollen in einem einheitlichen Dashboard erscheinen und gemeinsam fulfilled werden.

Warum Plugins hier scheitern

Multi-Store-Plugins wie Syncio können Produkte zwischen Stores synchronisieren. Aber selektive Synchronisation (nur bestimmte Felder, unterschiedliche Preise pro Store), konsolidierte Bestandslogik (Store A hat Priorität über Store B bei knappem Bestand) und einheitliches Order-Management übersteigen den Plugin-Umfang.

Die Custom-Lösung

// Cross-Store Inventar-Sync mit Priorisierung
async function crossStoreInventorySync() {
  const stores = [
    { name: 'Brand A', client: shopifyClientA, priority: 1, reserveStock: 5 },
    { name: 'Brand B', client: shopifyClientB, priority: 2, reserveStock: 3 },
    { name: 'Brand C', client: shopifyClientC, priority: 3, reserveStock: 0 }
  ];

  // Zentralen Bestand aus ERP holen
  const centralStock = await getCentralInventory();

  for (const sku of Object.keys(centralStock)) {
    const totalAvailable = centralStock[sku].quantity;
    let remaining = totalAvailable;

    // Bestand nach Priorität verteilen
    for (const store of stores) {
      const productMapping = await getProductMapping(store.name, sku);
      if (!productMapping) continue;

      // Reservebestand für höher priorisierte Stores
      const allocated = Math.max(0, remaining - store.reserveStock);
      remaining -= allocated;

      await store.client.rest.InventoryLevel.set({
        session: store.session,
        inventory_item_id: productMapping.inventoryItemId,
        location_id: productMapping.locationId,
        available: allocated
      });
    }
  }
}

// Konsolidiertes Order-Dashboard
async function getConsolidatedOrders(dateFrom, dateTo) {
  const allOrders = [];

  for (const store of stores) {
    const orders = await store.client.rest.Order.all({
      session: store.session,
      created_at_min: dateFrom,
      created_at_max: dateTo,
      status: 'any'
    });

    allOrders.push(
      ...orders.data.map(order => ({
        ...order,
        storeName: store.name,
        storeId: store.id
      }))
    );
  }

  return allOrders.sort((a, b) =>
    new Date(b.created_at) - new Date(a.created_at)
  );
}

Business Impact

  • Vorher: 3 separate Dashboards, manuelle Bestandsverteilung, häufige Überverkäufe
  • Nachher: Ein zentrales System, automatische Allokation, Überverkäufe eliminiert
  • ROI: 40% weniger Operations-Aufwand, 15% weniger entgangener Umsatz durch Stockouts

Prozess 6: Automatisierte B2B-Preislogik mit kundenspezifischen Rabatten

Das Problem

Sie haben 200 B2B-Kunden mit individuellen Rabattstrukturen: Kunde A bekommt 25% auf Kategorie X, aber nur 10% auf Kategorie Y. Kunde B hat Festpreise für seine Top-50-Artikel. Kunde C bekommt gestaffelte Mengenrabatte ab 100 Stück. Und das Ganze muss mit Shopify B2B und den Katalog-Funktionen zusammenarbeiten.

Warum Plugins hier scheitern

Shopify B2B bietet seit 2024 integrierte Preis-Kataloge. Aber die Logik ist statisch: Sie legen Prozentsätze pro Katalog fest. Dynamische Staffelpreise, kombinierte Rabattregeln (Menge + Kundenstatus + Saison) und automatische Preisübertragung aus bestehenden ERP-Verträgen gehen darüber hinaus.

Die Custom-Lösung

// Dynamische B2B-Preisberechnung
async function calculateB2BPrice(companyId, variantId, quantity) {
  // Kundenvertrag laden
  const contract = await getB2BContract(companyId);
  const variant = await getVariant(variantId);
  const basePrice = parseFloat(variant.price);

  let finalPrice = basePrice;
  let appliedRules = [];

  // Regel 1: Kategoriebasierter Rabatt
  if (contract.categoryDiscounts?.[variant.product_type]) {
    const discount = contract.categoryDiscounts[variant.product_type];
    finalPrice *= (1 - discount / 100);
    appliedRules.push(`Kategorie-Rabatt: ${discount}%`);
  }

  // Regel 2: Festpreise (überschreibt Kategorierabatt)
  if (contract.fixedPrices?.[variant.sku]) {
    finalPrice = contract.fixedPrices[variant.sku];
    appliedRules = [`Festpreis: ${finalPrice}€`];
  }

  // Regel 3: Mengenstaffel (kumulativ)
  if (contract.volumeTiers) {
    const tier = contract.volumeTiers
      .sort((a, b) => b.minQty - a.minQty)
      .find(t => quantity >= t.minQty);

    if (tier) {
      finalPrice *= (1 - tier.discount / 100);
      appliedRules.push(`Mengenstaffel ab ${tier.minQty}: ${tier.discount}%`);
    }
  }

  // Regel 4: Saisonaler Bonus
  const now = new Date();
  const seasonalBonus = contract.seasonalBonuses?.find(
    b => now >= new Date(b.start) && now <= new Date(b.end)
  );
  if (seasonalBonus) {
    finalPrice *= (1 - seasonalBonus.extraDiscount / 100);
    appliedRules.push(`Saison-Bonus: ${seasonalBonus.extraDiscount}%`);
  }

  return {
    originalPrice: basePrice,
    finalPrice: Math.round(finalPrice * 100) / 100,
    appliedRules,
    savings: ((1 - finalPrice / basePrice) * 100).toFixed(1) + '%'
  };
}

Business Impact

  • Vorher: Excel-basierte Preispflege, fehleranfällig, 1 Tag/Woche Verwaltungsaufwand
  • Nachher: Echtzeit-Preisberechnung, Self-Service-Portal für B2B-Kunden
  • ROI: 60% weniger Pricing-Fehler, 3× schnellere Angebotserstellung

Prozess 7: Custom Webhook-Chains für komplexe Order-Workflows

Das Problem

Wenn eine Bestellung eingeht, soll eine Kette von Aktionen ausgelöst werden — aber nicht linear, sondern bedingt und parallel:

  1. Bestellung geht ein → Betrugsprüfung
  2. Betrugsprüfung bestanden? → Parallel: Rechnung erzeugen + Fulfillment starten + CRM aktualisieren
  3. Betrugsprüfung fehlgeschlagen? → Bestellung halten + Support benachrichtigen
  4. Fulfillment gestartet? → Wenn B2B-Kunde: Lieferschein mit Custom-Template erzeugen
  5. Bestellung versendet → Tracking-Seite mit Upsells personalisieren

Warum Plugins hier scheitern

Shopify Flow kann einfache Wenn-Dann-Ketten. Aber parallele Ausführung, Fehlerbehandlung mit Retry-Logik, bedingte Verzweigungen über externe Systeme hinweg und transaktionale Sicherheit (wenn Schritt 3 fehlschlägt, Schritt 2 rückgängig machen) sind in Flow nicht abbildbar.

Die Custom-Lösung

// Webhook-Chain-Orchestrator
class OrderWorkflowOrchestrator {
  async handleNewOrder(order) {
    const context = { order, steps: [], errors: [] };

    try {
      // Schritt 1: Betrugsprüfung
      const fraudCheck = await this.checkFraud(order);
      context.steps.push({ name: 'fraud_check', result: fraudCheck });

      if (fraudCheck.risk === 'high') {
        // Verdächtig: Bestellung halten
        await Promise.all([
          this.holdOrder(order.id),
          this.notifySupport(order, fraudCheck),
          this.logEvent('order_held', context)
        ]);
        return;
      }

      // Schritt 2: Parallele Verarbeitung (nicht blockierend)
      const [invoice, fulfillment, crmUpdate] = await Promise.allSettled([
        this.generateInvoice(order),
        this.initiateFulfillment(order),
        this.updateCRM(order)
      ]);

      // Fehler einzelner Schritte verarbeiten (nicht gesamte Chain abbrechen)
      for (const result of [
        { name: 'invoice', result: invoice },
        { name: 'fulfillment', result: fulfillment },
        { name: 'crm', result: crmUpdate }
      ]) {
        if (result.result.status === 'rejected') {
          context.errors.push(result.name);
          await this.scheduleRetry(result.name, order.id);
        }
      }

      // Schritt 3: B2B-spezifische Logik
      if (order.customer?.tags?.includes('b2b')) {
        await this.generateCustomDeliveryNote(order, {
          template: 'b2b-lieferschein',
          includePurchaseOrderNumber: true
        });
      }

      await this.logEvent('order_processed', context);
    } catch (error) {
      await this.handleCriticalError(error, context);
    }
  }

  async scheduleRetry(stepName, orderId, attempt = 1) {
    const delay = Math.min(1000 * Math.pow(2, attempt), 60000); // Exponential Backoff
    setTimeout(async () => {
      try {
        await this[`retry_${stepName}`](orderId);
      } catch (e) {
        if (attempt < 5) {
          await this.scheduleRetry(stepName, orderId, attempt + 1);
        } else {
          await this.escalateToSupport(stepName, orderId);
        }
      }
    }, delay);
  }
}

Business Impact

  • Vorher: 5 separate Automatisierungstools, fragile Ketten, keine Fehlerbehandlung
  • Nachher: Eine orchestrierte Pipeline mit Logging, Retry und Fallback
  • ROI: 99,7% Verarbeitungsquote (vorher 91%), 4 Apps eingespart

Prozess 8: Automatische Retouren-Verarbeitung mit Condition-Checking

Das Problem

Der Kunde meldet eine Retoure an. Je nach Produktzustand (ungeöffnet, geöffnet, beschädigt), Retourengrund (Umtausch, Rücksendung, Garantie), Produktkategorie (Hygieneartikel, Elektronik, Textil) und Kundenhistorie (wie viele Retouren in den letzten 12 Monaten?) sollen unterschiedliche Workflows ausgelöst werden.

Warum Plugins hier scheitern

Returns-Apps wie Loop oder ReturnGO bieten regelbasierte Retouren. Aber Condition-Logik über mehrere Dimensionen (Zustand × Grund × Kategorie × Kundenhistorie × Warenwert) mit unterschiedlichen Outcomes (Sofort-Erstattung vs. Inspektion vs. Ablehnung vs. Teilerstattung) und Anbindung an das eigene Retourenlager-System ist zu komplex für vorgefertigte Lösungen.

Die Custom-Lösung

// Retouren-Entscheidungsengine
async function processReturn(returnRequest) {
  const { orderId, lineItemId, reason, condition, customerId } = returnRequest;

  // Daten sammeln
  const [order, customer, product, returnHistory] = await Promise.all([
    getOrder(orderId),
    getCustomer(customerId),
    getProductFromLineItem(lineItemId),
    getReturnHistory(customerId, 12) // Letzte 12 Monate
  ]);

  const itemValue = parseFloat(order.line_items
    .find(li => li.id === lineItemId)?.price || '0');

  // Entscheidungsmatrix
  const decision = evaluateReturnPolicy({
    condition,          // 'sealed' | 'opened' | 'damaged' | 'defective'
    reason,             // 'exchange' | 'refund' | 'warranty' | 'wrong_item'
    productCategory: product.product_type,
    isHygiene: product.tags.includes('hygiene'),
    itemValue,
    returnRate: returnHistory.count / 12,  // Durchschnittliche Retouren/Monat
    customerLifetimeValue: parseFloat(customer.total_spent),
    daysSinceDelivery: getDaysSinceDelivery(order)
  });

  // Entscheidung ausführen
  switch (decision.action) {
    case 'INSTANT_REFUND':
      // Bei Stammkunden mit niedrigem Warenwert → Sofort-Erstattung
      await createRefund(orderId, lineItemId, decision.amount);
      await sendEmail(customer.email, 'return_instant_refund', decision);
      break;

    case 'INSPECT_THEN_REFUND':
      // Retourenlabel erstellen, Inspektion bei Wareneingang
      const label = await generateReturnLabel(order.shipping_address);
      await createReturnOrder(orderId, lineItemId, label.trackingNumber);
      await sendEmail(customer.email, 'return_label', { label, decision });
      break;

    case 'PARTIAL_REFUND':
      // Geöffnete Ware: Teilerstattung anbieten
      await createRefund(orderId, lineItemId, decision.amount);
      await sendEmail(customer.email, 'return_partial', decision);
      break;

    case 'DENY':
      // Hygieneartikel geöffnet oder zu hohe Retourenquote
      await sendEmail(customer.email, 'return_denied', {
        reason: decision.denyReason
      });
      await flagCustomer(customerId, 'high_return_rate');
      break;
  }

  // Buchhaltung aktualisieren
  await syncReturnToAccounting(decision);
  return decision;
}

Business Impact

  • Vorher: Jede Retoure manuell bewertet (Ø 12 Min.), inkonsistente Entscheidungen
  • Nachher: 85% aller Retouren vollautomatisch verarbeitet in < 5 Sekunden
  • ROI: 2 Vollzeit-Support-Mitarbeiter entlastet, Kundenzufriedenheit +18%

Prozess 9: Dynamische Collection-Sortierung basierend auf Echtzeit-KPIs

Das Problem

Ihre "Bestseller"-Collection soll nicht einfach nach Verkaufszahlen sortiert sein. Das Ranking soll auf einem zusammengesetzten Score basieren: 40% Umsatz der letzten 7 Tage, 30% Conversion-Rate, 20% Lagerumschlag und 10% Marge. Produkte mit weniger als 10 Stück Restbestand sollen herabgestuft werden, und saisonale Produkte sollen einen temporären Boost bekommen.

Warum Plugins hier scheitern

Collection-Sortierungs-Apps sortieren nach vorgegebenen Metriken (Verkaufszahl, Umsatz, Datum). Einen gewichteten Multi-Faktor-Score mit externen Datenquellen (GA4 Conversion-Rate, ERP-Marge) und dynamischen Boosts gibt es nicht als Plugin.

Die Custom-Lösung

// Dynamische Collection-Sortierung
async function sortCollectionByScore(collectionId) {
  const products = await getCollectionProducts(collectionId);

  // KPIs für jedes Produkt laden
  const scoredProducts = await Promise.all(
    products.map(async (product) => {
      const [sales, analytics, inventory, margin] = await Promise.all([
        getSalesData(product.id, 7),           // Letzte 7 Tage
        getGA4ConversionRate(product.handle),   // Google Analytics 4
        getInventoryLevel(product.id),          // Aktueller Bestand
        getMarginFromERP(product.sku)           // Marge aus ERP
      ]);

      // Gewichteter Score
      let score =
        sales.revenue * 0.4 +
        analytics.conversionRate * 100 * 0.3 +  // Normalisiert
        inventory.turnoverRate * 0.2 +
        margin.percentage * 0.1;

      // Penalty: Niedriger Bestand
      if (inventory.available < 10) {
        score *= 0.5; // 50% Abzug
      }

      // Boost: Saisonale Produkte
      if (isInSeason(product.tags)) {
        score *= 1.3; // 30% Boost
      }

      return { productId: product.id, score };
    })
  );

  // Nach Score sortieren und Position aktualisieren
  scoredProducts.sort((a, b) => b.score - a.score);

  // Shopify Collection-Sortierung setzen (manuell)
  const moves = scoredProducts.map((p, index) => ({
    id: p.productId,
    newPosition: (index + 1).toString()
  }));

  const mutation = `
    mutation collectionReorderProducts(
      $id: ID!, $moves: [MoveInput!]!
    ) {
      collectionReorderProducts(id: $id, moves: $moves) {
        job { id }
        userErrors { field, message }
      }
    }
  `;

  await shopifyGraphQL(mutation, { id: collectionId, moves });
}

Relevant: Die Performance Ihrer Collections beeinflusst direkt die Ladezeiten. Mehr dazu in meinem Deep-Dive: Shopify Performance Optimierung.

Business Impact

  • Vorher: Manuelle Collection-Pflege, veraltete Sortierungen, Bauchgefühl
  • Nachher: Tägliche automatische Sortierung basierend auf echten KPIs
  • ROI: +22% Revenue per Visit auf Top-Collections, 5 Stunden/Woche weniger Merchandising-Aufwand

Prozess 10: Automatisierte Compliance-Prüfung (WEEE, Verpackungsgesetz, etc.)

Das Problem

Als deutscher E-Commerce-Händler müssen Sie eine Vielzahl regulatorischer Anforderungen erfüllen: WEEE-Registrierung für Elektrogeräte, Verpackungsgesetz (VerpackG) Lizenzierung, Produktsicherheitsverordnung, CE-Kennzeichnung, GPSR (General Product Safety Regulation seit 2024), Textilkennzeichnung und mehr. Bei 500+ Produkten aus verschiedenen Quellen ist die manuelle Prüfung ein Vollzeitjob.

Warum Plugins hier scheitern

Es gibt schlicht keine Standard-Plugins für deutsche E-Commerce-Compliance. Die Anforderungen sind zu spezifisch, zu dynamisch (Gesetzesänderungen) und zu stark von Produktkategorie und Lieferkette abhängig. Internationale App-Entwickler bauen keine Apps für das deutsche Verpackungsgesetz.

Die Custom-Lösung

// Compliance-Prüfungs-Engine
async function runComplianceCheck() {
  const products = await getAllActiveProducts();
  const issues = [];

  for (const product of products) {
    const productIssues = [];

    // WEEE-Prüfung: Elektrogeräte brauchen WEEE-Registrierung
    if (isElectrical(product)) {
      const weeeReg = product.metafields?.compliance?.weee_number;
      if (!weeeReg) {
        productIssues.push({
          severity: 'critical',
          type: 'WEEE',
          message: 'WEEE-Registrierungsnummer fehlt',
          action: 'Produkt muss bei stiftung ear registriert werden'
        });
      }
    }

    // VerpackG: Alle physischen Produkte brauchen Lizenzierung
    if (product.requires_shipping) {
      const verpackLicense = product.metafields?.compliance?.verpackg_licensed;
      if (!verpackLicense) {
        productIssues.push({
          severity: 'warning',
          type: 'VerpackG',
          message: 'Verpackungslizenz nicht hinterlegt',
          action: 'Verpackungsmengen bei LUCID registrieren'
        });
      }
    }

    // GPSR: Seit 13.12.2024 Pflicht — Verantwortliche Person in der EU
    const gpsrContact = product.metafields?.compliance?.gpsr_responsible;
    if (!gpsrContact) {
      productIssues.push({
        severity: 'critical',
        type: 'GPSR',
        message: 'Verantwortliche Person nach GPSR fehlt',
        action: 'EU-Verantwortlichen auf Produkt und im Listing angeben'
      });
    }

    // Textilkennzeichnung
    if (isTextile(product)) {
      const composition = product.metafields?.specs?.textile_composition;
      if (!composition) {
        productIssues.push({
          severity: 'critical',
          type: 'TextilKennzG',
          message: 'Materialzusammensetzung fehlt',
          action: 'Fasergehalt in % angeben (z.B. "80% Baumwolle, 20% Polyester")'
        });
      }
    }

    // CE-Kennzeichnung
    if (requiresCE(product)) {
      const ceMarking = product.metafields?.compliance?.ce_declaration;
      if (!ceMarking) {
        productIssues.push({
          severity: 'critical',
          type: 'CE',
          message: 'CE-Konformitätserklärung fehlt',
          action: 'Konformitätserklärung vom Hersteller anfordern und hinterlegen'
        });
      }
    }

    if (productIssues.length > 0) {
      issues.push({ product: product.title, sku: product.sku, issues: productIssues });

      // Produkt taggen für einfaches Filtern im Admin
      await tagProduct(product.id, productIssues.map(i =>
        `compliance:${i.type.toLowerCase()}:missing`
      ));
    }
  }

  // Compliance-Report generieren
  const report = generateComplianceReport(issues);
  await sendReportEmail(report);
  await saveReportToDashboard(report);

  return report;
}

Beispiel-Report

Der automatisch generierte Report könnte so aussehen:

SchweregradTypBetroffene ProdukteHandlungsbedarf
🔴 KritischGPSR47 ProdukteEU-Verantwortlichen angeben
🔴 KritischWEEE12 ProdukteWEEE-Nummer nachtragen
🟡 WarnungVerpackG8 ProdukteLizenz prüfen
🟢 OKCE0 ProdukteAlle vollständig

Business Impact

  • Vorher: Keine systematische Prüfung, Compliance-Risiken unbekannt
  • Nachher: Wöchentlicher automatischer Audit, sofortige Benachrichtigung bei neuen Produkten
  • ROI: Vermeidung von Bußgeldern (WEEE: bis 100.000€, VerpackG: bis 200.000€)

Vergleichstabelle: Plugin vs. Custom Automatisierung

KriteriumStandard-PluginCustom-Automatisierung
EinrichtungszeitMinuten bis StundenTage bis Wochen
Monatliche Kosten$20–$200/AppHosting: $5–$50
Entwicklungskosten$0$2.000–$15.000+
FlexibilitätBegrenzt auf vorgesehene FunktionenUnbegrenzt
Multi-System-IntegrationKaum (meist nur Shopify)Voll (ERP, PIM, WMS, etc.)
SkalierungApp-abhängigSelbst kontrollierbar
FehlerbehandlungGenerischAuf Ihren Workflow zugeschnitten
API-Rate-LimitsGeteilt mit anderen AppsDediziert für Ihre Prozesse
Datenschutz/DSGVODaten bei DrittanbieterDaten in Ihrer Infrastruktur
SupportTicket-System des AnbietersDirekter Draht zum Entwickler
Langfristige Kosten (3 Jahre)$720–$7.200/App$180–$1.800 + einmalig Entwicklung

Wann lohnt sich Custom?

Die einfache Faustregel:

Wenn (monatliche Plugin-Kosten × 18 Monate) > Custom-Entwicklung
→ Custom lohnt sich fast immer

Beispiel: Sie zahlen $400/Monat für 5 Apps, die zusammen Ihren Order-Workflow abdecken. In 18 Monaten sind das $7.200. Eine Custom-Lösung für $6.000 amortisiert sich innerhalb von 15 Monaten — und danach sparen Sie dauerhaft.


Wann sollten Sie in Custom-Automatisierung investieren?

Nicht jeder Shop braucht Custom-Automatisierung. Hier ist eine ehrliche Einschätzung:

✅ Custom lohnt sich, wenn:

  • Sie mehr als $300/Monat für Automatisierungs-Apps ausgeben
  • Sie regelmäßig manuelle Workarounds für Plugin-Limitierungen nutzen
  • Ihre Processes mehrere Systeme (ERP, PIM, WMS, CRM) einbeziehen
  • Sie spezifische deutsche Compliance-Anforderungen haben
  • Sie Multi-Store oder Multi-Brand betreiben
  • Ihre B2B-Preislogik zu komplex für Standard-Kataloge ist
  • Sie mehr als 500 Bestellungen/Monat verarbeiten

❌ Standard-Plugins reichen, wenn:

  • Sie einen einzelnen Store mit Standardprozessen betreiben
  • Ihre Fulfillment-Logik ein Lager, ein Carrier umfasst
  • Sie unter 200 Bestellungen/Monat haben
  • Ihre Preis- und Rabattlogik einfach und einheitlich ist
  • Sie keine ERP-Integration benötigen

Der Hybrid-Ansatz

In der Praxis empfehle ich oft eine Mischstrategie:

  1. Standard-Plugins für isolierte, gut gelöste Probleme (z.B. E-Mail-Marketing mit Klaviyo)
  2. Shopify Flow für einfache interne Automatisierungen (Tags setzen, Benachrichtigungen)
  3. Custom-Automatisierung für system-übergreifende, geschäftskritische Workflows

Die technische Umsetzung: So gehe ich vor

Wenn Sie sich für Custom-Automatisierung entscheiden, ist ein strukturiertes Vorgehen entscheidend:

Phase 1: Prozess-Audit (1–2 Tage)

  • Alle bestehenden Automatisierungen dokumentieren
  • Manuelle Workarounds identifizieren
  • Betroffene Systeme und APIs erfassen
  • Prioritäten nach Business Impact definieren

Phase 2: Architektur-Design (2–3 Tage)

  • System-Landkarte erstellen
  • API-Kompatibilität prüfen
  • Fehlerszenarien durchspielen
  • Hosting und Infrastruktur planen

Phase 3: Entwicklung (1–4 Wochen)

  • Iterative Entwicklung mit wöchentlichen Demos
  • Umfangreiche Error-Handling und Logging
  • Monitoring und Alerting einbauen
  • Dokumentation parallel erstellen

Phase 4: Testing & Go-Live (3–5 Tage)

  • Shadow-Mode: Parallel zum bestehenden Prozess laufen lassen
  • Ergebnisse vergleichen und validieren
  • Schrittweise Umstellung
  • Monitoring der ersten Wochen

Fazit: Automatisierung ist kein Plugin-Problem

Die meisten Shopify-Händler starten richtig: Sie nutzen Apps für Standard-Aufgaben. Aber ab einem gewissen Wachstumspunkt werden die Lücken zwischen den Apps größer als die Funktionalität der Apps selbst.

Custom-Automatisierung ist kein Luxus — es ist die logische nächste Stufe für Shops, die skalieren wollen, ohne proportional mehr Personal für manuelle Prozesse einzustellen.

Die 10 Prozesse in diesem Artikel sind keine theoretischen Szenarien. Es sind genau die Herausforderungen, die mir Shopify-Händler täglich beschreiben. Und in jedem Fall war die Custom-Lösung am Ende günstiger, zuverlässiger und wartbarer als der Plugin-Stack, den sie ersetzte.


Ihr nächster Schritt

Sie erkennen Ihren Shop in einem oder mehreren dieser Szenarien wieder? In einem kostenlosen 15-Minuten-Audit analysiere ich Ihre aktuellen Automatisierungen und zeige Ihnen, wo Custom-Lösungen den größten Impact hätten.

Kein Verkaufsgespräch. Keine Verpflichtung. Nur eine ehrliche Einschätzung, ob Custom-Automatisierung für Ihren Shop sinnvoll ist.

→ Kostenloses 15-Min Audit vereinbaren

Justin Kreutzmann

Über den Autor

Justin Kreutzmann ist Experte für Shopify-Entwicklung und E-Commerce-Skalierung. Er hilft Marken dabei, technische Grenzen zu überwinden und performante Online-Shops zu bauen.

Zusammenarbeiten →