1658842023
En mayo de 2022, Meta (la empresa antes conocida como Facebook, propietaria de WhatsApp) anunció que abriría al público la API empresarial de WhatsApp. Este artículo pretende darle la bienvenida al mundo de oportunidades de Meta, donde los chatbots de WhatsApp pueden ayudarlo a generar clientes potenciales, recibir pedidos, programar citas, realizar encuestas, recibir comentarios de los clientes, brindar atención al cliente escalable, enviar facturas y recibos, y más.
Este tutorial profundizará en los aspectos técnicos de la creación de un chatbot de WhatsApp desde cero.
Al final de este tutorial, habrá creado su propio chatbot de WhatsApp.
Nuestro tutorial se centra en una sencilla tienda de comercio electrónico familiar que vende artículos para el hogar y moda rápida. El negocio tendrá un chatbot de WhatsApp donde los clientes podrán navegar y comprar en la tienda de comercio electrónico.
Cada tienda de comercio electrónico necesita productos (precios, nombres, descripciones, etc.), y para este tutorial, usaremos los productos ficticios de FakeStoreAPI .
Antes de continuar, este artículo asume que:
El primer paso para usar cualquiera de las API de Meta es crear una aplicación en el tablero de Meta, que es gratis.
En esta pantalla, seleccione WhatsApp y haga clic en su botón Configurar .
Luego será conducido a una nueva pantalla, como se muestra a continuación.
En esta pantalla, toma nota de:
1184643492312754
EAAQ1bU6LdrIBA
...+1 555 025 3483
113362588047543
102432872486730
Tenga en cuenta que el token de acceso temporal caduca después de 24 horas, momento en el que tendremos que renovarlo. Cuando cambia su aplicación al modo en vivo, puede solicitar un token de acceso permanente, que no necesitamos hacer ya que nuestra aplicación está en modo de desarrollo.
El ID del número de teléfono y el ID de la cuenta comercial de WhatsApp están vinculados al número de teléfono de prueba.
A continuación, agreguemos un número de teléfono para usar para recibir mensajes.
En el modo de desarrollo, Meta nos restringe a cinco números de destinatarios por motivos relacionados con la prevención del spam/uso indebido. En el modo en vivo/producción, el número representa los números de teléfono de nuestros clientes.
Haga clic en Seleccione un número de teléfono del destinatario y agregue su propio número de WhatsApp, como se muestra en la siguiente captura de pantalla:
Después de agregar su número de destinatario, verá una pantalla similar a la siguiente. Si es la primera vez que agrega su número de teléfono a las plataformas Meta, como las páginas de Facebook, la suite Meta Business o el panel de desarrollo de Meta, recibirá un mensaje OTP de Facebook Business que le pedirá que verifique que realmente es el propietario del número del destinatario. .
Probemos si todo hasta este paso funcionó bien. Lo haremos haciendo clic en el botón Enviar mensaje .
Si todo está bien, debería ver un mensaje en su bandeja de entrada de WhatsApp desde su número de prueba.
Hasta este punto, ¡lo estamos haciendo bien! Haz una pausa y abre tu editor de código. No cierres la pestaña de tu navegador todavía porque volveremos al panel de Meta Developer en unos minutos.
Ahora que nuestra configuración puede enviar mensajes con éxito, configuremos una forma de recibir mensajes. Es hora de ensuciarse las manos y sumergirnos en la escritura de código. Todo el código que escribiremos para este tutorial está en este repositorio de GitHub .
Cree una nueva carpeta para contener nuestro proyecto. Abra esta carpeta en una terminal y ejecute el siguiente script:
npm init ---yes
A continuación, instalamos algunos paquetes:
npm install express pdfkit request whatsappcloudapi_wrapper
npm install nodemon --dev
He aquí una breve explicación de cada uno:
express
paquete es importante para configurar nuestro servidor. El servidor contendrá una ruta que actuará como nuestro webhook.pdfkit
paquete se utilizará para generar facturas para nuestros clientes cuando realicen el pago.request
paquete nos ayudará a ejecutar solicitudes de recuperación a FakeStoreAPIwhatsappcloudapi_wrapper
nos ayuda a enviar y recibir mensajes de WhatsAppA continuación, vamos a crear tres archivos:
./app.js
./.env.js
./routes/index.js
En nuestro ./.env.js
archivo, escriba el siguiente código:
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;
}
};
En el mismo ./.env.js
archivo:
Meta_WA_accessToken
con el token de acceso temporal para su aplicación MetaMeta_WA_SenderPhoneNumberId
con su ID de número de teléfonoMeta_WA_wabaId
con su ID de cuenta comercial de WhatsAppMeta_WA_VerifyToken
. Puede ser una cadena o un número; verás cómo lo usamos en el paso de webhooksEl código anterior primero importa las variables de entorno actuales y las desestructura, luego agrega nuevas variables de entorno y exporta la combinación de las dos como un objeto.
En el archivo ./app.js
file, inserte el siguiente código:
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();
La primera línea del bloque de código anterior simplemente importa el ./.env.js
archivo y lo asigna a process.env
, que es un objeto accesible globalmente en Node.js.
En el archivo ./routes/index.js
, inserte el siguiente código:
'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;
A continuación, abra la terminal y ejecute:
nodemon app.js
El servidor Express se ejecutará en el puerto 9000.
Luego, abra otra terminal separada y ejecute:
ngrok http 9000
Este comando expone nuestra aplicación Express a Internet en general. El objetivo aquí es configurar un webhook al que WhatsApp Cloud pueda hacer ping.
Tome nota de la URL que ngrok asigna a su servidor Express. En mi ejemplo, ngrok me envió esta URL: https://7b9b-102-219-204-54.ngrok.io
. Mantenga en funcionamiento tanto el servidor Express como el terminal ngrok.
A continuación, reanudemos nuestro trabajo en el panel de Meta Developer. Desplácese hasta la parte titulada Configurar webhooks para recibir mensajes y haga clic en Configurar webhooks . El enlace mostrará una página que se parece a la siguiente captura de pantalla:
Haga clic en el botón Editar y aparecerá una ventana emergente.
En el campo URL de devolución de llamada , pegue la URL que ngrok le envió y agréguela con la ruta de devolución de llamada, como en la ./routes/index.js
directiva. Mi URL completa, en este caso, es https://7b9b-102-219-204-54.ngrok.io/meta_wa_callbackurl
.
En el campo Verificar token , ingrese el valor de Meta_WA_VerifyToken tal como aparece en su ./.env.js
archivo.
Luego haga clic en Verificar y guardar .
Si configuró esto bien, verá un console.log
mensaje en la terminal de su servidor Express que dice:
GET: ¡Alguien me está haciendo ping!
Ahora, hagamos que nuestro servidor Express reciba mensajes de suscripción de Meta.
En la misma pantalla del tablero de Meta Developers, haga clic en Administrar y aparecerá una ventana emergente.
Seleccione Mensajes y haga clic en Prueba , que se encuentra en la misma fila.
Debería ver un console.log
mensaje en la terminal de su servidor Express que dice:
POST: ¡Alguien me está haciendo ping!
Si vio esto, regrese a la misma ventana emergente y haga clic en Suscribirse en la misma fila de mensajes. Luego, haga clic en Listo .
Primero, configuraremos nuestra lógica para obtener datos de FakeStoreAPI, generar una factura en PDF y generar una ubicación ficticia de recogida de pedidos. Envolveremos esta lógica en una clase de JavaScript, que luego importaremos a la lógica de nuestra aplicación.
Cree un archivo y asígnele un nombre ./utils/ecommerce_store.js
. En este archivo, pegue el siguiente código:
'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)
];
}
};
En el código anterior, hemos creado una clase llamada EcommerceStore
.
El primer método, _fetchAssistant
recibe un punto final que utiliza para hacer ping a fakestoreapi.com .
Los siguientes métodos actúan como generadores de consultas para el primer método:
getProductById
recibe una identificación de producto y luego obtiene datos relacionados con ese producto específicogetAllCategories
recupera todas las categorías que están en fakestoreapi.comgetProductsInCategory
recibe una categoría de productos y luego procede a buscar todos los productos en esa categoría específicaEstos generadores de consultas invocarán el primer método.
Continuando, el método generatePDFInvoice
recibe un fragmento de texto y una ruta de archivo. Luego crea un documento PDF, escribe el texto en él y luego almacena el documento en la ruta del archivo proporcionada.
El método generateRandomGeoLocation
simplemente devuelve una geolocalización aleatoria. Este método será útil cuando enviemos el lugar de recogida de pedidos de nuestra tienda a un cliente que quiera recoger su artículo.
Para manejar el viaje de nuestro cliente, debemos mantener una sesión que incluya un perfil de cliente y su carrito. Cada cliente tendrá, por lo tanto, su propia sesión única.
En producción, podríamos usar una base de datos como MySQL, MongoDB u otra resistente, pero para que nuestro tutorial sea sencillo y breve, usaremos la estructura de datos de ES2015Map
. Con Map
, podemos almacenar y recuperar datos iterables específicos, como datos de clientes únicos.
En su ./routes/index.js
archivo, agregue el siguiente código justo arriba 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
La primera línea importa la EcommerceStore
clase, mientras que la segunda línea la inicializa. La tercera línea crea la sesión del cliente que usaremos para almacenar el viaje del cliente.
¿Recuerdas el whatsappcloudapi_wrapper
paquete que instalamos antes? Es hora de importarlo e inicializarlo.
En el ./routes/index.js
archivo, agregue las siguientes líneas de código debajo de la declaración del enrutador 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,
});
Los siguientes valores son variables de entorno que definimos en nuestro ./.env.js
archivo:
process.env.Meta_WA_accessToken
process.env.Meta_WA_SenderPhoneNumberId
process.env.Meta_WA_wabaId
Inicializamos la clase WhatsAppCloudAPI con los tres valores anteriores y nombramos nuestra instancia Whatsapp
.
A continuación, analicemos todos los datos que ingresan al /meta_wa_callbackurl
webhook POST. Al analizar el cuerpo de las solicitudes, podremos extraer mensajes y otros detalles, como el nombre del remitente, el número de teléfono del remitente, etc.
Tenga en cuenta : todas las ediciones de código que hagamos desde este punto se realizarán por completo en el
./routes/index.js
archivo.
Agregue las siguientes líneas de código debajo del paréntesis de apertura de la try{
instrucción:
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
}
Ahora, cuando un cliente nos envíe un mensaje, nuestro webhook debería recibirlo. El mensaje está contenido en el cuerpo de solicitud del webhook. Para extraer información útil del cuerpo de la solicitud, debemos pasar el cuerpo al parseMessage
método de la instancia de WhatsApp.
Luego, usando una if
declaración, verificamos si el resultado del método contiene un mensaje de WhatsApp válido.
Dentro de la if
declaración, definimos incomingMessage
, que contiene el mensaje. También definimos otras variables:
recipientPhone
es el número del cliente que nos envió un mensaje. Les enviaremos un mensaje de respuesta, de ahí el prefijo “destinatario”recipientName
es el nombre del cliente que nos envió un mensaje. Este es el nombre que se han puesto en su perfil de WhatsApptypeOfMsg
es el tipo de mensaje que un cliente nos envió. Como veremos más adelante, algunos mensajes son textos simples, mientras que otros son respuestas a botones (¡no te preocupes, esto tendrá sentido pronto!)message_id
es una cadena de caracteres que identifica de forma única un mensaje que hemos recibido. Esto es útil cuando queremos hacer tareas que son específicas de ese mensaje, como marcar un mensaje como leídoHasta este punto, todo parece estar bien, pero lo confirmaremos en breve.
Dado que nuestro tutorial no se sumergirá en ninguna forma de IA o procesamiento de lenguaje natural (NLP), definiremos nuestro flujo de chat con una if…else
lógica simple.
La lógica de la conversación comienza cuando el cliente envía un mensaje de texto. No miraremos el mensaje en sí, por lo que no sabremos qué pretendían hacer, pero podemos decirle al cliente lo que puede hacer nuestro bot.
Démosle a nuestro cliente un contexto simple, al que pueda responder con una intención específica.
Le daremos al cliente dos botones:
Para hacer esto, inserte el siguiente código a continuació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',
},
],
});
}
La if
declaración anterior solo nos permite manejar mensajes de texto.
El sendSimpleButtons
método nos permite enviar botones a un cliente. Tome nota de las propiedades title
y id
. es lo title
que verá el cliente y id
es lo que usaremos para saber en qué botón hizo clic el cliente.
Veamos si lo hicimos bien. Abra su aplicación WhatsApp y envíe un mensaje de texto a la cuenta comercial de WhatsApp.
Si obtiene una respuesta como la captura de pantalla anterior, ¡felicidades! Acabas de enviar tu primer mensaje a través de la API de WhatsApp Cloud.
Dado que el cliente puede hacer clic en cualquiera de los dos botones, también nos ocuparemos del botón Hablar con una persona.
Fuera de la if
declaración de la text_message
lógica, inserte el siguiente código:
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',
},
],
},
});
}
};
El código anterior realiza dos acciones:
sendText
métodosendContact
métodoEste código también detecta la intención del usuario usando la ID del botón en el que el usuario hizo clic (en nuestro caso, la ID es el incomingMessage.button_reply.id
), y luego responde con las dos opciones de acción.
Ahora, regresa a WhatsApp y haz clic en Hablar con un humano . Si lo hiciste bien, verás una respuesta como la siguiente:
Al hacer clic en la tarjeta de contacto que recibió, debería ver lo siguiente:
A continuación, trabajemos en el botón Ver algunos productos .
Dentro de la simple_button_message if
declaración, pero justo debajo y fuera de la speak_to_human if
declaración, agregue el siguiente código:
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)
});
}
Esto es lo que hace el código anterior:
if
declaración asegura que el usuario hizo clic en el botón Ver algunos productosFakeStoreAPI
través del getAllCategories
métodoslice(0,3)
— porque WhatsApp solo nos permite enviar tres botones simplestitle
y una identificación única que tiene el prefijo concategory_
sendSimpleButtons
método, enviamos estos botones al cliente.Regrese nuevamente a su aplicación WhatsApp y haga clic en Ver más productos . Si realizó correctamente los pasos anteriores, debería ver una respuesta similar a la siguiente captura de pantalla:
Ahora, creemos la lógica para obtener productos en la categoría que seleccionó el cliente.
Todavía dentro de la simple_button_message if
declaración, pero debajo y fuera de la see_categories if
declaración, agregue el siguiente código:
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,
});
}
La if
declaración anterior confirma que el botón en el que el cliente hizo clic era de hecho el botón que contiene una categoría.
Lo primero que hacemos aquí es extraer la categoría específica de la ID del botón. Luego, consultamos nuestra FakeStoreAPI para productos que pertenecen a esa categoría específica.
Después de consultar, recibimos la lista de productos dentro de una matriz, listOfProducts.data
. Ahora recorremos esta matriz, y para cada producto en ella extraemos su precio, título, descripción e ID.
Agregamos product_
al id
, que nos ayudará a seleccionar la selección de un cliente en el siguiente paso. Asegúrese de recortar la longitud de la identificación, el título y la descripción de acuerdo con las restricciones del botón de radio (o lista) de la API de WhatsApp Cloud .
Luego devolvemos tres valores: ID, título y descripción. Dado que WhatsApp solo nos permite un máximo de 10 filas, limitaremos el número de productos a 10 mediante el método array .slice(0,10)
.
Después de eso, invocamos el sendRadioButtons
método para enviar los productos a los clientes. Tome nota de las propiedades , , y . headerTextbodyTextfooterTextlistOfSections
Regrese a la aplicación WhatsApp y haga clic en cualquier categoría de productos. Si siguió las instrucciones correctamente, debería ver una respuesta similar a la siguiente captura de pantalla:
Al hacer clic en Seleccionar un producto , debería ver la siguiente pantalla:
En este punto, los clientes pueden seleccionar un producto que les parezca interesante, pero ¿podemos saber qué han seleccionado? Todavía no, así que trabajemos en esta parte.
Fuera de la simple_button_message if
declaración, agreguemos otra if
declaración:
if (typeOfMsg === 'radio_button_message') {
let selectionId = incomingMessage.list_reply.id; // the customer clicked and submitted a radio button
}
Dentro de la if
declaración anterior y justo debajo del selectionId
, agregue el siguiente código:
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',
},
],
});
}
El código anterior hace lo siguiente:
emojiRating
función. Si una calificación es 3.8, generará emojis de tres estrellas.sendImage
métodoDespués de esto, enviamos al cliente una lista de tres botones usando el sendSimpleButtons
. Uno le da al cliente la oportunidad de agregar productos a su carrito. Tome nota de la identificación del botón que tiene el prefijo add_to_cart_
.
Ahora, regrese a su aplicación WhatsApp y seleccione un producto. Si siguió las instrucciones correctamente, debería ver una respuesta similar a la siguiente captura de pantalla:
Para realizar un seguimiento de los productos que un cliente agrega a su carrito, necesitamos tener un lugar para almacenar los artículos del carrito. Aquí es donde CustomerSession
entra en juego. Vamos a añadirle algo de lógica.
Fuera de la radio_button_message if
declaración, y justo debajo de la message_id
declaración, agregue el siguiente código:
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...
El código anterior comprueba si se ha creado la sesión de un cliente. Si no se ha creado, crea una nueva sesión que se identifica de forma única por el número de teléfono del cliente. Luego inicializamos una propiedad llamada cart
, que comienza como una matriz vacía.
La addToCart
función toma en un product_id
y el número del cliente específico. Luego, hace ping a FakeStoreAPI para obtener los datos del producto específico y empuja el producto a la cart
matriz.
Luego, la listOfItemsInCart
función toma el número de teléfono del cliente y recupera el asociado cart
, que se usa para calcular la cantidad de productos en el carrito y la suma de sus precios. Finalmente, devuelve los artículos del carrito y su precio total.
La clearCart
función toma el número de teléfono del cliente y vacía el carrito de ese cliente.
Una vez terminada la lógica del carrito, construyamos el botón Agregar al carrito . Dentro de la simple_button_message if
declaración y debajo de su button_id
declaración, agregue el siguiente código:
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',
},
],
});
}
El código anterior extrae el ID del producto del botón en el que hizo clic el cliente, luego invoca la addToCart
función para guardar el producto en el carrito de la sesión del cliente. Luego, extrae la cantidad de artículos en el carrito de la sesión del cliente y le dice al cliente cuántos productos tiene. También envía dos botones, uno de los cuales permite al usuario realizar el pago.
Tome nota del ID del botón y vuelva a su aplicación de WhatsApp. Haz clic en Añadir al carrito . Si siguió bien las instrucciones, debería ver una respuesta similar a la siguiente captura de pantalla:
Ahora que nuestros clientes pueden agregar artículos al carrito, podemos escribir la lógica para pagar.
Dentro de la simple_button_message if
declaración pero fuera de la add_to_cart_ if
declaración, agregue el siguiente código:
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 });
}
El código anterior hace lo siguiente:
finalBill
invoiceText
, que contendrá el texto que enviaremos al cliente, así como el texto que se redactará en la versión PDF de la factura.forEach
ciclo simplemente concatena el title
y price
de cada producto a la facturageneratePDFInvoice
método (el mismo que definimos en nuestra EcommerceStore
clase) toma los detalles del pedido, redacta un documento PDF y lo guarda en la ruta del archivo en nuestro directorio/carpeta local que le proporcionamossendText
método envía un mensaje de texto simple que contiene los detalles del pedido al clientesendSimpleButtons
envía algunos botones al cliente. Toma nota del botón Imprimir mi factura y su IDclearCart
método vacía el carro .Ahora, vuelva a su aplicación WhatsApp y haga clic en Finalizar compra . Si siguió bien las instrucciones, verá una respuesta similar a la siguiente captura de pantalla:
En este punto, el cliente debe recibir una factura en PDF imprimible. Por esta razón, trabajemos en alguna lógica con respecto al botón Imprimir mi factura .
Dentro de la simple_button_message if
declaración pero fuera de la checkout if
declaración, agregue el siguiente código:
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',
});
}
El código anterior obtiene el documento PDF generado en el paso anterior del sistema de archivos local y lo envía al cliente utilizando el sendDocument
método.
Cuando un cliente solicita un producto en línea, también necesita saber cómo recibirá el producto físico. Por esta razón, generamos algunas coordenadas aleatorias utilizando el generateRandomGeoLocation
método de la EcommerceStore
clase y enviamos estas coordenadas al cliente utilizando el sendLocation
método para informarle dónde puede recoger físicamente su producto.
Ahora, abra su aplicación WhatsApp y haga clic en Imprimir mi factura .
Si ha seguido las instrucciones anteriores correctamente, debería ver una respuesta similar a la siguiente captura de pantalla:
Por último, es posible que haya notado que las marcas de verificación debajo de los mensajes son grises, en lugar de azules. Esto indica que los mensajes que enviamos no devolvieron recibos de lectura a pesar de que nuestro bot los estaba leyendo.
Las marcas grises pueden ser frustrantes para los clientes y, por este motivo, debemos trabajar para mostrar las marcas azules.
Fuera de la simple_button_message if
declaración y antes de la llave de cierre de la data?.isMessage if
declaración, agregue el siguiente código:
await Whatsapp.markMessageAsRead({ message_id });
Esta simple línea marca un mensaje como leído tan pronto como lo hayamos respondido.
Ahora, abra su aplicación WhatsApp y envíe un mensaje de texto aleatorio. ¿Estás viendo lo que estoy viendo?
Si sus chats anteriores se han actualizado con marcas azules, ¡felicidades! Has llegado al final de este tutorial y has aprendido algunas cosas en el camino.
Con un total de 2000 millones de usuarios activos mensuales, ignorar WhatsApp como estrategia de comercio electrónico es una forma segura de quedarse atrás de la competencia de su negocio, y dado que la mayoría de sus clientes ya usan WhatsApp en sus actividades diarias, ¿por qué no hacerlo? t su negocio encontrarlos allí?
Espero que este tutorial haya sido útil para desmitificar la API de WhatsApp Cloud, y espero que te hayas divertido en el camino. Cuéntame qué otros temas te pueden interesar y no olvides compartir este artículo con tus círculos tecnológicos.
Fuente: https://blog.logrocket.com/build-ecommerce-app-whatsapp-cloud-api-node-js/
#whatsapp #ecommerce #cloud #api #nodejs
1658842023
En mayo de 2022, Meta (la empresa antes conocida como Facebook, propietaria de WhatsApp) anunció que abriría al público la API empresarial de WhatsApp. Este artículo pretende darle la bienvenida al mundo de oportunidades de Meta, donde los chatbots de WhatsApp pueden ayudarlo a generar clientes potenciales, recibir pedidos, programar citas, realizar encuestas, recibir comentarios de los clientes, brindar atención al cliente escalable, enviar facturas y recibos, y más.
Este tutorial profundizará en los aspectos técnicos de la creación de un chatbot de WhatsApp desde cero.
Al final de este tutorial, habrá creado su propio chatbot de WhatsApp.
Nuestro tutorial se centra en una sencilla tienda de comercio electrónico familiar que vende artículos para el hogar y moda rápida. El negocio tendrá un chatbot de WhatsApp donde los clientes podrán navegar y comprar en la tienda de comercio electrónico.
Cada tienda de comercio electrónico necesita productos (precios, nombres, descripciones, etc.), y para este tutorial, usaremos los productos ficticios de FakeStoreAPI .
Antes de continuar, este artículo asume que:
El primer paso para usar cualquiera de las API de Meta es crear una aplicación en el tablero de Meta, que es gratis.
En esta pantalla, seleccione WhatsApp y haga clic en su botón Configurar .
Luego será conducido a una nueva pantalla, como se muestra a continuación.
En esta pantalla, toma nota de:
1184643492312754
EAAQ1bU6LdrIBA
...+1 555 025 3483
113362588047543
102432872486730
Tenga en cuenta que el token de acceso temporal caduca después de 24 horas, momento en el que tendremos que renovarlo. Cuando cambia su aplicación al modo en vivo, puede solicitar un token de acceso permanente, que no necesitamos hacer ya que nuestra aplicación está en modo de desarrollo.
El ID del número de teléfono y el ID de la cuenta comercial de WhatsApp están vinculados al número de teléfono de prueba.
A continuación, agreguemos un número de teléfono para usar para recibir mensajes.
En el modo de desarrollo, Meta nos restringe a cinco números de destinatarios por motivos relacionados con la prevención del spam/uso indebido. En el modo en vivo/producción, el número representa los números de teléfono de nuestros clientes.
Haga clic en Seleccione un número de teléfono del destinatario y agregue su propio número de WhatsApp, como se muestra en la siguiente captura de pantalla:
Después de agregar su número de destinatario, verá una pantalla similar a la siguiente. Si es la primera vez que agrega su número de teléfono a las plataformas Meta, como las páginas de Facebook, la suite Meta Business o el panel de desarrollo de Meta, recibirá un mensaje OTP de Facebook Business que le pedirá que verifique que realmente es el propietario del número del destinatario. .
Probemos si todo hasta este paso funcionó bien. Lo haremos haciendo clic en el botón Enviar mensaje .
Si todo está bien, debería ver un mensaje en su bandeja de entrada de WhatsApp desde su número de prueba.
Hasta este punto, ¡lo estamos haciendo bien! Haz una pausa y abre tu editor de código. No cierres la pestaña de tu navegador todavía porque volveremos al panel de Meta Developer en unos minutos.
Ahora que nuestra configuración puede enviar mensajes con éxito, configuremos una forma de recibir mensajes. Es hora de ensuciarse las manos y sumergirnos en la escritura de código. Todo el código que escribiremos para este tutorial está en este repositorio de GitHub .
Cree una nueva carpeta para contener nuestro proyecto. Abra esta carpeta en una terminal y ejecute el siguiente script:
npm init ---yes
A continuación, instalamos algunos paquetes:
npm install express pdfkit request whatsappcloudapi_wrapper
npm install nodemon --dev
He aquí una breve explicación de cada uno:
express
paquete es importante para configurar nuestro servidor. El servidor contendrá una ruta que actuará como nuestro webhook.pdfkit
paquete se utilizará para generar facturas para nuestros clientes cuando realicen el pago.request
paquete nos ayudará a ejecutar solicitudes de recuperación a FakeStoreAPIwhatsappcloudapi_wrapper
nos ayuda a enviar y recibir mensajes de WhatsAppA continuación, vamos a crear tres archivos:
./app.js
./.env.js
./routes/index.js
En nuestro ./.env.js
archivo, escriba el siguiente código:
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;
}
};
En el mismo ./.env.js
archivo:
Meta_WA_accessToken
con el token de acceso temporal para su aplicación MetaMeta_WA_SenderPhoneNumberId
con su ID de número de teléfonoMeta_WA_wabaId
con su ID de cuenta comercial de WhatsAppMeta_WA_VerifyToken
. Puede ser una cadena o un número; verás cómo lo usamos en el paso de webhooksEl código anterior primero importa las variables de entorno actuales y las desestructura, luego agrega nuevas variables de entorno y exporta la combinación de las dos como un objeto.
En el archivo ./app.js
file, inserte el siguiente código:
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();
La primera línea del bloque de código anterior simplemente importa el ./.env.js
archivo y lo asigna a process.env
, que es un objeto accesible globalmente en Node.js.
En el archivo ./routes/index.js
, inserte el siguiente código:
'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;
A continuación, abra la terminal y ejecute:
nodemon app.js
El servidor Express se ejecutará en el puerto 9000.
Luego, abra otra terminal separada y ejecute:
ngrok http 9000
Este comando expone nuestra aplicación Express a Internet en general. El objetivo aquí es configurar un webhook al que WhatsApp Cloud pueda hacer ping.
Tome nota de la URL que ngrok asigna a su servidor Express. En mi ejemplo, ngrok me envió esta URL: https://7b9b-102-219-204-54.ngrok.io
. Mantenga en funcionamiento tanto el servidor Express como el terminal ngrok.
A continuación, reanudemos nuestro trabajo en el panel de Meta Developer. Desplácese hasta la parte titulada Configurar webhooks para recibir mensajes y haga clic en Configurar webhooks . El enlace mostrará una página que se parece a la siguiente captura de pantalla:
Haga clic en el botón Editar y aparecerá una ventana emergente.
En el campo URL de devolución de llamada , pegue la URL que ngrok le envió y agréguela con la ruta de devolución de llamada, como en la ./routes/index.js
directiva. Mi URL completa, en este caso, es https://7b9b-102-219-204-54.ngrok.io/meta_wa_callbackurl
.
En el campo Verificar token , ingrese el valor de Meta_WA_VerifyToken tal como aparece en su ./.env.js
archivo.
Luego haga clic en Verificar y guardar .
Si configuró esto bien, verá un console.log
mensaje en la terminal de su servidor Express que dice:
GET: ¡Alguien me está haciendo ping!
Ahora, hagamos que nuestro servidor Express reciba mensajes de suscripción de Meta.
En la misma pantalla del tablero de Meta Developers, haga clic en Administrar y aparecerá una ventana emergente.
Seleccione Mensajes y haga clic en Prueba , que se encuentra en la misma fila.
Debería ver un console.log
mensaje en la terminal de su servidor Express que dice:
POST: ¡Alguien me está haciendo ping!
Si vio esto, regrese a la misma ventana emergente y haga clic en Suscribirse en la misma fila de mensajes. Luego, haga clic en Listo .
Primero, configuraremos nuestra lógica para obtener datos de FakeStoreAPI, generar una factura en PDF y generar una ubicación ficticia de recogida de pedidos. Envolveremos esta lógica en una clase de JavaScript, que luego importaremos a la lógica de nuestra aplicación.
Cree un archivo y asígnele un nombre ./utils/ecommerce_store.js
. En este archivo, pegue el siguiente código:
'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)
];
}
};
En el código anterior, hemos creado una clase llamada EcommerceStore
.
El primer método, _fetchAssistant
recibe un punto final que utiliza para hacer ping a fakestoreapi.com .
Los siguientes métodos actúan como generadores de consultas para el primer método:
getProductById
recibe una identificación de producto y luego obtiene datos relacionados con ese producto específicogetAllCategories
recupera todas las categorías que están en fakestoreapi.comgetProductsInCategory
recibe una categoría de productos y luego procede a buscar todos los productos en esa categoría específicaEstos generadores de consultas invocarán el primer método.
Continuando, el método generatePDFInvoice
recibe un fragmento de texto y una ruta de archivo. Luego crea un documento PDF, escribe el texto en él y luego almacena el documento en la ruta del archivo proporcionada.
El método generateRandomGeoLocation
simplemente devuelve una geolocalización aleatoria. Este método será útil cuando enviemos el lugar de recogida de pedidos de nuestra tienda a un cliente que quiera recoger su artículo.
Para manejar el viaje de nuestro cliente, debemos mantener una sesión que incluya un perfil de cliente y su carrito. Cada cliente tendrá, por lo tanto, su propia sesión única.
En producción, podríamos usar una base de datos como MySQL, MongoDB u otra resistente, pero para que nuestro tutorial sea sencillo y breve, usaremos la estructura de datos de ES2015Map
. Con Map
, podemos almacenar y recuperar datos iterables específicos, como datos de clientes únicos.
En su ./routes/index.js
archivo, agregue el siguiente código justo arriba 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
La primera línea importa la EcommerceStore
clase, mientras que la segunda línea la inicializa. La tercera línea crea la sesión del cliente que usaremos para almacenar el viaje del cliente.
¿Recuerdas el whatsappcloudapi_wrapper
paquete que instalamos antes? Es hora de importarlo e inicializarlo.
En el ./routes/index.js
archivo, agregue las siguientes líneas de código debajo de la declaración del enrutador 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,
});
Los siguientes valores son variables de entorno que definimos en nuestro ./.env.js
archivo:
process.env.Meta_WA_accessToken
process.env.Meta_WA_SenderPhoneNumberId
process.env.Meta_WA_wabaId
Inicializamos la clase WhatsAppCloudAPI con los tres valores anteriores y nombramos nuestra instancia Whatsapp
.
A continuación, analicemos todos los datos que ingresan al /meta_wa_callbackurl
webhook POST. Al analizar el cuerpo de las solicitudes, podremos extraer mensajes y otros detalles, como el nombre del remitente, el número de teléfono del remitente, etc.
Tenga en cuenta : todas las ediciones de código que hagamos desde este punto se realizarán por completo en el
./routes/index.js
archivo.
Agregue las siguientes líneas de código debajo del paréntesis de apertura de la try{
instrucción:
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
}
Ahora, cuando un cliente nos envíe un mensaje, nuestro webhook debería recibirlo. El mensaje está contenido en el cuerpo de solicitud del webhook. Para extraer información útil del cuerpo de la solicitud, debemos pasar el cuerpo al parseMessage
método de la instancia de WhatsApp.
Luego, usando una if
declaración, verificamos si el resultado del método contiene un mensaje de WhatsApp válido.
Dentro de la if
declaración, definimos incomingMessage
, que contiene el mensaje. También definimos otras variables:
recipientPhone
es el número del cliente que nos envió un mensaje. Les enviaremos un mensaje de respuesta, de ahí el prefijo “destinatario”recipientName
es el nombre del cliente que nos envió un mensaje. Este es el nombre que se han puesto en su perfil de WhatsApptypeOfMsg
es el tipo de mensaje que un cliente nos envió. Como veremos más adelante, algunos mensajes son textos simples, mientras que otros son respuestas a botones (¡no te preocupes, esto tendrá sentido pronto!)message_id
es una cadena de caracteres que identifica de forma única un mensaje que hemos recibido. Esto es útil cuando queremos hacer tareas que son específicas de ese mensaje, como marcar un mensaje como leídoHasta este punto, todo parece estar bien, pero lo confirmaremos en breve.
Dado que nuestro tutorial no se sumergirá en ninguna forma de IA o procesamiento de lenguaje natural (NLP), definiremos nuestro flujo de chat con una if…else
lógica simple.
La lógica de la conversación comienza cuando el cliente envía un mensaje de texto. No miraremos el mensaje en sí, por lo que no sabremos qué pretendían hacer, pero podemos decirle al cliente lo que puede hacer nuestro bot.
Démosle a nuestro cliente un contexto simple, al que pueda responder con una intención específica.
Le daremos al cliente dos botones:
Para hacer esto, inserte el siguiente código a continuació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',
},
],
});
}
La if
declaración anterior solo nos permite manejar mensajes de texto.
El sendSimpleButtons
método nos permite enviar botones a un cliente. Tome nota de las propiedades title
y id
. es lo title
que verá el cliente y id
es lo que usaremos para saber en qué botón hizo clic el cliente.
Veamos si lo hicimos bien. Abra su aplicación WhatsApp y envíe un mensaje de texto a la cuenta comercial de WhatsApp.
Si obtiene una respuesta como la captura de pantalla anterior, ¡felicidades! Acabas de enviar tu primer mensaje a través de la API de WhatsApp Cloud.
Dado que el cliente puede hacer clic en cualquiera de los dos botones, también nos ocuparemos del botón Hablar con una persona.
Fuera de la if
declaración de la text_message
lógica, inserte el siguiente código:
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',
},
],
},
});
}
};
El código anterior realiza dos acciones:
sendText
métodosendContact
métodoEste código también detecta la intención del usuario usando la ID del botón en el que el usuario hizo clic (en nuestro caso, la ID es el incomingMessage.button_reply.id
), y luego responde con las dos opciones de acción.
Ahora, regresa a WhatsApp y haz clic en Hablar con un humano . Si lo hiciste bien, verás una respuesta como la siguiente:
Al hacer clic en la tarjeta de contacto que recibió, debería ver lo siguiente:
A continuación, trabajemos en el botón Ver algunos productos .
Dentro de la simple_button_message if
declaración, pero justo debajo y fuera de la speak_to_human if
declaración, agregue el siguiente código:
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)
});
}
Esto es lo que hace el código anterior:
if
declaración asegura que el usuario hizo clic en el botón Ver algunos productosFakeStoreAPI
través del getAllCategories
métodoslice(0,3)
— porque WhatsApp solo nos permite enviar tres botones simplestitle
y una identificación única que tiene el prefijo concategory_
sendSimpleButtons
método, enviamos estos botones al cliente.Regrese nuevamente a su aplicación WhatsApp y haga clic en Ver más productos . Si realizó correctamente los pasos anteriores, debería ver una respuesta similar a la siguiente captura de pantalla:
Ahora, creemos la lógica para obtener productos en la categoría que seleccionó el cliente.
Todavía dentro de la simple_button_message if
declaración, pero debajo y fuera de la see_categories if
declaración, agregue el siguiente código:
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,
});
}
La if
declaración anterior confirma que el botón en el que el cliente hizo clic era de hecho el botón que contiene una categoría.
Lo primero que hacemos aquí es extraer la categoría específica de la ID del botón. Luego, consultamos nuestra FakeStoreAPI para productos que pertenecen a esa categoría específica.
Después de consultar, recibimos la lista de productos dentro de una matriz, listOfProducts.data
. Ahora recorremos esta matriz, y para cada producto en ella extraemos su precio, título, descripción e ID.
Agregamos product_
al id
, que nos ayudará a seleccionar la selección de un cliente en el siguiente paso. Asegúrese de recortar la longitud de la identificación, el título y la descripción de acuerdo con las restricciones del botón de radio (o lista) de la API de WhatsApp Cloud .
Luego devolvemos tres valores: ID, título y descripción. Dado que WhatsApp solo nos permite un máximo de 10 filas, limitaremos el número de productos a 10 mediante el método array .slice(0,10)
.
Después de eso, invocamos el sendRadioButtons
método para enviar los productos a los clientes. Tome nota de las propiedades , , y . headerTextbodyTextfooterTextlistOfSections
Regrese a la aplicación WhatsApp y haga clic en cualquier categoría de productos. Si siguió las instrucciones correctamente, debería ver una respuesta similar a la siguiente captura de pantalla:
Al hacer clic en Seleccionar un producto , debería ver la siguiente pantalla:
En este punto, los clientes pueden seleccionar un producto que les parezca interesante, pero ¿podemos saber qué han seleccionado? Todavía no, así que trabajemos en esta parte.
Fuera de la simple_button_message if
declaración, agreguemos otra if
declaración:
if (typeOfMsg === 'radio_button_message') {
let selectionId = incomingMessage.list_reply.id; // the customer clicked and submitted a radio button
}
Dentro de la if
declaración anterior y justo debajo del selectionId
, agregue el siguiente código:
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',
},
],
});
}
El código anterior hace lo siguiente:
emojiRating
función. Si una calificación es 3.8, generará emojis de tres estrellas.sendImage
métodoDespués de esto, enviamos al cliente una lista de tres botones usando el sendSimpleButtons
. Uno le da al cliente la oportunidad de agregar productos a su carrito. Tome nota de la identificación del botón que tiene el prefijo add_to_cart_
.
Ahora, regrese a su aplicación WhatsApp y seleccione un producto. Si siguió las instrucciones correctamente, debería ver una respuesta similar a la siguiente captura de pantalla:
Para realizar un seguimiento de los productos que un cliente agrega a su carrito, necesitamos tener un lugar para almacenar los artículos del carrito. Aquí es donde CustomerSession
entra en juego. Vamos a añadirle algo de lógica.
Fuera de la radio_button_message if
declaración, y justo debajo de la message_id
declaración, agregue el siguiente código:
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...
El código anterior comprueba si se ha creado la sesión de un cliente. Si no se ha creado, crea una nueva sesión que se identifica de forma única por el número de teléfono del cliente. Luego inicializamos una propiedad llamada cart
, que comienza como una matriz vacía.
La addToCart
función toma en un product_id
y el número del cliente específico. Luego, hace ping a FakeStoreAPI para obtener los datos del producto específico y empuja el producto a la cart
matriz.
Luego, la listOfItemsInCart
función toma el número de teléfono del cliente y recupera el asociado cart
, que se usa para calcular la cantidad de productos en el carrito y la suma de sus precios. Finalmente, devuelve los artículos del carrito y su precio total.
La clearCart
función toma el número de teléfono del cliente y vacía el carrito de ese cliente.
Una vez terminada la lógica del carrito, construyamos el botón Agregar al carrito . Dentro de la simple_button_message if
declaración y debajo de su button_id
declaración, agregue el siguiente código:
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',
},
],
});
}
El código anterior extrae el ID del producto del botón en el que hizo clic el cliente, luego invoca la addToCart
función para guardar el producto en el carrito de la sesión del cliente. Luego, extrae la cantidad de artículos en el carrito de la sesión del cliente y le dice al cliente cuántos productos tiene. También envía dos botones, uno de los cuales permite al usuario realizar el pago.
Tome nota del ID del botón y vuelva a su aplicación de WhatsApp. Haz clic en Añadir al carrito . Si siguió bien las instrucciones, debería ver una respuesta similar a la siguiente captura de pantalla:
Ahora que nuestros clientes pueden agregar artículos al carrito, podemos escribir la lógica para pagar.
Dentro de la simple_button_message if
declaración pero fuera de la add_to_cart_ if
declaración, agregue el siguiente código:
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 });
}
El código anterior hace lo siguiente:
finalBill
invoiceText
, que contendrá el texto que enviaremos al cliente, así como el texto que se redactará en la versión PDF de la factura.forEach
ciclo simplemente concatena el title
y price
de cada producto a la facturageneratePDFInvoice
método (el mismo que definimos en nuestra EcommerceStore
clase) toma los detalles del pedido, redacta un documento PDF y lo guarda en la ruta del archivo en nuestro directorio/carpeta local que le proporcionamossendText
método envía un mensaje de texto simple que contiene los detalles del pedido al clientesendSimpleButtons
envía algunos botones al cliente. Toma nota del botón Imprimir mi factura y su IDclearCart
método vacía el carro .Ahora, vuelva a su aplicación WhatsApp y haga clic en Finalizar compra . Si siguió bien las instrucciones, verá una respuesta similar a la siguiente captura de pantalla:
En este punto, el cliente debe recibir una factura en PDF imprimible. Por esta razón, trabajemos en alguna lógica con respecto al botón Imprimir mi factura .
Dentro de la simple_button_message if
declaración pero fuera de la checkout if
declaración, agregue el siguiente código:
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',
});
}
El código anterior obtiene el documento PDF generado en el paso anterior del sistema de archivos local y lo envía al cliente utilizando el sendDocument
método.
Cuando un cliente solicita un producto en línea, también necesita saber cómo recibirá el producto físico. Por esta razón, generamos algunas coordenadas aleatorias utilizando el generateRandomGeoLocation
método de la EcommerceStore
clase y enviamos estas coordenadas al cliente utilizando el sendLocation
método para informarle dónde puede recoger físicamente su producto.
Ahora, abra su aplicación WhatsApp y haga clic en Imprimir mi factura .
Si ha seguido las instrucciones anteriores correctamente, debería ver una respuesta similar a la siguiente captura de pantalla:
Por último, es posible que haya notado que las marcas de verificación debajo de los mensajes son grises, en lugar de azules. Esto indica que los mensajes que enviamos no devolvieron recibos de lectura a pesar de que nuestro bot los estaba leyendo.
Las marcas grises pueden ser frustrantes para los clientes y, por este motivo, debemos trabajar para mostrar las marcas azules.
Fuera de la simple_button_message if
declaración y antes de la llave de cierre de la data?.isMessage if
declaración, agregue el siguiente código:
await Whatsapp.markMessageAsRead({ message_id });
Esta simple línea marca un mensaje como leído tan pronto como lo hayamos respondido.
Ahora, abra su aplicación WhatsApp y envíe un mensaje de texto aleatorio. ¿Estás viendo lo que estoy viendo?
Si sus chats anteriores se han actualizado con marcas azules, ¡felicidades! Has llegado al final de este tutorial y has aprendido algunas cosas en el camino.
Con un total de 2000 millones de usuarios activos mensuales, ignorar WhatsApp como estrategia de comercio electrónico es una forma segura de quedarse atrás de la competencia de su negocio, y dado que la mayoría de sus clientes ya usan WhatsApp en sus actividades diarias, ¿por qué no hacerlo? t su negocio encontrarlos allí?
Espero que este tutorial haya sido útil para desmitificar la API de WhatsApp Cloud, y espero que te hayas divertido en el camino. Cuéntame qué otros temas te pueden interesar y no olvides compartir este artículo con tus círculos tecnológicos.
Fuente: https://blog.logrocket.com/build-ecommerce-app-whatsapp-cloud-api-node-js/
1617257581
¿Quiere restaurar los buzones de correo de PST a Exchange Server? Entonces, estás en la página correcta. Aquí, lo guiaremos sobre cómo puede restaurar fácilmente mensajes y otros elementos de PST a MS Exchange Server.
Muchas veces, los usuarios necesitan restaurar los elementos de datos de PST en Exchange Server, pero debido a la falta de disponibilidad de una solución confiable, los usuarios no pueden obtener la solución. Háganos saber primero sobre el archivo PST y MS Exchange Server.
PST es un formato de archivo utilizado por MS Outlook, un cliente de correo electrónico de Windows y muy popular entre los usuarios domésticos y comerciales.
Por otro lado, Exchange Server es un poderoso servidor de correo electrónico donde todos los datos se almacenan en un archivo EDB. Los usuarios generalmente guardan la copia de seguridad de los buzones de correo de Exchange en el archivo PST, pero muchas veces, los usuarios deben restaurar los datos del archivo PST en Exchange. Para resolver este problema, estamos aquí con una solución profesional que discutiremos en la siguiente sección de esta publicación.
No le recomendamos que elija una solución al azar para restaurar los datos de PST en Exchange Server. Por lo tanto, al realizar varias investigaciones, estamos aquí con una solución inteligente y conveniente, es decir, Exchange Restore Software. Es demasiado fácil de manejar por todos los usuarios y restaurar cómodamente todos los datos del archivo PST a Exchange Server.
El software es demasiado simple de usar y se puede instalar fácilmente en todas las versiones de Windows. Con unos pocos clics, la herramienta puede restaurar los elementos del buzón de Exchange.
No es necesario que MS Outlook restaure los datos PST en Exchange. Todos los correos electrónicos, contactos, notas, calendarios, etc. se restauran desde el archivo PST a Exchange Server.
Todas las versiones de Outlook son compatibles con la herramienta, como Outlook 2019, 2016, 2013, 2010, 2007, etc. La herramienta proporciona varios filtros mediante los cuales se pueden restaurar los datos deseados desde un archivo PST a Exchange Server. El programa se puede instalar en todas las versiones de Windows como Windows 10, 8.1, 8, 7, XP, Vista, etc.
Descargue la versión de demostración del software de restauración de Exchange y analice el funcionamiento del software restaurando los primeros 50 elementos por carpeta.
No existe una solución manual para restaurar los buzones de correo de Exchange desde el archivo PST. Por lo tanto, hemos explicado una solución fácil e inteligente para restaurar datos de archivos PST en Exchange Server. Simplemente puede usar este software y restaurar todos los datos de PST a Exchange Server.
Más información:- https://www.datavare.com/software/exchange-restore.html
#intercambio de software de restauración #intercambio de restauración #buzón del servidor de intercambio #herramienta de restauración de intercambio
1598286700
Through whatsapp web you can easily run whatsapp on your android pc on your android mobile. Just like whatsapp mobile is for android device, whatsapp web is for windows device. Whatsapp web is quite popular which has quite cool features.
whatsapp web
how to use whatsapp web desktop
Whatsapp web is very easy to use. Simply you have to search web.whatsapp.com in your google chrome and click on first result which is the official website of whatsapp web.
As soon as you click, an interface will open in front of you, on which you will see a barcode. Follow the steps given below to use whatsapp web on your desktop
open your whatsapp on your mobile
You will see 3dots on the right side top inside whatsapp, you have to click
The 3rd option is whatsapp web, you have to click it
Now you have to capture the barcode you see on your desktop through your phone.
Now you can use whatsapp of your android mobile in your desktop
webs whatsapp
note: You can see whatsapp of anyone’s mobile by pointing to the barcode of your desktop. You can also call it whatsapp hack.
Remember that after using whatsapp web, logout it from your desktop. To logout follow the steps given below.
w app web
open your whatsapp on your mobile
You will see 3dots on the right side top inside whatsapp, you have to click
The 3rd option is whatsapp web, you have to click it
You will see the symbol for logout, you have to logout by clicking it.
#whatsapp #whatappweb #https://web.whatsapp.com/ #wsp web #web.whatsapp web #web whatsapp
1611816521
People used to write letters to their friends and families for communication in the olden days. Then came the invention of landline phones, which made it easier to communicate over long-distance. Followed by which came the trendsetting invention, the mobile phones. Communication became much easier. SMS packs were used and those became the fundamental communication mode across the world.
Though years passed by the normal SMS communication, it got largely replaced because of the emergence of instant messaging apps like Whatsapp. It became the household name for many users worldwide by bringing several advantages in the messaging world; one of which is, offering instant communication facilities without any cost but just with the internet connection. It is now topping the chart in the messaging industry by competing with many other apps like Telegram, Snapchat, etc.
Why choose WhatsApp Clone?
Whatsapp is basically a freeware, cross-platform messenger app owned by Facebook. It is an app that allows users to send text messages, videos, images, and documents along with the feature to make video and voice calls. The application runs on mobile devices and they are accessible from desktops as well.
For many business owners and investors, it has been and is being the top role model, and is paving the way to the development of Whatsapp Clone apps. Here are the reasons why developing the instant messaging app can offer you various benefits.
Due to the communication, it provides with just the internet, the app can be used all around the world. They come with some extra features like last seen, whether the message is read or not, with its timings, ability to share locations, and many more. The incredible features of the app made it have an audience of 900 million users.
As the years pass by, the active users of these apps are increasing. There are also many reports proving that messaging apps are more popular than the social media sites like Facebook, Twitter, etc.
Last but not the least, people are welcoming these technologies for the uniqueness and ease-of-access they are offering.
Cost for development:
The cost for developing an instant messaging app like Whatsapp is not fixed, as they will depend on the functionalities and features everyone adds. For every addition of a special feature, the cost may vary and the same goes for the functionality. Apart from that, the total hours of development, addition of plug-ins and add-ons also make the most part. However, purchasing and developing an app from Whatsapp Clone script can cut down the cost.
As a final note,
It is high time for you to reap profits. So select the right development company and get yourself a white-label solution as a smart choice and set foot in the industry soon!
#whatsapp clone app #whatsapp clone #whatsapp clone script #app like whatsapp #whatsapp clone app development
1654599600
Desde que apareció el COVID-19 en el mundo, el comercio electrónico se ha disparado. Según la Administración de Comercio Internacional, ha habido un aumento del 19 % en los ingresos entre el comercio electrónico anterior y posterior al covid en 2020. Como resultado, los empresarios recurrieron a plataformas en línea como Shopify, WordPress (y su complemento, wooCommerce) y Squarespace. para montar sus tiendas online.
Con Shopify, los usuarios no solo pueden configurar una tienda en línea, sino que, gracias a su Storefront API , los desarrolladores pueden crear una aplicación de cliente personalizada para conectarse a ella.
En este artículo, aprenderá cómo configurar una tienda Shopify y obtener su token API Storefront. Una vez que adquiera su token, creará una aplicación Next.js para enumerar y mostrar sus productos con datos ficticios. Finalmente, conectará su aplicación Next.js a su aplicación Shopify para obtener sus productos reales.
Si ya tiene una tienda Shopify configurada, puede omitir este paso y dirigirse al paso 2 .
Si no, dirígete al sitio web de Shopify y regístrate para una prueba gratuita. Una vez hecho esto, puede comenzar creando algunas colecciones, que se utilizan para categorizar sus productos.
Para crear una colección, vaya a Productos , luego a Colecciones . A continuación, haz clic en Crear colección . Ingrese un título y elija Manual para el tipo de colección. Con este último, puede agregar manualmente productos a esta colección.
Para este tutorial son necesarias dos colecciones: Hombre y Mujer.
Una vez que sus colecciones estén configuradas, haga clic en Productos y luego Agregar producto .
Para crear un producto, estos son algunos pasos a seguir:
Una vez que su tienda esté configurada, necesitará un token de API para acceder a ella desde una aplicación de terceros. Vaya a Aplicaciones , haga clic en Desarrollar aplicaciones para su tienda y siga el paso para habilitar el desarrollo de aplicaciones.
Una vez que esta función esté habilitada, haga clic en Crear una aplicación y complete el nombre de su aplicación.
Una vez que haya creado su aplicación, haga clic en su nueva aplicación y diríjase a Configuración . En la sección Storefront API, haga clic en Configurar .
A continuación, en Alcances de acceso a la API de Storefront , seleccione todas las casillas de verificación. Esto le otorgará la capacidad de obtener datos de su tienda, como sus productos, sin estar autenticado. Haga clic en Guardar .
Para obtener su token de acceso a Storefront, deberá instalar su aplicación recién creada, así que haga clic en el botón verde Instalar .
Ahora puede recuperar su token de acceso volviendo a su aplicación y a las credenciales de la API . Tenga en cuenta ese token, ya que lo necesitará más adelante.
Para este tutorial, utilizará MUI . Esta biblioteca de componentes le permite obtener componentes prediseñados para crear una interfaz de usuario mucho más rápido.
Afortunadamente, MUI también viene con un proyecto inicial de Next.js que puede obtener ejecutando este comando:
curl https://codeload.github.com/mui/material-ui/tar.gz/master | tar -xz --strip=2 material-ui-master/examples/nextjs
El comando anterior, desafortunadamente, creará el proyecto con el nombre predeterminado nextjs
. si desea cambiarle el nombre, ejecute:
mv nextjs <your_app_name> # Mac
ren nextjs <your_app_name> # Windows
Una vez hecho esto, ingrese su proyecto e instale las dependencias:
cd <your_app_name>
npm install
npm run dev
Crear su interfaz de usuario será más fácil con algunos datos ficticios. Para hacerlo, cree un data.js
archivo dentro de su carpeta raíz y agregue algunos productos ficticios:
const PRODUCTS = [
{
handle: 1,
name: 'Women Black Shoes',
price: '14.99',
collection: 'women',
image: 'https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fcohenwoodworking.com%2Fwp-content%2Fuploads%2F2016%2F09%2Fimage-placeholder-500x500.jpg&f=1&nofb=1'
},
{
handle: 2,
name: 'Women Brown Shoes',
price: '14.99',
collection: 'women',
image: 'https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fcohenwoodworking.com%2Fwp-content%2Fuploads%2F2016%2F09%2Fimage-placeholder-500x500.jpg&f=1&nofb=1'
},
{
handle: 3,
name: 'Women Purple Shoes',
price: '14.99',
collection: 'women',
image: 'https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fcohenwoodworking.com%2Fwp-content%2Fuploads%2F2016%2F09%2Fimage-placeholder-500x500.jpg&f=1&nofb=1'
}
];
export default PRODUCTS;
Dentro de su carpeta raíz Next.js, cree una components
carpeta.
Dentro de esta carpeta, creará un componente para listar sus productos llamado ProductList
. MUI tiene algunos componentes prediseñados para crear una lista con imágenes y títulos debajo de ellos en formato de cuadrícula.
Estos componentes se denominan: ImageList
, ImageListItem
y ImageListItemBar
. Puede encontrar la documentación aquí .
Dentro de su components
carpeta, cree ProductsList.js
. Importe todos los componentes necesarios desde MUI y configure su cuadrícula. Suponga que su componente recibirá una variedad de productos y mapeará sobre él para crear una sola entrada de producto con una imagen y un título debajo.
import * as React from 'react';
import Typography from '@mui/material/Typography';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
export default function ProductsList({products}) {
return (
<ImageList cols={5} gap={20}>
{products.map((product) => (
<ImageListItem key={product.image}>
{/* Product's image*/}
<img
src={`${product.image}?w=250&fit=crop&auto=format`}
srcSet={`${product.image}?w=250&fit=crop&auto=format&dpr=2 2x`}
alt={product.name}
loading="lazy"
/>
{/* Product's name + price under the image */}
<ImageListItemBar
title={product.name}
subtitle={<span>Price: {product.price}</span>}
position="below"
/>
</ImageListItem>
))}
</ImageList>
)
};
Otro componente que necesitará es una barra de navegación. Puede usar MUI AppBar
y Toolbar
crear uno. En el interior, agregue sus colecciones como texto. Más adelante aprenderá a configurar el enrutamiento.
Aquí está el resultado de su Navigation.js
componente:
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
export default function Navigation() {
return (
<AppBar position="static">
<Toolbar>
<Typography mr={2}>All products</Typography>
<Typography mr={2}>Women</Typography>
<Typography>Men</Typography>
</Toolbar>
</AppBar>
)
};
Finalmente, use sus componentes recién creados ProductsList
y Navigation.js
, en la página de inicio de su aplicación. Esta página está disponible en la URL y se procesa desde el pages/index.js
archivo.
Nota: si no está familiarizado con la funcionalidad de la página de Next.js y su representación, consulte la documentación .
Dentro index.js, import Navigation, ProductLists
, y sus datos ficticios. Puedes pasar tus datos a tu lista de productos:
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Navigation from '../components/Navigation';
import ProductsList from '../components/ProductsList';
import PRODUCTS from '../data.js';
export default function Index() {
return (
<Box>
<Navigation />
<Container maxWidth="lg">
<ProductsList products={PRODUCTS} />
</Container>
</Box>
);
}
En este punto, una vez que actualice su página, debería ver esto:
Ahora que su página de inicio está configurada, necesita páginas de colecciones. La mayoría de las tiendas en línea dividen sus productos en categorías, por lo que es más fácil para los usuarios navegar por sus catálogos. Como resultado, crearemos una colección específica.
Next.js ofrece enrutamiento dinámico. Si no está familiarizado con él, le permite crear rutas, como /collections/[collectionName
. Al hacerlo, las URL como /collections/women
o /collections/men
usarán la misma .js
página.
Dentro de su pages
carpeta, cree una collections
carpeta y dentro de ella, un [collectionName].js
archivo. Este componente será muy similar a su página de inicio. Sin embargo, filtrará sus productos para obtener solo los de la colección correcta.
Afortunadamente, Next.js también viene con un enlace useRouter
desde el cual puede obtener los parámetros de consulta de la URL. Una vez que tenga el collectionName
, puede usarlo para filtrar sus productos. Finalmente, pase su lista filtrada de productos a ProductsList
.
import * as React from 'react';
import { useRouter } from 'next/router'
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Navigation from '../../components/Navigation';
import BreadcrumbsNavigation from '../../components/BreadcrumbsNavigation';
import ProductsList from '../../components/ProductsList';
import PRODUCTS from '../../data.js';
export default function CollectionPage() {
const router = useRouter()
const { collectionName } = router.query
const products = PRODUCTS.filter(product => product.collection === collectionName)
return (
<Box>
<Navigation />
<Container maxWidth="lg">
<BreadcrumbsNavigation collection={collectionName} />
<ProductsList products={products} />
</Container>
</Box>
);
}
Tenga en cuenta que la página de su colección utiliza un componente personalizado llamado BreadcrumbsNavigation
. Esto es para generar migas de pan personalizadas para su tienda.
Para crear este componente, dentro de su components
carpeta, cree un archivo BreadcrumbsNavigation.js
. MIU ofrece un Breadcrumbs
componente para su uso. Sumado a esto, el Link
componente le permite agregar enrutamiento para volver a su página de inicio.
Aquí está el resultado final:
import * as React from 'react';
import Box from '@mui/material/Box';
import Breadcrumbs from '@mui/material/Breadcrumbs';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';
export default function BreadcrumbsNavigation({title}) {
return (
<Box mt={2}>
<Breadcrumbs separator="›" aria-label="breadcrumb">
<Link underline="hover" key="1" color="inherit" href="/">
Products
</Link>
<Typography key="3" color="text.primary">
{title && title.replace(/^\w/, c => c.toUpperCase())}
</Typography>
</Breadcrumbs>
</Box>
)
}
En Navigation.js
, importe el componente de MUI Link
que acaba de usar y agregue algunas rutas en la barra de su aplicación para su página de inicio y sus colecciones:
import * as React from 'react';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import Typography from '@mui/material/Typography';
import Link from '@mui/material/Link';
export default function Navigation() {
return (
<AppBar position="static">
<Toolbar>
<Link href="/" underline="none" color="inherit"><Typography mr={2}>All products</Typography></Link>
<Link href="/collections/women" underline="none" color="inherit"><Typography mr={2}>Women</Typography></Link>
<Link href="/collections/men" underline="none" color="inherit"><Typography>Men</Typography></Link>
</Toolbar>
</AppBar>
)
};
En su ProductList.js
, puede agregar un cheque en caso de que no haya productos:
import * as React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
...
export default function ProductsList({products}) {
return (
<Box>
{
(products && products.length > 0) ?
<ImageList cols={5} gap={20}>
...
</ImageList>:
<Typography variant="body1" align="center">There are no products in this collection</Typography>
}
</Box>
)
};
Si te diriges a http://localhost:3000/collections/women
, ahora deberías ver esto:
Con su página de inicio y las páginas de colecciones configuradas, puede pasar a la página del producto. Al igual que la página de colecciones, el enrutamiento dinámico de Next.js se puede usar para configurar la página.
Crea una products
carpeta. Dentro, agregue un nuevo archivo llamado [productHandle].js
. Ya sea que desee usar ID o identificadores black-converse
como productHandle
, esta página representará la página de su producto.
Después de obtener el productHandle
parámetro de la URL, puede usarlo para obtener su producto. Para mostrar la imagen de su producto, Next.js proporciona un Image
componente que viene con optimización de imagen integrada.
Aquí está el resultado de una página de producto:
import * as React from 'react';
import { useRouter } from 'next/router';
import Image from 'next/image';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid'
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Navigation from '../../components/Navigation';
import BreadcrumbsNavigation from '../../components/BreadcrumbsNavigation';
import ProductsList from '../../components/ProductsList';
import PRODUCTS from '../../data.js';
export default function ProductPage() {
const router = useRouter()
// Get productHandle from url: /products/[productHandle]
const { productHandle } = router.query
// Get product data
const product = PRODUCTS.find(product => product.handle === parseInt(productHandle))
const { name, image, price } = product || {}
return (
<Box>
<Navigation />
{product &&
<Container maxWidth="lg">
<BreadcrumbsNavigation title={name} />
<Grid container direction="row">
<Grid item xs={6}>
<Image
src={image}
alt={`Picture of ${name}`}
width={500}
height={500}
/>
</Grid>
<Grid item xs={6}>
<Typography variant="h3" my={2}>{name}</Typography>
<Grid mt={4}>
<Typography variant="h6" component="span">Price: </Typography>
<Typography variant="body1" component="span">{price}</Typography>
</Grid>
<Grid mt={1}>
<Button variant="contained">Add to cart</Button>
</Grid>
</Grid>
</Grid>
</Container>
}
</Box>
);
}
Si termina usando el Image
componente, deberá agregar el dominio de su imagen a next.config.js
, así:
module.exports = {
reactStrictMode: true,
images: {
domains: ['external-content.duckduckgo.com'],
},
};
Tendrá que reiniciar su servidor cuando cambie next.config.js
.
Finalmente, implementemos el enrutamiento a la página de su producto. Para hacerlo, regrese a su components/ProductsList.js
y use el enrutador Next.js para navegar a la página de su producto cuando un usuario haga clic en la imagen en la lista del producto. Agregue en onClick
su ImageListItem
, junto con el estilo del cursor, y cree un goToProductPage
para navegar.
Aquí está el resultado final:
import * as React from 'react';
import { useRouter } from 'next/router'
...
export default function ProductsList({products}) {
const router = useRouter()
// Navigate to product's page
const goToProductPage = productHandle => router.push(`/products/${productHandle}`)
return (
<Box>
{
(products && products.length > 0) ?
<ImageList cols={5} gap={20}>
{products.map((product) => (
<ImageListItem
key={product.image}
style={{cursor: 'pointer'}}
onClick={() => goToProductPage(product.handle)}>
...
</ImageListItem>
))}
</ImageList>:
<Typography variant="body1" align="center">There are no products in this collection</Typography>
}
</Box>
)
};
Una vez renderizado, esto es lo que obtendrá:
Su aplicación de cliente ya está configurada. Sin embargo, se basa en datos ficticios. Para finalizar su proyecto, los datos de su tienda Shopify deben obtenerse y usarse.
Para hacerlo, necesitará Shopify JavaScript Buy SDK , que puede obtener instalando el paquete necesario:
npm install shopify-buy
¿Recuerda los tokens de Storefront API que obtuvimos al principio de este artículo? Los almacenará en variables de entorno. Dentro de tu proyecto raíz, crea un archivo llamado .env.local
y agrega tu token, junto con tu dominio de Shopify:
SHOPIFY_STORE_FRONT_ACCESS_TOKEN=*****
SHOPIFY_STORE_DOMAIN=********
También puede encontrar su dominio haciendo clic en Configuración en su tienda Shopify:
Una vez que haya configurado sus variables, cree una lib
carpeta en su proyecto raíz y un shopify.js
archivo dentro. En este archivo, importará su shopify-buy
biblioteca y creará un cliente de Shopify con sus variables.
Una vez que realices una llamada a la API de Shopify, la respuesta devuelta deberá especificarse y analizarse para usarse como un objeto JSON más adelante. Como resultado, cree una parseShopifyResponse
función para que pueda usarla repetidamente en su aplicación:
import Client from "shopify-buy";
export const shopifyClient = Client.buildClient({
storefrontAccessToken: process.env.SHOPIFY_STORE_FRONT_ACCESS_TOKEN,
domain: process.env.SHOPIFY_STORE_DOMAIN,
});
export const parseShopifyResponse = (response) => JSON.parse(JSON.stringify(response));
Sigamos buscando todos sus productos para su página de inicio. En index.js
, use Next.js' getServerSideProps
para realizar una llamada a la API cuando se solicite la página. Con shopifyClient.product.fetchAll()
, puede buscar todos los productos en su tienda. Una vez que se devuelven los datos, se pasarán como accesorios a su página de inicio y en su ProductsList
componente:
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Navigation from '../components/Navigation';
import ProductsList from '../components/ProductsList';
import { shopifyClient, parseShopifyResponse } from '../lib/shopify'
export default function Index({products}) {
return (
<Box>
<Navigation />
<Container maxWidth="lg">
<ProductsList products={products} />
</Container>
</Box>
);
}
export const getServerSideProps = async () => {
// Fetch all the products
const products = await shopifyClient.product.fetchAll();
return {
props: {
products: parseShopifyResponse(products),
},
};
};
El formato de sus datos ficticios no es el mismo que devolverá Shopify. Tendrás que adaptarte ProductsList.js
a estos nuevos datos. Dentro de este archivo, creemos un Product
componente y tomemos la identificación, el título, la imagen, el precio y el identificador de su producto.
La respuesta de la API de Shopify puede ser un poco confusa. Por ejemplo, el precio de su producto estará en el variants
objeto. Aquí está la documentación completa para tener una mejor idea de la estructura de un producto.
El producto de Shopify también viene con un handle
campo generado cuando crea productos. Un producto etiquetado como "Conversaciones negras" tendría un asa como black-converses
. Esto es muy útil para el SEO, ya que productId/1
se prefieren las cadenas a las URL. Lo usará en su enrutamiento cuando navegue a la página de su producto.
Aquí está el resultado actualizado:
import * as React from 'react';
import { useRouter } from 'next/router'
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
const Product = ({product, goToProductPage}) => {
const { id, title, images, variants, handle } = product
const { src: productImage } = images[0]
const { price } = variants[0]
return (
<ImageListItem
style={{cursor: 'pointer'}}
onClick={() => goToProductPage(handle)}>
<img
src={`${productImage}?w=250&auto=format`}
srcSet={`${productImage}?w=250&auto=format&dpr=2 2x`}
alt={title}
loading="lazy"
/>
<ImageListItemBar
title={title}
subtitle={<span>Price: {price}</span>}
position="below"
/>
</ImageListItem>
)
}
export default function ProductsList({products}) {
const router = useRouter()
// Navigate to product page with handle i.e /products/black-converses
const goToProductPage = productHandle => router.push(`/products/${productHandle}`)
return (
<Box>
{
(products && products.length > 0) ?
<ImageList cols={5} gap={20}>
{products.map((product) => (
<Product
key={product.handle}
product={product}
goToProductPage={goToProductPage}
/>
))}
</ImageList>:
<Typography variant="body1" align="center">There are no products in this collection</Typography>
}
</Box>
)
};
Ahora, puede pasar a actualizar la página de su producto. En su products/[productHandle].js
, tome el identificador de su consulta. Shopify JS SDK viene con una función llamada fetchByHandle
que obtiene un solo producto de su identificador. Use su productHandle
para obtener su producto y agréguelo a sus accesorios.
Ahora, deberá actualizar la página de su producto como lo hizo con su lista de productos. Puede tomar su título, imagen y precio de la misma manera y usarlos en su página:
import * as React from 'react';
import Image from 'next/image'
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Grid from '@mui/material/Grid'
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Navigation from '../../components/Navigation';
import BreadcrumbsNavigation from '../../components/BreadcrumbsNavigation';
import ProductsList from '../../components/ProductsList';
import { shopifyClient, parseShopifyResponse } from '../../lib/shopify'
export default function ProductPage({product}) {
const { id, title, images, variants, handle } = product
const { src: productImage } = images[0]
const { price } = variants[0]
return (
<Box>
<Navigation />
{product &&
<Container maxWidth="lg">
<BreadcrumbsNavigation title={title} />
<Grid container direction="row">
<Grid item xs={6}>
<Image
src={productImage}
alt={`Picture of ${title}`}
width={500} automatically provided
height={500} automatically provided
/>
</Grid>
<Grid item xs={6}>
<Typography variant="h3" my={2}>{title}</Typography>
<Grid mt={4}>
<Typography variant="h6" component="span">Price: </Typography>
<Typography variant="body1" component="span">{price}</Typography>
</Grid>
<Grid mt={1}>
<Button variant="contained">Add to cart</Button>
</Grid>
</Grid>
</Grid>
</Container>
}
</Box>
);
}
export const getServerSideProps = async ({params}) => {
const { productHandle } = params
// Fetch one product
const product = await shopifyClient.product.fetchByHandle(productHandle);
return {
props: {
product: parseShopifyResponse(product),
},
};
};
Para la página de su colección, lamentablemente necesitará un poco de filtrado. En este momento, Shopify JS SDK solo viene con la opción de buscar productos por ID de colección, pero no con su identificador. En collections/[collectionName].js
, puede usar client.collection.fetchAllWithProducts()
para buscar todas las colecciones y sus productos.
Una vez que los tengas, puedes agarrar el correcto revisando su mango y pasándolo a tus accesorios.
import * as React from 'react';
import Container from '@mui/material/Container';
import Box from '@mui/material/Box';
import Navigation from '../../components/Navigation';
import BreadcrumbsNavigation from '../../components/BreadcrumbsNavigation';
import ProductsList from '../../components/ProductsList';
import { shopifyClient, parseShopifyResponse } from '../../lib/shopify'
export default function CollectionPage({products, collectionName}) {
return (
<Box>
<Navigation />
<Container maxWidth="lg">
<BreadcrumbsNavigation title={collectionName} />
<ProductsList products={products} />
</Container>
</Box>
);
}
export const getServerSideProps = async ({params}) => {
const { collectionName } = params
// Fetch all the collections
const collectionsData = await shopifyClient.collection.fetchAllWithProducts();
const collections = parseShopifyResponse(collectionsData);
// Get the right one
const collection = collections.find(collection => collection.handle === collectionName)
return {
props: {
collectionName,
products: collection.products,
},
};
};
Finalmente, si está utilizando el Image
componente, deberá actualizar next.config.js
para agregar el dominio de imagen de Shopify:
module.exports = {
reactStrictMode: true,
images: {
domains: ['external-content.duckduckgo.com', 'cdn.shopify.com'],
},
};
El resultado final se ve así:
El código base completo se puede encontrar en este repositorio de GitHub.
En este artículo, aprendió cómo configurar una tienda Shopify, habilitar la API Storefront y obtener su token de acceso. Luego descubrió cómo crear una aplicación Next.js y configurar su interfaz mediante la creación de una página de inicio, junto con páginas de colección y productos. Finalmente, instaló Shopify JS SDK y aprendió a usarlo para obtener productos de su tienda en línea.
A partir de ahí, se pueden implementar muchas más funcionalidades. Ya sea que se trate de la creación de un carrito, agregarle productos o completar el pago, el JS SDK de Shopify puede ayudarlo a lograr sus objetivos.
Esta historia se publicó originalmente en https://blog.logrocket.com/build-ecommerce-app-nextjs-shopify/