18 — الرصيد التشغيلي: السائق يشحن سلفاً، الراكب يدفع له الأجرة كاملة، والعمولة تُخصم من الرصيد لا من الأجرة. مقارنة صريحة: هذا نموذج inDrive/ Yango/سيرو لا نموذج أوبر — وهو المعيار الفعلي في أسواق الكاش المستهدفة. ثمنه ثلاثة التزامات: الحجب عند نفاد الرصيد (بدونه ينهار)، باقات لكل عملة، ومعالجة حاجز دخول السائق الجديد. 19 — الاستحقاقات: علم الميزة في التطبيق قرار عرض لا حدّ أمني. الهندسة العكسية تكشف شاشة تنادي نقطة ترجع 403. الحدّ الحقيقي FeatureGuard على السيرفر + tenant_id من JWT موقَّع. الحقيقة التي تُقال صراحةً: ما يعمل كلياً على الجهاز لا يُحمى، ووضع السيادة لا يُحمى تقنياً أصلاً (المستأجر يملك السيرفر) — فليشمل سعره كل شيء بدل وهم حماية. تصحيحان على المجموعة B (كلاهما بُني على افتراض خاطئ مني): - B3: مشوار الوصول تعويضُ عدم حضور عند الإلغاء بعد 5 دقائق انتظار، لا بند في كل أجرة كما بنيته - B6: price_for_driver = price_for_passenger — العمولة لا تُقتطع من الأجرة Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
9.7 KiB
18 — الرصيد التشغيلي وعمولة السائق (نموذج الدفع المسبق)
طبقتان مختلفتان لا تُخلطان:
- 05-pricing-billing = كيف تقبض Tripz من المستأجر (اشتراك + نسبة GMV).
- هذا الملف = كيف يقبض المستأجر (سيرو مثلاً) عمولته من سائقيه.
مصدره: قرار المالك (2026-07-17) — «فلسفة التطبيق اللي بدنا إياه».
1. الفكرة في سطر واحد
السائق يشتري رصيداً تشغيلياً مقدَّماً. الراكب يدفع للسائق الأجرة كاملة (كاش أو محفظة). المنصة لا تلمس أجرة الرحلة إطلاقاً — بل تخصم عمولتها من الرصيد التشغيلي المدفوع سلفاً.
2. المثال المرجعي (كلام المالك حرفياً)
السائق يشحن رصيده التشغيلي → 4.000 دينار
الراكب يصل ويدفع للسائق (كاش/محفظة) → 4.000 دينار ← يأخذها السائق كاملة
عمولة التطبيق 10% من 4.000 → 0.400 دينار
تُخصم من الرصيد التشغيلي → 4.000 - 0.400 = 3.600 دينار
الرصيد التشغيلي بعد الرحلة = 3.600 دينار. أجرة الراكب لم تُمسّ.
ملاحظة على الترقيم: الـ«4 دنانير» في المثال صدفة — رصيد الشحن وأجرة الرحلة رقمان مستقلان تماماً. لو شحن السائق 50 ديناراً وأنهى نفس الرحلة لصار رصيده 49.600.
3. هل هذا نموذج متعارف عليه عالمياً؟ — نعم، لكنه ليس نموذج أوبر
هناك نموذجان سائدان، وكلاهما شرعي:
| اقتطاع من الأرباح (take-rate) | الرصيد المدفوع مسبقاً (prepaid credit) ← نموذجنا | |
|---|---|---|
| من يقبض المال؟ | المنصة تقبض الأجرة ثم تحوّل للسائق الصافي | السائق يقبض الأجرة كاملة مباشرة |
| من يستعمله | Uber · Bolt · Careem · Grab · Didi | inDrive · Yango/Yandex (شركاء) · Maxim · أغلب التطبيقات المحلية في الشرق الأوسط وأفريقيا وجنوب آسيا · سيرو |
| يناسب | أسواق الدفع الإلكتروني الغالب | أسواق الكاش الغالب |
| مشكلة الكاش | السائق يصير مديناً للمنصة → تحصيل ومطاردة وديون معدومة | لا توجد — العمولة مقبوضة سلفاً |
| التدفق النقدي | متأخر | مقدَّم |
| حاجز الدخول للسائق | صفر | يدفع قبل أن يكسب ← يحتاج معالجة |
الحكم: النموذج الذي تصفه هو المعيار الفعلي في الأسواق التي تستهدفها (سوريا · الأردن · مصر) حيث الكاش هو الغالب. أوبر نفسها في الأسواق كثيفة الكاش تضطر لتتبّع رصيد سائق سالب ثم مطاردة تحصيله — وهو بالضبط الوجع الذي يلغيه نموذجك من أصله. اختيارك سليم، وليس نسخاً أعمى عن سيرو.
لكن ثمنه ثلاثة التزامات (تفصيلها في §5): الحجب عند نفاد الرصيد، وباقات شحن لكل عملة، ومعالجة حاجز دخول السائق الجديد.
4. ما الذي يتغيّر في الكود (تصحيح لما نُفِّذ في B6)
الشريحة الأولى من المجموعة B (commit f3fdc1b) نُفِّذت على نموذج الاقتطاع:
price_for_driver = price_for_passenger - commission // ❌ ليس فلسفتنا
الصحيح حسب هذا المستند:
price_for_driver = price_for_passenger // السائق يأخذ الأجرة كاملة
commission_amount = split(price_for_passenger) // تُحسب وتُسجَّل على الرحلة
→ ثم تُخصم من driver_credit (رصيد منفصل تماماً)، لا من الأجرة
price_for_driver وprice_for_passenger يبقيان مفيدين — لكن سبب اختلافهما ليس العمولة، بل: خصم على الراكب (كوبون/عرض) بينما السائق يقبض كاملاً، أو مكافأة تفاوض (ai_negotiated_bonus عند سيرو).
5. الآليات المطلوبة
5.1 الرصيد التشغيلي (driver_credit)
- محفظة ثانية منفصلة عن محفظة أرباح السائق. لا تُخلط: هذه للعمولة سلفاً، وتلك لأمواله.
- دفتر قيود append-only + خصم ذرّي — نفس نمط I1 حرفياً (
UPDATE … WHERE balance >= :amount). - الخصم يحدث عند
completed(لا عندpaid): الرحلة تمّت فالعمولة استُحقّت، بغضّ النظر عن وسيلة الدفع.
5.2 الحجب عند نفاد الرصيد — الالتزام الأهم
بلا حجب، النموذج ينهار: سائق برصيد صفر يعمل مجاناً إلى الأبد.
driver_credit < min_balance → لا يدخل فهرس geo:drivers:available
→ لا تُعرض عليه رحلات
→ إشعار: «اشحن رصيدك التشغيلي»
- الفحص عند:
setOnlineوقبلacceptوبعد كل خصم. min_balanceلكل مستأجر/عملة (قد يكون صفراً أو أعلى من أغلى عمولة متوقعة).- سباق حقيقي: رحلة تُنهى ورصيده لا يكفي العمولة → نسمح بأرضية سالبة محدودة (
credit_floor، مثلاً −1 عمولة) ثم نحجبه فوراً. البديل (رفض الخصم) يعني عمولة ضائعة.
5.3 باقات الشحن لكل عملة
كما في سيرو — لكل عملة باقاتها وأسعارها:
JOD: [5, 10, 25, 50]
SYP: [50k, 100k, 250k]
EGP: [100, 250, 500]
- الشحن عبر 07-integrations وطرق الدفع لكل دولة (كليك · شام كاش · إي كاش · بيموب · فوري …).
- باقة قد تحمل حافزاً: «اشحن 50 واحصل على 55» — أداة تسويق مباشرة بيد المستأجر.
- الحافز يُسجَّل قيداً منفصلاً في الدفتر (
bonus) لا يُخلط بالمدفوع فعلاً — وإلا فسدت المحاسبة.
5.4 حاجز دخول السائق الجديد
النموذج يطلب من السائق أن يدفع قبل أن يكسب — وهذا يقتل التجنيد. المعالجات (قرار مالك، لا افتراض تقني):
- رصيد ترحيبي (مثلاً 5 دنانير مجاناً) — الأشيع.
- فترة سماح (أول 7 أيام أو أول 20 رحلة بلا خصم).
- أرضية سالبة أوسع للسائق الجديد ثم تُشدَّد.
5.5 وسيلة الدفع لا تغيّر شيئاً
| الدفع | من يقبض الأجرة | من أين العمولة |
|---|---|---|
| كاش | السائق مباشرة | الرصيد التشغيلي |
| محفظة الراكب | السائق (تُحوَّل لمحفظة أرباحه) | الرصيد التشغيلي |
قاعدة واحدة لا استثناء لها — وهذا أبسط بكثير من تفريع المنطق حسب وسيلة الدفع، وهو ما قاله المالك حرفياً: «إن كانت كاش أو محفظة».
مفارقة تستحق الانتباه: في الدفع بالمحفظة، المنصة تلمس المال فعلاً، فتستطيع تقنياً اقتطاع العمولة منه. لكننا لا نفعل — عمداً. لأن قاعدتين مختلفتين حسب وسيلة الدفع تعني منطقاً مزدوجاً، ومحاسبةً مزدوجة، وسائقاً لا يفهم لماذا اختلف دخله. الاتساق أثمن من التحسين هنا.
6. لماذا تُحسب العمولة على الرحلة رغم أنها تُخصم من مكان آخر؟
لأن commission_amount على الرحلة هو سبب القيد في دفتر الرصيد التشغيلي. بدونه لا يستطيع السائق (ولا خدمة العملاء) الإجابة عن: «لماذا نقص رصيدي 400 فلس؟». كل خصم يشير إلى trip_id.
7. البنود المطلوبة (تُضاف للـ17-backend-backlog كمجموعة J)
| # | البند |
|---|---|
| J1 | جدول driver_credit + دفتر قيوده — خصم ذرّي داخل معاملة (نمط I1) |
| J2 | خصم العمولة من الرصيد التشغيلي عند completed (لا من الأجرة) |
| J3 | تصحيح B6: price_for_driver = price_for_passenger (السائق يقبض كاملاً) |
| J4 | الحجب عند نفاد الرصيد: setOnline + accept + بعد كل خصم |
| J5 | باقات الشحن لكل عملة + الحافز كقيد منفصل |
| J6 | رصيد ترحيبي / فترة سماح للسائق الجديد |
| J7 | أرضية سالبة محدودة (credit_floor) لسباق «أنهى الرحلة ورصيده لا يكفي» |
| J8 | شاشة/نقاط: رصيدي · تاريخ الخصومات · الشحن |
← ذو صلة: 05-pricing-billing · 17-backend-backlog · 19-entitlements-licensing