> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vibestrap.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# 在线客服

> 一行配置切换 Crisp、Tawk.to、Intercom、Chatwoot 四家在线客服 widget。

在线客服是产品可信度的信号 —— 没有客服的产品看起来像一个人折腾的副业,有客服的看起来
背后有一个团队。但每家客服工具都有自己的 SDK、认证模型、卸载怪癖,自己写一套集成会
浪费几天时间,而你本该在做产品差异化。

vibestrap 在 locale 布局里挂了一个 `<CustomerService />`,通过一行 config 切换四家
主流 provider(Crisp、Tawk.to、Intercom、Chatwoot)。Provider 会自己根据 env 变量决定
是否渲染 —— 没配齐就返回 `null`,不会把页面弄崩。

## 前置条件

* 在 Crisp、Tawk.to、Intercom、Chatwoot 四家里挑一家注册账号。
* 拿到 provider dashboard 里的 site / property / app ID。
* 在 provider 后台把生产域名加到白名单（绝大多数都会拒绝来自未知 origin 的嵌入）。

## 一步步配置

1. 在 `src/config/site.ts` 选 provider：

   ```ts theme={null}
   customerService: {
     enable: true,
     provider: 'crisp', // 'crisp' | 'tawk' | 'intercom' | 'chatwoot'
   },
   ```

2. 把对应的 env 变量加到 `.env.local`。所有变量都是 `NEXT_PUBLIC_*` 开头——
   loader 脚本在浏览器跑，所以必须暴露给客户端。

   ```bash theme={null}
   # Crisp
   NEXT_PUBLIC_CRISP_WEBSITE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

   # Tawk.to（两个都要）
   NEXT_PUBLIC_TAWK_PROPERTY_ID=000000000000000000000000
   NEXT_PUBLIC_TAWK_WIDGET_ID=000000000

   # Intercom
   NEXT_PUBLIC_INTERCOM_APP_ID=abcd1234

   # Chatwoot（token 必填，base URL 默认是 app.chatwoot.com）
   NEXT_PUBLIC_CHATWOOT_WEBSITE_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxx
   NEXT_PUBLIC_CHATWOOT_BASE_URL=https://app.chatwoot.com
   ```

3. 在 provider 后台把你的域名加白名单（Crisp → Settings → Website Settings；
   Tawk → Property → Widget → Restrictions，etc）。

4. **重启 `pnpm dev`** —— `NEXT_PUBLIC_*` 是构建时打进去的，不重启不会生效。

## 怎么挑

| Provider | 适用场景                                        |
| -------- | ------------------------------------------- |
| Crisp    | 免费额度大方（2 个坐席、不限联系人），后台漂亮，配置最快。独立开发者首选。      |
| Tawk.to  | 完全免费，没有任何套餐限制。代价：底栏带品牌水印，去掉要 \$19/月。        |
| Intercom | 企业标准款。有专门的支持团队、想上 product tour、工单、出站营销，就选它。 |
| Chatwoot | 开源、可自托管。要数据主权、或者想把客服后端跑在自己服务器上的，选这个。        |

## 验证生效

1. 用无痕窗口打开站点——气泡应该在右下角 1 秒内出现。
2. 发一条测试消息，去 provider 后台看收件箱。
3. 打开 `/login` 或 `/register`——这两个页面故意没有 widget（`<CustomerService />`
   挂在 app 布局里，不在 auth 布局里）。
4. 查看页面源码：应该只看到当前 provider 的 loader 脚本，其他三家应该没出现。

## 常见坑

1. **忘了 `NEXT_PUBLIC_` 前缀**。浏览器要读的变量必须带前缀，否则 Next.js 在打包时
   会把它剥掉，widget 就静默失败。
2. **没把域名加白名单**。大多数 provider 会在未知 origin 上拒绝渲染（表现是空气泡，
   或者 devtools 报 CORS 错）。
3. **CSP 拦了脚本**。你要是加了 Content-Security-Policy header，得把 provider 的 CDN
   加到 `script-src` 和 `connect-src`（如 `https://client.crisp.chat`、
   `https://embed.tawk.to`、`https://widget.intercom.io`、Chatwoot 的 base URL）。
4. **同时出现两个 widget**。`enable: true` + 上一家遗留的 env 不会重复渲染——
   `src/customer-service/index.tsx` 的 switch 只挂当前那个。残留 env 没害处，但建议清掉。
5. **认证页没 widget**。这是有意为之（layout 不一样）。要全站都显示，把
   `<CustomerService />` 挂到 root layout 而不是 app layout。

## 加一个新 provider

模式刻意做得很小——一个 client 组件，读 env 变量，返回 `<Script>` 标签（或 `null`）。
对照 `src/customer-service/provider/*.tsx` 任一文件依葫芦画瓢即可：

1. 在 `src/customer-service/types.ts` 的联合类型里加一个名字。
2. 在 `src/customer-service/index.tsx` 的 switch 加一个 `case`。
3. 在 `src/env.ts` 的 `client.NEXT_PUBLIC_*` 加 env 变量。

## 官方文档

* Crisp：[docs.crisp.chat](https://docs.crisp.chat/)
* Tawk.to：[help.tawk.to](https://help.tawk.to/)
* Intercom：[intercom.com/help/en](https://www.intercom.com/help/en/)
* Chatwoot：[chatwoot.com/docs](https://www.chatwoot.com/docs)
