الأهم أولاً: `PaymentsService.webhook()` كان يقبل أي جسم `{ payment_id }`
بلا أي تحقّق توقيع — من يعرف معرّف دفعة معلَّقة كان يستطيع تحويلها «ناجحة»
ويشحن رصيداً من عدم (محفظة راكب أو رصيد سائق تشغيلي). الآن كل تغيير حالة
محروس بـ`adapter.verifyWebhook(headers, payload, tenant)`، ولا شيء يُقرأ من
الحمولة قبل ذلك كقرار ثقة — قراءة المرجع لتحديد المستأجر ليست قراراً.
البنية (docs/07 · docs/24 — P1):
- `PaymentAdapter`: charge() يعيد instant (كاش) · redirect (بوابة API حقيقية)
· invoice (بلا API، تسوية عبر P2).
- PayMob (مصر): تسلسل auth→order→payment_key→iframe حقيقي عبر fetch،
وتحقّق HMAC-SHA512 على تسلسل حقول ثابت (بروتوكول PayMob الرسمي بالضبط)
بمقارنة ثابتة الزمن. مفاتيح كل مستأجر مستقلة — حساب تاجر خاص به.
- كليق/شام كاش/MTN/سيرياتيل/زين كاش: محوّل مشترك واحد لأن سلوكها متطابق
فعلياً في سيرو (`create_*_invoice.php` تُنشئ فاتورة فقط، لا نداء بوابة
حيّاً) — مرجع + حساب استلام معروض، والتسوية عبر رسالة SMS لا webhook.
MTN/سيرياتيل الحقيقيَّين (توكن+OTP) موثَّقان كبند مفتوح: لا نبني تكاملاً
لا نملك اعتماداً حيّاً للتحقّق منه.
- `PATCH /payments/settings` لأدمن المستأجر: مفاتيح PayMob · حسابات
الاستلام · سرّ webhook الرسائل — الاستجابة لا تُعيد الأسرار.
تنظيف: إزالة الإشارات المتبقّية لحاوية `martin` من docs/07 (أُزيلت فعلياً
سابقاً)، وتحديث هيكل الكود الموثَّق ليطابق ما هو مبنيّ فعلاً.
25 اختباراً جديداً (226 إجمالاً) — منها توقيع PayMob محسوب فعلياً ومُتحقَّق،
وتلاعبٌ بالحمولة بعد التوقيع يُرفض، وسبع حالات تثبت إغلاق ثغرة الـwebhook.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
P2 (كليك/شام كاش بلا API):
- الرسالة تُحفظ خاماً **قبل** أي تحليل: التحليل قد يفشل فنحتاج الأصل لإعادة
المعالجة، وعند النزاع يكون النصّ الأصلي هو الحجّة لا تفسيرُنا له.
- النقطة تصنع المال، فرسالة مزوّرة = رصيد من عدم. الحماية: سرّ لكل مستأجر
بمقارنة ثابتة الزمن (المقارنة النصّية تسرّب السرّ حرفاً حرفاً زمنياً)،
وبصمة محتوى فريدة تمنع احتساب إعادة الإرسال مرتين.
- Gemini بحرارة صفر ومطالَب بإرجاع null عند عدم اليقين: نموذج يخمّن مبلغاً
يسوّي فاتورة بمال لم يصل. بلا مبلغ صريح → مراجعة بشرية لا تسوية.
- المطابقة بالمرجع أولاً، ثم بالمبلغ خلال 24 ساعة وبشرط فاتورة وحيدة —
فاتورتان بنفس المبلغ التباسٌ يُراجَع، لا تسويةٌ عشوائية لإحداهما.
- التسوية تمرّ بـmarkSuccess نفسه فلا يتفرّع مسار مالي ثانٍ.
إزالة حاوية martin: انطلق منصّة قائمة بذاتها لها خوادمها؛ دورنا طلب وردّ لا
استضافة خرائط (قرار المالك).
docs/25: جرد الحاويات · لماذا الدفع ليس خدمة منفصلة · سيرفر السوبر-أدمن
المنفصل ونطاقه الفرعي · WebSocket مقابل الاستطلاع بالأرقام · أحجام السيرفرات
على أساس الذروة لا المعدّل · نقل مستأجر · ترتيب التوسّع.
13 اختباراً جديداً (201 إجمالاً، كلها خضراء).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
الأساس المالي حسب docs/24: فصل ما نملكه عمّا نحتفظ به لغيرنا.
- جدولان منفصلان (`tenant_revenue_ledger` · `tenant_pending_ledger`) لا جدول
واحد بعمود نوع: استعلامٌ ينسى الشرط كان يجعل المالك يسحب من مال الركّاب.
مضافان فقط، والرصيد مشتقّ لا حقل يُحدَّث.
- فهرس فريد (tenant_id, ref) = حارس التسوية المزدوجة. التصادم يُلتقط من
القاعدة لا بالفحص المسبق وحده — نداءان متزامنان يمرّان معاً قبل أي كتابة.
- رسم العملية من مصدر واحد (35 ل.س · 5 ج.م · 0.20 د.أ) قابل للتجاوز من إعداد
المستأجر، مع قصّه عند المبلغ حتى لا يخرج المستخدم بصافٍ سالب.
- توجيه الدفع: شحن السائق ← إيراد + رصيده التشغيلي · شحن الراكب ← أمانة ·
الرسم ← إيراد. وفُتح مسار شحن السائق الذي لم يكن له مدخل إطلاقاً.
- ثغرة سُدّت: `purpose` يصل من الجسم، فراكب كان يستطيع إرسال `credit_topup`
فيُسجَّل مالُه إيراداً ويُشحن حساب سائق لا يملكه. الدور الآن من التوكن.
- `/admin/overview`: الإيراد من الدفتر، وعمولة الرحلات حقل منفصل عنه.
- `/admin/wallet/summary` و`/revenue-by-reason` لأدمن المستأجر.
25 اختباراً جديداً (188 إجمالاً، كلها خضراء). أحدها أمسك عطلاً فعلياً: صافٍ
صفري كان ينادي الدفتر بصفر فيرمي خطأً بعد قيد الرسم — عملية نصف مطبَّقة.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
إصلاح انحدار أدخلتُه: /admin/overview كان يستعلم عن `t.created_at` وهو غير
موجود في كيان الرحلة (الاسم `requested_at`)، فترجع النقطة 500 ويُفرَّغ جدول
المستأجرين في لوحة السوبر-أدمن. فحص الأنواع لم يمسكه لأن استعلامات
QueryBuilder نصوص، والمحكّ الذي كان سيمسكه لم يُشغَّل.
قرارات المالك 2026-07-18:
- الخرائط: `intaleq_maps` حصراً (SDK انطلق على MapLibre). تصحيح قرار سابق
خاطئ: flutter_map + latlong2 طبقة منافسة تعطي نوعَي LatLng متضاربين.
السبب ليس عمل جوجل في سوريا — بل قِدَم بياناتها في المنطقة.
- docs/24 جديد: فلسفة المال — فصل الإيراد (شحن السائق · رسوم العمليات) عن
الأمانة (شحن الراكب)، بمحفظتين لا بحقل حالة، ودفتر مضاف فقط.
- P موسّعة (بوابات · تسوية بالرسائل لكليك/شام كاش · رسوم بالدولة · تقارير)
و O5 (ترتيب المهام المجدولة)، وتأجيل Android Auto/CarPlay.
- الحزم: نسخ سيرو الأحدث + live_activities لشاشة القفل في iOS.
تنظيف: إخراج 463 ملف بناء NDK من التتبّع، وإخراج شهادات التوقيع ومفتاح
App Store من المستودع (تُولَّد من جديد عند الحاجة) مع تحديث .gitignore.
الملفات باقية على القرص — أُزيلت من الفهرس فقط.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>