Widget trên website
Widget là khung chat của bot ngay trên website của bạn: một nút tròn ở góc trang, mở ra cuộc trò chuyện. Với khách truy cập, đó là thêm một cách để liên hệ; với bot, đó là một kênh bình thường — cùng các phản ứng và cùng các hội thoại của nhân viên như trong messenger. Tổng quan về đa kênh: Kênh.
Cài đặt chỉ là dán một đoạn mã vào mẫu giao diện website. Mọi thứ còn lại được cấu hình trong bảng điều khiển và áp dụng mà không cần động vào website nữa.
Lấy mã nhúng ở đâu
Trong bảng điều khiển, mở Widget → khối Cài đặt lên website → trường Mã để chèn. Nút Sao chép đưa toàn bộ đoạn mã vào bộ nhớ tạm.
Mã trông như sau, với khóa của riêng bạn thay cho dấu ba chấm:
<script>
window.mybot = { key: "eu-1a2b3c4d-..." };
</script>
<script async src="https://getmybot.dev/loader.js"></script>
Nếu thay vì mã bạn thấy dòng chữ rằng khóa cài đặt cho bot này chưa được cấp, nghĩa là kênh widget chưa được kết nối. Hãy liên hệ hỗ trợ: khóa được cấp khi kết nối kênh.
Bên dưới mã có gợi ý widget sẽ kết nối tới máy chủ nào. Nếu website của bạn có CSP (Content Security Policy), hãy cho phép địa chỉ đó tải script và nhận yêu cầu mạng, nếu không trình duyệt sẽ âm thầm chặn widget.
Đặt mã ở đâu
Đoạn mã được đặt vào HTML của mọi trang cần hiển thị widget. Trên thực tế: một lần duy nhất vào mẫu chung của website — chân trang, ô «mã trước </body>» trong CMS, hoặc một container trong trình quản lý thẻ.
Quy tắc rất ngắn:
- Chỗ tốt nhất là ngay trước thẻ
</body>đóng. Đặt trong<head>cũng chạy, nhưng khi đó trình duyệt dành thời gian cho widget trước nội dung của bạn. - Thứ tự hai thẻ rất quan trọng: thẻ đầu đặt khóa, thẻ sau tải widget. Đừng đảo chỗ và đừng tách rời trong trang.
- Thẻ thứ hai có
asyncnên không chặn việc dựng trang. Đừng bỏ thuộc tính này. - Mỗi trang một đoạn mã. Hai bản sao nghĩa là hai lần thử khởi động.
Từ đó bộ tải sẽ tự xác định bot của bạn được phục vụ từ trung tâm dữ liệu nào và lấy mã widget từ đó. Bạn không cần làm gì thêm.
Khóa cài đặt là công khai
Khóa trong thẻ đầu là định danh công khai của bot, không phải mật khẩu. Nó nằm trong mã nguồn trang: bất kỳ khách nào cũng có thể mở «Xem nguồn trang» và đọc được. Mọi widget chat đều hoạt động như vậy, và điều đó là bình thường.
Điều quan trọng:
- Với khóa này không thể đăng nhập bảng điều khiển, đọc hội thoại của người khác, xuất danh sách người đăng ký hay thay đổi bất cứ điều gì ở bot. Nó mở đúng một khả năng: bắt đầu cuộc trò chuyện mới với bot này.
- Đừng coi khóa là bí mật: giấu đi hay làm rối nó chẳng ích gì.
- Nếu cần thay khóa (ví dụ sau khi chia tay một nhà thầu), bộ phận hỗ trợ sẽ làm. Sau khi thay, mã cũ trên website ngừng hoạt động và phải cập nhật.
Danh sách tên miền được phép
Widget có danh sách tên miền được phép (origin allowlist) — các địa chỉ website mà widget được phép hoạt động. Danh sách được đặt khi kết nối kênh và có thể thay đổi sau đó mà không cần cấp lại khóa: mã trên website của bạn giữ nguyên.
Mỗi mục là một origin: giao thức, tên máy chủ và cổng nếu không phải cổng chuẩn.
https://example.com
https://www.example.com
https://shop.example.com
http://localhost:3000
Việc đối chiếu là chính xác, từng ký tự:
- Không có ký tự đại diện.
https://*.example.comsẽ không hoạt động — hãy liệt kê từng tên miền phụ. example.comvàwww.example.comlà hai mục khác nhau. Nếu website mở được ở cả hai, hãy thêm cả hai.http://vàhttps://cũng là hai mục khác nhau. Thường chỉ cầnhttps://, nhưng môi trường thử nghiệm trênhttp://phải thêm rõ ràng.- Không có đường dẫn:
https://example.com/shopsẽ bị từ chối; origin chỉ là địa chỉ website.
Nếu tên miền thật của bạn không có trong danh sách, widget sẽ không khởi động. Trình duyệt của khách nhận được từ chối «origin not allowed» và khung chat đơn giản là không mở ra. Điều này thường xuất hiện sau khi chuyển sang tên miền mới, thêm tên miền phụ hoặc mở phiên bản ngôn ngữ thứ hai ở địa chỉ riêng — mỗi trường hợp cần một mục riêng.
Nếu danh sách rỗng thì không có giới hạn nào và widget chạy từ bất kỳ đâu. Hãy coi đó là «chưa cấu hình», không phải mở cửa có chủ ý: khi đã biết tên miền của mình, hãy ghi vào.
Giới hạn này bảo vệ điều gì và không bảo vệ điều gì
Có bảo vệ: website khác không thể nhúng widget của bạn. Nếu không, ai đó có thể chép đoạn mã vào trang của họ và trình duyệt của khách thật sẽ giao nội dung cuộc trò chuyện với bot của bạn cho website đó. Danh sách đóng đúng lỗ hổng này.
Không bảo vệ: đây không phải là hàng rào chống người đã chép khóa rồi gọi thẳng tới nền tảng — không qua trình duyệt mà bằng script chẳng hạn. Tiêu đề chứa địa chỉ website do trình duyệt đặt; chương trình chạy ngoài trình duyệt có thể bỏ qua hoặc gửi bất kỳ giá trị nào. Vì vậy hãy xem danh sách là giới hạn nhúng, không phải ranh giới bảo mật. Bảo vệ trước lạm dụng là giới hạn tần suất và kiểm duyệt hội thoại phía nền tảng, không phải danh sách này.
Giao diện
Trong Widget → Giao diện có thể chỉnh:
- Vị trí — nút ở góc dưới bên trái hoặc bên phải.
- Màu nhấn — màu của nút và các phần tử chat. Hãy dùng màu thương hiệu để widget không lạc lõng.
- Tên khung chat — nhãn trên nút và tiêu đề bảng chat. Thường là tên công ty hoặc tên bot tự giới thiệu.
Bên cạnh có xem trước — một widget sống hiển thị kết quả ngay trước khi lưu.
Widget chưa có tin nhắn chào: chỉ ba tùy chỉnh này. Tin nhắn đầu tiên do bot gửi để đáp lại khách, theo các phản ứng thông thường của bạn.
Thay đổi tự đến với website: widget đọc cấu hình khi tải trang, mã đã dán không bao giờ cần sửa.
Khách truy cập là ẩn danh
Khách không phải điền gì để nhắn tin. Ở lần khởi động đầu tiên, widget nhận từ nền tảng một định danh khách ẩn danh và lưu vào trình duyệt, để khi quay lại họ thấy cuộc trò chuyện cũ chứ không phải khung chat trống.
Hệ quả:
- Trong hội thoại, khách như vậy hiển thị ẩn danh: không tên, không số điện thoại, không email cho tới khi chính họ viết ra.
- Định danh sống trong một trình duyệt cụ thể. Trình duyệt khác, thiết bị khác hay dữ liệu website bị xóa đều là một khách mới với lịch sử trắng.
- Khách ẩn danh có thể được liên kết với khách hàng bạn đã biết, ví dụ người dùng đã đăng nhập khu vực thành viên. Cách làm nằm ở phần tiếp theo.
Liên kết khách truy cập với người dùng của bạn
Nếu khách đã đăng nhập trên website của bạn, bạn có thể cho nền tảng biết đó là ai. Sau khi liên kết, cuộc trò chuyện thôi ẩn danh: nó được gộp vào hồ sơ của người đó, và những tin nhắn ẩn danh trước đây không bị mất.
Chỉ cần một lời gọi, kèm một hàm tạo ra bằng chứng:
mybot.identify(async (visitorId) => {
const res = await fetch("/mybot-sign", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ userId: currentUser.id, visitorId }),
});
return res.json(); // { userId, signature, expiresAt }
});
Widget gọi hàm của bạn kèm visitorId — định danh khách ẩn danh hiện tại cho trình duyệt này, giá trị duy nhất chỉ widget biết — và chờ nó trả về, hoặc resolve thành, { userId, signature, expiresAt }. Nhiệm vụ duy nhất của hàm bạn là chuyển visitorId cho máy chủ của chính bạn, cùng với định danh của người dùng đã đăng nhập (currentUser.id ở trên là bất cứ thứ gì tương ứng trên trang của bạn), và chuyển lại đúng những gì máy chủ của bạn trả về. Việc ký thực sự diễn ra trên máy chủ của bạn, không phải trong hàm này — xem bên dưới.
Một hàm callback, không phải giá trị đã tính sẵn hay cách đọc trực tiếp visitorId, vì hai lý do:
- Thời điểm tồn tại. Widget tạo
visitorIdkhông đồng bộ trong lúc khởi động — nó chưa tồn tại ngay khi script tải chạy, và bản thânmybot.identifychỉ được cài đặt sau khi quá trình đó hoàn tất (chi tiết bên dưới). Vì vậy, đến khi trang của bạn có thể gọimybot.identify, định danh mà hàm của bạn nhận được chắc chắn là thật. Một getter thông thường sẽ không có đảm bảo đó: không gì ngăn một trang đọc nó sớm hơn một dòng và không nhận được gì, lặng lẽ tạo ra chữ ký không bao giờ khớp mà chẳng có manh mối nào cho biết lý do. - Phạm vi. Mã trên trang của bạn không bao giờ phải giữ, lưu trữ hay truyền tay
visitorId— nó chỉ tồn tại bên trong hàm duy nhất này, cho đúng một lần gọi mà hàm cần thực hiện.
Kể từ khi rời khỏi widget, visitorId là một khả năng mang theo (bearer capability): bất kỳ ai lấy được chữ ký cho nó đều có thể được gộp vào phiên của khách đó. Chỉ gửi nó tới máy chủ của chính bạn, qua yêu cầu đã xác thực của riêng bạn, và không nơi nào khác — đừng ghi log nó, chuyển tiếp cho bên thứ ba, hay đặt nó trong một lời gọi phân tích phía client.
Chữ ký chỉ được tính trên chính máy chủ của bạn
Bên trong hàm của bạn ở trên, chính máy chủ của bạn — không bao giờ là trình duyệt — tính chữ ký: HMAC-SHA256, khóa bằng khóa bí mật của widget, ở dạng thập lục phân, 32 ký tự đầu, trên ba giá trị ghép thành một thông điệp. Cùng công thức đó được hiển thị trong bảng điều khiển cạnh khóa bí mật, dưới mục «Signing algorithm»:
signature = hex(HMAC-SHA256(key: secret, message: userId + "\n" + visitorId + "\n" + expiresAt)).slice(0, 32)
Mỗi + "\n" + ở trên là một ký tự xuống dòng thật sự giữa các phần, không phải hai ký tự dấu gạch chéo ngược và n. Nếu hàm HMAC của bạn nhận thông điệp dưới dạng một chuỗi duy nhất, hãy nối ba phần bằng một dấu xuống dòng thật — ba lời gọi .update() riêng lẻ mà không có dấu xuống dòng giữa chúng sẽ băm một thông điệp khác, sai.
Ba phần:
userId— định danh của người dùng đã đăng nhập, cùng giá trị mà máy chủ của bạn nhận từ hàm ở trên và trả lại trong phản hồi của nó.visitorId— định danh khách ẩn danh mà hàm của bạn nhận được dưới dạng tham số và chuyển tiếp nguyên vẹn cho máy chủ của bạn.expiresAt— một mốc thời gian unix tính bằng giây (không phải mili giây), cho đến khi chữ ký cụ thể này hết hiệu lực, do máy chủ của bạn tạo ra khi ký. Nền tảng từ chối lời gọi cóexpiresAtđã ở trong quá khứ, và cả lời gọi cóexpiresAtquá 24 giờ trong tương lai — hãy ký ngay trước khi trả về từ hàm của bạn, đừng ký một lần rồi lưu lại dùng cho các yêu cầu sau.
Thời hạn đó chính là trọng tâm của thay đổi này, không phải chi tiết phụ. Công thức trước chỉ bao gồm định danh người dùng, nên một chữ ký bị lộ dù chỉ một lần — bị ghi log đâu đó, bị chặn trên đường truyền, bằng cách nào cũng được — vẫn có giá trị mãi mãi và khớp với bất kỳ khách nào, không chỉ người mà nó được cấp cho. Ai lấy được chữ ký đó có thể phát lại nó trên một trình duyệt hoàn toàn khác, và nền tảng sẽ gộp lịch sử duyệt web ẩn danh của một người lạ vào hồ sơ của một khách hàng thật. Gắn chữ ký với một visitorId cụ thể và cho nó thời gian sống ngắn khép lại cả hai đầu của lỗ hổng đó: chữ ký chỉ có hiệu lực cho phiên mà nó được cấp, không nơi nào khác, và ngừng có hiệu lực hoàn toàn khi expiresAt trôi qua — nên ngay cả một chữ ký bị rò rỉ cũng chỉ là rủi ro ngắn hạn, giới hạn trong một phiên, không phải mối nguy lâu dài.
Hãy tính cả ba giá trị trên máy chủ của bạn và trả về từ hàm của bạn ở dạng đã tính sẵn. Đây không phải chuyện hình thức: muốn tính chữ ký trong trình duyệt thì phải đưa chính khóa bí mật xuống đó, tức là trao nó cho mọi khách vào trang. Sau đó bất kỳ ai cũng có thể mạo danh bất kỳ khách hàng nào của bạn và đọc cuộc trò chuyện của họ. Trả chữ ký (và visitorId/expiresAt mà nó được tính cho) về trình duyệt thì an toàn, đưa khóa bí mật thì không.
Bản thân lời gọi không hiển thị gì trên màn hình: việc liên kết diễn ra ở phía máy chủ và lặng lẽ. Nếu hàm của bạn ném lỗi hoặc promise của nó bị từ chối, hoặc nền tảng từ chối chữ ký thu được — không khớp, hoặc expiresAt bị thiếu, đã qua, hoặc quá 24 giờ trong tương lai — thì không có gì khác xảy ra: khách không nhận thấy gì và tiếp tục nhắn với tư cách ẩn danh.
mybot.identify không có ngay lập tức — nó xuất hiện sau khi gói mã chính của widget tải xong, cùng với một visitorId thật (xem «Thời điểm tồn tại» ở trên). Hãy gọi từ trình xử lý sự kiện tải trang, đừng đặt ở dòng đầu trong <head>.
Lấy khóa bí mật ở đâu và thay thế nào
Khóa bí mật widget là khóa bạn dùng để ký. Nó nằm trong phần cài đặt widget, ở mục «identify() secret». Nếu mục đó báo widget chưa được kết nối, hãy kết nối kênh web trước — khóa bí mật xuất hiện cùng với nó.
Xem. Nút «Reveal secret» lấy giá trị về. Giá trị không hiện ra dạng rõ: trước hết bạn thấy các dấu chấm, và một công tắc riêng «Show» hiển thị ký tự («Mask» che lại). Bên cạnh có nút «Clear from screen» xóa giá trị khỏi màn hình.
Bạn có thể xem bao nhiêu lần tùy ý, và việc xem không vô hiệu hóa gì cả: khóa bí mật vẫn còn hiệu lực sau khi bạn đóng màn hình. Sở dĩ vậy vì nó được lưu ở dạng mã hóa chứ không băm — nền tảng cần giá trị thật để kiểm tra từng chữ ký. Nếu nửa năm nữa bạn triển khai backend mới, cứ vào xem lại.
Sao chép xong thì tự dọn. Nền tảng không thể xóa bộ nhớ tạm của bạn: từ trình duyệt không làm được việc đó một cách đáng tin, nên chúng tôi cũng không hứa. Hãy xóa giá trị khỏi màn hình và tự làm sạch bộ nhớ tạm khi xong việc, nhất là trên máy dùng chung.
Thay. Nút «Rotate secret», có bước xác nhận. Đây là hành động làm hỏng ngay lập tức: ngay khi khóa mới tồn tại, khóa cũ ngừng xác thực. Không có khoảng chồng lấn — không có thời điểm nào cả hai khóa cùng hiệu lực.
Thứ bị hỏng chính là việc liên kết khách với người dùng của bạn. Các cuộc trò chuyện không bị gián đoạn — khách vẫn nhắn và vẫn được trả lời, chỉ là với tư cách ẩn danh — cho tới khi backend của bạn ký bằng khóa mới. Mã cài đặt và danh sách tên miền được phép không bị ảnh hưởng; trên website không phải sửa gì.
Vì vậy hãy thay khóa một cách có chủ đích: chuẩn bị bản triển khai backend với giá trị mới trước, rồi mới bấm «Rotate secret». Bấm thử xem chuyện gì xảy ra là một ý tồi. Giá trị mới hiện ngay trên màn hình sau khi thay, nên bạn có thể sao chép luôn.
Widget hoạt động thế nào trên trang
Widget được thiết kế có chủ đích để không va chạm với website của bạn:
- Nó sống trong vùng chứa tách biệt (Shadow DOM đóng). CSS của bạn không lọt vào trong, còn CSS của widget không lọt ra trang. Hệ quả phụ: bạn không thể đổi kiểu widget bằng CSS của mình — hãy dùng phần tùy chỉnh giao diện.
- Vùng chứa phủ toàn màn hình nhưng không chặn click: click xuyên qua tới trang, chỉ nút và bảng chat phản hồi.
- Widget luôn nằm trên nội dung của bạn; các lớp của bạn không che được nó.
- Mọi lỗi bên trong widget đều ở lại bên trong: tệ nhất là nút chat không hoạt động, website của bạn vẫn chạy. Nếu widget không xuất hiện, hãy xem console trình duyệt — hầu như luôn là CSP hoặc tên miền thiếu trong danh sách.
Nếu widget không xuất hiện
Kiểm tra theo thứ tự:
- Mở mã nguồn trang và chắc chắn cả hai thẻ đều có và khóa không rỗng.
- Kiểm tra địa chỉ website đúng y như trong danh sách, kể cả
https://vàwww. - Xem console trình duyệt: thông báo về Content Security Policy nghĩa là phải cho phép địa chỉ của nền tảng trong CSP của website.
- Chắc chắn trình chặn quảng cáo không gỡ widget — thử ở cửa sổ ẩn danh không tiện ích mở rộng.
Tiếp theo
- Kênh — đa kênh và khả năng của từng kênh.
- Phản ứng: cơ bản — bot sẽ trả lời khách như thế nào.
- Cuộc trò chuyện và người điều hành — nhân viên trả lời trong hội thoại ra sao.