ในการทดสอบเว็บฮุค WhatsApp Cloud API บนโลคอลโฮสต์ ให้เปิดเผยตำแหน่งข้อมูลในเครื่องของคุณผ่าน HTTPS ใช้ความท้าทายในการตรวจสอบ GET ของ Meta จากนั้นตรวจสอบคำขอ POST แต่ละรายการ X-Hub-Signature-256 กับเนื้อหาดิบ ลงทะเบียน URL ช่องสัญญาณใน แอป Meta สมัครบัญชี WhatsApp Business ไปที่ messages และส่งข้อความทดสอบเพื่อรับเพย์โหลดจริงโดยไม่ต้องปรับใช้
WhatsApp webhooks ใช้ขั้นตอนการตรวจสอบที่แตกต่างกันสองขั้นตอน
ความแตกต่างที่สำคัญที่สุดคือการตั้งค่า webhook และการจัดส่ง webhook นั้นได้รับการตรวจสอบสิทธิ์ที่แตกต่างกัน ในระหว่างการตั้งค่า Meta จะส่งคำขอ GET ที่มี hub.mode, hub.verify_token และ hub.challenge ตำแหน่งข้อมูลของคุณจะเปรียบเทียบโทเค็นการยืนยันและส่งกลับการท้าทายเป็นข้อความธรรมดา หลังจากนั้น การส่งมอบกิจกรรมจะเป็นคำขอ POST สิ่งเหล่านั้นควรได้รับการรับรองความถูกต้องโดยการตรวจสอบความถูกต้องของลายเซ็น HMAC ที่สร้างด้วย Meta App Secret ของคุณ
โทเค็นการตรวจสอบคือสตริงสุ่มที่คุณเลือก ไม่ใช่โทเค็นการเข้าถึง WhatsApp และไม่ใช่ความลับของแอป การส่งคืนการท้าทายเป็นการพิสูจน์การควบคุมจุดสิ้นสุดการโทรกลับ มันไม่ได้ตรวจสอบคำขอ POST ในอนาคต คู่มือ WhatsApp webhook อย่างเป็นทางการของ Meta ครอบคลุมการกำหนดค่าการโทรกลับ การสมัครรับข้อมูล และช่อง webhook
สร้างจุดสิ้นสุดเราเตอร์ของแอป Next.js
เส้นทางด้านล่างจัดการทั้งสองเฟส การอ่านข้อมูล POST ด้วย request.text() จะรักษาไบต์ที่แน่นอนที่จำเป็นสำหรับการตรวจสอบลายเซ็น
// app/api/webhooks/whatsapp/route.ts
import crypto from 'node:crypto';
import { NextRequest } from 'next/server';
export const runtime = 'nodejs';
export async function GET(request: NextRequest) {
const mode = request.nextUrl.searchParams.get('hub.mode');
const token = request.nextUrl.searchParams.get('hub.verify_token');
const challenge = request.nextUrl.searchParams.get('hub.challenge');
if (mode === 'subscribe' && token === process.env.META_VERIFY_TOKEN) {
return new Response(challenge ?? '', { status: 200 });
}
return new Response('Forbidden', { status: 403 });
}
export async function POST(request: Request) {
const rawBody = await request.text();
const supplied = request.headers.get('x-hub-signature-256') ?? '';
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.META_APP_SECRET!)
.update(rawBody)
.digest('hex');
const a = Buffer.from(supplied);
const b = Buffer.from(expected);
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return new Response('Invalid signature', { status: 401 });
}
const payload = JSON.parse(rawBody);
await enqueueWhatsAppPayload(payload);
return new Response('EVENT_RECEIVED', { status: 200 });
}
อย่าเรียก request.json() จากนั้นสร้าง JSON ใหม่สำหรับ HMAC ช่องว่าง การ Escape หรือการเรียงลำดับคีย์สามารถเปลี่ยนแปลงได้ ทำให้เกิดการแยกย่อยที่แตกต่างกัน หากคุณใช้ Express ให้บันทึก Buffer ก่อนตัวแยกวิเคราะห์ JSON ส่วนกลาง คำแนะนำลายเซ็น webhook ทั่วไป อธิบายการจัดการข้อมูลดิบข้ามเฟรมเวิร์ก
เริ่มทันเนลและกำหนดค่าการโทรกลับ
- เรียกใช้แอป Next.js ในเครื่อง โดยปกติจะใช้
npm run devบนพอร์ต 3000 - เรียกใช้
npx portpreview 3000ในเทอร์มินัลที่แยกต่างหาก - ตั้งค่า
META_VERIFY_TOKENเป็นค่าสุ่มและMETA_APP_SECRETเป็น App Secret จากการตั้งค่าแอปของ Meta - ในแดชบอร์ดนักพัฒนา Meta ให้เปิดหน้าการกำหนดค่าของผลิตภัณฑ์ WhatsApp
- ตั้งค่าการโทรกลับ URL ไปที่
https://YOUR-TUNNEL.portpreview.dev/api/webhooks/whatsappและป้อนโทเค็นการยืนยันเดียวกัน - หลังจากการยืนยันสำเร็จแล้ว ให้สมัครรับข้อมูลช่อง
messagesสำหรับบัญชี WhatsApp Business
อุโมงค์จะต้องยังคงทำงานอยู่ในระหว่างการท้าทาย GET และการส่งมอบ POST ครั้งต่อไป URL ที่คัดลอกมาจากเซสชันเก่าอาจแก้ไขได้แต่จะไม่ส่งต่อไปยังเครื่องของคุณอีกต่อไป ดังนั้น โปรดยืนยันการโทรกลับที่แน่นอนทุกครั้งที่อุโมงค์ในตัวเครื่องเปลี่ยนแปลง
ทดสอบความท้าทาย GET อย่างอิสระ
ก่อนใช้แดชบอร์ด ให้สร้างคำขอซ้ำในเครื่อง:
curl -i \
"http://localhost:3000/api/webhooks/whatsapp?hub.mode=subscribe&hub.verify_token=${META_VERIFY_TOKEN}&hub.challenge=123456"
การตอบสนองที่ถูกต้องคือสถานะ 200 ที่มีเนื้อหา 123456 ไม่ใช่ JSON และไม่ใช่ "123456" พร้อมเครื่องหมายคำพูด หากโทเค็นไม่ถูกต้อง 403 ก็ถือว่าเหมาะสม อย่าบันทึกพารามิเตอร์การค้นหาเนื่องจากโทเค็นการยืนยันปรากฏขึ้นที่นั่น
ทำความเข้าใจเพย์โหลดข้อความก่อนที่จะเขียนตรรกะทางธุรกิจ
WhatsApp หุ้มข้อมูลลึกหลายระดับ การแจ้งเตือนโดยทั่วไปจะมี object: "whatsapp_business_account", อาร์เรย์ entry, อาร์เรย์ changes และการเปลี่ยนแปลงที่มี field คือ messages ภายใน value เนื้อหาของผู้ใช้ขาเข้าจะปรากฏใน messages; การอัปเดตการจัดส่ง การอ่าน และความล้มเหลวสำหรับข้อความที่คุณส่งจะปรากฏใน statuses.
for (const entry of payload.entry ?? []) {
for (const change of entry.changes ?? []) {
if (change.field !== 'messages') continue;
for (const message of change.value.messages ?? []) {
await handleInboundMessage({
id: message.id,
from: message.from,
type: message.type,
text: message.text?.body,
});
}
for (const status of change.value.statuses ?? []) {
await updateDeliveryStatus(status.id, status.status);
}
}
}
อย่าถือว่าทุกการแจ้งเตือนมีข้อความตัวอักษร รูปภาพ เสียง เอกสาร สถานที่ การตอบกลับแบบโต้ตอบ ข้อความระบบ และเพย์โหลดเฉพาะสถานะจะมีรูปร่างที่แตกต่างกัน ให้ผู้มอบหมายงานคีย์โดย message.type ตรวจสอบฟิลด์ตัวเลือก และเก็บประเภทเหตุการณ์ที่ไม่รู้จักไว้สำหรับการตรวจสอบ แทนที่จะขัดข้อง
ตรวจสอบลายเซ็น POST อย่างถูกต้อง
ค่า X-Hub-Signature-256 ใช้แบบฟอร์ม sha256=<hex digest> คำนวณ HMAC-SHA256 บนไบต์คำขอดิบโดยใช้ Meta App Secret โทเค็นการเข้าถึง WhatsApp แบบถาวรหรือชั่วคราวใช้สำหรับการโทร Graph API ไม่ใช่คีย์ HMAC ใช้การเปรียบเทียบเวลาคงที่และปฏิเสธลายเซ็นที่ขาดหายไป
เปิดใช้งานการยืนยันในท้องถิ่นไว้ ใครก็ตามที่เรียนรู้ URL ของช่องสัญญาณสามารถโพสต์ JSON ลงไปได้ตามใจชอบ หากไม่มีการตรวจสอบ เหตุการณ์ปลอมแปลงอาจทำให้เกิดการตอบกลับอัตโนมัติ เปลี่ยนแปลงบันทึก CRM หรือเปิดเผยสถานะของลูกค้า สับเปลี่ยน App Secret หากมีการคอมมิต พิมพ์ หรือแชร์โดยไม่ได้ตั้งใจ
รับทราบอย่างรวดเร็วและขจัดข้อความที่ซ้ำกัน
ส่งคืน 200 หลังจากตรวจสอบสิทธิ์และเข้าคิวกิจกรรมอย่างคงทน อย่ารอขณะดาวน์โหลดสื่อ โทร LLM หรืออัปเดตบริการต่างๆ ผู้ให้บริการลองส่งอีกครั้งเมื่อการตอบรับล้มเหลว และความคลุมเครือของเครือข่ายหมายถึงการทำซ้ำเป็นเรื่องปกติ
ใช้ข้อความ WhatsApp id เป็นคีย์ idempotency สำหรับข้อความขาเข้าและออบเจ็กต์สถานะ วางข้อจำกัดเฉพาะเกี่ยวกับรหัสที่ประมวลผล การเปลี่ยนสถานะสามารถคืบหน้าไปอย่างถูกต้องตามกฎหมายจากส่งไปยังส่งเพื่ออ่าน ดังนั้น ขจัดความซ้ำซ้อนของการเปลี่ยนที่เกี่ยวข้องแต่ละรายการโดยไม่ละทิ้งสถานะในภายหลัง
แก้ไขปัญหาการตั้งค่าเว็บฮุคของ WhatsApp
ไม่สามารถตรวจสอบ URL โทรกลับได้
ทดสอบเส้นทาง GET ผ่าน URL สาธารณะ ตรวจสอบให้แน่ใจว่ายอมรับ GET เปรียบเทียบโทเค็นการตรวจสอบที่แน่นอน และตอบกลับด้วยความท้าทายเท่านั้น การเปลี่ยนเส้นทาง มิดเดิลแวร์สำหรับให้สิทธิ์ การเขียนภาษาใหม่ หรือ Wrapper JSON อาจทำให้การยืนยันเสียหายได้ ยืนยันว่าตัวแปรสภาพแวดล้อมถูกโหลดโดยกระบวนการ dev ที่ทำงานอยู่
การยืนยันสำเร็จ แต่ไม่มีข้อความมาถึง
การยืนยันการโทรกลับเพียงอย่างเดียวไม่ได้สมัครรับบัญชี WhatsApp Business ไปยังช่องต่างๆ ยืนยันการสมัคร messages ในแดชบอร์ดและหมายเลขโทรศัพท์เป็นของแอปและบัญชีที่คาดหวัง ส่งข้อความจากผู้รับที่ได้รับอนุญาตหากแอปยังอยู่ในโหมดการพัฒนา
ทุก POST ไม่ผ่านการตรวจสอบลายเซ็น
สาเหตุปกติคือการใช้โทเค็นการเข้าถึงแทน App Secret, การแฮช JSON ที่แยกวิเคราะห์, การละเว้น sha256= คำนำหน้า หรือการเปรียบเทียบการเข้ารหัสที่แตกต่างกัน บันทึกความยาวของเนื้อหาและดูว่ามีส่วนหัวอยู่หรือไม่ แต่อย่าพิมพ์ข้อมูลลับหรือเพย์โหลดของลูกค้าทั้งหมด
ข้อความทำงานได้ แต่การจัดการสื่อล้มเหลว
การแจ้งเตือนสื่อจะมี ID ซึ่งไม่จำเป็นต้องเป็นไบต์ของไฟล์ ดึงข้อมูลสื่อผ่าน Graph API ด้วยโทเค็นการเข้าถึงที่ถูกต้อง จากนั้นดาวน์โหลด รักษาเวิร์กโฟลว์ที่ช้าลงให้อยู่นอกเส้นทางการตอบรับของ Webhook
ปลายทางในเครื่องเห็นเหตุการณ์ที่ซ้ำกัน
ตรวจสอบสถานะการตอบสนองและเวลาในการตอบสนอง เพิ่มค่าเดิมที่คงทน และเล่นซ้ำเหตุการณ์ที่บันทึกไว้หนึ่งเหตุการณ์หลังจากการแก้ไขแต่ละครั้ง คู่มือการเล่นซ้ำ webhook แสดงวิธีหลีกเลี่ยงการส่งข้อความจริงใหม่สำหรับการเปลี่ยนแปลงโค้ดทุกครั้ง
ปกป้องข้อมูลลูกค้าในระหว่างการทดสอบในพื้นที่
- ใช้หมายเลขโทรศัพท์ทดสอบและการสนทนาสังเคราะห์หากเป็นไปได้
- ปกปิดหมายเลขโทรศัพท์ เนื้อหาข้อความ URL สื่อ ผู้ติดต่อ และชื่อโปรไฟล์จาก บันทึก
- จัดเก็บความลับของแอป โทเค็นการเข้าถึง และตรวจสอบโทเค็นเฉพาะในไฟล์สภาพแวดล้อมที่ถูกละเว้นหรือผู้จัดการความลับ
- จำกัดผู้ที่สามารถดูการดักจับทันเนลและลบออกได้หลังจากเซสชันการแก้ไขข้อบกพร่อง
- ตรวจสอบวัตถุ ฟิลด์ และตัวระบุบัญชีก่อนดำเนินการดำเนินธุรกิจ
ทันเนลทำให้การวนซ้ำรวดเร็ว แต่ยังนำข้อมูลส่วนบุคคลในรูปแบบการผลิตมาสู่เครื่องของนักพัฒนาอีกด้วย ใช้การควบคุมในรายการตรวจสอบความปลอดภัยของอุโมงค์ ก่อนที่จะทดสอบกับผู้ใช้จริง
