شرح Webhooks: الحمولات الموقعة وإعادة المحاولات

Backend2026-10-17TryQuickToolBox

لقد قمت للتو بدمج بوابة دفع. تقول الوثائق: "سنرسل webhook إلى نقطة النهاية الخاصة بك عند نجاح الدفع." قمت بإعداد مسار بسيط، واختبرته محليًا، ثم نشرته. ثم في الإنتاج، لاحظت أحداثًا مكررة، وإشعارات مفقودة، وأخطاء 500 غامضة. تبدو Webhooks بسيطة—حتى لا تكون كذلك.

يشرح هذا الدليل كيف تعمل Webhooks حقًا، ولماذا تهم الحمولات الموقعة، وكيفية بناء منطق إعادة المحاولة الذي يحافظ على تزامن نظامك حتى عند حدوث أخطاء.

ما هو Webhook؟

Webhook هو استدعاء HTTP: يرسل نظام واحد طلب HTTP POST إلى عنوان URL تقدمه عند حدوث حدث معين. على عكس الاستقصاء (polling)، حيث تسأل بشكل متكرر "هل حدث أي شيء؟"، تدفع Webhooks الأحداث إليك في الوقت الفعلي تقريبًا.

تشمل الاستخدامات الشائعة:

المرسل (مثل Stripe، GitHub) هو مزود webhook؛ وخادمك هو مستهلك webhook.

لماذا الحمولات الموقعة ضرورية

نقطة نهاية webhook الخاصة بك يمكن الوصول إليها علنًا. أي شخص يعرف عنوان URL يمكنه إرسال أحداث مزيفة. بدون التحقق، يمكن للمهاجم انتحال حدث "نجاح الدفع" وقد تشحن البضائع مجانًا.

تحل الحمولات الموقعة هذه المشكلة عن طريق تضمين توقيع تشفيري في رؤوس الطلب، يتم إنشاؤه باستخدام سر مشترك معروف فقط لك وللمزود. عند استلام webhook، تعيد حساب التوقيع وتقارنه. إذا تطابقا، فإن الحمولة أصلية.

كيف تعمل توقيعات HMAC

يستخدم معظم المزودين HMAC-SHA256. يحسب المزود:

signature = HMAC-SHA256(secret, payload)

ويرسله في رأس مثل X-Signature. يقوم خادمك بنفس الحساب ويتحقق من التطابق. لأن السر سري، فقط المزود يمكنه إنشاء توقيع صالح.

التحقق من التوقيع عمليًا

إليك مثالًا بسيطًا بـ Node.js باستخدام Express:

const crypto = require('crypto');
const express = require('express');
const app = express();

app.post('/webhook', express.raw({type: 'application/json'}), (req, res) => {
  const signature = req.headers['x-signature'];
  const secret = process.env.WEBHOOK_SECRET;
  const payload = req.body; // raw Buffer

  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }

  // Process the event
  const event = JSON.parse(payload);
  console.log('Received event:', event.type);
  res.status(200).send('OK');
});

app.listen(3000);

مهم: استخدم دائمًا جسم الطلب الخام (raw) للتحقق من التوقيع. إذا قمت بتحليل JSON أولاً، فقد تختلف السلسلة المسلسلة (المسافات البيضاء، ترتيب المفاتيح) ويفشل فحص التوقيع.

المزالق الشائعة

إعادة المحاولات: التعامل مع حالات الفشل بسلاسة

تفشل الشبكات، وتُعاد تشغيل الخوادم، وتحدث عمليات النشر. يتوقع مزودو webhook أن نقطة النهاية الخاصة بك قد تكون غير متاحة مؤقتًا، لذا يعيدون محاولة التسليمات الفاشلة. لكن إعادة المحاولات تقدم تحديات جديدة: أحداث مكررة، وتسليم غير مرتب، وحلقات لا نهائية إذا لم تكن حذرًا.

كيف يعيد المزودون المحاولة

يستخدم معظم المزودين backoff أسيًا: ينتظرون وقتًا قصيرًا بعد الفشل الأول، ثم فترات أطول تدريجيًا. على سبيل المثال، تعيد Stripe المحاولة حتى 3 أيام بتأخيرات متزايدة. تعيد GitHub المحاولة حتى 8 مرات. يختلف الجدول الدقيق، لكن النمط ثابت.

يجب أن تعيد نقطة النهاية الخاصة بك رمز حالة 2xx بسرعة لتأكيد الاستلام. أي حالة أخرى (4xx، 5xx) تشير إلى الفشل وتؤدي إلى إعادة المحاولة.

بناء معالجات Idempotent

لأن إعادة المحاولات يمكن أن تسلم نفس الحدث عدة مرات، يجب أن يكون المعالج الخاص بك idempotent: معالجة نفس الحدث مرتين يجب أن يكون لها نفس تأثير معالجته مرة واحدة.

الاستراتيجيات الشائعة:

مثال باستخدام مجموعة في الذاكرة بسيطة (للتوضيح؛ استخدم مخزنًا دائمًا في الإنتاج):

const processedEvents = new Set();

app.post('/webhook', (req, res) => {
  const event = req.body;
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed');
  }
  // Process event...
  processedEvents.add(event.id);
  res.status(200).send('OK');
});

الرد بسرعة

غالبًا ما يكون لدى المزودين مهلة (مثل 5 ثوانٍ). إذا كان المعالج الخاص بك يقوم بمعالجة ثقيلة، فأكد الاستلام فورًا وعالج بشكل غير متزامن. على سبيل المثال، ادفع الحدث إلى قائمة انتظار وأعد 200.

أفضل الممارسات لـ Webhooks موثوقة

  1. تحقق دائمًا من التوقيعات باستخدام الجسم الخام ومقارنة ثابتة الزمن.
  2. استخدم HTTPS لنقطة النهاية الخاصة بك لمنع التنصت والتلاعب.
  3. نفذ Idempotency للتعامل مع التسليمات المكررة.
  4. سجل كل شيء — الحمولات الواردة، والتوقيعات، وردودك — لتصحيح الأخطاء.
  5. راقب حالات الفشل وقم بإعداد تنبيهات للأخطاء المتكررة.
  6. اختبر بأحداث حقيقية باستخدام لوحات تحكم المزود أو أدوات CLI.
  7. تعامل مع الأحداث غير المرتبة عن طريق تضمين الطوابع الزمنية أو أرقام التسلسل في منطقك.

مقارنة: الاستقصاء مقابل Webhooks

الجانبالاستقصاءWebhooks
زمن الاستجابةيعتمد على الفاصل الزمنيفي الوقت الفعلي تقريبًا
حمل الخادمعالي (طلبات مستمرة)منخفض (فقط عند الأحداث)
التعقيدبسيط للتنفيذيتطلب نقطة نهاية، أمان، إعادة محاولات
الموثوقيةأنت تتحكم في إعادة المحاولاتالمزود يتحكم في إعادة المحاولات

الأسئلة الشائعة

ما هو توقيع webhook؟

توقيع webhook هو تجزئة تشفيرية (عادة HMAC-SHA256) للحمولة، يتم إنشاؤها باستخدام سر مشترك. يسمح للمستقبل بالتحقق من أن الطلب جاء من المرسل المتوقع ولم يتم التلاعب به.

كيف أتعامل مع أحداث webhook المكررة؟

اجعل المعالج الخاص بك idempotent عن طريق تتبع معرفات الأحداث أو استخدام عمليات قاعدة بيانات idempotent بطبيعتها. تحقق مما إذا تمت معالجة حدث بالفعل قبل تطبيق التغييرات.

ماذا يجب أن تعيد نقطة نهاية webhook الخاصة بي؟

أعد رمز حالة 2xx (مثل 200 OK) بمجرد استلام الحدث والتحقق من صحته. إذا كنت بحاجة إلى إجراء معالجة ثقيلة، فأكد الاستلام فورًا وتعامل مع العمل بشكل غير متزامن.

هل أنت مستعد لاختبار حمولات webhook الخاصة بك؟ استخدم JSON Formatter الخاص بنا لفحص والتحقق من بنية JSON للأحداث الواردة.