feat: P1 — بوابات دفع حقيقية (PayMob) + إغلاق ثغرة webhook حرجة

الأهم أولاً: `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>
This commit is contained in:
Hamza-Ayed
2026-07-18 15:50:05 +03:00
co-authored by Claude Sonnet 5
parent da035e46a4
commit d4ac38ca87
16 changed files with 852 additions and 42 deletions
+11 -1
View File
@@ -62,9 +62,19 @@ SMS_SENDER=
FCM_SERVER_KEY=
FCM_ENDPOINT=https://fcm.googleapis.com/fcm/send
# ---- Gemini (رؤية: قراءة وثائق + مطابقة وجه) ----
# ---- Gemini (رؤية: قراءة وثائق + مطابقة وجه + تحليل رسائل التحويل P2) ----
GEMINI_API_KEY=
GEMINI_MODEL=gemini-2.0-flash
# ---- PayMob (مصر) — احتياط تطوير فقط (docs/24 — P1) ----
# الإنتاج: كل مستأجر مفاتيحه الخاصة في tenant.settings.payments.paymob
# (PATCH /payments/settings من لوحة أدمن المستأجر) — حساب تاجر مستقل لكل
# مستأجر، فمالُه يصل لحسابه هو. هذه المتغيّرات تُستعمل فقط إن كان المستأجر
# بلا إعداد خاص، لحساب تجريبي مشترك وقت التطوير.
PAYMOB_API_KEY=
PAYMOB_INTEGRATION_ID=
PAYMOB_IFRAME_ID=
PAYMOB_HMAC_SECRET=
# ---- سرّ المنصة (لتعيين أول أدمن) — ولّده: openssl rand -hex 24 ----
PLATFORM_SECRET=