Acciones post-instalación y onboarding
Las acciones post-instalación determinan qué ocurre justo después de que alguien instala tu Stripe App. Una buena experiencia posterior a la instalación guía al usuario durante la configuración y aumenta las tasas de activación.
Tipos de acción post-instalación
Stripe admite cuatro tipos de acción post-instalación, y todos se configuran en el manifiesto de tu app:
1. Enlace a la app (predeterminado)
Abre la app en el viewport drawer predeterminado. Es el comportamiento por defecto si no indicas ninguna post_install_action:
{ "post_install_action": { "type": "default" }}El usuario ve el viewport drawer.default de la app en la barra lateral del Stripe Dashboard.
2. Enlace al onboarding
Abre la vista de onboarding específica de la app y ofrece así una experiencia de configuración enfocada:
{ "post_install_action": { "type": "onboarding" }}Esto exige declarar un viewport onboarding en tu manifiesto:
{ "ui_extension": { "views": [ { "viewport": "stripe.dashboard.onboarding", "component": "OnboardingView" } ] }, "post_install_action": { "type": "onboarding" }}3. Enlace a los ajustes
Abre la vista de ajustes de la app. Resulta útil cuando la app necesita claves de API o alguna configuración antes de poder usarse:
{ "post_install_action": { "type": "settings" }}Esto exige un viewport settings:
{ "ui_extension": { "views": [ { "viewport": "stripe.dashboard.settings", "component": "SettingsView" } ] }, "post_install_action": { "type": "settings" }}4. Enlace a una URL externa
Redirige al usuario a una URL externa para la configuración. Úsalo cuando tu flujo de onboarding viva fuera del Stripe Dashboard:
{ "post_install_action": { "type": "external", "url": "https://app.tajo.io/stripe/setup" }}Caution
Las URL externas deben usar HTTPS y deberían figurar en tus allowed_redirect_uris. El equipo de revisión de Stripe comprobará que la URL externa ofrece una experiencia de configuración funcional.
Buenas prácticas de onboarding
Hazlo fácil
Reduce al mínimo los pasos necesarios para empezar:
- Rellena de antemano la información que ya tienes del contexto de la cuenta de Stripe
- Usa valores predeterminados sensatos en las opciones de configuración
- Permite omitir los pasos opcionales, con un camino claro para completarlos más tarde
- Muestra el progreso con indicadores de paso en los flujos de varios pasos
Hazlo personalizable
Deja que cada usuario adapte la integración a sus necesidades:
- Opciones de mapeo de datos, para que elijan qué campos de Stripe se sincronizan con Brevo
- Frecuencia de sincronización, ofrece opciones en tiempo real, cada hora o una vez al día
- Sincronización selectiva, para que elijan qué clientes o productos se sincronizan
- Preferencias de notificación, para configurar avisos de errores de sincronización o de eventos importantes
Hazlo relevante
Demuestra el valor desde el primer momento:
- Muestra una vista previa de los datos sincronizados antes de activar la integración
- Explica qué va a pasar cuando la persona termine la configuración
- Ofrece una sincronización de prueba para comprobar que la conexión funciona
- Muestra métricas de éxito cuando termine la primera sincronización
Componente OnboardingView
El componente OnboardingView se renderiza en una modal enfocada cuando el usuario instala la app:
import { Box, Button, Inline, Icon, Banner, TextField, Select, Divider,} from '@stripe/ui-extension-sdk/ui';import type { ExtensionContextValue } from '@stripe/ui-extension-sdk/context';import { useState } from 'react';
const OnboardingView = ({ environment, userContext }: ExtensionContextValue) => { const [step, setStep] = useState(1); const [brevoApiKey, setBrevoApiKey] = useState(''); const [syncMode, setSyncMode] = useState('realtime'); const [isConnecting, setIsConnecting] = useState(false); const [error, setError] = useState<string | null>(null);
const totalSteps = 3;
const handleConnect = async () => { setIsConnecting(true); setError(null);
try { // Store the API key securely await storeBrevoApiKey(brevoApiKey);
// Verify the connection const result = await verifyBrevoConnection(brevoApiKey);
if (result.success) { setStep(2); } else { setError('Unable to connect to Brevo. Please check your API key.'); } } catch (err) { setError('Connection failed. Please try again.'); } finally { setIsConnecting(false); } };
return ( <Box css={{ padding: 'large' }}> {/* Progress indicator */} <Inline css={{ marginBottom: 'large' }}> Step {step} of {totalSteps} </Inline>
{error && ( <Banner type="critical" title="Connection Error"> {error} </Banner> )}
{step === 1 && ( <Box> <Inline css={{ fontWeight: 'bold', fontSize: 'large' }}> Connect Your Brevo Account </Inline> <Inline css={{ marginTop: 'small', color: 'secondary' }}> Enter your Brevo API key to start syncing customer data. </Inline>
<TextField label="Brevo API Key" placeholder="xkeysib-..." value={brevoApiKey} onChange={(e) => setBrevoApiKey(e.target.value)} css={{ marginTop: 'medium' }} />
<Inline css={{ marginTop: 'xsmall', color: 'secondary', fontSize: 'small' }}> Find your API key in Brevo under Settings > SMTP & API > API Keys </Inline>
<Button type="primary" onPress={handleConnect} disabled={!brevoApiKey || isConnecting} css={{ marginTop: 'medium' }} > {isConnecting ? 'Connecting...' : 'Connect Brevo'} </Button> </Box> )}
{step === 2 && ( <Box> <Inline css={{ fontWeight: 'bold', fontSize: 'large' }}> Configure Sync Settings </Inline>
<Select label="Sync Mode" value={syncMode} onChange={(value) => setSyncMode(value)} css={{ marginTop: 'medium' }} > <option value="realtime">Real-time (recommended)</option> <option value="hourly">Every hour</option> <option value="daily">Once per day</option> </Select>
<Divider css={{ marginY: 'medium' }} />
<Button type="primary" onPress={() => setStep(3)}> Continue </Button> <Button type="secondary" onPress={() => setStep(1)}> Back </Button> </Box> )}
{step === 3 && ( <Box> <Banner type="default" title="Ready to Sync"> Your Brevo account is connected. Tajo will begin syncing customer data automatically. </Banner>
<Box css={{ marginTop: 'medium' }}> <Inline css={{ fontWeight: 'bold' }}>What happens next:</Inline> <ul> <li>Existing Stripe customers will sync to Brevo contacts</li> <li>New customers and events will sync in real-time</li> <li>View sync status on any customer's detail page</li> </ul> </Box>
<Button type="primary" onPress={() => {/* Navigate to dashboard */}}> Go to Dashboard </Button> </Box> )} </Box> );};
export default OnboardingView;Flujo de inicio de sesión con SignInView
Si tu app exige que el usuario inicie sesión en una cuenta externa (como la de Tajo), usa una vista de inicio de sesión específica:
import { Box, Button, Inline, TextField, Banner, Link,} from '@stripe/ui-extension-sdk/ui';import { useState } from 'react';
const SignInView = ({ onSignInComplete }) => { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState<string | null>(null);
const handleSignIn = async () => { setIsLoading(true); setError(null);
try { const response = await fetch('https://api.tajo.io/v1/auth/stripe-app', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password }), });
if (!response.ok) { throw new Error('Invalid credentials'); }
const { token } = await response.json();
// Store the auth token securely in Stripe's Secret Store await storeAuthToken(token);
onSignInComplete(); } catch (err) { setError('Sign-in failed. Please check your credentials and try again.'); } finally { setIsLoading(false); } };
return ( <Box css={{ padding: 'large' }}> <Inline css={{ fontWeight: 'bold', fontSize: 'large' }}> Sign in to Tajo </Inline> <Inline css={{ marginTop: 'small', color: 'secondary' }}> Connect your Tajo account to enable Brevo sync. </Inline>
{error && ( <Banner type="critical" title="Sign-in Failed"> {error} </Banner> )}
<TextField label="Email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} css={{ marginTop: 'medium' }} />
<TextField label="Password" type="password" value={password} onChange={(e) => setPassword(e.target.value)} css={{ marginTop: 'small' }} />
<Button type="primary" onPress={handleSignIn} disabled={!email || !password || isLoading} css={{ marginTop: 'medium' }} > {isLoading ? 'Signing in...' : 'Sign In'} </Button>
<Link href="https://app.tajo.io/signup" external css={{ marginTop: 'small' }}> Don't have a Tajo account? Sign up </Link> </Box> );};Abrir un deep link con parámetros de consulta
Puedes abrir pasos concretos del onboarding o rellenar datos de antemano usando parámetros de consulta en los deep links:
import type { ExtensionContextValue } from '@stripe/ui-extension-sdk/context';
const OnboardingView = ({ environment }: ExtensionContextValue) => { // Access query parameters from the deep link const { queryParams } = environment;
// Pre-fill step from query parameter const initialStep = queryParams?.step ? parseInt(queryParams.step) : 1;
// Pre-fill API key from query parameter (e.g., from Tajo dashboard) const prefilledApiKey = queryParams?.brevo_key || '';
// Source tracking for analytics const installSource = queryParams?.source || 'marketplace';
const [step, setStep] = useState(initialStep); const [brevoApiKey, setBrevoApiKey] = useState(prefilledApiKey);
// ... rest of onboarding logic};Genera deep links que rellenen de antemano los datos del onboarding:
// From your Tajo dashboard, generate a link that pre-fills the Brevo API keyconst onboardingLink = [ 'https://dashboard.stripe.com/live/acct_xxxxx/dashboard', '?apps[com.tajo.brevo-integration][modal]=stripe.dashboard.onboarding', '&apps[com.tajo.brevo-integration][queryParams][step]=1', '&apps[com.tajo.brevo-integration][queryParams][source]=tajo_dashboard',].join('');Gestionar a los usuarios que vuelven
Cuando alguien abre tu app después de haber completado el onboarding, detecta su estado y muéstrale la vista adecuada:
const MainView = ({ environment, userContext }: ExtensionContextValue) => { const [authState, setAuthState] = useState<'loading' | 'signed-out' | 'onboarding' | 'ready'>('loading');
useEffect(() => { checkUserState().then((state) => { setAuthState(state); }); }, []);
switch (authState) { case 'loading': return <Spinner label="Loading..." />; case 'signed-out': return <SignInView onSignInComplete={() => setAuthState('onboarding')} />; case 'onboarding': return <OnboardingView onComplete={() => setAuthState('ready')} />; case 'ready': return <DashboardView />; }};Tip
Guarda el estado de finalización del onboarding en el Secret Store de Stripe, así podrás detectar a los usuarios que vuelven sin hacer ninguna llamada a una API externa.