---
title: "Hành Động Sau Cài Đặt & Onboarding | Tajo"
description: "Cấu hình hành động sau cài đặt và xây dựng luồng onboarding hiệu quả cho Stripe App của bạn"
url: https://tajo.io/vi/docs/stripe-apps/post-install/
markdown: https://tajo.io/vi/docs/stripe-apps/post-install.md
lang: vi-VN
author: "Set Noa"
author_url: https://tajo.io/vi/set-noa/
robots: noindex
---

Đăng ký quyền truy cập sớm

Nhập tên và địa chỉ email công việc của bạn. Chúng tôi sẽ gửi liên kết để xác nhận yêu cầu truy cập Tajo.

# Hành Động Sau Cài Đặt & Onboarding

[Set Noa](https://tajo.io/vi/set-noa/)

Hành động sau cài đặt xác định điều gì xảy ra ngay sau khi người dùng cài đặt Stripe App của bạn. Trải nghiệm sau cài đặt được thiết kế tốt sẽ hướng dẫn người dùng qua quá trình thiết lập và tăng tỷ lệ kích hoạt.

Caution

**Trạng thái: dự kiến, chưa được xây dựng.** Tajo không có Stripe App, vì vậy hiện chưa thể cài đặt bất kỳ nội dung nào trong phần này. Các định danh app, URL và thông tin xác thực trong trang này chỉ là ví dụ minh họa, không phải endpoint đang hoạt động.

## Các Loại Hành Động Sau Cài Đặt

Stripe hỗ trợ bốn loại hành động sau cài đặt, mỗi loại được cấu hình trong app manifest:

### 1. Liên Kết Đến App (Mặc Định)

Mở app trong viewport drawer mặc định. Đây là hành vi mặc định nếu không có `post_install_action` được chỉ định:

```json
{
  "post_install_action": {
    "type": "default"
  }
}
```

Người dùng thấy viewport `drawer.default` của app trong thanh bên Stripe Dashboard.

### 2. Liên Kết Đến Onboarding

Mở view onboarding chuyên dụng của app, cung cấp trải nghiệm thiết lập tập trung:

```json
{
  "post_install_action": {
    "type": "onboarding"
  }
}
```

Điều này yêu cầu viewport `onboarding` được khai báo trong manifest:

```json
{
  "ui_extension": {
    "views": [
      {
        "viewport": "stripe.dashboard.onboarding",
        "component": "OnboardingView"
      }
    ]
  },
  "post_install_action": {
    "type": "onboarding"
  }
}
```

### 3. Liên Kết Đến Settings

Mở view settings của app, hữu ích khi app yêu cầu API keys hoặc cấu hình trước khi sử dụng:

```json
{
  "post_install_action": {
    "type": "settings"
  }
}
```

Điều này yêu cầu viewport `settings`:

```json
{
  "ui_extension": {
    "views": [
      {
        "viewport": "stripe.dashboard.settings",
        "component": "SettingsView"
      }
    ]
  },
  "post_install_action": {
    "type": "settings"
  }
}
```

### 4. Liên Kết Đến URL Bên Ngoài

Chuyển hướng người dùng đến URL bên ngoài để thiết lập. Sử dụng khi luồng onboarding nằm ngoài Stripe Dashboard:

```json
{
  "post_install_action": {
    "type": "external",
    "url": "https://app.tajo.io/stripe/setup"
  }
}
```

Caution

URL bên ngoài phải dùng HTTPS và nên được liệt kê trong `allowed_redirect_uris`. Nhóm đánh giá Stripe sẽ xác minh rằng URL bên ngoài cung cấp trải nghiệm thiết lập hoạt động tốt.

## Các Thực Hành Tốt Nhất Cho Onboarding

### Làm Cho Dễ Dàng

Giảm thiểu số bước cần thiết để bắt đầu:

- **Điền trước thông tin** có sẵn từ context tài khoản Stripe
- **Sử dụng giá trị mặc định hợp lý** cho các tùy chọn cấu hình
- **Cho phép bỏ qua** các bước tùy chọn với đường dẫn rõ ràng để hoàn thành sau
- **Hiển thị tiến trình** với chỉ số bước cho luồng nhiều bước

### Làm Cho Có Thể Tùy Chỉnh

Cho phép người dùng cấu hình tích hợp theo nhu cầu:

- **Tùy chọn ánh xạ dữ liệu**, để người dùng chọn trường Stripe nào đồng bộ với Brevo
- **Tần suất đồng bộ**, cung cấp tùy chọn đồng bộ mỗi giờ hoặc hàng ngày
- **Đồng bộ có chọn lọc**, để người dùng chọn khách hàng hoặc sản phẩm nào cần đồng bộ
- **Tùy chọn thông báo**, cấu hình cảnh báo cho lỗi đồng bộ hoặc sự kiện quan trọng

### Làm Cho Phù Hợp

Hiển thị giá trị ngay lập tức:

- **Xem trước dữ liệu được đồng bộ** trước khi kích hoạt tích hợp
- **Cho thấy điều gì sẽ xảy ra** khi người dùng hoàn thành thiết lập
- **Cung cấp tùy chọn đồng bộ thử** để xác minh kết nối hoạt động
- **Hiển thị số liệu thành công** sau khi đồng bộ ban đầu hoàn thành

## Component OnboardingView

Component `OnboardingView` hiển thị trong modal tập trung khi người dùng cài đặt app:

```typescript
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 &gt; SMTP &amp; API &gt; 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;
```

## Luồng Đăng Nhập Với SignInView

Nếu app yêu cầu người dùng đăng nhập vào tài khoản bên ngoài (như Tajo), hãy sử dụng view đăng nhập chuyên dụng:

```typescript
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>
  );
};
```

## Khởi Chạy Deep Link Với Tham Số Query

Bạn có thể khởi chạy các bước onboarding cụ thể hoặc điền trước dữ liệu bằng tham số query trong deep links:

```typescript
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
};
```

Tạo deep links điền trước dữ liệu onboarding:

```javascript
// From your Tajo dashboard, generate a link that pre-fills the Brevo API key
const 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('');
```

## Xử Lý Người Dùng Quay Lại

Khi người dùng mở app sau khi hoàn thành onboarding, hãy phát hiện trạng thái của họ và hiển thị view phù hợp:

```typescript
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

Lưu trạng thái hoàn thành onboarding trong Stripe Secret Store để bạn có thể phát hiện người dùng quay lại mà không cần gọi API bên ngoài.
