命名應用程序并選擇與其關(guān)聯(lián)的頁面
選擇 WhatsApp 設(shè)置

在此屏幕上,選擇WhatsApp并單擊其設(shè)置按鈕。

然后您將進入一個新屏幕,如下所示。

入門頁面

在此屏幕上,請注意:

請注意,臨時訪問令牌將在 24 小時后過期,屆時我們需要更新它。當您的應用程序進入實時模式后,可以申請獲取永久訪問令牌。但由于我們的應用程序目前仍處于開發(fā)模式,因此這一步并非必需。

電話號碼 ID 和 WhatsApp 企業(yè)帳戶 ID 與測試電話號碼綁定。

接下來,我們添加一個用于接收消息的電話號碼。

在開發(fā)模式下,為了防范垃圾郵件和濫用行為,Meta對我們設(shè)置了限制,即只能向五個收件人號碼發(fā)送信息。在實時/生產(chǎn)模式下,該數(shù)字代表我們客戶的電話號碼。

單擊選擇收件人電話號碼并添加您自己的 WhatsApp 號碼,如下圖所示:

添加收件人電話號碼對話框

添加收件人號碼后,您將看到如下所示的屏幕。如果這是您第一次將電話號碼添加到 Meta 平臺(例如 Facebook Pages、Meta Business 套件或 Meta 開發(fā)人員儀表板),您將收到來自 Facebook Business 的 OTP 消息,提示您驗證您是否確實擁有收件人號碼。

使用 API 發(fā)送消息

測試我們的設(shè)置

讓我們測試一下到目前為止這一步是否一切順利。我們將通過單擊“發(fā)送消息”按鈕來完成此操作。

如果一切順利,您應該會在 WhatsApp 收件箱中看到一條來自您的測試號碼的消息。

WhatsApp 收件箱中來自 Facebook 的 Hello World 消息

到目前為止,我們進行得很順利!暫停一下并打開代碼編輯器。請勿關(guān)閉瀏覽器選項卡,因為我們將在幾分鐘后返回 Meta Developer 儀表板。

第 2 步:設(shè)置 webhook 來接收消息

現(xiàn)在我們的設(shè)置已經(jīng)能夠成功發(fā)送消息了,接下來讓我們來設(shè)置一種接收消息的方式。是時候親自動手,深入編寫代碼了。我們?yōu)楸窘坛叹帉懙乃写a都位于此 GitHub 存儲庫中。

創(chuàng)建一個新文件夾來包含我們的項目。在終端中打開此文件夾并運行以下腳本:

npm init ---yes

接下來,我們安裝一些軟件包:

npm install express pdfkit request whatsappcloudapi_wrapper
npm install nodemon --dev

以下是每項的簡要說明:

接下來,我們將創(chuàng)建三個文件:

  1. ./app.js
  2. ./.env.js
  3. ./routes/index.js

在我們的./.env.js文件中,輸入以下代碼:

const production = {
...process.env,
NODE_ENV: process.env.NODE_ENV || 'production',
};

const development = {
...process.env,
NODE_ENV: process.env.NODE_ENV || 'development',
PORT: '9000',
Meta_WA_accessToken:'EAAKGUD3eZA28BADAJOmO6L19TmZAIEUpdFGHEGHX5sQ3kk4LDQLlnyh607rKSr0x2SAOPJS0kXOUZAhRDIPPGs4vcXQCo2DnfSJMnnIzFW7vaw8EuL7A0HjGZBwE8VwjRnBNam0ARLmfyOCEh1',
Meta_WA_SenderPhoneNumberId: '113362588047543',
Meta_WA_wabaId: '102432872486730',
Meta_WA_VerifyToken: 'YouCanSetYourOwnToken',
};

const fallback = {
...process.env,
NODE_ENV: undefined,
};

module.exports = (environment) => {
console.log(Execution environment selected is: "${environment}"); if (environment === 'production') { return production; } else if (environment === 'development') { return development; } else { return fallback; } };

在同一個./.env.js文件中:

  1. Meta_WA_accessToken的值替換為您的元應用程序的臨時訪問令牌
  2. Meta_WA_SenderPhoneNumberId的值替換為您的電話號碼 ID
  3. Meta_WA_wabaId的值替換為您的 WhatsApp Business 帳戶 ID
  4. 為您的Meta_WA_VerifyToken設(shè)定一個專屬的值,這個值可以是字符串或數(shù)字。在后續(xù)的webhook設(shè)置步驟中,您將會看到我們?nèi)绾芜\用這個值。

上述代碼首先導入了當前的環(huán)境變量并進行了解構(gòu)處理,接著添加了新的環(huán)境變量,最終將這兩部分組合起來導出為一個對象。

在文件./app.js文件中,插入以下代碼:

process.env = require('./.env.js')(process.env.NODE_ENV || 'development');
const port = process.env.PORT || 9000;
const express = require('express');

let indexRoutes = require('./routes/index.js');

const main = async () => {
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use('/', indexRoutes);
app.use('*', (req, res) => res.status(404).send('404 Not Found'));
app.listen(port, () =>
console.log(App now running and listening on port ${port}) ); }; main();

上面代碼塊的第一行只是導入./.env.js文件并將其分配給process.env,這是 Node.js 中的全局可訪問對象。

在文件中./routes/index.js,插入以下代碼:

'use strict';
const router = require('express').Router();

router.get('/meta_wa_callbackurl', (req, res) => {
try {
console.log('GET: Someone is pinging me!');

let mode = req.query['hub.mode'];
let token = req.query['hub.verify_token'];
let challenge = req.query['hub.challenge'];

if (
mode &&
token &&
mode === 'subscribe' &&
process.env.Meta_WA_VerifyToken === token
) {
return res.status(200).send(challenge);
} else {
return res.sendStatus(403);
}
} catch (error) {
console.error({error})
return res.sendStatus(500);
}
});

router.post('/meta_wa_callbackurl', async (req, res) => {
try {
console.log('POST: Someone is pinging me!');
return res.sendStatus(200);
} catch (error) {
console.error({error})
return res.sendStatus(500);
}
});
module.exports = router;

接下來,打開終端并運行:

nodemon app.js

Express 服務(wù)器將在端口 9000 上運行。接下來,打開另一個單獨的終端并運行:

ngrok http 9000

此命令的作用是將我們的Express應用程序?qū)ν忾_放,使其能夠被更廣泛的互聯(lián)網(wǎng)用戶所訪問。這里的目標是設(shè)置一個 WhatsApp Cloud 可以 ping 通的 Webhook。

記下 ngrok 分配給您的 Express 服務(wù)器的 URL。在我的示例中,ngrok為我提供了一個如下的URL:https://7b9b-102-219-204-54.ngrok.io。請確保Express服務(wù)器和ngrok終端都處于運行狀態(tài)。

接下來,讓我們在 Meta Developer 儀表板中繼續(xù)我們的工作。滾動到標題為“配置 Webhooks 以接收消息”的部分,然后單擊“配置 Webhooks”。該鏈接將顯示一個類似于下面屏幕截圖的頁面:

Webhook 配置頁面

單擊“編輯”按鈕,將顯示一個彈出窗口。

“回調(diào) URL”字段中,粘貼 ngrok 向您發(fā)出的 URL,并將其附加到回調(diào)路由,如./routes/index.js指令中所示。在本例中,我的完整 URL 是https://7b9b-102-219-204-54.ngrok.io/meta_wa_callbackurl

驗證令牌字段中,輸入文件中顯示的Meta_WA_VerifyToken的值./.env.js

在驗證令牌字段中輸入值

然后點擊驗證并保存

如果您配置良好,您將console.log在 Express 服務(wù)器的終端中看到一條消息:

GET: Someone is pinging me!

配置我們的 Express 服務(wù)器

現(xiàn)在,讓我們的 Express 服務(wù)器接收來自 Meta 的訂閱消息。

在同一個 Meta Developers 儀表板屏幕上,單擊“管理”,將出現(xiàn)一個彈出窗口。

管理 Express 服務(wù)器訂閱消息彈出窗口

選擇消息并單擊位于同一行的測試。

您應該console.log在 Express 服務(wù)器的終端中看到一條消息:

POST: Someone is pinging me!

如果您看到此消息,請返回同一彈出窗口,然后單擊同一消息行中的“訂閱” 。然后,單擊“完成”

第三步:編寫我們的業(yè)務(wù)邏輯

配置電子商務(wù)數(shù)據(jù)源

首先,我們將設(shè)置相關(guān)邏輯,以便從FakeStore API獲取數(shù)據(jù),進而生成PDF格式的發(fā)票,并創(chuàng)建一個虛擬的訂單提貨地點。我們將把這個邏輯包裝到一個 JavaScript 類中,然后將其導入到應用程序的邏輯中。

創(chuàng)建一個文件并命名./utils/ecommerce_store.js。在此文件中,粘貼以下代碼:

'use strict';
const request = require('request');
const PDFDocument = require('pdfkit');
const fs = require('fs');

module.exports = class EcommerceStore {
constructor() {}
async _fetchAssistant(endpoint) {
return new Promise((resolve, reject) => {
request.get(
https://fakestoreapi.com${endpoint ? endpoint : '/'}, (error, res, body) => { try { if (error) { reject(error); } else { resolve({ status: 'success', data: JSON.parse(body), }); } } catch (error) { reject(error); } } ); }); } async getProductById(productId) { return await this._fetchAssistant(/products/${productId}); } async getAllCategories() { return await this._fetchAssistant('/products/categories?limit=100'); } async getProductsInCategory(categoryId) { return await this._fetchAssistant( /products/category/${categoryId}?limit=10 ); } generatePDFInvoice({ order_details, file_path }) { const doc = new PDFDocument(); doc.pipe(fs.createWriteStream(file_path)); doc.fontSize(25); doc.text(order_details, 100, 100); doc.end(); return; } generateRandomGeoLocation() { let storeLocations = [ { latitude: 44.985613, longitude: 20.1568773, address: 'New Castle', }, { latitude: 36.929749, longitude: 98.480195, address: 'Glacier Hill', }, { latitude: 28.91667, longitude: 30.85, address: 'Buena Vista', }, ]; return storeLocations[ Math.floor(Math.random() * storeLocations.length) ]; } };

上面的代碼中,我們創(chuàng)建了一個名為EcommerceStore的類。

第一個方法_fetchAssistant用于向fakestoreapi.com的特定端點發(fā)送ping請求。

以下方法充當?shù)谝粋€方法的查詢構(gòu)建器:

  1. getProductById接收產(chǎn)品 ID,然后獲取與該特定產(chǎn)品相關(guān)的數(shù)據(jù)
  2. getAllCategories獲取fakestoreapi.com中的所有類別
  3. getProductsInCategory接收一個產(chǎn)品類別,然后繼續(xù)獲取該特定類別中的所有產(chǎn)品

這些查詢構(gòu)建器將調(diào)用第一個方法。

接著,generatePDFInvoice這個方法會接收一段文本信息以及一個文件保存路徑作為輸入?yún)?shù)。然后,它創(chuàng)建一個 PDF 文檔,在其上寫入文本,然后將該文檔存儲在提供的文件路徑中。

該方法generateRandomGeoLocation只是返回一個隨機地理位置。當我們需要把商店的訂單提貨位置信息發(fā)送給打算前來提貨的客戶時,這個方法將會非常實用。

配置客戶會話

為了管理客戶的整個購物流程,我們需要維護一個會話,其中包含客戶的個人信息及其購物車內(nèi)容。這樣,每位客戶都會擁有一個專屬的會話。

在生產(chǎn)中,我們可以使用 MySQLMongoDB 或其他彈性數(shù)據(jù)庫,但為了保持我們的教程精簡和簡短,我們將使用ES2015 的Map數(shù)據(jù)結(jié)構(gòu)。通過Map,我們可以存儲和檢索特定的、可迭代的數(shù)據(jù),例如唯一的客戶數(shù)據(jù)。

在您的./routes/index.js文件中,在 router.get('/meta_wa_callbackurl', (req, res)上方添加以下代碼。

const EcommerceStore = require('./../utils/ecommerce_store.js');
let Store = new EcommerceStore();
const CustomerSession = new Map();

router.get('/meta_wa_callbackurl', (req, res) => {//this line already exists. Add the above lines

第一行導入EcommerceStore類,第二行初始化它。第三行創(chuàng)建客戶的會話,我們將用它來存儲客戶的旅程。

初始化我們的 WhatsApp Cloud API

還記得我們之前安裝的whatsappcloudapi_wrapper軟件包嗎?現(xiàn)在是時候?qū)⑵鋵氩⑦M行初始化了。

在該./routes/index.js文件中,在 Express 路由器聲明下方添加以下代碼行:

const router = require('express').Router(); // This line already exists. Below it add  the following lines:

const WhatsappCloudAPI = require('whatsappcloudapi_wrapper');
const Whatsapp = new WhatsappCloudAPI({
accessToken: process.env.Meta_WA_accessToken,
senderPhoneNumberId: process.env.Meta_WA_SenderPhoneNumberId,
WABA_ID: process.env.Meta_WA_wabaId,
});

以下值是我們在./.env.js文件中定義的環(huán)境變量:

我們使用上面的三個值初始化類 WhatsAppCloudAPI 并命名我們的實例Whatsapp

接下來,我們來解析通過POST方法發(fā)送到/meta_wa_callbackurl這個Webhook的所有數(shù)據(jù)。通過解析請求正文,我們將能夠提取消息和其他詳細信息,例如發(fā)件人的姓名、發(fā)件人的電話號碼等。

請注意:我們從此時起所做的所有代碼編輯都將完全在./routes/index.js文件中進行。

在語句try{的左括號下方添加以下代碼行:

try { // This line already exists. Add the below lines

let data = Whatsapp.parseMessage(req.body);

if (data?.isMessage) {
let incomingMessage = data.message;
let recipientPhone = incomingMessage.from.phone; // extract the phone number of sender
let recipientName = incomingMessage.from.name;
let typeOfMsg = incomingMessage.type; // extract the type of message (some are text, others are images, others are responses to buttons etc...)
let message_id = incomingMessage.message_id; // extract the message id
}

現(xiàn)在,當客戶向我們發(fā)送消息時,我們的 Webhook 應該會收到它。該消息包含在 Webhook 的請求正文中。為了從請求正文中提取出有用的信息,我們需要將這份正文內(nèi)容傳遞給parseMessageWhatsApp實例的相應方法進行處理。

然后,使用if語句檢查該方法的結(jié)果是否包含有效的 WhatsApp 消息。

在語句內(nèi)if,我們定義incomingMessage,其中包含消息。我們還定義了其他變量:

到目前為止,一切似乎都在順利進行中,但我們很快就會進行確認。

了解并響應客戶的意圖

由于我們的教程不會深入探討任何形式的人工智能或自然語言處理 (NLP),因此我們將用簡單的if…else邏輯定義聊天流程。

當客戶發(fā)送短信時,對話邏輯開始。我們暫時不會深入查看消息的具體內(nèi)容,因此也無法確切知道用戶打算做什么。但我們可以向用戶介紹我們的機器人具備哪些功能。

讓我們?yōu)榭蛻籼峁┮粋€簡單的上下文,他們可以根據(jù)特定的意圖進行回復。我們會給客戶兩個按鈕:

  1. 讓我們知道他們想與真人交談,而不是聊天機器人
  2. 另一個瀏覽產(chǎn)品

為此,請在message_id下面插入以下代碼:

if (typeOfMsg === 'text_message') {
await Whatsapp.sendSimpleButtons({
message: Hey ${recipientName}, \nYou are speaking to a chatbot.\nWhat do you want to do next?, recipientPhone: recipientPhone, listOfButtons: [ { title: "'View some products'," id: 'see_categories', }, { title: "'Speak to a human'," id: 'speak_to_human', }, ], }); }

上面的if語句僅允許我們處理文本消息。

sendSimpleButtons方法允許我們向客戶發(fā)送按鈕。記下titleid屬性。這個title是客戶將會看到的內(nèi)容,我們會利用它來判斷客戶點擊了哪一個帶有特定id的按鈕。

讓我們來檢查一下,看看我們是否做得正確。打開您的 WhatsApp 應用程序并向 WhatsApp 企業(yè)帳戶發(fā)送短信。

向 WhatsApp 企業(yè)帳戶發(fā)送短信

如果您收到如上面的屏幕截圖所示的回復,那么恭喜您!您剛剛通過 WhatsApp Cloud API 發(fā)送了第一條消息。

由于客戶可以單擊兩個按鈕中的任何一個,因此我們還要處理“與人工交談”按鈕。

if邏輯語句之外,插入以下代碼:

if (typeOfMsg === 'simple_button_message') {
let button_id = incomingMessage.button_reply.id;

if (button_id === 'speak_to_human') {
await Whatsapp.sendText({
recipientPhone: recipientPhone,
message: Arguably, chatbots are faster than humans.\nCall my human with the below details:, }); await Whatsapp.sendContact({ recipientPhone: recipientPhone, contact_profile: { addresses: [ { city: 'Nairobi', country: 'Kenya', }, ], name: { first_name: 'Daggie', last_name: 'Blanqx', }, org: { company: 'Mom-N-Pop Shop', }, phones: [ { phone: '+1 (555) 025-3483', }, { phone: '+254712345678', }, ], }, }); } };

上面的代碼執(zhí)行兩個操作:

  1. sendText使用方法發(fā)送短信告訴用戶他們將收到聯(lián)系人卡片
  2. sendContact使用該方法發(fā)送名片

此代碼還使用用戶單擊的按鈕的 ID(在我們的示例中,ID 是incomingMessage.button_reply.id)來檢測用戶的意圖,然后用兩個操作選項進行響應。

現(xiàn)在,返回 WhatsApp 并點擊與人交談。如果您操作正確,您將看到如下所示的回復:

發(fā)送

當您單擊收到的聯(lián)系人卡片時,您應該會看到以下內(nèi)容:

聯(lián)系卡顯示全名、公司和兩個電話號碼

接下來,我們來處理“查看某些產(chǎn)品”按鈕。

simple_button_messageif語句內(nèi)部,但在該if語句的下方、且在speak_to_humanif語句外部,請?zhí)砑右韵麓a:

if (button_id === 'see_categories') {
let categories = await Store.getAllCategories();
await Whatsapp.sendSimpleButtons({
message: We have several categories.\nChoose one of them., recipientPhone: recipientPhone, listOfButtons: categories.data .map((category) => ({ title: "category," id: category_${category}, })) .slice(0, 3) }); }

這是上面代碼的作用:

  1. if語句確保用戶單擊“查看某些產(chǎn)品”按鈕
  2. FakeStoreAPI通過getAllCategories方法獲取產(chǎn)品類別
  3. 使用數(shù)組方法將按鈕數(shù)量限制為三個 — slice(0,3)— 因為 WhatsApp 只允許我們發(fā)送三個簡單的按鈕
  4. 然后,它會遍歷每一個類別,為每個類別創(chuàng)建一個帶有唯一ID(該ID以”category_”為前綴)和對應標題(atitle)的按鈕。
  5. 通過該方法,我們將這些按鈕發(fā)送給客戶

再次返回您的 WhatsApp 應用程序并點擊查看更多產(chǎn)品。如果您正確執(zhí)行了上述步驟,您應該會看到類似于下面屏幕截圖的回復:

發(fā)送

按類別獲取產(chǎn)品

現(xiàn)在,讓我們創(chuàng)建邏輯來獲取客戶選擇的類別中的產(chǎn)品。

仍在simple_button_messageif語句內(nèi)部,但在該語句的下方、且在see_categoriesif語句外部,添加以下代碼:

if (button_id.startsWith('category_')) {
let selectedCategory = button_id.split('category_')[1];
let listOfProducts = await Store.getProductsInCategory(selectedCategory);

let listOfSections = [
{
title: "?? Top 3: ${selectedCategory}.substring(0,24)," rows: listOfProducts.data .map((product) => { let id = product_${product.id}.substring(0,256); let title = product.title.substring(0,21); let description = ${product.price}\n${product.description}.substring(0,68); return { id, title: "${title}...," description: "${description}..." }; }).slice(0, 10) }, ]; await Whatsapp.sendRadioButtons({ recipientPhone: recipientPhone, headerText: #BlackFriday Offers: ${selectedCategory}, bodyText: Our Santa ???? has lined up some great products for you based on your previous shopping history.\n\nPlease select one of the products below:, footerText: 'Powered by: BMI LLC', listOfSections, }); }

上面的語句確認客戶單擊的按鈕確實是包含類別的按鈕。

我們在這里做的第一件事是從按鈕的 ID 中提取特定類別。隨后,我們向FakeStoreAPI發(fā)起查詢,以搜索屬于該特定類別的產(chǎn)品。

查詢后,我們收到數(shù)組內(nèi)的產(chǎn)品列表listOfProducts.data。現(xiàn)在,我們循環(huán)遍歷該數(shù)組,對于其中的每個產(chǎn)品,我們提取其價格、標題、描述和 ID。

我們附加product_id,這將幫助我們在下一步中選擇客戶的選擇。請確保根據(jù)WhatsApp Cloud API對于單選按鈕(或列表)的ID、標題和描述的長度限制,對它們進行適當?shù)牟眉艋蛘{(diào)整。

然后我們返回三個值:ID、標題和描述。由于 WhatsApp 最多只允許 10 行,因此我們將使用數(shù)組方法將產(chǎn)品數(shù)量限制為 10 行。

之后,我們調(diào)用該sendRadioButtons方法將產(chǎn)品發(fā)送給客戶。記下屬性headerTextbodyTextfooterTextlistOfSections

返回 WhatsApp 應用程序并單擊任意產(chǎn)品類別。如果您嚴格按照說明進行操作,那么您應該會收到一個與下面屏幕截圖相似的回復:

選擇電子產(chǎn)品類別并接收回復

當您單擊“選擇產(chǎn)品”時,您應該看到以下屏幕:

在這個時刻,客戶可以挑選他們感興趣的產(chǎn)品,但我們能否知曉他們的選擇呢?答案是目前還不能。因此,接下來讓我們一同探究這部分內(nèi)容。

在該simple_button_message if語句之外,我們再添加一條if語句:

if (typeOfMsg === 'radio_button_message') {
let selectionId = incomingMessage.list_reply.id; // the customer clicked and submitted a radio button

}

在上述if語句內(nèi)的正下方selectionId,添加以下代碼:

if (selectionId.startsWith('product_')) {
let product_id = selectionId.split('_')[1];
let product = await Store.getProductById(product_id);
const { price, title, description, category, image: imageUrl, rating } = product.data;

let emojiRating = (rvalue) => {
rvalue = Math.floor(rvalue || 0); // generate as many star emojis as whole number ratings
let output = [];
for (var i = 0; i < rvalue; i++) output.push('?');
return output.length ? output.join('') : 'N/A';
};

let text = _Title_: *${title.trim()}*\n\n\n; text += _Description_: ${description.trim()}\n\n\n; text += _Price_: ${price}\n; text += _Category_: ${category}\n; text += ${rating?.count || 0} shoppers liked this product.\n; text += _Rated_: ${emojiRating(rating?.rate)}\n; await Whatsapp.sendImage({ recipientPhone, url: imageUrl, caption: text, }); await Whatsapp.sendSimpleButtons({ message: Here is the product, what do you want to do next?, recipientPhone: recipientPhone, listOfButtons: [ { title: "'Add to cart??'," id: add_to_cart_${product_id}, }, { title: "'Speak to a human'," id: 'speak_to_human', }, { title: "'See more products'," id: 'see_categories', }, ], }); }

上面的代碼執(zhí)行以下操作:

  1. 從客戶單擊的單選按鈕中提取產(chǎn)品 ID
  2. 使用該產(chǎn)品 ID 查詢 FakeStoreAPI
  3. 當它接收并提取產(chǎn)品數(shù)據(jù)時,它會格式化文本。 WhatsApp 使用下劃線以斜體顯示文本,而星號以粗體顯示文本
  4. 使用該函數(shù)渲染星星表情符號emojiRating。如果評分為 3.8,則會呈現(xiàn)三顆星表情符號
  5. sendImage方法會將產(chǎn)品的圖像作為附件添加到要發(fā)送的文本中,并通過相應的方式發(fā)送出去。

之后,我們使用 sendSimpleButtons向客戶發(fā)送三個按鈕的列表。客戶有機會將產(chǎn)品添加到購物車。記下前綴為 add_to_cart的按鈕 ID 。

現(xiàn)在,返回您的 WhatsApp 應用程序并選擇一個產(chǎn)品。如果您嚴格按照說明進行操作,那么您應該會收到一個與下面屏幕截圖相似的回復:

聊天機器人向客戶發(fā)送三個可選按鈕

構(gòu)建會話來存儲客戶購物車

為了追蹤客戶添加到購物車中的商品,我們需要一個存儲空間來保存這些購物車物品的信息。這就是CustomerSession發(fā)揮作用的地方。讓我們?yōu)槠涮砑右恍┻壿嫛?/p>

在該radio_button_message if語句之外的聲明下方message_id,添加以下代碼:

let message_id = incomingMessage.message_id; // This line already exists. Add the below lines...

// Start of cart logic
if (!CustomerSession.get(recipientPhone)) {
CustomerSession.set(recipientPhone, {
cart: [],
});
}

let addToCart = async ({ product_id, recipientPhone }) => {
let product = await Store.getProductById(product_id);
if (product.status === 'success') {
CustomerSession.get(recipientPhone).cart.push(product.data);
}
};

let listOfItemsInCart = ({ recipientPhone }) => {
let total = 0;
let products = CustomerSession.get(recipientPhone).cart;
total = products.reduce(
(acc, product) => acc + product.price,
total
);
let count = products.length;
return { total, products, count };
};

let clearCart = ({ recipientPhone }) => {
CustomerSession.get(recipientPhone).cart = [];
};
// End of cart logic

if (typeOfMsg === 'text_message') { ... // This line already exists. Add the above lines...

上述代碼會檢查是否已經(jīng)為客戶創(chuàng)建了會話。如果尚未創(chuàng)建,則會根據(jù)客戶的電話號碼為其生成一個新的、唯一的會話。然后我們初始化一個名為 的屬性cart,它最初是一個空數(shù)組。

addToCart函數(shù)接受 一個product_id和特定客戶的號碼。然后,它對 FakeStoreAPI 執(zhí)行 ping 操作以獲取特定產(chǎn)品的數(shù)據(jù),并將產(chǎn)品推送到cart數(shù)組中。

然后,該listOfItemsInCart函數(shù)接收客戶的電話號碼并檢索關(guān)聯(lián)的cart,用于計算購物車中的產(chǎn)品數(shù)量及其價格總和。最后,它返回購物車中的商品及其總價。

clearCart函數(shù)接收客戶的電話號碼并清空該客戶的購物車。完成購物車邏輯后,讓我們構(gòu)建“添加到購物車”按鈕。

在該simple_button_messageif語句內(nèi)部,并且在聲明button_id的下方,添加以下代碼:

if (button_id.startsWith('add_to_cart_')) {
let product_id = button_id.split('add_to_cart_')[1];
await addToCart({ recipientPhone, product_id });
let numberOfItemsInCart = listOfItemsInCart({ recipientPhone }).count;

await Whatsapp.sendSimpleButtons({
message: Your cart has been updated.\nNumber of items in cart: ${numberOfItemsInCart}.\n\nWhat do you want to do next?, recipientPhone: recipientPhone, listOfButtons: [ { title: "'Checkout ???'," id: checkout, }, { title: "'See more products'," id: 'see_categories', }, ], }); }

上面的代碼從客戶單擊的按鈕中提取產(chǎn)品 ID,然后調(diào)用該addToCart函數(shù)將產(chǎn)品保存到客戶會話的購物車中。然后,它提取客戶會話購物車中的商品數(shù)量,并告訴客戶他們有多少產(chǎn)品。它還提供了兩個按鈕供用戶選擇,其中一個按鈕允許用戶進行結(jié)賬操作。

記下按鈕 ID 并返回您的 WhatsApp 應用程序。單擊添加到購物車。如果您很好地遵循了說明,您應該會看到類似于以下屏幕截圖的回復:

添加到購物車

現(xiàn)在我們的客戶已經(jīng)可以把商品添加到購物車了,接下來我們可以著手編寫結(jié)賬的邏輯。

編寫結(jié)賬邏輯

simple_button_message if語句內(nèi)部但在add_to_cart_ if語句外部添加以下代碼:

if (button_id === 'checkout') {
let finalBill = listOfItemsInCart({ recipientPhone });
let invoiceText = List of items in your cart:\n; finalBill.products.forEach((item, index) => { let serial = index + 1; invoiceText += \n#${serial}: ${item.title} @ ${item.price}; }); invoiceText += \n\nTotal: ${finalBill.total}; Store.generatePDFInvoice({ order_details: invoiceText, file_path: ./invoice_${recipientName}.pdf, }); await Whatsapp.sendText({ message: invoiceText, recipientPhone: recipientPhone, }); await Whatsapp.sendSimpleButtons({ recipientPhone: recipientPhone, message: Thank you for shopping with us, ${recipientName}.\n\nYour order has been received & will be processed shortly., message_id, listOfButtons: [ { title: "'See more products'," id: 'see_categories', }, { title: "'Print my invoice'," id: 'print_invoice', }, ], }); clearCart({ recipientPhone }); }

上面的代碼執(zhí)行以下操作:

  1. 獲取購物車中的所有商品并放入其中finalBill
  2. 初始化一個變量invoiceText,其中包含我們將發(fā)送給客戶的文本以及將起草到 PDF 版本發(fā)票中的文本
  3. generatePDFInvoice這個方法(與我們之前在EcommerceStore類中定義的方法一樣)會接收訂單的詳細信息,然后基于這些信息起草一份PDF文檔,并將這份文檔保存在我們指定的本地目錄或文件夾的文件路徑中。
  4. sendText方法向客戶發(fā)送一條包含訂單詳細信息的簡單文本消息
  5. sendSimpleButtons向客戶發(fā)送一些按鈕。記下“打印我的發(fā)票”按鈕及其 ID
  6. 最后該clearCart方法清空購物車

現(xiàn)在,切換回您的 WhatsApp 應用程序并點擊“結(jié)賬”。如果您很好地遵循了說明,您將看到類似于以下屏幕截圖的回復:

單擊結(jié)賬按鈕

此時,客戶應該會收到可打印的 PDF 發(fā)票。因此,接下來讓我們深入探討一下與“打印我的發(fā)票”按鈕相關(guān)的邏輯處理。

編寫可打印的發(fā)票邏輯

simple_button_message if語句內(nèi)部但在checkout if語句外部添加以下代碼:

if (button_id === 'print_invoice') {
// Send the PDF invoice
await Whatsapp.sendDocument({
recipientPhone: recipientPhone,
caption:Mom-N-Pop Shop invoice #${recipientName} file_path: ./invoice_${recipientName}.pdf, }); // Send the location of our pickup station to the customer, so they can come and pick up their order let warehouse = Store.generateRandomGeoLocation(); await Whatsapp.sendText({ recipientPhone: recipientPhone, message: Your order has been fulfilled. Come and pick it up, as you pay, here:, }); await Whatsapp.sendLocation({ recipientPhone, latitude: warehouse.latitude, longitude: warehouse.longitude, address: warehouse.address, name: 'Mom-N-Pop Shop', }); }

上面的代碼從本地文件系統(tǒng)中獲取上一步生成的PDF文檔,并使用該sendDocument方法將其發(fā)送給客戶。

當客戶在線訂購產(chǎn)品時,他們還需要知道如何收到實物產(chǎn)品。因此,我們利用EcommerceStore類中的generateRandomGeoLocation方法來生成一些隨機的地理坐標,并通過sendLocation方法將這些坐標信息發(fā)送給客戶,以便他們了解可以在哪里實際提取所購買的產(chǎn)品。

現(xiàn)在,打開您的 WhatsApp 應用程序并點擊打印我的發(fā)票。如果您正確遵循了上述說明,您應該會看到類似于以下屏幕截圖的回復:

發(fā)短信

向客戶顯示已讀回執(zhí)

最后,您可能已經(jīng)注意到消息下方的復選標記是灰色的,而不是藍色的。這表明我們發(fā)送的消息并沒有收到已讀回執(zhí),盡管我們的機器人實際上已經(jīng)讀取了這些消息。

灰色的勾號可能會讓客戶感到不滿或失望,因此我們應該盡力確保顯示的是藍色的勾號,以給客戶帶來更好的體驗和信心。

simple_button_message語句外部和if語句的右大括號之前,添加以下代碼:

await Whatsapp.markMessageAsRead({ message_id });

我們一旦回復了消息,這條簡短的單行文字就會被系統(tǒng)自動標記為已讀狀態(tài)。

現(xiàn)在,打開您的 WhatsApp 應用程序并發(fā)送隨機短信。

留言

如果您之前的聊天記錄已更新為藍色勾號,那么 ?? 恭喜您!您已經(jīng)完成了本教程,并在此過程中學到了一些東西。

結(jié)論

每月活躍用戶總數(shù)高達20億,如果忽視將WhatsApp納入您的電子商務(wù)策略,那么您的企業(yè)很可能會在競爭中處于不利地位。既然您的大多數(shù)客戶在日常活動中都在使用WhatsApp,那么您的企業(yè)為什么不也應該在那里與他們見面呢?

我希望本教程對揭開 WhatsApp Cloud API 的神秘面紗有所幫助,并希望您在此過程中獲得一些樂趣。

原文鏈接:https://dev.to/logrocket/build-an-automated-ecommerce-app-with-whatsapp-cloud-api-and-nodejs-5g3a

上一篇:

如何使用 Web Scraper API 高效采集 Facebook 用戶帖子信息

下一篇:

用 Poe-API-wrapper 連接 DALLE、ChatGPT,批量完成AI繪圖或文字創(chuàng)作
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊

多API并行試用

數(shù)據(jù)驅(qū)動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費