لنفرض أن الشركة الناشئة التي تبيع اشتراكات الأندية الرياضية أطلقت الدفع داخل تطبيقها ببطاقات مدى وVisa وMastercard عبر مزود دفع، وفي نهاية الشهر الأول جلس المؤسس ليقارن ثلاثة أرقام: لوحة التحكم في التطبيق تقول إن الإيراد 495 ريالاً من ست عمليات تجريبية في البيئة التجريبية، وتقرير المزود يقول 396 ريالاً، وملف التسوية يقول إن صافي ما وصل أقل من ذلك بعد الرسوم واعتراض واحد خسرته الشركة. وعندما بحث الفريق عن السبب وجد كل شيء في الرسائل التي يرسلها المزود: إشعار وصل مرتين فحسب مرتين، وإشعار تحصيل وصل قبل إشعار التفويض، وتفويض انتهت صلاحيته دون أن يلاحظه أحد، واعتراض فتحه بنك العميل بعد ثلاثة أسابيع من الدفع.
هذه المشكلات كلها تأتي من أن «الدفع بالبطاقة» ليس حدثاً واحداً، وإنما سلسلة من الأحداث بين خمسة أطراف على مدى أيام وأحياناً أشهر: التفويض Authorization في ثوان، ثم التحصيل Capture، ثم المقاصة والتسوية بين البنوك، ثم الاسترداد Refund أو الاعتراض Chargeback بعد ذلك. وفي مقال المقاصة والتسوية شرحنا كيف يتحرك المال بين البنوك عموماً، وفي مقال منظومة المدفوعات في السعودية شرحنا مدى وقنواته، وهذا المقال يتعمق في عملية البطاقة نفسها من ضغطة «ادفع» إلى آخر اعتراض ممكن، مع مختبر في Python يعالج أحداث مزود دفع تجريبية بثلاث طرق ويطابقها مع ملف تسوية، لنرى بالأرقام كيف تصل الشركة إلى ثلاثة أرقام مختلفة.
وسوف نناقش في هذا المقال ما يلي:
- أطراف عملية البطاقة: حامل البطاقة والتاجر والمستحوذ والشبكة والبنك المصدر، ودور مزود الدفع بينهم.
- التفويض خطوة بخطوة، والتحقق 3D Secure، ولماذا يرفض البنك.
- التحصيل والحجز ومدة صلاحية التفويض بحسب قواعد Visa المنشورة.
- المقاصة والتسوية وملف التسوية والرسوم.
- الاسترداد والاعتراض، ومدد الاعتراض كما تذكرها قواعد Visa.
- مختبر: أحداث مزود تجريبية بثلاث طرق معالجة، ومطابقتها مع ملف التسوية.
- أمن بيانات البطاقة ونطاق PCI DSS، والأخطاء الشائعة، وقائمة تحقق.
من هم أطراف العملية؟
أبسط وصف لعملية البطاقة هو ما يسمى النموذج الرباعي Four-Party Model: حامل البطاقة Cardholder، والتاجر Merchant، والبنك المستحوذ Acquirer الذي يقبل العمليات نيابة عن التاجر ويدفع له، والبنك المصدر Issuer الذي أصدر البطاقة ويملك حساب العميل، وبينهما شبكة البطاقات Card Scheme مثل مدى وVisa وMastercard، التي تنقل الرسائل وتضع القواعد وتدير المقاصة. وفي الواقع يقف بين التاجر والمستحوذ غالباً مزود دفع أو بوابة دفع Payment Gateway، وهو ما تتعامل معه الشركة الناشئة فعلاً عبر API، والصورة التالية تبين الأطراف:

وفي الصورة أعلاه لاحظ التالي:
- القرار في التفويض يملكه البنك المصدر وحده، فلا التاجر ولا المزود يستطيع أن يقول «نعم» نيابة عنه، ولهذا يرى المطور أحياناً رفضاً لا يفهم سببه، لأن السبب عند المصدر.
- المال يتحرك في الاتجاه المعاكس للرسائل: يخرج من حساب العميل لدى المصدر، ويمر في التسوية بين البنوك، ويصل إلى المستحوذ، ثم إلى التاجر بعد خصم الرسوم.
- كل طرف له رسوم وقواعد، والرسوم التي يدفعها التاجر للمزود تجمع أجزاء تذهب لأطراف مختلفة، وتفاصيلها في عقدك مع المزود لا في هذا المقال.
- القواعد التي تحكم الاعتراضات ومدد الصلاحية تضعها الشبكة، وقواعد Visa منشورة للعامة في Visa Core Rules and Visa Product and Service Rules، والإصدار الذي رجعنا إليه بتاريخ 18 أبريل 2026، وسنستشهد به في هذا المقال كمثال على شكل هذه القواعد. أما قواعد مدى التشغيلية فلم نجدها منشورة بنفس التفصيل، فما ينطبق على عمليات مدى تحديداً تعرفه من مزودك.
التفويض Authorization: ماذا يحدث في الثانيتين الأوليين؟
عندما يضغط العضو «ادفع 99 ريالاً» في التطبيق، تبدأ سلسلة رسائل قصيرة تنتهي برد خلال ثوان، والصورة التالية تبين خطواتها لعملية عبر الإنترنت:

- البيانات: يكتب العضو بيانات بطاقته في حقل يملكه المزود داخل صفحة التطبيق، أو يختار بطاقة محفوظة في محفظة الجوال، فيستلم التطبيق توكن Token يمثل البطاقة، لا رقمها. وهذا قرار أمني سنعود إليه في فقرة PCI DSS.
- التحقق من حامل البطاقة: في عمليات الإنترنت يمر الطلب غالباً بالتحقق 3D Secure، وهو بروتوكول تصفه صفحة EMVCo بأنه يتيح تبادل البيانات بين التاجر والمصدر حتى يتحقق المصدر من المشتري، وتذكر صفحة مدى لدى البنك المركزي أن التجارة الإلكترونية في مدى تعمل عبره.
- طلب التفويض: يرسل المزود الطلب إلى المستحوذ، ثم إلى الشبكة، ثم إلى المصدر، ويحمل المبلغ والعملة ومعرف التاجر ونوع العملية ونتيجة التحقق. وشكل هذه الرسائل بين البنوك معيار اسمه ISO 8583، له مقال مستقل في السلسلة.
- القرار: يفحص المصدر الرصيد أو حد الائتمان، وحالة البطاقة، وحدود العميل اليومية، ونماذج الاحتيال عنده، ثم يوافق أو يرفض.
- الحجز: عند الموافقة يحجز المصدر المبلغ على حساب العميل، فيقل الرصيد المتاح، ولكن المال لم يخرج بعد.
- الرد: يعود الرد بنفس الطريق برمز موافقة، أو برمز رفض يحمل سبباً عاماً، ويصل إلى التطبيق كحدث أو رد مباشر من API المزود.
وقد يتساءل البعض: لماذا لا يعطي المصدر سبب الرفض بالتفصيل حتى نعرض للعميل رسالة مفيدة؟ والإجابة أن بعض أسباب الرفض، مثل الاشتباه في احتيال، لو شرحت بالتفصيل لساعدت المحتال على تجاوزها، لذلك تبقى رموز الرفض عامة في كثير من الحالات، والرسالة الصحيحة للعميل غالباً «تواصل مع بنكك أو جرب بطاقة أخرى»، لا تفاصيل الرمز.
ماذا لو انقطع الاتصال في منتصف التفويض؟
أخطر لحظة في الكود ليست الرفض، وإنما الحالة التي لا تعرف فيها النتيجة: أرسل التطبيق طلب الدفع إلى المزود، ثم انقطع الاتصال أو انتهت المهلة قبل أن يصل الرد. هل فوضت العملية أم لا؟ إذا أعاد التطبيق المحاولة بطلب جديد، فقد يدفع العضو مرتين، وإذا اعتبرها فاشلة، فقد يكون المبلغ محجوزاً فعلاً والعضو يرى رصيده ينقص دون اشتراك. والحل الذي تتيحه أغلب واجهات المزودين ثلاثي: أن يحمل طلب الإنشاء مفتاح منع التكرار Idempotency Key يولده التطبيق مرة لكل محاولة دفع منطقية، فإعادة نفس الطلب بنفس المفتاح تعيد نفس النتيجة ولا تنشئ عملية ثانية، وأن يسأل التطبيق المزود عن حالة العملية بمعرفها قبل أن يقرر، وأن تبقى الدفعة في نظامك بحالة «غير معروفة» حتى تصل الإجابة أو الحدث. وقد شرحنا فكرة المفتاح بالتفصيل في مقال Idempotency.
لماذا يرفض البنك؟ وكيف تعرض الرفض للعميل؟
في الأسبوع الأول جاءت للفريق شكاوى من أعضاء رفضت بطاقاتهم وهم متأكدون أن رصيدهم يكفي. وأسباب الرفض تقع في مجموعات قليلة يعرفها كل من عمل في المدفوعات: رصيد أو حد غير كاف، وبطاقة منتهية أو موقوفة، وبيانات خاطئة مثل تاريخ الانتهاء، وحد يومي أو حد للتجارة الإلكترونية وضعه العميل أو بنكه، وتحقق 3D Secure لم يكتمل، واشتباه احتيال من نماذج المصدر، وأخيراً أسباب تقنية مثل انقطاع بين الأطراف أو مهلة انتهت. وتذكر صفحة مدى لدى البنك المركزي أن البنك المصدر يستطيع تعديل الحد الأعلى اليومي، فالحد يختلف من عميل لآخر.
والدرس للمطور في ثلاث نقاط: الأولى أن تفرق في الكود بين الرفض النهائي الذي لا تعيد محاولته (بطاقة موقوفة) والرفض المؤقت الذي قد تنجح إعادته بعد وقت (خطأ تقني)، كما يصنفها مزودك في توثيقه، والثانية ألا تعيد محاولة عملية مرفوضة مرات كثيرة متتالية، لأن الشبكات تراقب ذلك وقد تعاقب التاجر، ولأنها تبدو للمصدر كمحتال يجرب، والثالثة أن رسالة العميل تكون مفيدة دون أن تكشف ما لا يجب: «لم يوافق بنكك على العملية، جرب بطاقة أخرى أو تواصل مع بنكك» أفضل من رمز تقني لا يفهمه، وأفضل من «رصيدك غير كاف» إذا لم يكن ذلك هو السبب فعلاً.
التحقق 3D Secure: دون احتكاك أو بخطوة تحقق
تصف صفحة EMVCo طريقين للعميل: في العملية منخفضة المخاطر يضغط العميل «شراء» فتعتمد العملية، وفي العملية الأعلى خطراً قد يضيف المصدر خطوة تحقق، فيثبت العميل هويته برمز لمرة واحدة أو أسئلة أو بصمة. والبيانات التي يرسلها التاجر تشمل معلومات عن العملية ووسيلة الدفع والجهاز، وكلما كانت أكمل زادت فرصة المسار دون احتكاك. وتذكر الصفحة أن EMVCo توصي بالإصدار 2.2 أو أعلى للحصول على أفضل وظائف، وأن مسودة الإصدار 2.4.0.0 نشرت في 3 يونيو 2026 للتعليق. والصورة التالية تقارن الطريقين:

ولهذا التحقق أثر مالي على التاجر وليس أمنياً فقط، لأن قواعد الشبكات تربط في حالات كثيرة مسؤولية الاحتيال بمن طلب التحقق ومن أكمله، وهذا ما يسمى انتقال المسؤولية Liability Shift، وتفاصيله تختلف بين الشبكات والمناطق، فاسأل مزودك عنها لكل نوع بطاقة تقبله. وللمطور قاعدة عملية: لا تبن تجربة الدفع على افتراض أن 3D Secure لن يظهر، فاختبر في البيئة التجريبية لمزودك بطاقات الاختبار التي تفرض خطوة التحقق، وتأكد أن التطبيق يعود إلى نفس الشاشة بعدها، وأن العضو الذي أغلق نافذة التحقق في منتصفها لا يبقى اشتراكه معلقاً دون حالة واضحة.
التوكن: البطاقة دون رقمها
التوكن الذي يستلمه التطبيق من المزود يمثل البطاقة داخل حساب الشركة عند المزود، ولا يصلح خارجه، فإذا سرقه أحد من قاعدة بيانات الشركة لا يستطيع أن يدفع به في متجر آخر. وفي المحافظ مثل Apple Pay توجد طبقة ترميز أخرى تذكرها صفحة مدى لدى البنك المركزي: البطاقة المضافة إلى المحفظة تحفظ برقم بديل خاص بالجهاز، فلا يعرف التاجر ولا المزود رقم البطاقة الحقيقي أصلاً. والنتيجة للمطور أن «البطاقة المحفوظة» في نظامك يجب أن تكون دائماً توكن من المزود مع آخر أربعة أرقام وتاريخ الانتهاء للعرض فقط، وأي حقل في قاعدة بياناتك يمكن أن يحتوي رقم بطاقة كاملاً هو خطأ تصميم يجب إزالته.
التحصيل Capture والحجز وصلاحية التفويض
بعد التفويض يحتاج التاجر أن يطلب المال فعلاً، وهذا هو التحصيل Capture. وأغلب المتاجر الإلكترونية تفوض وتحصل في نفس الخطوة، وهذا ما فعلته الشركة الناشئة للاشتراك الشهري، ولكن بعض الأعمال تفصل بينهما: الفندق يفوض عند الحجز ويحصل عند المغادرة، والمتجر يفوض عند الطلب ويحصل عند الشحن، والنادي قد يفوض عند حجز حصة تدريب خاصة ويحصل بعد حضورها. ويوضح توثيق Stripe عن فصل التفويض والتحصيل، كمثال من مزود معروف، ما يحدث في هذه الحالة: التفويض يضمن المبلغ بحجزه على وسيلة الدفع، ويجب التحصيل قبل انتهاء صلاحيته، وإلا يحرر المبلغ وتصبح العملية ملغاة، ويمكن تحصيل مبلغ أقل من المفوض فيحرر الباقي تلقائياً.
وما مدة صلاحية التفويض؟ هذا تحدده قواعد الشبكة ونوع العملية، ففي الجدول 5-12 من قواعد Visa (إصدار 18 أبريل 2026) الحد الأقصى لمعالجة العملية بعد تفويض صالح كما يلي:
| نوع العملية في قواعد Visa | الحد الأقصى للمعالجة |
|---|---|
| عمليات يبدؤها حامل البطاقة دون حضور البطاقة Card-Absent | 10 أيام تقويمية |
| نفس النوع مع مؤشر التفويض الممتد Extended Authorization | 30 يوماً تقويمياً |
| تفويض تقديري لشركات الرحلات البحرية والفنادق وتأجير السيارات | 30 يوماً تقويمياً |
| كل العمليات الأخرى بحضور البطاقة Card-Present | 5 أيام تقويمية |
| كل العمليات التي يبدؤها التاجر Merchant-initiated | 5 أيام تقويمية |
ولاحظ أن المزود قد يضع حدوداً أقصر من قواعد الشبكة، فتوثيق Stripe المذكور يقول إن التفويض لعمليات الإنترنت صالح عادة 7 أيام، وإن نافذة عمليات Visa التي يبدؤها التاجر عنده 4 أيام و18 ساعة لإتاحة وقت للمقاصة. وتذكر قواعد Visa أيضاً أن جدولاً آخر (5-13) يضع حدوداً خاصة لبعض الدول، وأن طلب التفويض الإضافي Incremental Authorization لا يمدد هذه المدد. والقاعدة للمطور: لا تكتب رقماً ثابتاً لصلاحية التفويض في كودك، وإنما خذه من رد المزود لكل عملية إذا كان يرسله، وراقب التفويضات التي تقترب من انتهائها، لأن التفويض المنتهي يعني خدمة قدمتها دون أن تحصل ثمنها.
والصورة التالية تجمع حالات الدفعة كلها كما يجب أن يراها نظامك:

التحصيل الجزئي والمتعدد
في المختبر أدناه سترى دفعة فوضت بـ 297 ريالاً وحصل منها 198، وهذا ما يحدث عندما يشتري العضو باقة ثلاثة أشهر ثم يطلب قبل التفعيل تغييرها إلى شهرين. والتحصيل الجزئي يحرر الباقي للعميل تلقائياً كما يقول توثيق Stripe المذكور، ولكنه في أغلب الحالات تحصيل واحد فقط: إذا حصلت جزءاً فلا تستطيع أن تعود لتحصيل الباقي لاحقاً، إلا في حالات خاصة يسميها المزود التحصيل المتعدد Multicapture لبعض العمليات المؤهلة. لذلك إذا كان نموذج عملك يحتاج تحصيل نفس التفويض على مراحل، مثل طلب يشحن في دفعتين، فتأكد أن مزودك يدعم ذلك لنوع العملية، وإلا فالحل تفويضان منفصلان أو حفظ وسيلة الدفع بموافقة العميل وإنشاء عملية جديدة لكل مرحلة.
الإلغاء Void أم الاسترداد Refund؟
عندما يلغي العضو حجز حصة قبل موعدها، هل تعيد الشركة المال باسترداد أم تلغي العملية؟ الفرق مهم: إذا كانت العملية مفوضة ولم تحصل بعد، فالإلغاء Void أو Reversal يحرر الحجز، ولا ينتقل المال أصلاً، ولا تظهر عملية في كشف العميل غالباً. أما إذا حصلت العملية، فالمال في طريقه أو وصل، والاسترداد عملية جديدة في الاتجاه المعاكس تمر بالمقاصة والتسوية مثل الأصلية، وتظهر في كشف العميل بعد مدة يحددها بنكه، وقد لا تعيد الرسوم التي دفعها التاجر على العملية الأصلية بحسب عقده. لذلك إذا كان نموذج عملك فيه إلغاء متكرر قبل تقديم الخدمة، فالتفويض ثم التحصيل عند تقديم الخدمة أوفر من التحصيل الفوري ثم الاسترداد، بشرط أن تقع الخدمة داخل مدة صلاحية التفويض.
الاشتراك الشهري: من يبدأ العملية؟
الاشتراك الشهري يطرح سؤالاً لم يكن في الدفعة الأولى: في الشهر الثاني لا يوجد عضو يضغط «ادفع»، فالتطبيق هو من يبدأ العملية ببطاقة محفوظة. وقواعد الشبكات تفرق بين نوعين من العمليات: عملية يبدؤها حامل البطاقة Customer-initiated Transaction واختصاراً CIT، وهو حاضر في التطبيق ويستطيع أن يكمل 3D Secure، وعملية يبدؤها التاجر Merchant-initiated Transaction واختصاراً MIT، ببيانات محفوظة بموافقة سابقة من العميل، مثل التجديد الشهري. ولكل نوع قواعده: في الجدول 5-12 من قواعد Visa المذكورة الحد الأقصى لمعالجة العمليات التي يبدؤها التاجر 5 أيام تقويمية، وتشمل الحاشية العمليات المتكررة والتقسيط وبيانات البطاقة المحفوظة غير المجدولة.
والطريقة المعتادة في المزودين أن تكون الدفعة الأولى عملية يبدؤها العميل مع التحقق والموافقة على حفظ البطاقة للتجديد، ثم تعلم الدفعات التالية بأنها يبدؤها التاجر، فلا يطلب من العميل تحقق في كل شهر. ويهم هذا الوسم لأن المصدر يقرر بناءً عليه، فالعملية المتكررة الموسومة بشكل صحيح تمر بسهولة أكبر من عملية مجهولة النوع. وما يحتاجه منتجك لهذا: موافقة صريحة ومحفوظة من العضو على التجديد وشروطه، وإشعار قبل التجديد بموعده ومبلغه، وطريقة إلغاء واضحة، ومعالجة لرفض التجديد تحاول مرة أو مرتين في أيام مختلفة ثم توقف الاشتراك وتخبر العضو، بدلاً من محاولات متكررة في نفس اليوم. والسبب أن الاعتراض على تجديد لم يعرف به العميل من أشهر أسباب الاعتراضات، وقواعد Visa نفسها فيها حالة اعتراض مستقلة للعملية المتكررة الملغاة Cancelled Recurring Transaction.
وهناك مشكلة يقابلها كل منتج اشتراكات بعد أشهر: البطاقة المحفوظة تنتهي أو يستبدلها البنك، فيرفض التجديد. وبعض المزودين يقدمون خدمات تحديث بيانات البطاقة تلقائياً من الشبكات، وبعضهم يحفظ البطاقة كتوكن من الشبكة Network Token يبقى صالحاً بعد تجديد البطاقة، فاسأل مزودك عما يتيحه لبطاقات مدى والشبكات الدولية، ولا تبن على أن البطاقة المحفوظة اليوم ستعمل بعد سنة.
المقاصة والتسوية وملف التسوية
بعد التحصيل تدخل العملية في المقاصة: يجمع المستحوذ عمليات التجار المحصلة ويرسلها إلى الشبكة في دفعات، وتحسب الشبكة ما على كل مصدر وما لكل مستحوذ، ثم تسوى الصافيات بين البنوك كما شرحنا في مقال المقاصة والتسوية، وفي مدى تنتهي التسوية بين البنوك في SARIE كما تذكر وثيقة أنظمة المدفوعات في دليل قواعد البنك المركزي. ثم يحول المستحوذ أو المزود للتاجر صافي ما يستحقه، بجدول مكتوب في العقد.
وما يصل إلى التاجر لتوثيق ذلك هو ملف التسوية Settlement Report: سطر لكل عملية أو حدث مالي، فيه المبلغ الإجمالي والرسوم والصافي ونوع الحدث (تحصيل أو استرداد أو اعتراض أو تعديل)، ومرجع يربطه بالعملية الأصلية، ومعرف الدفعة Payout التي وصلت إلى حساب التاجر البنكي. وهذا الملف، لا لوحة التحكم ولا الإشعارات، هو ما يجب أن تطابق عليه دفاتر الشركة، لأنه ما حدث فعلاً بالمال. وفي المختبر أدناه سنرى ما يحدث عندما لا تطابق الدفاتر هذا الملف.
الرسوم: من يأخذ ماذا؟
الرسوم التي يراها التاجر في ملف التسوية رقم واحد لكل عملية غالباً، ولكنها تجمع أجزاء تذهب لأطراف مختلفة: جزء للبنك المصدر يسمى عادة رسوم التبادل Interchange، وجزء للشبكة على معالجة العملية، وجزء للمستحوذ والمزود مقابل خدمتهما. وطريقة عرضها في العقد تختلف: بعض المزودين يعطي التاجر نسبة واحدة مجمعة، وبعضهم يفصل التكلفة الفعلية عن هامشه. ولن نذكر أرقاماً هنا، لأن الرسوم تختلف بين الشبكات ونوع البطاقة وطريقة القبول والعقد، وقد تحددها أنظمة في بعض البلدان، فخذها من عقدك ومن المصادر الرسمية لبلدك. والذي يهم المطور أن الرسوم تأتي في ملف التسوية مرتبطة بكل عملية، فيستطيع النظام أن يحسب تكلفة القبول الفعلية لكل وسيلة دفع، وأن يرى أثر الاعتراضات والاستردادات عليها، وهذا ما يحتاجه المؤسس عندما يفاوض المزود في السنة الثانية.
الاعتراض Chargeback: عندما يسحب المصدر المال
الاعتراض حق لحامل البطاقة عبر بنكه، وليس طلباً للتاجر: العميل يقول لبنكه «لم أطلب هذه العملية» أو «دفعت ولم تصلني الخدمة» أو «ألغيت ولم يرد لي المبلغ»، فيفتح البنك المصدر اعتراضاً عبر الشبكة، فيسحب المبلغ من المستحوذ، ويسحبه المستحوذ من صافي التاجر، غالباً مع رسوم على الاعتراض. ثم يملك التاجر فرصة للرد بأدلة في موعد يحدده المزود، وتقرر النتيجة بحسب قواعد الشبكة. والصورة التالية تبين المسار:

ومدد الاعتراض طويلة مقارنة بما يتوقعه المؤسس، ففي قواعد Visa (إصدار أبريل 2026) مثلاً:
- حالة الاعتراض 10.4، احتيال آخر دون حضور البطاقة: يجب أن يعالج الاعتراض في مدة لا تتجاوز 120 يوماً تقويمياً من تاريخ معالجة العملية.
- حالة الاعتراض 13.6، استرداد لم يتم Credit Not Processed: ينتظر المصدر 15 يوماً تقويمياً من تاريخ إيصال الاسترداد قبل أن يفتح الاعتراض، ويجب أن يعالج خلال 120 يوماً تقويمياً من تاريخ الإيصال، على ألا يتجاوز 540 يوماً تقويمياً من تاريخ معالجة العملية.
وهذا يعني للشركة الناشئة أن الإيراد الذي حصلته هذا الشهر قد يعود جزء منه بعد أربعة أشهر، فلا تعتبر المال نهائياً في التخطيط المالي فور تسويته، واحتفظ بأدلة كل عملية مدة تكفي لهذه المدد: سجل تسجيل الدخول، وتاريخ تفعيل الاشتراك، وسجل حضور الحصص، والرسائل مع العضو، وشروط الإلغاء التي وافق عليها. ويشرح توثيق Stripe عن الاعتراضات، كمثال من مزود، كيف تصل الاعتراضات وكيف يرد التاجر عليها، والإجراءات عند مزودك قد تختلف.
وأفضل طريقة لتقليل الاعتراضات ليست الرد عليها، وإنما منعها: اسم تجاري واضح يظهر في كشف حساب العميل يعرفه فيه، حتى لا يعترض على عملية لم يتعرف عليها، واسترداد سريع عندما يطلب العضو الإلغاء بحق، لأن الاعتراض أغلى من الاسترداد على التاجر، وإشعار قبل تجديد الاشتراك، وطريقة إلغاء واضحة داخل التطبيق.
ما الأدلة التي تحفظها لكل نوع اعتراض؟
الرد على الاعتراض ينجح أو يفشل بالأدلة، والأدلة لا تجمع بعد الاعتراض، وإنما تحفظ لحظة حدوثها. ولكل فئة من أسباب الاعتراض أدلة مختلفة، وهذه الفئات التي جهز لها الفريق سجلاته:
- «لم أقم بهذه العملية» (احتيال): نتيجة التحقق 3D Secure للعملية، وسجل تسجيل الدخول للحساب الذي دفع، والجهاز وعنوان الشبكة، واستخدام العضو للاشتراك بعد الدفع، مثل حضور الحصص بنفس الحساب.
- «لم تصلني الخدمة»: تاريخ تفعيل الاشتراك، وسجل الدخول إلى النادي أو حجز الحصص، والرسائل المرسلة للعضو.
- «ألغيت ولم يرد لي المبلغ»: شروط الإلغاء التي وافق عليها العضو وتاريخ موافقته، وتاريخ طلب الإلغاء إن وجد، وأي استرداد تم مع مرجعه.
- «التجديد لم أوافق عليه»: الموافقة على التجديد عند الدفعة الأولى، وإشعار التجديد المرسل قبل موعده، وأن طريقة الإلغاء كانت متاحة.
وكل هذه سجلات يملكها التطبيق أصلاً، والفرق أن تحفظ بطريقة يمكن استخراجها لعملية بعينها بعد أشهر، مرتبطة بمعرف الدفعة، وأن تحفظ المدة التي تكفي مدد الاعتراض، مع احترام ما يفرضه نظام حماية البيانات الشخصية على مدة الحفظ والغرض منه.
المختبر: أحداث المزود بثلاث طرق معالجة
الآن سوف نعيد قصة المؤسس بالكود. لدينا ملفان من بيئة تجريبية افتراضية: ملف أحداث كما يرسلها مزود الدفع إلى التطبيق عبر Webhooks، وملف تسوية كما يرسله المستحوذ. والبيانات كلها مكتوبة للتجربة: لا أرقام بطاقات إطلاقاً، والمعرفات مثل pay_A وهمية، والرسوم في ملف التسوية أرقام توضيحية لا تمثل أسعار أي مزود. والمبالغ بالهللة، فـ 9900 تعني 99 ريالاً. وهذا ملف الأحداث events.csv:
event_id,payment_id,type,amount,at
ev01,pay_A,authorized,9900,2026-10-01T09:00
ev02,pay_A,captured,9900,2026-10-01T09:00
ev03,pay_B,authorized,29700,2026-10-01T10:15
ev04,pay_B,captured,19800,2026-10-02T11:00
ev05,pay_C,captured,9900,2026-10-01T12:00
ev06,pay_C,authorized,9900,2026-10-01T11:59
ev07,pay_D,authorized,9900,2026-10-01T13:00
ev08,pay_D,expired,9900,2026-10-08T13:00
ev09,pay_E,authorized,9900,2026-10-01T14:00
ev10,pay_E,captured,9900,2026-10-01T14:00
ev10,pay_E,captured,9900,2026-10-01T14:00
ev11,pay_E,refunded,9900,2026-10-03T08:00
ev12,pay_F,authorized,9900,2026-10-01T15:00
ev13,pay_F,captured,9900,2026-10-01T15:00
ev14,pay_F,dispute_opened,9900,2026-10-20T09:00
ev15,pay_F,dispute_lost,9900,2026-11-05T09:00وفي الملف أعلاه لاحظ أنه يحتوي عمداً المشكلات التي واجهتها الشركة: الحدث ev10 مكرر لأن المزود أعاد إرساله بعد انقطاع، والحدث ev05 (تحصيل pay_C) وصل قبل ev06 (تفويضه) مع أن وقت التفويض أقدم، وpay_B فوض 297 ريالاً وحصل 198 فقط، وpay_D فوض ولم يحصل حتى انتهى، وpay_E استرد، وpay_F خسر اعتراضاً. وهذا ملف التسوية settlement.csv:
payment_id,kind,gross,fee,net
pay_A,capture,9900,248,9652
pay_B,capture,19800,495,19305
pay_C,capture,9900,248,9652
pay_E,capture,9900,248,9652
pay_E,refund,-9900,0,-9900
pay_F,capture,9900,248,9652
pay_F,chargeback,-9900,1500,-11400والملف التالي يعالج الأحداث بطريقتين: الطريقة البسيطة التي تجمع كل تحصيل وتطرح كل استرداد واعتراض خاسر بترتيب الوصول دون أي فحص، والطريقة الحذرة التي ترفض الحدث المكرر بمعرفه، وترتب الأحداث بوقت حدوثها، وتقبل فقط الانتقالات المسموحة بين الحالات. ثم يطابق دفتر الطريقة الحذرة مع ملف التسوية، والخيار --arrival-order يجعل الطريقة الحذرة تعالج بترتيب الوصول بدلاً من الترتيب الزمني:
# lifecycle.py: card payment events from a gateway, processed naively and carefully, then reconciled
import csv, sys
from collections import defaultdict
events = list(csv.DictReader(open("events.csv")))
def naive(evts):
"""add on captured, subtract on refunded and dispute_lost, in arrival order, no checks"""
revenue = 0
for e in evts:
amt = int(e["amount"])
if e["type"] == "captured":
revenue += amt
elif e["type"] in ("refunded", "dispute_lost"):
revenue -= amt
return revenue
# allowed transitions: anything not listed is rejected and logged
NEXT = {
None: {"authorized"},
"authorized": {"captured", "expired", "voided"},
"captured": {"refunded", "dispute_opened"},
"dispute_opened": {"dispute_won", "dispute_lost"},
}
def careful(evts):
seen, state, captured, ledger, problems = set(), {}, defaultdict(int), defaultdict(int), []
# order by event time, not arrival order (the gateway may deliver late or out of order)
ordered = evts if "--arrival-order" in sys.argv else sorted(evts, key=lambda e: e["at"])
for e in ordered:
if e["event_id"] in seen:
problems.append(f"{e['event_id']} duplicate delivery ignored")
continue
seen.add(e["event_id"])
pid, kind, amt = e["payment_id"], e["type"], int(e["amount"])
if kind not in NEXT.get(state.get(pid), set()):
problems.append(f"{e['event_id']} {pid}: {state.get(pid)} -> {kind} rejected")
continue
state[pid] = kind
if kind == "captured":
captured[pid] = amt
ledger[pid] += amt
elif kind in ("refunded", "dispute_lost"):
ledger[pid] -= amt
return state, ledger, problems
print("naive revenue (halalas): ", naive(events))
state, ledger, problems = careful(events)
print("careful revenue (halalas):", sum(ledger.values()))
for p in problems:
print(" note:", p)
print("\nfinal state per payment:")
for pid in sorted(state):
print(f" {pid}: {state[pid]:15} ledger {ledger[pid]:>6}")
# reconcile with the acquirer settlement file
settled = defaultdict(int)
fees = 0
for r in csv.DictReader(open("settlement.csv")):
settled[r["payment_id"]] += int(r["gross"])
fees += int(r["fee"])
print("\nreconciliation (our ledger vs settlement gross):")
for pid in sorted(set(ledger) | set(settled)):
flag = "ok" if ledger[pid] == settled[pid] else "MISMATCH"
print(f" {pid}: ours {ledger[pid]:>6} settled {settled[pid]:>6} {flag}")
print(f"fees and chargeback fees in the file: {fees} halalas")وعند تشغيله بالأمر python lifecycle.py على Python 3.13 سوف يكون المخرج كما يلي:
naive revenue (halalas): 49500
careful revenue (halalas): 39600
note: ev10 duplicate delivery ignored
final state per payment:
pay_A: captured ledger 9900
pay_B: captured ledger 19800
pay_C: captured ledger 9900
pay_D: expired ledger 0
pay_E: refunded ledger 0
pay_F: dispute_lost ledger 0
reconciliation (our ledger vs settlement gross):
pay_A: ours 9900 settled 9900 ok
pay_B: ours 19800 settled 19800 ok
pay_C: ours 9900 settled 9900 ok
pay_D: ours 0 settled 0 ok
pay_E: ours 0 settled 0 ok
pay_F: ours 0 settled 0 ok
fees and chargeback fees in the file: 2987 halalasفي المخرج أعلاه لاحظ التالي:
- الطريقة البسيطة حسبت 49,500 هللة، أي 495 ريالاً، وهو الرقم الذي ظهر في لوحة التحكم في القصة، والسبب أن الحدث
ev10حسب مرتين، فأضاف 99 ريالاً لم تدفع. - الطريقة الحذرة حسبت 39,600 هللة (396 ريالاً)، ورفضت التكرار برسالة واضحة، والحالة النهائية لكل دفعة صحيحة:
pay_Dمنتهية بلا إيراد، وpay_Eمستردة، وpay_Fخسرت الاعتراض. - المطابقة مع ملف التسوية سليمة لكل الدفعات، فدفتر الشركة يقول ما يقوله الملف عن المبالغ الإجمالية.
- الرسوم في الملف 2,987 هللة، منها 1,500 رسوم الاعتراض، وهي ليست في دفتر الإيراد، وإنما تسجل مصروفاً مستقلاً، وهذا هو الفرق بين 396 ريالاً وما يصل فعلاً إلى حساب الشركة.
pay_Bسجل 198 ريالاً لا 297، لأن الإيراد هو المحصل لا المفوض، والفرق حرر للعميل تلقائياً.
والصورة التالية تلخص النتائج الثلاث، ومنها نتيجة التجربة التالية:

ماذا لو عالجنا بترتيب الوصول؟
قد يبدو ترتيب الأحداث بوقتها تفصيلاً، فلنجرب الطريقة الحذرة نفسها بترتيب الوصول بالأمر python lifecycle.py --arrival-order:
naive revenue (halalas): 49500
careful revenue (halalas): 29700
note: ev05 pay_C: None -> captured rejected
note: ev10 duplicate delivery ignored
final state per payment:
pay_A: captured ledger 9900
pay_B: captured ledger 19800
pay_C: authorized ledger 0
pay_D: expired ledger 0
pay_E: refunded ledger 0
pay_F: dispute_lost ledger 0
reconciliation (our ledger vs settlement gross):
pay_A: ours 9900 settled 9900 ok
pay_B: ours 19800 settled 19800 ok
pay_C: ours 0 settled 9900 MISMATCH
pay_D: ours 0 settled 0 ok
pay_E: ours 0 settled 0 ok
pay_F: ours 0 settled 0 ok
fees and chargeback fees in the file: 2987 halalasوهنا الخطأ في الاتجاه المعاكس: الإيراد 29,700 هللة (297 ريالاً)، أقل من الحقيقة بـ 99 ريالاً. والسبب في السطر الأول من الملاحظات: ev05 تحصيل pay_C وصل قبل تفويضها، فرفضته آلة الحالات لأن الانتقال من «لا شيء» إلى «محصلة» غير مسموح، ثم وصل التفويض فأصبحت الحالة «مفوضة» وبقيت كذلك. والمطابقة كشفت الخطأ فوراً: MISMATCH لـ pay_C، فالملف يقول إن 99 ريالاً سويت، ودفتر الشركة يقول صفراً. ولو لم تكن هناك مطابقة يومية، لظن الفريق أن العميل لم يدفع، وربما أوقف اشتراكه.
والدرس من التجربتين أن معالجة الأحداث تحتاج ثلاثة أشياء معاً: رفض المكرر بمعرفه، وترتيب يعتمد على وقت الحدث أو تسلسله لا على وقت وصوله، وقواعد انتقال واضحة، ثم مطابقة يومية تكتشف ما فات الثلاثة. وفي الأنظمة الحقيقية خطوة رابعة أبسط من كل ذلك: عندما يصل حدث لا يتوافق مع الحالة الحالية، لا ترفضه وتنسى، وإنما اسأل API المزود عن الحالة الحالية للدفعة واعتمدها، لأن المزود هو مصدر الحقيقة عن حالة العملية، والأحداث مجرد إشعارات بأنها تغيرت. ولاحظ أخيراً أن الترتيب بالوقت في الكود يعتمد على أن sorted في Python مستقر Stable، فالحدثان بنفس الدقيقة (ev01 وev02) يبقيان بترتيب ورودهما، ولو كان الترتيب غير مستقر لاحتجنا تسلسلاً صريحاً من المزود.
وفي استقبال النادي: الدفع بحضور البطاقة
الدفع في استقبال النادي يمر بنفس الأطراف، ولكن بأدوات مختلفة. فالبطاقة الحاضرة تقرأ من الشريحة Chip أو دون تلامس بتقنية NFC، وهذا ما تسميه مدى «أثير»، أو من محفظة الجوال، والتحقق من حامل البطاقة يحدث عند الجهاز بالرقم السري أو ببصمة الجوال أو دون تحقق للمبالغ الصغيرة بحسب قواعد الشبكة والمصدر، بدلاً من 3D Secure. وتذكر صفحة مدى لدى البنك المركزي خدمة قبول المدفوعات على أجهزة Android دون جهاز نقاط بيع خاص، وأعلن البنك المركزي في سبتمبر 2026 قرب إتاحة Tap to Pay عبر iPhone، فقد يصبح جوال موظف الاستقبال هو جهاز القبول نفسه.
وللمطور فرقان مهمان بين القناتين: الأول أن مدد صلاحية التفويض بحضور البطاقة أقصر في قواعد Visa (5 أيام تقويمية لأغلب العمليات بحضور البطاقة)، والثاني أن الاعتراض على عملية بحضور البطاقة مع تحقق صحيح أصعب على المحتال من عملية عبر الإنترنت، لذلك تختلف أنماط الاحتيال والاعتراض بين القناتين. وفي التقارير يجب أن يعرف النظام قناة كل عملية، لأن الرسوم والمدد والاعتراضات تختلف بها، وهذا حقل يجب أن يأتي في كل حدث من المزود وكل سطر في ملف التسوية.
كيف تكتب معالج الأحداث في الإنتاج؟
المختبر قرأ الأحداث من ملف، أما في الإنتاج فتصل كطلبات HTTP من المزود إلى نقطة في تطبيقك، ولهذه النقطة قواعد تتكرر عند كل المزودين تقريباً، وتجدها في توثيق كل منهم بتفاصيله:
- تحقق من التوقيع: كل مزود يوقع أحداثه بسر مشترك، فتحقق من التوقيع قبل أي شيء، والسبب أن نقطة الأحداث رابط عام، وأي شخص يعرفه يستطيع أن يرسل «تم الدفع». والسر نفسه في مدير الأسرار، لا في الكود ولا في المستودع.
- احفظ ثم رد بسرعة: احفظ الحدث كما هو بمعرفه في جدول الأحداث، ورد بنجاح خلال ثوان، ثم عالجه في عامل خلفي، لأن المزود يعيد الإرسال إذا تأخر ردك، وكل إعادة حدث مكرر جديد تحتاج أن ترفضه.
- عالج آمناً للتكرار: القيد الفريد على معرف الحدث، كما في المختبر، يجعل الإعادة غير ضارة.
- لا تثق بالترتيب: رتب بالوقت أو التسلسل، وعند التعارض اسأل API المزود عن الحالة الحالية.
- أعد المعالجة عند الحاجة: لأن الحدث محفوظ كما وصل، تستطيع بعد إصلاح خطأ في المعالج أن تعيد معالجة أحداث يوم كامل، وهذا ما ينقذك عندما تكتشف خطأ بعد أسبوع.
وهذا النمط، حفظ الحدث ثم معالجته في الخلفية، هو نفس نمط Outbox وInbox الذي شرحناه في مقالات الطوابير، وفكرته واحدة: لا تخلط استلام الرسالة بتنفيذ أثرها.
بيانات البطاقة ونطاق PCI DSS
معيار أمن بيانات صناعة بطاقات الدفع PCI DSS ينطبق كما تقول صفحته لدى مجلس PCI SSC على الجهات التي تخزن بيانات حاملي البطاقات أو تعالجها أو ترسلها، أو التي قد تؤثر على أمن بيئة هذه البيانات، ويشمل ذلك التجار والمعالجين والمستحوذين والمصدرين ومقدمي الخدمات. وأهم قرار تتخذه الشركة الناشئة في هذا الموضوع يحدث في الأسبوع الأول، وهو أين تمر بيانات البطاقة، والصورة التالية تبين الخيارين:

فإذا مرت بيانات البطاقة على سيرفرات الشركة، ولو لثانية في طريقها إلى المزود، أصبحت السيرفرات والشبكة والسجلات وكل من يصل إليها داخل نطاق المعيار، وأصبح خطأ واحد مثل تسجيل جسم الطلب كاملاً في السجلات تسريباً لأرقام البطاقات. أما إذا استخدمت الشركة حقل الدفع الذي يقدمه المزود داخل صفحتها أو صفحة الدفع التي يستضيفها، فالمتصفح يرسل البيانات للمزود مباشرة، والشركة تستلم توكن لا يفيد غيرها، فيضيق النطاق كثيراً، ولكنه لا يختفي: الصفحة التي يظهر فيها حقل الدفع ما زالت مسؤوليتها، فالسكربت الخارجي الذي يضاف إليها دون مراجعة قد يقرأ ما يكتبه العميل. وتفاصيل المعيار وأنواع استبيانات التقييم الذاتي لها مقال مستقل في السلسلة عن أمن المدفوعات، والقاعدة هنا: لا تجعل بيانات البطاقة تصل إليك إلا إذا كان نموذج عملك يحتاجها فعلاً، ولا تستخدم بطاقة حقيقية في أي اختبار، فلكل مزود بطاقات اختبار في بيئته التجريبية، كما في توثيق الاختبار لدى Stripe كمثال.
ما الذي غيره الفريق في التطبيق؟
بعد الشهر الأول كتب الفريق قائمة تغييرات، وهي ملخص عملي لكل ما في هذا المقال:
- جدول أحداث لا جدول مدفوعات فقط: كل حدث من المزود يحفظ كما وصل بمعرفه في جدول مستقل، والقيد الفريد على معرف الحدث يمنع معالجته مرتين، ثم تحدث الحالة من الحدث.
- آلة حالات صريحة: الانتقالات المسموحة مكتوبة في الكود كما في المختبر، وكل انتقال مرفوض يفتح فحصاً يسأل API المزود عن الحالة الحالية بدلاً من الرفض الصامت.
- الإيراد من المحصل والمسوى: لوحة التحكم تعرض المفوض والمحصل والمسوى منفصلة، والتقارير المالية تبنى على ملف التسوية.
- مطابقة يومية: ملف التسوية يطابق مع الدفتر كل صباح، وكل
MISMATCHتنبيه في قناة الفريق. - مراقبة التفويضات: تقرير يومي بالتفويضات غير المحصلة التي تقترب من انتهاء صلاحيتها.
- الاعتراضات: لكل اعتراض حالة في النظام وموعد رد من المزود، وأدلة كل اشتراك محفوظة مدة تكفي مدد الاعتراض.
- الاسم في كشف الحساب: طلب الفريق من المزود أن يظهر اسم التطبيق المعروف في كشف حساب العميل، بدلاً من الاسم القانوني للشركة الذي لا يعرفه أحد.
أخطاء شائعة
| الخطأ | لماذا يضر | البديل |
|---|---|---|
| حساب الإيراد من التفويض | التفويض حجز قد ينتهي أو يحصل جزئياً | الإيراد من المحصل، والتقارير من التسوية |
| معالجة الإشعار المكرر | إيراد أو اشتراك مضاعف | قيد فريد على معرف الحدث |
| الثقة بترتيب وصول الإشعارات | حالة خاطئة ودفتر لا يطابق | وقت الحدث أو تسلسله، وسؤال المزود عند التعارض |
| رقم ثابت لصلاحية التفويض في الكود | المدة تختلف بالشبكة ونوع العملية والمزود | من رد المزود، مع مراقبة الاقتراب من الانتهاء |
| الاسترداد بدل الإلغاء قبل التحصيل | عملية إضافية ورسوم قد لا تعود | Void للتفويض غير المحصل |
| اعتبار المسوى نهائياً للتخطيط | الاعتراض قد يسحبه بعد أشهر | احتياطي للاعتراضات وأدلة محفوظة |
| بيانات البطاقة على سيرفرك | نطاق PCI DSS واسع وخطر تسريب في السجلات | حقل المزود أو صفحته وتوكن |
| اختبار ببطاقة حقيقية | بيانات حقيقية في بيئة اختبار، ورسوم حقيقية | بطاقات الاختبار في البيئة التجريبية للمزود |
قائمة تحقق قبل إطلاق الدفع بالبطاقة
- بيانات البطاقة لا تصل إلى سيرفراتي، وأستلم توكن من المزود.
- 3D Secure مختبر ببطاقات الاختبار التي تفرض خطوة التحقق، والتطبيق يتعامل مع إغلاقها في المنتصف.
- لكل دفعة حالات منفصلة: مفوضة ومحصلة ومسواة ومستردة ومعترض عليها ومنتهية.
- الأحداث تحفظ بمعرفها مع قيد فريد، وترتب بوقتها، والانتقال المرفوض يسأل المزود عن الحالة.
- أعرف مدة صلاحية التفويض لكل نوع عملية عندي من المزود، وأراقب التفويضات القريبة من الانتهاء.
- الإلغاء قبل التحصيل Void، والاسترداد بعده، وكلاهما مرتبط بالعملية الأصلية في الدفتر.
- مطابقة يومية مع ملف التسوية، والرسوم والاعتراضات مسجلة منفصلة عن الإيراد.
- أدلة كل اشتراك محفوظة مدة تكفي مدد الاعتراض، ولكل اعتراض حالة وموعد.
- الاسم في كشف حساب العميل واضح، وطريقة الإلغاء والاسترداد واضحة في التطبيق.
- طلب إنشاء الدفع يحمل مفتاح منع التكرار، والدفعة التي انقطع الاتصال أثناءها تبقى «غير معروفة» حتى أسأل المزود عن حالتها.
- الاشتراكات المتجددة موسومة بشكل صحيح عند المزود، والعضو يستلم إشعاراً قبل كل تجديد.
الخلاصة
وصلنا لنهاية الموضوع، وأهم ما فيه:
- عملية البطاقة سلسلة أحداث بين خمسة أطراف: التفويض في ثوان، ثم التحصيل، ثم المقاصة والتسوية، ثم الاسترداد أو الاعتراض بعد أيام أو أشهر.
- التفويض حجز يقرره البنك المصدر، و3D Secure يتيح للمصدر التحقق من المشتري، والتفويض له مدة صلاحية تحددها الشبكة والمزود، وفي قواعد Visa مثلاً 10 أيام للعمليات دون حضور البطاقة.
- الإيراد هو المحصل، والحقيقة المالية في ملف التسوية، والرسوم والاعتراضات بنود منفصلة.
- الاعتراض حق لحامل البطاقة عبر بنكه، ومدده في قواعد Visa تصل إلى 120 يوماً وأكثر في بعض الحالات، فالمال المسوى ليس نهائياً في التخطيط.
- في المختبر أعطت نفس الأحداث 495 و396 و297 ريالاً بحسب طريقة المعالجة، والمطابقة اليومية هي ما كشف الخطأ.
- أسهل بيانات بطاقة تحميها هي التي لا تصل إلى سيرفرك أصلاً.
وبقية مقالات قسم التقنية المالية والمدفوعات:
دليلكيف تنتقل الأموال بين البنوك؟ المقاصة Clearing والتسويةماذا يحدث بين ضغطة «ادفع» ووصول المال: الدفع والمقاصة والتسوية الإجمالية والصافية، مع محاكاة يوم من التسويات بين ستة بنوك.
دليلمنظومة المدفوعات في السعودية: سريع وSARIE ومدى وسدادالأنظمة الوطنية للمدفوعات في المملكة كما تصفها مصادر البنك المركزي، وما يعنيه كل نظام للمطور، مع تحليل تقارير نقاط البيع الأسبوعية.
دليلالمصرفية المفتوحة Open Banking: إطار البنك المركزي والواجهاتما تتيحه المصرفية المفتوحة في المملكة وما تفرضه لائحتها، والموافقة من الطلب إلى الحذف، مع مختبر لتطبيق يقرأ بموافقة العميل فقط.
دليلالمحفظة الرقمية من الداخل: دفتر القيد المزدوج Ledgerكيف تبني محفظة رقمية لا يضيع فيها ريال ولا يخصم مرتين، بالقيد المزدوج والمعاملات والمطابقة مع مزود الدفع.
دليلIdempotency: كيف تجعل العملية آمنة عند تكرارهالماذا تصل الرسالة نفسها أكثر من مرة، وكيف تكتب مستهلكاً يطبقها مرة واحدة فقط مهما تكررت.سجل التحديثات
- أكتوبر 2026: كتابة المقال، وتشغيل مختبر أحداث الدفع والمطابقة على Python 3.13.3 ببيانات اختبار، والتحقق من قواعد Visa (إصدار 18 أبريل 2026) وصفحة EMVCo لـ 3D Secure وصفحة PCI DSS وتوثيق Stripe بتاريخ 27 أكتوبر 2026.