Revenir aux actualités des développeurs

Prendre rendez-vous avec WhatsApp Flows : création d’un back-end Node.js

27 février 2024DeGafi G et Iryna Wagner

Avec WhatsApp Flows, vous pouvez créer des messages interactifs permettant aux utilisateur·ices d’effectuer des actions directement sur WhatsApp. Les flux vous permettent de créer des écrans d’interaction avec les utilisateur·ices. Par exemple, vous pouvez créer des formulaires simples pour collecter des prospects ou récupérer des avis. Vous pouvez également concevoir des flux complexes sur plusieurs écrans pour la prise de rendez-vous.

Ce guide explique comment créer une application Node.js qui permettra aux utilisateur·ices de prendre des rendez-vous via WhatsApp Flows. Après avoir créé un flux sur la plateforme WhatsApp Business, vous configurerez un webhook pour recevoir la réponse du flux et réserver le rendez-vous.

Conditions requises

Pour suivre ce tutoriel, vous devez avoir :

Créer un flux WhatsApp

Il existe deux façons de créer un flux WhatsApp : l’éditeur de flux, accessible via le Gestionnaire WhatsApp et l’API Flows. Ce tutoriel explique comment créer un flux avec l’éditeur de flux.

Créer un flux

Sur le menu de gauche du tableau de bord de votre Gestionnaire WhatsApp, sélectionnez Outils du compte. Cliquez ensuite sur Flux.

Image du Gestionnaire WhatsApp

Cliquez sur Créer un flux en haut à droite.

Image de création de flux

Dans la boîte de dialogue qui s’affiche, renseignez les détails du flux de rendez-vous :

  • Nom : saisissez PrendreRendez-vous ou choisissez un autre nom qui vous convient.
  • Catégories : sélectionnez Prise de rendez-vous.
  • Modèle : choisissez Prendre un rendez-vous. Vous utiliserez ce modèle, car il comporte les éléments nécessaires pour prendre un rendez-vous. Ces éléments incluent des écrans pour les détails du rendez-vous, la saisie des détails de l’utilisateur·ice, le résumé du rendez-vous et l’affichage des conditions générales de l’entreprise. Vous pouvez personnaliser ce modèle pour l’adapter à votre cas d’utilisation.
Image de la prise de rendez-vous

Cliquez sur Envoyer pour créer le flux.

Vous pouvez consulter un aperçu du flux à droite de l’UI de l’éditeur. L’écran de rendez-vous permet à l’utilisateur·ice de choisir les détails du rendez-vous, comme le lieu et la date. L’écran des détails contient les informations saisies par l’utilisateur·ice. L’écran de résumé affiche le résumé de la prise de rendez-vous. Le dernier écran affiche les conditions générales de l’entreprise.

Le flux reste à l’état de brouillon pendant que vous le modifiez. Vous pouvez le partager avec votre équipe à des fins de test uniquement. Pour le partager avec une audience plus large, vous devez le publier. Cependant, vous ne pourrez plus modifier le flux une fois que vous l’aurez publié. Vous devrez encore ajouter l’URL du point de terminaison pour ce flux de rendez-vous. Conservez-le donc en tant que brouillon pour l’instant et passez à l’étape suivante, où vous configurerez le point de terminaison.

Configurer le point de terminaison du flux

WhatsApp Flows vous permet de vous connecter à un point de terminaison externe. Ce point de terminaison permet de fournir des données dynamiques à votre flux et de contrôler le routage. Il reçoit également les réponses envoyées par les utilisateur·ices depuis le flux.

À des fins de test, cet article utilise Glitch pour héberger le point de terminaison. L’utilisation de Glitch est entièrement facultative et n’est pas nécessaire pour utiliser Flows. Vous pouvez cloner le code du point de terminaison à partir de GitHub et l’exécuter dans l’environnement de votre choix.

Accédez au code du point de terminaison dans Glitch et remixez-le pour avoir un domaine unique. Pour le remixer, cliquez sur Remixer en haut de la page. Un domaine unique apparaîtra en tant qu’espace réservé dans l’élément d’entrée à droite de la page Glitch.

Avant de poursuivre, examinons le code. Le répertoire src contient quatre fichiers JavaScript : encryption.js, flow.js, keyGenerator.js et server.js. Le fichier d’entrée est server.js, donc nous allons l’examiner en premier.

server.js

Le fichier server.js configure d’abord l’application Express pour qu’elle utilise le middleware express.json afin d’analyser les requêtes JSON entrantes. Il charge ensuite les variables d’environnement nécessaires pour le point de terminaison.

const { APP_SECRET, PRIVATE_KEY, PASSPHRASE, PORT = "3000" } = process.env;

APP_SECRET est utilisé pour la vérification de signature. Il vous permet de vérifier si un message provient de WhatsApp et si, à ce titre, il peut être traité en toute sécurité. Vous l’ajouterez au fichier .env.

Pour accéder à votre APP_SECRET, accédez à votre tableau de bord dans l’application sur Meta for Developers. Dans le panneau de navigation de gauche, sous Paramètres de l’application, choisissez Général. Cliquez sur Afficher sous Clé secrète et copiez la clé secrète. Ensuite, retournez dans Glitch, ouvrez le fichier .env et créez une variable nommée APP_SECRET avec la valeur de la clé secrète que vous avez copiée.

PRIVATE_KEY permet de déchiffrer les messages reçus. PASSPHRASE sert à vérifier la clé privée. En plus de la clé privée, vous avez également besoin de la clé publique correspondante, que vous importerez plus tard. N’utilisez jamais ici les clés privées de vos comptes en production. Créez une clé privée temporaire à tester sur Glitch, puis remplacez-la par votre clé de production dans votre propre infrastructure.

Générez les clés publiques et privées en exécutant la commande ci-dessous dans le terminal de Glitch. Remplacez <your-passphrase> par votre phrase de passe. Accédez au terminal de Glitch en cliquant sur l’onglet TERMINALen bas de la page.

node src/keyGenerator.js <your-passphrase>

Copiez la phrase de passe et la clé privée puis collez-les dans le fichier .env. Cliquez sur le fichier intitulé .env dans la barre latérale de gauche, puis cliquez sur ✏️Plain text (Texte brut) en haut. Ne le modifiez pas directement depuis l’UI, car cela endommagerait le formatage de votre clé.

Après avoir défini les variables d’environnement, copiez la clé publique que vous avez générée et importez la clé publique via l’API Graph.

Le fichier server.js contient également un point de terminaison POST qui exécute différentes étapes :

  • Vérification de la présence de la clé privée :
       if (!PRIVATE_KEY) { throw new Error('Private key is empty. Please check your env variable "PRIVATE_KEY".'); }
  • Validation de la signature de la requête à l’aide de la fonction isRequestSignatureValid qui se trouve au bas du fichier :
if(!isRequestSignatureValid(req)) { // Return status code 432 if request signature does not match. // To learn more about return error codes visit: https://developers.facebook.com/docs/whatsapp/flows/reference/error-codes#endpoint_error_codes return res.status(432).send(); }
  • Déchiffrement des messages entrants à l’aide de la fonction decryptRequestqui se trouve dans le fichier encryption.js :
      let decryptedRequest = null; try { decryptedRequest = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); } catch (err) { console.error(err); if (err instanceof FlowEndpointException) { return res.status(err.statusCode).send(); } return res.status(500).send(); } const { aesKeyBuffer, initialVectorBuffer, decryptedBody } = decryptedRequest; console.log("💬 Decrypted Request:", decryptedBody);
  • Sélection de l’écran de flux à afficher à l’utilisateur·ice. La fonction getNextScreenest présentée en détail plus loin.

const screenResponse = await getNextScreen(decryptedBody);

       console.log("👉 Response to Encrypt:", screenResponse);
  • Chiffrement de la réponse à envoyer à l’utilisateur·ice :
res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer));

encryption.js

Ce fichier contient la logique de chiffrement et de déchiffrement des messages échangés à des fins de sécurité. Ce tutoriel n’abordera pas le fonctionnement de ce fichier.

keyGenerator.js

Ce fichier permet de générer les clés privées et publiques, comme vous l’avez vu précédemment. Comme pour le fichier encryption.js, ce tutoriel n’abordera pas le fichier keyGenerator.js en détail.

flow.js

Ce fichier contient la logique de gestion du flux. Il commence par un objet nommé SCREEN_RESPONSES. Cet objet contient les ID d’écran et les détails correspondants, tels que les données prédéfinies utilisées dans les échanges de données. Cet objet est généré à partir de l’éditeur de flux sous « ... » > Point de terminaison > Extraits > Réponses. Dans le même objet, vous disposez également d’un autre ID, SUCCESS, qui est renvoyé à l’appareil client lorsque le flux est terminé avec succès. Cela ferme le flux.

La fonction getNextScreen contient la logique qui guide le point de terminaison pour déterminer les données du flux à afficher à l’utilisateur·ice. Elle commence par extraire les données nécessaires du message déchiffré.

const { screen, data, version, action, flow_token } = decryptedBody;

Les points de terminaison de WhatsApp Flows reçoivent généralement trois requêtes :

Vous pouvez consulter les détails dans la documentation relative au point de terminaison .

La fonction traite les notifications d’évaluation d’état et d’erreur à l’aide de conditions if et répond en conséquence, comme le montre l’extrait ci-dessous :

// handle health check request if (action === "ping") { return { version, data: { status: "active", }, }; } // handle error notification if (data?.error) { console.warn("Received client error:", data); return { version, data: { acknowledged: true, }, }; }
        

Lorsqu’un·e utilisateur·ice clique sur le bouton call to action (CTA) du flux, une action INIT est déclenchée. Cette action renvoie l’écran de rendez-vous avec les données. Elle désactive également les listes déroulantes relatives au lieu, à la date et à l’heure afin de s’assurer que l’utilisateur·ice remplisse tous les champs.

Par exemple, la liste déroulante de date n’est activée que lorsque la liste déroulante de lieu est remplie. L’activation et la désactivation des champs sont traitées lorsqu’une demande data_exchange est reçue.

// handle initial request when opening the flow and display APPOINTMENT screen if (action === "INIT") { return { ...SCREEN_RESPONSES.APPOINTMENT, data: { ...SCREEN_RESPONSES.APPOINTMENT.data, // these fields are disabled initially. Each field is enabled when previous fields are selected is_location_enabled: false, is_date_enabled: false, is_time_enabled: false, }, }; }

Pour les actions data_exchange, une structure « switch case » est utilisée pour déterminer les données à renvoyer en fonction de l’ID d’écran. Si l’ID d’écran est APPOINTMENT, les champs de liste déroulante ne sont activées que lorsque les listes déroulantes précédentes sont sélectionnées.

// Each field is enabled only when previous fields are selected is_location_enabled: Boolean(data.department), is_date_enabled: Boolean(data.department) && Boolean(data.location), is_time_enabled: Boolean(data.department) && Boolean(data.location) && Boolean(data.date)

Pour l’écran des détails, les titres des propriétés de l’objet de données, comme le lieu et le département, sont extraits de l’objet SCREEN_RESPONSES.APPOINTMENT.data. Ce code suppose qu’il existe une correspondance valide. Par conséquent, sachez qu’il peut générer une erreur si aucun objet correspondant n’est trouvé.

Prenez maintenant une instance de l’objet de lieu. La sélection spécifique de l’objet de lieu est déterminée par la correspondance entre la propriété id des objets du tableau et la valeur de data.location.

const departmentName = SCREEN_RESPONSES.APPOINTMENT.data.department.find( (dept) => dept.id === data.department ).title; const locationName = SCREEN_RESPONSES.APPOINTMENT.data.location.find( (loc) => loc.id === data.location ).title; const dateName = SCREEN_RESPONSES.APPOINTMENT.data.date.find( (date) => date.id === data.date

).title;

Les valeurs sont ensuite concaténées et renvoyées dans la réponse pour afficher l’écran de résumé.

const appointment = `${departmentName} at ${locationName} ${dateName} at ${data.time}`; const details = `Name: ${data.name} Email: ${data.email} Phone: ${data.phone} "${data.more_details}"`; return { ...SCREEN_RESPONSES.SUMMARY, data: { appointment, details, // return the same fields sent from client back to submit in the next step ...data, }, };
        

Une fois l’écran de résumé soumis par le client, une réponse de succès est envoyée à l’appareil du client pour signaler que le flux est terminé. flow_token est un identifiant unique que vous pouvez définir lorsque vous envoyez le flux à l’utilisateur·ice.

// send success response to complete and close the flow return { ...SCREEN_RESPONSES.SUCCESS, data: { extension_message_response: { params: { flow_token, }, }, }, };

L’écran des conditions générales n’a aucune donnée à échanger, donc il n’est pas pris en charge par le point de terminaison.

Ajouter le point de terminaison au flux

En haut à droite de la page Glitch, vous pouvez copier l’URL en cliquant sur l’icône du menu vertical et en sélectionnant Copy Link (Copier le lien). Vous pouvez également obtenir le lien en cliquant sur Share (Partager) en haut à droite.

Accédez à l’éditeur de flux. Cliquez sur Configurer dans la bannière marron qui apparaît en haut de l’éditeur.

Une fenêtre pop-up s’affiche, vous permettant de configurer l’URI du point de terminaison, le numéro de téléphone de l’entreprise et l’application sur Meta for Developers. Après avoir effectué les configurations nécessaires, contrôlez l’état. Commencez par exécuter l’aperçu interactif et veillez à sélectionner Demander des données sous Demander des données sur le premier écran dans les paramètres de l’aperçu interactif. Cette commande envoie une requête au point de terminaison afin de récupérer les données pour le premier écran, ce qui permet de vérifier que le point de terminaison est disponible et que vous avez mis en œuvre un contrôle d’état.

Publiez ensuite le flux en cliquant sur le menu horizontal (...) et en choisissant Publier. Une demande de contrôle d’état sera envoyée au point de terminaison avec action === "ping" pour vérifier que le point de terminaison est configuré avant la publication.

Image du point de terminaison

Tester le flux

Une fois les configurations terminées, activez à nouveau l’aperçu interactif dans l’UI de l’éditeur WhatsApp pour tester le flux. Dans la fenêtre pop-up qui s’affiche, sélectionnez le numéro de téléphone et choisissez l’option Demander des données sous Demander des données sur le premier écran. Fermez le flux en cliquant sur l’icône X pour recommencer à tester le flux à partir du bouton CTA.

Ouvrez le journal de Glitch en cliquant sur l’onglet LOGS (JOURNAUX). Effacez-le en cliquant sur Clear (Effacer). Revenez ensuite à l’aperçu de l’UI de l’éditeur de WhatsApp. Cliquez sur Aperçu du flux. Voici ce qui s’affiche à l’écran :

Image de prévisualisation du flux

Revenez maintenant aux journaux de Glitch. Sous la requête déchiffrée, vous verrez une action INIT, le token de flux ainsi que d’autres détails. Une fois la liste déroulante de département sélectionnée, une réponse à chiffrer est renvoyée au flux de l’utilisateur·ice.

Image de la requête déchiffrée

Sélectionnez le département. Remarquez que le code is_location_enabled est défini sur true et que l’action est devenue data_exchange.

Image data_exchange

Continuez à tester le flux et observez les changements de données dans les journaux Glitch. Des journaux similaires seront générés lorsque les utilisateur·ices interagiront avec le flux à partir de leurs appareils mobiles.

Dans la section suivante, vous allez créer un webhook qui envoie un message de confirmation à l’utilisateur·ice lors d’une prise de rendez-vous.

Configurer le webhook

Lorsqu’un·e utilisateur·ice termine le flux, un message marquant la fin du flux est envoyé au webhook faisant l’objet de l’abonnement. À partir de ce webhook, vous informerez l’utilisateur·ice que son rendez-vous a bien été fixé grâce à un message dans la discussion. Tout comme pour le point de terminaison, vous utiliserez Glitch pour les tests. Vous pouvez accéder au code et le remixer ici.

L’utilisation de Glitch est entièrement facultative : elle n’est pas nécessaire pour utiliser les flux. Vous pouvez cloner le code du webhook à partir de GitHub et l’exécuter dans l’environnement de votre choix.

Définir les variables d’environnement

Pour définir les variables d’environnement, ouvrez le fichier .env sur Glitch. Définissez VERIFY_TOKEN sur la chaîne de votre choix, FLOW_ID sur l’ID de votre flux et GRAPH_API_TOKEN sur le token d’accès de votre compte WhatsApp Business. Vous pouvez obtenir le token d’accès à partir du tableau de bord de votre application sur Meta for Developers en cliquant sur Configuration de l’API dans la section WhatsApp sur le panneau de navigation de gauche.

Image de la configuration de l’API

Sur la page qui s’affiche, cliquez sur le bouton Copier sous la fiche Token d’accès temporaire. Collez la clé dans le fichier .env.

S’abonner au webhook sur le tableau de bord Meta

Dans votre compte sur Meta for Developers, cliquez sur le menu Configuration sous WhatsApp dans le volet de navigation de gauche.

Image de configuration

Dans la fiche Webhook, cliquez sur Modifier. Dans la boîte de dialogue qui s’ouvre, collez l’URL de Glitch que vous avez copiée et ajoutez-y /webhook dans le champ URL de rappel. Dans le champ Vérifier le token, ajoutez le token de la variable VERIFY_TOKEN de votre fichier .env. Lorsque vous avez terminé, cliquez sur Vérifier et enregistrer. La boîte de dialogue se ferme et vous revenez à l’écran principal. Cliquez sur Gérer et vérifiez le champ des messages. Votre webhook est prêt.

Présentation du code du webhook

Le code comporte deux acheminements : POST /webhook et GET /webhook. L’acheminement GET gère les demandes de vérification du webhook en comparant le token fourni à un token de vérification prédéfini et en répondant avec les codes de statut appropriés et un token de challenge.

const verify_token = process.env.VERIFY_TOKEN; // Parse params from the webhook verification request let mode = req.query["hub.mode"]; let token = req.query["hub.verify_token"]; let challenge = req.query["hub.challenge"]; if (mode && token) { if (mode === "subscribe" && token === verify_token) { console.log("WEBHOOK_VERIFIED"); res.status(200).send(challenge); } else { res.sendStatus(403); } }

L’acheminement POST /webhook traite les notifications entrantes du webhook. Les requêtes de webhook peuvent avoir des charges utiles différentes. Ainsi, le code ci-dessous lit le message et le numéro de téléphone de l’entreprise en accédant aux champs de la requête en toute sécurité dans le cas où ils ne sont pas définis.

const message = req.body.entry?.[0]?.changes[0]?.value?.messages?.[0]; const business_phone_number_id =

req.body.entry?.[0].changes?.[0].value?.metadata?.phone_number_id;

Il vérifie ensuite si la demande entrante concerne un message de type "text" contenant le mot « rendez-vous ». Si le message contient ce mot, le flux est envoyé à l’utilisateur·ice. Le message de flux est envoyé avec flow_action: "data_exchange," ce qui signifie que le flux enverra une demande INIT au point de terminaison à son lancement afin d’obtenir l’écran et les données initiales.

if ( message.type === "text" && // for demo purposes, send the flow message whenever a user sends a message containing "rendez-vous" message.text.body.toLowerCase().includes("rendez-vous") ) { // send flow message as per the docs here https://developers.facebook.com/docs/whatsapp/flows/gettingstarted/sendingaflow#interactive-message-parameters await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, type: "interactive", interactive: { type: "flow", header: { type: "text", text: "Bonjour 👋", }, body: { text: "Prêt·e à transformer votre espace ? Planifiez une consultation personnalisée avec notre équipe de spécialistes !", }, footer: { text: "Cliquez sur le bouton ci-dessous pour continuer", }, action: { name: "flow", parameters: { flow_id: FLOW_ID, flow_message_version: "3", // replace flow_token with a unique identifier for this flow message to track it in your endpoint & webhook flow_token: "<FLOW_TOKEN_PLACEHOLDER>", flow_cta: "Prendre rendez-vous", flow_action: "data_exchange", }, }, }, }, }); } ...

Si le type de message entrant n’est pas "text", le code vérifie si le type de message est "interactive." Un message "nfm_reply" de type interactif signifie que le message entrant est une réponse de flux. Il renvoie ensuite à l’utilisateur·ice le message « Votre rendez-vous a bien été enregistré ».

... if ( message.type === "interactive" && message.interactive?.type === "nfm_reply" ) { // send confirmation message await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, text: { body: "Votre rendez-vous a bien été enregistré" }, }, }); } ...

Il marque ensuite le message entrant comme lu de sorte que l’utilisateur·ice voie les coches bleues.

... // mark incoming message as read await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", status: "read", message_id: message.id, }, }); ...
        

Expérience utilisateur

Dans cet exemple, l’utilisateur·ice envoie un message à votre numéro contenant le mot « rendez-vous », puis reçoit le message du flux. Vous pouvez également choisir d’envoyer le flux après une autre interaction ou en tant que modèle de message.

L’utilisateur·ice recevra un message de flux avec un bouton CTA pour la prise de rendez-vous, où il ou elle pourra renseigner ses coordonnées. Un message de confirmation lui sera ensuite envoyé une fois le flux terminé.

Image d’envoi du flux à l’utilisateur·ice

Dans ce guide, vous avez appris à configurer un flux WhatsApp pour une prise de rendez-vous conviviale. À l’aide de l’UI de l’éditeur de flux, vous avez créé un formulaire pour collecter les détails du rendez-vous auprès des utilisateur·ices.

Les flux permettent d’éviter de rediriger les utilisateur·ices vers un site Web externe pour la prise de rendez-vous, ce qui améliore l’expérience clientèle. Le processus intuitif permet aux utilisateur·ices d’effectuer des réservations directement dans WhatsApp. Vous pouvez utiliser WhatsApp Flows non seulement pour planifier des rendez-vous, mais aussi pour recueillir les commentaires des clients ou les aider à s’inscrire à des promotions ou à des listes de diffusion. WhatsApp Flows vous offre également la possibilité de vous connecter à des API externes ou à d’autres applications dans votre point de terminaison.

Vous pouvez facilement créer des flux WhatsApp à l’aide de l’UI de l’éditeur de flux. Vous pouvez également utiliser l’API Flow pour créer des flux par programmation. Pour plus d’informations, consultez la documentation WhatsApp Flows.