شرح Webhooks: الحمولات الموقعة وإعادة المحاولات
لقد قمت للتو بدمج بوابة دفع باستخدام webhooks. كل شيء يعمل في مرحلة الاختبار، ولكن في بيئة الإنتاج، تضيع الأحداث أحيانًا، أو تتلقى إشعارات مكررة تؤدي إلى خصم مزدوج. غالبًا ما يكمن السبب الجذري في كيفية تعاملك مع الحمولات الموقعة وإعادة المحاولات. تشرح هذه المقالة آليات webhooks وتقدم خطوات عملية لبناء تكامل قوي وآمن.
ما هي Webhooks؟
Webhooks هي عمليات استدعاء HTTP (callbacks) يحددها المستخدم. عند حدوث حدث في نظام المصدر (على سبيل المثال، معالجة دفعة)، يرسل طلب HTTP POST إلى عنوان URL تحدده، يحتوي على بيانات الحدث. على عكس الاستقصاء (polling)، تدفع webhooks البيانات في الوقت الفعلي تقريبًا، مما يقلل من زمن الاستجابة وحمل الخادم.
ومع ذلك، تقدم webhooks تحديات: التحقق من الأصالة، والتعامل مع حالات الفشل، وضمان المعالجة مرة واحدة بالضبط. دعنا نتعامل مع كل منها.
الحمولات الموقعة: التحقق من الأصالة
نظرًا لأن نقاط نهاية webhook متاحة للعامة، يمكن لأي شخص إرسال طلبات مزيفة. لمنع ذلك، يوقّع المزودون الحمولة بمفتاح سري. تتحقق من التوقيع للتأكد من أن الطلب أصلي.
كيف تعمل توقيعات HMAC
يستخدم معظم المزودين HMAC (رمز المصادقة القائم على التجزئة) مع SHA-256. يحسب المزود توقيعًا باستخدام جسم الطلب ومفتاح سري مشترك، ثم يضعه في رأس (header) مثل X-Signature. تعيد حساب التوقيع من جانبك وتقارن.
مثال في Python:
import hmac
import hashlib
def verify_signature(payload, secret, received_signature):
expected = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received_signature)
مهم: استخدم جسم الطلب الخام، وليس JSON المُحلل، لأن التحليل يمكن أن يغير المسافات البيضاء ويكسر التوقيع.
المزالق الشائعة
- استخدام البيانات المُحللة: تحقق دائمًا من البايتات الخام.
- هجمات التوقيت: استخدم مقارنة ثابتة الوقت (مثل
hmac.compare_digest). - تجاهل الطوابع الزمنية: بعض المزودين يضمون طابعًا زمنيًا في التوقيع لمنع هجمات إعادة التشغيل. تحقق من أنه ضمن نافذة تسامح (مثل 5 دقائق).
إعادة المحاولات: التعامل مع حالات الفشل بأناقة
قد تكون نقطة النهاية الخاصة بك معطلة مؤقتًا، أو قد تسبب مشكلات الشبكة حالات فشل. تعيد أنظمة webhook الموثوقة محاولة التسليمات الفاشلة مع تراجع أسي.
استراتيجيات إعادة المحاولة
عادةً ما يعيد المزودون المحاولة عند استجابات غير 2xx أو المهلات. الأنماط الشائعة:
| الاستراتيجية | الوصف | مثال |
|---|---|---|
| فاصل ثابت | إعادة المحاولة كل N ثانية | كل 30 ثانية، حتى 5 مرات |
| تراجع أسي | مضاعفة التأخير في كل محاولة | 1s, 2s, 4s, 8s... |
| تراجع أسي مع jitter | إضافة عشوائية لتجنب القطيع المتدافع | 1s ± 0.5s, 2s ± 1s... |
كمستقبل، لا يمكنك التحكم في سياسة إعادة المحاولة للمرسل، ولكن يمكنك تصميم نقطة النهاية الخاصة بك لتكون مرنة.
أفضل الممارسات للمستقبلين
- استجب بسرعة: أعد 2xx في غضون ثوانٍ قليلة. قم بتفويض المعالجة إلى مهمة خلفية.
- كن idempotent: استخدم مفتاح idempotency من الحمولة لتجنب المعالجة المكررة.
- سجل كل شيء: خزّن webhooks الواردة لتصحيح الأخطاء وإعادة التشغيل.
- راقب حالات الفشل: قم بإعداد تنبيهات لحالات الفشل المتكررة.
تنفيذ Idempotency
تحدث التكرارات: قد يعيد المرسل المحاولة لأن استجابتك كانت بطيئة، أو قد تعالج نفس الحدث مرتين عن طريق الخطأ. يضمن idempotency أن معالجة حدث ما عدة مرات لها نفس تأثير المعالجة مرة واحدة.
استخدم معرف حدث فريد (غالبًا ما يتم توفيره في الحمولة أو الرؤوس) وخزّنه في قاعدة بيانات مع قيد فريد. قبل المعالجة، تحقق مما إذا كان المعرف موجودًا؛ إذا كان كذلك، تخطاه.
def process_event(event_id, data):
if EventLog.exists(event_id):
return # already processed
EventLog.create(event_id)
# process data...
للأنظمة ذات الإنتاجية العالية، استخدم قفلًا موزعًا أو معاملة قاعدة بيانات لتجنب ظروف التسابق.
اعتبارات الأمان
بالإضافة إلى التحقق من التوقيع، ضع في اعتبارك:
- HTTPS: استخدم TLS دائمًا لمنع التنصت.
- قائمة IP المسموح بها: إذا نشر المزود نطاقات IP، قيد الطلبات الواردة.
- تحديد المعدل: احمِ نقطة النهاية الخاصة بك من إساءة الاستخدام.
- التحقق من الحمولة: حتى بعد التحقق من التوقيع، تحقق من مخطط البيانات لتجنب هجمات الحقن.
اختبار Webhooks محليًا
أثناء التطوير، تحتاج إلى عنوان URL عام لتلقي webhooks. أدوات مثل ngrok أو localtunnel تعرض خادمك المحلي. بدلاً من ذلك، يقدم العديد من المزودين CLI لإعادة توجيه الأحداث إلى localhost الخاص بك.
حاكي حالات الفشل لاختبار التعامل مع إعادة المحاولة: أعد أخطاء 500 عن قصد ولاحظ نمط إعادة المحاولة للمزود.
الأسئلة الشائعة
كيف أتحقق من توقيع webhook؟
استخدم HMAC مع المفتاح السري المشترك. احسب تجزئة جسم الطلب الخام وقارنها برأس التوقيع باستخدام دالة مقارنة ثابتة الوقت.
ماذا أفعل إذا تلقيت webhooks مكررة؟
نفذ idempotency عن طريق تخزين معرفات الأحداث. تحقق مما إذا كان الحدث قد تمت معالجته بالفعل قبل تنفيذ منطق الأعمال.
هل يمكنني الاعتماد على إعادة محاولات webhook وحدها للموثوقية؟
لا. تساعد إعادة المحاولات ولكنها لا تضمن التسليم. للأحداث الحرجة، اجمع webhooks مع احتياطي استقصاء أو قائمة انتظار رسائل تستمر في الأحداث.
عند تصحيح حمولات webhook، قد تحتاج إلى فحص بيانات JSON. استخدم JSON Formatter الخاص بنا لطباعة وتنسيق الحمولات والتحقق منها بسرعة.