Esta guía es para desarrolladores que crean páginas personalizadas en la tienda. Si solo quieres una página de fidelización ya preparada, usa el Shopify Loyalty Hub.

El Storefront SDK es una pequeña librería de JavaScript que la aplicación JeriCommerce añade a tu tema de Shopify. Da a tus propias páginas los mismos datos y acciones de fidelización que usa el Loyalty Hub: los puntos del cliente, su nivel y su progreso, las recompensas, los códigos de descuento, las formas de ganar puntos, el historial de puntos y la tarjeta de Wallet.

Tú escribes el HTML, los estilos y el diseño. El SDK se encarga del resto: inicia la sesión del cliente a través de Shopify, llama a la API de JeriCommerce, guarda en caché lo que lees, actualiza tus datos después de cada acción y avisa a tu página cuando algo cambia.

Cuándo usar el SDK

  • Quieres una página de fidelización con tu propio diseño, más allá de lo que permiten los ajustes del Loyalty Hub.
  • Quieres mostrar datos de fidelización en otros lugares de tu tema, como la cabecera, el carrito o una página de producto.
  • Quieres abrir el widget de JeriCommerce desde tus propios botones, o ocultar su botón flotante en algunas páginas.

Configúralo

1

Activa el app embed del SDK

Ve a Shopify Admin > Online Store > Themes > Customize > App embeds y activa SDK de la aplicación JeriCommerce. Guarda el tema.

Este app embed carga el SDK en todas las páginas de tu tienda. Es el mismo embed que muestra el widget de la tienda. El widget es opcional: puedes desactivarlo en JeriCommerce y el SDK sigue funcionando.

2

Añade tu código al tema

Añade tu página como una sección del tema (por ejemplo sections/jeri-loyalty.liquid) y añade esa sección a una plantilla de página. La guía de la página de fidelización personalizada recorre cada paso con una plantilla completa.

3

Espera al SDK antes de usarlo

Ejecuta tu código como un script diferido (deferred), un módulo o después de DOMContentLoaded. Después comprueba que el SDK está instalado y espéralo con JeriCommerce.ready().

Cuando el app embed SDK está desactivado, window.JeriCommerce no existe. Comprueba siempre su existencia y muestra un mensaje breve en lugar de una página rota.

Inicio rápido

<div data-jeri-points>Loading your points...</div>

<script type="module">
  const box = document.querySelector("[data-jeri-points]");

  if (!window.JeriCommerce) {
    box.textContent = "Loyalty is not available right now.";
  } else {
    const jeri = await window.JeriCommerce.ready();
    const session = await jeri.auth.getSession();

    if (!session.authenticated) {
      box.innerHTML = '<button type="button">Sign in to see your points</button>';
      box.querySelector("button").addEventListener("click", () => jeri.auth.login());
    } else {
      const render = data => {
        box.textContent = `You have ${data?.customer.loyaltyData?.balance ?? 0} points`;
      };
      render(await jeri.customer.get());
      jeri.on("customer:updated", render);
    }
  }
</script>

JeriCommerce.ready() descarga el SDK la primera vez que lo llamas y siempre devuelve la misma instancia. No carga ningún dato hasta que llamas a un método.

Cómo funciona el inicio de sesión

El SDK siempre actúa en nombre del cliente que ha iniciado sesión en tu tienda Shopify. Solo hay dos estados:

  • Visitante: auth.getSession() devuelve { authenticated: false }. Los datos públicos funcionan (programa, niveles, recompensas públicas, formas de ganar puntos, boosts públicos). customer.get() y customer.getTierProgress() devuelven null. Los métodos marcados como Customer en la referencia se rechazan con el código de error authentication_required.
  • Cliente con sesión iniciada: { authenticated: true }. Todo funciona para ese cliente.

El SDK nunca acepta un token, un email ni un id de cliente, así que una página nunca puede leer ni modificar los datos de otro cliente. Para pedir a un visitante que inicie sesión, llama a jeri.auth.login(). Lo envía al inicio de sesión de Shopify y de vuelta a la página actual, o a la página que pases como returnTo.

Qué ocurre después de una acción

Cuando un cliente reclama una recompensa, completa una forma de ganar puntos o actualiza su perfil:

  1. La promesa se resuelve de inmediato con el resultado de esa acción, por ejemplo el código de descuento de una recompensa reclamada. Muéstralo al instante.
  2. El SDK marca los datos afectados como desactualizados, de modo que la siguiente lectura nunca devuelve los datos de antes de la acción.
  3. En segundo plano, vuelve a leer los datos que tu página ya había cargado y lanza los eventos correspondientes, como customer:updated con el nuevo saldo.

Tu página nunca necesita llamar a refresh() después de una acción. Suscríbete a los eventos que muestras y vuelve a renderizarlos cuando se lancen. El historial de puntos, el enlace de referido y los enlaces del wallet no tienen evento. Para mantener el historial al día, vuelve a llamar a history.list() cuando se lance customer:updated.

Métodos

Todos los métodos devuelven una promesa. Los métodos marcados como Customer necesitan un cliente con sesión iniciada y se rechazan con authentication_required para los visitantes. Los métodos marcados como Public funcionan para todos.

Sesión y programa
Método Acceso Se resuelve con
auth.getSession() Public { authenticated: boolean }
auth.login({ returnTo? }) Public Envía al visitante al inicio de sesión de Shopify. returnTo es una ruta de tu tienda que empieza por /; si no, la llamada rechaza con invalid_argument. Por defecto, la página actual
program.get() Public Program
config.get() Public ProgramConfig. widget contiene los ajustes activos del widget
Cliente
Método Acceso Se resuelve con
customer.get() Public CustomerData, o null para un visitante
customer.getTierProgress() Public TierProgressStatus, o null para un visitante
customer.updateProfile(fields) Customer Customer. fields: { firstName?, lastName?, phone?, birthdate? }
customer.updatePreferences(preferences) Customer Customer. Consulta preferencias
Niveles y recompensas
Método Acceso Se resuelve con
tiers.list() Public Tier[]
rewards.list({ scope: "public" }) Public PublicRewardDto[], el catálogo para visitantes
rewards.list({ scope: "customer" }) Customer Reward[], con redeemable para este cliente
rewards.claim({ rewardId, pointsToRedeem? }) Customer ClaimedReward. pointsToRedeem, un número entero positivo, solo para recompensas de tipo dynamic
Códigos de descuento y formas de ganar puntos
Método Acceso Se resuelve con
discounts.list({ page?, limit? }) Customer PaginatedCustomerCouponCodes
earnings.list() Public EngagementFlowWithProgramEngagementFlowDto[]
earnings.execute({ earningId, action: "flow" }) Customer ExecuteCustomEarningFlowResponseDto
earnings.execute({ earningId, action: "click", linkId }) Customer Nada. Registra el clic en una red social de una forma de ganar Follow us
Boosts, referidos, historial y wallet
Método Acceso Se resuelve con
boosts.list({ scope, window? }) Public o Customer CustomerBoostsResponseDto. scope es "public" o "customer"
referrals.getLink() Public El enlace de referido del cliente, o null
history.list({ page?, limit? }) Customer PaginatedTransactions
wallet.getLinks() Public Enlaces { ios, android, generic } para añadir la tarjeta de fidelización a un wallet, o null
wallet.sendEmail() Customer Nada. Envía por email la tarjeta de Wallet al cliente
Widget, refresh y eventos
Método Acceso Se resuelve con
widget.open({ page? }) Public Abre el widget. page es una de las páginas del widget
widget.close() Public Cierra el widget
widget.showLauncher() / widget.hideLauncher() Public Muestra u oculta el botón del widget en esta vista de página
refresh() Public Vuelve a leer la sesión y todos los recursos que tu página ya había cargado
on(event, callback) Public Una función que elimina la suscripción

Si omites page y limit, la API usa sus propios valores por defecto.

Datos que puedes mostrar

Todos los resultados son los objetos de la API de JeriCommerce, sin modificar. Estos son los campos que más usarás.

CustomerData

Lo devuelve customer.get() y se envía con customer:updated.

Campo Tipo Descripción
customer.email string Email del cliente
customer.userData.firstName / lastName string Nombre del perfil
customer.loyaltyData.balance number Puntos que el cliente puede gastar
customer.loyaltyData.referralLink string Enlace de referido (también disponible con referrals.getLink())
customer.customerTier.currentTier Tier o null Nivel VIP actual
customer.customerTier.currentTierPoints number Puntos que cuentan para los niveles
customer.balanceNotifications, tierNotifications, locationNotifications, rewardAvailableNotifications, marketingNotifications boolean Preferencias de notificaciones
engagementFlows object Progreso por cada forma de ganar puntos, indexado por tipo de acumulación (por ejemplo CUSTOM_1). count es el número de veces que el cliente la completó
TierProgressStatus

Lo devuelve customer.getTierProgress(). Es el mismo cálculo que usa el Loyalty Hub.

Campo Tipo Descripción
currentTier Tier o null Nivel actual
nextTier Tier o null Siguiente nivel, o null en el nivel más alto
pointsToNextTier number Puntos de nivel que faltan para llegar a nextTier
progressValue number Progreso a lo largo de todos los niveles, de 0 (primer nivel) a 100 (nivel más alto). Úsalo como porcentaje en una barra de progreso
sortedTiers Tier[] Todos los niveles, del más bajo al más alto
Tier
Campo Tipo Descripción
id string Id del nivel
name string Nombre del nivel
requiredPoints number Puntos de nivel necesarios para alcanzarlo
content string Texto de beneficios definido en JeriCommerce (HTML). Sanitízalo antes de insertarlo con innerHTML
image string o null Imagen del nivel
Reward y PublicRewardDto

rewards.list({ scope: "customer" }) devuelve Reward, y rewards.list({ scope: "public" }) devuelve PublicRewardDto.

Campo Tipo Descripción
id string Úsalo como rewardId para reclamar
title, description, image string Contenido de la recompensa
type "fixed" o "dynamic" Fixed reward o Points as cash
cost (customer) / redeemCost (public) number Puntos que cuesta una recompensa fija
minPoints, maxPoints, currencyPerPoint number o null Límites y valor de una recompensa Points as cash
redeemable (customer) boolean El cliente puede reclamarla ahora
nonRedeemableReason (customer) string Por qué no, por ejemplo NOT_ENOUGH_BALANCE
ClaimedReward

Lo devuelve rewards.claim().

Campo Tipo Descripción
code string Código de descuento que se muestra al cliente
pointsRedeemed number, opcional Puntos gastados
discountValue number, opcional Valor del descuento
expiresAt string o null Fecha de caducidad
PaginatedCustomerCouponCodes

Lo devuelve discounts.list(). data es la lista de códigos; count, total, page y pageCount describen la página. Cada código tiene value (el código), expiresAt, discountValue, claimed y coupon (el cupón al que pertenece).

Formas de ganar puntos

earnings.list() devuelve una entrada por cada tipo de acumulación. Cada entrada tiene name (por ejemplo FOLLOW_US, MEMBER_GET_MEMBER, CUSTOM_1), active y programEngagementFlows, la configuración del programa para ese tipo. Usa el primer elemento:

Campo de programEngagementFlows[0] Descripción
id Úsalo como earningId en earnings.execute()
active Muestra solo las formas de ganar activas
title, description, buttonText, url Contenido definido en JeriCommerce
displayIconUrl Icono
properties.points Puntos que se otorgan
properties.executeOnClick true cuando el cliente gana puntos al pulsar un botón: llama a earnings.execute({ earningId, action: "flow" }). No se aplica a FOLLOW_US, MEMBER_GET_MEMBER ni COMPLETE_USER_PROFILE, que rechazan con invalid_argument
properties.followUs Redes sociales de una forma de ganar Follow us, indexadas por red (instagram, facebook, ...). Cada una tiene username y points
properties.requiredFields Campos del perfil que necesita una forma de ganar Complete profile

Un clic necesita la clave de la red como linkId, por ejemplo earnings.execute({ earningId, action: "click", linkId: "instagram" }). El SDK registra el enlace definido en JeriCommerce; nunca abre una ventana, así que abre tú mismo la página de la red social.

earnings.execute() con action: "flow" se resuelve con pointsAwarded, remainingExecutions y recurrence.

PaginatedTransactions

Lo devuelve history.list(). Cada elemento de data tiene name, amount, balanceChange, status y createdAt.

Boosts

boosts.list() se resuelve con { boosts }. Cada boost tiene name, startsAt, endsAt y isActive. El SDK actualiza la lista cuando un boost empieza o termina.

Actualizar preferencias

customer.updatePreferences() necesita todos los indicadores de notificación, además de language si quieres cambiarlo:

const data = await jeri.customer.get();
await jeri.customer.updatePreferences({
  balanceNotifications: data.customer.balanceNotifications,
  tierNotifications: data.customer.tierNotifications,
  locationNotifications: data.customer.locationNotifications,
  rewardAvailableNotifications: data.customer.rewardAvailableNotifications,
  marketingNotifications: true,
});

Las solicitudes siempre usan el idioma de la página. Guardar una preferencia de idioma actualiza los datos del cliente, pero no cambia el idioma de la página.

Eventos

jeri.on(event, callback) devuelve una función que elimina la suscripción. Los eventos también se lanzan la primera vez que tu página lee esos datos, y on() no vuelve a enviar el último valor. Renderiza lo que devuelve cada lectura, como en el inicio rápido, y usa los eventos para los cambios posteriores.

Evento Cuándo se lanza Qué recibe el callback
session:changed Se conoce la sesión por primera vez, y cada vez que cambia { authenticated }
customer:updated Cambiaron los datos del cliente, por ejemplo el saldo tras reclamar una recompensa CustomerData, o null para un visitante
rewards:updated Se actualizó una lista de recompensas que leyó tu página { scope, rewards }, con la misma lista que rewards.list({ scope })
discounts:updated Se actualizó una lista de códigos de descuento que leyó tu página { query, discounts }, con el { page, limit } que leíste y un PaginatedCustomerCouponCodes
earnings:updated Se actualizaron las formas de ganar puntos La misma lista que earnings.list()
boosts:updated Un boost empezó o terminó, o se actualizaron los boosts { scope, boosts }, con el mismo valor que boosts.list({ scope })

Los eventos solo informan de cambios en los datos. El resultado de una acción es el valor con el que se resuelve su promesa.

Errores

Toda llamada fallida se rechaza con un StorefrontSdkError. Comprueba su code, no el texto del mensaje:

Código Significado
invalid_argument La llamada tiene un valor que falta o es incorrecto, por ejemplo un rewardId vacío
authentication_required La llamada necesita un cliente con sesión iniciada, o la sesión del cliente caducó
session_unavailable No se pudo cargar la sesión del cliente. Inténtalo más tarde; nunca se trata como un visitante
api_error JeriCommerce rechazó la solicitud. status contiene el estado HTTP y serverCode el código de la API, como NOT_ENOUGH_POINTS
network_error Una lectura no llegó a JeriCommerce. Una acción que no llega rechaza con mutation_unconfirmed
widget_unavailable El widget está desactivado, o no se pudieron cargar sus ajustes o su código, así que no se puede abrir
mutation_unconfirmed Puede que la acción no se haya aplicado, por ejemplo porque no llegó a JeriCommerce. Vuelve a leer los datos antes de reintentar

El widget y el SDK

El SDK puede controlar el widget de la tienda en la página actual: jeri.widget.open(), close(), showLauncher() y hideLauncher(). Estas llamadas solo afectan a la vista de la página; los ajustes guardados del widget no cambian. Cuando el widget está desactivado en JeriCommerce, widget.open() se rechaza con widget_unavailable y todo lo demás sigue funcionando.

Páginas del widget

widget.open({ page }) acepta estas páginas:

page Abre
advantages Beneficios del programa
profile Perfil
orders Pedidos
coupons Códigos de descuento
rewards Recompensas
loyalty Formas de ganar puntos
referrals Referidos

Los visitantes siempre ven la página de beneficios del programa y de registro.

Limitaciones

  • El SDK solo está disponible en tiendas Shopify a través del app embed. No hay paquete de npm.
  • El editor de temas Canvas de Shopify aún no admite app embeds, así que el SDK no está disponible en temas Canvas.
  • El SDK no crea cuentas ni registra clientes. Los visitantes inician sesión a través de Shopify.
  • Los puntos nunca se calculan en el navegador. Si la API todavía devuelve el saldo anterior, ese es el dato más reciente hasta la siguiente lectura.

Ejemplos

Reclamar una recompensa y mostrar el código
try {
  const claimed = await jeri.rewards.claim({ rewardId: reward.id });
  showCode(claimed.code); // The balance updates through "customer:updated".
} catch (error) {
  if (error.code === "api_error" && error.serverCode === "NOT_ENOUGH_POINTS") {
    showMessage("You need more points for this reward.");
  } else {
    showMessage("Something went wrong. Please try again.");
  }
}
Reclamar una recompensa de tipo Points as cash
await jeri.rewards.claim({ rewardId: reward.id, pointsToRedeem: 200 });
Ganar puntos con un botón
const result = await jeri.earnings.execute({ earningId: earning.id, action: "flow" });
showMessage(`+${result.pointsAwarded} points`);
Abrir el widget en la página de recompensas
document.querySelector("[data-open-rewards]").addEventListener("click", () => {
  jeri.widget.open({ page: "rewards" }).catch(() => {
    window.location.href = "/pages/loyalty"; // The widget is turned off.
  });
});
Mantener actualizado el saldo en la cabecera
const badge = document.querySelector("[data-points-badge]");
const show = data => {
  badge.hidden = !data;
  if (data) badge.textContent = data.customer.loyaltyData.balance;
};
show(await jeri.customer.get());
jeri.on("customer:updated", show);

Una página completa creada con estos métodos está en la guía de la página de fidelización personalizada.