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>
117 lines
9.7 KiB
Markdown
117 lines
9.7 KiB
Markdown
# 18 — الرصيد التشغيلي وعمولة السائق (نموذج الدفع المسبق)
|
|
|
|
> **طبقتان مختلفتان لا تُخلطان:**
|
|
> - **[05-pricing-billing](05-pricing-billing.md)** = كيف تقبض **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`) نُفِّذت على **نموذج الاقتطاع**:
|
|
```ts
|
|
price_for_driver = price_for_passenger - commission // ❌ ليس فلسفتنا
|
|
```
|
|
الصحيح حسب هذا المستند:
|
|
```ts
|
|
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](07-integrations.md)** وطرق الدفع لكل دولة (كليك · شام كاش · إي كاش · بيموب · فوري …).
|
|
- باقة قد تحمل **حافزاً**: «اشحن 50 واحصل على 55» — أداة تسويق مباشرة بيد المستأجر.
|
|
- الحافز يُسجَّل قيداً منفصلاً في الدفتر (`bonus`) لا يُخلط بالمدفوع فعلاً — وإلا فسدت المحاسبة.
|
|
|
|
### 5.4 حاجز دخول السائق الجديد
|
|
النموذج يطلب من السائق أن يدفع **قبل** أن يكسب — وهذا يقتل التجنيد.
|
|
المعالجات (قرار مالك، لا افتراض تقني):
|
|
- **رصيد ترحيبي** (مثلاً 5 دنانير مجاناً) — الأشيع.
|
|
- **فترة سماح** (أول 7 أيام أو أول 20 رحلة بلا خصم).
|
|
- **أرضية سالبة أوسع للسائق الجديد** ثم تُشدَّد.
|
|
|
|
### 5.5 وسيلة الدفع لا تغيّر شيئاً
|
|
| الدفع | من يقبض الأجرة | من أين العمولة |
|
|
|---|---|---|
|
|
| كاش | السائق مباشرة | الرصيد التشغيلي |
|
|
| محفظة الراكب | السائق (تُحوَّل لمحفظة أرباحه) | الرصيد التشغيلي |
|
|
|
|
**قاعدة واحدة لا استثناء لها** — وهذا أبسط بكثير من تفريع المنطق حسب وسيلة الدفع، وهو ما قاله المالك حرفياً: «إن كانت كاش أو محفظة».
|
|
|
|
> **مفارقة تستحق الانتباه:** في الدفع بالمحفظة، المنصة **تلمس المال فعلاً**، فتستطيع تقنياً اقتطاع العمولة منه. لكننا **لا نفعل** — عمداً. لأن قاعدتين مختلفتين حسب وسيلة الدفع تعني منطقاً مزدوجاً، ومحاسبةً مزدوجة، وسائقاً لا يفهم لماذا اختلف دخله. الاتساق أثمن من التحسين هنا.
|
|
|
|
## 6. لماذا تُحسب العمولة على الرحلة رغم أنها تُخصم من مكان آخر؟
|
|
لأن `commission_amount` على الرحلة هو **سبب** القيد في دفتر الرصيد التشغيلي. بدونه لا يستطيع السائق (ولا خدمة العملاء) الإجابة عن: «لماذا نقص رصيدي 400 فلس؟». كل خصم يشير إلى `trip_id`.
|
|
|
|
## 7. البنود المطلوبة (تُضاف للـ[17-backend-backlog](17-backend-backlog.md) كمجموعة 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](05-pricing-billing.md) · [17-backend-backlog](17-backend-backlog.md) · [19-entitlements-licensing](19-entitlements-licensing.md)
|