Aller au contenu

Tester son intégration

Il n’existe ni bac à sable ni mode test. Une réservation prise par le widget est une vraie réservation dans l’agenda d’un vrai restaurant, et le restaurant en est notifié comme de n’importe quelle autre.

Testez contre un restaurant que vous contrôlez, ou avec un restaurant prévenu. L’écran de confirmation porte un lien Modifier ou annuler : utilisez-le sur chaque réservation que vous créez, avant que le restaurant n’ait à le faire.

La plupart des erreurs d’intégration se manifestent par un widget qui fonctionne parfaitement et qui, discrètement, en fait moins que prévu. Voici la liste à parcourir avant la mise en production.

Ce que vous voyezCe que cela signifie
Aucun bouton de retour à la finL’origine de return_url n’est pas dans votre liste d’autorisation.
Champs non préremplis, aucun messageLe jeton a été rejeté.
« Session expirée »Le jeton a été vérifié, et son exp était dépassé.
Un champ que le client peut encore modifierIl figure dans reservation mais pas dans locked.
?t= ne fait rien sur votre propre pageC’est le comportement attendu — voir plus bas.

Enregistrer les origines de retour est une étape à part

Section intitulée « Enregistrer les origines de retour est une étape à part »

return_url est filtrée côté serveur contre une liste d’autorisation propre à l’intégration. Une intégration qui livre le code sans enregistrer ses origines n’affiche aucun bouton et ne journalise aucune erreur — le client arrive simplement au bout de la réservation sans nulle part où revenir.

Enregistrez les origines, puis testez l’écran de confirmation, pas le chemin de code.

Distinguer un jeton rejeté d’un jeton mal formé

Section intitulée « Distinguer un jeton rejeté d’un jeton mal formé »

Un jeton rejeté produit le parcours anonyme ordinaire et aucun message. C’est délibéré : une signature invalide, un émetteur inconnu, un émetteur appartenant à un autre compte, un segment malformé et une durée de vie supérieure à 15 minutes aboutissent tous au même résultat, afin qu’une vérification échouée ne révèle rien sur le contrôle qui a échoué.

Ce qui est précisément le problème lorsque la partie fautive, c’est vous. Servez-vous du seul résultat qui, lui, se distingue :

Générez un jeton correctement signé dont le exp est déjà passé. Si le widget annonce que la session a expiré, votre signature, votre émetteur et le restaurant sont corrects, et ce qui cloche dans votre vrai jeton se trouve dans ses revendications ou dans sa durée de vie. S’il n’affiche rien du tout, la défaillance est en amont : signature, émetteur, ou le compte auquel appartient le restaurant.

Passez ensuite le reste en revue :

  • exp - iat ne doit pas dépasser 15 minutes. Un jeton d’une durée de vie de 24 heures est rejeté d’emblée, et non raccourci — et il est rejeté comme invalide, ce qui ressemble exactement à une signature erronée.
  • Le MAC couvre le segment base64url tel que transmis, et non les octets JSON. Si vous re-sérialisez la charge utile avant de la hacher, l’ordre des clés ou l’échappement unicode finiront par diverger.
  • Le jeton entier doit rester sous 32 768 octets une fois encodé. Mesurez la chaîne finale, pas l’objet : le base64 ajoute un tiers, et ensure_ascii dans le json.dumps de Python ou le json_encode de PHP peut tripler le texte non ASCII avant cela.
  • Les champs verrouillés s’écrivent avec l’orthographe snake_case du filparty_size, section_key, shift_id.
  • ?t= dans la barre d’adresse de votre propre page est ignoré, à dessein. Seule la page de réservation autonome le lit ; sur votre site, utilisez setGuestToken(). Voir liens profonds.

create() lève une exception synchrone lorsque mode: "inline" et que son conteneur est introuvable. Un sélecteur mal orthographié se manifeste donc à l’appel plutôt que par un formulaire qui n’apparaît jamais : regardez la console avant de conclure que le widget est cassé.

Abonnez-vous aux deux événements de réservation et affichez-les une fois avant d’y brancher quoi que ce soit :

widget.on("reservation:created", (e) => console.log("created", e.reservation));
widget.on("reservation:confirmed", (e) => console.log("confirmed", e.reservation));

Dans un restaurant qui valide les réservations à la main, ou qui prend une empreinte bancaire, seul le premier est émis à la fin du parcours. C’est ce comportement qu’il faut tester, car c’est celui qui envoie un « votre table est confirmée » à un client qui n’a ni l’un ni l’autre.

step:changed rapporte les transitions et jamais l’étape sur laquelle le parcours s’ouvre. Si la première étape de votre tunnel est vide, c’est la cause : prenez l’entrée sur opened ou ready, voir événements.

Le widget s’exécute sous la politique de votre page, pas sous la nôtre. Une page à CSP stricte a besoin de :

DirectiveValeurPour
script-srchttps://app.useservice.appLe bundle du widget.
connect-srchttps://app.useservice.appLes appels de disponibilité et de réservation.
img-srcL’origine qui sert le logo du restaurant, s’il y en a un.L’en-tête.
script-srchttps://js.stripe.comEmpreintes bancaires uniquement.
frame-srchttps://js.stripe.com https://hooks.stripe.comEmpreintes bancaires et 3-D Secure.

Le widget rappelle l’origine qui a servi son bundle : c’est pourquoi connect-src désigne le même domaine que script-src.

Aucune entrée style-src n’est nécessaire : la feuille de style du widget est adoptée via le CSSOM plutôt qu’injectée, et la CSP ne gouverne pas cela. Une réserve toutefois — une politique qui définit style-src sans style-src-attr 'unsafe-inline' bloque l’attribut en ligne qui porte la couleur de marque du restaurant, et le widget s’affiche alors dans sa couleur par défaut. Voir apparence.

Testez avec la politique appliquée, et non en report-only : une sous-ressource bloquée, c’est un widget qui n’apparaît pas, et la seule trace est dans la console.

  • Les réservations créées pendant les tests sont annulées.
  • Les origines de retour sont enregistrées, et le bouton vérifié sur l’écran de confirmation.
  • Les jetons sont générés sur votre serveur, à chaque ouverture du widget, jamais dans le source de la page.
  • Le client peut toujours mener la réservation à son terme avec un jeton rejeté — c’est le chemin que prend chaque session expirée.
  • Les champs verrouillés correspondent à ce que votre bon contraint réellement. Un parcours dont la date, le service et le nombre de couverts sont tous épinglés, un jour où rien n’est disponible, est une impasse.
  • Vos messages de confirmation sont branchés sur reservation:confirmed, ou sur un webhook si l’enregistrement compte.