رفتن به محتوای اصلی

وب‌هوک‌ها

تأیید چالش اشتراک، اعتبارسنجی امضای بار داده، پاسخ سریع، و مدیریت تلاش‌های مجدد، رویدادهای تکراری و تحویل بی‌ترتیب.

تأیید اشتراک

پیش از آنکه متا رویدادها را تحویل دهد، مالکیت نقطهٔ پایانی را با یک درخواست GET حامل یک چالش بررسی می‌کند. چالش را تنها در صورتی بازگردانید که توکن تأیید مطابقت داشته باشد.

کد نمونهٔ توضیحی — تأیید اشتراک
export async function GET(request: Request) {
  const params = new URL(request.url).searchParams

  const mode = params.get('hub.mode')
  const token = params.get('hub.verify_token')
  const challenge = params.get('hub.challenge')

  const expected = process.env.META_WEBHOOK_VERIFY_TOKEN

  if (mode !== 'subscribe' || !token || !expected) {
    return new Response('Forbidden', { status: 403 })
  }

  if (!timingSafeEqual(token, expected)) {
    return new Response('Forbidden', { status: 403 })
  }

  // چالش را بی‌کم‌وکاست به‌صورت متن ساده بازگردانید.
  return new Response(challenge ?? '', { status: 200 })
}

اعتبارسنجی امضای بار داده

هر تحویل رویداد یک سرایند امضای HMAC دارد که روی بدنهٔ خام و دقیق درخواست با کلید محرمانهٔ اپلیکیشن محاسبه شده است. بار دادهٔ بدون امضا یا با امضای نادرست باید پیش از آنکه به‌عنوان ورودی مورد اعتماد تجزیه شود رد گردد.

کد نمونهٔ توضیحی — اعتبارسنجی امضا
import crypto from 'node:crypto'

export async function POST(request: Request) {
  // بدنهٔ خام را بخوانید. تجزیه و سپس سریال‌سازی مجدد، HMAC را خراب می‌کند.
  const raw = await request.text()
  const header = request.headers.get('x-hub-signature-256')

  if (!header?.startsWith('sha256=')) {
    return new Response('Unauthorized', { status: 401 })
  }

  const expected = crypto
    .createHmac('sha256', process.env.META_APP_SECRET!)
    .update(raw, 'utf8')
    .digest('hex')

  const received = header.slice('sha256='.length)

  const a = Buffer.from(received, 'hex')
  const b = Buffer.from(expected, 'hex')

  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return new Response('Unauthorized', { status: 401 })
  }

  const event = JSON.parse(raw)
  await enqueue(event)

  // بی‌درنگ تأیید کنید؛ پردازش را غیرهمزمان انجام دهید.
  return new Response('OK', { status: 200 })
}
  • مقدار HMAC را روی بایت‌های خام و پیش از هر تجزیهٔ JSON یا تغییر بدنه محاسبه کنید.
  • چکیده‌ها را با مقایسهٔ زمان‌ثابت بسنجید، هرگز با تساوی رشته‌ای.
  • هر درخواستی با امضای غایب، بدشکل یا نامطابق را رد کنید.
  • پیش از انتشار در محیط تولید، نام سرایند امضا و الگوریتم کنونی را در مستندات رسمی توسعه‌دهندگان متا تأیید کنید.

پاسخ سریع

متا انتظار تأیید دریافت را در یک بازهٔ زمانی کوتاه دارد. کمینهٔ کار را به‌صورت همزمان انجام دهید: امضا را بررسی کنید، بار داده را ذخیره کنید، کد ۲۰۰ برگردانید. تمام منطق کسب‌وکار پس از آن انجام می‌شود.

  1. امضا را بررسی کنید.
  2. رویداد خام را در ذخیره‌سازی پایدار یا یک صف بنویسید.
  3. بی‌درنگ کد ۲۰۰ برگردانید.
  4. رویداد را در یک کارگر پس‌زمینه با سیاست تلاش مجدد مخصوص خودش پردازش کنید.

تلاش مجدد، رویدادهای تکراری و ترتیب

تحویل حداقل یک‌بار است. یک رویداد می‌تواند بیش از یک‌بار برسد و رویدادهای مرتبط می‌توانند بی‌ترتیب برسند. هندلرها باید خودتوان باشند.

حذف تکرار بر پایهٔ یک شناسهٔ پایدار
شناسهٔ رویداد ارائه‌دهنده را با یک محدودیت یکتایی ثبت کنید. اگر درج تعارض داشت، تأیید کنید و متوقف شوید؛ دو بار پردازش نکنید.
نوشتن‌ها را خودتوان کنید
به‌جای درج کورکورانه، از upsert کلیدگذاری‌شده با شناسهٔ ارائه‌دهنده استفاده کنید تا یک بازپخش نتواند گفت‌وگو یا مخاطب تکراری بسازد.
در برابر به‌روزرسانی‌های کهنه محافظت کنید
پیش از بازنویسی یک رکورد، مهرهای زمانی رویداد را مقایسه کنید. رویدادی را که قدیمی‌تر از وضعیت موجود شماست نادیده بگیرید.
هرگز به ترتیب رسیدن تکیه نکنید
ویرایش یک پیام می‌تواند پیش از خود آن پیام برسد. به‌جای توالی دریافت، با استفاده از شناسه‌ها و مهرهای زمانی هم‌سان‌سازی کنید.
کد نمونهٔ توضیحی — پردازش خودتوان
const inserted = await db
  .insertInto('webhook_events')
  .values({ providerEventId: event.id, payload: event })
  .onConflict((c) => c.column('providerEventId').doNothing())
  .executeTakeFirst()

if (inserted.numInsertedRows === 0n) {
  // پیش‌تر دیده شده است. بدون پردازش مجدد تأیید کنید.
  return
}

await applyEvent(event)

مدیریت خطا

  • برای خطای امضا کد ۴۰۱ و برای خطای تأیید کد ۴۰۳ برگردانید؛ این موارد را خودتان تکرار نکنید.
  • خطاهای گذرای پردازش را با تأخیر نمایی و پراکندگی تصادفی در کارگر تکرار کنید، نه در هندلر درخواست.
  • رویدادهایی که تلاش‌های مجددشان تمام شده را همراه با بار دادهٔ کامل برای بررسی به مخزن نامه‌های مرده منتقل کنید.
  • برای نرخ فزایندهٔ نامه‌های مرده هشدار تنظیم کنید، زیرا معمولاً نشانهٔ تغییر ساختار داده است نه یک خطای منفرد.
  • برای هر رویداد یک شناسهٔ همبستگی ثبت کنید تا یک تحویل واحد سرتاسر قابل ردیابی باشد.