# PROGRESS - منصة SMS & Email Gateway SaaS

> ملاحظة مهمة: Claude ما عنده ذاكرة تلقائية بين الجلسات المختلفة.
> عشان أكمّل بشكل صحيح من حيث توقفت، ارفع هذا الملف (أو انسخ محتواه)
> مع مجلد المشروع في أول رسالة بالجلسة الجديدة وقل لي "اكمل من هنا".

## آخر تحديث
2026-08-12 (تحويل الإرسال من "جدولة دورية" إلى "إرسال فوري متزامن" - أهم تعديل بالمشروع، يلغي مشكلة تضارب Schedulers نهائيًا - مُختبر بتوقيت دقيق وطلبات متزامنة حقيقية)

⚠️ **تحديث مهم بخصوص الأندرويد:** المستخدم أخبرني إنه ما عنده أي جهاز أندرويد إطلاقًا. هذا قيد فيزيائي حقيقي (إرسال SMS يتطلب جهاز فعلي بشريحة SIM أو مزوّد SMS تجاري خارجي - لا بديل تقني ثالث). لذلك:
- **الإيميل:** حللناها بالكامل — يُرسل مباشرة من السيرفر عبر SMTP (Gmail أو أي مزوّد) بدون أي جهاز أندرويد، عبر `cron/process_email_queue.php` + زر اختبار فوري بصفحة الإعدادات
- **SMS:** لسه معلّق. الخيارات المطروحة على المستخدم: (أ) يشتري/يحصل على جهاز أندرويد رخيص لتشغيل `android_gateway_app` الموجود أصلًا، أو (ب) يشترك بمزوّد SMS تجاري (Twilio, Unifonic, Msegat, Taqnyat...) ونربط API الخاص فيه بنفس أسلوب الإيميل. **بانتظار قرار المستخدم لأي مسار ياخذ.**

⚠️ **الفرق المهم بين هذا الجزء والأجزاء السابقة:** كل ملفات PHP تم بناؤها **واختبارها فعليًا end-to-end** بتشغيل حقيقي (PHP + MariaDB). تطبيق الأندرويد **تمت كتابته بعناية فقط** (تحقّق يدوي من تطابق كل الأسماء/المراجع بين الملفات، وصحة بنية XML) لكن **لم يُبنى (compile) فعليًا** لأن بيئة العمل بدون Android SDK. لازم تفتحه بـ Android Studio وتسوي Build حقيقي، وأي خطأ يطلع أرسله لي وأصلحه فورًا.

## ✅ تم إنجازه (Phase 1 - Backend + Dashboard) — تم اختباره فعليًا وشغال 100%

### قاعدة البيانات
- `database/schema.sql` — جداول: admins, packages, users (العملاء), messages_queue, api_logs
- بيانات أولية: أدمن افتراضي (admin / Admin@12345) + 3 باقات جاهزة

### PHP Backend APIs (مُختبرة end-to-end بنجاح)
- `api/client_send_api.php` — استقبال طلب إرسال من العميل (يتحقق من api_key، الاشتراك، الرصيد، ينقص الرصيد بأمان مع قفل صف SQL لمنع تعارض الطلبات المتزامنة)
- `api/get_pending_messages.php` — لتطبيق الأندرويد: يسحب الرسائل المعلّقة ويحوّلها لحالة processing فورًا لمنع سحبها مرتين، ويعيد أي رسالة عالقة processing لأكثر من 5 دقائق إلى pending تلقائيًا
- `api/update_status.php` — لتطبيق الأندرويد: يحدّث حالة الرسالة sent/failed

### لوحة التحكم (Dashboard) - Bootstrap 5 RTL
- تسجيل دخول/خروج للأدمن (`dashboard/login.php`, `logout.php`)
- الصفحة الرئيسية بإحصائيات حية (`dashboard/index.php`)
- إدارة الباقات: عرض/إضافة/تعديل/حذف (`dashboard/packages/`)
- إدارة العملاء: عرض/إضافة (توليد api_key تلقائي + تعبئة رصيد حسب الباقة)/تعديل (تغيير رصيد، باقة، تاريخ انتهاء، كلمة مرور)/حذف/تفعيل-تعطيل (`dashboard/clients/`)
- ملف تفصيلي لكل عميل مع إحصائيات وسجل رسائله (`dashboard/clients/view.php`)
- سجل رسائل كامل مع فلاتر (نوع/حالة/بحث) وترقيم صفحات (`dashboard/messages/index.php`)
- **صفحة الإعدادات** (`dashboard/settings/index.php`) — مُختبرة بالكامل:
  - تغيير اسم/اسم مستخدم/كلمة مرور الأدمن (يتطلب تأكيد كلمة المرور الحالية أولًا)
  - توليد Gateway Secret Key جديد بضغطة زر، أو إدخال مفتاح مخصص يدويًا
  - المفتاح يُخزّن في جدول `settings` بقاعدة البيانات (مع fallback لقيمة config.php لو الجدول غير موجود بعد)
  - جدول `settings` مُضاف في `database/schema.sql` للتنصيبات الجديدة، وفي ملف منفصل `database/migration_settings.sql` للتنصيبات القديمة
  - **ذاتية الإصلاح:** لو جدول settings غير موجود، الكود ينشئه تلقائيًا أول ما يُستخدم (`ensureSettingsTable()` في helpers.php) — ما يحتاج تشغيل migration يدوي أبدًا

### بوابة العملاء (Client Portal) — `client/` — مُختبرة بالكامل end-to-end
- تسجيل دخول بالإيميل وكلمة المرور المُنشأة من لوحة الأدمن (`client/login.php`, `logout.php`)
- لوحة معلومات (`client/index.php`): الباقة الحالية، رصيد SMS/Email، تاريخ انتهاء الاشتراك مع تنبيه لو باقي 5 أيام أو أقل أو منتهي فعلًا، عرض ونسخ الـ api_key، آخر 5 رسائل
- سجل رسائل كامل مع فلاتر (نوع/حالة) وترقيم صفحات (`client/messages.php`)
- صفحة "حسابي" (`client/profile.php`):
  - تعديل الاسم/الهاتف وتغيير كلمة المرور (يتطلب تأكيد كلمة المرور الحالية)
  - تجديد الـ api_key بنفسه (يتطلب تأكيد كلمة المرور) — المفتاح القديم يتوقف عن العمل فورًا
- حماية: الحسابات الموقوفة (status=0) ما تقدر تسجّل دخول، فيه رسالة واضحة لها
- كل الصفحات محمية بـ `requireClientLogin()` وتحويل تلقائي لصفحة الدخول لو مو مسجّل

### الأمان المطبّق
- كل الاستعلامات Prepared Statements (لا SQL Injection)
- كل المخرجات معالجة بـ htmlspecialchars (لا XSS)
- CSRF token في كل نموذج بلوحة التحكم
- Gateway Secret Key منفصل تمامًا عن api_key الخاص بالعملاء (`GATEWAY_SECRET_KEY` في config.php)
- كلمات المرور بـ password_hash (bcrypt)
- قفل صف SQL (FOR UPDATE) عند خصم الرصيد لمنع الاستغلال بطلبات متزامنة (race condition)

## ⏳ لم يتم بعد (المراحل القادمة)

### Phase 2 - تحسينات Backend/Dashboard (كل شي أساسي تم)
- [x] ~~صفحة تعديل بيانات الأدمن + تغيير كلمة مروره من اللوحة~~ ✅ تم
- [x] ~~صفحة إعدادات لتغيير GATEWAY_SECRET_KEY من الواجهة بدل الملف~~ ✅ تم
- [x] ~~تسجيل دخول للعملاء أنفسهم (Client Portal) لمتابعة رصيدهم وسجل رسائلهم وتوليد/تجديد api_key~~ ✅ تم

### Phase 3 - تطبيق الأندرويد (Gateway App) — `android_gateway_app/` — مكتوب بالكامل، غير مُختبر (يحتاج Build في Android Studio)
- [x] مشروع Android Studio (Kotlin) بالكامل — Gradle files, Manifest, كل الأصناف
- [x] Polling دوري لـ get_pending_messages.php عبر Coroutine loop داخل Foreground Service (فاصل الاستطلاع قابل للتعديل من الشاشة)
- [x] إرسال SMS عبر SmsManager (مع دعم الرسائل الطويلة multipart) + إذن SEND_SMS
- [x] إرسال Email عبر JavaMail/SMTP (إعدادات SMTP كاملة قابلة للتعديل من الشاشة)
- [x] استدعاء update_status.php بعد كل محاولة إرسال (sent/failed مع رسالة الخطأ)
- [x] شاشة إعدادات (MainActivity) — رابط السيرفر، Gateway Secret Key، فاصل الاستطلاع، إعدادات SMTP الكاملة، أزرار تشغيل/إيقاف وطلب الأذونات
- [x] Foreground Service + إشعار دائم يبيّن الحالة وآخر فحص وآخر خطأ
- [x] BootReceiver لإعادة تشغيل الخدمة تلقائيًا بعد إعادة تشغيل الجهاز (لو كانت شغالة)
- [ ] **يحتاج منك:** فتح المشروع في Android Studio، Sync Gradle، Build، تجربة فعلية على جهاز حقيقي — راجع `android_gateway_app/README.md` للخطوات الكاملة
- [ ] تحسين مستقبلي: تأكيد تسليم SMS حقيقي (Delivery Report) بدل الاعتماد على قبول SmsManager للطلب فقط (موضّح كـ "حدود معروفة" في README)

### Phase 4 - تحسينات مستقبلية اختيارية
- [x] ~~Rate limiting على client_send_api.php لمنع إساءة الاستخدام~~ ✅ تم واختُبر بالكامل:
  - حد قابل للتعديل لكل عميل بالدقيقة (افتراضي 60) — مُختبر: خفّضته لـ 3 وتأكدت إن الطلب الرابع يترفض بـ HTTP 429
  - حد منفصل لمحاولات api_key الخاطئة من نفس IP (افتراضي 20) لمنع تخمين المفاتيح — مُختبر: خفّضته لـ 2 وتأكدت من الرفض
  - كل محاولة (ناجحة أو فاشلة) تُسجَّل في جدول `api_logs` مع كود الاستجابة
  - فهارس أداء مركّبة مُضافة في `schema.sql` (وملف `migration_rate_limit_indexes.sql` اختياري للتنصيبات القديمة)
  - تحكم كامل بالحدود من **لوحة التحكم → الإعدادات** مع Validation (1-10000 / 1-1000)
- [x] ~~Export سجل الرسائل إلى Excel/CSV~~ ✅ تم واختُبر بالكامل:
  - تصدير للأدمن (`dashboard/messages/export.php`) — يحترم نفس فلاتر صفحة سجل الرسائل (نوع/حالة/بحث)، لكل العملاء
  - تصدير للعميل (`client/export.php`) — رسائله الخاصة فقط، مع فلاتر نوع/حالة
  - BOM مضاف حتى إكسل يفتح العربي صح
  - محمي بنفس نظام تسجيل الدخول (أدمن/عميل) — تأكدت فعليًا إن طلب غير مسجّل دخول يتحوّل لصفحة الدخول بدون أي تسريب بيانات
  - تأكدت من عزل البيانات: أنشأت عميلين، وتأكدت إن تصدير العميل الأول ما فيه أي أثر لرسائل العميل الثاني
- [x] ~~إشعار العميل بالإيميل قبل انتهاء اشتراكه بأيام~~ ✅ تم واختُبر بالكامل:
  - يستخدم نفس بنية `messages_queue` الموجودة (بدون نظام إرسال بريد منفصل بالسيرفر) — التنبيه يتحوّل لرسالة Email عادية يرسلها تطبيق الأندرويد، بدون خصم من رصيد العميل
  - عدد الأيام قبل الانتهاء قابل للتعديل من الإعدادات (افتراضي 3)
  - حماية من التكرار: عميل واحد ما يتنبّه أكثر من مرة باليوم — مُختبر فعليًا
  - مُختبر: عميل خلال المدة يتنبّه ✅، عميل بعيد ما يتنبّه ✅، عميل منتهي فعلًا ما يتنبّه ✅
  - سكربت `cron/send_expiry_notices.php` قابل للتشغيل من CLI (لجدولته عبر Task Scheduler بويندوز) أو عبر الويب بمفتاح سري `CRON_SECRET_KEY` — مُختبرة حمايته (403 بدون المفتاح الصحيح)
  - زر "شغّل الفحص الآن" بلوحة التحكم → الإعدادات للتجربة اليدوية
  - ذاتية الإصلاح: لو عمود `expiry_notice_sent_at` غير موجود (تنصيب قديم)، يُضاف تلقائيًا — مُختبر بحذف العمود يدويًا والتأكد من إضافته لحاله
### نظام الفوترة اليدوي (تحويل بنكي + موافقة الأدمن) — `client/upgrade.php`, `dashboard/purchases/`, `includes/billing.php` — مُختبر بالكامل end-to-end
**قرار تصميم مهم:** ما ربطت بوابة دفع خارجية حقيقية (Moyasar/PayTabs/Stripe) لأن هذا يحتاج بيانات تاجر فعلية (API keys، حساب تاجر مفعّل) ما أقدر أختبرها بنفسي. بدلها بنيت نظام تحويل بنكي يدوي بموافقة الأدمن - أسلوب شائع وواقعي جدًا للمشاريع الصغيرة والمتوسطة بالمنطقة. **البنية التحتية جاهزة لربط بوابة دفع حقيقية لاحقًا** — بدل ما الأدمن يوافق يدويًا، الموافقة تصير تلقائية من webhook البوابة (نفس دالة `approvePurchaseRequest()` تُستدعى تلقائيًا بدل الزر اليدوي).

- **العميل** (`client/upgrade.php`): يشوف بيانات التحويل البنكي (تُدار من الإعدادات)، يختار باقة، يدخل رقم عملية التحويل، ويشوف تاريخ كل طلباته السابقة وحالتها وسبب الرفض لو رُفض
- **الأدمن** (`dashboard/purchases/index.php`): يشوف الطلبات مفلترة بالحالة (قيد المراجعة/موافق عليها/مرفوضة/الكل)، يوافق أو يرفض مع ملاحظة اختيارية
- **عند الموافقة** (`approvePurchaseRequest()`): تلقائيًا يتفعّل للعميل package_id الجديد + يُضاف رصيد SMS/Email حسب حدود الباقة + يُمدَّد تاريخ الانتهاء
- **منطق تمديد ذكي:** لو اشتراك العميل لسه ساري، المدة الجديدة تُضاف فوق تاريخ انتهائه الحالي (ما يخسر الأيام المتبقية)؛ لو منتهي، تُحسب من تاريخ اليوم — مُختبر فعليًا بالحالتين
- **حماية من الطلبات المكررة:** عميل ما يقدر يقدّم طلب جديد وعنده طلب قيد المراجعة أصلًا — مُختبر
- **حماية من الموافقة/الرفض المزدوج:** لو حاول الأدمن يوافق مرتين على نفس الطلب (مثلًا نقرتين بالغلط)، الثانية تترفض تلقائيًا ولا يتكرر تطبيق الرصيد — مُختبر فعليًا والرصيد تأكدت إنه ما تضاعف
- **الرفض بدون أي أثر جانبي:** تأكدت إن رصيد العميل ما يتغيّر أبدًا عند الرفض، والعميل يقدر يقدّم طلب جديد بعدها ويشوف سبب الرفض بوضوح
- **بيانات التحويل البنكي** قابلة للتعديل من لوحة التحكم → الإعدادات (اسم البنك، آيبان، اسم المستفيد)
- **ذاتية الإصلاح:** لو جدول `purchase_requests` غير موجود (تنصيب قديم)، يُنشأ تلقائيًا — مُختبر بحذف الجدول يدويًا والتأكد من إعادة إنشائه لحاله

### إرسال الإيميل مباشرة من السيرفر (بديل الأندرويد - للإيميل فقط) — `cron/process_email_queue.php`, `includes/PHPMailer/` — مُختبر بالكامل end-to-end
**السياق:** المستخدم أخبرني إنه ما عنده أي جهاز أندرويد. الإيميل ما يحتاج جهاز فيزيائي أصلًا (بعكس SMS)، فحوّلت مساره ليُرسل مباشرة من السيرفر.

- استخدمت **PHPMailer الرسمي** (نزّلته مباشرة من GitHub raw، بدون Composer لأن الشبكة هنا ما توصل packagist.org)
- إعدادات SMTP كاملة قابلة للإدارة من لوحة التحكم → الإعدادات (Host, Port, Username, Password, From, TLS) — تُخزَّن بجدول settings
- **زر "أرسل تجريبي"** بنفس الصفحة يرسل إيميل فوري بدون طابور، للتأكد السريع إن الإعدادات صح قبل تفعيل أي شيء تلقائي
- **اختبرت فعليًا بمنهجية بديلة** (بما إني ما أملك حساب Gmail حقيقي لأختبره): شغّلت سيرفر SMTP وهمي محلي (Python aiosmtpd) بدل مزوّد حقيقي، وتأكدت:
  - الاتصال والمصادقة يشتغلوا صح عبر PHPMailer
  - المحتوى العربي يوصل كامل بدون أي تلف بالترميز (تحققت من محتوى الرسالة المستلمة فعليًا بالسيرفر الوهمي)
  - `cron/process_email_queue.php` يسحب الرسائل المعلّقة من `messages_queue` (**نفس الجدول اللي يستخدمه تطبيق الأندرويد** - يعني لو لاحقًا صار عندك جهاز أندرويد، الاثنين يقدروا يشتغلوا بالتوازي بدون تعارض) ويرسلها فعليًا ويحدّث حالتها لـ sent
  - معالجة الأخطاء: جرّبت host غلط عمدًا، ظهرت رسالة خطأ واضحة بدل ما يكسر الصفحة أو يعطي Fatal Error
- تعليمات كاملة لربط Gmail (تفعيل 2-Step Verification + App Password) موجودة بالتفصيل بـ README
- محمي بنفس آلية processing/pending الموجودة أصلًا (منع إرسال نفس الرسالة مرتين لو اشتغل السكربت بالتزامن)

**ملاحظة مهمة:** هذا يحل الإيميل بالكامل. **SMS لسه معلّق وبانتظار قرار المستخدم**: إما يحصل على جهاز أندرويد (حتى رخيص/مستعمل) لتشغيل `android_gateway_app`، أو يشترك بمزوّد SMS تجاري (Twilio, Unifonic, Msegat, Taqnyat) ونبني له تكامل مشابه تمامًا لللي سويناه بالإيميل (سكربت cron يرسل عبر API المزوّد بدل SMTP).

### سيرفر SMTP Gateway (بروتوكول SMTP بديل عن HTTP API) — `smtp-server/gateway_smtp_server.php`, `includes/smtp_auth.php` — مُختبر بالكامل ببروتوكول SMTP حقيقي
**السياق:** المستخدم عنده نظام خارجي منفصل (نظام شاشات Digital Signage) عنده خانات "SMTP Settings" عادية جاهزة، ويبي يستخدم منصتنا لإرسال إيميلاته **بدون ما يعدّل كود ذاك النظام إطلاقًا**. الحل: نخلي منصتنا نفسها تتصرف كسيرفر SMTP حقيقي، فأي نظام يقدر يحط بياناتنا بخانات SMTP بتاعته العادية ويشتغل فورًا.

- **بروتوكول SMTP كامل مكتوب من الصفر** (EHLO, AUTH LOGIN, AUTH PLAIN, MAIL FROM, RCPT TO, DATA, RSET, NOOP, QUIT) - سكربت PHP مستقل (Standalone) يفتح socket ويستمع باستمرار، مو صفحة ويب عادية
- **بيانات الدخول:** Username = بريد العميل، Password = نفس api_key الخاص فيه (بالضبط نفس أسلوب Mailgun/SendGrid SMTP relay الحقيقي)
- **نفس قواعد العمل بالضبط** المطبّقة على HTTP API (فحص الاشتراك، خصم الرصيد بأمان مع قفل صف SQL، تسجيل بـ api_logs) - العميل يقدر يستخدم أي طريقة (API أو SMTP) بنفس القواعد الموحّدة
- **اختبرت فعليًا ببروتوكول SMTP حقيقي** (مو محاكاة) باستخدام مكتبة `smtplib` القياسية في Python (تُحاكي بالضبط أي نظام خارجي حقيقي مثل PHPMailer):
  - اتصال + AUTH PLAIN + MAIL FROM + RCPT TO + DATA بمحتوى عربي → نجح والرسالة انحفظت بالطابور
  - **اكتشفت وأصلحت باگ حقيقي أثناء الاختبار:** محتوى عربي مُرسل من مكتبات بريد قياسية بييجي مُشفّر بصيغة MIME/Base64 (RFC 2047 للعنوان، Content-Transfer-Encoding للمتن) - أضفت فك التشفير الصحيح (`iconv_mime_decode` للعنوان، `base64_decode`/`quoted_printable_decode` للمتن حسب الهيدر المُعلن) وأعدت الاختبار وتأكدت من وصول النص العربي سليم 100% بقاعدة البيانات
  - كلمة مرور خاطئة → رُفضت بكود 535 صحيح
  - أمر MAIL FROM بدون مصادقة → رُفض بكود 530 صحيح (اختبرت بقراءة بروتوكول متعدد الأسطر صح بعد ما اكتشفت خطأ بأسلوب اختباري بدائي عندي أول مرة، مو خلل بالسيرفر)
  - رصيد email_credits = 0 → رُفض بكود 452 "Insufficient credits"
  - اشتراك منتهي → رُفض بكود 452 "Subscription expired"
- **واجهة واضحة:** بيانات الاتصال (Host/Port/Username/Password) تظهر جاهزة للنسخ في صفحة كل عميل بلوحة التحكم، وفي بوابة العميل نفسه
- رقم المنفذ (افتراضي 2525) قابل للتعديل من الإعدادات
- تعليمات تشغيل دائم بويندوز عبر NSSM موجودة بالتفصيل بـ README
- **حدود معروفة:** بدون TLS/SSL حاليًا (مناسب للشبكة المحلية فقط)، ويعالج اتصال واحد بالتزامن (كافي جدًا لحجم استخدام داخلي/متوسط، مو مصمم لآلاف الاتصالات المتزامنة)

### الإرسال الفوري المتزامن (Synchronous Instant Send) — `includes/mailer.php` — **أهم تعديل معماري بالمشروع**، مُختبر بتوقيت دقيق end-to-end
**السياق:** المستخدم واجه مشكلة تضارب/تأخير حقيقية أثناء استخدام نظام الشاشات: كان الإرسال يعتمد على `cron/process_email_queue.php` يشتغل كل دقيقة-دقيقتين، وده يسبب تأخير وأحيانًا تضارب لو اشتغلت نسختين متزامنتين من الـ Scheduler على نفس رسائل pending (مشكلة معروفة اسمها Race Condition). طلب المستخدم صراحة: إرسال فوري لحظي بديل كامل عن الاعتماد على أي جدولة.

**الحل المعماري:**
- أضفت دالة موحّدة `sendQueuedEmailNow(int $messageId)` بملف `includes/mailer.php` تحاول إرسال رسالة واحدة فورًا عبر PHPMailer وتحدّث حالتها مباشرة (sent/failed)
- **كل نقاط الدخول تستدعيها فورًا بمجرد ما تتأكد الاشتراك والرصيد وقبل ما ترد على الطالب:**
  - `api/client_send_api.php` — يرسل فورًا بعد queuing، ويرجّع `status: sent` أو `status: failed` بنفس رد الـ API (مو "queued" بس زي قبل)
  - `smtp-server/gateway_smtp_server.php` — نفس الشيء، يرسل فورًا قبل ما يرد 250 OK على العميل المتصل عبر SMTP
- `cron/process_email_queue.php` **تحوّل دوره بالكامل**: من "الطريقة الأساسية للإرسال" إلى **"شبكة أمان احتياطية"** فقط — يمسك أي رسالة علقت processing لأكثر من 5 دقائق (بسبب عطل طارئ) ويعيد محاولتها بنفس الدالة الموحّدة `sendQueuedEmailNow()`. الوضع الطبيعي إنه يلقى 0 رسالة تقريبًا دائمًا لأن الإرسال الفوري بيكون خلص شغله
- هذا **يلغي مشكلة تضارب الـ Schedulers المتزامنة نهائيًا** لأنه ببساطة ما فيه أي Scheduler يتنافس مع نفسه على "مين ياخذ الرسائل pending" - كل طلب يرسل رسالته هو بنفسه

**اختبرت فعليًا بتوقيت دقيق:**
- طلب واحد عبر HTTP API: **0.057 ثانية** من الطلب لحد `status: sent` بالرد - بدون أي cron شغال إطلاقًا
- طلب واحد عبر بروتوكول SMTP (اتصال + مصادقة + إرسال + تأكيد): **0.1 ثانية** بالضبط
- **5 طلبات متزامنة بنفس اللحظة** (`curl` بالخلفية + `wait`): كل الخمسة انرسلوا بنجاح `sent`، ولا رسالة واحدة علقت بـ processing، والرصيد اتخصم بالتسلسل الصحيح لكل واحدة بدون أي تعارض أو تكرار - أثبت إن المشكلة الأصلية (تضارب متزامن) انحلت نهائيًا
- معالجة الأخطاء: جرّبت السيناريو اللي فيه سيرفر SMTP الوهمي متوقف (محاكاة Gmail غير متاح) - الفشل انسجل بوضوح بعمود `error_message` بدل ما يختفي بصمت، والرسالة فضلت بحالة `failed` جاهزة لإعادة المحاولة من شبكة الأمان

**ملاحظة تصميم مهمة:** بما إن الإرسال بقى متزامن (Synchronous) داخل نفس الطلب، وقت استجابة الـ API/SMTP بقى مرتبط بسرعة اتصال SMTP الخارجي (Gmail أو غيره) - وضعت `Timeout = 10` ثواني بـ PHPMailer عشان لو السيرفر الخارجي تعلّق، الطلب ما يعلّق للأبد. لحجم استخدام متوسط (عشرات-مئات الرسائل بالدقيقة) هذا التصميم ممتاز؛ لو لاحقًا احتاج المستخدم نطاق أضخم جدًا (آلاف متزامنة)، الحل التالي يكون Queue حقيقي بمعالج خلفي متعدد العمليات (زي Redis/RabbitMQ) بدل الإرسال المباشر داخل الطلب - نقطة تحسين مستقبلية لو الحجم كبر كثير.

## 🔑 معلومات مهمة يجب تذكرها
- بيانات دخول الأدمن الافتراضية: `admin` / `Admin@12345` — **غيّرها فورًا بعد أول تسجيل دخول**
- `GATEWAY_SECRET_KEY` الحالي في config.php هو قيمة placeholder — **لازم تُستبدل بمفتاح عشوائي طويل** قبل أي استخدام حقيقي (أو الأفضل: ولّده من لوحة التحكم → الإعدادات)
- تم اختبار كامل الـ workflow (تسجيل دخول → إنشاء عميل → إرسال SMS/Email عبر API → سحبها → تحديث الحالة) على بيئة PHP 8.3 + MariaDB محليًا ونجح 100%
- **تطبيق الأندرويد يحتاج Build حقيقي في Android Studio قبل الاستخدام** — راجع `android_gateway_app/README.md`
- **المستخدم ما عنده جهاز أندرويد.** الإيميل يشتغل بدونه تمامًا (SMTP مباشر من السيرفر). SMS لسه يحتاج قرار: جهاز أندرويد أو مزوّد SMS تجاري
- لتفعيل إرسال الإيميل الفعلي: عبّي إعدادات SMTP بلوحة التحكم → الإعدادات، جرّب بزر "أرسل تجريبي"، ثم شغّل `cron/process_email_queue.php` (يدويًا أو مجدول)

## 🎯 المتبقي (بالترتيب المقترح)
1. **الأهم الآن:** تجرّب تطبيق الأندرويد فعليًا في Android Studio وترسل لي أي خطأ Build يطلع
2. **الإيميل مكتمل بالكامل.** المتبقي: **قرار SMS** — جهاز أندرويد أو مزوّد SMS تجاري (Twilio/Unifonic/Msegat/Taqnyat). بمجرد ما تقرر، أبني التكامل وأختبره فورًا
3. اختياري مستقبلي: ربط بوابة دفع حقيقية (Moyasar/PayTabs/Stripe) بدل التحويل البنكي اليدوي

## بنية المشروع الحالية
```
sms_gateway_saas/                  (المشروع الرئيسي - PHP، مُختبر بالكامل)
├── database/schema.sql, migration_settings.sql, migration_rate_limit_indexes.sql, migration_expiry_notice_column.sql, migration_purchase_requests.sql
├── config/config.php, Database.php
├── includes/helpers.php, notifications.php, billing.php, smtp_auth.php, mailer.php, PHPMailer/ (Exception.php, PHPMailer.php, SMTP.php)
├── cron/send_expiry_notices.php, process_email_queue.php
├── smtp-server/gateway_smtp_server.php
├── api/client_send_api.php, get_pending_messages.php, update_status.php
├── dashboard/                     (لوحة تحكم الأدمن)
│   ├── login.php, logout.php, index.php, _header.php, _footer.php
│   ├── packages/index.php, add.php, edit.php
│   ├── clients/index.php, add.php, edit.php, view.php
│   ├── messages/index.php, export.php
│   ├── purchases/index.php
│   └── settings/index.php
├── client/                        (بوابة العملاء)
│   ├── login.php, logout.php, index.php, _header.php, _footer.php
│   ├── messages.php, export.php
│   ├── upgrade.php
│   └── profile.php
├── android_gateway_app/           (تطبيق الأندرويد - مكتوب، يحتاج Build)
│   ├── README.md                  (خطوات التشغيل الكاملة)
│   ├── build.gradle, settings.gradle, gradle.properties
│   └── app/
│       ├── build.gradle
│       └── src/main/
│           ├── AndroidManifest.xml
│           ├── java/com/smsgateway/app/
│           │   ├── MainActivity.kt, GatewayService.kt
│           │   ├── network/ApiClient.kt, PendingMessage.kt
│           │   ├── sender/SmsSender.kt, EmailSender.kt
│           │   ├── util/Prefs.kt, NotificationHelper.kt
│           │   └── receiver/BootReceiver.kt
│           └── res/ (layout, values, drawable)
├── PROGRESS.md
└── README.md
```
