Kembali ke Beranda untuk Developer

Memesan Janji Temu dengan WhatsApp Flows: Membuat Back-End Node.js

27 Februari 2024OlehGafi G & Iryna Wagner

Dengan WhatsApp Flows, Anda bisa menciptakan pesan interaktif untuk pengguna agar menyelesaikan tindakan secara langsung di WhatsApp. Dengan Flows, Anda bisa membuat layar untuk interaksi pengguna. Misalnya, Anda bisa membuat formulir input sederhana untuk mengumpulkan prospek atau mengirimkan ulasan. Selain itu, Anda bisa merancang Flows yang kompleks di beberapa layar untuk menjadwalkan janji temu.

Panduan ini akan mengajarkan cara membuat aplikasi Node.js yang memungkinkan pengguna untuk memesan janji temu melalui WhatsApp Flows. Anda akan membuat Flow di Platform WhatsApp Business, lalu mengonfigurasi webhook untuk menerima tanggapan Flow dan memesan janji temu.

Prasyarat

Agar bisa mengikuti tutorial ini, pastikan Anda memiliki:

Membuat WhatsApp Flow

Ada dua cara untuk membuat WhatsApp Flow: Flows Builder, yang bisa diakses dari Pengelola WhatsApp, dan Flows API. Tutorial ini menggunakan Flows Builder.

Membuat Flow

Di menu kiri dasbor Pengelola WhatsApp Anda, pilih Fitur akun. Lalu, klik Flows.

WhatsApp Manager graphic

Klik Buat Flow, di sudut kanan atas.

create flow graphic

Di kotak dialog yang muncul, lengkapi detail untuk Flow janji temu:

  • Nama — Ketik BookAppointment, atau pilih nama lain yang Anda suka.
  • Kategori — Pilih Pemesanan janji temu.
  • Template — Pilih Pesan janji temu. Anda akan menggunakan template tersebut karena template ini berisi elemen penting untuk memesan janji temu. Elemen ini mencakup layar untuk detail janji temu, entri detail pengguna, ringkasan janji temu, dan tampilan ketentuan perusahaan. Anda bisa menyesuaikan template lebih lanjut agar sesuai dengan penggunaan Anda.
Book an appointment graphic

Klik Kirim untuk membuat Flow.

Anda bisa meninjau Flow di sisi kanan Builder UI. Dengan layar janji temu, pengguna bisa memilih detail janji temu, seperti lokasi dan tanggal. Layar detail adalah tempat pengguna memasukkan informasi mereka. Layar ringkasan menampilkan ringkasan pemesanan janji temu. Layar terakhir menampilkan ketentuan perusahaan.

Flow tetap dalam status draf saat Anda mengeditnya. Saat ini, Anda bisa membagikannya dengan tim Anda untuk tujuan pengujian saja. Untuk membagikannya dengan pemirsa yang lebih luas, Anda perlu menerbitkannya. Namun, Anda tidak bisa mengedit Flow setelah menerbitkannya. Karena Anda masih perlu menambahkan URL endpoint untuk Flow janji temu ini, biarkan Flow ini sebagai draf dahulu dan lanjutkan ke langkah berikutnya yang akan mengonfigurasi endpoint.

Mengonfigurasi Endpoint Flow

Dengan WhatsApp Flows, Anda bisa terhubung dengan endpoint eksternal. Endpoint ini bisa menyediakan data dinamis untuk Flow dan routing kontrol Anda. Endpoint ini juga menerima tanggapan yang dikirim pengguna dari Flow.

Untuk tujuan pengujian, artikel ini menggunakan Glitch untuk menjalankan endpoint. Penggunaan Glitch ini sepenuhnya opsional, dan bukan syarat untuk menggunakan Flows. Anda bisa mengkloning kode endpoint dari GitHub dan menjalankannya di lingkungan yang Anda inginkan.

Akses kode endpoint di Glitch dan lakukan remix untuk mendapatkan domain unik Anda. Untuk melakukan remix, klik Remix di bagian atas halaman. Domain unik akan muncul sebagai pengganti di elemen input pada sisi kanan halaman Glitch.

Sebelum melanjutkan, mari pahami kodenya. Ada empat file JavaScript pada direktori src: encryption.js, flow.js, keyGenerator.js, dan server.js. File entri adalah server.js, jadi mari lihat ini terlebih dahulu.

server.js

File server.js dimulai dengan mengonfigurasi aplikasi Express untuk menggunakan middleware express.json untuk melakukan parsing permintaan JSON yang datang. Lalu, file ini memuat variabel lingkungan yang dibutuhkan untuk endpoint.

const { APP_SECRET, PRIVATE_KEY, PASSPHRASE, PORT = "3000" } = process.env;

APP_SECRET digunakan saat verifikasi tanda tangan. Kode ini membantu Anda memeriksa apakah sebuah pesan datang melalui WhatsApp sehingga aman untuk diproses. Anda akan menambahkannya ke file .env.

Untuk mengakses APP_SECRET Anda, buka dasbor Anda di aplikasi pada Meta untuk Developer. Pada panel navigasi kiri di bagian Pengaturan aplikasi, pilih Dasar. Klik Tampilkan di bagian Rahasia aplikasi dan salin nilai rahasianya. Lalu, kembali ke Glitch, buka file .env, dan buat variabel yang diberi nama APP_SECRET dengan nilai rahasia yang Anda salin.

PRIVATE_KEY membantu mendekripsi pesan yang diterima. Kemudian, PASSPHRASE akan digunakan untuk memverifikasi kode pribadi. Selain kode pribadi, Anda juga membutuhkan kode publik yang sesuai, yang akan Anda unggah nanti. Jangan pernah menggunakan kode pribadi untuk akun produksi Anda di sini. Buat kode pribadi sementara untuk pengujian di Glitch, kemudian gantilah dengan kode produksi di infrastruktur Anda sendiri.

Buat pasangan kode publik-pribadi dengan menjalankan perintah berikut di terminal Glitch. Ganti <your-passphrase> dengan frasa sandi buatan Anda. Akses terminal Glitch dengan mengklik tab TERMINAL di bagian bawah halaman.

node src/keyGenerator.js <your-passphrase>

Salin frasa sandi dan kode pribadi, lalu tempel ke file .env. Klik file yang berlabel .env di bilah sisi kiri, lalu klik ✏️ Plain text di bagian atas. Jangan mengeditnya langsung dari UI karena akan merusak format kode Anda.

Setelah Anda mengatur variabel lingkungan, salin kode publik yang Anda buat dan unggah kode publik melalui Graph API.

File server.js juga berisi endpoint POST yang melakukan berbagai langkah:

  • Memeriksa bahwa kode pribadi tersedia:
       if (!PRIVATE_KEY) { throw new Error('Private key is empty. Please check your env variable "PRIVATE_KEY".'); }
  • Memastikan tanda tangan permintaan menggunakan fungsi isRequestSignatureValid yang ada di bagian bawah file:
if(!isRequestSignatureValid(req)) { // Return status code 432 if request signature does not match. // To learn more about return error codes visit: https://developers.facebook.com/docs/whatsapp/flows/reference/error-codes#endpoint_error_codes return res.status(432).send(); }
  • Mendekripsi pesan yang datang menggunakan fungsi decryptRequest yang ada di file encryption.js:
      let decryptedRequest = null; try { decryptedRequest = decryptRequest(req.body, PRIVATE_KEY, PASSPHRASE); } catch (err) { console.error(err); if (err instanceof FlowEndpointException) { return res.status(err.statusCode).send(); } return res.status(500).send(); } const { aesKeyBuffer, initialVectorBuffer, decryptedBody } = decryptedRequest; console.log("💬 Decrypted Request:", decryptedBody);
  • Memutuskan layar Flow apa yang ditampilkan ke pengguna. Anda akan melihat fungsi getNextScreen secara mendetail nanti.

const screenResponse = await getNextScreen(decryptedBody);

       console.log("👉 Response to Encrypt:", screenResponse);
  • Mengenkripsi tanggapan yang akan dikirimkan ke pengguna:
res.send(encryptResponse(screenResponse, aesKeyBuffer, initialVectorBuffer));

encryption.js

File ini berisi logika untuk mengenkripsi dan mendekripsi pesan yang ditukar demi tujuan keamanan. Tutorial ini tidak akan berfokus pada pengerjaan file tersebut.

keyGenerator.js

File ini membantu membuat kode pribadi dan publik, seperti yang Anda lihat di awal. Sama dengan file encryption.js, tutorial ini tidak akan membahas file keyGenerator.js secara mendetail.

flow.js

Logika untuk menangani Flow dipusatkan di file ini. Langkah ini dimulai dengan objek yang menetapkan nama SCREEN_RESPONSES. Objek tersebut berisi ID layar beserta perinciannya, seperti data preset yang digunakan dalam pertukaran data. Objek ini dibuat dari Flow Builder di bagian "..." > Endpoint > Snippet > Tanggapan. Di objek yang sama, Anda juga memiliki ID lain, SUCCESS, yang dikirim kembali ke perangkat klien saat Flow tersebut berhasil diselesaikan. Ini akan menutup Flow.

Fungsi getNextScreen berisi logika yang memandu endpoint tentang data Flow apa yang ditampilkan ke pengguna. Fungsi ini dimulai dengan mengekstrak data yang diperlukan dari pesan yang didekripsi.

const { screen, data, version, action, flow_token } = decryptedBody;

Endpoint WhatsApp Flows biasanya menerima tiga permintaan:

Anda bisa menemukan detailnya di dokumentasi endpoint.

Fungsi tersebut menangani pemeriksaan kesehatan dan notifikasi kesalahan menggunakan pernyataan if dan menanggapinya dengan sesuai, seperti yang ditunjukkan di snippet berikut:

// handle health check request if (action === "ping") { return { version, data: { status: "active", }, }; } // handle error notification if (data?.error) { console.warn("Received client error:", data); return { version, data: { acknowledged: true, }, }; }
        

Saat pengguna mengklik tombol ajakan bertindak (CTA) Flow, tindakan INIT terpicu. Tindakan ini mengembalikan layar janji temu bersama dengan data. Ini juga menonaktifkan menu pilihan lokasi, tanggal, dan waktu untuk memastikan pengguna mengisi semua kolom.

Misalnya, menu pilihan tanggal hanya aktif saat menu pilihan lokasi diisi. Pengaktifan dan penonaktifan beberapa kolom ini ditangani saat permintaan data_exchange diterima.

// handle initial request when opening the flow and display APPOINTMENT screen if (action === "INIT") { return { ...SCREEN_RESPONSES.APPOINTMENT, data: { ...SCREEN_RESPONSES.APPOINTMENT.data, // these fields are disabled initially. Each field is enabled when previous fields are selected is_location_enabled: false, is_date_enabled: false, is_time_enabled: false, }, }; }

Untuk tindakan data_exchange, struktur switch case digunakan untuk menentukan data apa yang dikirim kembali berdasarkan ID layar. Jika ID layar adalah APPOINTMENT, kolom menu pilihan hanya aktif saat menu pilihan yang sebelumnya dipilih.

// Each field is enabled only when previous fields are selected is_location_enabled: Boolean(data.department), is_date_enabled: Boolean(data.department) && Boolean(data.location), is_time_enabled: Boolean(data.department) && Boolean(data.location) && Boolean(data.date)

Untuk layar DETAILS, judul properti objek data, seperti lokasi dan departemen, diekstrak dari objek SCREEN_RESPONSES.APPOINTMENT.data. Kode ini mengasumsikan ada kecocokan yang valid, jadi ingatlah bahwa kode ini mungkin memunculkan kesalahan jika tidak ditemukan objek yang cocok.

Sekarang, ambil contoh objek lokasi. Pemilihan objek lokasi spesifik ini ditentukan dengan mencocokkan properti id objek dalam array dengan nilai data.location.

const departmentName = SCREEN_RESPONSES.APPOINTMENT.data.department.find( (dept) => dept.id === data.department ).title; const locationName = SCREEN_RESPONSES.APPOINTMENT.data.location.find( (loc) => loc.id === data.location ).title; const dateName = SCREEN_RESPONSES.APPOINTMENT.data.date.find( (date) => date.id === data.date

).title;

Nilai tersebut kemudian digabungkan dan dikembalikan sebagai tanggapan untuk menampilkan layar SUMMARY.

const appointment = `${departmentName} at ${locationName} ${dateName} at ${data.time}`; const details = `Name: ${data.name} Email: ${data.email} Phone: ${data.phone} "${data.more_details}"`; return { ...SCREEN_RESPONSES.SUMMARY, data: { appointment, details, // return the same fields sent from client back to submit in the next step ...data, }, };
        

Setelah layar SUMMARY dikirimkan oleh klien, tanggapan sukses dikirimkan ke perangkat klien sebagai tanda Flow tersebut selesai. flow_token adalah pengidentifikasi unik yang bisa Anda atur saat mengirimkan Flow ke pengguna.

SUMMARY // send success response to complete and close the flow return { ...SCREEN_RESPONSES.SUCCESS, data: { extension_message_response: { params: { flow_token, }, }, }, };

Tidak ada data yang harus ditukar di layar TERMS, jadi endpoint tidak menanganinya.

Menambahkan Endpoint ke Flow

Di kanan atas halaman Glitch, Anda bisa menyalin URL dengan mengklik ikon menu kebab dan memilih Copy Link. Anda juga bisa mendapatkan tautannya dengan mengklik Share di kanan atas.

Buka Flow Editor. Klik Siapkan di banner cokelat yang muncul di bagian atas editor.

Sebuah popup akan muncul yang memungkinkan Anda mengonfigurasi URI endpoint, nomor telepon bisnis, serta aplikasi di Meta untuk Developer. Setelah membuat konfigurasi yang diperlukan, lakukan pemeriksaan kesehatan. Pertama, jalankan pratinjau interaktif dan pastikan Anda memilih Minta data, di bagian Minta data pada layar pertama, pada pengaturan pratinjau interaktif. Langkah ini mengirim permintaan ke endpoint agar mengambil data untuk layar pertama, yang memverifikasi bahwa endpoint tersedia dan Anda telah menerapkan pemeriksaan kesehatan.

Lalu, terbitkan Flow dengan mengklik menu titik-titik (...) dan pilih Terbitkan. Langkah ini akan mengirimkan permintaan pemeriksaan kesehatan ke endpoint Anda dengan action === "ping" untuk memastikan endpoint telah siap sebelum menerbitkan.

Endpoint graphic

Menguji Flow

Setelah menyelesaikan konfigurasi, aktifkan pratinjau interaktif lagi di WhatsApp Builder UI untuk menguji Flow. Dalam popup yang muncul, pilih nomor telepon dan pilih opsi Minta data di bagian Minta data pada layar pertama. Tutup Flow dengan mengklik ikon X untuk mulai menguji Flow secara penuh dari tombol CTA.

Buka log Glitch dengan mengklik tab LOGS. Hapus log dengan mengklik Clear. Lalu, kembali ke pratinjau WhatsApp Builder UI. Klik Tinjau alur. Anda akan melihat tampilan seperti ini:

Preview flow graphic

Kini, kembali ke log Glitch. Anda akan melihat tindakan INIT, token Flow, dan detail lainnya di bagian permintaan yang didekripsi. Ada pula tanggapan untuk mengenkripsi yang dikirim kembali ke Flow pengguna setelah menu pilihan departemen dipilih.

decrypted request graphic

Lanjutkan untuk memilih departemen. Perhatikan bagaimana is_location_enabled diatur ke true dan tindakan telah berubah menjadi data_exchange.

data_exchange graphic

Terus coba Flow dan amati perubahan data pada log Glitch. Log serupa akan dibuat saat pengguna berinteraksi dengan Flow dari perangkat selulernya.

Di bagian selanjutnya, Anda akan membuat webhook yang mengirimkan pesan konfirmasi kepada pengguna saat mereka memesan janji temu.

Menyiapkan Webhook

Saat pengguna menyelesaikan Flow, suatu pesan yang menandai penyelesaian Flow dikirimkan ke webhook yang berlangganan. Dari webhook ini, Anda akan memberi tahu pengguna tentang pemesanan janji temu yang berhasil dengan pesan di obrolan. Serupa dengan endpoint, Anda juga menggunakan Glitch untuk pengujian. Anda bisa mengakses kode ini dan melakukan remix di sini.

Penggunaan Glitch ini sepenuhnya opsional dan bukan syarat untuk menggunakan Flow. Anda bisa mengkloning kode webhook dari GitHub dan menjalankannya di lingkungan yang Anda inginkan.

Mengatur Variabel Lingkungan

Untuk mengatur variabel lingkungan, buka file .env di Glitch. Atur VERIFY_TOKEN ke string yang Anda inginkan, FLOW_ID dengan ID Flow Anda, dan GRAPH_API_TOKEN ke token akses akun WhatsApp Business Anda. Anda bisa mendapatkan token akses dari dasbor aplikasi Anda di Meta untuk Developer saat Anda mengklik Penyiapan API di bagian WhatsApp pada panel navigasi kiri.

API Setup graphic

Di halaman yang melakukan render, klik tombol Salin di bagian kartu Token akses sementara. Tempelkan kodenya di file .env Anda.

Berlangganan Webhook di Dasbor Meta

Di akun Meta untuk Developer Anda, klik menu Konfigurasi di bagian WhatsApp pada panel navigasi kiri.

configuration graphic

Di kartu Webhook, klik Edit. Di dialog yang muncul, tempelkan Glitch URL yang disalin, dan tambahkan /webhook ke URL di kolom URL Callback. Untuk kolom Verifikasi token, tambahkan token dari variabel VERIFY_TOKEN di file .env Anda. Setelah selesai, klik Verifikasi dan simpan. Dialog ini akan tertutup dan kembali ke layar utama. Klik Kelola dan centang kolom pesan. Webhook Anda sudah siap.

Panduan Kode Webhook

Kode ini berisi dua rute: POST /webhook dan GET /webhook. Rute GET menangani permintaan verifikasi webhook dengan cara membandingkan token yang diberikan terhadap token verifikasi yang ditentukan sebelumnya dan menanggapi dengan kode status yang sesuai dan token tantangan.

const verify_token = process.env.VERIFY_TOKEN; // Parse params from the webhook verification request let mode = req.query["hub.mode"]; let token = req.query["hub.verify_token"]; let challenge = req.query["hub.challenge"]; if (mode && token) { if (mode === "subscribe" && token === verify_token) { console.log("WEBHOOK_VERIFIED"); res.status(200).send(challenge); } else { res.sendStatus(403); } }

Rute POST /webhook menangani notifikasi webhook yang masuk. Permintaan webhook bisa memiliki payload berbeda. Jadi, kode di bawah ini membaca pesan dan nomor telepon bisnis dengan mengakses kolom permintaan secara aman jika pesan dan nomor telepon tidak ditentukan.

const message = req.body.entry?.[0]?.changes[0]?.value?.messages?.[0]; const business_phone_number_id =

req.body.entry?.[0].changes?.[0].value?.metadata?.phone_number_id;

Kode ini lalu memeriksa apakah permintaan yang masuk untuk pesan jenis "text" yang berisi kata “appointment”. Jika pesan ini berisi kata ini, Flow dikirimkan kepada pengguna. Pesan Flow dikirimkan dengan flow_action: "data_exchange," yang artinya Flow akan membuat permintaan INIT ke endpoint saat diluncurkan untuk mendapatkan layar dan data awal.

if ( message.type === "text" && // for demo purposes, send the flow message whenever a user sends a message containing "appointment" message.text.body.toLowerCase().includes("appointment") ) { // send flow message as per the docs here https://developers.facebook.com/docs/whatsapp/flows/gettingstarted/sendingaflow#interactive-message-parameters await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, type: "interactive", interactive: { type: "flow", header: { type: "text", text: "Hello there 👋", }, body: { text: "Ready to transform your space? Schedule a personalized consultation with our expert team!", }, footer: { text: "Click the button below to proceed", }, action: { name: "flow", parameters: { flow_id: FLOW_ID, flow_message_version: "3", // replace flow_token with a unique identifier for this flow message to track it in your endpoint & webhook flow_token: "<FLOW_TOKEN_PLACEHOLDER>", flow_cta: "Book an appointment", flow_action: "data_exchange", }, }, }, }, }); } ...

Jika jenis pesan yang masuk bukan "text", kode memeriksa apakah jenis pesannya "interactive." Jenis pesan interaktif "nfm_reply" menandakan bahwa pesan yang masuk adalah tanggapan Flow. Lalu, kode ini mengirim balik pesan “You’ve successfully booked an appointment” kepada pengguna.

... if ( message.type === "interactive" && message.interactive?.type === "nfm_reply" ) { // send confirmation message await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", to: message.from, text: { body: "You've successfully booked an appointment" }, }, }); } ...

Kode ini lalu menandai pesan masuk sebagai telah dibaca agar pengguna bisa melihat centang biru.

... // mark incoming message as read await axios({ method: "POST", url: `https://graph.facebook.com/v18.0/${business_phone_number_id}/messages`, headers: { Authorization: `Bearer ${GRAPH_API_TOKEN}`, }, data: { messaging_product: "whatsapp", status: "read", message_id: message.id, }, }); ...
        

Pengalaman Pengguna

Dalam contoh ini, pengguna mengirimkan pesan ke nomor Anda yang berisi kata “appointment”, lalu menerima pesan Flow. Anda juga bisa memilih untuk mengirim Flow setelah interaksi yang berbeda atau sebagai template pesan.

Pengguna akan menerima pesan Flow dengan tombol CTA untuk memesan janji temu, tempat mereka bisa mengisikan informasi detail mereka. Lalu, mereka menerima pesan konfirmasi saat mereka menyelesaikan Flow.

Sending the Flow to the User graphic

Dalam panduan ini, Anda mempelajari cara menyiapkan WhatsApp Flow untuk penjadwalan janji temu yang lancar. Dengan menggunakan Flow Builder UI, Anda membuat formulir untuk mengumpulkan detail janji temu dari pengguna.

Flows meniadakan kebutuhan untuk mengarahkan ulang pengguna ke situs web eksternal untuk pemesanan janji temu, agar bisa meningkatkan pengalaman pelanggan. Dengan proses yang sederhana ini, pengguna bisa menyelesaikan pemesanan langsung dari dalam WhatsApp. Selain menjadwalkan janji temu, Anda bisa menggunakan WhatsApp Flows untuk mengumpulkan masukan layanan pelanggan, atau membantu pengguna mendaftar promosi atau milis. WhatsApp Flows juga menawarkan fleksibilitas untuk terhubung dengan API eksternal atau aplikasi lain di endpoint Anda.

Membuat WhatsApp Flows mudah dilakukan dengan Flow Builder UI. Namun, Anda juga bisa menggunakan Flow API untuk membuat Flows secara terprogram. Untuk informasi selengkapnya, baca dokumentasi WhatsApp Flows.