Check-In: تسجيل وصول ذاتي للضيوف، مرتبط بـ Oracle OPERA
الـ backend بتاع تطبيق لضيوف الفنادق (كان اسمه Hayyak في الأول، وبعدين بقى Check-In): الضيف بيربط حجزه اللي على OPERA بالتطبيق، ويضيف ضيوف مرافقين، ويسجّل وصوله (check-in)، ويمدّد إقامته، ويسجّل مغادرته (check-out) مع الدفع من جوه التطبيق، والنظام بيدعم أكتر من فندق. قُدت شغل الـ backend، وكنت المساهم الأساسي فيه وصاحب الـ repository.
- مؤسسة ← مجموعة ← فندق
- 3 مستويات
- بوابات دفع، بواجهة واحدة
- 2
- المرجع الأساسي للبيانات (OHIP)
- OPERA Cloud
- بلغتين
- AR / EN
المشكلة
إيه المشكلة اللي كنا بنحلها؟
الفنادق المستهدفة في السعودية شغالة على Oracle OPERA، ولسه الوصول للفندق معناه طابور عند الاستقبال عشان التحقق من الهوية وتخصيص الغرفة والدفع. هدف المنتج كان إن الضيف يقدر يعمل ده من موبايله.
وكان لازم OPERA يفضل هو المرجع الأساسي للبيانات (system of record): أي حاجة الضيف يعملها في التطبيق — تخصيص الغرفة، تسجيل الوصول (check-in)، تسجيل رسوم على الـ folio، تسجيل المغادرة (check-out) — لازم توصل OPERA بالضبط زي ما كانت هتوصل لو موظف الاستقبال هو اللي عملها. وكمان المنصة كان لازم تخدم كذا فندق، وكل فندق ليه الـ credentials بتاعته على OPERA، وبوابة الدفع وسياسات الإقامة الخاصة بيه.
القيود
إيه القيود اللي أثّرت على التصميم؟
- OPERA Cloud هو المرجع؛ لازم حالة الحجز المعروضة في التطبيق تتوافق معاه.
- إعدادات منفصلة لكل فندق: الـ credentials بتاعة OHIP، وبوابة الدفع، وفترات ورسوم الوصول المبكر (early check-in) / المغادرة المتأخرة (late check-out).
- بيانات الكارت ممنوع توصل للـ backend خالص — اللي بيوصل هو الـ tokens بتاعة بوابة الدفع بس.
- الضيف لازم يخلّص التحقق من هويته قبل ما ينفع يتضاف على حجز.
- عربي وإنجليزي في المنصة كلها، ودعم لنظام الإبلاغ عن الإقامة في السعودية (Shomoos).
الـ architecture
النظام متصمم إزاي؟
Django REST API، معاه Daphne للـ WebSockets، و Celery للـ webhooks والإشعارات، و Redis، و PostgreSQL. الـ backend بيكلّم OPERA بشكل synchronous عن طريق OHIP، عشان الضيف يشوف رد OPERA الحقيقي؛ والمدفوعات بتعدّي على طبقة بوابات دفع واحدة وراها اتنين providers.
الـ clients
تطبيق الضيف (Flutter)
قراءة جواز السفر/الهوية على الجهاز نفسه (الـ MRZ وشريحة الـ NFC) ومطابقة الوجه؛ والـ SDKs بتاعة بوابات الدفع هي اللي بتاخد بيانات الكارت.
لوحة موظفي الفندق
إعدادات المؤسسة والمجموعة والفندق، والغرف والاختيارات.
Django admin
التشغيل والدعم.
الـ Backend
Django REST API
سجلات هوية الضيوف، والفنادق وأدوار الموظفين، والحجوزات اللي مصدرها OPERA، والمدفوعات، والإشعارات.
Daphne + Channels
WebSockets عليها authentication بـ Knox، لشات الدعم بين الضيف والموظفين.
Celery + beat
معالجة الـ webhooks، وتوزيع إشعارات الـ push والإيميلات، وإعادة التحقق من المدفوعات اللي واقفة في حالة in progress.
PostgreSQL · Redis · Cloud Storage
schema واحدة مشتركة لكل الفنادق؛ الـ broker والـ cache والـ channel layer؛ الميديا والمستندات.
الأنظمة الخارجية
Oracle OPERA Cloud (OHIP)
OAuth2 لكل فندق لوحده؛ والـ APIs بتاعة الحجوزات والـ front office والـ cashiering.
Tap Payments · HyperPay
البوابة بتتحدد لكل فندق؛ webhooks عليها توقيع أو متشفّرة.
Firebase Cloud Messaging
إشعارات push للضيوف.
adapter لـ Shomoos (FastAPI)
sidecar بيغلّف الـ API السعودي بتاع الإبلاغ عن الإقامة.
القرارات الهندسية
اخترنا إيه، وليه؟
كل الفنادق في schema واحدة (multi-tenancy)
مؤسسة ← مجموعة فنادق ← فندق، مع أدوار للموظفين (super user، مشرف، مدير مجموعة، مدير فرع)، ولكل فندق rows فيها الـ credentials بتاعة OHIP وبوابة الدفع وسياسات الإقامة. العزل بين الفنادق متطبّق في الـ querysets والـ permission classes؛ والضيوف وموظفي الفنادق ليهم user models منفصلة، وكل نوع فيهم ليه جدول tokens منفصل.
OPERA الأول: تحديث منه قبل كل خطوة، وكل تغيير يتبعت له
قبل ما الضيف يسجّل وصوله (check-in) أو يمدّد إقامته أو يسجّل مغادرته (check-out)، الـ backend بيحدّث الحجز من OHIP، وبعدين بيبعت الإجراء نفسه — تخصيص الغرفة، تسجيل الوصول، تسجيل المبالغ على الـ folio، تسجيل المغادرة — لـ OPERA في نفس الـ request، عشان التطبيق يعرض الحالة الفعلية اللي على OPERA، مش نسخة محلية.
تسجيل المغادرة كعملية تسوية منسّقة
تسجيل المغادرة بيقرأ الـ folio windows من OPERA، ويحدد حالة كل window: عليها مبلغ مستحق، أو فيها مبلغ مدفوع زيادة، أو رصيدها متسوّي، ويحصّل أي رصيد متبقي عن طريق بوابة الدفع بتاعة الفندق، ويسجّل الدفعة على كل window، ويقفل الـ folios، وبعد كده بس يسجّل مغادرة الضيف — مع تحويل خطأ OPERA بتاع الرصيد اللي لسه متسوّاش لـ domain error واضح.
بوابتين للدفع بواجهة برمجية واحدة
Tap و HyperPay عبارة عن strategies ورا طبقة بوابات دفع مشتركة، والبوابة بتتحدد لكل فندق. الـ transactions بتمشي في states صريحة، ومعاها audit log. الـ webhooks بيتعملها verification (الـ HMAC في Tap، والـ AES-GCM في HyperPay)، وبيتعامل معاها على إنها إشارة لإعادة جلب الـ transaction من البوابة تحت row lock، وعمرها ما بتتاخد على إنها الـ source of truth. بيانات الكارت بتتكتب في الـ SDKs بتاعة البوابات؛ والـ backend بيخزّن الـ tokens ونوع الكارت (brand) وآخر أربع أرقام بس.
Shomoos ورا adapter خاص بيه
service صغيرة بـ FastAPI بتغلّف الـ API الحكومي بتاع الإقامة للضيوف والزوار والاستعلامات، وبتوحّد شكل الأخطاء (faults) بتاعه في response envelope واحد، ومعاها documentation بـ Swagger، عشان تطبيق Django ما يحتاجش يتعامل مع بروتوكول النظام الخارجي بشكل مباشر.
المفاضلات
كسبنا إيه، وإيه اللي اتنازلنا عنه؟
استدعاء OHIP بشكل synchronous
بسيط وفوري ومتّسق مع OPERA، بس لو رد OPERA بطيء، الـ request بتاع الضيف بيبطّأ معاه؛ ولسه مفيش طبقة retry شغالة بـ queue.
schema واحدة مشتركة لكل الفنادق
قاعدة بيانات واحدة بس اللي محتاجة تشغيل؛ والعزل معتمد على إن كل queryset وكل permission check يكونوا صح.
بوابة دفع لكل فندق، من غير failover أوتوماتيك
الـ reconciliation واضح لكل فندق لوحده؛ بس لو الـ provider وقع، مدفوعات الفندق ده بتتأثر لحد ما بوابته تتغيّر.
إعادة الجلب مع كل webhook
call زيادة للبوابة مع كل event، مقابل إن محتوى الـ webhook عمره ما بيتاخد على إنه موثوق.
التحقق من الهوية على الجهاز نفسه
سريع للضيوف؛ السيرفر بيخزّن المستندات والنتايج، بس مبيتحققش منهم تاني.
النتايج
وصلنا لإيه؟
إقامة الضيف من أولها لآخرها
ربط الحجز بالتطبيق، وإضافة ضيوف مرافقين مسجّلين أو مش مسجّلين، وتسجيل الوصول (check-in)، والتمديد، والمغادرة المتأخرة (late check-out)، وتسجيل المغادرة مع الدفع — و OPERA بيتحدّث في كل خطوة.
إعدادات على مستوى الفندق
كل فندق ليه الـ credentials بتاعته على OPERA، وبوابة الدفع بتاعته، وقواعد الوصول المبكر / المغادرة المتأخرة بمقابل.
شات الدعم
شات لحظي (real-time) بين الضيف والموظفين، على WebSockets عليها authentication.
deployments آمنة
services شغالة في containers (Gunicorn، Daphne، Celery، Nginx)، والـ deployment بيتم بـ GitHub Actions، مع backup قبل الـ deploy و rollback أوتوماتيك.
دوري
- المساهم الأساسي في الـ backend وصاحب الـ repository (أكتر من نص الـ commits بتاعة الـ backend بشوية).
- صممت الـ data model اللي بيخدم أكتر من فندق، وأدوار الموظفين.
- بنيت الـ client بتاع OHIP، والـ orchestration بتاع تسجيل الوصول (check-in) وتسجيل المغادرة (check-out)، ومنها تسوية الـ folio.
- بنيت شات الدعم على Django Channels، وجهّزت الـ deployment في containers والـ CI/CD.
- صممت طبقة المدفوعات بالمشاركة مع زميل في الفريق، وهو اللي كتب أغلب كود البوابات والإشعارات.
- شاركت في تطوير الـ adapter بتاع Shomoos؛ أما تطبيق الضيف بـ Flutter ولوحة الموظفين التانية فبناهم زمايلي في الفريق.
حدود الشغل
- مفيش هنا أرقام عن استخدام المنتج أو تحسّن الكفاءة؛ المؤشرات دي ما اتجهّزش ليها قياس (instrumentation).
- الإبلاغ الأوتوماتيك لـ Shomoos عن الضيوف اللي سجّلوا وصولهم اتبنى على branch لسه ما اتعملوش merge.
- دراسة الحالة دي بتغطي الـ backend المبني بـ Django/DRF وعمليات الربط اللي في الـ repositories بتاعتي.
دراسة الحالة اللي بعدها
MURI
منصة لنقل الطلاب في السعودية شغالة بالاشتراكات: تطبيق للطالب وتطبيق للسواق، ولوحة للأدمن، و backend بـ Django/PostGIS. كقائد للفريق، جهّزت الـ backend والـ delivery pipeline بتاعه، وبنيت الأجزاء الخاصة بالجغرافيا والتحقق من الرحلات؛ أما أغلب مميزات المنصة اللي اتوصفت هنا فبناها زمايلي في الفريق.
اقرأها