Advarsler om fjernede rabatkoder
Hvornår skal denne arbejdsgang anvendes?
Brug denne arbejdsgang, når en rabatkode fjernes, og du har brug for en øjeblikkelig oversigt over, hvad der er berørt. Den hjælper dig med at registrere den fjernede kode, antallet af anvendelser, kampagnedetaljerne samt en oversigt, som du kan sende til økonomi- eller driftsafdelingen via Slack.
Dette er især nyttigt i forbindelse med lækkede influencerkoder, utilsigtet offentliggørelse af kuponer eller i alle tilfælde, hvor du har brug for en dokumenteret effektanalyse, efter at en kode er blevet fjernet.
Hvad du skal bygge
- En Slack-workflow, der modtager variablen »
text« fra en webhook og sender den samme tekst til en Slack-kanal. - En »Shopify Flow«-arbejdsgang, der udløses af »Rabatkode fjernet«.
- Et datatrin af typen »Hent rabat«, der slår rabatten op ved hjælp af ID’et fra triggeren.
- Et »Kør kode«-trin, der formaterer en fjernelsesrapport.
- Et HTTP-anmodningstrin, der sender rapporten til Slack.
Før du går i gang
- Sørg for, at appen er installeret og kan ses i Shopify admin under Apps > Workflow Trigger Extensions.
- Sørg for, at du har adgang til Shopify Flow.
- Opret eller vælg en Slack-kanal, hvor meddelelserne skal vises.
- Opret en Slack-workflow, der starter med en webhook og accepterer et felt med navnet »
text«.
Videovejledning
Du kan også se den fulde video om implementeringen.
Opret først Slack-workflowet
I Slack skal du oprette en arbejdsgang med udløseren »Starter med en webhook«. Tilføj en tekstvariabel med navnet »text«, og tilføj derefter trinnet »besked«, der sender en besked til den valgte kanal ved hjælp af netop denne variabel »text«.

Giv adgang med rabat i »Workflow Trigger Extensions«
Inden triggeren vises i Shopify Flow, skal du aktivere adgang til rabatter i appen.
Gå til **Apps > Workflow Trigger Extensions **i Shopify-administrationspanelet.
På Dashboardet skal du kigge efter opsætningschecklisten.
Aktivér »Giv adgang til rabatter«. Dette aktiverer rabatrelaterede triggere såsom »Rabatt oprettet«, »Rabatt opdateret«, »Rabatt slettet«, »Rabatkode tilføjet«, »Rabatkode fjernet« og »Rabatt udløbet«.

Byg »Shopify Flow«
Åbn Shopify Flow og opret en ny arbejdsgang.
Søg efter udløsere relateret til rabatter, og vælg »Rabatkode fjernet« fra »Workflow Trigger Extensions«.

Tilføj handlingen »Shopify« - »Hent rabatoplysninger«.
I afsnittet »Vælg en forespørgsel for at filtrere data« skal du vælge »Avanceret«. I »Rediger forespørgsel« skal du bruge rabat-ID’et fra triggeren og fjerne GID-præfikset »Shopify«:
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"
Indsæt et »Kør kode«-trin efter »Hent rabatdata«.
Brug følgende GraphQL-forespørgsel i indtastningsfelterne »Vælg« fra de foregående trin:
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
}
}
}
}
}
}
}Definer udgangene på følgende måde:
"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!
}Indsæt derefter denne kode i feltet »Skriv kode«:
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,
};
}
Tilføj handlingen »HTTP-anmodning«. I feltet »Variabler« eller »Body Override (JSON)« skal du sende det resumé, der returneres af kodetrinnet »Kør«:
{
"body": {
"text": "{{runCode.summary}}"
}
}Dette sender det genererede resumé til Slack-webhook-workflowet ved hjælp af variablen text.

Gem arbejdsgangen, når alle trin er på plads.
For at teste det skal du slette en rabatkode fra din rabatkampagne og kontrollere, at Slack-kanalen modtager oversigtsbeskeden.

Hvad Slack-beskeden indeholder
- Den fjernede rabatkode
- Uanset om der var tale om en større udflytning
- Kampagnens titel
- Rabatbeløbet
- Start- og slutdatoer
- Tider anvendt til den fjernede kode
- Anslået indvirkning af den fjernede kode
- Anvendelse og virkning på tværs af kampagnen
- En opdeling kode for kode, når den foreligger
- Tidspunktet for, hvornår sletningen blev gennemført
Fejlfinding
Gå tilbage til Apps > Workflow Trigger Extensions, og kontroller, at »Grant Discount Access« er aktiveret. Rabattriggerne vises først, når adgangen er bevilget.
Kontroller den avancerede forespørgsel nøje under »Rediger forespørgsel«. ID’et skal renses nøjagtigt som vist:
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"Sørg for, at Slack-workflowet starter med en webhook, at webhook-URL’en er den samme som den, der bruges i din HTTP-anmodning, og at anmodningens indhold indeholder feltet text.
Nogle sletninger kan være en del af en samlet handling eller medfører, at der ikke returneres fuldstændige brugsoplysninger. I sådanne tilfælde vil oversigten stadig indeholde de bedst tilgængelige oplysninger og tydeligt markere manglende værdier.
Gode eksempler
- Send disse advarsler til en dedikeret kanal for økonomi eller drift i stedet for en generel supportkanal.
- Giv arbejdsgangen et konkret navn, f.eks. »Rabatkode fjernet«, så den er nem at genkende i Shopify Flow og Slack.
- Prøv først med en rabatkode, der ikke er til produktionsbrug, for at kontrollere meddelelsesformatet og webhookens funktion.

