# Bắt đầu nhanh: gắn chat lên website

Hướng dẫn này đặt nút chat lên website của bạn, cho hộp thư biết người dùng đã đăng nhập là ai, và để bạn trả lời tin nhắn đầu tiên từ dashboard, từ ứng dụng Hạ Tầng trên điện thoại hoặc từ server của bạn. Cuối cùng, bạn thêm cùng khung chat đó vào ứng dụng iOS, Android, Flutter hoặc React Native.

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

- Một tài khoản Hạ Tầng tại [app.hatang.dev](https://app.hatang.dev) đã bật module **Hỗ trợ** ([Doanh nghiệp và module](https://hatang.dev/vi/help/getting-started/modules)).
- Một website bạn sửa được, có quyền vào HTML chung của trang hoặc vào tag manager.
- Để đổi cài đặt Hỗ trợ, bạn cần là chủ sở hữu, quản trị viên, hoặc thành viên có quyền **Quản lý** ở Hỗ trợ ([Nhân viên và phân quyền](https://hatang.dev/vi/help/account/employees-and-roles)).

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

1. Trong dashboard, mở **Hỗ trợ → Kênh** và chọn website của bạn. Muốn thêm website khác, bấm **Thêm kênh** rồi chọn ở nhóm **Website**.
2. Ở khung **Cài đặt**, bấm **Sao chép mã**.

Đoạn mã gồm hai dòng. Dòng đầu đặt site id (bắt đầu bằng `site_`), dòng sau tải widget từ chat gateway:

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

## 2. Gắn vào website

Dán đoạn mã ngay trước `</body>` ở mọi trang, hoặc ở layout dùng chung cho mọi trang. Script tải bất đồng bộ nên không làm chậm trang.

Tải lại website: nút chat hiện ở góc màn hình. Trong dashboard, khung **Cài đặt** chuyển sang **Đã cài** và hiện tên miền nơi widget được thấy.

Dùng WordPress, Shopify, Haravan, LadiPage, Sapo, Google Tag Manager, Webflow hay React? [Hướng dẫn cài widget](https://hatang.dev/vi/docs/guides/support/web-widget/install.md) có các bước cho từng nền tảng, còn [plugin WordPress](https://hatang.dev/vi/docs/guides/integrations/wordpress.md) không cần viết mã.

> **Note: Widget chạy ở đâu**
>
> Widget chỉ khởi động trên tên miền của website và các tên miền phụ. Để thử trên `http://localhost:3000` hoặc bản xem trước, thêm origin đó vào **Origin được phép thêm** trên trang website. Origin khác bị từ chối với `origin_not_allowed`.

## 3. Nhận diện người dùng đã đăng nhập

Nếu không viết thêm mã, mọi khách truy cập đều là khách ẩn danh. Khi người dùng đã đăng nhập trên website, hãy truyền thông tin của họ cho widget để nhân viên biết mình đang nói chuyện với ai:

```html
<script>
  window.$hatang = window.$hatang || [];
  $hatang.push(["identify", {
    user_id: "42",
    email: "an.nguyen@example.com",
    name: "Nguyễn Văn An",
    phone: "0901234567",
    hash: "<%= hatangHash %>"
  }]);
</script>
```

`$hatang.push` dùng được cả trước và sau khi widget tải xong, nên bạn đặt đoạn này ở đâu cũng được, miễn là sau dòng đầu của đoạn mã cài đặt.

`hash` chứng minh user id đến từ server của bạn chứ không phải do ai đó gõ trong console của trình duyệt. Không có `hash`, thông tin được hiển thị là **Chưa xác minh** và không bao giờ được gộp vào lịch sử của khách khác. Có `hash` hợp lệ, hộp thư coi user id là **Đã xác minh** và gộp mọi phiên của người đó, trên mọi thiết bị, vào một khách hàng.

Tính `hash` ở server bằng HMAC-SHA256 của user id, khoá là **Mã bí mật định danh** của site (trên trang website, bấm **Tạo mới & hiển thị**), dạng hex chữ thường:

**Node.js**

```js
import { createHmac } from "node:crypto";

export const hatangHash = (userId) =>
  createHmac("sha256", process.env.HATANG_IDENTITY_SECRET).update(String(userId)).digest("hex");
```

**Python**

```python
import os
from hatang import identity_hash  # pip install hatang

def hatang_hash(user_id) -> str:
    return identity_hash(str(user_id), os.environ["HATANG_IDENTITY_SECRET"])
```

**PHP**

```php
$hash = hash_hmac('sha256', (string) $userId, getenv('HATANG_IDENTITY_SECRET'));
```

> **Danger: Giữ mã bí mật định danh ở server**
>
> Ai có mã bí mật định danh đều có thể vào chat dưới danh nghĩa bất kỳ người dùng nào của bạn. Không đặt nó vào JavaScript của trang, ứng dụng di động hay plugin công khai; chỉ gửi `hash` đã tính xuống trình duyệt.

[Hướng dẫn nhận diện khách](https://hatang.dev/vi/docs/guides/support/web-widget/identity-verification.md) giải thích cách gộp phiên, xoay mã bí mật và cú pháp cho từng SDK. [JavaScript API](https://hatang.dev/vi/docs/guides/support/web-widget/javascript-api.md) liệt kê các lệnh khác như `open`, `setLocale` và `on`.

## 4. Trả lời từ hộp thư

Mở website trong cửa sổ ẩn danh và gửi một tin nhắn từ widget. Tin nhắn hiện ngay trong **Hỗ trợ → Hộp thư**. Gõ câu trả lời, khách thấy ngay trong widget.

Nhân viên cũng trả lời được từ ứng dụng Hạ Tầng trên iPhone, Android và Mac, có thông báo đẩy khi có tin nhắn mới.

Muốn trả lời từ hệ thống của bạn, dùng REST API với key có scope `chat:write`. Lấy danh sách hội thoại đang mở, rồi gửi tin nhắn dưới tên đội ngũ:

**cURL**

```bash
curl "https://api.hatang.dev/chat/conversations?status=open&limit=20" \
  -H "Authorization: Bearer $HATANG_API_KEY"

curl -X POST https://api.hatang.dev/chat/conversations/cnv_123.../messages \
  -H "Authorization: Bearer $HATANG_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-cnv_123-1" \
  -d '{ "content": "Chào anh An, đơn của anh được giao trong hôm nay ạ." }'
```

**Node.js**

```js
import { Hatang } from "@hatang/sdk";

const hatang = new Hatang(process.env.HATANG_API_KEY, { baseUrl: "https://api.hatang.dev" });

const { data: open } = await hatang.chat.conversations.list({ status: "open", limit: 20 });
const conversation = open.data[0];

const { data, error } = await hatang.chat.conversations.send(
  conversation.id,
  { content: "Chào anh An, đơn của anh được giao trong hôm nay ạ." },
  { idempotencyKey: `reply-${conversation.id}-1` },
);
```

**Python**

```python
from hatang import Hatang

hatang = Hatang()  # đọc HATANG_API_KEY

open_ = hatang.chat.conversations.list(status="open", limit=20)
conversation_id = open_["data"][0]["id"]

message = hatang.chat.conversations.send(
    conversation_id,
    {"content": "Chào anh An, đơn của anh được giao trong hôm nay ạ."},
    idempotency_key=f"reply-{conversation_id}-1",
)
print(message)
```

**PHP**

```php
<?php
$client = new GuzzleHttp\Client([
    'base_uri' => 'https://api.hatang.dev',
    'headers' => ['Authorization' => 'Bearer ' . getenv('HATANG_API_KEY')],
]);

$open = json_decode((string) $client->get('/chat/conversations', ['query' => ['status' => 'open', 'limit' => 20]])->getBody(), true);
$conversationId = $open['data'][0]['id'];

$res = $client->post("/chat/conversations/{$conversationId}/messages", [
    'headers' => ['Idempotency-Key' => "reply-{$conversationId}-1"],
    'json' => ['content' => 'Chào anh An, đơn của anh được giao trong hôm nay ạ.'],
]);
```

Lệnh gửi tin nhắn trả `201`:

```json
{ "object": "chat_message", "id": "msg_123...", "seq": 7, "conversation_id": "cnv_123...", "duplicate": false }
```

Để phản hồi tin nhắn mới của khách mà không phải hỏi liên tục, đăng ký [webhook](https://hatang.dev/vi/docs/guides/getting-started/webhooks.md) cho sự kiện `chat.message.created`. [Hướng dẫn REST API Hỗ trợ](https://hatang.dev/vi/docs/guides/support/rest-api.md) có thêm cách giao hội thoại, gắn tag, ghi chú nội bộ, khách hàng và đơn hàng.

## 5. Thêm chat vào ứng dụng di động

SDK gốc nói chuyện với cùng gateway như widget, nên tin nhắn từ ứng dụng về cùng hộp thư, và người dùng đã xác minh giữ một lịch sử chung giữa web và di động.

1. Ở **Hỗ trợ → Kênh**, bấm **Thêm kênh**, chọn **Ứng dụng di động**, rồi **Tạo khoá ứng dụng**. Khoá bắt đầu bằng `hak_` và chỉ hiện một lần.
2. Cài SDK cho nền tảng của bạn và cấu hình bằng site id và khoá ứng dụng.
3. Gọi `identify` với cùng user id và `hash` như trên web.
4. Nếu cần, tải thông tin Firebase lên để ứng dụng nhận [thông báo đẩy](https://hatang.dev/vi/docs/guides/sdks/push-notifications.md).

| Nền tảng | Hướng dẫn |
|---|---|
| Flutter | [SDK Flutter](https://hatang.dev/vi/docs/guides/sdks/flutter.md) |
| iOS (Swift) | [SDK iOS](https://hatang.dev/vi/docs/guides/sdks/ios.md) |
| Android (Kotlin) | [SDK Android](https://hatang.dev/vi/docs/guides/sdks/android.md) |
| React Native / Expo | [SDK React Native](https://hatang.dev/vi/docs/guides/sdks/react-native.md) |
| React / Next.js (web) | [Component React](https://hatang.dev/vi/docs/guides/sdks/react.md) |

Khoá ứng dụng không bí mật như API key (nó nằm trong bản build), nhưng bạn thu hồi và cấp khoá mới được bất cứ lúc nào. Đừng bao giờ đưa API key `ht_` hay mã bí mật định danh vào ứng dụng.

## Lỗi thường gặp

| Hiện tượng | Nguyên nhân | Cách xử lý |
|---|---|---|
| Không thấy nút chat, console báo `origin_not_allowed` | Trang không nằm trên tên miền của website | Thêm origin vào **Origin được phép thêm** |
| Không thấy nút chat, báo `product_disabled` | Module Hỗ trợ đang tắt | Nhờ chủ sở hữu hoặc quản trị viên bật lại |
| Không thấy nút chat ở một số trang | Quy tắc hiển thị trên trang website đang ẩn widget ở đó | Kiểm tra cài đặt nơi hiện widget |
| Người dùng hiện **Chưa xác minh** | Thiếu `hash` hoặc `hash` không khớp | Tính từ đúng chuỗi user id, với mã bí mật định danh hiện tại |
| Ứng dụng báo `invalid_app_key` | Khoá sai, hoặc khoá của site khác | Sao chép lại khoá ứng dụng của đúng website |

## Bước tiếp theo

- [Tổng quan Hỗ trợ](https://hatang.dev/vi/docs/guides/support/overview.md): kênh, phân chia hội thoại, phiếu hỗ trợ và trợ lý AI
- [JavaScript API của widget](https://hatang.dev/vi/docs/guides/support/web-widget/javascript-api.md)
- [Nhận diện khách](https://hatang.dev/vi/docs/guides/support/web-widget/identity-verification.md)
- [Tổng quan SDK](https://hatang.dev/vi/docs/guides/sdks/overview.md)

Source: https://hatang.dev/vi/docs/guides/getting-started/quickstart-chat
