Với WhatsApp Flows, bạn có thể xây dựng tin nhắn tương tác để người dùng hoàn tất hành động ngay trên WhatsApp. Flow hỗ trợ bạn tạo màn hình để tương tác với người dùng. Ví dụ: Bạn có thể tạo mẫu nhập đơn giản để thu thập dữ liệu khách hàng tiềm năng hoặc gửi bài đánh giá. Bên cạnh đó, bạn có thể thiết kế Flow phức tạp trên nhiều màn hình để đặt lịch hẹn.
Hướng dẫn này sẽ giới thiệu cho bạn từng bước xây dựng ứng dụng Node.js nhằm hỗ trợ người dùng đặt lịch hẹn thông qua WhatsApp Flows. Bạn sẽ tạo Flow trên Nền tảng WhatsApp Business, sau đó đặt cấu hình webhook để nhận câu trả lời của Flow và đặt lịch hẹn.
Để làm theo hướng dẫn này, hãy đảm bảo bạn:
Có hai cách để tạo WhatsApp Flow: Công cụ tạo Flow, truy cập được qua Trình quản lý WhatsApp và API Flow. Hướng dẫn này sử dụng Công cụ tạo Flow.
Trên menu bên trái của bảng điều khiển Trình quản lý WhatsApp, chọn Công cụ trong tài khoản. Sau đó, nhấp vào Flow.
Nhấp vào Tạo Flow ở góc trên cùng bên phải.
Trong hộp thoại xuất hiện, điền chi tiết cho Flow cuộc hẹn:
Nhấp vào Gửi để tạo Flow.
Bạn có thể xem trước Flow ở bên phải giao diện người dùng Công cụ tạo. Màn hình cuộc hẹn hỗ trợ người dùng chọn chi tiết cuộc hẹn, chẳng hạn như vị trí và ngày. Màn hình chi tiết là nơi người dùng nhập thông tin của họ. Màn hình tóm tắt hiển thị nội dung tóm tắt việc đặt lịch hẹn. Màn hình cuối cùng hiển thị điều khoản của công ty.
Flow vẫn ở trạng thái bản nháp khi bạn chỉnh sửa. Lúc này, bạn chỉ có thể chia sẻ Flow đó với đội ngũ của mình cho mục đích thử nghiệm. Để chia sẻ với đối tượng lớn hơn, bạn phải đăng Flow. Tuy nhiên, bạn không thể chỉnh sửa Flow đã đăng. Vì bạn vẫn phải thêm URL điểm cuối cho Flow cuộc hẹn này nên bây giờ cứ giữ trạng thái bản nháp và tiến hành bước tiếp theo để đặt cấu hình điểm cuối.
WhatsApp Flows tạo điều kiện cho bạn kết nối với điểm cuối bên ngoài. Điểm cuối này có thể cung cấp dữ liệu động cho Flow và kiểm soát việc định tuyến. Điểm cuối này cũng nhận câu trả lời mà người dùng gửi từ Flow.
Vì mục đích thử nghiệm, bài viết này sử dụng Glitch để lưu trữ điểm cuối. Việc sử dụng Glitch là hoàn toàn tùy chọn, không bắt buộc thực hiện để sử dụng Flow. Bạn có thể sao chép mã điểm cuối từ GitHub và chạy mã này trong bất kỳ môi trường nào bạn muốn.
Truy cập mã điểm cuối trong Glitch và phối lại mã này để lấy miền duy nhất của bạn. Để phối lại, hãy nhấp vào Remix ở đầu trang. Miền duy nhất sẽ xuất hiện dưới dạng phần giữ chỗ trong thành phần nhập ở bên phải trang Glitch.
Trước khi tiến hành, hãy xem xét chi tiết mã nói trên. Có bốn file JavaScript trong thư mục src
: encryption.js
, flow.js
, keyGenerator.js
và server.js
. File mục nhập là server.js
, vậy nên hãy xem xét file này trước.
File server.js
bắt đầu bằng cách đặt cấu hình ứng dụng Express để sử dụng phần mềm trung gian express.json
nhằm phân tích cú pháp yêu cầu JSON nhận được. Sau đó, file này tải các biến môi trường cần thiết cho điểm cuối.
const { APP_SECRET, PRIVATE_KEY, PASSPHRASE, PORT = "3000" } = process.env;
APP_SECRET
được sử dụng để xác minh chữ ký. Biến này hỗ trợ bạn kiểm tra xem tin nhắn có đến qua WhatsApp hay không, nghĩa là có an toàn để xử lý hay không. Bạn sẽ thêm biến này vào file .env
.
Để truy cập APP_SECRET
, hãy đi đến bảng điều khiển trên ứng dụng trên Meta for Developers. Trong bảng điều hướng bên trái bên dưới Cài đặt ứng dụng, chọn Cơ bản. Nhấp vào Hiển thị bên dưới Khóa bí mật của ứng dụng và sao chép khóa bí mật. Sau đó, trở lại Glitch, mở file .env
và tạo biến có tên APP_SECRET
với giá trị là khóa bí mật mà bạn đã sao chép.
PRIVATE_KEY
hỗ trợ bạn giải mã tin nhắn nhận được. PASSPHRASE
sẽ được sử dụng để xác minh khóa riêng tư. Cùng với khóa riêng tư, bạn cũng cần có khóa công khai tương ứng mà sau này bạn sẽ tải lên. Đừng bao giờ sử dụng khóa riêng tư cho tài khoản sản xuất ở đây. Hãy tạo khóa riêng tư tạm thời để thử nghiệm trên Glitch, rồi thay thế bằng khóa sản xuất trong cơ sở hạ tầng của riêng bạn.
Tạo cặp khóa công khai - riêng tư bằng cách chạy lệnh dưới đây trong cửa sổ dòng lệnh Glitch. Thay thế <your-passphrase>
bằng cụm mật khẩu được chỉ định của bạn. Truy cập cửa sổ dòng lệnh Glitch bằng cách nhấp vào tab TERMINAL ở cuối trang.
node src/keyGenerator.js <your-passphrase>
Sao chép cụm mật khẩu và khóa riêng tư rồi dán vào file .env
. Nhấp vào file có nhãn .env trên thanh bên bên trái, rồi nhấp vào ✏️ Văn bản thuần túy ở trên cùng. Đừng chỉnh sửa trực tiếp trong UI, vì việc đó sẽ làm hỏng định dạng khóa.
Sau khi đặt biến môi trường, hãy sao chép khóa công khai đã tạo và tải khóa công khai lên qua API Đồ thị.
File server.js
cũng chứa điểm cuối POST
thực hiện nhiều bước khác nhau:
if (!PRIVATE_KEY) { throw new Error('Khóa riêng tư đang trống. Vui lòng kiểm tra biến env "PRIVATE_KEY".'); }
isRequestSignatureValid
tìm thấy ở phía dưới cùng của file: if(!isRequestSignatureValid(req)) { // Trả về mã trạng thái 432 nếu chữ ký yêu cầu không khớp. // Để tìm hiểu thêm về mã lỗi trả về, hãy truy cập: https://developers.facebook.com/docs/whatsapp/flows/reference/error-codes#endpoint_error_codes return res.status(432).send(); }
decryptRequest
tìm thấy trong file encryption.js
: let decryptedRequest = null; try { decryptedRequest = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); } catch (err) { console.error(err); if (err instanceof FlowEndpointException) { return res.status(err.statusCode).send(); } return res.status(500).send(); } const { aesKeyBuffer, initialVectorBuffer, decryptedBody } = decryptedRequest; console.log("💬 Yêu cầu đã giải mã:", decryptedBody);
getNextScreen
sau này. const screenResponse = await getNextScreen(decryptedBody);
console.log("👉 Phản hồi cần mã hóa:", screenResponse);
res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer));
File này chứa logic để mã hóa và giải mã tin nhắn được trao đổi vì mục đích bảo mật. Nội dung hướng dẫn ở đây không tập trung vào việc xử lý file này.
File này hỗ trợ tạo khóa riêng tư và khóa công khai, như bạn thấy ở trên. Như với file encryption.js
, hướng dẫn này không khám phá chi tiết file keyGenerator.js
.
Logic dùng để xử lý Flow được lưu trữ trong file này. Việc đó bắt đầu với đối tượng được chỉ định tên là SCREEN_RESPONSES
. Đối tượng này chứa ID màn hình với chi tiết tương ứng, chẳng hạn như dữ liệu đặt sẵn được sử dụng trong quá trình trao đổi dữ liệu. Đối tượng này được tạo từ Công cụ tạo Flow ở phần "..." > Điểm cuối > Đoạn mã > Phản hồi. Trong cùng đối tượng đó, bạn cũng có ID khác là SUCCESS
, được gửi trở lại thiết bị máy khách khi Flow được hoàn tất thành công. Việc này sẽ đóng Flow.
Hàm getNextScreen
chứa logic hướng dẫn điểm cuối về việc hiển thị dữ liệu Flow nào cho người dùng. Việc này bắt đầu bằng cách trích xuất dữ liệu cần thiết từ tin nhắn đã giải mã.
const { screen, data, version, action, flow_token } = decryptedBody;
Điểm cuối trong WhatsApp Flows thường nhận ba yêu cầu:
data_exchange
data.error
ping
Bạn có thể tìm thấy chi tiết về những yêu cầu này trong tài liệu về điểm cuối.
Hàm nói trên xử lý việc kiểm tra sự cố và thông báo lỗi bằng cách sử dụng câu lệnh if
và phản hồi tương ứng, như được thể hiện trong đoạn mã dưới đây:
// xử lý yêu cầu kiểm tra sự cố if (action === "ping") { return { version, data: { status: "active", }, }; } // xử lý thông báo lỗi if (data?.error) { console.warn("Lỗi máy khách nhận được:", data); return { version, data: { acknowledged: true, }, }; }
Khi người dùng nhấp vào nút kêu gọi hành động (CTA) của Flow, hành động INIT
sẽ kích hoạt. Hành động này trả về màn hình cuộc hẹn cùng với dữ liệu. Hành động này cũng vô hiệu hóa trường thông tin thả xuống dành cho vị trí, ngày và giờ để đảm bảo người dùng điền tất cả các trường.
Ví dụ: Trường thông tin thả xuống dành cho ngày chỉ được bật khi trường thông tin thả xuống dành cho vị trí được điền. Việc bật và vô hiệu hóa các trường thông tin được xử lý khi yêu cầu data_exchange
được nhận.
// xử lý yêu cầu ban đầu khi mở flow và hiển thị màn hình APPOINTMENT if (action === "INIT") { return { ...SCREEN_RESPONSES.APPOINTMENT, data: { ...SCREEN_RESPONSES.APPOINTMENT.data, // những trường này ban đầu bị vô hiệu hóa. Mỗi trường sẽ được bật khi các trường trước đó được chọn is_location_enabled: false, is_date_enabled: false, is_time_enabled: false, }, }; }
Đối với hành động data_exchange
, cấu trúc switch case được sử dụng để xác định dữ liệu nào cần gửi trở về dựa trên ID màn hình. Nếu ID màn hình là APPOINTMENT
thì các trường thông tin thả xuống chỉ được bật khi những trường thông tin thả xuống trước đó được chọn.
// Mỗi trường chỉ được bật khi các trường trước đó được chọn is_location_enabled: Boolean(data.department), is_date_enabled: Boolean(data.department) && Boolean(data.location), is_time_enabled: Boolean(data.department) && Boolean(data.location) && Boolean(data.date)
Đối với màn hình DETAILS, tiêu đề của các thuộc tính đối tượng dữ liệu như vị trí và bộ phận được trích xuất từ đối tượng SCREEN_RESPONSES.APPOINTMENT.data
. Mã này giả định là có đối tượng khớp hợp lệ, nên hãy lưu ý rằng mã có thể báo lỗi nếu không tìm thấy đối tượng khớp.
Bây giờ, hãy lấy một phiên bản của đối tượng vị trí. Việc lựa chọn đối tượng vị trí cụ thể được xác định bằng cách so khớp thuộc tính id
của đối tượng trong mảng với giá trị của data.location
.
const departmentName = SCREEN_RESPONSES.APPOINTMENT.data.department.find( (dept) => dept.id === data.department ).title; const locationName = SCREEN_RESPONSES.APPOINTMENT.data.location.find( (loc) => loc.id === data.location ).title; const dateName = SCREEN_RESPONSES.APPOINTMENT.data.date.find( (date) => date.id === data.date
).title;
Sau đó, các giá trị được ghép nối và trả về trong phản hồi để kết xuất màn hình SUMMARY.
const appointment = `${departmentName} at ${locationName} ${dateName} at ${data.time}`; const details = `Name: ${data.name} Email: ${data.email} Phone: ${data.phone} "${data.more_details}"`; return { ...SCREEN_RESPONSES.SUMMARY, data: { appointment, details, // trả về những trường giống như được gửi từ máy khách để gửi trong bước tiếp theo ...data, }, };
Sau khi màn hình SUMMARY được gửi từ máy khách, phản hồi thành công được gửi đến thiết bị máy khách để đánh dấu Flow là hoàn tất. flow_token
là mã nhận dạng duy nhất mà bạn có thể đặt khi gửi Flow đến người dùng.
// gửi phản hồi thành công để hoàn tất và đóng flow return { ...SCREEN_RESPONSES.SUCCESS, data: { extension_message_response: { params: { flow_token, }, }, }, };
Màn hình TERMS không có dữ liệu để trao đổi, nên điểm cuối không xử lý.
Ở trên cùng bên phải trang Glitch, bạn có thể sao chép URL bằng cách nhấp vào biểu tượng menu ba chấm và chọn Sao chép liên kết. Bạn cũng có thể lấy liên kết bằng cách nhấp vào Chia sẻ ở trên cùng bên phải.
Đi đến Công cụ chỉnh sửa Flow. Nhấp vào Thiết lập trong biểu ngữ màu nâu xuất hiện ở đầu công cụ chỉnh sửa.
Một cửa sổ bật lên sẽ xuất hiện để bạn đặt cấu hình URI điểm cuối, số điện thoại doanh nghiệp và ứng dụng trên Meta for Developers. Sau khi đặt những cấu hình cần thiết, hãy tiến hành kiểm tra sự cố. Trước tiên, chạy bản xem trước tương tác và nhớ chọn Yêu cầu dữ liệu bên dưới Yêu cầu dữ liệu trên màn hình đầu tiên trong phần cài đặt bản xem trước tương tác. Thao tác này gửi yêu cầu đến điểm cuối để lấy dữ liệu cho màn hình đầu tiên, xác minh rằng điểm cuối có sẵn và bạn đã tiến hành kiểm tra sự cố.
Sau đó, đăng Flow bằng cách nhấp vào menu ba chấm (...) và chọn Đăng. Thao tác này sẽ gửi yêu cầu kiểm tra sự cố đến điểm cuối với action === "ping"
để xác minh điểm cuối được thiết lập trước khi đăng.
Sau khi hoàn tất việc đặt cấu hình, hãy bật bản xem trước tương tác một lần nữa trong giao diện người dùng Công cụ tạo WhatsApp để thử nghiệm Flow. Trong cửa sổ bật lên xuất hiện, chọn số điện thoại và chọn tùy chọn Yêu cầu dữ liệu bên dưới Yêu cầu dữ liệu trên màn hình đầu tiên. Đóng Flow bằng cách nhấp vào biểu tượng X để bắt đầu thử nghiệm Flow từ đầu từ nút CTA.
Mở nhật ký Glitch bằng cách nhấp vào tab LOGS. Xóa sạch nhật ký bằng cách nhấp vào Clear. Sau đó, trở lại bản xem trước trong giao diện người dùng Công cụ tạo WhatsApp. Nhấp vào Xem trước flow. Bạn sẽ thấy giao diện như sau:
Bây giờ, trở lại nhật ký Glitch. Bạn sẽ thấy hành động INIT
, mã Flow và các chi tiết khác bên dưới yêu cầu đã giải mã. Cũng có phản hồi cần giải mã được gửi về lại Flow người dùng khi trường thông tin thả xuống dành cho bộ phận được chọn.
Tiến hành chọn bộ phận. Lưu ý việc is_location_enabled
được đặt thành true
và hành động được thay đổi thành data_exchange
.
Tiếp tục thử nghiệm Flow và theo dõi sự thay đổi dữ liệu trong nhật ký Glitch. Những nhật ký tương tự sẽ được tạo khi người dùng tương tác với Flow từ thiết bị di động.
Trong phần tiếp theo, bạn sẽ tạo webhook gửi tin nhắn xác nhận đến người dùng khi họ đặt lịch hẹn.
Khi người dùng hoàn tất Flow, tin nhắn đánh dấu việc hoàn tất Flow được gửi đến webhook đã đăng ký. Từ webhook này, bạn sẽ thông báo cho người dùng về việc đặt lịch hẹn thành công bằng một tin nhắn trong đoạn chat. Tương tự như với điểm cuối, bạn cũng sẽ sử dụng Glitch để thử nghiệm. Bạn có thể truy cập mã và phối lại mã tại đây.
Việc sử dụng Glitch là hoàn toàn tùy chọn - không bắt buộc thực hiện để sử dụng Flow. Bạn có thể sao chép mã webhook từ GitHub và chạy mã này trong bất kỳ môi trường nào bạn muốn.
Để đặt các biến môi trường, hãy mở file .env
trên Glitch. Đặt VERIFY_TOKEN
thành bất kỳ chuỗi nào bạn muốn, FLOW_ID
bằng ID của Flow và GRAPH_API_TOKEN
thành mã truy cập của tài khoản WhatsApp Business. Bạn có thể lấy mã truy cập từ bảng điều khiển của ứng dụng trên Meta for Developers khi bạn nhấp vào Thiết lập API bên dưới phần WhatsApp trên bảng điều hướng bên trái.
Trên trang hiển thị, nhấp vào nút Sao chép bên dưới thẻ Mã truy cập tạm thời. Dán khóa đó vào file .env
.
Trong tài khoản của bạn trên Meta for Developers, nhấp vào menu Cấu hình bên dưới WhatsApp trong bảng điều hướng bên trái.
Trong thẻ Webhook, nhấp vào Chỉnh sửa. Trong hộp thoại mở ra, dán URL Glitch đã sao chép và nối thêm /webhook
vào đó trong trường thông tin URL gọi lại. Đối với trường thông tin Xác minh mã, thêm mã từ biến VERIFY_TOKEN
trong file .env
. Khi hoàn tất, nhấp vào Xác minh và lưu. Hộp thoại sẽ đóng và bạn trở lại màn hình chính. Nhấp vào Quản lý và kiểm tra trường tin nhắn. Webhook của bạn giờ đã sẵn sàng.
Mã này có hai lộ trình: POST /webhook
và GET /webhook
. Lộ trình GET
xử lý yêu cầu xác minh webhook bằng cách đối chiếu mã được cung cấp với mã xác minh được định trước và phản hồi bằng mã trạng thái thích hợp và mã thử thách.
const verify_token = process.env.VERIFY_TOKEN; // Phân tích cú pháp các thông số từ yêu cầu xác minh webhook let mode = req.query["hub.mode"]; let token = req.query["hub.verify_token"]; let challenge = req.query["hub.challenge"]; if (mode && token) { if (mode === "subscribe" && token === verify_token) { console.log("WEBHOOK_VERIFIED"); res.status(200).send(challenge); } else { res.sendStatus(403); } }
Lộ trình POST /webhook
xử lý thông báo webhook nhận được. Yêu cầu webhook có thể có phần tải dữ liệu khác nhau. Do đó, mã dưới đây đọc tin nhắn và số điện thoại của doanh nghiệp bằng cách truy cập các trường thông tin yêu cầu một cách an toàn trong trường hợp những thông tin này chưa được xác định.
const message = req.body.entry?.[0]?.changes[0]?.value?.messages?.[0]; const business_phone_number_id =
req.body.entry?.[0].changes?.[0].value?.metadata?.phone_number_id;
Sau đó, mã kiểm tra xem yêu cầu nhận được có phải là cho tin nhắn thuộc loại "text"
chứa từ ngữ "hẹn" hay không. Nếu tin nhắn chứa từ ngữ này thì Flow được gửi đến người dùng. Tin nhắn Flow được gửi với flow_action: "data_exchange
,"
nghĩa là Flow sẽ đưa ra yêu cầu INIT
cho điểm cuối khi được khởi chạy để lấy dữ liệu và màn hình ban đầu.
if ( message.type === "text" && // cho mục đích minh họa, gửi tin nhắn flow bất cứ khi nào người dùng gửi tin nhắn chứa "hẹn" message.text.body.toLowerCase().includes("hẹn") ) { // gửi tin nhắn flow theo tài liệu ở đây https://developers.facebook.com/docs/whatsapp/flows/gettingstarted/sendingaflow#interactive-message-parameters await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, type: "interactive", interactive: { type: "flow", header: { type: "text", text: "Chào bạn 👋", }, body: { text: "Bạn sẵn sàng biến đổi không gian của mình chưa? Hãy đặt lịch tham gia buổi tư vấn dành riêng cho bạn với đội ngũ chuyên gia của chúng tôi!", }, footer: { text: "Nhấp vào nút dưới đây để tiến hành", }, action: { name: "flow", parameters: { flow_id: FLOW_ID, flow_message_version: "3", // thay thế flow_token bằng mã nhận dạng duy nhất cho tin nhắn flow này để theo dõi trong điểm cuối & webhook flow_token: "<FLOW_TOKEN_PLACEHOLDER>", flow_cta: "Đặt lịch hẹn", flow_action: "data_exchange", }, }, }, }, }); } ...
Nếu loại tin nhắn nhận được không phải là "text"
thì mã sẽ kiểm tra xem loại tin nhắn có phải là "interactive
"
hay không. Loại tương tác "nfm_reply"
biểu thị rằng tin nhắn nhận được là phản hồi Flow. Sau đó, mã gửi lại tin nhắn "Bạn đã đặt lịch hẹn thành công" cho người dùng.
... if ( message.type === "interactive" && message.interactive?.type === "nfm_reply" ) { // gửi tin nhắn xác nhận await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, text: { body: "Bạn đã đặt lịch hẹn thành công" }, }, }); } ...
Sau đó, mã sẽ đánh dấu tin nhắn nhận được là đã đọc để người dùng thấy dấu tích xanh.
... // đánh dấu tin nhắn nhận được là đã đọc await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", status: "read", message_id: message.id, }, }); ...
Trong ví dụ này, người dùng gửi tin nhắn đến số của bạn với từ ngữ "hẹn", sau đó nhận được tin nhắn Flow. Bạn cũng có thể chọn gửi Flow sau tương tác khác hoặc dưới dạng mẫu tin nhắn.
Người dùng sẽ nhận được tin nhắn Flow với nút CTA khi đặt lịch hẹn. Ở đó, họ có thể điền các chi tiết của mình. Sau đó, họ sẽ nhận được tin nhắn xác nhận khi hoàn tất Flow.
Trong hướng dẫn này, bạn học được cách thiết lập WhatsApp Flow để đặt lịch hẹn liền mạch. Sử dụng giao diện người dùng Công cụ tạo Flow, bạn đã tạo mẫu để thu thập chi tiết cuộc hẹn từ người dùng.
Flow loại bỏ nhu cầu chuyển hướng người dùng đến trang web bên ngoài để đặt lịch hẹn, từ đó nâng cao trải nghiệm khách hàng. Quy trình đơn giản này tạo điều kiện cho người dùng hoàn tất việc đặt lịch hẹn ngay trong WhatsApp. Ngoài việc đặt lịch hẹn, bạn có thể sử dụng WhatsApp Flow để thu thập ý kiến đóng góp về dịch vụ khách hàng hoặc hỗ trợ người dùng đăng ký danh sách gửi email hoặc nhận thông tin về chương trình khuyến mãi. WhatsApp Flow cũng mang lại khả năng linh hoạt để kết nối với API bên ngoài hoặc ứng dụng khác trong điểm cuối của bạn.
WhatsApp Flow rất dễ tạo bằng giao diện người dùng Công cụ tạo Flow. Tuy nhiên, bạn cũng có thể sử dụng API Flow để xây dựng Flow bằng cách lập trình. Để biết thêm thông tin, hãy tham khảo Tài liệu WhatsApp Flows.