0

Vibe code xong web bán hàng? Bảo AI thêm live chat, gom Zalo, Messenger và để bot trả lời khách thay bạn

Bạn dựng một web bán hàng (hay một app nhỏ) bằng Cursor hoặc Claude Code trong vài buổi tối. Sản phẩm chạy, quảng cáo chạy, rồi khách bắt đầu nhắn: ô chat trên web, Zalo OA, Messenger của fanpage. 11 giờ đêm vẫn có người hỏi "còn size M không shop?". Bạn chỉ có một mình.

Bài này dành cho đúng hoàn cảnh đó: không có team CSKH, nhưng có một AI coding agent. Mỗi bước dưới đây là một câu lệnh bạn đưa cho agent, kèm đoạn code nó nên viết ra và những chỗ bạn cần soát lại (vì AI hay sai đúng mấy chỗ đó). Kết quả cuối:

  • Live chat trên web Next.js, nhận ra khách đã đăng nhập.
  • Zalo OA, Messenger, Instagram, TikTok Shop, website đổ về một hộp thư.
  • Bot AI trả lời khách bằng dữ liệu sản phẩm và chính sách của bạn, không bịa.
  • Khi bot không trả lời được, điện thoại bạn nhận tin qua Telegram.
  • Chốt đơn và nhận tiền VietQR ngay trong cuộc chat.

Ví dụ dùng Hạ Tầng, nền tảng mình đang làm. Cách làm (định danh bằng HMAC, xác minh webhook, đồng bộ dữ liệu cho bot) dùng được cho các hệ thống live chat khác.

Vì sao không tự code một ô chat?

Một ô chat WebSocket thì agent viết được trong một buổi. Cái tốn thời gian là mọi thứ xung quanh, và đó là phần bài này không bắt bạn tự làm:

  • Một hộp thư cho mọi kênh: website, app, Zalo OA, Messenger, Instagram, TikTok Shop, WhatsApp, Telegram, LINE, X và email. Một khách nhắn từ nhiều nơi vẫn là một hồ sơ.
  • Bán ngay trong lúc chat: gửi thẻ sản phẩm, lên đơn và gửi mã VietQR trong hội thoại; tiền về là đơn tự chuyển sang đã thanh toán. Bot cũng tự gửi sản phẩm và ghi đơn nháp khi khách chốt.
  • Thấy cả trước và sau cuộc chat: trước khi nhắn khách đã xem trang nào, đến từ quảng cáo nào; sau đó họ mua gì, đánh giá thế nào, lần sau quay lại hỏi gì.
  • Bot càng dùng càng giỏi: câu nào bot không trả lời được được gom lại để bạn trả lời một lần; câu bạn gõ cho khách có thể "dạy" lại cho bot ngay trong hộp thư.
  • Trả lời từ điện thoại: app iPhone, Android và Mac có thông báo đẩy, cho lúc bạn không ngồi trước máy.
  • Chat, email giao dịch, thanh toán VietQR và hoá đơn điện tử trên một nền tảng: ít dịch vụ phải ghép, ít key phải giữ.

Bước 0: Cho agent đọc đúng tài liệu

Lý do số một khiến agent viết sai tích hợp: nó đoán API theo trí nhớ. Hãy cho nó tài liệu thật.

Claude Code: cài MCP server, agent sẽ tự tra tài liệu (không cần key để đọc docs):

claude mcp add hatang -- npx -y @hatang/mcp

Cursor: thêm vào .cursor/mcp.json:

{ "mcpServers": { "hatang": { "command": "npx", "args": ["-y", "@hatang/mcp"] } } }

Rồi thêm luật dự án để agent nhớ những quy tắc quan trọng (key để ở server, xác minh chữ ký...):

curl -s https://hatang.dev/docs/agents/AGENTS.md >> AGENTS.md

Dùng Lovable, Bolt, v0 hay công cụ không cài được MCP? Dán link https://hatang.dev/vi/docs/llms.txt vào prompt và bảo nó đọc trước.

Trên dashboard, tạo website ở Hỗ trợ → Kênh → Thêm kênh → Website để lấy site id (site_...).

Bước 1: Gắn live chat vào web

Prompt: "Thêm widget chat Hạ Tầng vào app Next.js này bằng @hatang/chat-react. Site id để trong NEXT_PUBLIC_HATANG_SITE_ID. Đọc hướng dẫn React trong docs Hạ Tầng trước khi viết."

Agent nên tạo ra gần như thế này:

// 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={process.env.NEXT_PUBLIC_HATANG_SITE_ID!} />
      </body>
    </html>
  );
}

Màu, lời chào, giờ làm việc, form xin số điện thoại trước khi chat: chỉnh trên dashboard, không cần bảo agent sửa code.

Xem trước widget live chat trên website: cửa sổ chat của Mộc Hương Tea với lời chào và ô nhập tin nhắn

Bước 2: Nhận ra khách đã đăng nhập (chỗ AI hay làm sai nhất)

Prompt: "Khi user đã đăng nhập, truyền userId, email, name cho widget Hạ Tầng, kèm hash HMAC-SHA256 của userId tính ở server bằng HATANG_IDENTITY_SECRET. Secret không được lộ ra client."

// app/layout.tsx (Server Component)
import { createHmac } from "node:crypto";
import { HatangChat } from "@hatang/chat-react";
import { getCurrentUser } from "@/lib/auth"; // hàm lấy user của app bạn

export default async function RootLayout({ children }: { children: React.ReactNode }) {
  const user = await getCurrentUser();
  const identity = user && {
    userId: String(user.id),
    email: user.email,
    name: user.name,
    hash: createHmac("sha256", process.env.HATANG_IDENTITY_SECRET!).update(String(user.id)).digest("hex"),
  };
  return (
    <html lang="vi">
      <body>
        {children}
        <HatangChat siteId={process.env.NEXT_PUBLIC_HATANG_SITE_ID!} user={identity ?? undefined} />
      </body>
    </html>
  );
}

Soát lại:

  • HATANG_IDENTITY_SECRET không có tiền tố NEXT_PUBLIC_ và không nằm trong file có "use client". Agent rất hay "sửa lỗi undefined" bằng cách đổi thành NEXT_PUBLIC_, tức là công khai secret.
  • userId truyền cho widget và chuỗi đem đi hash phải giống hệt nhau (cùng String(...)).

Vì sao phải ký: nếu chỉ gửi email từ trình duyệt, ai cũng có thể gõ email của người khác để đọc lịch sử chat của họ. Có hash hợp lệ thì hội thoại được đánh dấu Đã xác minh và gộp với lịch sử của đúng khách đó. Lấy secret ở Hỗ trợ → Kênh → website của bạn → Identity secret.

Bước 3: Gom Zalo, Messenger, Instagram về một hộp thư

Bước này không cần agent: kết nối từng kênh bằng tài khoản chính thức trên dashboard.

Trang Thêm kênh: website, ứng dụng di động, Facebook và Instagram, TikTok Shop, Zalo OA, WhatsApp, Telegram, LINE, X và email

Sau đó mọi tin nhắn về chung một chỗ. Với solo builder, cái lợi lớn nhất là cột bên phải: khách đang xem trang nào, đến từ quảng cáo nào, đã mua gì. Không phải hỏi lại.

Hộp thư hỗ trợ khách hàng: danh sách hội thoại, khung chat và hồ sơ khách gồm trang đang xem, nguồn truy cập, chiến dịch

Prompt (tuỳ chọn): "Khi user áp mã giảm giá hoặc thêm vào giỏ, gọi hatang().pushEvent(...) của @hatang/chat-react để sự kiện hiện trong hộp thư."

"use client";
import { hatang } from "@hatang/chat-react";

hatang().pushEvent("added_to_cart", { product: "tra-sen-100g", price: 120000 });

Bước 4: Cho bot trả lời bằng dữ liệu của bạn

Trợ lý AI chỉ trả lời từ kiến thức bạn đưa vào: FAQ, chính sách, trang web (tự crawl), Google Sheet, Notion, PDF. Với người làm một mình, chế độ hợp nhất là "Trả lời đầu tiên": bot tiếp mọi khách mới, không chắc thì chuyển cho bạn. Hoặc "Khi không ai trực tuyến" nếu bạn chỉ muốn bot trực lúc bạn offline.

Trang Kiến thức của trợ lý AI: các câu hỏi khách hỏi mà AI chưa trả lời được, form thêm FAQ và các nguồn đã lập chỉ mục

Dữ liệu sản phẩm của bạn nằm trong database, nên đừng copy tay. Bảo agent đồng bộ:

Prompt: "Viết script scripts/sync-knowledge.ts đọc toàn bộ sản phẩm và các trang chính sách trong DB, đẩy vào kho kiến thức Hạ Tầng bằng hatang.knowledge.sync (collection products), mỗi sản phẩm một document Markdown có tên, giá, size, tình trạng hàng và link. Chạy sau mỗi lần deploy."

// scripts/sync-knowledge.ts
import { Hatang } from "@hatang/sdk";
import { db } from "@/lib/db"; // DB của app bạn

const hatang = new Hatang(); // HATANG_API_KEY (scope chat:write)

const products = await db.product.findMany();
const { data, error } = await hatang.knowledge.sync(
  "products",
  products.map((p) => ({
    external_id: `product-${p.id}`,
    title: p.name,
    content: `# ${p.name}\n\nGiá: ${p.price.toLocaleString("vi-VN")}đ\nSize: ${p.sizes.join(", ")}\nCòn hàng: ${p.stock > 0 ? "có" : "hết"}\n\n${p.description}`,
    url: `https://shop.example.com/san-pham/${p.slug}`,
    locale: "vi",
  })),
);
if (error) throw new Error(`${error.name}: ${error.message}`);
console.log(data);

sync làm cho collection chứa đúng các document bạn gửi: sản phẩm đã xoá trong DB cũng biến mất khỏi kiến thức của bot. Thử xem bot sẽ tìm thấy gì bằng hatang.knowledge.search("còn size M không").

Hai thứ giúp bot giỏi lên theo thời gian mà không cần code thêm: danh sách "Câu hỏi chưa có đáp án" (những gì bot phải chuyển cho bạn), và nút "Dạy AI từ câu trả lời này" ngay trong hộp thư: bạn trả lời khách một lần, bot nhớ cho lần sau.

Hộp thoại Dạy AI từ câu trả lời này: câu hỏi, câu trả lời và tuỳ chọn thêm vào hướng dẫn cho AI

Bước 5: Bot bí thì báo về Telegram của bạn

Prompt: "Tạo route app/api/hatang/webhooks/route.ts nhận webhook Hạ Tầng, xác minh chữ ký bằng constructWebhookEvent trên raw body. Khi nhận chat.conversation.handoff, gửi tin Telegram cho tôi qua bot (token và chat id trong env) kèm tóm tắt."

// app/api/hatang/webhooks/route.ts
import { constructWebhookEvent, HatangSignatureError } from "@hatang/sdk";

export async function POST(req: Request) {
  const raw = await req.text(); // xác minh trên body gốc, trước khi parse JSON
  let event;
  try {
    event = await constructWebhookEvent(raw, req.headers, process.env.HATANG_WEBHOOK_SECRET!);
  } catch (err) {
    if (err instanceof HatangSignatureError) return new Response(null, { status: 400 });
    throw err;
  }

  if (event.type === "chat.conversation.handoff") {
    const c = event.data as { channel: string; reason: string; summary: string | null };
    await fetch(`https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/sendMessage`, {
      method: "POST",
      headers: { "content-type": "application/json" },
      body: JSON.stringify({ chat_id: process.env.TELEGRAM_CHAT_ID, text: `Khách cần bạn (${c.channel}): ${c.summary ?? c.reason}` }),
    });
  }
  return new Response(null, { status: 200 });
}

Tạo endpoint ở Cài đặt → Webhooks, chọn sự kiện chat.conversation.handoff, copy signing secret (whsec_...) vào HATANG_WEBHOOK_SECRET.

Soát lại: agent hay dùng await req.json() rồi mới xác minh. Sai: chữ ký tính trên body gốc, parse rồi stringify lại là lệch. Phải là req.text() như trên. Và trả 200 nhanh: webhook lỗi sẽ được gửi lại, xử lý nặng thì đưa vào hàng đợi.

Bán hàng ngay trong lúc chat

Phần này cũng không cần code, chỉ cần một danh mục sản phẩm (nhập tay trên dashboard, hoặc bảo agent đồng bộ từ DB như bước 4). Trong hộp thư, nút Sản phẩm gửi thẻ có ảnh, giá và nút mua; nút Đơn hàng lên đơn ngay trong hội thoại; sau đó gửi thẻ thanh toán VietQR với số tiền và nội dung chuyển khoản điền sẵn. Khi tiền về tài khoản của bạn, đơn tự chuyển sang đã thanh toán và khách nhận lời cảm ơn trong chat: không phải mở app ngân hàng để soi.

Hội thoại có thẻ sản phẩm, thẻ đơn hàng và thẻ thanh toán VietQR, bên phải là hồ sơ khách và các đơn đã mua

Bot làm được nửa đầu việc này: gửi sản phẩm với giá lấy đúng từ danh mục, và khi nhận ra khách đã chốt thì ghi một đơn nháp kèm ghi chú nội bộ. Bạn chỉ bấm xác nhận rồi gửi link thanh toán. Bot không tự nhận tiền, không sửa đơn, không hoàn tiền.

Nếu web của bạn đã có checkout riêng, đơn hàng và thanh toán cũng tạo được bằng API (hatang.payments.createOrder), xem bài nhận thanh toán VietQR.

Thấy cả trước và sau cuộc chat

Trước: trang Đang truy cập cho biết ai đang ở trên web ngay lúc này, đang đọc trang nào, đến từ Google, Cốc Cốc hay quảng cáo Facebook nào, quay lại lần thứ mấy. Thấy ai đó ngồi mãi ở trang sản phẩm thì chào trước. Muốn tự động hơn, chiến dịch hiện lời mời chat hoặc form xin số điện thoại khi khách ở trang bảng giá đủ lâu, cuộn nửa trang hay sắp rời đi.

Trang Đang truy cập: khách đang online, trang đang đọc, nguồn truy cập, số lượt ghé và thiết bị

Sau: hồ sơ khách gom mọi thứ theo thời gian: các đơn đã mua và trạng thái thanh toán, hội thoại ở mọi kênh, ghi chú của bạn, nhãn, giai đoạn (mới, đã liên hệ, đủ điều kiện, khách hàng). Khi khách quay lại hỏi "đơn hôm trước của chị đâu rồi em?", bạn (và bot) thấy ngay. Xong hội thoại, khách được mời đánh giá mức độ hài lòng.

Hồ sơ khách hàng: thông tin liên hệ, giai đoạn, người phụ trách, ghi chú và dòng hoạt động gồm đơn hàng và tin nhắn

Dạy bot cách cư xử, không chỉ kiến thức

Kiến thức cho bot biết điều gì đúng; tình huống cho bot biết nên làm gì: khách hỏi giá sỉ thì gọi bạn vào, khách bực bội thì xin lỗi và chuyển người, ngoài giờ thì vẫn trả lời và xin số điện thoại để gọi lại. Trước khi bật cho khách thật, viết vài câu hỏi mẫu kèm câu trả lời mong đợi ở tab Kiểm thử và chạy lại mỗi khi sửa kiến thức, giống viết test cho code.

Checklist trước khi deploy

  • [ ] Không có HATANG_API_KEY, HATANG_IDENTITY_SECRET, HATANG_WEBHOOK_SECRET nào có tiền tố NEXT_PUBLIC_ hay bị import vào client component.
  • [ ] Hash định danh tính ở server, từ đúng chuỗi userId truyền cho widget.
  • [ ] Webhook xác minh chữ ký trên req.text().
  • [ ] Domain của website trên dashboard đúng với domain thật (widget chỉ chạy trên domain đó và các origin bạn cho phép thêm).
  • [ ] Đã chạy thử bot ở tab Kiểm thử với 5–10 câu khách hay hỏi trước khi bật.

Câu hỏi thường gặp

Làm app di động (Flutter, React Native) chứ không phải web thì sao? Cùng hộp thư, cùng bot, có SDK cho Flutter, React Native, iOS, Android. Prompt gần như y hệt, đổi tên gói.

Web làm bằng WordPress, Shopify, Haravan, Sapo, LadiPage? Có plugin WordPress; nơi khác dán hai dòng script:

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

Bot có bịa không? Bot chỉ trả lời từ kiến thức đã lập chỉ mục; không tìm thấy thì chuyển cho bạn (và câu đó vào "Câu hỏi chưa có đáp án") thay vì đoán.

Agent dùng MCP server làm được gì ngoài đọc docs? Có API key (HATANG_API_KEY) thì agent còn đọc được hội thoại, tìm trong kiến thức, gửi email, tạo thanh toán VietQR thử. Thêm --read-only nếu chỉ muốn cho nó tra cứu. Xem MCP server.

Tài liệu


Mình đang làm ở Hạ Tầng. Code lấy từ tài liệu chính thức và SDK hiện tại.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí