Fonctionnement des extensions d'interface utilisateur
Découvrir le système des extensions d'interface utilisateur et comment ajouter des fonctionnalités au Dashboard Stripe.
Les extensions d’interface utilisateur de Stripe Apps vous permettent d’afficher votre propre interface utilisateur dans les produits Stripe à l’aide de TypeScript et React. Ces outils devraient vous être familiers si vous avez déjà codé en React. Ils diffèrent à plusieurs égards des applications en ligne React standards en raison de leur exécution dans un iframe sécurisé en environnement de test intégré à 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.
: 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).json package.
: 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.json 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 à l’intérieur de l’iframe en fonction du contexte fourni par la fenêtre d’affichage.
- Stripe transmet la vue au Dashboard, qui à son tour l’affiche aux utilisateurs.
- Lors des interactions des utilisateurs avec l’extension d’interface utilisateur (par exemple, lorsqu’ils cliquent sur un bouton), les gestionnaires d’événements dans l’iframe de l’extension d’interface utilisateur 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 invisible en environnement de test qui envoie de manière asynchrone les mises à jour de l’interface utilisateur au Dashboard Stripe. Un seul iframe en environnement de test peut prendre en charge plusieurs vues simultanément.
Voici le cycle de vie de l’iframe en environnement de test et des vues qu’il permet d’afficher :
- Le Dashboard charge l’iframe de l’extension d’interface utilisateur, une opération qui se déroule 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 l’iframe en environnement de test, puis lui indique quelle vue monter et la transmet dans 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.
- L’iframe en environnement de test peut rester en cours d’exécution ou s’arrêter en fonction de l’utilisation des ressources. Nous vous garantissons que le Dashboard est optimisé afin de permettre l’exécution de useEffect et d’autres gestionnaires de nettoyage avant la fermeture de l’iframe.

Cycle de vie de l’extension d’interface utilisateur de Stripe Apps
Limites de l’iframe en environnement de test
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 dans lequel le code d’extension d’interface utilisateur s’exécute est verrouillé par l’iframe en environnement de test. Cela signifie que les API de premier niveau telles que localStorage, indexedDB et BroadcastChannel ne sont pas disponibles. Aucune API DOM qui s’appuie sur la politique d’origine unique ne fonctionne comme prévu, car les iframes en environnement de test ont une origine null
.
Les propriétés ref React ne sont pas prises en charge
Les composants de l’interface utilisateur ne prennent pas en charge les propriétés ref
de React, car l’arborescence React est sérialisée et transmise au Dashboard Stripe pour affichage. Le DOM dans lequel les composants sont finalement affichés est inaccessible à partir du code de l’application s’exécutant dans l’iframe en environnement de test.
Les applications ne peuvent pas contrôler la version de React
Le fichier package.
par défaut généré avec chaque application Stripe n’a pas d’entrée dependency
pour react
. L’ajout d’une version spécifique de React dans le fichier package.
de l’application Stripe ne contrôle pas la version de React qui affiche votre application ; la dépendance effectue uniquement une vérification du type et des tests unitaires. Le Dashboard Stripe utilise sa version de React (actuellement la version 17.0.2) pour afficher toutes les applications. Pour assurer la compatibilité, ne la changez que si Stripe vous le demande.
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 packages comme bon vous semble. Cependant, les packages ne fonctionnent pas tous comme prévu étant donné les limites de l’iframe en environnement de test 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} /> ); };