شرح Webhooks: الحمولات الموقعة وإعادة المحاولة
لقد قمت للتو بدمج مزود دفع خارجي. يكمل المستخدم عملية الشراء، لكن قاعدة بياناتك لا يتم تحديثها أبدًا. لماذا؟ لأن webhook الذي كان من المفترض أن يخطر تطبيقك فشل بصمت. بدون التحقق الصحيح من التوقيع ومنطق إعادة المحاولة، يمكن أن تصبح webhooks مصدرًا لأخطاء غامضة وثغرات أمنية.
في هذه المقالة، سنشرح كيفية عمل webhooks، ولماذا يجب عليك التحقق من الحمولات الموقعة، وكيفية تصميم آلية إعادة محاولة قوية. سنوضح لك أيضًا كيفية اختبار وتصحيح webhooks بفعالية.
ما هي Webhooks؟
webhooks هي عمليات استدعاء HTTP محددة من قبل المستخدم. تسمح لنظام واحد بإرسال بيانات في الوقت الفعلي إلى نظام آخر عند حدوث حدث. على سبيل المثال، عند معالجة دفعة، يرسل مزود الدفع طلب HTTP POST إلى عنوان URL تحدده، يحتوي على تفاصيل الحدث.
على عكس الاستقصاء (polling)، حيث يطلب تطبيقك التحديثات بشكل متكرر، تدفع webhooks البيانات إليك بمجرد توفرها. هذا يجعلها فعالة وشبه فورية.
المشكلة الأمنية: لماذا التحقق من التوقيعات؟
نقاط نهاية webhook هي عناوين URL متاحة للجمهور. يمكن لأي شخص إرسال طلب POST إليها. بدون التحقق، يمكن للمهاجم تزوير طلب، على سبيل المثال، لوضع علامة على طلب كمدفوع دون دفع فعلي. لمنع ذلك، يوقع المزودون كل حمولة بمفتاح سري.
كيف تعمل توقيعات HMAC
يستخدم معظم المزودين HMAC (رمز مصادقة الرسائل القائم على التجزئة) مع SHA-256. يحسبون تجزئة لنص الطلب باستخدام سر مشترك ويضمونها في رأس، مثل X-Signature. يعيد خادمك حساب التجزئة ويقارنها بالرأس. إذا تطابقت، فإن الطلب أصلي.
إليك مثال بسيط في Python:
import hmac
import hashlib
def verify_signature(payload, secret, signature):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
استخدم دائمًا دالة مقارنة بزمن ثابت مثل hmac.compare_digest لتجنب هجمات التوقيت.
الأخطاء الشائعة التي يجب تجنبها
- استخدام السر الخاطئ: يجب أن يكون لكل بيئة (اختبار، حية) سرها الخاص.
- عدم التحقق من الطابع الزمني: بعض المزودين يضمون طابعًا زمنيًا لمنع هجمات إعادة التشغيل. ارفض الطلبات الأقدم من بضع دقائق.
- تسجيل الأسرار: لا تسجل أبدًا السر أو التوقيع الكامل في الإنتاج.
تصميم إعادة محاولة موثوقة
يتم إرسال webhooks عبر الإنترنت، لذا فإن حالات الفشل لا مفر منها. تسقط الشبكات، وتعيد الخوادم التشغيل، وتنتهي مهلة نقاط النهاية. نظام webhook الجيد يعيد محاولة التسليمات الفاشلة مع تأخير أسي.
كيف تعمل إعادة المحاولة عادةً
عادةً ما يعيد المزودون المحاولة بتأخيرات متزايدة: بعد دقيقة واحدة، 5 دقائق، 30 دقيقة، إلخ، حتى الحد الأقصى لعدد المحاولات (مثل 5-10). إذا فشلت جميع المحاولات، يتم وضع علامة على الحدث كفاشل وقد يكون متاحًا لإعادة التشغيل اليدوي.
يجب أن تستجيب نقطة النهاية الخاصة بك بسرعة برمز حالة 2xx لتأكيد الاستلام. إذا أرجعت رمزًا غير 2xx أو انتهت المهلة، فسيعيد المزود المحاولة.
تنفيذ Idempotency
نظرًا لأن إعادة المحاولة يمكن أن تسبب تسليمات مكررة، يجب أن تكون نقطة النهاية الخاصة بك خاملة (idempotent). استخدم معرف الحدث (المضمن في الحمولة أو الرؤوس) لتتبع الأحداث المعالجة. قبل المعالجة، تحقق مما إذا كنت قد تعاملت بالفعل مع هذا المعرف. قم بتخزين المعرفات المعالجة في قاعدة بيانات مع TTL.
مثال على فحص idempotency:
def process_event(event_id, data):
if db.exists(f"processed:{event_id}"):
return # already processed
# process data
db.setex(f"processed:{event_id}", 86400, "1") # expire after 24h
بناء مستقبل webhook: خطوة بخطوة
- كشف نقطة نهاية HTTPS: استخدم TLS لحماية البيانات أثناء النقل.
- التحقق من التوقيع: ارفض الطلبات ذات التوقيعات غير الصالحة برمز 401.
- تحقق من الطابع الزمني: إذا تم توفيره، تأكد من أنه ضمن نافذة مقبولة (مثل 5 دقائق).
- تحليل الحمولة: تحقق من بنية JSON والحقول المطلوبة.
- تحقق من idempotency: استخدم معرف الحدث لتجنب التكرارات.
- المعالجة بشكل غير متزامن: استجب بـ 200 فورًا، ثم عالج في مهمة خلفية لتجنب المهلات.
- التسجيل والمراقبة: سجل حالات الفشل وقم بإعداد التنبيهات.
اختبار Webhooks محليًا
أثناء التطوير، تحتاج إلى طريقة لتلقي webhooks على جهازك المحلي. أدوات مثل ngrok أو localtunnel تنشئ عنوان URL عامًا يعيد التوجيه إلى localhost الخاص بك. بدلاً من ذلك، يقدم العديد من المزودين واجهة سطر أوامر (CLI) لتفعيل أحداث الاختبار.
اختبر دائمًا التحقق من التوقيع بتوقيعات صالحة وغير صالحة. حاكي إعادة المحاولة عن طريق إرجاع رموز خطأ ومراقبة سلوك إعادة المحاولة لدى المزود.
مقارنة: Webhooks مقابل الاستقصاء
| الجانب | Webhooks | الاستقصاء |
|---|---|---|
| الوقت الفعلي | شبه فوري | متأخر بالفاصل الزمني |
| الكفاءة | يرسل البيانات فقط عند حدوث الحدث | طلبات متكررة حتى في حالة عدم وجود بيانات |
| التعقيد | يتطلب نقطة نهاية، التحقق من التوقيع، إعادة المحاولة | أبسط في التنفيذ |
| الموثوقية | تعتمد على آلية إعادة المحاولة | أنت تتحكم في الجدول الزمني |
الأسئلة الشائعة
ما هي حمولة webhook الموقعة؟
تتضمن الحمولة الموقعة توقيعًا تشفيريًا (عادة HMAC) يسمح للمستلم بالتحقق من أن الطلب جاء من المرسل المتوقع ولم يتم العبث به.
كم مرة يجب إعادة محاولة webhook؟
يعيد معظم المزودين المحاولة 5-10 مرات مع تأخير أسي. يعتمد العدد الدقيق على سياسة المزود. يجب أن تكون نقطة النهاية الخاصة بك خاملة للتعامل مع التكرارات.
ما رمز حالة HTTP الذي يجب أن يعيده webhook الخاص بي؟
أعد رمز حالة 2xx (مثل 200 OK) لتأكيد الاستلام الناجح. أي رمز آخر (4xx أو 5xx) يشير إلى الفشل ويؤدي إلى إعادة المحاولة.
هل أنت مستعد لاختبار حمولات webhook الخاصة بك؟ استخدم JSON Formatter الخاص بنا للتحقق من حمولات JSON وطباعتها بشكل جميل بسرعة.