يا جماعة الخير، خليني أحكيلكم قصة صارت معي قبل كم سنة، قصة علّمتني درس ما بنساه بحياتي. كنا وقتها شغالين على بوابة دفع إلكتروني جديدة لواحد من العملاء، وكان الحماس واصل للسما. بعد شهور من السهر والقهوة والتكويد، أطلقنا المشروع وكنا مبسوطين على الآخر.
في الأسبوع الأول، كل شي كان تمام التمام. لكن فجأة، بلشت توصلنا إيميلات من خدمة العملاء، إيميلات معصبة شوي. عميل بحكي “انخصم مني المبلغ مرتين!”، وعميلة ثانية بتبعت سكرين شوت لحسابها البنكي وبتسأل “ليش ساحبين مني 50 دولار مرتين لنفس الطلب؟”. بالبداية فكرناها حالات فردية، خطأ بسيط. بس لما صاروا 5 حالات، وبعدين 10، عرفت إنه “ولعت الدنيا” وفي مصيبة تقنية كبيرة.
قعدنا أنا والفريق نحلل السجلات (logs)، وشفنا نمط غريب: العميل بضغط على زر “ادفع الآن”، والنت عنده بطيء شوي، فما بتوصله رسالة التأكيد بسرعة. من خوفه أو قلقه، برجع بضغط على الزر مرة ثانية. نظامنا، اللي كان وقتها “غشيم” شوي، استقبل الطلبين على إنهم معاملتين منفصلتين، وخصم المبلغ مرتين. تخيلوا حجم الصداع: عملاء غاضبون، وفريق مالي بده يعمل تسويات يدوية معقدة، وسمعتنا على المحك. يومها، حسيت بغصة حقيقية، وقلت لحالي: “يا أبو عمر، لازم في حل لهالمشكلة، الشغلة مش لعبة”. ومن هنا بدأت رحلتي مع منقذي الصغير: الـ Idempotency Key.
ما هو مفتاح عدم التكرار (Idempotency Key)؟ حامي حمى المعاملات المالية
قبل ما ندخل في التفاصيل التقنية، خلينا نبسّط المفهوم. كلمة “Idempotent” بالإنجليزية، أو “عدم التكرار” بتبسيطنا، بتعني إنك لو كررت نفس الفعل أكتر من مرة، النتيجة النهائية بتكون نفسها كأنك عملته مرة واحدة بس.
أفضل مثال هو زر المصعد. لما تضغط على زر طلب المصعد، هو بستجيب وبيجي. لو رجعت ضغطت عليه 10 مرات وهو جاي بالطريق، هل رح يجي أسرع؟ لأ. هل رح يوصل 10 مصاعد؟ لأ. الفعل تم مرة واحدة، وتكراره ما بغير النتيجة. هذا هو جوهر الـ Idempotency.
في عالم الـ API والمدفوعات، مفتاح عدم التكرار (Idempotency Key) هو عبارة عن مُعرّف فريد (Unique ID) ببعته العميل (تطبيق الموبايل أو الموقع) مع كل طلب ممكن يكون حساس، زي طلب الدفع. السيرفر بستخدم هاد المفتاح عشان يتأكد: “هل أنا شفت هاد الطلب من قبل؟”. لو الجواب “نعم”، بتجاهل الطلب الجديد وبرجع نفس نتيجة الطلب الأصلي. لو الجواب “لأ”، بنفذ الطلب وبحفظ نتيجته مربوطة بهاد المفتاح للمستقبل.
كيف يعمل السحر؟ آلية عمل الـ Idempotency Key خلف الكواليس
العملية بسيطة ومنطقية جداً، وبتتقسم لـ 3 خطوات رئيسية. خلينا نمشي فيها خطوة بخطوة.
الخطوة الأولى: العميل يُنشئ المفتاح 🔑
كل القصة بتبدأ من عند العميل (Client-side). قبل ما يبعت طلب الدفع، الكود الموجود في تطبيقك أو موقعك لازم ينشئ سلسلة نصية فريدة تماماً. أفضل طريقة لعمل هاد الشي هي باستخدام ما يسمى بـ UUID (Universally Unique Identifier). هاي بتكون سلسلة طويلة من الحروف والأرقام شبه مستحيل تتكرر.
مثلاً، في لغة JavaScript، ممكن ننشئ مفتاح زي هيك بسهولة:
// في كود الـ JavaScript قبل إرسال الطلب
const idempotencyKey = self.crypto.randomUUID();
// الناتج بكون إشي زي هيك: "123e4567-e89b-12d3-a456-426614174000"
// الآن سنستخدم هذا المفتاح في الطلب
الخطوة الثانية: إرسال الطلب مع المفتاح
بعد ما أنشأنا المفتاح، لازم نبعته للسيرفر. المكان المتعارف عليه لوضع هذا المفتاح هو في “هيدرز” (Headers) طلب الـ API. معظم بوابات الدفع الكبيرة زي Stripe وAdyen بتستخدم اسم هيدر مثل Idempotency-Key.
الطلب بصير شكله كالتالي (مثال باستخدام `fetch` في JavaScript):
fetch('https://api.my-payment-gateway.com/v1/charges', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY',
'Idempotency-Key': idempotencyKey // هنا نضع المفتاح الذي أنشأناه
},
body: JSON.stringify({
amount: 5000, // 50.00 USD
currency: 'usd',
source: 'tok_visa'
})
});
الخطوة الثالثة: الخادم (السيرفر) يستقبل ويفحص 🛡️
هنا يحدث السحر الحقيقي. لما السيرفر يستقبل الطلب، بعمل الخطوات التالية:
- يقرأ قيمة الـ
Idempotency-Keyمن الهيدر. - يبحث في قاعدة بيانات مؤقتة (عادةً ما تكون سريعة جداً مثل Redis) أو في جدول خاص بالـ Idempotency Keys: “هل رأيت هذا المفتاح من قبل؟”.
- إذا كان المفتاح جديداً (لم يره من قبل):
- يقوم بتخزين المفتاح فوراً (مع حالة “قيد المعالجة”).
- ينفذ العملية المطلوبة (مثلاً، خصم المبلغ من البطاقة).
- بعد انتهاء العملية بنجاح أو فشل، يقوم بتخزين نتيجة الاستجابة (Response) النهائية ويربطها بالمفتاح.
- يرسل الاستجابة للعميل.
- إذا كان المفتاح موجوداً مسبقاً (مكرر):
- لا يقوم بتنفيذ عملية الخصم مرة أخرى أبداً.
- يسترجع الاستجابة الأصلية التي تم تخزينها سابقاً والمرتبطة بهذا المفتاح.
- يرسل نفس الاستجابة الأصلية للعميل مرة أخرى.
بهذه الطريقة، حتى لو أرسل العميل نفس الطلب 100 مرة بسبب مشكلة في الشبكة، عملية الخصم ستتم مرة واحدة فقط لا غير. وفي كل مرة من الـ 99 المتبقية، سيحصل على نفس رد التأكيد الأصلي، وكأن شيئاً لم يكن.
لماذا لا يكفي التحقق من جانب العميل فقط؟
ممكن مبرمج مبتدئ يحكي: “طيب يا أبو عمر، ليش كل هالغلبة؟ أنا ممكن ببساطة أعطل زر الدفع (disable the button) بعد أول نقرة، وخلصنا!”.
هذا تفكير جيد، وهو إجراء ضروري لتحسين تجربة المستخدم، لكنه غير كافٍ أبداً للحماية من الخصومات المزدوجة. والسبب:
- مشاكل الشبكة (Network Timeouts): أخطر سيناريو! ممكن العميل يرسل الطلب، والسيرفر يستقبله وينفذه بنجاح، لكن الرد (Response) يضيع في الطريق ولا يصل للعميل أبداً. العميل رح يفكر إن العملية فشلت ورح يحاول مرة ثانية، وهنا تحدث الكارثة لو ما في Idempotency Key.
- المستخدم المحترف أو المخترق: أي شخص عنده معرفة تقنية بسيطة بيقدر يتجاوز تعطيل الزر ويرسل طلبات API مباشرة.
- الأنظمة التي تتواصل مع بعضها (Server-to-Server): في كثير من الحالات، الطلبات لا تأتي من متصفح، بل من سيرفر آخر. هنا لا يوجد “زر” لتعطيله أصلاً.
الحكي بينا، المصدر الوحيد للحقيقة (Single Source of Truth) يجب أن يكون دائماً وأبداً في الخادم (السيرفر). لا تثق بالعميل أبداً (Never trust the client).
نصائح أبو عمر الذهبية لتطبيق مفتاح عدم التكرار
من خبرتي في هذا المجال، جمعتلكم كم نصيحة عملية لما تيجوا تطبقوا هاي الآلية في أنظمتكم.
- اختر مفتاحاً فريداً بحق وحقيق: لا تستخدم رقم الطلب (Order ID) أو أي شيء يمكن تخمينه. دائماً استخدم UUID v4. هذا يضمن أن المفتاح فريد عالمياً وليس فقط في نظامك.
- حدد فترة صلاحية للمفتاح (Expiration): لا تحتفظ بالمفاتيح إلى الأبد، فهذا يستهلك مساحة تخزين بلا داعي. فترة 24 ساعة تعتبر كافية لمعظم الحالات. بعد 24 ساعة، يمكن للعميل أن يبدأ معاملة جديدة بنفس التفاصيل إذا أراد.
- خزّن المفتاح والنتيجة في خطوة واحدة (Atomically): تأكد من أن عملية حفظ المفتاح وحفظ نتيجة الطلب المرتبطة به تحدثان معاً كوحدة واحدة لا تتجزأ. هذا يمنع حدوث حالة “race condition” حيث قد يعالج نظامك طلبين بنفس المفتاح في نفس اللحظة.
- وثّق (Document) كل شيء: إذا كنت تبني API ليستخدمها مطورون آخرون، اشرح بوضوح في التوثيق أنهم *يجب* أن يستخدموا Idempotency Key، وكيف يولدونه، وأين يضعونه في الطلب. هذا يرفع من جودة الـ API تبعك ويحمي الجميع.
الخلاصة: نقرة آمنة تساوي عميلاً سعيداً 🧘
في النهاية، قد يبدو مفتاح عدم التكرار تفصيلاً تقنياً صغيراً، لكنه في عالم التكنولوجيا المالية هو الفاصل بين النظام الموثوق الذي يبني ثقة العملاء، وبين النظام الفوضوي الذي يسبب كوابيس مالية وتشغيلية.
تذكروا دائماً: الوقاية من خطأ برمجي واحد أسهل وأرخص بألف مرة من محاولة إصلاح آثاره على مئات العملاء الغاضبين. لا تستهينوا بقوة هذا المفتاح الصغير، فهو درعكم الواقي في معارك المدفوعات الرقمية. استثمِروا الوقت في تطبيقه بشكل صحيح من اليوم الأول، وستشكرون أنفسكم لاحقاً. ✅