บทความทั้งหมด
เหตุการณ์ข้อความมือถือที่ผ่านการยืนยันเว็บฮุกแบบ Meta และอุโมงค์ที่ปลอดภัยไปยังแอปพลิเคชันบน localhost
WhatsApp Cloud APIMeta webhookslocalhostwebhook security

ทดสอบ Webhook ของ WhatsApp Cloud API บน localhost

ในการทดสอบเว็บฮุค 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 ทั่วไป อธิบายการจัดการข้อมูลดิบข้ามเฟรมเวิร์ก

เริ่มทันเนลและกำหนดค่าการโทรกลับ

  1. เรียกใช้แอป Next.js ในเครื่อง โดยปกติจะใช้ npm run dev บนพอร์ต 3000
  2. เรียกใช้ npx portpreview 3000 ในเทอร์มินัลที่แยกต่างหาก
  3. ตั้งค่า META_VERIFY_TOKEN เป็นค่าสุ่มและ META_APP_SECRET เป็น App Secret จากการตั้งค่าแอปของ Meta
  4. ในแดชบอร์ดนักพัฒนา Meta ให้เปิดหน้าการกำหนดค่าของผลิตภัณฑ์ WhatsApp
  5. ตั้งค่าการโทรกลับ URL ไปที่ https://YOUR-TUNNEL.portpreview.dev/api/webhooks/whatsapp และป้อนโทเค็นการยืนยันเดียวกัน
  6. หลังจากการยืนยันสำเร็จแล้ว ให้สมัครรับข้อมูลช่อง 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 สื่อ ผู้ติดต่อ และชื่อโปรไฟล์จาก บันทึก
  • จัดเก็บความลับของแอป โทเค็นการเข้าถึง และตรวจสอบโทเค็นเฉพาะในไฟล์สภาพแวดล้อมที่ถูกละเว้นหรือผู้จัดการความลับ
  • จำกัดผู้ที่สามารถดูการดักจับทันเนลและลบออกได้หลังจากเซสชันการแก้ไขข้อบกพร่อง
  • ตรวจสอบวัตถุ ฟิลด์ และตัวระบุบัญชีก่อนดำเนินการดำเนินธุรกิจ

ทันเนลทำให้การวนซ้ำรวดเร็ว แต่ยังนำข้อมูลส่วนบุคคลในรูปแบบการผลิตมาสู่เครื่องของนักพัฒนาอีกด้วย ใช้การควบคุมในรายการตรวจสอบความปลอดภัยของอุโมงค์ ก่อนที่จะทดสอบกับผู้ใช้จริง

คำถามที่พบบ่อย

ฉันจะทดสอบ WhatsApp Cloud API webhook บน localhost ได้อย่างไร
เรียกใช้ตัวจัดการ webhook ของคุณภายในเครื่อง เปิดเผยด้วยทันเนล HTTPS ลงทะเบียน URL เรียกกลับสาธารณะและยืนยันโทเค็นใน Meta สมัครรับข้อความ จากนั้นส่งข้อความทดสอบ
จุดสิ้นสุดการยืนยัน WhatsApp webhook ควรส่งคืนอะไร
สำหรับคำขอ GET ที่ถูกต้อง โดยที่ hub.mode สมัครรับข้อมูลและ hub.verify_token ตรงกัน ให้ส่งคืนค่า hub.challenge เป็นข้อความธรรมดาด้วย HTTP 200
ฉันจะตรวจสอบคำขอ POST webhook ของ WhatsApp ได้อย่างไร
คำนวณ HMAC-SHA256 บนเนื้อหาคำขอดิบที่แน่นอนด้วย Meta App Secret นำหน้าการแยกย่อยฐานสิบหกด้วย sha256= และเปรียบเทียบเวลาอย่างปลอดภัยกับ X-Hub-Signature-256
เหตุใด WhatsApp webhook ที่ตรวจสอบแล้วของฉันจึงไม่ได้รับเหตุการณ์
การยืนยันการโทรกลับไม่ได้สมัครรับทุกช่องโดยอัตโนมัติ ยืนยันว่าบัญชี WhatsApp Business ได้สมัครรับข้อความและผู้ทดสอบและหมายเลขโทรศัพท์ของคุณพร้อมใช้งานสำหรับแอป