
أحد أكبر المشاكل اللي ممكن نواجهها في تصميم الأنظمة الخاصة بالدفع , والمعاملات المالية هي أنك تدفع العميل أكتر من مرة, وعشان كده واحنا بنصمم Payment System محتاجين ناخد في الاعتبار ان عملية الدفع لازم نضمن انها هتتم مرة واحدة فقط لا غير.
وطبعًا عشان نحقق الضمان ده واللي بنسميه Exactly-Once Delivery
بيقابلنا تحديات مش سهلة خصوصًا لو بنتعامل كمان مع Distributed Systems.
المعادلة الرياضية
لو جينا نبص للمشكلة اللي بنواجهها دي رياضيًا فالمفترض عشان نقول أن فيه حاجة تتم مرة واحدة بس فده معناه انها بتطبق الشروط دي:
- اتنفذت على الأقل مرة
- اتنفذت كحد أقصى مرة
يعني ايه الكلام الغريب ده باه ؟
بكل بساطة عشان نعالج مشكلة الـ Double Payment واننا منخليش الـ Customer يتحاسب مرتين أو أكتر, احنا عاوزين طريقة نضمن بيها , ان عملية الدفع على الأقل حصلت مرة .. وفي نفس الوقت نضمن أنها متحصلش أكتر من مرة عشان ميتحاسبش مرتين أو أكتر.
وعشان نحقق ده هنستعمل:
- الـ
Retryer
عشان نضمن أول شرط ان عملية الدفع على الأقل حصلت مرة. - الـ
Idempotency Key
عشان نضمن تاني شرط وهو أن عملية الدفع ما تحصلش أكتر من مرة.
Retryer
من خلال استعمالنا للـ Retryer
كده احنا هنضمن أن لو حصل أي مشكلة في عملية الدفع , سواء كانت مشكلة Network
أو Timeout
مثلًا أو Server Failure
أو أيا كان السبب ايه .. فاحنا ضامنين أن عملية الدفع هيتعاد تنفيذها تاني لحد ما تنجح وبالتالي هنا احنا بنحقق أول شرط الا وهو الـ At Least Once Delivery
.
فكده احنا حققنا أول شرط وضمنا ان لو حصل أي مشكلة في الدفع , هنحاول أكتر من مرة لحد مالعملية تنجح , فعلى الأقل مرة واحدة هتنجح.
فاحنا لو بنحاول نشتري حاجة بـ 100 جنيه , والعملية دي فشلت , ممكن نفضل نحاول لحد ما تنجح ..
طب ماذا لو نجحت , وحاولنا مرة تانية بالخطأ ؟ أو حصل هنا ان اتبعت Retrying تاني بعد ما العملية نجحت ؟ هنا هنروح لتاني حل وهو الـ Idempotency
Key عشان نضمن أننا منقعش في المشكلة دي وندفع الـ Customer أكتر من مرة.
Idempotency Key
يعني ايه Idempotency ؟
الـ Idempotency
معناها من وجهة نظر الـ APIs
ان الـ Request
مهما اتبعت هيلاقي نفس النتيجة فعشان كده مثلا بنقول أن الـ POST Request
مش Idempotent
لانه مع كل Request
مش هيحصل على نفس النتائج عكس الـ GET
على سبيل المثال.
الـ Idempotency Key
محتاجين انه يكون Unique زي ما شوفنا عشان الـ Server يقدر يميز بين كل عملية دفع والتانية , وبالتالي الشائع في أغلب الشركات زي Stripe , PayPal وغيرهم كتير هو استعمال الـ UUID
.
طب الـ Idempotency Key
ده بيتبعت ازاي ؟
المتعارف عليه والشائع انه الـ Client هو اللي بيعمله Generate وبيكون بـ Expiry
معينة على سبيل المثال فبعد وقت محدد بيحصله Expiration
و بيتبعت في الـ Request Headers
وبيكون بالشكل الآتي :
// Client-side implementation
const idempotencyKey = crypto.randomUUID();
const paymentRequest = {
amount: 100,
currency: 'USD',
customerId: 'cust_123'
};
fetch('/api/payments', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey
},
body: JSON.stringify(paymentRequest)
});