Benachrichtigungen über entfernte Rabattcodes

Wann sollte dieser Workflow verwendet werden?

Verwenden Sie diesen Workflow, wenn ein Rabattcode entfernt wird und Sie sofort eine Übersicht darüber benötigen, welche Elemente davon betroffen waren. So können Sie den entfernten Code, die Anzahl seiner Einlösungen, die Kampagnendetails sowie eine Zusammenfassung erfassen, die Sie über Slack an die Finanz- oder Betriebsabteilung senden können.

Dies ist besonders nützlich bei durchgesickerten Influencer-Codes, unbeabsichtigter Veröffentlichung von Gutscheinen oder in allen Fällen, in denen Sie nach der Entfernung eines Codes einen dokumentierten Wirkungsbericht benötigen.

Was Sie entwickeln werden

  • Ein Slack-Workflow, der über einen Webhook die Variable text empfängt und denselben Text in einem Slack-Kanal veröffentlicht.
  • Ein Shopify Flow-Workflow, der durch die Entfernung eines Rabattcodes ausgelöst wurde.
  • Ein Datenschritt „Rabatt abrufen“, der den Rabatt anhand der ID aus dem Trigger ermittelt.
  • Ein „Code ausführen“-Schritt, der einen Entfernungsbericht formatiert.
  • Ein HTTP-Request-Schritt, der den Bericht an Slack sendet.

Bevor Sie beginnen

  • Stellen Sie sicher, dass die App installiert ist und im Admin-Bereich von Shopify unter „Apps“ > „Workflow Trigger Extensions“ angezeigt wird.
  • Stellen Sie sicher, dass Sie Zugriff auf Shopify Flow haben.
  • Erstellen Sie einen Slack-Kanal oder wählen Sie einen aus, in dem Benachrichtigungen angezeigt werden sollen.
  • Erstellen Sie einen Slack-Workflow, der mit einem Webhook startet und ein Feld text akzeptiert.

Video-Anleitung

Sie können sich auch das vollständige Video zur Umsetzung ansehen.

Erstellen Sie zunächst den Slack-Workflow

Erstellen Sie in Slack einen Workflow mit dem Auslöser „Beginnt mit einem Webhook“. Fügen Sie eine Textvariable namens text hinzu und fügen Sie anschließend den Schritt „Nachricht senden“ hinzu, der mithilfe derselben Variable text eine Nachricht an den von Ihnen ausgewählten Kanal sendet.

Slack-Workflow, der mit einem Webhook beginnt und die Textvariable an einen Slack-Kanal sendet
Slack-Workflow, der mit einem Webhook beginnt und die Textvariable an einen Slack-Kanal sendet

Rabattzugang in Workflow Trigger Extensions gewähren

Bevor der Trigger in Shopify Flow angezeigt wird, müssen Sie den Zugriff auf Rabatte in der App aktivieren.

Gehen Sie im Shopify-Adminbereich zu „Apps“ > „Workflow Trigger Extensions“.

Suchen Sie im Dashboard nach der Einrichtungscheckliste.

Aktivieren Sie die Option „Rabattzugang gewähren“. Dadurch werden rabattbezogene Trigger aktiviert, wie z. B. „Rabatt angelegt“, „Rabatt aktualisiert“, „Rabatt gelöscht“, „Rabattcode hinzugefügt“, „Rabattcode entfernt“ und „Rabatt abgelaufen“.

Workflow Trigger Extensions Dashboard mit der Anzeige „Zugriff auf Rabatte gewähren“ aktiviert
Workflow Trigger Extensions Dashboard mit der Anzeige „Zugriff auf Grant-Rabatt aktiviert“

Erstellen Sie die Shopify Flow

Öffnen Sie Shopify Flow und erstellen Sie einen neuen Workflow.

Suchen Sie nach Auslösern im Zusammenhang mit Rabatten und wählen Sie „Rabattcode entfernt“ unter „Workflow Trigger Extensions“ aus.

Shopify Flow Trigger-Auswahlfeld mit Rabattcode „Entfernt“ ausgewählt
Shopify Flow Trigger-Auswahlfeld mit „Rabattcode entfernt“ ausgewählt

Fügen Sie die Aktion Shopify („Rabattdaten abrufen“) hinzu.

Wählen Sie unter „Abfrage zum Filtern der Daten auswählendie Option „Erweitert“ aus. Verwenden Sie in „Abfrage bearbeiten“ die Rabatt-ID aus dem Trigger und entfernen Sie das Präfix „Shopify GID“:

bash
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"
Rufen Sie Rabattdaten mithilfe einer erweiterten Abfrage unter Verwendung der bereinigten Rabatt-ID ab
Rufen Sie Rabattdaten mithilfe einer erweiterten Abfrage mit der bereinigten Rabatt-ID ab

Fügen Sie nach dem Schritt „Rabattdaten abrufen“ einen Schritt „Code ausführen“ ein.

Verwenden Sie die folgende GraphQL-Abfrage in den Eingabefeldern „Auswählen“ aus den vorherigen Schritten:

graphql
query {
  redeemCode {
    code
    usageCount
    bulkRemoval
  }
  getDiscountData {
    discount {
      ... on DiscountCodeBasic {
        title
        startsAt
        endsAt
        asyncUsageCount
        codes {
          code
          asyncUsageCount
        }
        customerGets {
          value {
            ... on DiscountAmount {
              amount { amount currencyCode }
            }
            ... on DiscountPercentage {
              percentage
            }
          }
        }
      }
    }
  }
}

Definieren Sie die Ausgänge wie folgt:

typescript
"Output of discount code removal report"
type Output {
  "Discount code that was removed"
  code: String!
  "Discount campaign title"
  title: String!
  "Campaign start date"
  startsAt: String!
  "Campaign end date"
  endsAt: String!
  "Number of times it was redeemed"
  usageCount: Int!
  "Total discount impact as a formatted string"
  totalImpact: String!
  "When it was removed"
  removedAt: String!
  "Full summary for alerts"
  summary: String!
}

Fügen Sie diesen Code dann in das Feld „Code eingeben“ ein:

javascript
export default function main(input) {
  // ── Trigger: removed code ──
  const removedCode  = input?.redeemCode?.code         ?? "Unknown";
  const usageCount   = input?.redeemCode?.usageCount   ?? null; // null = bulk removal or unknown
  const bulkRemoval  = input?.redeemCode?.bulkRemoval  ?? false;

  const currency = input?.shop?.currencyCode ?? "";
  const shopName = input?.shop?.name         ?? "(not bound)";
  const removedAt = new Date().toISOString();

  // ── Find matching discount ──
  const dataItems = input?.getDiscountData ?? [];
  let matchedDiscount = null;
  for (const item of dataItems) {
    const d = item?.discount?.DiscountCodeBasic ?? item?.discount;
    if (!d) continue;
    const codesArray = Array.isArray(d.codes)
      ? d.codes.map(c => (typeof c === "string" ? c : c?.code ?? ""))
      : [];
    if (codesArray.includes(removedCode)) {
      matchedDiscount = d;
      break;
    }
  }
  // fallback to first entry if removed code already gone from list
  if (!matchedDiscount && dataItems.length > 0) {
    matchedDiscount =
      dataItems[0]?.discount?.DiscountCodeBasic ?? dataItems[0]?.discount ?? null;
  }

  const title              = matchedDiscount?.title            ?? "Untitled";
  const startsAt           = matchedDiscount?.startsAt         ?? "N/A";
  const endsAt             = matchedDiscount?.endsAt           ?? "None (no expiry)";
  const totalUsageCount    = matchedDiscount?.asyncUsageCount  ?? null;

  // ── Discount value ──
  const valueWrapper = matchedDiscount?.customerGets?.value ?? {};
  const pctObj       = valueWrapper?.DiscountPercentage ?? null;
  const amtObj       = valueWrapper?.DiscountAmount ?? valueWrapper?.amount ?? null;

  let discountLabel = "Unknown";
  if (pctObj?.percentage != null) {
    discountLabel = `${(pctObj.percentage * 100).toFixed(0)}% off`;
  } else if (amtObj?.amount != null) {
    const cur = amtObj.currencyCode || currency;
    discountLabel = `${cur} ${parseFloat(amtObj.amount).toFixed(2)} off`;
  }

  // ── This code's impact ──
  let thisCodeImpact;
  if (bulkRemoval) {
    thisCodeImpact = `Not available - removed as part of bulk deletion`;
  } else if (usageCount === null) {
    thisCodeImpact = `Could not be determined`;
  } else if (pctObj?.percentage != null) {
    thisCodeImpact = `${discountLabel} × ${usageCount} use(s)`;
  } else if (amtObj?.amount != null) {
    const perUse = parseFloat(amtObj.amount);
    const cur    = amtObj.currencyCode || currency;
    thisCodeImpact = `${cur} ${(perUse * usageCount).toFixed(2)} (${cur} ${perUse.toFixed(2)} × ${usageCount} uses)`;
  } else {
    thisCodeImpact = "Unknown";
  }

  // ── Campaign-wide impact using asyncUsageCount on the discount ──
  let campaignImpact;
  if (totalUsageCount === null) {
    campaignImpact = "Not available";
  } else if (pctObj?.percentage != null) {
    campaignImpact = `${discountLabel} × ${totalUsageCount} total use(s) across all codes`;
  } else if (amtObj?.amount != null) {
    const perUse = parseFloat(amtObj.amount);
    const cur    = amtObj.currencyCode || currency;
    campaignImpact = `${cur} ${(perUse * totalUsageCount).toFixed(2)} total (${cur} ${perUse.toFixed(2)} × ${totalUsageCount} uses)`;
  } else {
    campaignImpact = "Unknown";
  }

  // ── Per-code breakdown ──
  const codesNodes = Array.isArray(matchedDiscount?.codes)
    ? matchedDiscount.codes
    : [];

  const allCodes = codesNodes
    .map(c => (typeof c === "string" ? c : c?.code ?? ""))
    .filter(Boolean);

  const codesRoster = codesNodes.length
    ? codesNodes.map(c => {
        const code = typeof c === "string" ? c : c?.code ?? "";
        const used = c?.asyncUsageCount ?? "?";
        const flag = code === removedCode ? " ← REMOVED" : "";
        return `${code} (used ${used}×)${flag}`;
      }).join("\n                  ")
    : "N/A";

  const usageCountLabel = bulkRemoval
    ? "N/A (bulk removal)"
    : usageCount !== null
      ? String(usageCount)
      : "Unknown";

  const summary =
    `DISCOUNT CODE REMOVED\n` +
    `─────────────────────\n` +
    `Store:            ${shopName}\n` +
    `Code removed:     ${removedCode}\n` +
    `Bulk removal:     ${bulkRemoval ? "Yes" : "No"}\n` +
    `Campaign:         ${title}\n` +
    `Discount:         ${discountLabel}\n` +
    `Valid from:       ${startsAt}\n` +
    `Valid until:      ${endsAt}\n` +
    `\n` +
    `THIS CODE\n` +
    `  Times used:     ${usageCountLabel}\n` +
    `  Impact:         ${thisCodeImpact}\n` +
    `\n` +
    `CAMPAIGN TOTAL\n` +
    `  Total uses:     ${totalUsageCount ?? "Unknown"}\n` +
    `  Total impact:   ${campaignImpact}\n` +
    `\n` +
    `CODE BREAKDOWN\n` +
    `  ${codesRoster}\n` +
    `\n` +
    `Removed at:       ${removedAt}`;

const safeSummary = summary
  .replace(/\\/g, "\\\\")   // escape backslashes first
  .replace(/"/g, '\\"')     // escape quotes
  .replace(/\n/g, "\\n")    // escape newlines
  .replace(/\r/g, "\\r");

  return {
    code:           removedCode,
    bulkRemoval,
    title,
    discountLabel,
    startsAt,
    endsAt,
    usageCount:     usageCount ?? 0,
    usageCountLabel,
    thisCodeImpact,
    totalUsageCount: totalUsageCount ?? 0,
    campaignImpact,
    allCodes,
    codesRoster,
    removedAt,
    summary: safeSummary,
  };
}
Führen Sie den Code-Schritt in Shopify Flow mit der Abfrage, den Ausgaben und dem JavaScript-Berichtsgenerator aus.
Führen Sie den Code-Schritt in Shopify Flow mit der Abfrage, den Ausgaben und dem JavaScript-Berichtsgenerator aus.

Fügen Sie die Aktion „HTTP-Anfrage“ hinzu. Geben Sie im Feld „Variablen“ oder „Body-Überschreibung (JSON)“ die vom Code-Schritt „Ausführen“ zurückgegebene Zusammenfassung ein:

css
{
  "body": {
    "text": "{{runCode.summary}}"
  }
}

Dadurch wird die generierte Zusammenfassung mithilfe der Variablen text an den Slack-Webhook-Workflow gesendet.

HTTP-Anfrage-Aktion in Shopify Flow, bei der die „runCode“-Zusammenfassung im Textfeld gesendet wird
HTTP-Request-Aktion in Shopify Flow, bei der die „runCode“-Zusammenfassung in das Textfeld gesendet wird

Speichern Sie den Workflow, nachdem alle Schritte eingerichtet sind.

Lösche zur Überprüfung einen Rabattcode aus deiner Rabattkampagne und vergewissere dich, dass der Slack-Kanal die Zusammenfassungsnachricht erhält.

Zusammenfassung der Slack-Nachricht, in der der Rabattcode entfernt wurde
Zusammenfassung der Slack-Nachricht, in der der Rabattcode entfernt wurde

Was die Slack-Benachrichtigung enthält

  • Der entfernte Rabattcode
  • Ob es sich nun um eine Großabfuhr handelte
  • Der Titel der Kampagne
  • Der Rabattbetrag
  • Start- und Enddatum
  • Zeiten, die für den entfernten Code verwendet wurden
  • Geschätzte Auswirkungen des entfernten Codes
  • Nutzung und Wirkung der gesamten Kampagne
  • Eine Aufschlüsselung nach Codes, sofern verfügbar
  • Der Zeitstempel, zu dem die Löschung durchgeführt wurde

Fehlerbehebung

Gehen Sie zurück zu „Apps“ > Workflow Trigger Extensions und vergewissern Sie sich, dass die Option „Zugriff auf Rabatte gewähren“ aktiviert ist. Die Rabatt-Trigger werden erst angezeigt, nachdem der Zugriff gewährt wurde.

Überprüfen Sie die erweiterte Abfrage unter „Abfrage bearbeiten“ noch einmal. Die ID sollte genau wie hier gezeigt bereinigt sein:

bash
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"

Stellen Sie sicher, dass der Slack-Workflow mit einem Webhook beginnt, dass die Webhook-URL mit der in Ihrer HTTP-Anfrage verwendeten übereinstimmt und dass der Anfragetext das Feld text enthält.

Manche Löschvorgänge können Teil einer Massenaktion sein oder möglicherweise keine vollständigen Nutzungsdaten zurückgeben. In diesen Fällen werden in der Zusammenfassung dennoch die bestmöglichen Informationen bereitgestellt und fehlende Werte deutlich gekennzeichnet.

Bewährte Verfahren

  • Leiten Sie diese Benachrichtigungen an einen speziellen Kanal für den Finanz- oder Betriebsbereich weiter, anstatt an einen allgemeinen Support-Kanal.
  • Wählen Sie einen aussagekräftigen Namen für den Workflow, z. B. „Rabattcode entfernt“, damit er in Shopify Flow und Slack leicht zu erkennen ist.
  • Testen Sie zunächst mit einem Rabattcode aus der Nicht-Produktionsumgebung, um das Format der Meldung und das Verhalten des Webhooks zu überprüfen.