Desencadenantes personalizados

Los desencadenantes integrados cubren los cambios que más importan a la mayoría de las tiendas. Un desencadenante personalizado se encarga del resto: eliges un evento de Shopify, escribes unas pocas líneas de JavaScript para determinar si es relevante y qué datos enviar, y se convierte en un desencadenante que puedes utilizar en Shopify Flow.

Tres cosas que resuelve y que un disparador integrado no puede:

  • Activa la función solo si se cumple tu condición. «Solo cuando un pedido superior a 500 EUR reciba la etiqueta vip» es una sola línea de código, en lugar de un flujo de trabajo que se ejecute en cada pedido y luego lo filtre.
  • Activa la condición en una transición, no en un estado. Tu código detecta el valor antes y después del cambio, por lo que es posible que «el estado haya pasado de borrador a activo» o que «las existencias hayan bajado por debajo de 5». Una condición basada en el valor actual no puede expresar eso.
  • Envía exactamente los campos que quieras. Reestructura el evento con los valores que tu flujo de trabajo utiliza realmente, en lugar de volver a leerlos uno por uno.

Un disparador personalizado ni siquiera necesita un evento Shopify: también puede ejecutarse según una programación y decidir por sí mismo qué ha cambiado.

Cómo funciona

  1. Elige cómo quieres que funcione: un evento Shopify o una programación.
  2. Para un evento, selecciona el evento al que responde - cualquier evento de Shopify que la aplicación ya reciba - o empieza con una plantilla, que selecciona el evento y rellena el código.
  3. Captura una carga útil real. Realiza ese cambio en tu tienda y la aplicación capturará el cuerpo exacto del evento.
  4. Escribe una transformación: devuelve un objeto para activarla; devuelve unnullo para omitirla.
  5. Pruébalo con el evento capturado y comprueba exactamente qué recibiría tu flujo de trabajo.
  6. Enciéndelo.
El editor de disparadores personalizados con nombre, identificador, la opción de elegir entre un evento Shopify y una programación, el evento y una plantilla
Un nuevo disparador personalizado: elige qué lo activa, selecciona el evento y, si lo deseas, empieza con una plantilla.

Escribir la transformación

Tu «trigger» es un módulo de JavaScript que exporta una función llamada transform. Recibe cuatro argumentos:

  • payload - el cuerpo del evento Shopify sin procesar, tal y como se recibe.
  • topic - qué evento se ha activado, por ejemplo, PRODUCTS_UPDATE.
  • shop - tu dominio de MyShopify.
  • ctx - ctx.log(...) muestra el resultado en el panel de registro situado junto al editor, ctx.shopify(...) ejecuta una consulta GraphQL de administración y ctx.fetch(...) realiza una llamada a una URL en Internet pública.

Lo que devuelvas determinará lo que suceda:

  • Devuelve un objeto y el disparador se activa, transportando dicho objeto.
  • Si **devuelves ``null**, el evento se omite. Así es como funciona el filtrado: no hay que aprender ningún lenguaje de filtrado específico.
  • Si dejas el archivo vacío, se activará cada vez que se produzca un evento de ese 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,
  };
}

El valor antes del cambio

Shopify

En «Actualización de producto», «Actualización de pedido» y «Actualización de cliente», payload._changes muestra todos los campos objeto de seguimiento que se han modificado con esta actualización, cada uno con oldValue y newValue:

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

La lista aparece vacía cuando la actualización no ha afectado a esos campos - por ejemplo, un cambio en el inventario - o cuando la aplicación ve el registro por primera vez. El evento de ejemplo que capturas en el editor muestra el campo, por lo que puedes ver su estructura real antes de escribir en él.

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

Los eventos más específicos tienen sus propios valores antiguos y nuevos, por lo que no es necesario utilizar _changes en ese caso: un cambio de precio tiene oldPrice, newPrice y percentChange; un cambio de inventario tiene _oldAvailable, _newAvailable y _delta; un cambio de metacampo tiene previousValue junto a metafield.value.

Empieza con una plantilla

En la sección «Fires on» del editor, la opción «Empezar desde una plantilla» selecciona el evento e introduce código funcional. Edita las constantes de la parte superior, comprueba el resultado y, a continuación, guarda los cambios. Cada plantilla lee un valor antes y otro después del cambio:

  • Un campo de un producto, un pedido o un cliente ha cambiado de un valor a otro: el estado ha pasado de «borrador» a «activo», un pedido ha pasado a estar pagado o una cuenta se ha activado.
  • El precio ha bajado más de un N por ciento: se trata de una rebaja real, no de un simple ajuste de precio.
  • Las existencias cayeron por debajo de un umbral: se activa una vez, en el momento en que las existencias cruzan la línea, no en cada venta mientras ya estén bajas.
  • El valor de un metacampo del producto ha superado un umbral: una valoración ha caído por debajo de 3 y un margen ha superado el 40.
  • Pedido pagado por un cliente de alto valor: lee el gasto total que el cliente ha realizado en tu tienda a lo largo de su relación contigo y se activa solo cuando se supera el importe que hayas establecido.
La tarjeta «Transform» del editor con el código de una plantilla que dice «payload._changes»
Una plantilla genera código funcional. Edita las constantes que aparecen al principio, pruébalo y, a continuación, guárdalo.

Obtener datos adicionales con ctx.shopify

Los cuerpos de los webhooks solo incluyen los campos que envía Shopify. Si necesitas cualquier otra información - como el número de pedidos de un cliente, el stock de una variante o un metacampo - , consulta la API de administración directamente desde tu transformación:

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

Devuelve el objeto data de la consulta y lanza una excepción si la consulta contiene errores, de modo que el error se muestre en tu prueba en lugar de que la consulta no devuelva ningún resultado sin avisar.

Tres límites que conviene conocer:

  • Hasta 10 llamadas por ejecución. El enriquecimiento requiere unas cuantas; si son más, suele tratarse de un bucle. Recupera lo que necesites en una sola consulta siempre que sea posible.
  • Lee, pero no escribe. Utiliza los permisos que le has concedido, y la aplicación solo solicita acceso de lectura. Una consulta de datos de pedidos fallará a menos que se haya concedido el permiso «Acceso a datos de pedidos» en la página «Permisos». Para realizar cambios en tu tienda, hazlo en las acciones de Flow que siguen al desencadenador.
  • Las credenciales de tu tienda nunca llegan a tu código. La aplicación ejecuta la consulta en tu nombre, por lo que no hay ningún token de acceso dentro del entorno de pruebas que pueda filtrarse.

ctx.fetch(url, options) Funciona igual que la función fetch del navegador para cualquier elemento de la Internet pública: el feed de existencias de un proveedor, un tipo de cambio o tu propia API. Se rechazan las direcciones de redes internas y privadas. Si guardas tu código de activación en GitHub, no incluyas ninguna clave de API en él.

Disparadores que se ejecutan según una programación

Shopify Flow Reacciona cuando ocurre algo. No puede reaccionar cuando no ocurre nada, y no puede ver nada fuera de tu tienda. Para ello, selecciona «Según un horario» en «¿Qué hace que se ejecute esto?». Este tipo de activador no se basa en ningún evento Shopify: tu código se ejecuta a intervalos, desde cada 30 segundos hasta una vez al día, y decide por sí mismo qué se considera un cambio.

Se trata de la misma función transform, pero con un payload diferente y un valor de retorno distinto:

  • payload.state - lo que haya devuelto tu código como state en su ejecución anterior. null en la primera ejecución.
  • payload.now y payload.lastRunAt - marcas de tiempo.
  • Devuelve un objeto { state, events }: el nuevo estado que se debe recordar (hasta 32 KB) y una lista de eventos que se deben activar (hasta 100 por ejecución). Cada evento activa el «Custom Trigger» una vez; el resourceId de un evento, si se ha definido uno, se convierte en el ID del registro en Flow. Devuelve un objeto null cuando no hay nada que informar.

En la primera ejecución, recuerda: nunca actives nada. En la primera ejecución, tu código no tiene nada con lo que comparar, por lo que todo le parecerá nuevo. Guarda lo que veas y no devuelvas ningún evento; así, al activar el disparador, nunca se saturarán tus flujos de trabajo. Todas las plantillas hacen esto.

El editor de activadores personalizados en el modo «Según un calendario», con el intervalo y el número de ejecuciones en 30 días
Según un calendario: elige el intervalo - el editor te muestra a cuántas ejecuciones equivale - y empieza con una plantilla de programación.
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 }],
  }
}

Plantillas para activadores programados, en la sección «Empezar desde una plantilla» de la ficha «Programación»:

  • Producto, pedido o cliente sin actualizar desde hace N días: revisiones de catálogo obsoletas, pedidos atascados, recuperación de clientes. Cada registro se activa una vez por cada periodo de inactividad, y los que ya estuvieran vencidos en el momento de activar la función no se activan todos a la vez.
  • Se ha modificado un valor externo a Shopify: un feed de existencias de un proveedor, un tipo de cambio o una lista de precios, con la diferencia y el porcentaje correspondientes a las cifras.
  • Nueva entrada en un canal RSS o Atom: noticias de proveedores, un canal de listados, una página de estado.
  • No hay pedidos durante N horas: el «latido» de tu tienda. Se activa una vez cuando dejan de llegar pedidos y otra vez cuando vuelven a llegar.

«Test» ejecuta tu código una vez sin activar nada y sin guardar el estado, por lo que puedes ejecutarlo tantas veces como quieras.

Cómo utilizarlo en Shopify Flow

Todos los desencadenantes personalizados aparecen en Flow con el mismo nombre: «Desencadenante personalizado». Añádelo a un flujo de trabajo y, a continuación, añade una condición del tipo «El identificador del desencadenante es igual al identificador de tu desencadenante».

Ese identificador aparece en la página del disparador y nunca cambia, aunque le cambies el nombre al disparador, por lo que tu flujo de trabajo sigue funcionando.

Cada clave que devuelve tu transformación se convierte en un campo del disparador, y el objeto completo también está disponible en formato JSON por si prefieres analizarlo tú mismo.

Guarda el código en GitHub

Puedes conectar un repositorio de GitHub para que el código de tu activador esté bajo control de versiones. Los cambios se pueden revisar en una solicitud de incorporación de cambios, puedes ver quién ha modificado qué y puedes revertir una transformación que haya dejado de funcionar.

Conéctalo en la página «Desarrollador», en la sección «Conexiones». Tú eliges a qué repositorios puede acceder la aplicación y puedes revocar ese acceso desde GitHub en cualquier momento.

Una vez seleccionado un repositorio, todos los desencadenantes existentes se escriben en él de inmediato, y se mantiene la sincronización en ambas direcciones:

A qué te dedicas ¿Qué ocurre?
Crear, editar o duplicar un activador en la aplicación El archivo se ha guardado en tu repositorio.
Eliminar un activador de la aplicación El archivo se ha eliminado de tu repositorio
Enviar una modificación a la rama conectada El código del disparador se actualiza en la aplicación

Cada disparador es un archivo cuyo nombre coincide con su identificador y que contiene exactamente el módulo que ves en el editor, sin nada más a su alrededor. Esto significa que puedes abrirlo en tu propio editor, ejecutarlo y comprobar su sintaxis como cualquier otro archivo de JavaScript.

Volver a una versión anterior

No hace falta saber usar Git para deshacer un cambio. Una vez conectado el repositorio, el editor muestra un menú desplegable «Versión» en el que aparecen todas las versiones anteriores de ese archivo, junto con su fecha y autor. Elige una y se cargará en el editor como un cambio sin guardar, para que puedas leerla primero; al guardarla es cuando se restablece.

Prueba mientras editas localmente

Si estás editando el archivo en tu propio editor, puedes ejecutarlo con el evento de muestra capturado sin necesidad de guardarlo primero en la aplicación. Utiliza una clave de API con el nivel «ejecutar» de la página de desarrolladores:

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}')"

Obtienes el mismo resultado que muestra el botón «Probar» de la aplicación: si se ejecutaría, el objeto de salida, las líneas de ctx.log y el tiempo de ejecución.

Este punto final no** activa ninguna acción ni guarda nada**, y no consume ningún crédito del plan; por lo tanto, se puede ejecutar con total seguridad cada vez que se guarde desde un observador de archivos. (El botón «Ejecutar prueba» de la aplicación sí que activa tu flujo de trabajo de Flow, para que puedas ver cómo se ejecuta de principio a fin; eso también es gratuito).

También puedes descargar la carga útil de ejemplo por separado con una clave de lectura, guardarla localmente y ejecutar el archivo sin conexión:

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"
¿Un disparador personalizado consume la asignación de mi plan?

Sí, y cuenta todos los eventos que analiza, no solo aquellos que activan tu código. Si tu disparador está configurado para detectar «Actualización de producto» y tu tienda tiene 60 000 actualizaciones de producto al mes, eso supone 60 000 eventos, aunque tu código solo se active en 100 de ellos.

Recibimos, deduplicamos y ponemos en cola cada uno de esos eventos; a continuación, ejecutamos tu código en un entorno aislado, todo ello antes de que tu código decida si se activa o no. El recuento refleja ese trabajo.

Dicho de otro modo: cuesta lo mismo que costaría el activador integrado para el mismo evento. No se cobra ningún suplemento por el filtrado, y las pruebas son siempre gratuitas. Un activador programado cuenta una vez por ejecución.

¿Las pruebas son gratuitas?

Sí. Tanto el botón «Ejecutar prueba» de la aplicación como el punto final de la API /test están exentos, aunque «Ejecutar prueba» realmente active tu flujo de trabajo de Flow para que puedas ver cómo se ejecuta. Solo las ejecuciones en tiempo real consumen tu cuota, por lo que puedes iterar sobre una transformación tantas veces como quieras.

¿Por qué está vacío «payload._changes»?

O bien la actualización se ha producido fuera de los campos objeto de seguimiento - por ejemplo, un cambio en el inventario o en una variante de un producto - , o bien la aplicación aún no dispone de una línea de base para ese registro. La línea de base se almacena la primera vez que la aplicación detecta un registro, por lo que la primera actualización de un registro que nunca ha visto no incluye ningún valor anterior. Todas las actualizaciones posteriores sí lo incluyen.

¿Puede mi código modificar los datos de mi tienda?

No. ctx.shopify se ejecuta con los permisos de lectura que le has concedido, y la aplicación nunca solicita acceso de escritura. Esto es intencionado: un disparador que edita el registro que está monitorizando se vuelve a activar, lo que provoca el bucle por el que Shopify desactiva los flujos de trabajo. Modifica los datos en las acciones de Flow que siguen al disparador.

¿Puedo cambiar el tirador más adelante?

No, y eso es a propósito. Tu flujo de trabajo de Flow se filtra por el identificador, por lo que cambiarlo detendría de forma silenciosa la ejecución de ese flujo de trabajo. Puedes cambiar el nombre del desencadenante como quieras; el identificador se mantiene igual.

El nombre de usuario es también el nombre del archivo en tu repositorio de GitHub, por lo que tampoco cambia nunca.

¿Qué pasa si mi código tiene un error?

El evento se omite y el error queda registrado en el disparador, por lo que puedes ver qué ha fallado. Una transformación fallida nunca bloquea nada más: tus otros disparadores, ya sean personalizados o integrados, siguen funcionando sin verse afectados.

¿Dónde se ejecuta mi código?

En un entorno aislado, separado del resto de la aplicación, con un límite de tiempo reducido y sin acceso a las credenciales de tu tienda. Solo ve la carga útil del evento que has capturado, además de lo que obtengas con ctx.shopify o ctx.fetch.

¿Qué pasa si edito el archivo en GitHub y en la aplicación al mismo tiempo?

Gana el que guardes en último lugar. Al guardar en la aplicación, se confirma el cambio en el archivo, y al enviarlo a la rama conectada se sobrescribe el código que hay en la aplicación. Si trabajas principalmente en tu repositorio, considera el editor de la aplicación como de solo lectura para evitar sorpresas.

¿Puede un archivo de mi repositorio crear un nuevo desencadenante?

No. Un disparador también necesita saber a qué evento de Shopify está atento, y el archivo solo contiene código; si se intentara adivinar el evento, se vincularía a algo incorrecto. Crea primero el disparador en la aplicación y, a continuación, edita su archivo como quieras.

¿Puede activarse ante eventos que la aplicación aún no reciba?

No. Un disparador personalizado detecta los eventos a los que la aplicación ya está suscrita para tu tienda, lo cual depende de los permisos que hayas concedido. Si concedes el permiso para un recurso, sus eventos también estarán disponibles para los disparadores personalizados. Para cualquier otra cosa, ejecútalo según una programación.

Próximos pasos