Post-Install-Aktionen & Onboarding
Post-Install-Aktionen bestimmen, was unmittelbar nach der Installation deiner Stripe App passiert. Ein gut gestalteter Ablauf nach der Installation führt Nutzer:innen durch die Einrichtung und erhöht die Aktivierungsrate.
Typen von Post-Install-Aktionen
Stripe unterstützt vier Typen von Post-Install-Aktionen, die du jeweils im Manifest deiner App konfigurierst:
1. Link zur App (Standard)
Öffnet die App im Standard-Drawer-Viewport. Das ist das Standardverhalten, wenn du kein post_install_action angibst:
{ "post_install_action": { "type": "default" }}Nutzer:innen sehen dann den Viewport drawer.default deiner App in der Seitenleiste des Stripe Dashboards.
2. Link zum Onboarding
Öffnet die eigene Onboarding-Ansicht der App und schafft so eine fokussierte Einrichtung:
{ "post_install_action": { "type": "onboarding" }}Dafür muss im Manifest ein Viewport onboarding deklariert sein:
{ "ui_extension": { "views": [ { "viewport": "stripe.dashboard.onboarding", "component": "OnboardingView" } ] }, "post_install_action": { "type": "onboarding" }}3. Link zu den Einstellungen
Öffnet die Einstellungsansicht der App. Das ist sinnvoll, wenn die App vor der Nutzung API-Schlüssel oder eine Konfiguration braucht:
{ "post_install_action": { "type": "settings" }}Dafür muss ein Viewport settings vorhanden sein:
{ "ui_extension": { "views": [ { "viewport": "stripe.dashboard.settings", "component": "SettingsView" } ] }, "post_install_action": { "type": "settings" }}4. Link zu einer externen URL
Leitet Nutzer:innen zur Einrichtung auf eine externe URL weiter. Nutze das, wenn dein Onboarding außerhalb des Stripe Dashboards liegt:
{ "post_install_action": { "type": "external", "url": "https://app.tajo.io/stripe/setup" }}Caution
Externe URLs müssen HTTPS verwenden und sollten in deinen allowed_redirect_uris stehen. Das Review-Team von Stripe prüft, ob die externe URL eine funktionierende Einrichtung bietet.
Bewährte Vorgehensweisen für das Onboarding
Mach es mühelos
Halte die Zahl der Schritte bis zum Start so klein wie möglich:
- Fülle Angaben vor, die aus dem Kontext des Stripe-Kontos verfügbar sind
- Setze sinnvolle Standardwerte für Konfigurationsoptionen
- Erlaube das Überspringen optionaler Schritte und zeige klar, wie man sie später nachholt
- Zeige den Fortschritt bei mehrstufigen Abläufen mit Schrittanzeigen
Mach es anpassbar
Lass Nutzer:innen die Integration auf ihren Bedarf zuschneiden:
- Optionen für die Datenzuordnung, damit sie wählen können, welche Stripe-Felder nach Brevo synchronisiert werden
- Sync-Frequenz, biete Synchronisierung in Echtzeit, stündlich oder täglich an
- Selektive Synchronisierung, damit sie wählen können, welche Kundschaft oder Produkte synchronisiert werden
- Benachrichtigungseinstellungen, damit sie Warnungen zu Sync-Fehlern oder wichtigen Ereignissen konfigurieren können
Mach es relevant
Zeige den Nutzen sofort:
- Zeige eine Vorschau der synchronisierten Daten, bevor die Integration aktiviert wird
- Zeige, was passiert, sobald die Einrichtung abgeschlossen ist
- Biete einen Test-Sync an, mit dem sich die Verbindung überprüfen lässt
- Zeige Erfolgskennzahlen, sobald die erste Synchronisierung durch ist
Komponente OnboardingView
Die Komponente OnboardingView wird in einem fokussierten Modal gerendert, sobald die App installiert wird:
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;Anmeldeablauf mit SignInView
Wenn sich Nutzer:innen für deine App bei einem externen Konto anmelden müssen, etwa bei Tajo, nutze eine eigene Anmeldeansicht:
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> );};Deep Link mit Query-Parametern starten
Mit Query-Parametern in Deep Links startest du bestimmte Onboarding-Schritte oder füllst Daten vor:
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};So erzeugst du Deep Links, die Onboarding-Daten vorbefüllen:
// 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('');Wiederkehrende Nutzer:innen behandeln
Wenn jemand deine App nach abgeschlossenem Onboarding wieder öffnet, erkenne den Zustand und zeige die passende Ansicht:
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
Speichere den Abschlussstatus des Onboardings im Stripe Secret Store. So erkennst du wiederkehrende Nutzer:innen ohne einen externen API-Aufruf.