Gatilhos personalizados

Os gatilhos integrados abrangem as alterações mais importantes para a maioria das lojas. Um gatilho personalizado cobre o restante: você escolhe um evento do Shopify, escreve algumas linhas de JavaScript para determinar se ele é relevante e o que deve ser enviado, e ele se torna um gatilho que você pode usar no Shopify Flow.

Três coisas que ele resolve e que um gatilho embutido não consegue:

  • Execute apenas se a condição for atendida. “Somente quando um pedido acima de 500 euros receber a tag vip” é uma única linha de código, em vez de um fluxo de trabalho que seja executado em todos os pedidos e, em seguida, faça a filtragem.
  • Aplique a condição a uma transição, não a um estado. Seu código vê o valor antes e depois da alteração; portanto, é possível dizer que “o status passou de rascunho para ativo” ou que “o estoque ficou abaixo de 5”. Uma condição baseada no valor atual não consegue expressar isso.
  • Envie exatamente os campos que você deseja. Reestruture o evento de acordo com os valores que seu fluxo de trabalho realmente utiliza, em vez de recuperá-los um por um.

Um gatilho personalizado nem precisa de um evento do tipo Shopify: ele também pode ser executado de acordo com uma programação e determinar por conta própria o que mudou.

Como funciona

  1. Escolha como ele será executado: um evento Shopify ou uma programação.
  2. Para um evento, escolha o evento ao qual ele responde - qualquer evento Shopify que o aplicativo já receba - ou comece a partir de um modelo, que seleciona o evento e preenche o código.
  3. Capture uma carga útil real. Faça essa alteração na sua loja e o aplicativo capturará o corpo exato do evento.
  4. Escreva uma transformação - retorne um objeto para acionar o evento; retorne umnulle para ignorá-lo.
  5. Teste isso com o evento capturado e veja exatamente o que seu fluxo de trabalho receberia.
  6. Ligue-o.
O editor de gatilhos personalizados com nome, identificador, a opção entre um evento Shopify e uma programação, o evento e um modelo
Um novo gatilho personalizado: escolha o que o aciona, selecione o evento e, se quiser, comece a partir de um modelo.

Escrevendo a transformação

Seu gatilho é um módulo JavaScript que exporta uma função chamada transform. Ela recebe quatro argumentos:

  • payload - o corpo bruto do evento Shopify, exatamente como ele é recebido.
  • topic - qual evento foi disparado, por exemplo, PRODUCTS_UPDATE.
  • shop - seu domínio do MyShopify.
  • ctx - ctx.log(...) exibe o resultado no painel de log ao lado do editor, ctx.shopify(...) executa uma consulta GraphQL de administração e ctx.fetch(...) acessa uma URL na internet pública.

O que você retornar determina o que vai acontecer:

  • Retorne um objeto e o gatilho será acionado, transportando esse objeto.
  • Retorne null e o evento será ignorado. É assim que a filtragem funciona - não há uma linguagem de filtragem separada para aprender.
  • Deixe o arquivo em branco e ele será acionado a cada evento desse tipo.
flow-triggers/high-value-vip-order.jsjavascript
/**
 * Only fire for high-value orders carrying the vip tag.
 */
export async function transform(payload, topic, shop, ctx) {
  const total = parseFloat(payload.total_price || "0");
  const tags = (payload.tags || "").split(",").map(t => t.trim());

  if (total < 500) return null;
  if (!tags.includes("vip")) return null;

  ctx.log("firing for", payload.name, total);

  return {
    orderId: payload.admin_graphql_api_id,
    orderNumber: payload.name,
    total,
    currency: payload.currency,
    customerEmail: payload.email,
  };
}

O valor antes da alteração

Shopify

Nas atualizações de produto, pedido e cliente, payload._changes lista todos os campos rastreados que foram alterados por essa atualização, cada um com oldValue e newValue:

  • Produtos: title, handle, description, status, vendor, productType, tags
  • Pedidos: financialStatus, fulfillmentStatus, tags, note, lineItems e customAttributes.<name>
  • Clientes: tags, note, state (ENABLED, DISABLED, INVITED, DECLINED)

A lista fica vazia quando a atualização não envolve esses campos - uma alteração no estoque, por exemplo - ou quando o aplicativo acessa o registro pela primeira vez. O evento de exemplo que você captura no editor mostra o campo, para que você possa ver sua estrutura real antes de inserir dados nele.

flow-triggers/product-went-live.jsjavascript
/**
 * Fire only when a product BECOMES active - not on every later edit of an
 * active product, which is what a condition on the current status would do.
 */
export async function transform(payload, topic, shop, ctx) {
  const change = (payload._changes || []).find((c) => c.field === "status");
  if (!change) return null;
  if (change.oldValue !== "draft" || change.newValue !== "active") return null;

  return {
    productId: payload.admin_graphql_api_id,
    title: payload.title,
    oldStatus: change.oldValue,
    newStatus: change.newValue,
  };
}

Os eventos mais específicos possuem seus próprios valores antigos e novos, portanto, não é necessário usar _changes nesses casos: uma alteração de preço possui oldPrice, newPrice e percentChange; uma alteração de estoque possui _oldAvailable, _newAvailable e _delta; uma alteração de metacampo possui previousValue ao lado de metafield.value.

Comece com um modelo

Na seção “Fires on” do editor, a opção “Começar a partir de um modelo” seleciona o evento e insere o código funcional. Edite as constantes na parte superior, teste e, em seguida, salve. Cada modelo lê um valor antes e depois da alteração:

  • Um campo de produto, pedido ou cliente mudou de um valor para outro - o status passou de “rascunho” para “ativo”, um pedido passou para o status “pago”, uma conta foi ativada.
  • O preço caiu mais de N por cento - uma redução real, não apenas uma alteração de preço qualquer.
  • O estoque caiu abaixo de um limite - o sistema dispara uma vez, no momento em que o estoque ultrapassa a linha, e não a cada venda enquanto ele já estiver baixo.
  • O valor de um metacampo do produto ultrapassou um limite - uma avaliação ficou abaixo de 3, uma margem ultrapassou 40.
  • Pedido pago por um cliente de alto valor - analisa o total gasto pelo cliente em sua loja ao longo do tempo e é acionado somente quando o valor ultrapassa o limite definido por você.
A aba “Transformar” do editor com o código de um modelo que lê payload._changes
Um modelo preenche o código com um exemplo funcional. Edite as constantes na parte superior, teste e, em seguida, salve.

Recuperação de dados adicionais com ctx.shopify

Os corpos dos webhooks contêm apenas os campos enviados pelo Shopify. Quando você precisar de outras informações - como o número de pedidos de um cliente, o estoque de uma variante ou um metacampo - , consulte a API de Admin diretamente a partir da sua transformação:

Enrich the event before decidingjavascript
export async function transform(payload, topic, shop, ctx) {
  const data = await ctx.shopify(`
    query($id: ID!) {
      customer(id: $id) { numberOfOrders tags }
    }
  `, { id: payload.customer.admin_graphql_api_id });

  // Only fire for repeat customers
  if (data.customer.numberOfOrders < 5) return null;

  return {
    orderId: payload.admin_graphql_api_id,
    orderCount: data.customer.numberOfOrders,
  };
}

Ela retorna o objeto data da consulta e lança uma exceção caso haja erros na consulta, para que o erro seja exibido no seu teste, em vez de simplesmente não produzir nenhum resultado sem aviso prévio.

Três limites que vale a pena conhecer:

  • Até 10 chamadas por execução. O enriquecimento requer algumas; mais do que isso geralmente já é um loop. Recupere o que for necessário em uma única consulta, sempre que possível.
  • Ele lê, não grava. Ele utiliza as permissões que você concedeu, e o aplicativo sempre solicita apenas acesso de leitura. Uma consulta aos dados de pedidos falhará, a menos que o acesso aos dados de pedidos seja concedido na página “Permissões”. Para alterar algo em sua loja, faça isso nas ações do Fluxo que se seguem ao gatilho.
  • As credenciais da sua loja nunca chegam ao seu código. A consulta é executada pelo aplicativo em seu nome; portanto, não há nenhum token de acesso dentro da sandbox que possa vazar.

ctx.fetch(url, options) funciona como o recurso fetch do navegador para qualquer conteúdo na internet pública: um feed de estoque de um fornecedor, uma taxa de câmbio, sua própria API. Endereços de redes internas e privadas são rejeitados. Se o seu código de acionamento estiver salvo no GitHub, não inclua uma chave de API nele.

Gatilhos executados de acordo com uma programação

Shopify Flow reage quando algo acontece. Ele não pode reagir quando nada acontece e não consegue detectar nada fora da sua loja. Para isso, selecione “De acordo com uma programação” na seção “O que faz isso ser executado”. Não há nenhum evento Shopify por trás desse gatilho: seu código é executado em intervalos, que variam de a cada 30 segundos até uma vez por dia, e decide por si mesmo o que conta como uma alteração.

É a mesma função transform, com um payload diferente e um valor de retorno diferente:

  • payload.state - o que quer que seu código tenha retornado como state na execução anterior. null na primeira execução.
  • payload.now e payload.lastRunAt - registros de data e hora.
  • **Retorne { state, events }`**`: o novo estado a ser lembrado (até 32 KB) e uma lista de eventos a serem acionados (até 100 por execução). Cada evento aciona **o** **`Custom Trigger`** uma vez; o resourceIdde um evento, caso você tenha definido um, passa a ser o ID do registro no Flow. Retornenull`` quando não houver nada a relatar.

Faça com que a primeira execução apenas registre, sem nunca acionar nada. Na primeira execução, seu código não tem nada com que comparar, então tudo parecerá novo. Armazene o que for observado e não retorne nenhum evento; assim, ativar o gatilho nunca sobrecarregará seus fluxos de trabalho. Todos os modelos fazem isso.

O editor de gatilhos personalizados no modo “Por programação”, com o intervalo e o número de execuções em 30 dias
De acordo com uma programação: escolha o intervalo - o editor mostra quantas execuções isso totaliza - e comece a partir de um modelo programado.
flow-triggers/supplier-stock-changed.jsjavascript
// Fires when a value at a JSON URL changes, with the value before and after.
const URL = "https://api.example.com/stock/1042"

export async function transform(payload, topic, shop, ctx) {
  const res = await ctx.fetch(URL, { headers: { accept: "application/json" } })
  if (!res.ok) throw new Error("The URL answered with HTTP " + res.status)
  const value = String((await res.json()).stock)

  // The first run only remembers the value.
  const previous = payload.state ? payload.state.value : undefined
  if (previous === undefined || previous === value) return { state: { value } }

  return {
    state: { value },
    events: [{ oldValue: previous, newValue: value }],
  }
}

Modelos para acionadores programados, na seção “Iniciar a partir de um modelo” do cartão “Programação”:

  • Produto, pedido ou cliente não atualizado há N dias - revisões de catálogo desatualizadas, pedidos estagnados, reconquista de clientes. Cada registro é acionado uma vez por período de inatividade, e os itens que já estavam em atraso no momento em que a função foi ativada não são acionados todos de uma vez.
  • Um valor fora de Shopify foi alterado - um feed de estoque de fornecedor, uma taxa de câmbio, uma lista de preços, com a diferença e a porcentagem para os números.
  • Novo item em um feed RSS ou Atom - notícias de fornecedores, um feed de listagem, uma página de status.
  • Sem pedidos por N horas - um “batimento cardíaco” para a sua loja. É acionado uma vez quando os pedidos param e outra vez quando eles voltam.

O Test executa seu código uma vez, sem acionar nada e sem salvar o estado, para que você possa executá-lo quantas vezes quiser.

Como usá-lo no Shopify Flow

Todo gatilho personalizado chega ao Flow com o mesmo nome: Gatilho Personalizado. Adicione-o a um fluxo de trabalho e, em seguida, adicione uma condição do tipo “O identificador do gatilho é igual ao identificador do seu gatilho”.

Esse identificador aparece na página do gatilho e nunca muda, mesmo que você renomeie o gatilho - assim, seu fluxo de trabalho continua funcionando.

Cada chave retornada pela sua transformação se torna um campo no gatilho, e o objeto completo também está disponível em formato JSON, caso você prefira analisá-lo por conta própria.

Mantenha o código no GitHub

Você pode conectar um repositório do GitHub para que o código do seu gatilho fique sob controle de versão. As alterações ficam disponíveis para revisão em uma solicitação de pull; você pode ver quem alterou o quê e reverter uma transformação que deixou de funcionar.

Conecte-o na página “Desenvolvedor”, na seção “Conexões”. Você escolhe quais repositórios o aplicativo pode acessar e pode revogar esse acesso no GitHub a qualquer momento.

Assim que um repositório é selecionado, todos os gatilhos existentes são gravados nele imediatamente, e a sincronização é mantida em ambas as direções:

O que você faz O que acontece
Criar, editar ou duplicar um gatilho no aplicativo O arquivo foi enviado ao seu repositório
Excluir um gatilho no aplicativo O arquivo foi removido do seu repositório
Enviar uma alteração para o branch conectado O código do gatilho é atualizado no aplicativo

Cada trigger é um arquivo cujo nome corresponde ao seu identificador, contendo exatamente o módulo que você vê no editor - sem nada a mais. Isso significa que você pode abri-lo em seu próprio editor, executá-lo e verificar sua validade como qualquer outro arquivo JavaScript.

Voltar a uma versão anterior

Você não precisa saber usar o Git para desfazer uma alteração. Assim que um repositório estiver conectado, o editor exibe um menu suspenso “Versão” que lista todas as versões anteriores daquele arquivo, com a data e o autor. Escolha uma delas e ela será carregada no editor como uma alteração não salva, para que você possa lê-la primeiro - é só salvar para que ela volte a ser aplicada.

Teste enquanto edita localmente

Se você estiver editando o arquivo em seu próprio editor, poderá executá-lo com o evento de amostra capturado sem precisar salvá-lo primeiro no aplicativo. Use uma chave de API com o nível de execução disponível na página do Desenvolvedor:

Test the file you are editingbash
curl -X POST https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
  -H "Authorization: Bearer ftk_your_key_here" \
  -H "Content-Type: application/json" \
  -d "$(jq -Rn --rawfile c flow-triggers/high-value-vip-order.js '{code:$c}')"

Você recebe o mesmo resultado que o botão “Testar” do aplicativo exibe: se o código seria executado, o objeto de saída, suas linhas de ctx.log e o tempo de execução.

Nada é acionado e nada é salvo por esse endpoint, e ele não utiliza nenhuma cota do plano - portanto, é seguro executá-lo a cada salvamento a partir de um monitor de arquivos. (O botão “Executar teste” dentro do aplicativo aciona o seu fluxo de trabalho do Flow, para que você possa acompanhá-lo de ponta a ponta; isso também é gratuito.)

Você também pode baixar a carga útil de exemplo separadamente usando uma chave de leitura, salvá-la localmente e executar o arquivo totalmente offline:

Get the captured sample payloadbash
curl https://shopify.workflow-trigger-extensions.app/api/v1/triggers/custom/high-value-vip-order/test \
  -H "Authorization: Bearer ftk_your_key_here"
Um gatilho personalizado consome a cota do meu plano?

Sim, e ele conta todos os eventos que monitora - não apenas aqueles que acionam o seu código. Se o seu gatilho estiver configurado para monitorar “Atualização de produto” e sua loja tiver 60.000 atualizações de produto por mês, isso significa 60.000 eventos, mesmo que seu código seja acionado apenas em 100 deles.

Recebemos, deduplicamos e colocamos cada um desses eventos em fila; em seguida, executamos seu código em um ambiente isolado - tudo isso antes que seu código decida se será acionado ou não. A contagem reflete esse trabalho.

Dito de outra forma: o custo é o mesmo que o do gatilho integrado para o mesmo evento. Não há cobrança extra pela filtragem, e os testes são sempre gratuitos. Um gatilho programado é contabilizado uma vez por execução.

O teste é gratuito?

Sim. Tanto o botão “Executar teste” no aplicativo quanto o endpoint da API /test estão isentos, mesmo que o botão “Executar teste” realmente acione seu fluxo de trabalho do Flow para que você possa observá-lo em execução. Apenas as execuções em tempo real consomem sua cota, portanto, você pode iterar sobre uma transformação quantas vezes quiser.

Por que `payload._changes` está vazio?

Ou a atualização ocorreu fora dos campos monitorados - uma alteração no estoque ou na variante de um produto, por exemplo - ou o aplicativo ainda não possui uma linha de base para esse registro. A linha de base é armazenada na primeira vez que o aplicativo detecta um registro; portanto, a primeira atualização de um registro que ele nunca viu não contém nenhum valor anterior. Todas as atualizações posteriores, porém, contêm.

Meu código pode alterar os dados na minha loja?

Não. O ctx.shopify é executado com as permissões de leitura que você concedeu, e o aplicativo nunca solicita acesso de gravação. Isso é intencional: um gatilho que edita o registro que está monitorando se reinicia automaticamente, o que constitui o loop pelo qual o Shopify desativa os fluxos de trabalho. Altere os dados nas ações do Flow que se seguem ao gatilho.

Posso trocar a alça mais tarde?

Não, e isso é proposital. Seu fluxo de trabalho no Flow é filtrado pelo identificador; portanto, alterá-lo interromperia silenciosamente a execução desse fluxo de trabalho. Você pode renomear o gatilho à vontade - o identificador permanece o mesmo.

O nome de usuário também é o nome do arquivo no seu repositório do GitHub, portanto, ele também nunca muda.

O que acontece se meu código tiver um bug?

O evento é ignorado e o erro é registrado no gatilho, para que você possa verificar o que deu errado. Uma transformação com falha nunca bloqueia nada mais - seus outros gatilhos, sejam eles personalizados ou integrados, continuam funcionando normalmente.

Onde meu código é executado?

Em um ambiente isolado, separado do restante do aplicativo, com um prazo curto e sem acesso às credenciais da sua loja. Ele só tem acesso à carga útil do evento que você capturou, além do que você obtiver por meio de ctx.shopify ou ctx.fetch.

E se eu editar o arquivo no GitHub e no aplicativo ao mesmo tempo?

O que você salvar por último é o que prevalece. Salvar no aplicativo confirma as alterações no arquivo, e enviar para o branch conectado sobrescreve o código armazenado no aplicativo. Se você trabalha principalmente no seu repositório, considere o editor do aplicativo como somente leitura para evitar surpresas.

Um arquivo no meu repositório pode criar um novo gatilho?

Não. Um gatilho também precisa saber a qual evento Shopify ele deve ficar atento, e o arquivo contém apenas código - tentar adivinhar o evento faria com que ele fosse vinculado ao elemento errado. Crie primeiro o gatilho no aplicativo e, em seguida, edite o arquivo dele à vontade.

Ele pode ser acionado por eventos que o aplicativo ainda não recebe?

Não. Um gatilho personalizado monitora os eventos aos quais o aplicativo já está inscrito para a sua loja, o que depende das permissões que você concedeu. Ao conceder permissão para um recurso, seus eventos também ficam disponíveis para gatilhos personalizados. Para qualquer outra coisa, execute-o de acordo com uma programação.

Próximos passos