Alertas de código de desconto removido

Quando utilizar este fluxo de trabalho

Utilize este fluxo de trabalho quando um código de desconto for removido e for necessário registar imediatamente o que foi afetado. Este fluxo de trabalho ajuda-o a registar o código removido, o número de utilizações, os detalhes da campanha e um resumo que pode enviar aos departamentos financeiro ou de operações no Slack.

Isto é especialmente útil no caso de códigos de influenciadores que tenham sido divulgados acidentalmente, da divulgação não planeada de cupões ou em qualquer situação em que seja necessário um relatório documentado do impacto após a remoção de um código.

O que irá desenvolver

  • Um fluxo de trabalho do Slack que recebe uma variável text de um webhook e publica esse mesmo texto num canal do Slack.
  • Um fluxo de trabalho Shopify Flow acionado pela remoção do código de desconto.
  • Um passo de dados «Obter desconto» que procura o desconto utilizando o ID do gatilho.
  • Um passo de execução de código que formata um relatório de remoção.
  • Uma etapa de pedido HTTP que envia o relatório para o Slack.

Antes de começar

  • Certifique-se de que a aplicação está instalada e visível na área de administração do Shopify, em «Apps» > «Workflow Trigger Extensions».
  • Certifique-se de que tem acesso a Shopify Flow.
  • Crie ou selecione um canal do Slack onde os alertas devem aparecer.
  • Crie um fluxo de trabalho no Slack que comece com um webhook e aceite um campo text.

Guia em vídeo

Também pode ver o vídeo completo da implementação.

Crie primeiro o fluxo de trabalho do Slack

No Slack, crie um fluxo de trabalho com o gatilho «Começa com um webhook». Adicione uma variável de texto chamada text e, em seguida, adicione a etapa de mensagem que envia uma mensagem para o canal que escolheu, utilizando essa mesma variável text.

Fluxo de trabalho do Slack que começa com um webhook e envia a variável de texto para um canal do Slack
Fluxo de trabalho do Slack que começa com um webhook e envia a variável de texto para um canal do Slack

Conceder acesso com desconto no Workflow Trigger Extensions

Antes de o gatilho aparecer no Shopify Flow, ative o acesso a descontos na aplicação.

No painel de administração do Shopify, aceda a «Apps» > «Workflow Trigger Extensions».

No Painel de Controlo, procure a lista de verificação de configuração.

Ative a opção «Conceder acesso a descontos». Isto ativa os gatilhos relacionados com descontos, tais como «Desconto criado», «Desconto atualizado», «Desconto eliminado», «Código de desconto adicionado», «Código de desconto removido» e «Desconto expirado».

Workflow Trigger Extensions painel a indicar que a funcionalidade «Conceder acesso com desconto» está ativada
Workflow Trigger Extensions painel a indicar que a opção «Conceder acesso com desconto» está ativada

Construir o Shopify Flow

Aceda a Shopify Flow e crie um novo fluxo de trabalho.

Procure os gatilhos relacionados com descontos e selecione «Código de desconto removido» em «Workflow Trigger Extensions».

Shopify Flow Seletor de acionadores com código de desconto: «Removido» selecionado
Shopify Flow Seletor de acionadores com código de desconto: «Removido» selecionado

Adicione a ação Shopify (Obter dados de descontos).

Em «Selecionar uma consulta para filtrar dados», escolha «Avançado». Em «Editar consulta», utilize o ID do desconto do gatilho e remova o prefixo GID Shopify:

bash
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"
Obter dados sobre ações de desconto utilizando uma consulta avançada com o ID do desconto já limpo
Obter dados sobre ações de desconto utilizando uma consulta avançada com o ID do desconto já limpo

Adicione um passo «Executar código» após «Obter dados de descontos».

Utilize a seguinte consulta GraphQL nos campos de seleção das etapas anteriores:

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

Defina as saídas da seguinte forma:

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

Em seguida, cole este código na secção «Escrever código»:

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,
  };
}
Executar o passo de código em Shopify Flow com a consulta, os resultados e o gerador de relatórios em JavaScript
Executar o passo de código em Shopify Flow com a consulta, os resultados e o gerador de relatórios em JavaScript

Adicione a ação «Pedido HTTP». No campo «Variáveis» ou «Substituição do corpo (JSON)», envie o resumo devolvido pela etapa de código «Executar»:

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

Isto publica o resumo gerado no fluxo de trabalho do webhook do Slack, utilizando a variável text.

Ação «Pedido HTTP» em Shopify Flow, que envia o resumo do runCode no campo de texto
Ação «Pedido HTTP» em Shopify Flow, que envia o resumo do runCode no campo de texto

Guarde o fluxo de trabalho depois de ter definido todos os passos.

Para testar, elimine um código de desconto da sua campanha de descontos e verifique se o canal do Slack recebe a mensagem de resumo.

Mensagem no Slack a mostrar o resumo do código de desconto removido
Mensagem no Slack a mostrar o resumo do código de desconto removido

O que o alerta do Slack inclui

  • O código de desconto que foi removido
  • Quer se tratasse de uma remoção em grande quantidade
  • O título da campanha
  • O valor do desconto
  • Datas de início e fim
  • Tempos utilizados para o código removido
  • Impacto estimado da remoção do código
  • Utilização e impacto em toda a campanha
  • Uma análise código a código, quando disponível
  • A data e hora em que a remoção foi processada

Resolução de problemas

Volte a «Aplicações» > Workflow Trigger Extensions e confirme se a opção «Conceder acesso a descontos» está ativada. Os gatilhos de desconto só aparecem depois de o acesso ter sido concedido.

Verifique novamente a consulta avançada em «Editar consulta». O ID deve ser limpo exatamente como se mostra:

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

Certifique-se de que o fluxo de trabalho do Slack começa com um webhook, que o URL do webhook é o mesmo utilizado pela sua solicitação HTTP e que o corpo da solicitação inclui o campo text.

Algumas remoções podem fazer parte de uma ação em massa ou podem não apresentar detalhes completos sobre a utilização. Nesses casos, o resumo continuará a apresentar as melhores informações disponíveis e a identificar claramente os valores em falta.

Melhores práticas

  • Envie estes alertas para um canal específico de finanças ou operações, em vez de para um canal de apoio geral.
  • Escolha um nome específico para o fluxo de trabalho, como «Código de desconto removido», para que seja fácil de identificar no Shopify Flow e no Slack.
  • Teste primeiro com um código de desconto que não seja de produção para confirmar o formato da mensagem e o comportamento do webhook.