Nuzul Umrah
Multi-tenant SaaS · Web + Mobile · Solo-built · Arabic-first
Nuzul Umrah — Operations Platform for Umrah Companies
A subscription platform that runs an Umrah company end to end. Staff manage packages, departures and seats, bookings and payments, leads, trips, room assignments, tasks and documents from one Arabic-first dashboard. Pilgrims get a single companion app that follows them through the whole journey — a day-by-day programme, their miqat, their room and group, announcements and support. Nuzul is a technology provider only: pilgrims pay the company's own merchant account, and Nuzul never holds customer funds.
Architecture sketch

Pilgrim — home

Pilgrim — my trip

Pilgrim — day by day

Staff — trips in the field
$ ./vendor/bin/pest
✓ 865 passed
$ flutter test
✓ 58 passed
app/Services/Bookings/BookSeats.php// Serialize concurrent bookings of the SAME departure and re-read
// the row so seatsRemaining can't act on a stale instance.
$fresh = PackageDeparture::allCompanies()->lockForUpdate()->findOrFail($departure->id);
// Computed on the LOCKED row — the whole point of the lock.
if ($fresh->seatsRemaining() < $seats) {
throw new RuntimeException('Not enough seats remaining on this departure.');
}
// Halalas snapshot: later price changes never touch old bookings.
$booking->unit_price = $unitPrice;
$booking->total_price = $seats * $unitPrice;نُزُل عمرة
SaaS متعدد الشركات · ويب + جوال · بناء فردي كامل · عربي أولًا
نُزُل عمرة — منصة تشغيل شركات العمرة
منصة باشتراك شهري تدير شركة العمرة من البداية إلى النهاية. يدير الموظفون الباقات والمواعيد والمقاعد، والحجوزات والمدفوعات، والعملاء المحتملين، والرحلات والتسكين، والمهام والمستندات — من لوحة تحكم واحدة عربية أولًا. ويحصل المعتمر على تطبيق واحد عام يرافقه طوال رحلته: برنامج يومًا بيوم، وميقاته، وغرفته ومجموعته، والإعلانات والدعم. نُزُل مزوّد تقني فقط: يدفع المعتمر لحساب الشركة مباشرة، ولا تمسك المنصة أموال العملاء أبدًا.
مخطط البنية

المعتمر — الرئيسية

المعتمر — رحلتي

المعتمر — يومًا بيوم

الموظف — الرحلات في الميدان
$ ./vendor/bin/pest
✓ 865 passed
$ flutter test
✓ 58 passed
app/Services/Bookings/BookSeats.php// Serialize concurrent bookings of the SAME departure and re-read
// the row so seatsRemaining can't act on a stale instance.
$fresh = PackageDeparture::allCompanies()->lockForUpdate()->findOrFail($departure->id);
// Computed on the LOCKED row — the whole point of the lock.
if ($fresh->seatsRemaining() < $seats) {
throw new RuntimeException('Not enough seats remaining on this departure.');
}
// Halalas snapshot: later price changes never touch old bookings.
$booking->unit_price = $unitPrice;
$booking->total_price = $seats * $unitPrice;