Diese Anleitung richtet sich an Entwickler, die eigene Storefront-Seiten bauen. Wenn du nur eine fertige Loyalty-Seite möchtest, nutze stattdessen den Shopify Loyalty Hub.

Das Storefront SDK ist eine kleine JavaScript-Bibliothek, die die JeriCommerce-App zu deinem Shopify-Theme hinzufügt. Es gibt deinen eigenen Seiten dieselben Loyalty-Daten und Aktionen wie dem Loyalty Hub: Punkte, Stufe und Fortschritt des Kunden, Belohnungen, Rabattcodes, Sammelmöglichkeiten, Punkteverlauf und Wallet-Karte.

Du schreibst HTML, Styles und Layout. Den Rest übernimmt das SDK: Es meldet den Kunden über Shopify an, ruft die JeriCommerce-API auf, speichert gelesene Daten im Cache, aktualisiert deine Daten nach jeder Aktion und meldet deiner Seite, wenn sich etwas ändert.

Wann das SDK sinnvoll ist

  • Du möchtest eine Loyalty-Seite mit eigenem Design, die über die Einstellungen des Loyalty Hub hinausgeht.
  • Du möchtest Loyalty-Daten an anderen Stellen deines Themes anzeigen, zum Beispiel im Header, im Warenkorb oder auf einer Produktseite.
  • Du möchtest das JeriCommerce-Widget über eigene Buttons öffnen oder seinen Launcher auf manchen Seiten ausblenden.

Einrichten

1

SDK-App-Embed aktivieren

Gehe zu Shopify Admin → Online Store → Themes → Customize → App embeds und aktiviere SDK der JeriCommerce-App. Speichere das Theme.

Dieses App-Embed lädt das SDK auf jeder Seite deines Shops. Es ist dasselbe Embed, das das Storefront-Widget anzeigt. Das Widget ist optional: Du kannst es in JeriCommerce ausschalten, und das SDK funktioniert weiter.

2

Code zum Theme hinzufügen

Füge deine Seite als Theme-Section hinzu (zum Beispiel sections/jeri-loyalty.liquid) und füge diese Section zu einem Seitentemplate hinzu. Die Anleitung zur eigenen Loyalty-Seite führt dich Schritt für Schritt mit einem vollständigen Template durch.

3

Auf das SDK warten, bevor du es nutzt

Führe deinen Code als verzögertes Script, als Modul oder nach DOMContentLoaded aus. Prüfe dann, ob das SDK installiert ist, und warte mit JeriCommerce.ready() darauf.

Wenn das App-Embed SDK ausgeschaltet ist, existiert window.JeriCommerce nicht. Prüfe das immer und zeige eine kurze Meldung statt einer kaputten Seite.

Schnellstart

<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() lädt das SDK beim ersten Aufruf herunter und liefert immer dieselbe Instanz zurück. Es lädt keine Daten, bevor du eine Methode aufrufst.

So funktioniert die Anmeldung

Das SDK handelt immer für den Kunden, der in deinem Shopify-Shop angemeldet ist. Es gibt nur zwei Zustände:

  • Besucher: auth.getSession() liefert { authenticated: false }. Öffentliche Daten funktionieren (Programm, Stufen, öffentliche Belohnungen, Sammelmöglichkeiten, öffentliche Boosts). customer.get() und customer.getTierProgress() liefern null. Die Methoden, die in der Referenz mit Customer markiert sind, werden mit dem Fehlercode authentication_required abgelehnt.
  • Angemeldeter Kunde: { authenticated: true }. Alles funktioniert für diesen Kunden.

Das SDK akzeptiert nie ein Token, eine E-Mail-Adresse oder eine Kunden-ID, daher kann eine Seite nie die Daten eines anderen Kunden lesen oder ändern. Um einen Besucher zur Anmeldung aufzufordern, rufe jeri.auth.login() auf. Das leitet ihn zum Shopify-Login und danach zurück zur aktuellen Seite oder zu der Seite, die du als returnTo übergibst.

Was nach einer Aktion passiert

Wenn ein Kunde eine Belohnung einlöst, eine Sammelmöglichkeit abschließt oder sein Profil aktualisiert:

  1. Das Promise wird sofort mit dem Ergebnis dieser Aktion aufgelöst, zum Beispiel mit dem Rabattcode einer eingelösten Belohnung. Zeige ihn direkt an.
  2. Das SDK markiert die betroffenen Daten als veraltet, damit der nächste Lesezugriff nie die Daten von vor der Aktion liefert.
  3. Im Hintergrund liest es die Daten, die deine Seite bereits geladen hat, erneut und löst die passenden Events aus, zum Beispiel customer:updated mit dem neuen Kontostand.

Deine Seite muss nach einer Aktion nie refresh() aufrufen. Abonniere die Events der Daten, die du anzeigst, und rendere sie neu, wenn sie ausgelöst werden. Der Punkteverlauf, der Empfehlungslink und die Wallet-Links haben kein Event. Damit der Verlauf aktuell bleibt, rufe history.list() erneut auf, wenn customer:updated ausgelöst wird.

Methoden

Jede Methode gibt ein Promise zurück. Methoden mit der Markierung Customer benötigen einen angemeldeten Kunden und werden für Besucher mit authentication_required abgelehnt. Methoden mit der Markierung Public funktionieren für alle.

Sitzung und Programm
Methode Zugriff Liefert
auth.getSession() Public { authenticated: boolean }
auth.login({ returnTo? }) Public Leitet den Besucher zum Shopify-Login. returnTo ist ein Pfad deines Shops, der mit / beginnt, sonst schlägt der Aufruf mit invalid_argument fehl. Standardmäßig die aktuelle Seite
program.get() Public Program
config.get() Public ProgramConfig. widget enthält die aktiven Widget-Einstellungen
Kunde
Methode Zugriff Liefert
customer.get() Public CustomerData, oder null für einen Besucher
customer.getTierProgress() Public TierProgressStatus, oder null für einen Besucher
customer.updateProfile(fields) Customer Customer. fields: { firstName?, lastName?, phone?, birthdate? }
customer.updatePreferences(preferences) Customer Customer. Siehe Einstellungen
Stufen und Belohnungen
Methode Zugriff Liefert
tiers.list() Public Tier[]
rewards.list({ scope: "public" }) Public PublicRewardDto[], der Katalog für Besucher
rewards.list({ scope: "customer" }) Customer Reward[], mit redeemable für diesen Kunden
rewards.claim({ rewardId, pointsToRedeem? }) Customer ClaimedReward. pointsToRedeem, eine positive ganze Zahl, nur für Belohnungen vom Typ dynamic
Rabattcodes und Sammelmöglichkeiten
Methode Zugriff Liefert
discounts.list({ page?, limit? }) Customer PaginatedCustomerCouponCodes
earnings.list() Public EngagementFlowWithProgramEngagementFlowDto[]
earnings.execute({ earningId, action: "flow" }) Customer ExecuteCustomEarningFlowResponseDto
earnings.execute({ earningId, action: "click", linkId }) Customer Nichts. Zeichnet den Klick auf ein soziales Netzwerk bei einer Follow us-Sammelmöglichkeit auf
Boosts, Empfehlungen, Verlauf und Wallet
Methode Zugriff Liefert
boosts.list({ scope, window? }) Public oder Customer CustomerBoostsResponseDto. scope ist "public" oder "customer"
referrals.getLink() Public Der Empfehlungslink des Kunden, oder null
history.list({ page?, limit? }) Customer PaginatedTransactions
wallet.getLinks() Public { ios, android, generic } Links, um die Loyalty-Karte zu einer Wallet hinzuzufügen, oder null
wallet.sendEmail() Customer Nichts. Sendet die Wallet-Karte per E-Mail an den Kunden
Widget, Refresh und Events
Methode Zugriff Liefert
widget.open({ page? }) Public Öffnet das Widget. page ist eine der Widget-Seiten
widget.close() Public Schließt das Widget
widget.showLauncher() / widget.hideLauncher() Public Zeigt oder verbirgt den Widget-Button in dieser Seitenansicht
refresh() Public Liest die Sitzung und jede Ressource, die deine Seite bereits geladen hat, erneut
on(event, callback) Public Eine Funktion, die das Abonnement entfernt

Wenn du page und limit weglässt, verwendet die API ihre eigenen Standardwerte.

Daten, die du rendern kannst

Alle Ergebnisse sind unveränderte Objekte der JeriCommerce-API. Dies sind die Felder, die du am häufigsten brauchst.

CustomerData

Wird von customer.get() zurückgegeben und mit customer:updated gesendet.

Feld Typ Beschreibung
customer.email string E-Mail-Adresse des Kunden
customer.userData.firstName / lastName string Name im Profil
customer.loyaltyData.balance number Punkte, die der Kunde ausgeben kann
customer.loyaltyData.referralLink string Empfehlungslink (auch mit referrals.getLink() verfügbar)
customer.customerTier.currentTier Tier oder null Aktuelle VIP-Stufe
customer.customerTier.currentTierPoints number Punkte, die für Stufen zählen
customer.balanceNotifications, tierNotifications, locationNotifications, rewardAvailableNotifications, marketingNotifications boolean Benachrichtigungseinstellungen
engagementFlows object Fortschritt pro Sammelmöglichkeit, nach Sammeltyp geordnet (zum Beispiel CUSTOM_1). count gibt an, wie oft der Kunde sie abgeschlossen hat
TierProgressStatus

Wird von customer.getTierProgress() zurückgegeben. Es ist dieselbe Berechnung, die der Loyalty Hub verwendet.

Feld Typ Beschreibung
currentTier Tier oder null Aktuelle Stufe
nextTier Tier oder null Nächste Stufe, oder null bei der höchsten Stufe
pointsToNextTier number Stufenpunkte, die bis nextTier fehlen
progressValue number Fortschritt über alle Stufen, von 0 (erste Stufe) bis 100 (höchste Stufe). Verwende ihn als Prozentwert für einen Fortschrittsbalken
sortedTiers Tier[] Alle Stufen von der niedrigsten bis zur höchsten
Tier
Feld Typ Beschreibung
id string Stufen-ID
name string Name der Stufe
requiredPoints number Stufenpunkte, die zum Erreichen nötig sind
content string In JeriCommerce festgelegter Vorteilstext (HTML). Bereinige ihn, bevor du ihn mit innerHTML einfügst
image string oder null Bild der Stufe
Reward und PublicRewardDto

rewards.list({ scope: "customer" }) liefert Reward, und rewards.list({ scope: "public" }) liefert PublicRewardDto.

Feld Typ Beschreibung
id string Verwende sie als rewardId zum Einlösen
title, description, image string Inhalt der Belohnung
type "fixed" oder "dynamic" Fixed reward oder Points as cash
cost (customer) / redeemCost (public) number Punkte, die eine feste Belohnung kostet
minPoints, maxPoints, currencyPerPoint number oder null Grenzen und Wert einer Points as cash-Belohnung
redeemable (customer) boolean Der Kunde kann sie jetzt einlösen
nonRedeemableReason (customer) string Warum nicht, zum Beispiel NOT_ENOUGH_BALANCE
ClaimedReward

Wird von rewards.claim() zurückgegeben.

Feld Typ Beschreibung
code string Rabattcode, den du dem Kunden anzeigst
pointsRedeemed number, optional Ausgegebene Punkte
discountValue number, optional Wert des Rabatts
expiresAt string oder null Ablaufdatum
PaginatedCustomerCouponCodes

Wird von discounts.list() zurückgegeben. data ist die Liste der Codes; count, total, page und pageCount beschreiben die Seite. Jeder Code hat value (den Code), expiresAt, discountValue, claimed und coupon (den Coupon, zu dem er gehört).

Sammelmöglichkeiten

earnings.list() liefert einen Eintrag pro Sammeltyp. Jeder Eintrag hat name (zum Beispiel FOLLOW_US, MEMBER_GET_MEMBER, CUSTOM_1), active und programEngagementFlows, die Einrichtung des Programms für diesen Typ. Verwende das erste Element:

Feld von programEngagementFlows[0] Beschreibung
id Verwende sie als earningId in earnings.execute()
active Zeige nur aktive Sammelmöglichkeiten an
title, description, buttonText, url In JeriCommerce festgelegter Inhalt
displayIconUrl Symbol
properties.points Vergebene Punkte
properties.executeOnClick true, wenn der Kunde durch Drücken eines Buttons Punkte sammelt: Rufe earnings.execute({ earningId, action: "flow" }) auf. Gilt nie für FOLLOW_US, MEMBER_GET_MEMBER oder COMPLETE_USER_PROFILE, die mit invalid_argument fehlschlagen
properties.followUs Soziale Netzwerke einer Follow us-Sammelmöglichkeit, nach Netzwerk geordnet (instagram, facebook, ...). Jedes hat username und points
properties.requiredFields Profilfelder, die eine Complete profile-Sammelmöglichkeit benötigt

Ein Klick braucht den Netzwerkschlüssel als linkId, zum Beispiel earnings.execute({ earningId, action: "click", linkId: "instagram" }). Das SDK zeichnet den in JeriCommerce festgelegten Link auf; es öffnet nie ein Fenster, öffne die Social-Media-Seite also selbst.

earnings.execute() mit action: "flow" liefert pointsAwarded, remainingExecutions und recurrence.

PaginatedTransactions

Wird von history.list() zurückgegeben. Jedes Element von data hat name, amount, balanceChange, status und createdAt.

Boosts

boosts.list() liefert { boosts }. Jeder Boost hat name, startsAt, endsAt und isActive. Das SDK aktualisiert die Liste, wenn ein Boost beginnt oder endet.

Einstellungen aktualisieren

customer.updatePreferences() benötigt alle Benachrichtigungs-Flags, plus language, wenn du die Sprache ändern möchtest:

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

Anfragen verwenden immer die Sprache der Seite. Das Speichern einer Spracheinstellung aktualisiert die Daten des Kunden, ändert aber nicht die Sprache der Seite.

Events

jeri.on(event, callback) gibt eine Funktion zurück, die das Abonnement entfernt. Events werden auch ausgelöst, wenn deine Seite diese Daten zum ersten Mal liest, und on() sendet den letzten Wert nicht erneut. Rendere das Ergebnis jeder Abfrage wie im Schnellstart und nutze die Events für spätere Änderungen.

Event Wann es ausgelöst wird Was der Callback erhält
session:changed Die Sitzung ist zum ersten Mal bekannt und bei jeder Änderung { authenticated }
customer:updated Die Daten des Kunden haben sich geändert, zum Beispiel der Kontostand nach einer Einlösung CustomerData, oder null für einen Besucher
rewards:updated Eine Liste von Belohnungen, die deine Seite gelesen hat, wurde aktualisiert { scope, rewards }, mit derselben Liste wie rewards.list({ scope })
discounts:updated Eine Liste von Rabattcodes, die deine Seite gelesen hat, wurde aktualisiert { query, discounts }, mit dem gelesenen { page, limit } und einem PaginatedCustomerCouponCodes
earnings:updated Die Sammelmöglichkeiten wurden aktualisiert Dieselbe Liste wie earnings.list()
boosts:updated Ein Boost hat begonnen oder geendet, oder die Boosts wurden aktualisiert { scope, boosts }, mit demselben Wert wie boosts.list({ scope })

Events melden nur Datenänderungen. Das Ergebnis einer Aktion ist der Wert, mit dem ihr Promise aufgelöst wird.

Fehler

Jeder fehlgeschlagene Aufruf wird mit einem StorefrontSdkError abgelehnt. Prüfe seinen code, nicht den Meldungstext:

Code Bedeutung
invalid_argument Dem Aufruf fehlt ein Wert oder er ist falsch, zum Beispiel eine leere rewardId
authentication_required Der Aufruf benötigt einen angemeldeten Kunden, oder die Sitzung des Kunden ist abgelaufen
session_unavailable Die Sitzung des Kunden konnte nicht geladen werden. Versuche es später erneut; sie wird nie als Besucher behandelt
api_error JeriCommerce hat die Anfrage abgelehnt. status enthält den HTTP-Status und serverCode den API-Code, zum Beispiel NOT_ENOUGH_POINTS
network_error Eine Abfrage hat JeriCommerce nicht erreicht. Eine Aktion, die es nicht erreicht, schlägt mit mutation_unconfirmed fehl
widget_unavailable Das Widget ist ausgeschaltet, oder seine Einstellungen oder sein Code konnten nicht geladen werden, daher kann es nicht geöffnet werden
mutation_unconfirmed Die Aktion wurde möglicherweise nicht ausgeführt, zum Beispiel weil sie JeriCommerce nicht erreicht hat. Lies die Daten erneut, bevor du es noch einmal versuchst

Das Widget und das SDK

Das SDK kann das Storefront-Widget auf der aktuellen Seite steuern: jeri.widget.open(), close(), showLauncher() und hideLauncher(). Diese Aufrufe wirken nur auf die Seitenansicht; die gespeicherten Widget-Einstellungen ändern sich nicht. Wenn das Widget in JeriCommerce ausgeschaltet ist, wird widget.open() mit widget_unavailable abgelehnt, und alles andere funktioniert weiter.

Widget-Seiten

widget.open({ page }) akzeptiert diese Seiten:

page Öffnet
advantages Programmvorteile
profile Profil
orders Bestellungen
coupons Rabattcodes
rewards Belohnungen
loyalty Sammelmöglichkeiten
referrals Empfehlungen

Besucher sehen immer die Programmvorteile und die Anmeldeseite.

Einschränkungen

  • Das SDK ist nur auf Shopify-Storefronts über das App-Embed verfügbar. Es gibt kein npm-Paket.
  • Der Theme-Editor Canvas von Shopify unterstützt App-Embeds noch nicht, daher ist das SDK in Canvas-Themes nicht verfügbar.
  • Das SDK erstellt keine Konten und registriert keine Kunden. Besucher melden sich über Shopify an.
  • Punkte werden nie im Browser berechnet. Wenn die API noch den vorherigen Kontostand liefert, sind das die aktuellsten Daten bis zum nächsten Lesezugriff.

Beispiele

Eine Belohnung einlösen und den Code anzeigen
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.");
  }
}
Eine Points-as-cash-Belohnung einlösen
await jeri.rewards.claim({ rewardId: reward.id, pointsToRedeem: 200 });
Mit einem Button Punkte sammeln
const result = await jeri.earnings.execute({ earningId: earning.id, action: "flow" });
showMessage(`+${result.pointsAwarded} points`);
Das Widget auf der Belohnungsseite öffnen
document.querySelector("[data-open-rewards]").addEventListener("click", () => {
  jeri.widget.open({ page: "rewards" }).catch(() => {
    window.location.href = "/pages/loyalty"; // The widget is turned off.
  });
});
Den Kontostand im Header aktuell halten
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);

Eine vollständige Seite, die mit diesen Methoden gebaut wurde, findest du in der Anleitung zur eigenen Loyalty-Seite.