Skip to content

Vendor Onboarding & Subscriptions (SRS-VOS)

Purpose

Owns the path from "business wants to sell on Yam3at" to "verified, subscribed, revenue-generating vendor": business registration, commercial-license KYC, the admin approval state machine, subscription plans and their enforcement, plan payments via the PSP (KNET-first), renewals, proration, and subscription invoices. It does not own the vendor's public storefront content (that is Vendor Listings) or customer-side payments (Payments module, Part 2).

Vendor subscriptions are the platform's primary revenue stream.

Actors

Actor Description
Vendor applicant Authenticated user filling the business application.
Vendor owner Applicant after approval; manages subscription and billing.
Admin (Operations) Reviews applications, requests changes, approves/rejects.
Admin (Finance) Manages plan catalog and pricing, views subscription revenue, issues billing adjustments.
PSP MyFatoorah or Tap Payments, behind the payment-provider abstraction.
System Renewal jobs, grace-period expiry, limit counters.

User stories

  • As a business owner, I want to register my company with its license and category, so that I can start receiving event leads.
  • As a vendor applicant, I want to save my application as a draft and see exactly what is missing, so that I can complete it over several sittings.
  • As an operations admin, I want a review queue with the license document inline, so that I can approve or send back applications quickly with clear reasons.
  • As a vendor owner, I want to compare plans and pay with KNET, so that I can unlock more quotations and a featured listing.
  • As a vendor owner, I want upgrades to apply instantly with fair proration, so that I never pay twice for the same period.
  • As a vendor owner, I want a grace period if my renewal payment fails, so that one card issue doesn't take my business offline.
  • As a finance admin, I want every subscription charge to produce a numbered invoice, so that accounting is clean.

Functional requirements

Business registration

ID Requirement Phase
SRS-VOS-001 An authenticated, phone-verified user MUST be able to start a vendor application. The application is a multi-step wizard with autosaved Draft state: (1) business identity, (2) license & documents, (3) categories & service area, (4) contact & branches, (5) review & submit. MVP
SRS-VOS-002 Business identity fields: business_name_ar (required), business_name_en (required), legal_name (as on license), business_type (establishment/company/freelancer-license), description_ar/description_en (optional at this stage), logo (optional). MVP
SRS-VOS-003 License step: commercial_license_number (Kuwait MOCI license), license_expiry_date, and an upload of the license document (PDF/JPEG/PNG, ≤ 10 MB). Additional optional documents: signature authorization (اعتماد التوقيع), Civil ID of the authorized signatory. Documents are stored privately with signed-URL access for admins only. MVP
SRS-VOS-004 Category selection: at least 1 and at most 3 categories from the marketplace taxonomy (SRS-MKT-001). Primary category is explicit. MVP
SRS-VOS-005 Contact & location: business phone (+965), business email, optional website/Instagram handle, headquarters address (governorate, area, block/street free text, Google Maps pin). At MVP exactly one branch (the HQ) is captured; the schema supports N branches. MVP
SRS-VOS-006 Submission is blocked until all mandatory fields and the license document are present; the review screen lists every gap with a deep link to fix it. MVP
SRS-VOS-007 License expiry monitoring: the system MUST warn the vendor 30 days before license_expiry_date and flag the vendor to admins at expiry; an expired license suspends the ability to receive new RFQs until a renewed license is uploaded and re-verified. MVP
SRS-VOS-008 Multi-branch management (add/edit branches, per-branch service area and contact) Scale
SRS-VOS-009 Team members: invite staff by email/phone with roles vendor_manager (all except billing) and vendor_staff (RFQs + messages only); seat count limited by plan. Scale

KYC / verification & approval workflow

ID Requirement Phase
SRS-VOS-020 On submission the application enters Submitted and appears in the admin review queue (FIFO, filterable by category/age). Target first-response SLA: 2 business days, tracked and reported. MVP
SRS-VOS-021 Admin review checklist (recorded per application): license number matches document, license is valid (not expired), business activity on license plausibly matches selected categories, business name matches, contact reachable. Each item is checked off individually. MVP
SRS-VOS-022 Admin decisions: Approve, Reject (terminal, reason mandatory, re-application allowed after 30 days), or Needs Changes (returns the application to the vendor with a mandatory bilingual-capable comment; vendor edits and resubmits, unlimited cycles). MVP
SRS-VOS-023 On approval the system creates the vendor aggregate, assigns the applicant the vendor_owner role scoped to it, starts the Free plan automatically, and notifies the vendor (email + push). The vendor's public profile remains unpublished until listing content passes moderation (SRS-LST-060). MVP
SRS-VOS-024 All state transitions and decisions are audit-logged with admin identity and reason; the vendor-facing history shows dates and comments but not admin names. MVP
SRS-VOS-025 A verified badge ("موثّق / Verified") is displayed on approved vendors whose license is currently valid. MVP

Subscription plans

ID Requirement Phase
SRS-VOS-030 Four plans: Free, Basic, Professional, Enterprise, each with monthly and annual billing cycles (annual = 11 × monthly price, i.e. 1 month free — see Confirmed Decisions). Plan catalog (names AR/EN, prices, limits, feature flags) is data-driven and editable by Finance admins without deployment. MVP
SRS-VOS-031 Plan limits are enforced server-side in real time (see launch matrix below). Counters reset at the start of each billing period (vendor's anchor date), not the calendar month. MVP
SRS-VOS-032 When a limit is reached, the vendor action is blocked with an explanatory message and a one-tap upgrade CTA; incoming customer demand is never silently dropped — RFQs beyond the quota remain visible but locked ("upgrade to respond"). MVP
SRS-VOS-033 Grandfathering: price/limit changes to a plan apply to new subscriptions and to renewals occurring ≥ 30 days after the change announcement; active periods are never modified mid-cycle. MVP
SRS-VOS-034 Promo codes for subscription discounts (percent or fixed, first-cycle or recurring N cycles). Scale

Launch plan matrix (initial values — Finance-editable data, not hardcoded):

Limit / feature Free Basic Professional Enterprise
Price (KWD/month) 0.000 15.000 39.000 99.000
Price (KWD/year, 1 month free) 165.000 429.000 1089.000
Quotation responses / month 5 30 100 Unlimited
Gallery images 5 15 40 100
Videos on listing 0 1 5 15
Packages / services 3 10 30 Unlimited
Featured-listing eligibility ✔ (priority)
Analytics depth Views only Basic funnel Full funnel + trends Full + export
Team members (Scale) 1 2 5 15
Ad credits (Scale, KWD/month) 5.000 20.000
Support Standard Standard Priority Priority + account manager

Payment, renewal & billing

ID Requirement Phase
SRS-VOS-040 Plan checkout MUST support KNET, Visa/Mastercard, Apple Pay, and Google Pay via the PSP abstraction (MyFatoorah or Tap). Yam3at never handles raw card data (hosted/tokenized flows). Amounts are KWD with 3 decimals. MVP
SRS-VOS-041 Auto-renewal: card-tokenized subscriptions renew automatically. KNET does not support recurring charges, so KNET-paid subscriptions send renewal reminders at T-7, T-3, and T-0 days with a pay-now link; the vendor MAY store a card as backup for auto-renew. MVP
SRS-VOS-042 Failed/missed renewal starts a 7-day grace period: full plan features continue, banner + daily reminder shown. Retry schedule for tokenized cards: T+0, T+2, T+5. MVP
SRS-VOS-043 If grace expires unpaid, the subscription becomes Expired and the vendor is downgraded to Free limits immediately (content over Free limits is hidden, not deleted — restored on re-subscription). MVP
SRS-VOS-044 Upgrades apply immediately with proration: unused value of the current cycle (daily basis) is credited against the new plan's charge; the billing anchor date resets to the upgrade date. MVP
SRS-VOS-045 Downgrades take effect at the end of the current period (no mid-cycle refunds). If current usage/content exceeds the target plan's limits, the vendor is warned at scheduling time and over-limit content is hidden at switchover. A scheduled downgrade can be cancelled any time before it executes. MVP
SRS-VOS-046 Cancellation = scheduled downgrade to Free at period end. Subscription fees are non-refundable except where required by law or via Finance-admin goodwill refund (audit-logged). MVP
SRS-VOS-047 Every successful charge generates a sequential, immutable invoice (INV-YYYY-NNNNNN): vendor legal name, license number, plan, period, amount in KWD (3 decimals), payment method, PSP reference. Invoices are listed in the vendor dashboard and downloadable as PDF (AR/EN per locale). MVP
SRS-VOS-048 Webhook handling from the PSP MUST be idempotent (event id dedupe), signature-verified, and reconciled daily against PSP settlement reports; mismatches raise a Finance alert. MVP

Business rules

  1. One commercial license number = at most one active vendor account. Duplicate license submissions are auto-flagged to admins.
  2. A vendor may operate in at most 3 categories; changing categories after approval re-triggers a lightweight admin review of the affected categories only.
  3. The Free plan is permanent (no trial expiry) — it is the funnel, not a trial.
  4. Quotation quota consumption: a quota unit is consumed when the vendor sends a quote, not when an RFQ is received. Withdrawn quotes do not refund quota.
  5. Subscription state never affects existing confirmed bookings or open conversations — only new demand capture (quota-gated actions, search boost, featured placement).
  6. All plan prices are VAT-ready: an invoice tax line exists with rate 0 % for Kuwait at launch (Kuwait currently has no VAT), so KSA/UAE expansion (15 %/5 %) is a configuration change.
  7. Approval decisions, plan changes, refunds, and manual limit overrides are audit-logged with actor and reason.

Validation rules

Field Rule
business_name_ar 3–100 chars, Arabic letters/digits/spaces/common punctuation.
business_name_en 3–100 chars, Latin letters/digits/spaces/common punctuation.
commercial_license_number Numeric, 4–12 digits (MOCI license format tolerance); uniqueness enforced against active vendors.
license_expiry_date Must be a future date at submission.
License document PDF/JPEG/PNG, ≤ 10 MB, min 600 px shorter side for images; virus-scanned before admin exposure.
business_phone E.164; Kuwait ^\+965[124569]\d{7}$ (landlines allowed for businesses).
business_email RFC 5322, ≤ 254 chars.
Categories 1–3 selections; primary must be among them.
Maps pin Valid lat/lng within Kuwait bounding box at launch (28.5–30.1 N, 46.5–48.5 E).
Plan checkout amount Exactly equals catalog price ± proration credit; server-computed, never client-supplied.

Permissions matrix

Action Vendor applicant Vendor owner Vendor manager (Scale) Admin Ops Admin Finance Admin Super
Create/edit draft application
Submit / resubmit application
Review, approve, reject, request changes
View license documents own own
Choose/upgrade/downgrade/cancel plan ✔ (support override)
Pay subscription
View invoices
Edit plan catalog & pricing
Issue goodwill refund / manual override

Status / state machines

Vendor application (approval workflow):

stateDiagram-v2
    [*] --> Draft: applicant starts wizard
    Draft --> Submitted: submit (all mandatory data present)
    Submitted --> UnderReview: admin picks from queue
    UnderReview --> Approved: checklist passes
    UnderReview --> NeedsChanges: admin returns with comments
    UnderReview --> Rejected: reason recorded (terminal)
    NeedsChanges --> Submitted: vendor resubmits
    Approved --> [*]: vendor created, Free plan started
    Rejected --> [*]: re-application allowed after 30 days

Subscription lifecycle:

stateDiagram-v2
    [*] --> FreeActive: vendor approved
    FreeActive --> PaidActive: checkout success
    PaidActive --> PaidActive: renewal success / upgrade (proration)
    PaidActive --> Grace: renewal fails or not paid at T0
    Grace --> PaidActive: payment succeeds within 7 days
    Grace --> ExpiredToFree: grace ends unpaid
    PaidActive --> DowngradeScheduled: vendor downgrades or cancels
    DowngradeScheduled --> PaidActive: vendor cancels the schedule
    DowngradeScheduled --> FreeActive: period ends (to Free)
    ExpiredToFree --> PaidActive: vendor re-subscribes

Edge cases

  • License expires mid-subscription: subscription keeps billing; only new-RFQ intake is suspended (SRS-VOS-007). The vendor sees both banners independently.
  • Upgrade during grace period: allowed — payment of the new plan clears the grace state; proration base is zero (the unpaid period has no value to credit).
  • KNET payment succeeds but the redirect back drops (user closes browser): webhook is the source of truth; the subscription activates on webhook receipt and the vendor is notified. UI shows "payment processing" for pending references up to 15 min.
  • Double-click / duplicate checkout: idempotency key per checkout session; a second successful charge for the same period is auto-flagged for refund.
  • Downgrade scheduled, then limit-exceeding content added: warning recalculated and re-shown at execution; content hidden by lowest-quality-score-first order (SRS-LST-055).
  • Vendor rejected but already paid (paid before approval — not possible in the normal flow since plans start post-approval; if it occurs via support flow, auto-refund full amount).
  • Application submitted, then applicant deletes identity account: application auto-cancelled; documents purged with the identity per SRS-AUTH-061.
  • Two admins open the same application: optimistic locking — first decision wins, second admin gets a "state changed" refresh, never a silent overwrite.

Empty / loading / error states

Screen Empty Loading Error
Application wizard Fresh form with progress 0/5; autosave indicator Step-level skeletons; upload progress bars with cancel Field-level errors AR/EN; upload failures retryable without losing other fields
Admin review queue "No applications waiting 🎉" with SLA stats Skeleton table Retry banner; decision submit failures preserve entered comments
Plans page n/a (catalog always present) Price skeletons If catalog fetch fails, show cached last-known prices flagged "may be outdated", block checkout
Checkout n/a PSP redirect interstitial "Do not close this window" Failure screen with PSP reason mapped to friendly bilingual text + retry / change-method
Invoices list "No invoices yet — you're on the Free plan" Skeleton rows Retry; PDF generation failure falls back to HTML view

Acceptance criteria

  • A new vendor can go Draft → Submitted → Approved and land on the Free plan with vendor_owner role, receiving email + push at each transition.
  • Needs-Changes round trip works: vendor sees admin comments, edits only, resubmits; history preserved.
  • Duplicate license number is blocked with a clear message and admin flag.
  • Each launch-matrix limit is enforced server-side; hitting the quotation quota shows the upgrade CTA and locks (not hides) further RFQs.
  • KNET and card checkout complete end-to-end in the PSP sandbox; webhook idempotency proven by replaying events.
  • Upgrade mid-cycle charges exactly new_price − unused_daily_value (3-decimal KWD) and resets the anchor date; downgrade executes at period end and hides over-limit content.
  • Grace period: features intact for 7 days, retries at T+0/T+2/T+5 for tokenized cards, downgrade to Free at day 7 with content hidden not deleted.
  • Every charge yields a sequential PDF invoice in the vendor's locale with correct KWD formatting.
  • All approval decisions and plan changes appear in the audit log.

Data entities touched

vendor_applications, vendor_application_documents, vendor_application_reviews (checklist + decisions), vendors, vendor_branches, vendor_category (pivot), subscription_plans, plan_prices, subscriptions, subscription_usage_counters, subscription_schedules (downgrades/cancellations), invoices, invoice_lines, payment_transactions (shared with Payments), psp_webhook_events, audit_logs.

POST   /api/v1/vendor-applications
GET    /api/v1/vendor-applications/current
PATCH  /api/v1/vendor-applications/{id}
POST   /api/v1/vendor-applications/{id}/documents
POST   /api/v1/vendor-applications/{id}/submit
GET    /api/v1/admin/vendor-applications
GET    /api/v1/admin/vendor-applications/{id}
POST   /api/v1/admin/vendor-applications/{id}/decision     (approve|reject|needs_changes)
GET    /api/v1/subscription-plans
GET    /api/v1/vendors/{vendorId}/subscription
POST   /api/v1/vendors/{vendorId}/subscription/checkout
POST   /api/v1/vendors/{vendorId}/subscription/change      (upgrade|downgrade|cancel)
DELETE /api/v1/vendors/{vendorId}/subscription/schedule
GET    /api/v1/vendors/{vendorId}/invoices
GET    /api/v1/vendors/{vendorId}/invoices/{invoiceId}.pdf
POST   /api/v1/webhooks/psp                                (signature-verified)
GET    /api/v1/admin/subscriptions
PATCH  /api/v1/admin/subscription-plans/{planId}