المعمارية English

arc42 §6c — تدفقات بيانات الميزات: التجارة والإدارة

الحالة: مسودة v0.1 · التاريخ: 2026-07-20 يغطي: SRC-BRD §7.1 — الدفع · السوق الإلكتروني (marketplace) · الإدارة · التقارير · الإشعارات وثيقة مرافقة: 06b-feature-flows-core.md · 06-runtime-view.md

⚠️ كل تدفق في هذه الوثيقة مشروط بـ R-01 (ترخيص SAMA). إذا لم يكن مسموحًا لـ Qwizin بحيازة الأموال وصرفها، فإن طوبولوجيا الدفع تتغيّر — لا الميزات، بل مَن يحرّك المال. جميع التدفقات أدناه مرسومة وفق Model B (مزوّد خدمات دفع مرخّص PSP يحرّك الأموال؛ وQwizin تنقل التعليمات فقط)، وهو الوضع الأقل مخاطرة بغضّ النظر عن الإجابة القانونية.


فهرس التدفقات#

ميزة BRD §7.1 التدفق
دفع الاستشارة C-01
دفع الطلب المخصّص F-08 في 06b
دفع اشتراك المزوّد C-02
دفع الإدراج المميّز (featured) C-03
الإدراجات المميّزة (أثر الترتيب) C-04
التحويل للمزوّد (payout) للمستشارين والمزوّدين C-05
إدارة متعددة الأدوار · اعتماد المستخدمين C-06
إدارة المحتوى · إدارة المستشارين والمزوّدين C-07
مراقبة المدفوعات · التقارير · لوحة المعلومات C-08
الإشعارات (BRD §13 التبعيات 8/12/13) C-09

C-00 — طوبولوجيا تدفق الأموال (Model B)#

تصف SRC-BRD §9.4 تدفق الإيرادات العميل → منصة Qwizin → المزوّد/المستشار. هذا هو Model A، وهو الشكل الذي يستدعي الترخيص.

flowchart TB
    subgraph MA["❌ Model A — Qwizin holds funds"]
        direction LR
        CA["👤 Customer"] -->|pays| QA["Qwizin<br/>merchant account"]
        QA -->|"holds balance"| QB[("Qwizin<br/>ledger")]
        QB -->|disburses| PA["🎓 Consultant"]
    end

    subgraph MB["✅ Model B — Qwizin never touches funds"]
        direction LR
        CB["👤 Customer"] -->|pays| PSP["💳 SAMA-licensed PSP"]
        PSP -->|"split settlement"| PB["🎓 Consultant<br/><i>sub-merchant under<br/>PSP's licence</i>"]
        PSP -->|"commission"| QC["Qwizin<br/><i>separate merchant txn</i>"]
        QD["Qwizin API"] -.->|"transmits split<br/>instructions only"| PSP
    end

    style MA fill:#fde8e8,stroke:#c94a4a
    style MB fill:#e8f5e9,stroke:#2d8659
Model A Model B ✅
الأموال تستقر في حساب تتحكم فيه Qwizin نعم لا
Qwizin تدير سجل أرصدة (ledger) نعم لا
الوضع التنظيمي المرجّح مؤسسة دفع مزوّد تقنية
onboarding المستشار مشكلة Qwizin عملية KYC/AML لدى الـ PSP

القرار: التصميم وفق Model B. إذا أكّدت الاستشارة القانونية لاحقًا أن Model A مسموح، فالانتقال إليه مباشر وبسيط. أما العكس — اكتشاف الحاجة إلى ترخيص في منتصف البناء — فهو الاتجاه المكلف. لا تبنِ ledger بأرصدة مملوكة للمستخدمين حتى يُجاب R-01.

معايير اختيار بوابة الدفع (gateway) الناتجة عن هذه التدفقات#

يغذّي OQ-01. أي gateway يخفق في أي من هذه المعايير يُستبعد:

المتطلب التدفق المصدر
دعم mada واقع السوق السعودي
الفصل بين authorize / capture / void (حجز / تحصيل / إلغاء) C-01 — يحلّ CONF-02
الاسترداد، الكامل والجزئي C-01, F-08
المدفوعات المقسّمة (split payments) في marketplace / onboarding التجار الفرعيين (sub-merchant) C-05 — Model B
فوترة متكررة مع منطق إعادة المحاولة C-02
webhooks موقّعة وقابلة لإعادة التشغيل الكل
حقول مستضافة (hosted fields) أو إعادة توجيه (بيانات البطاقة لا تمرّ بـ Qwizin إطلاقًا) تقليل نطاق PCI

C-01 — دفع الاستشارة#

التسلسل التفصيلي في Flow 3. مُلخَّص هنا على شكل آلة حالات (state machine)، لأن الحالات هي ما يتفاعل معه بقية النظام.

stateDiagram-v2
    [*] --> pending_payment: Booking created,<br/>slot locked
    pending_payment --> awaiting_consultant: 💳 AUTHORIZE (hold)<br/><i>funds NOT taken</i>
    pending_payment --> abandoned: Payment window expires<br/>→ release slot

    awaiting_consultant --> confirmed: Consultant accepts<br/>→ 💳 CAPTURE
    awaiting_consultant --> declined: Consultant rejects<br/>→ 💳 VOID
    awaiting_consultant --> expired: SLA timer elapses<br/>→ 💳 VOID + release slot

    confirmed --> completed: Session held<br/>→ eligible for payout
    confirmed --> no_show_customer: Customer absent
    confirmed --> no_show_consultant: Consultant absent<br/>→ 💳 REFUND
    confirmed --> cancelled_by_customer: → refund per policy ⚠
    confirmed --> failed_technical: Session never established<br/>→ 💳 REFUND ⚠

    completed --> [*]
    declined --> [*]
    expired --> [*]

    note right of awaiting_consultant
        AUTHORIZE not CAPTURE.
        This is the fix for CONF-02 —
        money is never taken for a
        service not yet agreed.
    end note

    note right of cancelled_by_customer
        ⚠ Refund policy UNDEFINED
        in all source documents.
        Free cancellation window?
        Partial refund tiers?
        → OQ-05
    end note

الخاصية التكرارية الآمنة (idempotency) — غير قابلة للتفاوض#

كل استدعاء يغيّر حالة الدفع يحمل idempotency key يوفّره العميل، وكل webhook من الـ gateway يُتحقَّق من توقيعه ويُعالَج بشكل idempotent مرتبطًا بمعرّف الحدث لدى الـ gateway. البوابات تعيد المحاولة. المعالجة المكرّرة لعملية capture تعني خصمًا مزدوجًا، وهو في آن واحد حدث يمسّ ثقة العميل واسترداد نزاعي (chargeback).


C-02 — اشتراك مزوّد الخدمة#

SRC-BRD §7.1, §9.3.3 · SRC-MOM BR-3 · CON-07 (الخطط قابلة للتهيئة عبر Admin Panel)

حالة الاشتراك هي ما يتحكم في الظهور ضمن الدليل — وهذا هو العمود الفقري للإيرادات المتكررة للمنصة.

sequenceDiagram
    autonumber
    participant SP as 🚚 Service Provider
    participant API as Qwizin API
    participant PAY as 💳 PSP
    participant SCH as Scheduler
    participant IDX as Search Index
    participant N as Notifications

    rect rgb(240,240,255)
    Note over SP,IDX: Initial subscription
    SP->>API: GET /v1/subscription-plans
    Note over API: Plans are ADMIN-CONFIGURABLE data,<br/>not code (CON-07).<br/>Monthly + annual tiers.
    SP->>API: POST /v1/subscriptions {plan_id}
    API->>API: ⚠ Precondition: account.state == active<br/>(documents approved — BR-RL-1)
    API->>PAY: Create subscription / initial charge
    PAY-->>API: Confirmed
    API->>DB: subscription(state: active, period_end)
    API->>IDX: 🔍 Index listing as VISIBLE
    N->>SP: 🔔 Subscription active
    end

    rect rgb(255,245,235)
    Note over SCH,IDX: Renewal cycle
    SCH->>API: T-7d: upcoming renewal
    N->>SP: 🔔 Renewal reminder
    SCH->>PAY: Charge on period_end
    alt Payment succeeds
        PAY-->>API: Confirmed
        API->>DB: period_end += interval
    else Payment fails
        API->>DB: subscription(state: past_due)
        N->>SP: 🔔 Payment failed
        loop Dunning: retry schedule
            SCH->>PAY: Retry
        end
        alt Still failing after grace period
            API->>DB: subscription(state: lapsed)
            API->>IDX: 🔍 Remove listing from index
            N->>SP: 🔔 Listing no longer visible
            Note over API,IDX: ⚠ Grace period length UNDEFINED<br/>→ OQ-20
        end
    end
    end

    rect rgb(255,240,240)
    Note over SP,IDX: Cancellation
    SP->>API: DELETE /v1/subscriptions/{id}
    API->>DB: cancel_at_period_end = true
    Note over API: Access retained until period_end —<br/>they paid for it. Do NOT revoke immediately.
    SCH->>API: At period_end
    API->>IDX: 🔍 Remove from index
    end

حالة الاشتراك تتحكم في الظهور ضمن الدليل#

stateDiagram-v2
    [*] --> none: Provider approved,<br/>no subscription
    none --> active: Payment succeeds
    active --> past_due: Renewal fails
    past_due --> active: Retry succeeds
    past_due --> lapsed: Grace period exhausted
    lapsed --> active: Provider resubscribes
    active --> cancelling: Provider cancels
    cancelling --> lapsed: period_end reached

    note right of none
        Visible in directory: ❌
    end note
    note right of active
        Visible in directory: ✅
    end note
    note right of past_due
        Visible: ✅ (grace)
        — do not punish a
        transient card failure
    end note
    note right of lapsed
        Visible: ❌
    end note

⚠️ يجب ألا يكون الـ index هو المرجع الموثوق هنا إطلاقًا. فهرس بحث قديم يعرض مزوّدًا منتهي الاشتراك على أنه نشط هو في الوقت نفسه مشكلة تجارية (ظهور غير مدفوع) ومشكلة ثقة (عملاء يتواصلون مع جهات غير نشطة). نتائج البحث تُرشَّح بعديًا مقابل قاعدة البيانات قبل إرجاعها (Flow 5).


SRC-BRD §7.1, §9.3.4 · SRC-MOM REQ-06

flowchart LR
    SP["🚚 Provider"] --> CHK{"Precondition:<br/>active subscription?"}
    CHK -->|No| DENY["❌ Featured requires<br/>an active base subscription"]
    CHK -->|Yes| PLANS["Featured packages<br/><i>admin-configurable</i><br/>duration · category · placement"]
    PLANS --> PAY2["💳 Payment"]
    PAY2 --> GRANT["featured_placement<br/>(category, starts_at, ends_at)"]
    GRANT --> IDX2["🔍 Reindex with<br/>featured_rank + expiry"]
    IDX2 --> RANK["→ C-04 ranking effect"]

    SCH2["⏰ Scheduler"] -->|"at ends_at"| EXPIRE["Expire placement"]
    EXPIRE --> IDX3["🔍 Reindex without boost"]

    style DENY fill:#fde8e8,stroke:#c94a4a
    style GRANT fill:#e8f5e9,stroke:#2d8659

سؤال المخزون الذي يثيره هذا التدفق: هل الإدراج المميّز غير محدود (كل من يدفع يحصل على تعزيز) أم نادر (N مقعدًا لكل فئة)؟

النموذج النتيجة
غير محدود بسيط في البناء. لكن إذا كان كل مزوّد في فئة ما مميّزًا، فإن التعزيز يصبح بلا قيمة ويتدهور المنتج إلى رسوم مقابل لا شيء.
نادر (N لكل فئة) يحافظ على القيمة ويدعم التسعير المميّز. يتطلب إدارة مخزون، وقائمة انتظار، ومعالجة التعارض عند لحظة الشراء.

لم تتناوله أي وثيقة مصدر → OQ-21. التوصية: نادر، لأن التعزيز غير المحدود يلغي نفسه — لكن هذا قرار يخصّ نموذج العمل.


C-04 — أثر ترتيب الإدراج المميّز#

SRC-MOM REQ-06: "المزوّدون المشتركون في باقة Featured يظهرون في الأعلى"

هنا يتصادم الاهتمام التجاري مع جودة البحث، والخطأ في ذلك يدمّر الاثنين معًا.

flowchart TB
    Q["🔍 Query: 'مورد أغذية' + filters"] --> NORM["Arabic normalisation<br/><i>alef · teh marbuta · yeh ·<br/>diacritics · tatweel</i>"]
    NORM --> REL["Compute relevance score<br/>per candidate"]
    REL --> BOOST["Apply featured multiplier"]

    BOOST --> CHECK{"Boost model?"}
    CHECK -->|"❌ Sort by featured DESC,<br/>then relevance"| BAD["Irrelevant featured providers<br/>appear above relevant ones.<br/><b>Users stop trusting search.</b><br/>Featured product loses value."]
    CHECK -->|"✅ Relevance × bounded multiplier"| GOOD["Featured providers outrank<br/>comparable non-featured.<br/>An irrelevant result<br/><b>cannot</b> be promoted."]

    GOOD --> DECAY["Apply recency decay<br/><i>stale featured listing must not<br/>outrank a perfect match forever</i>"]
    DECAY --> HYD2["Hydrate from DB"]
    HYD2 --> POST["⚠ Post-filter:<br/>approved AND subscription current"]
    POST --> OUT["Ranked results<br/>+ featured badge"]

    style BAD fill:#fde8e8,stroke:#c94a4a
    style GOOD fill:#e8f5e9,stroke:#2d8659
    style POST fill:#fff4e5,stroke:#d98c1f

قاعدة التصميم#

تعزيز featured هو مُضاعِف محدود يُطبَّق على درجة الملاءمة (relevance)، وليس تجاوزًا لها إطلاقًا. يجب ألا يظهر مورّد تغليف مميّز فوق مورّد أغذية ملائم حين يبحث المستخدم عن الأغذية. وفي اللحظة التي يتعلّم فيها المستخدمون أن النتائج الأولى مشتراة لا ملائمة، يتوقفون عن قراءتها — ويصبح منتج featured الذي اشتُريت به بلا قيمة.

⚠️ هذا يقيّد اختيار محرك البحث. أظهر البحث أن أحد المرشحين البارزين (Meilisearch) لا يدعم التعزيز وقت الاستعلام (query-time boosting) إطلاقًا — فالتعزيز فيه قاعدة ترتيب على مستوى الفهرس، وبسبب الفرز التجميعي (bucket sort) لا يفصل إلا بين المستندات المتساوية أصلًا في كل قاعدة سابقة. ورفعه في ترتيب القواعد يُضعف ملاءمة النص عالميًا، ولكل استعلام، لأنه إعداد وليس معاملًا. أما المحركات ذات التقييم التعبيري وقت الاستعلام فتلبّي هذا المتطلب أصلًا. انظر R-11.


C-05 — التحويلات (payouts) للمستشارين والمزوّدين ⚠#

التدفق الأكثر تأثرًا بـ R-01.

sequenceDiagram
    autonumber
    participant SCH as ⏰ Scheduler
    participant API as Qwizin API
    participant PSP as 💳 SAMA-licensed PSP
    participant CON as 🎓 Consultant
    participant ADM as 🛡️ Admin

    rect rgb(240,240,255)
    Note over CON,PSP: Onboarding — Model B
    CON->>API: Provide payout details
    API->>PSP: Initiate sub-merchant onboarding
    PSP->>CON: KYC / AML directly with PSP
    Note over PSP,CON: ⚠ KYC is the PSP's obligation<br/>under ITS licence — not Qwizin's.<br/>This is the point of Model B.
    PSP-->>API: {sub_merchant_ref, status}
    API->>DB: Store reference ONLY — never bank details
    end

    rect rgb(240,255,240)
    Note over SCH,PSP: Earnings & settlement
    SCH->>API: Compute settlement period
    API->>DB: Aggregate completed sessions + delivered requests
    API->>API: Apply commission
    API->>DB: statement(gross, commission, net) — immutable
    API->>PSP: Instruct split settlement
    PSP->>CON: 💰 Funds — direct, never via Qwizin
    API->>DB: 📝 Hash-chained financial audit entry
    end

    rect rgb(255,240,240)
    Note over CON,ADM: ⚠ Highest-value fraud target
    CON->>API: Change payout bank details
    API->>API: 🔐 Step-up MFA required
    API->>API: ⏳ Delay window before effective
    API->>CON: 🔔 Out-of-band notification to<br/>PREVIOUS contact channel
    API->>DB: 📝 Audit with actor + IP
    Note over API,CON: Account takeover → redirect payouts<br/>is the classic marketplace fraud.<br/>MFA + delay + out-of-band alert<br/>are all required, not optional.
    end

الضوابط#

الضابط المبرر
عدم تخزين البيانات البنكية إطلاقًا — فقط مرجع الـ sub-merchant لدى الـ PSP يزيل فئة كاملة من الاختراقات وعبء الامتثال
مصادقة مُعزَّزة (step-up) بـ MFA عند تغيير بيانات الـ payout الهدف الأعلى قيمة للاحتيال في النظام بأكمله
نافذة تأخير + إشعار خارج النطاق (out-of-band) إلى القناة السابقة يمنح المالك الشرعي فرصة للتدخل بعد الاستيلاء على الحساب
سجل تدقيق مسلسل بالتجزئة (hash-chained) لكل الأحداث المالية خطر الإنكار — السجل العادي القابل للتعديل غير كافٍ حين يستطيع أي شخص لديه صلاحية الكتابة في قاعدة البيانات إعادة كتابة التاريخ
كشوف حساب غير قابلة للتعديل التصحيحات قيود تسوية جديدة، لا تعديلات

C-06 — الإدارة وسير عمل الاعتماد#

SRC-BRD §7.1 ("إدارة متعددة الأدوار") · SRC-UFC (Admin Portal)

flowchart TB
    subgraph ROLES["⚠ Multi-role administration — CONF-03 unresolved"]
        R1["Approval Reviewer<br/>documents, accounts"]
        R2["Content Manager<br/>templates, learning, assessments"]
        R3["Marketplace Manager<br/>consultants, providers, featured"]
        R4["Finance<br/>payments, payouts, refunds"]
        R5["Support<br/>read-mostly, impersonation"]
        R6["Super Admin<br/>role assignment"]
    end

    subgraph GUARD["Admin access controls"]
        G1["🔐 MFA mandatory"]
        G2["Separate origin from<br/>user-facing app"]
        G3["📝 EVERY action audited"]
        G4["Impersonation explicitly<br/>logged + time-boxed"]
    end

    subgraph QUEUES["Work queues"]
        Q1["📥 Document review<br/><i>oldest-first, SLA-tracked</i>"]
        Q2["📥 Custom template requests"]
        Q3["📥 Disputes / refunds"]
        Q4["📥 Reported content"]
    end

    ROLES --> GUARD --> QUEUES

    style R6 fill:#fde8e8,stroke:#c94a4a
    style G1 fill:#fff4e5,stroke:#d98c1f

⚠️ تقسيم أدوار المسؤولين أعلاه مقترح، لا مواصفة. تقول SRC-BRD §7.1 "إدارة متعددة الأدوار" دون تعداد الأدوار، ومصفوفة §11 غير متسقة داخليًا (CONF-03). لا يمكن بناء التخويل حتى تصدر مصفوفة مصحّحة (OQ-06).

لماذا المسؤول هو الهدف الأعلى قيمة#

يستطيع مسؤول المنصة اعتماد الحسابات، والاطلاع على الوثائق القانونية لكل مستأجر، ولمس المدفوعات. اختراق حساب مسؤول واحد يعادل اختراق المنصة بأكملها. ومن هنا: MFA إلزامي، وأصل (origin) منفصل، وتغطية تدقيق كاملة، وانتحال هوية المستخدم (impersonation) مسجَّل صراحةً ومحدود زمنيًا — فموظفو الدعم سيحتاجون إلى رؤية ما يراه المستخدم، ويجب أن تكون هذه القدرة قابلة للملاحظة لا خفيّة.


C-07 — إدارة المحتوى والـ marketplace#

flowchart LR
    subgraph CM["Content management"]
        T["Templates<br/>create · version · publish · retire"]
        L["Learning modules<br/>author · publish"]
        A["Assessments<br/>question banks · passing score ⚠"]
        CAT["Categories &amp; taxonomy<br/><i>bilingual</i>"]
    end

    subgraph MM["Marketplace management"]
        CN["Consultants<br/>approve · suspend · verify specialties"]
        PR["Providers<br/>approve · suspend · categories"]
        FT["Featured inventory<br/><i>if scarce — OQ-21</i>"]
        PL["Subscription plans<br/><i>CON-07: configurable</i>"]
    end

    CM --> PUB["Publish → versioned,<br/>previous version retained"]
    MM --> IDX4["→ Reindex affected listings"]

    A -.->|"⚠ CON-08"| SCORE["Passing score is<br/>ADMIN-CONFIGURABLE<br/>per assessment"]

    style SCORE fill:#fff4e5,stroke:#d98c1f

قيدان من SRC-BRD §12 يجعلان هذه الأمور بيانات، لا كودًا:

  • CON-07 — باقات الاشتراك قابلة للتهيئة عبر Admin Panel
  • CON-08 — درجة النجاح في التقييم قابلة للتهيئة من قِبل المسؤول

كلا القيدين يُستوفى ببساطة تامة إذا صُمّم كتهيئة (configuration) من البداية، ويتطلب تغييرًا في الكود ونشرًا لكل تعديل إذا كُتب بشكل صلب. تغيير درجة النجاح يجب ألا يستلزم إصدارًا إطلاقًا.

⚠️ سؤال الأثر الرجعي: إذا خفّض المسؤول درجة النجاح، فهل تنجح المحاولات الراسبة سابقًا بأثر رجعي؟ شبه مؤكد أن لا — لكن على النظام أن يقرّر ذلك صراحةً لا عرضًا. → OQ-22


C-08 — التقارير ولوحة المعلومات#

SRC-BRD §7.1 · SRC-MOM §13 (مؤشرات الأداء الأربعة)

flowchart TB
    subgraph SOURCES["Event sources — Pipeline 1"]
        E1["BookingConfirmed<br/>ConsultationCompleted"]
        E2["TemplateDownloaded<br/>CustomRequestDelivered"]
        E3["AssessmentPassed<br/>CertificateIssued"]
        E4["SubscriptionActivated<br/>FeaturedPurchased<br/>PaymentCaptured"]
    end

    subgraph PROJ["Projection — Pipeline 4"]
        OUT2["Outbox"] --> P["Projectors"]
        P --> RM[("Reporting read model<br/><i>pre-aggregated</i>")]
    end

    subgraph KPI["SRC-MOM §13 KPIs"]
        K1["📊 Consultations booked<br/>&amp; completed / month"]
        K2["📊 Templates downloaded<br/>&amp; customised"]
        K3["📊 % employees passing<br/>&amp; certified"]
        K4["📊 MRR — subscriptions<br/>+ featured packages"]
    end

    subgraph VIEWS["Dashboards"]
        V1["🛡️ Platform admin<br/><i>all tenants</i>"]
        V2["🏢 Company admin<br/><i>own employees only —<br/>RLS enforced</i>"]
        V3["🎓 Consultant<br/><i>own bookings + earnings</i>"]
        V4["🚚 Provider<br/><i>own listing performance</i>"]
    end

    E1 & E2 & E3 & E4 --> OUT2
    RM --> K1 & K2 & K3 & K4
    K1 & K2 & K3 & K4 --> V1
    RM --> V2 & V3 & V4

    style V2 fill:#fff4e5,stroke:#d98c1f

نقطتا تصميم#

  1. مؤشرات الأداء الأربعة هي الحد الأدنى القابل للتطبيق من سطح التحليلات، ويجب تصميمها ضمن النظام منذ البداية. كل مؤشر منها يتطلب التقاط أحداث في module محدّد. وإعادة بناء "عدد القوالب المُنزَّلة" من سجلات الوصول بعد عام أغلى بكثير — وأقل دقة — من إصدار حدث (domain event) لحظة التنزيل.

  2. ⚠️ التقارير سطح خطر على عزل المستأجرين (tenant isolation). استعلامات التجميع هي بالضبط الموضع الذي يُغرى فيه المطوّر بتجاوز نطاق المستأجر "لأنه مجرد عدّ". ولوحة معلومات شركة تسرّب إحصاءات موظفي شركة أخرى هي الاختراق نفسه الذي يمثله تسريب سجلاتهم (QAS-SEC-01). نموذج القراءة الخاص بالتقارير محصور بالمستأجر ومحمي بـ RLS تمامًا كالجداول المعاملاتية — بلا استثناءات للتجميعات.


C-09 — توزيع الإشعارات (fan-out)#

SRC-BRD §13 التبعيات 8/12/13 · SRC-MOM "Could Have" (تذكيرات المواعيد)

flowchart TB
    subgraph TRIG["Triggers"]
        T1["Account state changed"]
        T2["Booking confirmed / declined /<br/>expired / reminder"]
        T3["Document approved / rejected"]
        T4["Certificate issued"]
        T5["Training assigned / overdue"]
        T6["Payment succeeded / failed"]
        T7["Custom request delivered"]
    end

    TRIG --> EV["Domain event → Outbox"]
    EV --> DISP["Notification dispatcher"]

    DISP --> PREF{"User channel<br/>preferences<br/>+ locale"}

    PREF --> EMAIL["✉️ Email"]
    PREF --> SMS["📱 SMS ⚠"]
    PREF --> PUSH["🔔 Push (APNs/FCM)"]
    PREF --> INAPP["📥 In-app inbox"]

    EMAIL & SMS & PUSH --> RETRY{"Delivered?"}
    RETRY -->|No| BACKOFF["Exponential backoff<br/>retry queue"]
    BACKOFF --> RETRY
    RETRY -->|"Exhausted"| DLQ["Dead letter<br/>+ alert"]
    RETRY -->|Yes| LOG2["📝 Delivery record"]

    style SMS fill:#fff4e5,stroke:#d98c1f
    style DLQ fill:#fde8e8,stroke:#c94a4a

قواعد التصميم#

القاعدة المبرر
لا تكون أبدًا في المسار المتزامن تعطّل مزوّد الإشعارات يجب ألا يُفشل عملية حجز (QAS-AVL-04)
القوالب ثنائية اللغة، تُحسم من locale المستلِم لا من locale المرسِل، ولا من locale التطبيق الحالي
صندوق الوارد داخل التطبيق هو القناة المعمّرة البريد والـ SMS بذل أفضل جهد؛ السجل داخل التطبيق هو ما يجب أن يبقى
إزالة التكرار مهام التذكير تعيد المحاولة؛ وتلقّي العميل خمس نسخ من تذكير واحد حدث يمسّ الثقة
ساعات هدوء للإشعارات غير العاجلة مراعاة ساعات العمل في السعودية

⚠️ الـ SMS مُعلَّم بإشارة تحذير. تتحفّظ SRC-BRD §13 التبعية 13 بعبارة "إن نُفِّذت"، لكن OTP متطلب صارم إذا شُحن التسجيل بالهاتف (F-01). وقد ظهر قيدان إضافيان في البحث: متطلبات تسجيل مُعرِّف المرسِل (sender ID) في السعودية لدى هيئة الاتصالات (غير مُتحقَّق منه — تحقّق قبل اختيار مزوّد)، واحتيال ضخّ الرسائل (SMS pumping)، وهو مسار خسارة مالية مباشرة يتطلب حصصًا لكل رقم هاتف، ولكل IP، ولكل دولة.


الأسئلة المفتوحة الجديدة المجمّعة#

المعرّف السؤال التدفق معيق
OQ-20 ما طول فترة السماح في الاشتراك قبل إزالة الإدراج من الدليل؟ C-02 متوسط
OQ-21 هل الإدراج المميّز غير محدود أم نادر (N لكل فئة)؟ C-03 نعم — نموذج العمل
OQ-22 هل خفض درجة النجاح يُنجح المحاولات السابقة بأثر رجعي؟ C-07 متوسط
OQ-23 نموذج نسبة العمولة — نسبة ثابتة، متدرّجة، أم حسب الفئة؟ C-05 نعم — يعيق تصميم التسوية المالية
OQ-24 تواتر التسوية المالية — أسبوعي، شهري، عند الطلب؟ C-05 نعم
OQ-25 من يتحمّل رسوم بوابة الدفع — المنصة، أم المستشار، أم العميل؟ C-01, C-05 نعم

التتبعية — تغطية كاملة لـ BRD §7.1#

كل ميزة ضمن نطاق الـ MVP باتت الآن ذات تدفق موثّق.

مجموعة BRD §7.1 الميزات مغطّاة بـ
إدارة المستخدمين 9 ميزات F-01, F-02, F-03, Flow 1
التحقق من الحساب 5 ميزات Flow 2, F-04
وحدة الاستشارات 7 ميزات F-05, F-06, Flow 3
القوالب 4 ميزات F-07, F-08
التدريب 4 ميزات F-09
التقييمات 4 ميزات Flow 4
الشهادات 3 ميزات Flow 4
Marketplace 4 ميزات Flow 5, C-03, C-04
الدفع 4 ميزات C-01, C-02, C-03, F-08
الإدارة 8 ميزات C-06, C-07, C-08
(ضمنية) الإشعارات C-09
(ضمنية) Payouts C-05

الصفّان الأخيران جديران بالانتباه: الـ payouts والإشعارات لا تظهر إلا كتبعيات في SRC-BRD §13، ولم تُدرج قط كميزات ضمن النطاق — ومع ذلك تحمل الـ payouts أكبر خطر تنظيمي في المشروع (R-01)، والإشعارات حاملة للأحمال في كل تدفق تقريبًا. كلاهما يحتاج تحديد نطاق صريح.

القائد التقني · أمير هارون مسودة v0.1 · بحث وتصميم فقط