Aksi Pasca-Instalasi & Onboarding
Aksi pasca-instalasi (post-install action) menentukan apa yang terjadi segera setelah pengguna memasang Stripe App Anda. Pengalaman pasca-instalasi yang dirancang dengan baik memandu pengguna melewati proses penyiapan dan meningkatkan tingkat aktivasi.
Caution
Status: direncanakan, belum dibangun. Tajo tidak memiliki Stripe App, jadi saat ini tidak ada apa pun di bagian ini yang dapat dipasang. Identifier aplikasi, URL, serta kredensial pada halaman ini adalah placeholder ilustratif, bukan endpoint aktif.
Jenis aksi pasca-instalasi
Stripe mendukung empat jenis aksi pasca-instalasi, masing-masing dikonfigurasi di manifest aplikasi Anda:
1. Tautan ke aplikasi (bawaan)
Membuka aplikasi pada viewport drawer bawaan. Ini adalah perilaku bawaan jika post_install_action tidak ditentukan:
{ "post_install_action": { "type": "default" }}Pengguna melihat viewport drawer.default milik aplikasi di sidebar Stripe Dashboard.
2. Tautan ke onboarding
Membuka tampilan onboarding khusus milik aplikasi, sehingga memberikan pengalaman penyiapan yang terfokus:
{ "post_install_action": { "type": "onboarding" }}Ini membutuhkan viewport onboarding yang dideklarasikan di manifest Anda:
{ "ui_extension": { "views": [ { "viewport": "stripe.dashboard.onboarding", "component": "OnboardingView" } ] }, "post_install_action": { "type": "onboarding" }}3. Tautan ke pengaturan
Membuka tampilan pengaturan aplikasi, berguna ketika aplikasi membutuhkan API key atau konfigurasi sebelum digunakan:
{ "post_install_action": { "type": "settings" }}Ini membutuhkan viewport settings:
{ "ui_extension": { "views": [ { "viewport": "stripe.dashboard.settings", "component": "SettingsView" } ] }, "post_install_action": { "type": "settings" }}4. Tautan ke URL eksternal
Mengalihkan pengguna ke URL eksternal untuk penyiapan. Gunakan ini ketika alur onboarding Anda berada di luar Stripe Dashboard:
{ "post_install_action": { "type": "external", "url": "https://app.tajo.io/stripe/setup" }}Caution
URL eksternal harus menggunakan HTTPS dan sebaiknya dicantumkan pada allowed_redirect_uris Anda. Tim peninjau Stripe akan memverifikasi bahwa URL eksternal tersebut menyediakan pengalaman penyiapan yang berfungsi.
Praktik terbaik onboarding
Buat semudah mungkin
Kurangi jumlah langkah yang dibutuhkan untuk mulai:
- Isi otomatis informasi yang tersedia dari konteks akun Stripe
- Gunakan nilai bawaan yang masuk akal untuk opsi konfigurasi
- Izinkan melewati langkah opsional dengan jalur yang jelas untuk menyelesaikannya nanti
- Tampilkan kemajuan dengan indikator langkah untuk alur bertahap
Buat dapat disesuaikan
Biarkan pengguna mengonfigurasi integrasi sesuai kebutuhan mereka:
- Opsi pemetaan data, biarkan pengguna memilih bidang Stripe mana yang disinkronkan ke Brevo
- Frekuensi sinkronisasi, tawarkan opsi sinkronisasi tiap jam atau harian
- Sinkronisasi selektif, biarkan pengguna memilih pelanggan atau produk mana yang disinkronkan
- Preferensi notifikasi, konfigurasikan peringatan untuk error sinkronisasi atau peristiwa penting
Buat relevan
Tunjukkan nilainya sejak awal:
- Pratinjau data yang disinkronkan sebelum mengaktifkan integrasi
- Tunjukkan apa yang akan terjadi ketika pengguna menyelesaikan penyiapan
- Sediakan opsi sinkronisasi uji coba untuk memastikan koneksi berfungsi
- Tampilkan metrik keberhasilan setelah sinkronisasi awal selesai
Komponen OnboardingView
Komponen OnboardingView dirender dalam modal terfokus saat pengguna memasang aplikasi:
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('hourly'); 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="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 will sync on the schedule you chose</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;Alur masuk dengan SignInView
Jika aplikasi Anda mengharuskan pengguna masuk ke akun eksternal (seperti Tajo), gunakan tampilan masuk khusus:
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.example.com/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> );};Peluncuran deep link dengan parameter kueri
Anda dapat meluncurkan langkah onboarding tertentu atau mengisi data terlebih dahulu menggunakan parameter kueri pada deep link:
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};Buat deep link yang mengisi data onboarding terlebih dahulu:
// 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('');Menangani pengguna yang kembali
Ketika pengguna membuka aplikasi Anda setelah menyelesaikan onboarding, deteksi status mereka dan tampilkan tampilan yang sesuai:
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
Simpan status penyelesaian onboarding di Stripe Secret Store agar Anda dapat mendeteksi pengguna yang kembali tanpa panggilan API eksternal.