Développer l'application de la borne

Développez l'application de la borne autour d'une expérience OmniLab : URLs, messages, identité et réinitialisation.

10 min de lecture

Ce guide explique les aspects techniques du maintien d'OmniLab dans un wrapper borne ou une application wrapper. Utilisez-le avec les articles d'intégration publics lorsque votre équipe contrôle la page hôte, le conteneur WebView ou l'environnement d'exécution de l'appareil autour d'OmniLab.

Choisir le bon modèle de wrapper

Modèle de wrapperBon usageCe que votre équipe gère
Iframe manuel dans une page webUne page borne navigateur ou un wrapper de site existantLa page hôte, le dimensionnement de l'iframe, les permissions et l'UI environnante
Tag JavaScript playerUne page hôte réutilisable doit charger différentes campagnes depuis l'URLLe template de page hôte et la stratégie de paramètre de page
WebView native ou managéeUne app mobile ou un wrapper borne contrôle tout l'écranL'environnement WebView, le bridge, les permissions et le comportement de session

Le modèle de wrapper détermine si la connexion fonctionne

Si votre borne charge l'expérience directement comme page dans une WebView, OmniLab est la page de premier niveau et ses cookies sont first-party — connexion, fci et continuité de session fonctionnent normalement.

Si en revanche vous construisez une page HTML de borne sur votre propre domaine et y placez l'expérience dans une iframe, OmniLab devient une iframe tierce. Les navigateurs bloquent ou isolent ses cookies, et l'aller-retour de connexion perd son état en cours de route — une carte de fidélité scannée arrive donc jusqu'à la vérification d'identité et pas plus loin.

Chargez l'expérience comme page de premier niveau, ou servez-la depuis un sous-domaine du domaine qui sert la page de la borne. C'est la différence entre un scan de carte qui fonctionne et un qui échoue seulement une fois sur site.

Utiliser des URLs prêtes pour l'intégration

Partez des mêmes URLs OmniLab documentées dans les guides d'intégration publics :

  • page d'accueil de campagne lorsque le wrapper doit afficher plusieurs expériences
  • URL directe de point de contact lorsque le wrapper doit lancer immédiatement une expérience
  • embedded=1 lorsque vous utilisez un chemin d'intégration iframe manuel ou WebView

Messages parent-enfant

Une borne occupe tout l'écran : l'essentiel de ce que dit le guide d'intégration sur la hauteur ne s'applique donc pas ici. C'est votre wrapper qui décide de la taille de l'iframe, et rien de ce qu'envoie OmniLab ne doit la modifier. Deux points restent importants.

Mode de dimensionnement

L'expérience annonce à chaque chargement de page comment elle veut être dimensionnée :

Annonce de dimensionnement envoyée par l'expérience
{ type: "sizing", mode: "content" | "viewport" }

Les jeux annoncent viewport : ils remplissent l'iframe qu'on leur donne et ne rapportent volontairement aucune hauteur. Les pages d'accueil et les listings annoncent content, puis envoient des messages resize portant une hauteur mesurée.

Sur une borne en plein écran, vous pouvez garder l'iframe à la taille de l'écran et ignorer les deux. Ne lisez le mode que si votre wrapper affiche parfois l'iframe sur moins que le plein écran : dans ce cas, les messages resize d'une page content sont ce qui vous indique sa hauteur nécessaire.

Ne dimensionnez pas l'iframe d'un jeu d'après ce qu'il rapporte

Une page dimensionnée par le viewport mesure window.innerHeight, qui dans une iframe est la hauteur de cette iframe. Réinjecter cette valeur crée une boucle sans entrée extérieure et l'iframe se fige à sa taille initiale. OmniLab supprime resize en mode viewport pour l'éviter — donc si votre borne attend une hauteur avant d'afficher un jeu, elle attendra indéfiniment. Donnez à l'iframe la taille de l'écran dès le départ.

Placement des drawers

C'est le point qui piège les wrappers de borne écrits à la main. Quand l'expérience ouvre un drawer — panneau de récompense, formulaire, étape de connexion — elle envoie open-modal puis affiche le drawer sans positionnement tant que l'hôte n'a pas répondu.

SensMessageSignification
D'OmniLabopen-modalLe premier drawer s'est ouvert. Les drawers imbriqués ne le répètent pas.
D'OmniLabclose-modal (avec source)Le dernier drawer s'est fermé : cela signifie toujours « entièrement fermé ».
Vers OmniLabready-to-open-modal (avec parentHeight, parentWidth, scrollTop, stickyOffset, position)Accuse réception de l'ouverture et indique au drawer la portion de l'iframe visible par le visiteur. À envoyer à chaque open-modal.
Vers OmniLabready-to-close-modal (avec source)Ferme le drawer au sommet de la pile, quand votre propre interface le referme.

Sans accusé de réception, les drawers s'ouvrent quand même — ils se replient sur une présentation par défaut en pleine hauteur. Sur une borne en plein écran c'est souvent acceptable, puisque l'iframe est toute la zone visible et qu'aucun header sticky n'est à éviter. Testez sur le panneau réel avant de décider de vous en passer, en particulier en portrait.

Listener de messages côté borne
<script>
  const OMNILAB_ORIGIN = "https://experience.example.com";
  const frame = document.getElementById("omnilab-embed");

  window.addEventListener("message", (event) => {
    if (event.origin !== OMNILAB_ORIGIN) return;
    const data = event.data;
    if (!data || typeof data !== "object") return;

    if (data.type === "open-modal") {
      // Indiquez au drawer l'espace dont il dispose : sur une borne en plein
      // écran, c'est simplement l'iframe elle-même.
      frame.contentWindow.postMessage(
        {
          type: "ready-to-open-modal",
          position: 0,
          parentHeight: frame.clientHeight,
          parentWidth: frame.clientWidth,
          scrollTop: 0,
          stickyOffset: 0,
        },
        OMNILAB_ORIGIN
      );
    }
  });
</script>

Vérifiez toujours event.origin. Un wrapper de borne qui réagit à n'importe quel message reçu réagira aux messages de n'importe quelle page chargée dans l'iframe.

Pattern JavaScript player

Lorsqu'une seule page borne doit charger différentes campagnes, utilisez le tag player OmniLab avec un paramètre de requête tel que id.

Page hôte borne réutilisable
<div id="omnilab-container"></div>

<script>
  (function (b, o, n, u, s) {
    var a, t;
    a = b.createElement(u);
    a.async = 1;
    a.src = s;
    t = b.getElementsByTagName(u)[0];
    t.parentNode.insertBefore(a, t);
    o[n] = o[n] || [];
  })(document, window, "_om_async", "script", "https://<omnilab-script-host>/omplayer.js");

  _om_async.push([
    "init",
    {
      domain: "experience.example.com",
      targetId: "omnilab-container",
      queryParam: "id",
    },
  ]);
</script>

Démarrer une session en tant que membre connu avec fci

fci (foreign contact identifier) porte l'identifiant utilisé par votre propre système client pour un contact — typiquement la valeur encodée dans le code-barres d'une carte de fidélité. Ajoutez-le à l'URL de l'expérience quand la borne scanne une carte :

https://experience.example.com/<campaign-public-link>?fci=<valeur-scannée>&embedded=1

Ce qu'OmniLab en fait :

  • La présence de fci impose une session authentifiée. OmniLab redirige vers le flux d'identité client configuré pour l'organisation au lieu de charger l'expérience anonymement, et conserve la query string d'origine à travers la redirection.
  • Si l'identifiant correspond à un contact, le visiteur arrive dans l'expérience déjà connecté, et la participation est attribuée à ce contact.
  • S'il ne correspond à rien, OmniLab affiche une boîte de dialogue bloquante et le visiteur ne peut pas continuer. Il n'y a pas de repli anonyme.

fci est également lu depuis l'en-tête Referer sur les appels API de l'expérience, il continue donc de s'appliquer aux requêtes émises après le chargement initial de la page.

`fci` nécessite un contexte first-party

fci impose une redirection via votre flux d'identité, et cet aller-retour repose sur des cookies. Il n'aboutit que si OmniLab est chargé comme page de premier niveau, ou depuis un sous-domaine de la page qui l'encadre — voir la note sur le modèle de wrapper ci-dessus. Dans une iframe cross-domain, la redirection revient sans rien à reprendre, et tous les scans échouent de la même façon : on croit alors que les identifiants sont faux plutôt que le conteneur.

`fci` nécessite une connexion d'identité client

Sans intégration d'identité client configurée pour l'organisation, OmniLab n'a rien contre quoi résoudre l'identifiant et chaque scan aboutit à la boîte de dialogue bloquante. Vérifiez que la connexion est active dans l'environnement cible avant de démarrer les tests borne. Voir Comptes clients.

Le titre et le corps de cette boîte de dialogue sont surchargeables via les labels personnalisés de l'expérience (fci.identity.error.title et fci.identity.error.description), ce qui vous permet de remplacer le texte par défaut par un message indiquant au visiteur quoi faire sur la borne.

Pour des sessions borne anonymes, omettez complètement fci et placez un formulaire d'acquisition sur le point de contact. Le visiteur le complète sur le clavier de la borne avant de jouer.

Détecter une session abandonnée avec omnilab:idle

Une borne laissée en cours de jeu bloque le visiteur suivant. L'expérience peut signaler au wrapper que personne n'a touché l'écran, pour qu'il propose une réinitialisation.

Le travail se répartit clairement en deux, et la frontière suit celle de l'iframe :

CôtéRôleQui le développe
À l'intérieur de l'iframeSurveille l'activité et émet omnilab:idleUn script d'organisation, installé côté OmniLab
À l'extérieur de l'iframeReçoit le signal, affiche la fenêtre, met fin à la sessionL'application de la borne

Aucune des deux moitiés n'est un comportement natif de la plateforme, et OmniLab ne fournit pas du tout la moitié côté parent. Le code ci-dessous est un document de référence pour celui qui développe l'application de la borne : il vit dans son code, s'exécute dans son wrapper, et c'est à lui de l'implémenter, de le tester et de le maintenir. Considérez-le comme une spécification à transmettre, pas comme quelque chose que vous installez.

La moitié à l'intérieur de l'iframe est un script d'organisation qu'un administrateur ajoute au niveau de l'organisation globale, limité aux campagnes qui tournent sur borne. Voir Écrire des scripts d'organisation pour la façon dont ces scripts sont écrits et installés.

Le script à l'intérieur de l'expérience

Détection d'inactivité, limitée aux campagnes borne
<script>
(function () {
  // Ne s'exécute que sur les campagnes qui tournent réellement sur borne.
  const TRIGGER_STRINGS = ["summer-experience-kiosk", "back-to-school-2026-kiosk"];

  const shouldActivate = TRIGGER_STRINGS.some((trigger) =>
    window.location.href.includes(trigger)
  );
  if (!shouldActivate) return;

  const IDLE_TIMEOUT = 10000;
  let timer;

  function sendIdle() {
    window.parent.postMessage(
      { type: "omnilab:idle", timestamp: Date.now() },
      "*"
    );
    reset();
  }

  function reset() {
    clearTimeout(timer);
    timer = setTimeout(sendIdle, IDLE_TIMEOUT);
  }

  ["click", "mousedown", "mousemove", "keydown", "scroll", "touchstart", "touchmove"].forEach(
    (e) => document.addEventListener(e, reset, { passive: true })
  );

  reset();
})();
</script>

À savoir avant de développer le côté parent :

  • La liste d'autorisation est une correspondance de sous-chaîne sur l'URL complète. Chaque entrée est comparée avec includes() à window.location.href : un lien public de campagne fonctionne donc comme entrée. Tout ce qui ne correspond pas sort immédiatement et n'installe aucun listener — c'est ce qui garde le script inerte sur vos campagnes hors borne.
  • Le minuteur redémarre après chaque signal. Une borne abandonnée émet omnilab:idle toutes les 10 secondes, pas une seule fois. Votre listener doit tolérer les répétitions.
  • Sept événements d'activité réinitialisent le minuteurclick, mousedown, mousemove, keydown, scroll, touchstart, touchmove — enregistrés en mode passif sur document.
  • Seules les interactions à l'intérieur de l'iframe comptent. Un visiteur qui touche l'interface propre à la borne ne réinitialise pas ce minuteur.

Installez-le comme script iframe uniquement sur le déclencheur de visite, puis republiez chaque campagne concernée. Un script enregistré mais non republié n'atteint pas l'expérience en ligne.

Ce que l'application de la borne doit implémenter

Tout ce qui suit s'exécute dans le wrapper de la borne, pas dans OmniLab. Transmettez-le à votre prestataire borne comme le contrat que son côté doit respecter.

Gestion de l'inactivité côté parent — développée par le prestataire borne
let modalOpen = false;
let countdownInterval = null;

window.addEventListener("message", (event) => {
  if (event.data?.type === "omnilab:idle" && !modalOpen) {
    showInactivityModal();
  }
});

function showInactivityModal() {
  modalOpen = true;
  let countdown = 10;
  updateCountdown(countdown);

  countdownInterval = setInterval(() => {
    countdown -= 1;
    updateCountdown(countdown);
    if (countdown <= 0) closeIframe();
  }, 1000);
}

function stayInSession() {
  clearInterval(countdownInterval);
  modalOpen = false;
  hideModal();
}

function closeIframe() {
  clearInterval(countdownInterval);
  modalOpen = false;
  document.getElementById("omnilab-embed").src = "";
  showWelcomeScreen();
}

Testez `event.data.type`, pas `event.data`

Le message est un objet : event.data === "omnilab:idle" ne correspond donc jamais et la fenêtre n'apparaît pas. Lisez event.data?.type. C'est la même enveloppe que les messages resize et de modale ci-dessus : un seul listener peut donc tous les traiter.

Le garde modalOpen n'est pas optionnel. Sans lui, le signal répété relance le compte à rebours toutes les 10 secondes et la fenêtre n'expire jamais.

Prévoyez la durée totale de réinitialisation lors du dimensionnement du parcours : 10 secondes de silence avant le premier signal, plus la durée du compte à rebours de votre fenêtre. Avec un compte à rebours de 10 secondes, une borne abandonnée se libère en une vingtaine de secondes.

Ce que la fenêtre doit proposer

C'est le prestataire borne qui la conçoit et la développe. OmniLab n'a aucun contrôle dessus et ne peut pas la styler.

ÉlémentRôle
MessageIndique au visiteur que la session va se terminer
Compte à reboursMinuteur visible pour que la réinitialisation ne soit pas une surprise
ResterFerme la fenêtre et revient à l'expérience
Retour à l'accueilFerme l'iframe immédiatement et affiche l'écran d'accueil

C'est le vidage du src de l'iframe qui met réellement fin à la session — l'expérience OmniLab n'a aucun moyen de se décharger elle-même : si l'application de la borne n'agit pas sur le signal, il ne se passe tout simplement rien.

Responsabilités de l'hôte hors d'OmniLab

Votre wrapper doit prendre en charge :

  • les permissions au niveau de l'appareil pour la caméra, le microphone et la géolocalisation
  • la fenêtre d'inactivité et la décision de réinitialiser l'iframe — OmniLab peut signaler l'inactivité, mais seul le wrapper peut fermer la session
  • le matériel de scan ou les périphériques externes
  • le passage de session entre votre wrapper et OmniLab
  • la journalisation et les diagnostics de support à distance pour le conteneur lui-même

Checklist de debug

  • Confirmez que le wrapper charge la bonne URL de campagne ou de point de contact.
  • Vérifiez que l'iframe ou la WebView peut recevoir la caméra et les autres permissions requises.
  • Contrôlez que les événements postMessage d'OmniLab atteignent le wrapper parent, et que votre listener vérifie event.origin.
  • Ouvrez un jeu et vérifiez que l'iframe est déjà en plein écran — une page dimensionnée par le viewport n'envoie aucune hauteur, donc un wrapper qui en attend une n'affiche rien.
  • Testez les drawers, claviers et flux de modales dans le vrai conteneur borne, pas seulement dans un onglet de navigateur autonome.
  • Journalisez les messages OmniLab bruts pendant le staging pour comparer rapidement le comportement attendu et le comportement réel.

Gardez les secrets hors du client borne

Si la borne a aussi besoin d'un accès API server-to-server, effectuez l'authentification et les appels API depuis votre propre backend. N'intégrez pas les identifiants client OmniLab dans la page borne ou le bundle WebView.

Patterns d'URL et paramètres de requête pour borne

Les déploiements borne utilisent généralement l'un de ces patterns d'URL. Incluez toujours embedded=1 lors du chargement dans une iframe ou une WebView.

Cas d'usagePattern d'URL
Page d'accueil de campagnehttps://experience.example.com/<campaign-public-link>?embedded=1
Point de contact directhttps://experience.example.com/<campaign-public-link>?c=<touchpoint-id>&embedded=1
Espace spécifiquehttps://experience.example.com/<campaign-public-link>?s=<space-id>&embedded=1
Langue fixeAjoutez &l=<language-code>
Variante fixeAjoutez &v=<variant-id>
Membre fidélité connuAjoutez &fci=<foreign-contact-identifier>

Pour les déploiements utilisant le tag JavaScript player, l'URL de la page hôte contrôle quelle campagne se charge :

  • https://kiosk.example.com/?id=summer-campaign
  • https://kiosk.example.com/?id=summer-campaign%3Fc%3D<touchpoint-id>%26embedded%3D1

Encodez toute query string imbriquée avant de publier le lien vers le tag player.

Pour aller plus loin

Sur cette page