Powiadomienia o usunięciu kodu rabatowego

Powiadomienia o usunięciu kodu rabatowego

Skopiuj stronę

Skonfiguruj Shopify Flow, by po usunięciu kodu rabatowego wysyłać do Slacka raport z kodem, liczbą użyć i szczegółami kampanii przez webhook.

Kiedy należy stosować ten schemat postępowania

Proszę skorzystać z tego schematu działania, gdy kod rabatowy zostanie usunięty i konieczne jest natychmiastowe zarejestrowanie informacji o tym, na co miało to wpływ. Umożliwia on zarejestrowanie usuniętego kodu, liczby jego wykorzystań, szczegółów kampanii oraz podsumowania, które można przesłać do działu finansowego lub operacyjnego za pośrednictwem Slacka.

Jest to szczególnie przydatne w przypadku wycieku kodów influencerów, nieplanowanego ujawnienia kuponów lub w każdej sytuacji, w której po usunięciu kodu konieczne jest sporządzenie udokumentowanego raportu dotyczącego skutków.

Co Państwo stworzą

  • Proces w serwisie Slack, który odbiera zmienną text z webhooka i publikuje ten sam tekst w kanale Slacka.
  • Proces w Shopify Flow uruchomiony przez usunięcie kodu rabatowego.
  • Etap przetwarzania danych Pobierz rabat, który wyszukuje rabat na podstawie identyfikatora z wyzwalacza.
  • Krok Uruchom kod, który formatuje raport dotyczący usunięcia.
  • Krok HTTP Request, który wysyła raport do serwisu Slack.

Zanim zaczną Państwo

  • Proszę upewnić się, że aplikacja jest zainstalowana i widoczna w panelu administracyjnym Shopify w sekcji Aplikacje > Workflow Trigger Extensions.
  • Proszę upewnić się, że mają Państwo dostęp do serwisu Shopify Flow.
  • Proszę utworzyć lub wybrać kanał w serwisie Slack, na którym mają pojawiać się powiadomienia.
  • Proszę utworzyć przepływ pracy w serwisie Slack, który uruchamia się na podstawie webhooka i obsługuje pole typu text.

Przewodnik wideo

Można również obejrzeć film przedstawiający pełną realizację.

Najpierw proszę utworzyć przepływ pracy w Slacku

W serwisie Slack proszę utworzyć przepływ pracy z wyzwalaczem Rozpoczyna się od webhooka. Proszę dodać zmienną tekstową o nazwie text, a następnie dodać krok „Wiadomość”, który wysyła wiadomość do wybranego kanału, wykorzystując tę samą zmienną text.

Proces w serwisie Slack, który uruchamia się na podstawie webhooka i wysyła zmienną tekstową do kanału w serwisie Slack
Proces w serwisie Slack, który uruchamia się na podstawie webhooka i wysyła zmienną tekstową do kanału w serwisie Slack

Przyznanie dostępu z rabatem w rozszerzeniach wyzwalaczy przepływu pracy (Workflow Trigger Extensions)

Zanim wyzwalacz pojawi się w Shopify Flow, należy włączyć dostęp do rabatów w aplikacji.

W panelu administracyjnym Shopify przejdź do sekcji Aplikacje > Workflow Trigger Extensions.

Na pulpicie nawigacyjnym proszę odszukać listę kontrolną dotyczącą konfiguracji.

Włącz opcję Przyznaj dostęp do rabatów. Spowoduje to aktywację zdarzeń związanych z rabatami, takich jak: Utworzono rabat, Zaktualizowano rabat, Usunięto rabat, Dodano kod rabatowy, Usunięto kod rabatowy oraz Rabat wygasł.

Pulpit nawigacyjny Workflow Trigger Extensions z włączoną opcją „Grant Discount Access”
Pulpit nawigacyjny Workflow Trigger Extensions z włączoną opcją „Grant Discount Access”

Skonfiguruj usługę Shopify Flow

Proszę otworzyć aplikację Shopify Flow i utworzyć nowy przepływ pracy.

Proszę wyszukać wyzwalacze związane z rabatami i wybrać opcję Usunięcie kodu rabatowego z sekcji Workflow Trigger Extensions.

Wybór wyzwalacza w Shopify Flow: opcja „Kod rabatowy usunięty” została zaznaczona
Wybór wyzwalacza w Shopify Flow: opcja „Kod rabatowy usunięty” została zaznaczona

Proszę dodać akcję Pobierz dane dotyczące zniżek w Shopify.

W sekcji „Wybierz zapytanie, aby filtrować dane” proszę wybrać opcję Zaawansowane. W sekcji Edytuj zapytanie proszę użyć identyfikatora rabatu z wyzwalacza i usunąć prefiks GID serwisu Shopify:

bash
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"
Proszę pobrać dane dotyczące akcji rabatowych, korzystając z zaawansowanego zapytania z wykorzystaniem oczyszczonego identyfikatora rabatu
Proszę pobrać dane dotyczące akcji rabatowych, korzystając z zaawansowanego zapytania z wykorzystaniem oczyszczonego identyfikatora rabatu

Proszę dodać krok Uruchom kod po kroku Pobierz dane dotyczące zniżek.

W polach wyboru z poprzednich kroków proszę użyć następującego zapytania GraphQL:

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
            }
          }
        }
      }
    }
  }
}

Proszę zdefiniować dane wyjściowe w następujący sposób:

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!
}

Następnie wklej ten kod w polu Wpisz kod:

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,
  };
}
Uruchomienie kroku kodu w Shopify Flow z wykorzystaniem zapytania, wyników oraz narzędzia do tworzenia raportów w języku JavaScript
Uruchomienie kroku kodu w Shopify Flow z wykorzystaniem zapytania, wyników oraz narzędzia do tworzenia raportów w języku JavaScript

Proszę dodać akcję HTTP Request. W polu Zmienne** lub Zastąpienie treści (JSON)** proszę przesłać podsumowanie zwrócone przez krok Uruchom kod:

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

Spowoduje to wysłanie wygenerowanego podsumowania do przepływu pracy webhooka serwisu Slack przy użyciu zmiennej text.

Akcja HTTP Request w Shopify Flow, wysyłająca podsumowanie runCode w polu tekstowym
Akcja HTTP Request w Shopify Flow, wysyłająca podsumowanie runCode w polu tekstowym

Po skonfigurowaniu wszystkich kroków proszę zapisać przepływ pracy.

Aby to sprawdzić, proszę usunąć kod rabatowy z kampanii rabatowej i upewnić się, że kanał Slack otrzymał komunikat podsumowujący.

Wiadomość w serwisie Slack zawierająca podsumowanie dotyczące usunięcia kodu rabatowego
Wiadomość w serwisie Slack zawierająca podsumowanie dotyczące usunięcia kodu rabatowego

Co zawiera powiadomienie w serwisie Slack

  • Usunięty kod rabatowy
  • Niezależnie od tego, czy chodziło o wywóz dużych ilości odpadów
  • Tytuł kampanii
  • Wartość zniżki
  • Daty rozpoczęcia i zakończenia
  • Czasy zastosowane w usuniętym kodzie
  • Szacowany wpływ usunięcia kodu
  • Wykorzystanie i wpływ w ramach całej kampanii
  • Podział według poszczególnych kodów, o ile są one dostępne
  • Sygnatura czasowa momentu przetworzenia operacji usunięcia

Rozwiązywanie problemów

Proszę powrócić do sekcji Aplikacje > Workflow Trigger Extensions i upewnić się, że opcja Grant Discount Access jest włączona. Wyzwalacze rabatów pojawiają się dopiero po przyznaniu dostępu.

Proszę dokładnie sprawdzić zapytanie zaawansowane w sekcji Edytuj zapytanie. Identyfikator powinien być oczyszczony dokładnie tak, jak pokazano poniżej:

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

Proszę upewnić się, że przepływ pracy w Slacku rozpoczyna się od webhooka, że adres URL webhooka jest zgodny z tym używanym w Państwa żądaniu HTTP oraz że treść żądania zawiera pole text.

Niektóre usunięcia mogą stanowić część operacji zbiorczej lub mogą nie zawierać pełnych danych dotyczących wykorzystania. W takich przypadkach w podsumowaniu nadal będą przekazywane najlepsze dostępne informacje, a brakujące wartości zostaną wyraźnie oznaczone.

Najlepsze praktyki

  • Prosimy o przesyłanie tych powiadomień na dedykowany kanał ds. finansów lub operacji zamiast na ogólny kanał pomocy technicznej.
  • Prosimy o nadanie przepływowi pracy konkretnej nazwy, np. Usunięto kod rabatowy, tak aby można go było łatwo zidentyfikować w aplikacjach Shopify Flow i Slack.
  • Prosimy najpierw przeprowadzić test przy użyciu kodu rabatowego spoza środowiska produkcyjnego, aby sprawdzić format komunikatu oraz działanie webhooka.