---
title: "安装链接与深度链接 | Tajo"
description: "生成用于分发应用的安装 URL，以及把用户导航到特定应用视图的深度链接"
url: https://tajo.io/zh-cn/docs/stripe-apps/install-links/
markdown: https://tajo.io/zh-cn/docs/stripe-apps/install-links.md
lang: zh-CN
author: "Set Noa"
author_url: https://tajo.io/zh-cn/set-noa/
robots: noindex
---

申请抢先体验

请输入您的名字和公司电子邮箱地址。我们会发送确认 Tajo 访问申请的链接。

# 安装链接与深度链接

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

安装链接（install links）让你在应用市场之外分发 Stripe App，深度链接（deep links）则把用户直接带到已安装应用中的特定视图。两者对顺畅的上手引导和集成流程都不可或缺。

Caution

**状态：计划中，尚未构建。** Tajo 没有 Stripe App，因此本章中的任何内容目前都无法安装。本页中的 app 标识、URL 和凭据都是用于示意的占位值，而不是可用的线上端点。

## 安装链接

安装链接提供一个直接 URL，商家点击后即可安装你的应用。用户点击安装链接时，Stripe 会处理安装流程，然后重定向回你指定的 URI。

### 前提条件

使用安装链接之前，先在应用清单（app manifest）中配置 `allowed_redirect_uris`：

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

### 安装链接格式

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

| 参数 | 是否必需 | 说明 |
| --- | --- | --- |
| `client_id` | 是 | 你的应用 ID（例如 `com.tajo.brevo-integration`） |
| `redirect_uri` | 是 | 必须与 `allowed_redirect_uris` 中的某一项一致 |
| `state` | 建议 | 用于 CSRF 防护的随机字符串 |

### 重定向参数

安装成功后，Stripe 会把用户重定向到你的 `redirect_uri`，并附带以下查询参数：

| 参数 | 说明 |
| --- | --- |
| `user_id` | 执行安装的账号的 Stripe user ID |
| `account_id` | Stripe account ID（例如 `acct_xxxxx`） |
| `state` | 你提供的 `state` 值（用于 CSRF 校验） |
| `install_signature` | 用于验证安装是否合法的 HMAC 签名 |

重定向 URL 示例：

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

### CSRF 保护

始终使用 `state` 参数来防止跨站请求伪造攻击：

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

### 签名验证

使用应用的签名密钥（signing secret）验证 `install_signature`：

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

比较签名时始终使用 `crypto.timingSafeEqual`，以防时序攻击。不要使用简单的字符串相等判断（`===`）。

### 签名密钥

你的应用签名密钥可以在 Stripe Dashboard 的应用设置中找到。它的用途包括：

- 验证重定向回调中的安装签名
- 校验来自 Stripe 的 webhook 负载
- 为后端与 Stripe 之间的请求做身份认证

请妥善存储签名密钥：

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

不要把签名密钥硬编码在源代码里，也不要提交到版本控制。

## 深度链接

深度链接把用户直接带到已安装 Stripe App 中的某个视图。用它可以把用户从外部沟通渠道（邮件、通知、支持页面）引导到相关的应用上下文。

### 深度链接 URL 格式

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

| 组成部分 | 说明 | 示例 |
| --- | --- | --- |
| `MODE` | `live` 或 `test` | `live` |
| `acct_ID` | 目标 Stripe account ID | `acct_1234567890` |
| `PAGE` | Dashboard 页面路径 | `customers/cus_xxxxx` |
| `APP_ID` | 你的应用 ID | `com.tajo.brevo-integration` |
| `TARGET` | `drawer` 或 `modal` | `drawer` |
| `VIEWPORT_ID` | 要打开的 viewport | `stripe.dashboard.customer.detail` |

### Drawer 与 Modal 目标

| 目标 | 行为 | 适用场景 |
| --- | --- | --- |
| `drawer` | 在侧边面板（drawer）中打开应用 | 默认的应用交互，在页面旁提供上下文 |
| `modal` | 在全屏 modal 浮层中打开应用 | 需要专注的工作流、上手引导、复杂表单 |

### 深度链接示例

#### 在 drawer 中打开客户详情视图

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

#### 在 modal 中打开设置页

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

#### 打开上手引导流程

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

#### 在测试模式下打开支付详情视图

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

### 以编程方式生成深度链接

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

### 在对外沟通中使用深度链接

深度链接在以下场景尤其有用：

- **邮件通知**：“查看该客户的 Brevo 同步状态”
- **支持回复**：“点击这里查看你的集成设置”
- **上手引导邮件**：“完成你的 Brevo 配置”
- **错误告警**：“查看客户 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>
```

## 结合使用安装链接与深度链接

要获得最佳的上手体验，把安装链接和安装后的深度链接串起来：

1. 用户在你的网站或邮件中点击 **安装链接**
2. 用户完成应用安装，并被重定向到你的回调 URL
3. 你的回调处理这次安装，然后把用户重定向到一个 **深度链接**，打开上手引导 viewport

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

安装链接和深度链接都要在 live 和 test 两种模式下测试，确保它们在所有环境中都能正常工作。
