Fonctionnement des extensions d'interface utilisateur
Les extensions d’interface utilisateur pour Stripe Apps vous permettent d’afficher votre propre interface utilisateur dans les produits Stripe en utilisant TypeScript et React. Ces outils devraient vous être familiers si vous avez déjà codé en React. Ils se distinguent des applications en ligne React standard grâce à leur exécution dans une sandbox sécurisée intégrée à une autre page web.
Aperçu
Les extensions d’interface utilisateur sont écrites en TypeScript et utilisent React pour créer l’interface utilisateur à l’aide du kit d’outils de l’interface utilisateur de Stripe. Contrairement aux autres environnements React, les extensions d’interface utilisateur ne prennent pas en charge le code HTML arbitraire, mais utilisent les composants d’interface utilisateur de Stripe. La structure d’une extension d’interface utilisateur comporte obligatoirement les répertoires et fichiers suivants :
stripe-app.json
: le manifeste de l’application. Il décrit la façon dont les applications interagissent avec Stripe ainsi que les autorisations dont elles ont besoin, et il indique si elles possèdent une extension d’interface utilisateur (et l’endroit où apparaît cette extension dans l’interface utilisateur de Stripe, le cas échéant).package.json
: les métadonnées du paquet NPM. Les extensions d’interface utilisateur sont des paquets NPM standard. Vous pouvez gérer leurs dépendances en utilisant npm ou yarn.src
: le véritable code source TypeScript pour l’extension d’interface utilisateur. Par défaut, la CLI place une vue générique danssrc/views
avec une entrée correspondante dansstripe-app.json
.
Le développement d’une extension d’interface utilisateur s’appuie sur le plugin d’application de la CLI Stripe. La CLI initialise les applications avec la structure appropriée, configure le manifeste de l’application, exécute un serveur de développement et inclut votre application dans un lot pour soumission à Stripe.
Développement d’une extension d’interface utilisateur
- En tant que développeur de l’application, vous créez des vues, à savoir des composants React enregistrés pour apparaître dès qu’une fenêtre d’affichage spécifique s’affiche à l’écran. Par exemple, pour faire apparaître une vue dès qu’un utilisateur lit une page relative aux détails d’une facture, enregistrez-la sous la fenêtre d’affichage
stripe.dashboard.invoice.detail
. - Au moment de charger votre application, utilisez les commandes de la CLI pour compresser votre code, le charger sur Stripe et héberger votre application sur le CDN de Stripe.
- Lors de l’initialisation de l’extension d’interface utilisateur de votre application, Stripe télécharge le code de l’application dans un iframe sous sandbox.
- Lorsqu’un utilisateur se rend sur une page ayant une fenêtre d’affichage particulière (par exemple,
/invoices/inv_1283
) :- Stripe définit la vue de l’extension d’interface utilisateur dans la sandbox avec le contexte fourni dans la fenêtre d’affichage.
- Stripe transmet la vue au Dashboard, qui à son tour l’affiche aux utilisateurs.
- Lorsque les utilisateurs interagissent avec l’extension d’interface utilisateur (en cliquant sur un bouton par exemple), les gestionnaires d’événements de la sandbox de l’extension reçoivent l’événement et peuvent mettre la vue à jour.
Vues et fenêtres d’affichage
Créez une vue React et enregistrez-la sous une fenêtre d’affichage pour afficher l’interface utilisateur aux utilisateurs d’une application.
Les vues sont des composants React exportés par l’application. Les fenêtres d’affichage sont des identifiants qui indiquent l’emplacement de l’affichage de la vue. Lorsque vous chargez l’application, toutes les vues exportées par l’application sont enregistrées sous la fenêtre d’affichage associée.
Lorsque vous exécutez stripe apps add view
, les vues s’enregistrent automatiquement sous des fenêtres d’affichage. Une entrée est ajoutée au manifeste de l’application en arrière-plan.
{ //... other manifest properties "ui_extension": { "views": [ { "viewport": "stripe.dashboard.invoice.detail", // See all valid values at stripe.com/docs/stripe-apps/reference/viewports "component": "NameOfComponent" // This is provided by you } // ... additional views ] } }
Cycle de vie d’une extension d’interface utilisateur
Les extensions d’interface utilisateur s’exécutent dans un iframe sous sandbox invisible qui envoie de façon asynchrone des mises à jour de l’interface utilisateur au Dashboard Stripe, qui les affiche à son tour. Une même sandbox peut prendre en charge simultanément plusieurs vues.
Voici le cycle de vie de la sandbox et des vues qu’elle permet d’afficher :
- Le Dashboard charge la sandbox de l’extension d’interface utilisateur. Ce chargement survient entre le chargement du Dashboard et l’ouverture de l’application par l’utilisateur.
- Lorsqu’une vue doit être affichée, le Dashboard attend l’initialisation de la sandbox, puis lui indique quelle vue monter et lui transmet le contexte approprié.
- Lorsque l’utilisateur quitte la vue (par exemple, lorsqu’il ferme le volet des applications), la vue est démontée. Elle est donc supprimée du DOM et de l’arborescence React sous sandbox.
- La sandbox peut être arrêtée ou non selon l’utilisation des ressources. Nous pouvons seulement garantir que le Dashboard tentera autant que possible d’exécuter useEffect et d’autres gestionnaires de nettoyage avant d’arrêter la sandbox.
Cycle de vie de l’extension d’interface utilisateur de Stripe Apps
Limitations de la sandbox
Comme le code des extensions d’interface utilisateur Stripe Apps est exécuté seulement dans un environnement de sandbox, celles-ci ne peuvent pas offrir autant de fonctionnalités qu’une application React standard exécutée dans un navigateur complet.
Principales différences entre les applications Stripe et les applications React standard
- Les applications Stripe ne disposent pas d’un accès direct au DOM. Elles s’exécutent dans un iframe avec un DOM distinct, invisible depuis le Dashboard.
- Le Dashboard transmet par proxy et sérialise toutes les données à l’application. Les composants du kit d’outils de l’interface utilisateur acceptent uniquement les données sérialisables.
- Le Dashboard fait de même pour toutes les propriétés, c’est pourquoi les fonctions transmises à ou déclenchées par des composants du kit d’outils de l’interface utilisateur sont asynchrones.
Limitations de React et JavaScript
Les restrictions ci-dessous affectent les fonctionnalités React et JavaScript disponibles lors du développement de votre application. Le rendu de l’arborescence React n’apparaît pas sur le DOM tant que l’environnement d’hébergement du Dashboard Stripe ne l’a pas désérialisé ni évalué. Le DOM pour l’application se met à jour, puis l’instance de React dans le Dashboard gère les entrées de données.
Les objets global document et window sont limités
L’environnement DOM exécuté par le code d’extension d’interface utilisateur est verrouillé par l’iframe sous sandbox. Cela signifie que les API de premier niveau comme localStorage, indexedDB et BroadcastChannel ne sont pas disponibles. Aucune API DOM qui s’appuie sur la same-origin policy ne fonctionne comme prévu, car les iframes sous sandbox ont une origine null
.
Les propriétés ref React ne sont pas prises en charge
Les composants d’interface utilisateur ne prennent pas en charge la propriété React ref
, car l’arborescence React est sérialisée et transférée au Dashboard Stripe pour affichage. Le DOM dans lequel les composants sont finalement affichés est inaccessible depuis le code de l’application sous sandbox.
Les applications ne peuvent pas contrôler la version de React
Le fichier package.json
généré par défaut avec chaque application Stripe possède une entrée dependency
pour react
, mais la modification de cette version n’a aucune incidence sur la version de React qui affiche votre application. Le Dashboard Stripe utilise sa version de React (actuellement la version 17) pour afficher toutes les applications. La dépendance react
du fichier package.json
local effectue uniquement une vérification du type et des tests unitaires. Pour des raisons de compatibilité, vous ne devez pas modifier cette fonction (sauf instruction contraire de Stripe).
Utiliser des composants non contrôlés pour les interactions
Le Dashboard sérialise et transmet toutes les entrées de données vers l’application, ce qui entraîne un décalage de saisie lors de l’utilisation des composants contrôlés React. Ce décalage est perceptible par l’utilisateur et peut potentiellement écraser les caractères qu’il a saisis entre-temps. Il en résulte également que le curseur se déplace à la fin d’une saisie de texte si l’utilisateur essaie de modifier du texte au début.
Pour réduire le décalage dans votre application, utilisez les entrées de l’utilisateur de manière non contrôlée :
import {useState} from 'react'; import {TextArea} from '@stripe/ui-extension-sdk/ui'; const App = () => { const defaultValue = 'Initial TextArea value'; const [text, setText] = useState(defaultValue); return ( <> <TextArea label="Message" // This doesn't work ❌ // Attempting to edit text at the beginning skips the cursor to the end value={text} onChange={e => setText(e.target.value)} /> <TextArea label="Message" // This will work ✅ defaultValue={defaultValue} onChange={e => setText(e.target.value)} /> </> ); };
Restrictions concernant les composants de l’interface utilisateur
Les restrictions ci-dessous s’appliquent aux composants de l’interface utilisateur. Même si votre extension s’exécute dans un environnement isolé, les composants de l’interface utilisateur s’affichent directement dans le Dashboard. Le SDK donne l’ordre au Dashboard d’afficher ces composants du kit d’outils, ce qui entraîne les limitations suivantes.
Les composants ne peuvent pas arrêter la propagation des événements
Puisque les gestionnaires d’événements sont appelés de manière asynchrone, l’événement s’est déjà propagé au moment où le gestionnaire d’événements de l’application est appelé. Par conséquent, l’application ne peut pas arrêter la propagation ou la remontée de l’événement.
Les composants acceptent uniquement les types de données sérialisables en tant que propriétés
Les composants de l’interface utilisateur n’acceptent que les types de données sérialisables. Transmettre des types de données non sérialisables comme Map
ou Set
en tant que propriétés pour un composant du kit d’outils de l’interface utilisateur génère une erreur.
Utilisez seulement des types, des fonctions ou des événements React simples en tant que propriétés. Voici les types pris en charges :
- Les chaînes, les numéros,
true
,false
,null
etundefined
- Objets dont les clés et les valeurs sont toutes de type simple
- Les tableaux dont les valeurs sont toutes de type simple
- Les fonctions, mais elles deviennent asynchrones lors de leur transmission par proxy. Toutes les fonctions renvoyées ou transmises en tant qu’arguments sont aussi concernées par les limitations de type.
- Événements React
Les composants ne prennent pas en charge les fonctions de rendu
React affiche un rendu de manière synchronisée, mais les fonctions transmises aux composants de l’interface utilisateur deviennent asynchrones une fois que le Dashboard les transmet par proxy à l’application. Les fonctions qui génèrent un balisage transmis à un composant de l’interface utilisateur ne permettent pas de finaliser l’affichage à temps pour que React utilise ces résultats. Par conséquent, aucun composant de l’interface utilisateur ne prend en charge les fonctions de rendu.
Cela signifie que les modèles suivants ne fonctionnent pas :
// This doesn't work ❌ <ItemProvider> {(data) => ( <Item data={data} /> )} </ItemProvider>
// This doesn't work ❌ <Item renderFooter={() => <div>footer</div>} />
JSX ne peut être transmis à des propriétés non-enfant qu’en tant que nœud unique
Les composants de l’interface utilisateur prennent en charge les propriétés qui acceptent un seul élément React :
// This will work ✅ <Item footer={<div>footer</div>} />
Cependant, les structures de données JSX plus complexes ne sont pas prises en charge :
// This doesn't work ❌ <Item footer={[<div>one</div>, <div>two</div>]} />
// This doesn't work ❌ <Item footer={{ one: <div>one</div>, two: <div>two</div> }} />
Si vous voulez transmettre plusieurs éléments React à un composant de l’interface utilisateur, vous devez les wrapper dans un fragment :
// This works ✅ <Item footer={ <> <div>one</div> <div>two</div> </> }/>
La même contrainte s’applique à children
. Les tableaux et objets qui contiennent du JSX ne sont pas pris en charge, mais il est possible d’utiliser plusieurs éléments React :
// This works ✅ <Item> <div>one</div> <div>two</div> </Item>
Installation de paquets NPM
L’ajout de packages NPM tiers aux applications Stripe ne fait l’objet d’aucune restriction. N’hésitez pas à installer les paquets comme bon vous semble. Cependant, tous les paquets ne fonctionnent pas comme prévu étant donné les limitations du bac à sable des extensions d’interface utilisateur.
L’utilisation d’une bibliothèque d’utilitaires comme lodash
convient, car lodash
ne nécessite pas d’accès au DOM :
import { Box, Button } from "@stripe/ui-extension-sdk/ui"; import { useState } from "react"; import kebabCase from "lodash/kebabCase"; const text = "A note to the user"; const App = () => { const [isKebabCase, setIsKebabCase] = useState(false); return ( <> {/* This will work ✅ */} <Box>{isKebabCase ? kebabCase(text) : text}</Box> <Button onPress={() => { setIsKebabCase(!isKebabCase); }} > Toggle kebab-case </Button> </> ); };
L’utilisation d’une bibliothèque de formulaires telle que react-hook-form
ne convient pas, car react-hook-form
utilise des propriétés Ref pour gérer l’état des formulaires :
import { TextField } from "@stripe/ui-extension-sdk/ui"; import { useForm } from "react-hook-form"; const App = () => { const { register } = useForm(); const { onChange, name, ref } = register("firstName"); return ( <TextField label="First name" placeholder="Enter your name" name={name} onChange={onChange} // This doesn't work ❌ ref={ref} /> ); };