أ
منصة أبجورة · Phase 1

تقرير فحص الأخطاء البرمجية

فحص شامل عبر الـ repositories الثلاثة: abjora-backend · abajora-website · abajora-dashboard — التركيز على: الـ Checkout، بوابة الدفع Tap، إدارة المخزون، عقود الـ API، و Salasa Integration.

📅 تاريخ الإنشاء: ٣ يونيو ٢٠٢٦ 🔍 النطاق: Checkout · Payments · Stock · Salasa · API Contracts
١٧
إجمالي الأخطاء
٤
حرج (Critical)
٧
عالي (High)
٦
متوسط (Medium)

دليل الألوان والرموز

⚙️ Backend فقط 🖥️ Frontend فقط 🔗 Backend + Frontend | حرج عالي متوسط
فلترة: |
🔴 حرج ⚙️ Backend فقط abjora-backend BUG-001

الويب هوك بتاع الإلغاء بيعمل bypass للـ Observer — المخزون المحجوز بيتسرب نهائياً

⚙️ Backend ProcessTapWebhookJob.php → handleCancelled() سطر ١٥٠-١٥٨

الـ handleCancelled() بيستخدم Order::where()->update() (query builder) بدل $order->update() (Eloquent model). ده بيعمل bypass للـ OrderObserver::updated() اللي هو المسؤول الوحيد عن استدعاء ReleaseOrderReservationAction اللي بتحرر الـ quantity_reserved.

💥 التأثير

خسارة مالية وتآكل تدريجي للمخزون. كل طلب بيتلغى من Tap بيسيب الـ stock محجوز للأبد. مع الوقت، المنتجات بتظهر "نفدت" رغم إنها موجودة في المخزن فعلاً. المشكلة دي بتتراكم وبتكبر مع كل عملية دفع فاشلة.

عرض الكود والحل
// File: app/Jobs/Tap/ProcessTapWebhookJob.php
// استبدل handleCancelled() (سطر ١٥٠-١٥٨)

-    private function handleCancelled(Order $order, TapWebhookDTO $dto): void
-    {
-        Order::where('id', $order->id)
-            ->where('status', OrderStatusEnum::Pending)
-            ->update([
-                'status'            => OrderStatusEnum::Cancelled,
-                'tap_charge_status' => 'CANCELLED',
-            ]);
-    }

+    private function handleCancelled(Order $order, TapWebhookDTO $dto): void
+    {
+        // استخدم Eloquent model ->update() عشان الـ Observer يشتغل
+        // والـ ReleaseOrderReservationAction تحرر المخزون.
+        $order->refresh();
+
+        if ($order->status !== OrderStatusEnum::Pending) {
+            return;
+        }
+
+        $order->update([
+            'status'            => OrderStatusEnum::Cancelled,
+            'tap_charge_status' => 'CANCELLED',
+        ]);
+    }
🔴 حرج ⚙️ Backend فقط abjora-backend BUG-002

الويب هوك بتاع الدفع الفاشل مش بيحرر المخزون — نفس مشكلة الـ Observer Bypass

⚙️ Backend ProcessTapWebhookJob.php → handleFailed() سطر ١٤١-١٤٨

الـ handleFailed() بيعمل update للـ tap_charge_status بس بيسيب الـ order status Pending. يعني الـ stock فاضل محجوز ومحدش بيحرره. الـ CancelUnpaidOrderJob بيشيل الطلبات القديمة بعد ٣٠ دقيقة بس، يعني في الفترة دي المخزون مقفول.

💥 التأثير

المخزون محتجز ٣٠+ دقيقة بعد كل دفع فاشل. في أوقات الذروة والعروض، العميل اللي كارته اترفض بيقفل المنتجات لمدة نص ساعة، وده بيمنع باقي العملاء من الشراء.

عرض الكود والحل
// File: app/Jobs/Tap/ProcessTapWebhookJob.php
// استبدل handleFailed() (سطر ١٤١-١٤٨)

-    private function handleFailed(Order $order, TapWebhookDTO $dto): void
-    {
-        Order::where('id', $order->id)
-            ->where('status', OrderStatusEnum::Pending)
-            ->update(['tap_charge_status' => 'FAILED']);
-
-        Log::warning('ProcessTapWebhookJob: payment failed', ['order_id' => $order->id]);
-    }

+    private function handleFailed(Order $order, TapWebhookDTO $dto): void
+    {
+        $order->refresh();
+
+        if ($order->status !== OrderStatusEnum::Pending) {
+            return;
+        }
+
+        // ألغي الطلب فوراً عشان الـ stock يتحرر عن طريق الـ Observer
+        $order->update([
+            'status'            => OrderStatusEnum::Cancelled,
+            'tap_charge_status' => 'FAILED',
+        ]);
+
+        Log::warning('ProcessTapWebhookJob: payment failed — order cancelled', [
+            'order_id' => $order->id,
+        ]);
+    }
ملاحظة: لو عايز تسمح للعميل يعيد محاولة الدفع على نفس الطلب، خلي الطلب Pending بس حرر الـ stock مباشرةً عن طريق ReleaseOrderReservationAction. عملية إعادة الدفع هتعمل reserve جديد.
🔴 حرج ⚙️ Backend فقط abjora-backend BUG-003

الـ TapCallbackController بيستخدم متغيرات مش معرفة في الـ catch block — بيعمل crash

⚙️ Backend TapCallbackController.php → __invoke() سطر ٥٥

في الـ catch (TapException) block، الكود بيستخدم $payment_method و $amount و $currency — بس دول بيتعرفوا جوا الـ try block بس. لو الـ $tap->getCharge() عمل throw، المتغيرات دي مش موجودة. PHP 8 بيعمل warning وبيبعت null للـ redirect.

💥 التأثير

الـ redirect بتاع العميل بيبوظ لما Tap API بتقع. العميل بيتوجه لصفحة الفشل بـ URL فيها قيم null زي: ?payment_method=&amount=¤cy=. والـ error logs بتتملي بـ undefined variable warnings.

عرض الكود والحل
// File: app/Http/Controllers/Payment/TapCallbackController.php
// عرف المتغيرات قبل الـ try block (سطر ٤١-٥٦)

+        $payment_method = null;
+        $amount         = null;
+        $currency       = null;
+
         try {
             $charge         = $tap->getCharge($tapId);
             $success        = ($charge['status'] ?? '') === 'CAPTURED';
             $payment_method = data_get($charge, 'card.brand', null);
             $amount         = data_get($charge, 'amount', null);
             $currency       = data_get($charge, 'currency', null);
         } catch (TapException $e) {
             // دلوقتي المتغيرات معرفة — الـ redirect هيشتغل صح
             return $this->toFailure($order, $payment_method, $amount, $currency);
         }
🔴 حرج 🔗 Backend + Frontend Cross-Repo BUG-004

الموقع مش بيبعت country_id — الشحن دايماً بيتحسب ٠.٠٠

🖥️ Frontend checkout.service.ts → mapAddress() سطر ١٠٧-١٢٩

الـ CheckoutAddress interface مفيهاش field اسمها country_id — فيها country بس كـ string (زي "Saudi Arabia"). الـ mapAddress() مش بتبعت الـ ID خالص.

⚙️ Backend CheckoutService.php → placeOrder() سطر ٩٣

الباك اند بيعمل fallback للـ country_id بـ ?? 0. ومفيش shipping zone عندها country بـ ID = 0. فالـ resolveZone() بترجع null والشحن بيبقى ٠.٠٠ دايماً.

💥 التأثير

مفيش إيرادات شحن خالص على أي طلب. كل تكاليف الشحن بيتم تجاهلها بالكامل. مناطق وأسعار الشحن اللي الـ admin عملها كلها bypassed. ده خسارة مالية مباشرة على كل طلب.

عرض الكود والحل
// الحل — جزء الـ Frontend:
// File: abajora-website/app/services/checkout.service.ts

// ١. ضيف country_id في الـ CheckoutAddress interface:
+   country_id: number    // ← ضيف الـ field ده

// ٢. في mapAddress() ابعت الـ country_id:
+     country_id: formAddr.countryId,

// ─────────────────────────────────────────────────
// الحل — جزء الـ Backend:
// File: app/Http/Requests/Checkout/PlaceOrderRequest.php

// ضيف validation rule:
+ 'shipping_address.country_id' => ['required', 'integer', 'exists:countries,id'],

// File: app/Services/Checkout/CheckoutService.php سطر ٩٣
- countryId: (int) ($data['shipping_address']['country_id'] ?? 0),
+ countryId: (int) $data['shipping_address']['country_id'],
🟠 عالي ⚙️ Backend فقط abjora-backend BUG-005

N+1 Query في resolveCartWeight() — كويريز مش محدودة وقت الـ Checkout

⚙️ Backend CheckoutService.php → resolveCartWeight() سطر ٣٤٣-٣٥٦

الفانكشن بتعمل Product::find() لكل منتج في السلة لوحده. وبعدين buildItems() بتعمل نفس الكلام تاني. يعني لو في السلة ١٠ منتجات = ٢٠ كويري بدل واحدة. وكمان resolveCartWeight() بتشتغل برا الـ lock scope.

⚠️ التأثير

ضغط مضاعف على الداتابيز وقت الـ Checkout. في أوقات الذروة والفلاش سيلز، ده بيزود الـ contention على جدول المنتجات في أهم flow عند العملاء.

عرض الكود والحل
// الحل: احذف resolveCartWeight() واستخدم بيانات buildItems()
// في placeOrder()، حرك الشحن بعد buildItems():

+ $orderItems = $this->buildItems($data['items']);
+ $subtotal = $orderItems->sum('line_total');
+
+ $shipping = $this->shippingCalculator->calculate(
+     countryId:     (int) $data['shipping_address']['country_id'],
+     subtotal:      $subtotal,
+     totalWeightKg: $orderItems->sum('_weight'),
+ );

// في buildItems()، ضيف الوزن:
+ '_weight' => (float) ($product->weight ?? 0) * $qty,

// وامسح resolveCartWeight() خالص (سطر ٣٤٣-٣٥٦)
🟠 عالي ⚙️ Backend فقط abjora-backend BUG-006

مفيش Index على orders.order_reference — Full Table Scan على كل ويب هوك

⚙️ Backend Migration: add_salasa_fields_to_orders_table.php سطر ٢٩

الـ order_reference column بيُستخدم في كل webhook lookup بس مفيش عليه index. كل webhook بيعمل full table scan. مع زيادة الطلبات، الأداء بيتدهور.

⚠️ التأثير

أداء الويب هوكس بيبطئ مع كل طلب جديد. على ١٠٠ ألف+ طلب، كل webhook بياخد وقت أطول. في ساعات الذروة ممكن الـ queue يتكدس.

عرض الكود والحل
// migration جديد:
+ Schema::table('orders', function (Blueprint $table): void {
+     $table->unique('order_reference');
+ });
🟠 عالي ⚙️ Backend فقط abjora-backend BUG-007

إعادة محاولة الدفع بتعمل charge تاني من غير ما تتأكد — خطر دفع مزدوج

⚙️ Backend PaymentService.php → retryCharge() سطر ١٠٧-١٢٧

retryCharge() بتعمل charge جديد لو الطلب Pending بس مش بتشيك لو الـ charge الأول لسه INITIATED (يعني Tap لسه بتعالجه). لو العميل دوس "إعادة المحاولة" قبل ما Tap تخلص، بيتعمل charge تاني. لو الاتنين اتقبضوا = العميل بيدفع مرتين. والـ tap_charge_id بيتمسح ويتحط الجديد.

⚠️ التأثير

ممكن العميل يتخصم منه مرتين. الـ charge الأول الـ ID بتاعه بيتمسح فمش هتعرف ترجعه أوتوماتيك — لازم تعمل refund يدوي من لوحة Tap.

عرض الكود والحل
// File: app/Services/Payments/PaymentService.php
// ضيف الحماية بعد سطر ١١٦:

+        // امنع إعادة المحاولة لو في charge لسه شغال
+        if ($order->tap_charge_status === 'INITIATED') {
+            throw new \RuntimeException(
+                "مينفعش تعيد المحاولة — في عملية دفع لسه شغالة للطلب {$order->order_reference}."
+            );
+        }
🟠 عالي ⚙️ Backend فقط abjora-backend BUG-008

CancelUnpaidOrderJob مش بيحرر المخزون مباشرةً — معتمد كلياً على الـ Observer

⚙️ Backend CancelUnpaidOrderJob.php → handle() سطر ٥١-١١١

الـ docblock بيقول "Step 3: Release quantity_reserved" بس الكود مفيهوش أي call مباشر للـ ReleaseOrderReservationAction. الجوب بيعتمد على إن الـ $order->update() هيفعّل الـ Observer. لو حد عدل الـ Observer أو عمله disable = المخزون مش هيتحرر. ده تصميم هش وخطير.

⚠️ التأثير

اعتماد ضمني وخطير. الجوب مشتغل صح دلوقتي بالصدفة عشان الـ Observer مسجل. بس أي تعديل مستقبلي (زي withoutEvents أو seeder) ممكن يكسر تحرير المخزون من غير ما حد ياخد باله.

عرض الكود والحل
// File: app/Jobs/Orders/CancelUnpaidOrderJob.php
// ضيف call صريح بعد الـ update (حوالي سطر ٧٠):

         $order->update(['status' => OrderStatusEnum::Cancelled]);

+        // حرر المخزون صراحةً — متعتمدش على الـ Observer لوحده
+        app(ReleaseOrderReservationAction::class)->execute($order);
🟡 متوسط ⚙️ Backend فقط abjora-backend BUG-009

Cache::tags() في الشحن — بتعمل crash على الـ File/Database cache driver

⚙️ Backend ShippingCalculatorService.php → calculate() سطر ٤٥

Cache::tags(['shipping_zones']) بيشتغل بس على Redis و Memcached. لو الـ env بيستخدم file أو database cache driver، بيعمل BadMethodCallException. ده بيكسر حساب الشحن بالكامل.

⚠️ التأثير

الـ Checkout بيقع في بيئات مفيهاش Redis. أي بيئة development أو staging أو production بـ file cache، حساب الشحن بيعمل crash.

عرض الكود والحل
// File: app/Services/Shipping/ShippingCalculatorService.php

- Cache::tags(['shipping_zones'])->remember(...);

+ // استخدم Cache::remember() بدون tags — بيشتغل على أي driver
+ Cache::remember("shipping_zone_{$countryId}", 3600, function () use (...) {
+     return ...;
+ });
🟡 متوسط 🔗 Backend + Frontend Cross-Repo BUG-010

الداشبورد بيبعت country كنص بدل country_id — نفس مشكلة الموقع

🖥️ Frontend (Dashboard) abajora-dashboard → stores/orders

الداشبورد لما بيعمل طلبات يدوي أو بيعدل عناوين، ممكن يبعت country كنص بدل country_id كرقم.

⚙️ Backend PlaceOrderRequest.php — مفيش validation

الباك اند مفيهوش required validation على country_id — بيعمل fallback لـ 0 بهدوء. محتاج validation صريح.

⚠️ التأثير

مشكلة الشحن المجاني بتتكرر من الداشبورد كمان — مش الموقع بس.

عرض الكود والحل
// نفس حل BUG-004 — ضيف validation في الباك اند:
+ 'shipping_address.country_id' => ['required', 'integer', 'exists:countries,id'],

// وعدل الداشبورد يبعت country_id
🟡 متوسط 🔗 Backend + Frontend Cross-Repo BUG-011

الكوبون بيتطبق في الباك اند بس العرض في الفرونت اند مش دقيق — مفيش grand_total_preview

🖥️ Frontend checkout.vue → applyCoupon() سطر ١٠٦٧-١٠٩٦

الفرونت بيحاول يقرأ res.discount_amount أو res.discount أو res.total_after_discount — بس مش واثق أي واحد فيهم الباك اند بيرجعه. بيعمل Number(res.discount_amount ?? res.discount ?? 0) كـ fallback chain.

⚙️ Backend CouponController.php → validate()

الباك اند بيرجع response مش واضح الـ key names فيه. الفرونت بيحاول يخمن الـ field name. لازم يبقى في عقد واضح بين الطرفين.

⚠️ التأثير

العميل ممكن يشوف خصم غلط في صفحة الـ checkout — الرقم الحقيقي بيتحسب بعد الدفع.

عرض الكود والحل
// وحد response الـ coupon validation API:
+ return response()->json([
+     'valid'                => true,
+     'discount_amount'      => $discount,
+     'total_after_discount' => $grandTotal,
+ ]);
🟠 عالي 🔗 Backend + Frontend Cross-Repo BUG-012

الباك اند مفيهوش Validation على country_id — Fallback هادئ لـ 0

🖥️ Frontend

مفيش validation في الفرونت اند على الـ country_id — حتى لو المستخدم ما اختارش بلد، الفورم بيتبعت.

⚙️ Backend PlaceOrderRequest.php + CheckoutService.php

الباك اند مفيهوش rule required على country_id. بيعمل ?? 0 وبيكمل عادي — ده مفروض يرجع 422 error مش يعمل fallback هادئ.

⚠️ التأثير

أي client (موقع، داشبورد، API خارجي) يقدر يعمل طلب بشحن ٠.٠٠ لو نسي يبعت الـ country_id.

عرض الكود والحل
// File: app/Http/Requests/Checkout/PlaceOrderRequest.php
+ 'shipping_address.country_id' => ['required', 'integer', 'exists:countries,id'],
+ 'billing_address.country_id'  => ['required_if:same_as_shipping,false', 'integer', 'exists:countries,id'],

// File: CheckoutService.php سطر ٩٣ — شيل الـ fallback:
- countryId: (int) ($data['shipping_address']['country_id'] ?? 0),
+ countryId: (int) $data['shipping_address']['country_id'],
🟠 عالي ⚙️ Backend فقط abjora-backend BUG-013

سلاسة بتحط رقم التتبع في عمود carrier_name — بيمسح اسم شركة الشحن

⚙️ Backend SalasaWebhookService.php → updateOrder() سطر ١٨٤

في updateOrder()، الـ $dto->trackingNumber بيتحط في عمود carrier_name بدل ما يتحط في عمود tracking_number (اللي مش موجود أصلاً). يعني: (١) اسم شركة الشحن بيتمسح، (٢) رقم التتبع بيظهر مكان اسم الشركة في الداشبورد، (٣) مفيش طريقة تتبع الشحنة صح.

⚠️ التأثير

بيانات الطلبات مخربة. الداشبورد بيعرض رقم التتبع مكان اسم شركة الشحن. أي تقارير أو فلترة بناء على اسم الشركة بترجع بيانات غلط.

عرض الكود والحل
// ١. migration جديد — ضيف عمود tracking_number:
+ $table->string('tracking_number', 200)->nullable()->after('carrier_name');

// ٢. File: SalasaWebhookService.php سطر ١٨٤:
- $fields['carrier_name'] = $dto->trackingNumber;
+ $fields['tracking_number'] = $dto->trackingNumber;
🟠 عالي 🖥️ Frontend فقط abajora-website BUG-014

خطوة الشحن في الـ Checkout بتعرض "شحن مجاني ٠.٠٠" hardcoded — مش بتجيب السعر الحقيقي

🖥️ Frontend checkout.vue → Step 3 template سطر ٦٧٠-٦٨٤

خطوة الشحن (Step 3) في الـ guest checkout بتعرض radio button واحد ثابت: "Free" — 0.00 SAR. مش بتعمل API call للباك اند عشان تجيب سعر الشحن الفعلي بناءً على عنوان العميل. حتى لو BUG-004 اتصلح، العميل هيفضل يشوف "مجاني" عشان الـ UI ثابت.

⚠️ التأثير

تجربة checkout مضللة. العميل بيشوف "شحن مجاني" بس ممكن يتفاجئ بمبلغ مختلف في الإيصال. ده ممكن يخالف قوانين حماية المستهلك في دول الخليج اللي بتلزم بعرض السعر النهائي قبل الدفع.

عرض الكود والحل
// ١. ضيف API endpoint في الباك اند:
//    GET /api/user/v1/checkout/shipping-estimate
//    Params: country_id, subtotal
//    Returns: { shipping_cost: number, method: string }

// ٢. في checkout.vue، ضيف:
+ const shippingCost = ref(0)
+ const shippingLoading = ref(false)
+
+ async function fetchShippingEstimate() {
+   shippingLoading.value = true
+   try {
+     const res = await $fetch('/api/user/v1/checkout/shipping-estimate', {
+       params: { country_id: form.shipping.countryId, subtotal: totalPrice.value }
+     })
+     shippingCost.value = res.data?.shipping_cost ?? 0
+   } finally { shippingLoading.value = false }
+ }

// ٣. استدعي fetchShippingEstimate() لما المستخدم يتقدم للخطوة ٣
🟡 متوسط ⚙️ Backend فقط abjora-backend BUG-015

SalasaWebhookController بيرجع HTTP 200 على أي error — بيخبي الأخطاء

⚙️ Backend SalasaWebhookController.php → __invoke() سطر ٥٣

الكنترولر بيمسك كل الـ exceptions ويرجع 200. ده معناه: (١) أي باج حقيقي بيتخبى ومش بيظهر في الـ monitoring، (٢) الـ order status updates بتضيع نهائي مفيش retry، (٣) لو Salasa غيرت format الـ webhook، الـ integration كله بيقع من غير ما حد يحس.

⚠️ التأثير

حالة الطلبات مش بتتحدث ومحدش حاسس. الطلبات المشحونة بتفضل "قيد المعالجة" للأبد.

عرض الكود والحل
// خزن الويب هوكس الفاشلة عشان تقدر تعيد تشغيلها:

+ } catch (\Throwable $e) {
+     Log::critical('Salasa webhook processing failed', [
+         'error' => $e->getMessage(),
+         'trace' => $e->getTraceAsString(),
+     ]);
+
+     DB::table('failed_salasa_webhooks')->insert([
+         'payload'    => json_encode($request->all()),
+         'error'      => $e->getMessage(),
+         'created_at' => now(),
+     ]);
+
+     return response()->json(['status' => 'error'], 200);
+ }
🟡 متوسط ⚙️ Backend فقط abjora-backend BUG-016

سينك مخزون سلاسة بيمسح الـ quantity_reserved المحلي — بيحذف حجوزات الـ checkout

⚙️ Backend SyncProductsService.php → updateStockForSku() سطر ٢٢٠

updateStockForSku() بتحط quantity_reserved من بيانات سلاسة. بس سلاسة الـ reserved بتاعها = حجوزات المخزن. مش حجوزات الـ checkout المحلية اللي CheckoutService عملها. لما السينك بيشتغل، بيمسح كل حجوزات العملاء اللي في الـ checkout — وده بيسمح لعملاء تانيين يشتروا نفس المنتجات = overselling.

⚠️ التأثير

حجوزات الـ checkout بتتمسح مع كل stock sync. كل ما السينك يشتغل أكتر، كل ما خطر الـ overselling يزيد.

عرض الكود والحل
// File: app/Services/Salasa/SyncProductsService.php سطر ٢٢٠
// امسح الـ quantity_reserved من السينك:

- 'quantity_reserved' => $reserved,

+ // متعملش سينك للـ quantity_reserved من سلاسة.
+ // الـ quantity_reserved المحلي بيتدار من:
+ //   CheckoutService (حجز) و ReleaseOrderReservationAction (تحرير)
+ // سلاسة الـ "reserved" بتاعها = حجوزات مخزن مختلفة تماماً.
🟡 متوسط 🖥️ Frontend فقط abajora-website BUG-017

كوكي الـ checkout بصلاحية ٧ أيام — بيانات قديمة بتسبب مشاكل

🖥️ Frontend checkout.vue → useCookie() سطر ٩٥٨

الـ checkout state بيتحفظ في cookie صلاحيتها ٧ أيام. لو العميل رجع بعد أسبوع، الفورم بيترجع بالبيانات القديمة — بس السلة ممكن تكون اتغيرت خالص. والكوبون اللي كان متطبق ممكن يكون انتهى. النتيجة: بيانات مش متوافقة وتجربة مربكة.

⚠️ التأثير

تجربة checkout مربكة ببيانات قديمة. العملاء بيشوفوا بيانات عناوين قديمة وكوبونات منتهية مع سلة مختلفة.

عرض الكود والحل
// File: abajora-website/app/pages/checkout.vue
// قلل صلاحية الكوكي وضيف validation للسلة:

- maxAge: 60 * 60 * 24 * 7, // 7 days
+ maxAge: 60 * 60 * 2, // ساعتين بس

// ضيف cart hash عشان تتأكد السلة ما اتغيرتش:
+ const cartHash = computed(() =>
+   items.value.map(i => `${i.product.id}:${i.quantity}`).join(',')
+ )
+ if (checkoutCookie.value?.cartHash &&
+     checkoutCookie.value.cartHash !== cartHash.value) {
+   checkoutCookie.value = null // السلة اتغيرت — امسح البيانات القديمة
+ }

جدول ملخص

# الخطورة الموقع الوصف المختصر المنطقة
001حرج⚙️ BackWebhook Cancellation bypass — تسريب المخزونTap Payments
002حرج⚙️ Backالدفع الفاشل مش بيحرر المخزونTap Payments
003حرج⚙️ BackTapCallback undefined variables — crashTap Payments
004حرج🔗 الاتنينcountry_id مش بيتبعت — الشحن = ٠.٠٠Shipping
005عالي⚙️ BackN+1 query في resolveCartWeight()Checkout
006عالي⚙️ Backمفيش index على order_referenceDatabase
007عالي⚙️ BackRetry payment — خطر دفع مزدوجTap Payments
008عالي⚙️ BackCancelUnpaidOrderJob — Observer dependencyStock
009متوسط⚙️ BackCache::tags() crash على file driverShipping
010متوسط🔗 الاتنينالداشبورد كمان مش بيبعت country_idShipping
011متوسط🔗 الاتنينعقد الكوبون مش واضح بين Front و BackCoupons
012عالي🔗 الاتنينمفيش validation على country_id — fallback ٠Validation
013عالي⚙️ Backtracking_number في carrier_nameSalasa
014عالي🖥️ Frontالشحن hardcoded "مجاني" في الـ UICheckout UI
015متوسط⚙️ BackSalasa webhook بتخبي الأخطاءSalasa
016متوسط⚙️ BackStock sync بيمسح checkout reservationsSalasa / Stock
017متوسط🖥️ FrontCheckout cookie — ٧ أيام + بيانات قديمةCheckout UX