ما الذي كشفه تدقيق نظام الدفع الخاص بنا
نتيجتان حرجتان، ولم تكن أي منهما حيث كنا سنبحث. تقرير صريح عن أخطاء أُصلحت منذ ذلك الحين.

في أغسطس 2026 راجعنا نظام الدفع الخاص بنا من الأساس. ليس لأن شيئًا قد حدث، بل لأنه كان قد أُطلق قبل ذلك بوقت قصير وأردنا أن نعرف ما الذي فاتنا.
وقد فاتنا الكثير. هذا المقال يسمّي النتائج. كل ما يُوصف هنا قد أُصلح، ومعظمه كان مذكورًا أصلًا في سجل التغييرات العلني.
خطأ النمذجة الذي نشأت عنه النتيجتان الحرجتان
عندما يشتري أحدهم Premium منا، يُنشأ سجل عميل لدى مزوّد الدفع. خزّنّا هذا السجل تحت معرّف السيرفر، لأن Premium يصل في النهاية إلى سيرفر.
وهذا بالضبط النموذج الخاطئ، وكان أصل أخطر نتيجتين.
سجل العميل يخص المشتري. فهو يحمل وسيلة الدفع الخاصة به، وسجل فواتيره، وبمجرد أن يدفع أحدهم لسيرفرين، عدة اشتراكات. وربطه بمعرّف السيرفر يعني أن عدة أشخاص يشيرون إلى الصف نفسه.
النتيجة 1: بيانات عميل آخر قابلة للكتابة فوقها دون دفع
الشراء متاح لأي عضو في السيرفر، والدالة التي تجلب سجل العميل أو تنشئه تعمل قبل الدفع. كانت تجد الصف عبر معرّف السيرفر وتعيد كتابته بصمت لصالح المشتري المحتمل الجديد، بما في ذلك عنوان بريده الإلكتروني لدى مزوّد الدفع.
عمليًا كان ذلك يعني: نقرة واحدة على سيرفر اشترى فيه أحدهم في وقت ما كانت كافية. بعدها كانت فواتير كل اشتراكات ذلك المشتري الأصلي ستذهب إلى عنوان البريد الإلكتروني للشخص الغريب. دون دفع، ودون تأكيد.
أُصلح ذلك بالتحقق، قبل أي إعادة استخدام، من أن السجل يخص هذا الحساب فعلًا. وعند عدم التطابق يُنشأ سجل منفصل ويُترك الآخر دون مساس.
النتيجة 2: بوابة الفوترة كشفت اشتراكات أشخاص آخرين
خطأ النمذجة نفسه، على مستوى أبعد. كان التحقق من الملكية ينطبق على اشتراك السيرفر، لكن جلسة البوابة كانت تُفتح لـ سجل العميل، الذي يمكن بحكم تصميمه أن يحمل عدة سيرفرات.
فكان بإمكان أي شخص اشترى بشكل مشروع على سيرفر واحد أن يرى ويدير الاشتراكات التي يحملها السجل نفسه لسيرفرات أخرى.
أُصلح ذلك بتحديد سجل العميل من عملية الشراء المحددة بدلًا من صف السيرفر.
نتيجتان كان الكود فيهما يناقض شروطنا نحن
هذه الفئة هي التي أزعجتنا أكثر من غيرها، لأنها ليست تقنية.
القائمة السوداء كانت تسلب الوقت المدفوع. تنص شروطنا على أن الاشتراك ينتهي في نهاية المدة الحالية. لكن عند حظر سيرفر، كان الكود يطلب إلغاءً فوريًا: دون استرداد، ودون خدمة من تلك اللحظة عن فترة مدفوعة سلفًا. كان الكود يفعل شيئًا مختلفًا عما وعدنا به كتابةً. الآن تستمر الفترة المدفوعة حتى نهايتها.
التنازل عن حق الانسحاب لم يكن قابلًا للإثبات. قبل الشراء يجب أن تؤكد أن الخدمة تبدأ فورًا. هذا التأكيد كان يعيش في المتصفح فقط، ولم يُرسل أبدًا ولم يُخزَّن في أي مكان. عبء الإثبات يقع علينا، ولم يكن لدينا ببساطة أي دليل. الآن أصبحت الموافقة حقلًا إلزاميًا في مسار الدفع، تُخزَّن مع طابع زمني وصياغة ذات إصدار، والصياغة نفسها ذات إصدارات: النص الموجود لا يُعدَّل أبدًا، بل يُنشأ دائمًا إصدار جديد، وإلا لما عادت السجلات القديمة قابلة لإعادة الإنتاج.
الجولة الثانية كانت أسوأ من الأولى
بعد أول 21 نتيجة، نظرنا مجددًا بعد بضعة أسابيع. وكان ذلك القرار الصحيح، لأن أخطر نتيجة على الإطلاق لم تظهر إلا حينها.
استرداد على السيرفر B كان يحذف ترخيص مدى الحياة المدفوع للسيرفر A. إشعارات الاسترداد والاسترجاع (Chargeback) تذكر عملية دفع فقط، لا سيرفرًا. كنا نحدد السيرفر عبر سجل العميل. وبعد أن قررنا بشكل صحيح في الجولة الأولى أن السجل يخص مشتريًا عبر عدة سيرفرات، لم يعد هذا التحديد بالذات قاطعًا. فكان بإمكان شخص دفع لسيرفرين وحصل على استرداد لأحدهما أن يفقد ترخيص مدى الحياة للآخر.
يجري التحديد الآن عبر أربع خطوات دقيقة، وكل ما لا يمكن تحديده بشكل قاطع يُبلَّغ عنه بدلًا من تخمينه.
الاسترجاع الذي كسبناه لم يُعِد شيئًا. عند الاسترجاع كان الكود يحذف صف الشراء، وهو الدليل الوحيد على أن هذا السيرفر امتلك يومًا ترخيص مدى الحياة. وكسب القضية لاحقًا لم يكن قادرًا على التراجع عن ذلك. الآن يضع الكود علامة بدلًا من الحذف.
الاحتفاظ بالبيانات كان يحذف سيرفرات مدفوعة. عندما يغادر البوت سيرفرًا، تُزال بياناته بعد 30 يومًا. وعملية التنظيف هذه لم تسأل أبدًا إن كان اشتراك جارٍ مرتبطًا به.
ما الذي استخلصناه
الأخطاء لم تكن في كود الدفع. ولا نتيجة حرجة واحدة تعلقت بمبلغ أو عملة أو عملية حسابية. كلها كانت في النموذج الكامن وراءه: من يملك سجل العميل، ومن يملك عملية الدفع، ومن يملك السيرفر. ومن يدقق نظام دفع بالنظر إلى المبالغ، فإنه ينظر بعيدًا عن الجزء الخطير.
التدقيق يحتاج إلى جولة ثانية. أخطر نتيجة كانت نتيجة مباشرة لإصلاح من الجولة الأولى. وهذه ليست مصادفة: تصحيح النموذج يُبطل افتراضات لا تزال قائمة في مواضع أخرى.
«هكذا تقول شروطنا» جملة يمكن التحقق منها. نتيجتان كانتا اختلافًا بين ما وعدنا به كتابةً وما يفعله الكود. وهذا يمكن قراءته، ولا أحد يفعل ذلك، لأنه يبدو قانونًا لا هندسة.
شروط الشراء كاملةً موجودة في شروط الخدمة، وحق الانسحاب تحت الانسحاب.
مقالات أخرى
القانون داخل الكود الخاص بنا
زر إلغاء يجب أن يعمل دون تسجيل دخول. ونموذج يجب ألا يرفضك أبدًا. لماذا تُعد الالتزامات القانونية عندنا بنية تقنية، لا مجرد نصوص جاهزة.
خلف الكواليسلماذا يبقى كل شيء تقريبًا في AlphaCloud مجانيًا
معظم بوتات Discord تحجب أكثر ميزاتها فائدة خلف اشتراك. نحن لا نفعل ذلك عمدًا، وهذا له علاقة بالكرم أقل مما يبدو.
خلف الكواليسمراقبة 4000 سيرفر Discord: Grafana وPostHog وما أظهراه لنا
حلقة تسببت بصمت في نصف حركة البيانات لدينا، وثلاثة ملايين طلب عديم الفائدة يوميًا، وهجوم انتهى بعد دقيقة واحدة. ما الذي تراه مراقبتنا، وما الذي لا تراه عمدًا.