返回开发者新闻

将 WhatsApp 集成到电商流程

2022年11月14日发布者:Rashed Talukder

WhatsApp Business 开放平台支持商家使用 API 发送标准化信息、接收客户回复和作出相应回复,从而帮助商家与客户直接交流。

本教程将展示一家在线销售商品的虚构杂货店如何让客户及时了解最新订单状态。您的应用将使用 Meta 托管的云端 API 创建 WhatsApp 消息模板。这些模板随后可用于向电商客户提供一致的标准化信息。

在现实世界中,订单状态随客户操作(例如注册、使用信用卡支付或完成购买)而更改。为此,您可将 WhatsApp Business 开放平台与 CRM 或电子商务系统集成,但本教程不会介绍这些内容。在本教程中,我们将创建一个手动更新系统,以便将重点放在消息模板的创建和管理方面。

前提条件

首先,我们将介绍如何创建由 WhatsApp 提供支持的 Node.js 网站应用。如果您想预览最终成果,可以下载完整的应用程序代码

设置开发者资产和开放平台访问权限教程操作,以使用测试电话号码发送和接收消息。请务必在 Meta 开发者上构建初始应用,将该应用与 WhatsApp 和商务管理平台关联。

此外,您还需了解 Node.js 并安装所需的全部工具。

使用 Node.js 和 Express 构建极简应用

本文假定您知道如何在 Node.js 中构建基本应用。我们将提供应用代码示例,并展示如何实现 WhatsApp 消息模板功能。

下载源代码时,您会在代码编辑器中看到以下文件夹结构。我们主要在 bin、public、routes views 文件夹下操作,以及编辑 .env、app.js package.json

  • bin 文件夹:包含节点模块中可执行的二进制文件。
  • public 文件夹:包含图片、JavaScript 和 CSS 文件等所有静态文件。
  • routes 文件夹:包含 app.js 文件中注册的路由。路由是 JavaScript 代码,Express.js 使用此类代码将 HTTP 谓词(GET、POST、PUT 等)与网址路径和处理 HTTP 请求的函数关联。
  • views 文件夹:包含具有标记逻辑的 EJS/HTML 文件,EJS 视图引擎使用这类标记逻辑来呈现网页。
  • app.js 文件:注册路由以及启动 Node.js 网站服务器。
  • .env 文件:包含定义项目所需环境变量的键值对。此文件允许在不修改项目代码的情况下配置应用程序。
  • package.json 文件:这是 Node.js 项目的核心文件,包含项目管理和安装依赖项、运行脚本和确定项目接入点所需的必要元数据。

首先,打开位于项目根文件夹中的 package.json 文件:

{
  "name": "ecommerce-node",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "axios": "^0.27.2",
    "body-parser": "^1.20.0",
    "cookie-parser": "~1.4.4",
    "debug": "~2.6.9",
    "dotenv": "^16.0.1",
    "ejs": "~3.1.8",
    "express": "~4.16.1",
    "http-errors": "~1.6.3",
    "morgan": "~1.9.1"
  }
}

请注意依赖项部分中列出的要求。如要运行应用程序,您必须先安装这些依赖项。请打开控制台并运行 npm install ,以安装这些依赖项。

此示例使用通过 Node.js、ExpressEJS 编写的极简应用。应用的首页Bootstrap 上构建,会展示一个简单的表单,您之后可使用此表单创建发送给客户的消息模板。

在继续操作之前,Node.js 应用程序需要 Meta 开发者中开发者帐户的特定数据。

编辑项目根文件夹中的 .env 文件,根据前提条件部分中提及的教程,将下列括号中的值替换为您 WhatsApp Business 商业帐号面板中的数据:

APP_ID=<<your WhatsApp business app id>> APP_SECRET=<<your WhatsApp business app secret>> RECIPIENT_WAID=<<your recipient test phone number>> PHONE_NUMBER_ID=<<sender test phone number id>> BUSINESS_ACCOUNT_ID=<<WhatsApp business account id>> ACCESS_TOKEN=<<access token you generated for your System User>> VERSION=v13.0 TEMPLATE_NAME_PREFIX=mkt 

使用 Node.js 和 WhatsApp Business 开放平台创建消息模板

现在,我们可以开始构建第一个消息模板。

您在 index.ejs 文件(位于 views/ 文件夹)中的表单操作指示应用向 /createTemplates 路由发送 POST 请求。因此,您需要使用新路由器执行以下操作:

  • 处理 createTemplates HTTP POST 请求。
  • 验证模板是否已存在。
  • 获取创建各消息模板所需的配置。
  • 使用云端 API 创建消息模板。
  • 创建模板后将应用重定向至首页。

使用下方代码,在 /routes 文件夹中新建名为 createTemplates.js 的文件。此文件包含一个 POST 端点,在处理来自首页的请求和调用其他函数以生成要使用的消息模板时,您需要使用此端点:

const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
require('dotenv').config()
const { messageTemplates } = require("../public/javascripts/messageTemplates")
const { createMessageTemplate } = require("../messageHelper");
const { listTemplates } = require("../messageHelper");

router.use(bodyParser.json());

router.post('/', async function (req, res, next) {

  try {
      const templatesResponse = await listTemplates()
      const remoteApprovedTemplates =
        templatesResponse.data.data.filter(t => t.status === 'APPROVED');

      const localTemplates = messageTemplates

      for (let index = 0; index < localTemplates.length; index++) {
        const localTemplate = localTemplates[index];

        const queryResult = remoteApprovedTemplates
        .filter(t => t.name == process.env.TEMPLATE_NAME_PREFIX
          + '_' + localTemplate.name)

        console.log(`Creating template: ${localTemplate.name}.`)

        if (queryResult.length > 0) {
          console.log(`Template ${queryResult[0].name} already exists.`)
          continue;
        }

        try {
          await createMessageTemplate(localTemplate);
          console.log(`Template ${localTemplate.name} created successfully.`)
        } catch (error) {
          console.log(`Failed creating template ${localTemplate.name}.`)
          console.log(error.response.data);
        }

      }
    } catch (error) {
      console.log(“Failed obtaining remote template list.”)
      console.log(error);
    }

    console.log(“Redirecting to the backoffice.”)
    res.redirect('/backoffice');
});

module.exports = router;

接下来,您需要使用函数封装云端 API 代码,以获取消息模板清单和创建消息模板。使用以下代码,在项目的根文件夹中新建名为 messageHelper.js 的文件:

const axios = require('axios');

async function listTemplates() {

  return await axios({
    method: 'get',
    url: `https://graph.facebook.com/${process.env.VERSION}/${process.env.BUSINESS_ACCOUNT_ID}/message_templates`
      + '?limit=1000'
      + `&access_token=${process.env.ACCESS_TOKEN}`
  })
}

使用上方代码可将 GET 请求发送到 message_templates 端点,以确定哪些模板已经存在。根据回复,您可以判断是否必须新建模板。

打开 messageHelper.js 文件,并在其中添加以下代码:

async function createMessageTemplate(template) {

  const config = {
    method: 'post',
    url: `https://graph.facebook.com/${process.env.VERSION}/${process.env.BUSINESS_ACCOUNT_ID}/message_templates`,
    headers: {
      'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
      'Content-Type': 'application/json'
    },
    data: {
      name:  process.env.TEMPLATE_NAME_PREFIX + '_' + template.name,
      category: "TRANSACTIONAL",
      components: template.components,
      language: template.language
    }
  };

  return await axios(config)
}

module.exports = {
  listTemplates: listTemplates,
  createMessageTemplate: createMessageTemplate
};

使用上方代码可访问 Meta 图谱 API,并向 /message_templates 端点发出 HTTP 请求,以获取 (HTTP GET) 的清单和创建 (HTTP POST) 模板。这些模板可传递以下内容:

  • 您正在使用的 Meta 图谱 API 版本
  • 您的 WhatsApp Business 商业帐号编号
  • 您为系统用户生成的访问口令

请注意,在此文件中使用的 createMessageTemplate 函数可创建具有特定数据结构的模板,该模板的参数满足电商应用示例的需求。如需详细了解并尝试创建不同的参数,请访问消息模板指南页面。

app.js 文件可定义 Node.js 应用的接入点。在应用程序源代码示例中, app.js 文件的内容如下:

const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const app = express();

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');


// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', indexRouter);
app.use('/users', usersRouter);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
  next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;

您可以看到上方的 app.js 文件如何声明所需的模块。然后,该文件为 Express.js 框架注册所需的路由,以确定使用哪些 JavaScript 函数来处理网页应用程序要处理的各个 HTTP 请求。

修改 app.js 文件,为 /createTemplate 路由创建路由器变量:

const createTemplatesRouter = require('./routes/createTemplates');

然后,允许应用使用新的 createTemplatesRouter 变量:

app.use('/createTemplates', createTemplatesRouter);

现在,为了创建测试消息模板 请使用示例库中 messageTemplates.js 文件内容,在 public/javascripts/ 文件夹中新建名为 messageTemplates.js 的文件。

最后,重新运行应用:

> npm start

点击“创建消息模板”。您将在终端窗口中看到消息模板的创建过程:

现在您已创建全部消息模板,可使用这些模板向 WhatsApp 用户发送预先设好格式的消息。

创建后台页面

下一步是创建商品目录和订单的模拟数据。您要将数据存储在与 JavaScript 代码分离的文件中。

public/javascripts 文件夹中新建 2 个名为 products.js orders.js 的文件,该文件夹包含示例库中的 product.jsorders.js 文件。

您现在需要新的路由,供用户用于访问后台页面:

使用以下代码在 /routes 文件夹中新建名为 backoffice.js 的文件,以呈现带有订单数据的后台页面:

const express = require('express');
const { products } = require("../public/javascripts/products");
const { orders, orderActions, statuses } = require("../public/javascripts/orders");
const { getMessageData, sendWhatsAppMessage } = require("../messageHelper")
const router = express.Router();

const viewModel = products;

router.post('/', async function (req, res, next) {
  const orderId = req.body.orderId;
  const order = orders.filter(o => o.id == orderId)[0]
  order.statusId++;

  const data = getMessageData(process.env.RECIPIENT_WAID, order);
  try {
    const response = await sendWhatsAppMessage(data)
    console.log(response);
  } catch (error) {
    console.log(error);
  }
  
  renderBackoffice(res);
});

router.get('/', function (req, res, next) {
  renderBackoffice(res);
});

module.exports = router;
function renderBackoffice(res) {
  res.render('backoffice',
  {
    title: 'e-Commerce Demo for Node.js',
    products: viewModel,
    statuses: statuses,
    orders: orders,
    orderActions: orderActions
  });
}

使用示例 backoffice.js 文件的内容,在 /views 文件夹中新建名为 backoffice.ejs 的文件,以使用 EJS 模板语法呈现订单数据。

接下来,打开 app.js 文件,并为 /backoffice 路由创建路由器变量:

const backofficeRouter = require('./routes/backoffice');

允许应用使用新的 backofficeRouter 变量:

app.use('/backoffice', backofficeRouter);

最后,重新运行应用:

> npm start

现在,点击“创建消息模板”,使用云端 API 创建全部消息模板,然后重定向至后台视图:

发送模板消息

在生产期间,商家发起的对话需要使用已获批准的消息模板,在开发期间,商家发起的对话至少应标记为“启用”。这些对话可以包括客户服务消息、预约提醒、支付或配送更新,以及提醒。

您必须使用各模板所需的参数发送消息。

打开 messageHelper.js 文件,并删除 module.exports 代码块。接下来,添加 sendWhatsAppMessage getMessageData 函数。然后,导入商品和消息模板所需的 javascript 文件。最后,使用下方代码添加新的 module.exports 代码块:

const { messageTemplates } = require('./public/javascripts/messageTemplates')
const { products } = require('./public/javascripts/products')

async function sendWhatsAppMessage(data) {
  const config = {
    method: 'post',
    url: `https://graph.facebook.com/${process.env.VERSION}/${process.env.PHONE_NUMBER_ID}/messages`,
    headers: {
      'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
      'Content-Type': 'application/json'
    },
    data: data
  };

  return await axios(config)
}

function getMessageData(recipient, order) {

  const messageTemplate = messageTemplates[order.statusId - 1]
    
  let messageParameters

  switch (messageTemplate.name) {
    case 'welcome':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
      ];
      break;
    case 'payment_analysis':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
        { type: "text", text: products[order.items[0].productId - 1].name },
      ];
      break;
    case 'payment_approved':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
        { type: "text", text: order.id },
        { type: "text", text: order.deliveryDate },
      ];
      break;
    case 'invoice_available':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
        { type: "text", text: products[order.items[0].productId - 1].name },
        { type: "text", text: `https://customer.your-awesome-grocery-store-demo.com/my-account/orders/${order.id}` },
      ];
      break;
    case 'order_picked_packed':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
        { type: "text", text: order.id },
        { type: "text", text: `https://customer.your-awesome-grocery-store-demo.com/my-account/orders/${order.id}` },
      ];
      break;
    case 'order_in_transit':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
        { type: "text", text: order.id },
        { type: "text", text: order.deliveryDate },
        { type: "text", text: `https://customer.your-awesome-grocery-store-demo.com/my-account/orders/${order.id}` },
      ];
      break;
    case 'order_delivered':
      messageParameters = [
        { type: "text", text: order.customer.split(' ')[0] },
        { type: "text", text: order.id },
        { type: "text", text: order.deadlineDays },
      ];
      break;
  }
  
  const messageData = {
    messaging_product: "whatsapp",
    to: recipient,
    type: "template",
    template: {
      name: process.env.TEMPLATE_NAME_PREFIX + '_' + messageTemplate.name,
      language: { "code": "en_US" },
      components: [{
          type: "body",
          parameters: messageParameters
        }]}}

  return JSON.stringify(messageData);
}

module.exports = {
  sendWhatsAppMessage: sendWhatsAppMessage,
  listTemplates: listTemplates,
  createMessageTemplate: createMessageTemplate,
  getMessageData: getMessageData
};

请注意, getMessageData 函数将返回一种数据结构,在发送已针对后台模板设置格式的消息时,您需要使用这种数据结构。您可以尝试使用其他模板,或前往消息模板页面新建模板。

最后,运行应用:

> npm start

在后台页面上,点击各个操作按钮,顺着销售漏斗更改订单状态。应用将向代表客户的测试电话号码发送相应的 WhatsApp 模板消息。每条消息均对应新的订单状态。

选择第一个订单,并点击“批准”按钮。应用将向云端 API 的 /messages 端点发出 POST 请求。然后应用根据自己创建的 payment_analysis template 向客户发送消息。随后,应用将订单状态更改为“付款通过审核”。

现在打开 WhatsApp,以显示模板消息:

接下来,选择第二个订单,点击“配送”,然后重复上述流程:

最后,选择第五个订单,并点击“派送”:

上述 WhatsApp 消息包含 2 个基本要素:表示电商漏斗中当前购物订单状态的消息模板,以及包含客户或商品相关信息的原始数据。

在本练习中,原始数据来自静态文件(orders.js products.js),但在现实生活的应用中,您需要从数据库、CRM 或电商 API 等企业数据源中提取这类数据。通过此集成,您还可根据这些系统中的客户更新自动执行此流程。

使用 Meta 商务管理平台用户界面创建和管理消息模板

您已了解应用程序如何通过云端 API 以编程方式创建消息模板。除了这个强大的功能以外,您还可依靠 Meta 商务管理平台用户界面,直接通过网页创建和修改模板,而无需编写程序。

如要访问 Meta 商务管理平台用户界面,请登录 Meta 开发者平台,然后导航至您的应用。在左侧导航窗口的“产品”下,依次点击“WhatsApp”和“入门指南”。

在“第 2 步:使用 API 发送消息”下,您可看到此段落包含一个链接:

“如要发送测试消息,请将此命令复制并粘贴到终端,然后按 Enter。如要创建自己的消息模板,请点击此处。”

点击此链接。您也可以直接前往“消息模板”页面

如要查看您使用 Node.js 应用程序创建的其中一条消息,请点击铅笔图标,随后系统将显示以下页面:

如要新建模板,请点击“创建消息模板”按钮,然后选择模板的类别、名称以及一种或多种语言:

最后,为消息模板输入一些文本。消息正文可以包含变量,即应用稍后向 WhatsApp 用户发送消息时必须提供的数据的占位符,如本文的示例所示。如要进一步自定义模板,您可加入包含图片、视频或文档的消息页脚和页眉。

结论

WhatsApp Business 开放平台是一个简单工具,可促进公司与客户之间的交流。通过 Meta 托管的云端 API,您可轻松关联自己的应用、与客户有效交流,以及推广您的业务。

本文展示了如何将基于 Node.js 的电商后台应用与 WhatsApp Business 开放平台的关联,以使用云端 API 创建消息模板、提供信息以及向 WhatsApp 用户发送消息。

想要了解更多信息?请参阅通过应用和云端 API 构建和发送富媒体消息的有关教程。