Configuration de l’authentification par lien profond

Les liens profonds permettent à l’utilisateur de s’authentifier dans le Web Shop via le jeu en un seul clic, contournant ainsi le processus d’authentification par ID utilisateur ou via Xsolla Login.

Si le jeu est déjà installé sur l’appareil mobile de l’utilisateur, il sera automatiquement redirigé vers le jeu via un lien profond pour obtenir un jeton d’autorisation.

Flux utilisateur

  1. Un utilisateur non autorisé du Web Shop clique sur le bouton de connexion ou sur le bouton d'achat. Une fenêtre modale permettant de saisir l'ID utilisateur ou de se connecter via le jeu mobile s'affiche.
  1. L'utilisateur clique sur le bouton de connexion via le jeu.
  2. L'utilisateur est redirigé vers le jeu où, il est automatiquement redirigé vers le Web Shop en tant qu'utilisateur autorisé.

  1. Un utilisateur non autorisé du Web Shop clique sur le bouton de connexion ou sur le bouton d'achat. Une fenêtre modale permettant de saisir l'ID utilisateur ou de se connecter à la version mobile du jeu à l'aide d'un code QR s'affiche.

  1. L'utilisateur scanne le code QR à l'aide de son appareil mobile, après quoi le Web Shop s'ouvre sur celui-ci.
  2. La version mobile du jeu s'ouvre sur l'appareil de l'utilisateur.
  3. L'utilisateur est automatiquement redirigé vers le Web Shop sur son appareil mobile en tant qu'utilisateur autorisé.
  1. Dans les paramètres de l'application mobile de votre jeu, enregistrez un schéma d'URL pour ouvrir le jeu via un lien profond :
    • dans les applications Android — dans le fichier AndroidManifest.xml ;
    • dans les applications iOS — dans le fichier Info.plist.
Après l’enregistrement du système, lorsque l’utilisateur s’authentifie dans le Web Shop via le jeu, le jeu doit s’ouvrir à l’adresse spécifiée. Exemple de schéma d’URL pour l’enregistrement d’un jeu : gamename://authorize, où :
  • gamename — nom de votre jeu qui doit s’ouvrir sur un appareil mobile pour l’authentification de l’utilisateur ;
  • authorize — exemple de nom d’action à effectuer après l’ouverture du jeu gamename. Utilisez le nom d’action qui correspond aux actions du système d’exploitation de votre application.
Exemple d’enregistrement d’un schéma d’URL dans les applications iOS :
Copy
Full screen
Small screen
    <key>CFBundleURLTypes</key>
      <array>
        <dict>
          <key>CFBundleTypeRole</key>
          <string>Editor</string>
          <key>CFBundleURLSchemes</key>
          <array>
            <string>gamename</string>
          </array>
        </dict>
      </array>
    

    Exemple d’enregistrement d’un schéma d’URL dans les applications Android :

    Copy
    Full screen
    Small screen
      <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="gamename" android:host="authorize" />
      </intent-filter>
      

      1. Implémentez la génération d'un jeton d'autorisation au format JWT en utilisant l'ID utilisateur dans le jeu.

      ParamètreTypeDescription
      stringID de la méthode d’autorisation du Compte éditeur. Obligatoire.
      stringURL pour recevoir le webhook Validation utilisateur dans le Web Shop. L’URL doit correspondre à l’adresse spécifiée lors de la configuration de l’authentification par ID utilisateur. Xsolla attend un code HTTP 200 en réponse au webhook pour autoriser l’utilisateur du Web Shop. En cas de réception d’une réponse avec un code HTTP 404 ou d’absence de réponse, l’utilisateur ne sera pas autorisé.

      Pour désactiver les webhooks lors de l’authentification par lien profond, spécifiez la valeur "webhookUrl": "https://nowebhook.com". Dans ce cas, Xsolla ne vérifie pas l’existence de l’utilisateur, assurez-vous donc que dans le paramètre user.id vous passez l’ID d’un utilisateur qui existe dans le jeu.
      Obligatoire.
      stringL’ID du projet se trouve dans le Compte éditeur, à côté du nom de votre projet ou dans la barre d’adresse du navigateur. L’URL présente le format suivant : https:​//publisher.xsolla.com/merchant ID/Publisher Account section. Obligatoire.
      stringID utilisateur dans le jeu. Obligatoire.
      stringNom d’utilisateur. Obligatoire.

      Exemple d’appel à la méthode API de génération de jeton utilisateur à l’aide de curl :

      Copy
      Full screen
      Small screen
      curl --location 'https://sb-user-id-service.xsolla.com/api/v1/user-id' \
      --header 'Content-Type: application/json' \
      --data '{
       "loginId": "000001aa-001a-0ab0-00001-01a01a01a01a",
       "webhookUrl": "https://nowebhook.com",
       "settings": {
         "projectId": 123456,
         "merchantId": 123456
       },
       "user": {
         "id": "123",
         "name": "a-user-name"
       }
      }'
      

      Vous obtiendrez une réponse du type {token=“JWT_TOKEN”}.

      1. Ajoutez une fenêtre contextuelle avec une notification d'autorisation réussie (facultatif).
      2. Implémentez l'ouverture du Web Shop dans le navigateur à l'aide du jeton utilisateur obtenu.

      Exemple de création d’une URL pour ouvrir le Web Shop dans le navigateur d’un utilisateur autorisé :

      • https:​//example.com/?token={token}, si vous utilisez un domaine personnalisé
      • https:​//example.xsollasitebuilder.com/?token={token}, si vous utilisez un domaine Xsolla
      {token} est le jeton d’autorisation de l’utilisateur.Vous pouvez effectuer vous-même les réglages du côté du Site Builder en utilisant les exemples de code fournis ci-dessous.
      Pour obtenir de l’aide pour la configuration, contactez votre responsable de la réussite client ou envoyez un e-mail à csm@xsolla.com.
      Note
      Commencez par configurer l’authentification par ID utilisateur. Cela est nécessaire afin que l’utilisateur dispose d’une méthode alternative d’authentification si l’authentification par lien profond n’est pas disponible. Par exemple, si le jeu n’est pas installé sur l’appareil mobile de l’utilisateur.
      1. Ouvrez le projet dans le Compte éditeur.
      2. Dans le menu latéral, cliquez sur Site Builder.
      3. Cliquez sur Configure sur la carte du site de votre Web Shop avec authentification par ID utilisateur.
      4. Ajoutez l’authentification par lien profond à l’aide d’un bloc Custom code.
      5. Dans le bloc Custom code de l’onglet JavaScript, ajoutez le code personnalisé ci-dessous, qui implémente l’authentification par lien profond pour les versions mobiles et de bureau.
      1. Remplacez les valeurs des constantes dans le code par vos valeurs, où :
        • DEEPLINK_URL — schéma d'URL pour ouvrir le jeu via le lien profond ;
        • QR_CODE — code QR pour ouvrir le jeu au format SVG. Le code QR doit contenir un lien vers le Web Shop avec le paramètre source = pc ;
        • deeplink__description — description affichée sous le bouton d'authentification du jeu dans l'application mobile ;
        • qr-code-auth__description — description affichée sous le code QR dans la version de bureau du jeu.
      Copy
      Full screen
      Small screen
      {
          const DEEPLINK_URL = 'gamename://authorize';
          const QR_CODE = `YOUR_SVG_WITH_QR_CODE`;
          handleQRCodeRedirect();
      
      // Event handlers are set up for opening the authentication modal or for successful authentication.
      // If the event of opening the authentication modal occurs, the handler is triggered and a button or QR code is added to this window.
      // If the event of successful authentication occurs, the handler is triggered and the successful authentication modal is opened.
      
          window.addEventListener('custom-event:modal:render', updateLoginModal);
          window.addEventListener('custom-event:authorization:login', showLoginSuccessPopup);
          let getToken;
      
          const searchParams = new URLSearchParams(window.location.search);
          const tokenParam = searchParams.get('token');
          if (tokenParam) {
              showLoginSuccessPopup(tokenParam);
          }
      
          window.SB.subscribe(api => {
              getToken = api.getToken;
          });
      
      // Customization of the authentication modal window (adding a QR code for the desktop version of the game or a login button for the mobile version of the game).
      
          function updateLoginModal({detail}) {
              const {name, element} = detail;
              if (name !== 'user-id' || element.dataset.isQrAdded === 'true') return;
              const isMobile = checkMobileDevice();
              const ModalBody = element.querySelector('.ui-site-modal-window__body');
              const ModulInput = ModalBody.querySelector('.user-id-modal__input');
              element.classList.toggle('mobile-layout', isMobile);
              ModalBody.append(getTemplate(isMobile));
      
              element.dataset.isQrAdded = true;
          }
      
      // Function to display the successful authentication modal window.
      
        function showLoginSuccessPopup(t) {
          let username = 'Gamer';
          try {
            const token = getToken?.() || t;
            if (token) {
              const jwtData = parseJwt(token);
              const payload = JSON.parse(jwtData.payload);
              if (payload.userInfo.name) {
                username = payload.userInfo.name;
              } else if (jwtData.username) {
                username = jwtData.username;
              } else if (jwtData.server_custom_id) {
                username = jwtData.server_custom_id;
              }
            }
          } catch (e) {
            console.log('error', e);
          }
      
          const Modal = document.querySelector('.deeplink-auth');
          const ModalTitle = Modal.querySelector('.deeplink-auth__title');
      
          ModalTitle.textContent = ModalTitle.textContent.replace(/\{username}/g, username);
      
          Modal.classList.add('_visible');
          Modal.querySelector('.deeplink-auth__button').addEventListener('click', closeModal);
      
          function closeModal() {
            Modal.classList.remove('_visible');
          }
        }
      
      // Function that displays a QR code for the desktop version of the game or a login button for the mobile version of the game. Your_game should be replaced with the actual name of your game. To continue with the authentication, the game has to be installed on the user's phone.
      
        function getTemplate(isMobile) {
          const Deeplink = document.createElement('div');
      
          if (isMobile) {
            Deeplink.innerHTML = `
              <div class="deeplink__title"><span class="deeplink__title-text">or login via game</span></div>
              <a href="${DEEPLINK_URL}" class="deeplink__button" rel="noopener">Login via Mobile Game</a>
              <div class="deeplink__description">
                You need to have the Your_game installed on your phone in order to proceed with authorization and purchase
              </div>
            `;
      
            return Deeplink;
          }
      
          Deeplink.innerHTML = `
            <div class="separator">
              <span class="separator__text">or login via game</span>
            </div>
            <div class="qr-code-auth">
              <div class="qr-code-auth__image">${QR_CODE}</div>
              <span class="qr-code-auth__description">
              Scan the QR code with your phone and you will continue purchasing on your mobile device
              </span>
            </div>
          `;
      
          return Deeplink;
        }
      
        function checkMobileDevice() {
          return (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent));
        }
      
      //  Function that decodes the JWT token and returns its payload as a JavaScript object.
      
        function parseJwt(token) {
          const base64Url = token.split('.')[1];
          const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
          const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
          }).join(''));
      
          return JSON.parse(jsonPayload);
        }
      
      // Function to verify the vent occurred when the user scanned the QR code.
      
        function handleQRCodeRedirect() {
          const url = new URL(window.location.href);
          const source = url.searchParams.get('source');
          if (source === 'pc') {
            if (checkMobileDevice()) {
              const button = document.getElementById("deeplink-redirect");
              button.click();
            }
      
            setTimeout(function () {
              url.searchParams.delete('source');
              window.history.pushState({}, '', url);
            }, 2000);
          }
        }
      }
      
      1. Dans le bloc Custom code de l'onglet HTML, ajoutez le code pour une fenêtre modale indiquant une authentification réussie dans le Web Shop. Vous pouvez définir vos propres valeurs de texte.
      Copy
      Full screen
      Small screen
      <div class="ui-site-modal-window deeplink-auth">
        <div class="deeplink-auth__body">
          <div class="deeplink-auth__image"></div>
          <div class="deeplink-auth__title">Hi, {username}!</div>
          <div class="deeplink-auth__description">You’ve successfully logged in to the Web Shop</div>
          <button class="deeplink-auth__button">Continue</button>
        </div>
      </div>
      
      <a href="gamename://authorize" id="deeplink-redirect" style="display: none;"></a>
      
      1. Dans le bloc Custom code de l'onglet CSS, ajoutez les styles pour les nouveaux composants d'interface. Les styles recommandés sont suggérés ci-dessous. Spécifiez l'URL de votre propre image affichée lors d'une authentification réussie.
      Copy
      Full screen
      Small screen
      --deeplink-success-image-url: url("Successful_authorization_image_url");
      
      .html-v2:has(.deeplink-auth._visible) {
        z-index: var(--modalsZIndex);
      }
      
      .user-id-modal__content {
        padding: 0 16px !important;
      }
      
      @media (min-width: 768px) {
        .user-id-modal__content {
          max-width: 460px !important;
          padding: 0 3.8rem 4rem !important;
          overflow: auto;
          justify-content: center;
        }
      }
      
      .mobile-layout .user-id-modal__close {
        right: 8px;
        top: 16px;
      }
      
      .user-id-modal__logo {
        width: 109px;
        height: 38px;
        margin: 4rem auto 2rem;
      }
      
      .user-id-modal.mobile-layout .user-id-modal__logo {
        margin: 3rem auto 2.5rem;
      }
      
      .user-id-modal__wrapper {
        margin-top: 0;
      }
      
      .user-id-modal__title {
        margin-bottom: 3rem;
      }
      
      .user-id-modal__input-wrapper .ui-site-input {
        height: 41px;
      }
      
      .user-id-modal__input {
        margin-top: 0;
        border-radius: var(--border-radius-16);
      }
      
      .user-id-modal__button {
        height: auto;
        margin: 12px auto 0;
        padding: 1.25rem;
        border-radius: var(--border-radius-16);
      }
      
      .user-id-modal__button .simple-button__text {
        font-size: 1.8rem;
        font-weight: 500;
        line-height: 2.5rem;
        letter-spacing: 0.5px;
      }
      
      .user-id-modal .ui-site-instruction-cut__title .ui-site-description {
        margin-bottom: 3rem;
        font-size: 1.2em;
      }
      
      .user-id-modal .ui-site-instruction-cut__steps {
        overflow-y: visible;
      }
      
      .user-id-modal .ui-site-instruction-card:first-child {
        margin-top: 0;
      }
      
      .user-id-modal .ui-site-instruction-card:last-child {
        padding-bottom: 4rem;
      }
      
      .user-id-modal .separator {
        display: flex;
        margin-bottom: 3.8rem;
        align-items: center;
      }
      
      .user-id-modal .separator__text {
        padding: 0 1.5rem;
        font-family: var(--main-font);
        font-size: 1.8rem;
        font-weight: 500;
        line-height: 2.5rem;
        letter-spacing: 0.8px;
      }
      
      .user-id-modal .separator::before,
      .user-id-modal .separator::after {
        content: "";
        background-color: #797979;
        height: 1px;
        flex-grow: 1;
      }
      
      .user-id-modal .qr-code-auth__image {
        display: block;
        width: 160px;
        height: 160px;
        margin: 0 auto 20px;
      }
      
      .user-id-modal .qr-code-auth__image svg {
        display: block;
        width: 100%;
        height: 100%;
      }
      
      .user-id-modal .qr-code-auth__description {
        display: inline-block;
        font-family: var(--main-font);
        font-size: 1.5rem;
        font-weight: 400;
        line-height: 2.5rem;
        text-align: center;
        color: rgba(255, 255, 255, 0.7);
      }
      
      .deeplink__title {
        display: flex;
        margin-bottom: 3.8rem;
        align-items: center;
      }
      
      .deeplink__title-text {
        padding: 0 1.5rem;
        font-family: var(--main-font);
        font-size: 1.8rem;
        font-weight: 500;
        line-height: 2.5rem;
        letter-spacing: 0.8px;
      }
      
      .user-id-modal .deeplink__title::before,
      .user-id-modal .deeplink__title::after {
        content: "";
        background-color: #797979;
        height: 1px;
        flex-grow: 1;
      }
      
      .deeplink__button {
        display: block;
        margin-bottom: 2.2rem;
        padding: 1.3rem;
        font-family: var(--main-font);
        font-size: 1.8rem;
        font-weight: 500;
        line-height: 2.5rem;
        letter-spacing: 0.5px;
        text-align: center;
        color: var(--button-text-color);
        background: var(--accent-color);
        border-radius: var(--border-radius-16);
        text-decoration: none;
        transition: transform .2s cubic-bezier(.509, .001, .25, 1);
      }
      
      .deeplink__button:hover {
        transform: scale(.95);
      }
      
      .deeplink__description {
        margin-bottom: 2rem;
        font-family: var(--main-font);
        font-size: 1.6rem;
        line-height: 2.3rem;
        text-align: center;
        color: rgba(255, 255, 255, 0.7);
        letter-spacing: 0.5px;
      }
      
      .deeplink__cta-wrapper {
        display: flex;
        flex-direction: row;
        gap: 1.8rem;
        padding: 0 1.2rem;
      }
      
      .deeplink__cta-wrapper .ui-site-calltoaction__item {
        margin: 0;
        flex-grow: 2;
      }
      
      .deeplink__cta-wrapper .ui-site-calltoaction {
        width: 100%;
        min-width: auto;
        height: auto;
        min-height: auto;
        padding-bottom: 36%;
        background-size: contain;
        border-radius: 0;
      }
      
      .deeplink-auth {
        display: none;
      }
      
      .deeplink-auth._visible {
        display: flex;
      }
      
      .deeplink-auth__body {
        position: relative;
        width: 100%;
        max-width: 336px;
        padding: 16px;
        border-radius: 12px;
        background-color: var(--colors-core-background-secondary-rgb);
        border: var(--border-size) solid var(--border-color);
        font-family: var(--main-font);
        color: var(--colors-core-text-primary);
      }
      
      @media (min-width: 768px) {
        .deeplink-auth__body {
          max-width: 460px;
          padding: 24px 30px;
        }
      }
      
      .deeplink-auth__image {
        margin-bottom: 20px;
        padding-bottom: 49%;
        border-radius: 12px;
        background-image: var(--deeplink-success-image-url);
        background-repeat: no-repeat;
        background-position: center;
        background-size: cover;
      }
      
      @media (min-width: 768px) {
        .deeplink-auth__image {
          padding-bottom: 37%;
        }
      }
      
      .deeplink-auth__title {
        margin-bottom: 10px;
        font-size: 20px;
        font-weight: 500;
        line-height: 24px;
        text-align: center;
        letter-spacing: 0.7px;
      }
      
      @media (min-width: 768px) {
        .deeplink-auth__title {
          margin-bottom: 20px;
          font-size: 24px;
          line-height: 30px;
        }
      }
      
      .deeplink-auth__description {
        margin-bottom: 20px;
        font-size: 13px;
        font-weight: 400;
        line-height: 18px;
        text-align: center;
        letter-spacing: 0.5px;
        color: rgba(255, 255, 255, 0.7);
      }
      
      .deeplink-auth__button {
        display: block;
        width: 100%;
        padding: 1.3rem;
        font-family: var(--main-font);
        font-size: 1.8rem;
        font-weight: 500;
        line-height: 2.5rem;
        letter-spacing: 0.5px;
        text-align: center;
        color: var(--button-text-color);
        background: var(--accent-color);
        border-radius: var(--border-radius-16);
        text-decoration: none;
        transition: transform .2s cubic-bezier(.509, .001, .25, 1);
      }
      
      @media (min-width: 768px) {
        .deeplink-auth__button {
          padding: 12px;
          font-size: 16px;
          line-height: 24px;
        }
      }
      
      .deeplink-auth__button:hover {
        transform: scale(.95);
      }
      
      Cet article vous a été utile ?
      Merci !
      Que pouvons-nous améliorer ? Message
      Nous sommes désolés de l'apprendre
      Dites-nous pourquoi vous n'avez pas trouvé cet article utile. Message
      Merci pour votre commentaire !
      Nous examinerons votre message et l'utiliserons pour améliorer votre expérience.
      Évaluer cette page
      Évaluer cette page
      Que pouvons-nous améliorer ?

      Préfère ne pas répondre

      Merci pour votre commentaire !
      Dernière mise à jour: 2 Mai 2024

      Faute de frappe ou autre erreur dans le texte ? Sélectionnez le texte concerné et appuyez sur Ctrl+Entée.

      Signaler un problème
      Nous améliorons continuellement notre contenu grâce à vos commentaires.
      Indiquez votre adresse e-mail pour un suivi
      Merci pour votre commentaire !