# Cài đặt widget

Widget là bong bóng chat trực tiếp mà khách thấy trên website của bạn. Trang này hướng dẫn tạo kênh website, gắn đoạn mã vào website (tự dán hoặc qua WordPress, Shopify, Haravan, LadiPage, Sapo, Google Tag Manager, Webflow, React) và kiểm tra widget đã chạy.

## Trước khi bắt đầu

- Module **Hỗ trợ** đang bật cho doanh nghiệp. Module tắt thì widget không hiện.
- Vai trò của bạn quản lý được Hỗ trợ. Trang **Hỗ trợ → Kênh** cần quyền `chat:manage` (Chủ sở hữu, Quản trị viên, Developer, hoặc thành viên có mức "quản lý" ở Hỗ trợ). Xem [Nhân viên và phân quyền](https://hatang.dev/vi/help/account/employees-and-roles).
- Bạn biết tên miền mà widget sẽ chạy, ví dụ `shop.vn`.

## 1. Tạo kênh website

Mỗi website là một kênh, có `site_id`, đoạn mã và cài đặt riêng. Một doanh nghiệp có thể có nhiều website.

1. Trong dashboard, vào **Hỗ trợ → Kênh → Thêm kênh → Website**.
2. Điền form:

   | Trường | Nhập gì |
   |---|---|
   | **Tên site** | Tên để đội của bạn nhận ra, ví dụ "Cửa hàng chính". Tên này cũng là tiêu đề trên đầu widget; đổi được sau. |
   | **Tên miền** | Host mà widget chạy, ví dụ `shop.vn`. Tên miền này và mọi tên miền phụ của nó luôn được phép. |
   | **Origin được phép thêm** | Không bắt buộc. Mỗi dòng một origin khác cần tải widget, ví dụ `https://app.example.com` hoặc `http://localhost:3000`. |

3. Bấm **Tạo site**. Trang của site mở ra, đoạn mã đã sẵn sàng.

Lần sau, vào **Hỗ trợ → Kênh** rồi chọn website.

## 2. Sao chép đoạn mã

Thẻ **Cài đặt** trên trang site hiển thị đoạn mã đã điền sẵn `site_id`. Bấm **Sao chép mã**.

```html
<script>window.$hatang=window.$hatang||[];window.HATANG_SITE_ID="site_123...";</script>
<script src="https://chat.hatang.dev/widget.js" async></script>
```

Thẻ thứ nhất tạo hàng đợi lệnh và đặt site ID. Thẻ thứ hai tải loader của widget. Loader rất nhỏ, được cache năm phút; nó lấy cài đặt của site rồi tải bản widget có đánh phiên bản. Widget chạy trong shadow DOM, nên CSS của trang không ảnh hưởng widget và CSS của widget không ảnh hưởng trang.

> **Tip**
>
> Nếu muốn chỉ dùng một thẻ, loader cũng đọc site ID từ thuộc tính `data-site-id`:
> `<script src="https://chat.hatang.dev/widget.js" data-site-id="site_123..." async></script>`.
> Dùng dạng hai thẻ nếu bạn gọi `$hatang.push(...)` trước khi widget tải xong, vì thẻ thứ nhất tạo hàng đợi.

## 3. Dán vào mọi trang

Dán đoạn mã ngay trước `</body>` trên mọi trang mà khách cần chat. Theo từng nền tảng:

| Nền tảng | Cách làm |
|---|---|
| WordPress | Cài plugin [Hạ Tầng Chat](https://wordpress.org/plugins/hatang-chat/) (Plugins → Add New, tìm "Hạ Tầng Chat") và kích hoạt. Vào **Settings → Hạ Tầng Chat**, dán Site ID rồi lưu. Người dùng đã đăng nhập được nhận diện tự động. Xem [WordPress](https://hatang.dev/vi/docs/guides/integrations/wordpress.md). Không dùng plugin: Appearance → Theme File Editor → `footer.php`, dán đoạn mã trước `</body>`. |
| Shopify | Online Store → Themes → **Edit code**. Mở `layout/theme.liquid`, dán đoạn mã ngay trước `</body>`. Lưu. Widget xuất hiện trên mọi trang cửa hàng, kể cả trang thanh toán dùng layout của theme. |
| Haravan | Website → Giao diện → **Chỉnh sửa mã nguồn**. Mở `layout/theme.liquid`, dán đoạn mã trước `</body>` rồi lưu. |
| LadiPage | Mở landing page, trên thanh công cụ chọn **Thiết lập → Mã JavaScript/CSS**. Dán đoạn mã vào ô mã, trước `</body>`. Lưu rồi bấm Xuất bản; trang chỉ có chat sau khi xuất bản lại. Mỗi landing page cài riêng. |
| Sapo | Website → Giao diện → Thao tác → **Chỉnh sửa HTML/CSS**. Mở `Theme.bwt`, dán đoạn mã ngay trước `</body>`. Lưu. |
| Google Tag Manager | Tags → New → **Custom HTML**, dán đoạn mã. Trigger: **All Pages**. Lưu và Publish container. Chỉ tích "Support document.write" nếu container của bạn yêu cầu; đoạn mã không cần. |
| Webflow | Project settings → **Custom code** → Footer code, dán đoạn mã. Publish. |
| React / Next.js | Cài `@hatang/chat-react` và render `<HatangChat siteId="..." />` một lần trong root layout. Xem bên dưới. |
| Website HTML bất kỳ | Dán đoạn mã trước `</body>` trên mọi trang. Thẻ thứ hai có thể dùng `defer` thay vì `async`. |

### React và Next.js

```bash
npm install @hatang/chat-react
```

```tsx title="app/layout.tsx"
import { HatangChat } from "@hatang/chat-react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="vi">
      <body>
        {children}
        <HatangChat siteId="site_123..." />
      </body>
    </html>
  );
}
```

`HatangChat` không render gì ở phía server và chỉ tải widget một lần trên trình duyệt. Dùng `useHatang()` hoặc `hatang()` để gọi [JavaScript API](https://hatang.dev/vi/docs/guides/support/web-widget/javascript-api.md). Chi tiết ở trang [SDK React](https://hatang.dev/vi/docs/guides/sdks/react.md).

## 4. Kiểm tra cài đặt

Mở website trên trình duyệt. Nút mở chat hiện ở góc dưới phải (hoặc nơi bạn chọn trong [Cấu hình widget](https://hatang.dev/vi/docs/guides/support/web-widget/configuration.md)).

Quay lại trang site, thẻ **Cài đặt** hiện **Đã cài trên** `shop.vn`, **lần cuối** vài phút trước khi widget đã tải trên tên miền của bạn. Trước đó thẻ hiện **Chưa phát hiện**. Trạng thái này cập nhật tối đa mỗi phút một lần.

Gửi thử một tin nhắn từ widget. Tin hiện trong **Hỗ trợ → Hộp thư** thành một hội thoại mới.

## Origin nào được tải widget

Mọi request của widget được kiểm tra theo `Origin` của trang:

- **Tên miền** của site và mọi tên miền phụ của nó luôn được phép. Với `shop.vn`, cả `https://shop.vn` lẫn `https://www.shop.vn` đều chạy.
- Mỗi dòng trong **Origin được phép thêm** phải khớp chính xác, kể cả scheme và cổng: `http://localhost:3000` không cho phép `http://localhost:5173`.
- Request không có header `Origin` (ví dụ gọi từ server) bị từ chối, trừ khi đến từ [ứng dụng di động](https://hatang.dev/vi/docs/guides/support/mobile-apps.md) có app key.

Khi thử nghiệm, thêm địa chỉ chạy trên máy và các bản preview vào **Origin được phép thêm**.

## Nhiều website

Tạo một kênh website cho mỗi tên miền hoặc thương hiệu: **Hỗ trợ → Kênh → Thêm kênh → Website**. Mỗi site có đoạn mã, cài đặt, mã bí mật định danh và các kênh riêng (Zalo, Messenger và các kênh khác gắn với một website). Mọi hội thoại về cùng một **Hộp thư**.

## Xử lý sự cố

Khi không tải được, widget im lặng: không có nút chat và không báo lỗi trên trang. Mở công cụ dành cho nhà phát triển của trình duyệt, tab **Network**, và xem request `bootstrap` tới `chat.hatang.dev`.

| Bạn thấy | Nguyên nhân | Cách xử lý |
|---|---|---|
| `bootstrap` trả 403 kèm `origin_not_allowed` | Origin của trang không phải tên miền của site, tên miền phụ của nó, hay một origin được phép thêm. | Sửa **Tên miền** hoặc thêm origin vào **Origin được phép thêm**. |
| `bootstrap` trả 403 kèm `site_disabled` | **Trạng thái** của site là **Đã tắt (ẩn widget)**. | Chuyển lại **Đang hoạt động** trong cài đặt site. |
| `bootstrap` trả 403 kèm `product_disabled` | Module Hỗ trợ đang tắt cho doanh nghiệp. | Bật lại Hỗ trợ. Xem [Doanh nghiệp và module](https://hatang.dev/vi/help/getting-started/modules). |
| `bootstrap` trả 429 | Quá 30 lần bootstrap mỗi phút từ một địa chỉ IP. | Chờ một phút; kiểm tra đoạn mã không bị tải lặp. |
| Không có request `bootstrap` nào | Thiếu site ID hoặc trang chưa có đoạn mã. | Xem mã nguồn trang, tìm `HATANG_SITE_ID`. |
| `bootstrap` thành công nhưng không có nút chat | Trang bị loại bởi **Nơi hiện widget**, đang bật **Ẩn hẳn bong bóng khi ngoại tuyến** mà không ai trực, `window.HATANG_CONFIG` có `hideLauncher`, hoặc trang chạy trong iframe khác origin. | Xem [Cấu hình widget](https://hatang.dev/vi/docs/guides/support/web-widget/configuration.md). |
| Console báo script hoặc socket bị chặn | Content Security Policy của trang chưa cho phép widget. | Cho phép `https://chat.hatang.dev` trong `script-src` và `connect-src`, và `wss://chat.hatang.dev` trong `connect-src`. |

## Bước tiếp theo

- [Cấu hình widget](https://hatang.dev/vi/docs/guides/support/web-widget/configuration.md): màu, nút mở chat, lời chào, form trước khi chat, giờ làm việc.
- [JavaScript API](https://hatang.dev/vi/docs/guides/support/web-widget/javascript-api.md): mở chat từ nút của bạn và nghe sự kiện.
- [Nhận diện khách](https://hatang.dev/vi/docs/guides/support/web-widget/identity-verification.md): gắn hội thoại với người dùng đã đăng nhập.

Source: https://hatang.dev/vi/docs/guides/support/web-widget/install
