تلخيص سريع
صمم Webhooks موثوقة باستخدام إعادة المحاولة بأسلوب التراجع الأُسي (5-10 محاولات)، ومفاتيح المعاودة الآمنة، والتحقق من توقيع HMAC، ومهلات 5 ثوانٍ. أعد استجابة 2xx فورًا، وقم بالمعالجة بشكل غير متزامن. تطبق PetstoreAPI الحديثة Webhooks لتحديثات الطلبات، وعمليات تبني الحيوانات الأليفة، وإشعارات الدفع مع إعادة محاولة وأمان كاملين.
مقدمة
ترسل Webhook لإخطار عميل بأن حيوانه الأليف قد تم تبنيه. خادم العميل معطل. يفشل الـ Webhook الخاص بك. هل تعيد المحاولة؟ كم مرة؟ وماذا لو استلم العميل الـ Webhook مرتين وقام بخصم المبلغ من العميل مرتين؟
Webhooks هي استدعاءات HTTP تعيد الأحداث إلى عناوين URL للعملاء. إنها بسيطة نظريًا ولكنها معقدة عمليًا. تفشل الشبكات، وتتعطل الخوادم، ويحتوي العملاء على أخطاء. تتطلب Webhooks في بيئات الإنتاج منطق إعادة المحاولة، والمعاودة الآمنة، والأمان، والمراقبة.
تطبق PetstoreAPI الحديثة Webhooks جاهزة للإنتاج لتحديثات الطلبات، وعمليات تبني الحيوانات الأليفة، وإشعارات الدفع. يتضمن كل Webhook منطق إعادة المحاولة، والتحقق من التوقيع، والمعاودة الآمنة.
في هذا الدليل، ستتعلم كيفية تصميم Webhooks موثوقة باستخدام أنماط PetstoreAPI الحديثة.
أساسيات Webhook
Webhooks هي طلبات HTTP POST تُرسل إلى عناوين URL المقدمة من العميل عند وقوع الأحداث.
كيف تعمل Webhooks
1. يقوم العميل بتسجيل عنوان URL الخاص بالـ Webhook:
POST /webhooks
{
"url": "https://client.com/webhooks/petstore",
"events": ["pet.adopted", "order.completed"]
}
2. يقع الحدث (تبني حيوان أليف)
3. يرسل الخادم الـ Webhook:
POST https://client.com/webhooks/petstore
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
{
"event": "pet.adopted",
"data": {
"petId": "019b4132",
"userId": "user-456",
"timestamp": "2026-03-13T10:30:00Z"
}
}
4. يستجيب العميل:
200 OK
مشكلة الموثوقية
يمكن أن تفشل Webhooks لأسباب عديدة:
- خادم العميل معطل
- مهلة الشبكة
- يعيد العميل خطأ 500
- العميل بطيء (يستغرق 30 ثانية)
- يتلقى العميل الـ Webhook ولكنه يتعطل قبل المعالجة
بدون منطق إعادة المحاولة، تُفقد الأحداث. بدون المعاودة الآمنة، تتسبب الـ webhooks المكررة في إجراءات مكررة.
منطق إعادة المحاولة مع التراجع الأُسي
أعد محاولة الـ webhooks الفاشلة مع تأخيرات متزايدة.
استراتيجية التراجع الأُسي
المحاولة 1: فورية
المحاولة 2: بعد ثانية واحدة
المحاولة 3: بعد ثانيتين
المحاولة 4: بعد 4 ثوانٍ
المحاولة 5: بعد 8 ثوانٍ
المحاولة 6: بعد 16 ثانية
لماذا أُسي؟ إذا كان العميل معطلًا، فإن إغراقه بمحاولات إعادة المحاولة لن يساعد. يمنح التراجع الأُسي وقتًا للتعافي.
التطبيق
async function sendWebhook(url, payload, attempt = 1, maxAttempts = 6) {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Webhook-Signature': generateSignature(payload)
},
body: JSON.stringify(payload),
timeout: 5000 // 5 second timeout
});
if (response.ok) {
return { success: true, attempt };
}
// Retry on 5xx errors
if (response.status >= 500 && attempt < maxAttempts) {
const delay = Math.pow(2, attempt - 1) * 1000;
await sleep(delay);
return sendWebhook(url, payload, attempt + 1, maxAttempts);
}
// Don't retry 4xx errors (client error)
return { success: false, status: response.status };
} catch (error) {
// Network error or timeout - retry
if (attempt < maxAttempts) {
const delay = Math.pow(2, attempt - 1) * 1000;
await sleep(delay);
return sendWebhook(url, payload, attempt + 1, maxAttempts);
}
return { success: false, error: error.message };
}
}
متى تتم إعادة المحاولة
أعد المحاولة عند:
- أخطاء الخادم 5xx (500, 502, 503, 504)
- مهلات الشبكة
- رفض الاتصال
- فشل DNS
لا تعد المحاولة عند:
- أخطاء العميل 4xx (400, 401, 404) - لن يقوم العميل بإصلاحها
- نجاح 2xx - لقد نجحت بالفعل
قائمة الرسائل المعطلة
بعد الوصول للحد الأقصى من محاولات إعادة المحاولة، انقل الـ webhooks الفاشلة إلى قائمة الرسائل المعطلة للمراجعة اليدوية:
if (!result.success) {
await deadLetterQueue.add({
url,
payload,
attempts: maxAttempts,
lastError: result.error,
timestamp: new Date()
});
}
المعاودة الآمنة لمنع التكرار
قد يتلقى العملاء نفس الـ Webhook عدة مرات. تمنع المعاودة الآمنة المعالجة المكررة.
مفاتيح المعاودة الآمنة
أدرج معرّفًا فريدًا مع كل Webhook:
{
"id": "webhook_019b4132",
"event": "pet.adopted",
"data": {...}
}
يخزن العميل المعرفات التي تمت معالجتها:
app.post('/webhooks/petstore', async (req, res) => {
const webhookId = req.body.id;
// Check if already processed
const processed = await db.webhooks.findOne({ id: webhookId });
if (processed) {
return res.status(200).json({ message: 'Already processed' });
}
// Process webhook
await processPetAdoption(req.body.data);
// Mark as processed
await db.webhooks.insert({ id: webhookId, processedAt: new Date() });
res.status(200).json({ message: 'Processed' });
});
عمليات المعاودة الآمنة
صمم العمليات لتكون معاودة آمنة:
سيء (غير معاود آمن):
// Charging twice causes double charge
await chargeCustomer(userId, amount);
جيد (معاود آمن):
// Charging with idempotency key prevents double charge
await chargeCustomer(userId, amount, { idempotencyKey: webhookId });
التحقق من التوقيع للأمان
تحقق من أن الـ webhooks تأتي من واجهة برمجة التطبيقات الخاصة بك، وليس من مهاجم.
توقيع HMAC
قم بإنشاء توقيع باستخدام مفتاح سري مشترك:
// Server generates signature
const crypto = require('crypto');
function generateSignature(payload, secret) {
const hmac = crypto.createHmac('sha256', secret);
hmac.update(JSON.stringify(payload));
return hmac.digest('hex');
}
// Include in header
headers['X-Webhook-Signature'] = `sha256=${generateSignature(payload, webhookSecret)}`;
العميل يتحقق من التوقيع:
function verifySignature(payload, signature, secret) {
const expected = generateSignature(payload, secret);
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(`sha256=${expected}`)
);
}
app.post('/webhooks/petstore', (req, res) => {
const signature = req.headers['x-webhook-signature'];
if (!verifySignature(req.body, signature, WEBHOOK_SECRET)) {
return res.status(401).json({ error: 'Invalid signature' });
}
// Process webhook
...
});
التحقق من الطابع الزمني
أدرج طابعًا زمنيًا لمنع هجمات إعادة التشغيل (Replay attacks):
{
"id": "webhook_019b4132",
"timestamp": "2026-03-13T10:30:00Z",
"event": "pet.adopted",
"data": {...}
}
ارفض الـ webhooks القديمة:
const webhookAge = Date.now() - new Date(req.body.timestamp);
if (webhookAge > 5 * 60 * 1000) { // 5 minutes
return res.status(400).json({ error: 'Webhook too old' });
}
معالجة المهلات
قم بتعيين مهلات صارمة لمنع العملاء البطيئين من حظر نظامك.
مهلة 5 ثوانٍ
const response = await fetch(url, {
method: 'POST',
body: JSON.stringify(payload),
timeout: 5000 // 5 seconds
});
لماذا 5 ثوانٍ؟ يجب أن تعود الـ webhooks فورًا. إذا استغرق العميل وقتًا أطول، فإنه يقوم بمعالجة متزامنة (نمط خاطئ).
نمط المعالجة غير المتزامنة
سيء (متزامن):
app.post('/webhooks/petstore', async (req, res) => {
// This takes 30 seconds - webhook will timeout
await processOrder(req.body.data);
await sendEmail(req.body.data);
await updateInventory(req.body.data);
res.status(200).json({ message: 'Processed' });
});
جيد (غير متزامن):
app.post('/webhooks/petstore', async (req, res) => {
// Return immediately
res.status(200).json({ message: 'Received' });
// Process asynchronously
queue.add('process-webhook', req.body);
});
كيف تطبق PetstoreAPI الحديثة Webhooks
تطبق PetstoreAPI الحديثة Webhooks جاهزة للإنتاج.
أحداث Webhook
pet.adopted - تم تبني الحيوان الأليف
pet.status_changed - تغيرت حالة الحيوان الأليف
order.created - تم إنشاء الطلب
order.completed - اكتمل الطلب
payment.succeeded - نجح الدفع
payment.failed - فشل الدفع
حمولة Webhook
{
"id": "webhook_019b4132-70aa-764f-b315-e2803d882a24",
"event": "pet.adopted",
"timestamp": "2026-03-13T10:30:00Z",
"data": {
"petId": "019b4132-70aa-764f-b315-e2803d882a24",
"userId": "user-456",
"orderId": "order-789",
"adoptionDate": "2026-03-13"
},
"apiVersion": "v1"
}
تكوين إعادة المحاولة
- الحد الأقصى للمحاولات: 10
- التراجع: أُسي (ثانية واحدة، ثانيتان، 4 ثوانٍ، 8 ثوانٍ، 16 ثانية، 32 ثانية، 64 ثانية، 128 ثانية، 256 ثانية، 512 ثانية)
- إجمالي نافذة إعادة المحاولة: ~17 دقيقة
- قائمة الرسائل المعطلة بعد الحد الأقصى للمحاولات
الأمان
- توقيع HMAC-SHA256 في رأس
X-Webhook-Signature - التحقق من الطابع الزمني (رفض ما هو أقدم من 5 دقائق)
- يتطلب HTTPS لعناوين URL الخاصة بالـ Webhook
اختبار Webhooks باستخدام Apidog
يدعم Apidog اختبار الـ webhooks.
اختبار تسليم الـ Webhook
- أنشئ نقطة نهاية Webhook وهمية في Apidog
- سجل نقطة النهاية مع PetstoreAPI
- قم بتشغيل الحدث (تبني حيوان أليف)
- تحقق من استلام الـ Webhook
- تحقق من تنسيق الحمولة
اختبار التحقق من التوقيع
// Apidog test script
const signature = pm.request.headers.get('X-Webhook-Signature');
const payload = pm.request.body.raw;
const secret = pm.environment.get('WEBHOOK_SECRET');
const expected = generateSignature(payload, secret);
pm.test('Signature valid', () => {
pm.expect(signature).to.equal(`sha256=${expected}`);
});
اختبار منطق إعادة المحاولة
- أعد خطأ 500 من نقطة النهاية الوهمية
- تحقق من محاولات إعادة المحاولة باستخدام التراجع الأُسي
- تحقق من قائمة الرسائل المعطلة بعد الوصول للحد الأقصى من المحاولات
اختبار المعاودة الآمنة
- استلم الـ Webhook
- أعد استجابة 200
- استلم نفس الـ Webhook مرة أخرى (إعادة محاولة محاكية)
- تحقق من عدم وجود معالجة مكررة
الخلاصة
تتطلب Webhooks الموثوقة ما يلي:
- إعادة المحاولة بأسلوب التراجع الأُسي (5-10 محاولات)
- مفاتيح المعاودة الآمنة لمنع التكرار
- التحقق من توقيع HMAC للأمان
- مهلات 5 ثوانٍ
- معالجة غير متزامنة من جانب العميل
- قائمة الرسائل المعطلة للـ webhooks الفاشلة
تطبق PetstoreAPI الحديثة جميع هذه الأنماط. راجع وثائق الـ Webhook للحصول على أمثلة كاملة.
اختبر الـ webhooks الخاصة بك باستخدام Apidog للتحقق من منطق إعادة المحاولة والتوقيعات والمعاودة الآمنة قبل الانتقال إلى بيئة الإنتاج.
الأسئلة الشائعة
كم عدد محاولات إعادة المحاولة التي يجب أن تحتوي عليها الـ webhooks؟
5-10 محاولات مع التراجع الأُسي. يغطي هذا الانقطاعات المؤقتة (5-17 دقيقة) دون إثقال كاهل العميل.
هل يجب أن تعيد الـ webhooks المحاولة عند أخطاء 4xx؟
لا. تشير أخطاء 4xx إلى مشاكل العميل (عنوان URL خاطئ، فشل المصادقة). لن تؤدي إعادة المحاولة إلى إصلاح هذه المشاكل. أعد المحاولة فقط عند أخطاء 5xx وأخطاء الشبكة.
كم يجب أن تكون مدة مهلات الـ Webhook؟
الحد الأقصى 5 ثوانٍ. يجب أن يعيد العملاء استجابة 200 فورًا ويعالجوا بشكل غير متزامن. تشير المهلات الأطول إلى أن العميل يقوم بمعالجة متزامنة.
ماذا لو لم يستجب العميل أبدًا للـ webhooks؟
بعد الوصول للحد الأقصى من محاولات إعادة المحاولة، انقلها إلى قائمة الرسائل المعطلة. نبه العميل عبر البريد الإلكتروني. فكر في تعطيل الـ webhooks لهذا العميل بعد الإخفاقات المتكررة.
هل يجب أن تكون عناوين URL الخاصة بالـ Webhook عبر HTTPS؟
نعم، يجب دائمًا طلب HTTPS. يمكن اعتراض وتعديل الـ webhooks عبر HTTP. ترفض PetstoreAPI الحديثة عناوين URL الخاصة بالـ Webhook التي تستخدم HTTP.
كيف تمنع هجمات إعادة التشغيل؟
أدرج طابعًا زمنيًا في الحمولة وارفض الـ webhooks التي يزيد عمرها عن 5 دقائق. ادمج هذا مع التحقق من التوقيع.
هل يمكن للعملاء طلب إعادة تسليم الـ Webhook؟
نعم. توفر PetstoreAPI الحديثة نقطة نهاية لإعادة تسليم webhooks محددة: POST /webhooks/{id}/redeliver
كيف تختبر الـ webhooks محليًا؟
استخدم أدوات مثل ngrok لتعريض الخادم المحلي (localhost) للإنترنت، أو استخدم خادم Apidog الوهمي لمحاكاة نقاط نهاية الـ Webhook أثناء التطوير.
