---
title: "Liên kết cài đặt và liên kết sâu | Tajo"
description: "Tạo URL cài đặt để phân phối ứng dụng và liên kết sâu để đưa người dùng tới đúng màn hình trong ứng dụng"
url: https://tajo.io/vi/docs/stripe-apps/install-links/
markdown: https://tajo.io/vi/docs/stripe-apps/install-links.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.

# Liên kết cài đặt và liên kết sâu

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

Liên kết cài đặt giúp bạn phân phối Stripe App bên ngoài marketplace, còn liên kết sâu đưa người dùng thẳng tới một màn hình cụ thể bên trong ứng dụng đã cài. Cả hai đều thiết yếu để luồng làm quen và luồng tích hợp diễn ra mượt mà.

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.

## Liên kết cài đặt

Liên kết cài đặt cung cấp một URL trực tiếp để nhà bán hàng cài ứng dụng của bạn. Khi người dùng nhấp vào liên kết cài đặt, Stripe xử lý luồng cài đặt rồi chuyển hướng trở lại URI mà bạn đã chỉ định.

### Điều kiện tiên quyết

Trước khi dùng liên kết cài đặt, hãy cấu hình `allowed_redirect_uris` trong manifest của ứng dụng:

```json
{
  "id": "com.tajo.brevo-integration",
  "allowed_redirect_uris": [
    "https://tajo.io/stripe/callback",
    "https://tajo.io/stripe/oauth/complete"
  ]
}
```

### Định dạng liên kết cài đặt

```
https://marketplace.stripe.com/oauth/v2/authorize?client_id=APP_ID&redirect_uri=REDIRECT_URI&state=STATE_VALUE
```

| Tham số | Bắt buộc | Mô tả |
| --- | --- | --- |
| `client_id` | Có | ID ứng dụng của bạn (ví dụ `com.tajo.brevo-integration`) |
| `redirect_uri` | Có | Phải khớp với một trong các `allowed_redirect_uris` của bạn |
| `state` | Nên có | Chuỗi ngẫu nhiên để chống CSRF |

### Tham số chuyển hướng

Sau khi cài đặt thành công, Stripe chuyển hướng người dùng tới `redirect_uri` của bạn kèm các tham số truy vấn sau:

| Tham số | Mô tả |
| --- | --- |
| `user_id` | ID người dùng Stripe của tài khoản thực hiện cài đặt |
| `account_id` | ID tài khoản Stripe (ví dụ `acct_xxxxx`) |
| `state` | Giá trị `state` bạn đã cung cấp (để xác minh chống CSRF) |
| `install_signature` | Chữ ký HMAC dùng để xác minh lượt cài đặt là hợp lệ |

Ví dụ URL chuyển hướng:

```
https://tajo.io/stripe/callback
  ?user_id=usr_xxxxx
  &account_id=acct_xxxxx
  &state=abc123random
  &install_signature=sig_xxxxx
```

### Chống CSRF

Luôn dùng tham số `state` để ngăn các cuộc tấn công giả mạo yêu cầu liên trang:

```javascript
import crypto from 'crypto';

// Generate a random state value and store it in the session
const generateInstallLink = (req, res) => {
  const state = crypto.randomBytes(32).toString('hex');

  // Store state in session for later verification
  req.session.stripeInstallState = state;

  const installUrl = new URL('https://marketplace.stripe.com/oauth/v2/authorize');
  installUrl.searchParams.set('client_id', 'com.tajo.brevo-integration');
  installUrl.searchParams.set('redirect_uri', 'https://tajo.io/stripe/callback');
  installUrl.searchParams.set('state', state);

  res.redirect(installUrl.toString());
};

// Handle the redirect callback
const handleInstallCallback = async (req, res) => {
  const { state, user_id, account_id, install_signature } = req.query;

  // Verify state matches what we stored
  if (state !== req.session.stripeInstallState) {
    return res.status(403).json({ error: 'Invalid state parameter' });
  }

  // Clear the stored state
  delete req.session.stripeInstallState;

  // Verify the install signature
  if (!verifyInstallSignature(install_signature, account_id)) {
    return res.status(403).json({ error: 'Invalid install signature' });
  }

  // Process the successful installation
  await processInstallation(user_id, account_id);

  res.redirect('/dashboard/stripe-connected');
};
```

### Xác minh chữ ký

Xác minh `install_signature` bằng khóa ký bí mật của ứng dụng:

```javascript
import crypto from 'crypto';

const verifyInstallSignature = (signature, accountId) => {
  const signingSecret = process.env.STRIPE_APP_SIGNING_SECRET;

  const expectedSignature = crypto
    .createHmac('sha256', signingSecret)
    .update(accountId)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
};
```

Caution

Luôn dùng `crypto.timingSafeEqual` khi so sánh chữ ký để chống tấn công dựa trên thời gian. Đừng bao giờ so sánh chuỗi theo cách đơn giản (`===`).

### Khóa ký bí mật

Khóa ký bí mật của ứng dụng nằm trong Stripe Dashboard, ở phần cài đặt ứng dụng của bạn. Hãy dùng nó để:

- Xác minh chữ ký cài đặt từ các lệnh gọi lại chuyển hướng
- Kiểm tra tính hợp lệ của payload webhook từ Stripe
- Xác thực các yêu cầu giữa backend của bạn và Stripe

Lưu khóa ký bí mật ở nơi an toàn:

```bash
# Set as environment variable
export STRIPE_APP_SIGNING_SECRET="whsec_xxxxx"
```

Đừng bao giờ gán cứng khóa ký bí mật trong mã nguồn hay commit chúng vào hệ thống quản lý phiên bản.

## Liên kết sâu

Liên kết sâu đưa người dùng thẳng tới một màn hình cụ thể bên trong Stripe App đã cài. Hãy dùng chúng để dẫn người dùng từ các kênh liên lạc bên ngoài (email, thông báo, trang hỗ trợ) tới đúng ngữ cảnh trong ứng dụng.

### Định dạng URL của liên kết sâu

```
https://dashboard.stripe.com/MODE/acct_ID/PAGE?apps[APP_ID][TARGET]=VIEWPORT_ID
```

| Thành phần | Mô tả | Ví dụ |
| --- | --- | --- |
| `MODE` | `live` hoặc `test` | `live` |
| `acct_ID` | ID tài khoản Stripe đích | `acct_1234567890` |
| `PAGE` | Đường dẫn trang trong Dashboard | `customers/cus_xxxxx` |
| `APP_ID` | ID ứng dụng của bạn | `com.tajo.brevo-integration` |
| `TARGET` | `drawer` hoặc `modal` | `drawer` |
| `VIEWPORT_ID` | Viewport cần mở | `stripe.dashboard.customer.detail` |

### So sánh đích drawer và modal

| Đích | Hành vi | Trường hợp sử dụng |
| --- | --- | --- |
| `drawer` | Mở ứng dụng trong bảng bên cạnh (drawer) | Tương tác mặc định, xem ngữ cảnh song song với trang đang mở |
| `modal` | Mở ứng dụng trong lớp phủ modal toàn màn hình | Quy trình cần tập trung, luồng làm quen, biểu mẫu phức tạp |

### Ví dụ về liên kết sâu

#### Mở màn hình chi tiết khách hàng trong drawer

```
https://dashboard.stripe.com/live/acct_xxxxx/customers/cus_xxxxx
  ?apps[com.tajo.brevo-integration][drawer]=stripe.dashboard.customer.detail
```

#### Mở phần cài đặt trong modal

```
https://dashboard.stripe.com/live/acct_xxxxx/settings
  ?apps[com.tajo.brevo-integration][modal]=stripe.dashboard.settings
```

#### Mở luồng làm quen

```
https://dashboard.stripe.com/live/acct_xxxxx/dashboard
  ?apps[com.tajo.brevo-integration][modal]=stripe.dashboard.onboarding
```

#### Mở màn hình chi tiết thanh toán ở chế độ test

```
https://dashboard.stripe.com/test/acct_xxxxx/payments/pi_xxxxx
  ?apps[com.tajo.brevo-integration][drawer]=stripe.dashboard.payment.detail
```

### Tạo liên kết sâu bằng mã lệnh

```javascript
const generateDeepLink = ({
  accountId,
  mode = 'live',
  page,
  appId = 'com.tajo.brevo-integration',
  target = 'drawer',
  viewport,
}) => {
  const baseUrl = `https://dashboard.stripe.com/${mode}/${accountId}/${page}`;
  const params = new URLSearchParams();
  params.set(`apps[${appId}][${target}]`, viewport);

  return `${baseUrl}?${params.toString()}`;
};

// Generate a link to view a customer's Brevo profile
const customerLink = generateDeepLink({
  accountId: 'acct_xxxxx',
  page: 'customers/cus_xxxxx',
  viewport: 'stripe.dashboard.customer.detail',
});

// Generate a link to app settings
const settingsLink = generateDeepLink({
  accountId: 'acct_xxxxx',
  page: 'settings',
  viewport: 'stripe.dashboard.settings',
  target: 'modal',
});
```

### Dùng liên kết sâu trong các kênh liên lạc

Liên kết sâu đặc biệt hữu ích trong:

- **Email thông báo**: “Xem trạng thái đồng bộ Brevo của khách hàng này”
- **Phản hồi hỗ trợ**: “Nhấp vào đây để kiểm tra cài đặt tích hợp của bạn”
- **Email làm quen**: “Hoàn tất thiết lập Brevo của bạn”
- **Cảnh báo lỗi**: “Xem lại sự cố đồng bộ của khách hàng X”

```html
<!-- Example in an email template -->
<a href="https://dashboard.stripe.com/live/acct_xxxxx/customers/cus_xxxxx?apps[com.tajo.brevo-integration][drawer]=stripe.dashboard.customer.detail">
  View Brevo Profile in Stripe
</a>
```

## Kết hợp liên kết cài đặt với liên kết sâu

Để có trải nghiệm làm quen tốt nhất, hãy kết hợp liên kết cài đặt với liên kết sâu chạy sau khi cài:

1. Người dùng nhấp vào một **liên kết cài đặt** từ website hoặc email của bạn
2. Người dùng cài ứng dụng và được chuyển hướng tới URL gọi lại của bạn
3. Lệnh gọi lại của bạn xử lý việc cài đặt rồi chuyển hướng người dùng tới một **liên kết sâu** mở viewport làm quen

```javascript
const handleInstallCallback = async (req, res) => {
  const { account_id, install_signature, state } = req.query;

  // Verify state and signature
  // ... (verification code)

  // Process installation
  await processInstallation(account_id);

  // Redirect to the app's onboarding view via deep link
  const onboardingLink = generateDeepLink({
    accountId: account_id,
    page: 'dashboard',
    viewport: 'stripe.dashboard.onboarding',
    target: 'modal',
  });

  res.redirect(onboardingLink);
};
```

Tip

Luôn kiểm thử liên kết cài đặt và liên kết sâu ở cả chế độ live lẫn test để chắc chắn chúng hoạt động đúng trong mọi môi trường.
