إنجاح دمج الويب هوك
خمسة أخطاء تتكرّر في كل عملية دمج تقريبًا، والمعالج في خمسة عشر سطرًا الذي يتفاداها جميعًا.
لا تكتمل عملية الدفع لحظة استدعائكم لها. يمرّ المشتري ببنكه، ويجتاز مصادقة، ثم يعود — أو لا يعود. تصلكم النتيجة عبر الويب هوك، وهذا الإشعار هو الفيصل.
أي أن جودة معالجتكم للويب هوك تقرّر موثوقية تحصيلكم كله. وهذه أكثر خمسة أخطاء نراها.
1. التحقق من التوقيع بعد تحليل الـ JSON
هذا أكثر الأخطاء تكرارًا وأشدّها صمتًا. يُحسب التوقيع على الجسم الخام للطلب. فإن حلّل إطار العمل الـ JSON قبل أن تلمسوه، ثم أعدتم أنتم تسلسل الكائن للتحقق، حصلتم على بايتات مختلفة — مسافة، أو ترتيب مفاتيح، أو رقم معاد التنسيق — فلا يعود التوقيع مطابقًا.
اضبطوا مساركم ليحتفظ بالجسم الخام، وتحقّقوا من التوقيع عليه، ولا تحلّلوا إلا بعد ذلك.
2. الاشتراك في كل شيء
تتيح لكم الواجهة التصريح بقائمة أحداث صريحة. والإغراء هو تعليم الكل «تحسّبًا».
كل حدث تشتركون فيه هو حدث يجب أن يعرف كودكم كيف يتجاهله بنظافة. switch بلا فرع افتراضي، وسجل ينتفخ، وتنبيه ينطلق بلا سبب: القائمة القصيرة هي القائمة الآمنة. اشتركوا فيما تعالجونه، ولا شيء أكثر.
3. عدم إزالة التكرار
قد يصل الإشعار نفسه مرتين. وهذا ليس عيبًا بل ضمانة: نفضّل التسليم مرتين على الضياع مرة.
كل تسليم يحمل معرّف حدث. احفظوه — جدول بقيد تفرُّد يكفي — واخرجوا فورًا إن كنتم قد رأيتموه. وبدون ذلك، يجعلكم إقرار ضائع تشحنون الطلبية نفسها مرتين.
4. إنجاز العمل الثقيل داخل الطلب
إرسال بريد، وتوليد فاتورة PDF، واستدعاء ثلاث خدمات داخلية: إن جرى كل ذلك قبل ردّكم، استغرقت معالجتكم ثوانيَ. فنعتبر التسليم فاشلًا ونعيده. فتعيدون أنتم العمل. وتتسارع الدورة، وتنتهي النقطة معلّقة.
أقرّوا بالاستلام بـ 200 فور كتابتكم الحدث في مكان ما، وأنجزوا الباقي في الخلفية.
5. تصديق إعادة التوجيه بدل الويب هوك
ليست هذه مشكلة في الويب هوك، بل هي المشكلة التي يحلّها الويب هوك. مشترٍ يغلق تبويبه بعد الدفع لن يرى صفحة عودتكم أبدًا. وإن كانت طلبيتكم لا تُؤكَّد إلا هناك، فلن تُؤكَّد أبدًا — بينما وصل المال فعلًا.
المعالج، في خمسة عشر سطرًا
export async function POST(request) {
const raw = await request.text(); // الجسم الخام أولًا
if (!verifySignature(raw, request.headers)) {
return new Response('bad signature', { status: 401 });
}
const event = JSON.parse(raw);
const known = await db.events.findByEventId(event.id);
if (known) return new Response('ok'); // عولج سابقًا
await db.events.insert({ eventId: event.id, payload: event });
await queue.push('handle-payment-event', event.id);
return new Response('ok'); // أُقرّ، والباقي يتبع
}تحقّقوا، وأزيلوا التكرار، وأقرّوا، وفوّضوا. أما بقية منطق أعمالكم فتعيش في المهمة الخلفية، حيث يمكنها أن تفشل وتُعاد دون أي أثر على التسليم.
إعادة تشغيل تسليم فاشل
كان خادمكم في صيانة، وفشل تسليم: لا شيء يضيع. يعرض سجل التسليمات في البوابة كل تسليم — عدد محاولاته، وآخر ردّ من خادمكم (برمز HTTP)، والمحتوى الدقيق للحدث. وإعادة التشغيل تتم بنقرة واحدة — أو عبر الواجهة البرمجية إن أردتم دمجها في مراقبتكم.
عادتان تجعلان إعادة التشغيل بلا خطر. أولًا، إزالة التكرار من النقطة 3: يحمل الحدث المعاد نفس المعرّف، فيتعرف عليه معالجكم ويخرج. ثانيًا، لا تصلحوا حادثًا بتعديل قاعدتكم يدويًا *ثم* إعادة تشغيل الحدث — فستطبقون الأثر نفسه مرتين. أعيدوا التشغيل أولًا، وتحققوا بعده.
النقطة التي تفشل باستمرار ينتهي بها الأمر معلَّقة، لحمايتكم وحمايتنا. والاستئناف يتبع المسار نفسه: أصلحوا، اختبروا بحدث تجريبي، أعيدوا التفعيل، ثم أعيدوا تشغيل التسليمات الفائتة بترتيبها الزمني.
تأمين نقطة الاستقبال بما يتجاوز التوقيع
يوثّق توقيع HMAC المحتوى؛ لكنه لا يغني عن قواعد النظافة حوله.
- HTTPS فقط — ترفض الواجهة البرمجية أصلًا تسجيل نقطة بـ HTTP غير مشفَّر.
- التحقق من الطابع الزمني: كل تسليم مؤرَّخ. ارفضوا ما هو أقدم من بضع دقائق، وتُغلقون الباب أمام إعادة إرسال طلب مُلتقط.
- جواب مقتضب: ليس لنقطتكم ما تحكيه. يكفي
200فارغ؛ فجسم خطأ مفصَّل يفيد المهاجم قبل غيره. - سرّ لكل بيئة: سرّ التوقيع في بيئة الاختبار غير سرّ الإنتاج. وحدث تجريبي موقَّع بمفتاح الاختبار يجب ألا تقبله نقطة إنتاجكم أبدًا.
لا تكلف أي من هذه القواعد أكثر من بضعة أسطر. ومجتمعةً تجعل نقطة ويب هوك معروضة على الإنترنت آمنة كسائر إدماجكم.
نصيحة أخيرة
أرسلوا لأنفسكم حدث اختبار قبل فتح الإنتاج. تقترحه الواجهة على كل نقطة مصرَّح بها، ويقول لكم سجل التسليمات بماذا ردّ خادمكم. اكتشاف مشكلة توقيع حينها يكلّف خمس دقائق؛ واكتشافها في الإنتاج يكلّف يوم تحقيق.
بقلم فريق ChariPay.