PRD: Organizer & merchant
| Module | Commerce | PRD ID | PRD-ORG-001 |
| Status | Ready to dev | FEAT | ORG |
| Epic | — | Plane | BANA-1494 |
| Date | 2026-06-01 | Version | v1.0 |
| Packages | @nx/commerce | URD | ORG |
| Surface | BO · OpsClient · Owner/Mgr | ||
| Owner | Phát Nguyễn | ||
What organizer & merchant are
Everything in BANA hangs off a merchant - products, orders, inventory, finance. This PRD defines the business foundation for the whole system: one organizer for the brand, and at least one merchant for the legal selling unit, with its own sale channels and categories.
The organizer is virtual, the merchant is legal
The organizer is the brand: a multilingual name, a slug, a hierarchy, branding. It has no legal existence - no tax code, no e-invoice account, no wallet, no business type. Every legal and financial attribute - business type, tax identity, e-invoice details, finance accounts and wallets, currency, industry, physical location - belongs to the merchant, and never to the organizer. Every organizer always has exactly one headquarters merchant, created together with the organizer in the onboarding step. The full table of organizer/merchant principles lives in §8.1.
Why this feature exists
Building the structure piece by piece is slow and error-prone. A step that fails midway leaves a half-built business. Slug clashes only surface at save time. And there is no consistent rule for who may see which organizers and merchants. This feature covers it end to end: it stands the whole structure up in one step, saves a merchant as one package with its channels and categories, checks slugs before submit, and filters every list by the user's role.
One business, from sign-up to the first sale
A new owner onboards: the organizer, a default merchant, its sale channels, and access are created together in one step. The owner then adds more merchants, one at a time or together with their categories and channels. When something needs editing, the owner opens the Edit-merchant screen and changes the merchant details together with its sale channels and categories in a single save - either everything applies, or nothing changes. Each user who opens a launchpad afterward sees exactly the organizers and merchants their role permits: the owner sees their own business, an employee sees the merchants they are assigned to, and the Super Admin sees everything.
The easiest thing to get wrong
The merchant carries the tax code, the organizer does not
The organizer is a brand, not a legal seller. Tax identity, e-invoice accounts, wallets, and business type all belong to the merchant. One brand can cover several merchants with different tax registrations - confusing the two roles means confusing where every legal and financial attribute of the business actually lives.
1. Goals & Non-Goals
Goals
- One-step onboarding that creates the organizer, a default merchant, its sale channels, and owner access together - all of it or none of it.
- Save a merchant together with its categories and sale channels in one step. Within that save, an item sent with its details is updated, a new item is added, and an item marked for removal is removed.
- A pre-submit slug check confirming uniqueness - global for organizers, per-organizer for merchants.
- Role-filtered launchpads (summaries and overviews, including how many users hold each role) so each user sees only the organizers and merchants their role permits.
- A reworked organizer back-office (create/edit pages, role visibility).
- Organizer vs merchant principles - the organizer stays a virtual brand; every legal and financial attribute belongs to a merchant, never to the organizer.
Non-Goals
- Cross-organizer access or sharing merchants between organizers.
- Ownership transfer (Planned).
- Re-designing standalone sale-channel / category management - those standalone paths already exist (see PRD-SC-001 / PRD-CAT-001); this PRD covers onboarding and the combined merchant save.
- Permanent deletion - everything is deactivated or archived and can be restored.
2. Success Metrics
| Metric | Target / signal |
|---|---|
| Onboarding success | Every completed onboarding yields an organizer, default merchant, channels, and owner access - or leaves nothing behind |
| Slug integrity | No duplicate organizer slugs globally; no duplicate merchant slugs within an organizer |
| Save integrity | No partially-saved merchant updates (no leftover channels or categories) |
| Isolation between businesses | No data of one business visible to another through role-filtered lists |
| Onboarding time | Median time from sign-up to a usable merchant trends down |
3. Personas & Scenarios
| Persona | Goal |
|---|---|
| Business Owner | Onboard a business, manage the organizer and its merchants, control channels and categories |
| Manager | Manage the merchants in scope and their channels and categories |
| Employee | View only the merchants they are assigned to |
| Super Admin | See all organizers and merchants (no role filtering) |
Core scenario: a new owner onboards (organizer + default merchant + channels + access in one step) → adds more merchants one at a time or together with their categories and channels → edits a merchant and its channels and categories in a single save → each user opens a launchpad showing exactly the organizers and merchants their role permits.
4. User Stories
| # | As a | I want | So that |
|---|---|---|---|
| 01 | business owner | onboarding to create my organizer, a default merchant, its sale channels, and my owner access in one step | I can start selling without setting up each piece by hand |
| 02 | business owner | to add, change, or remove a merchant's categories and sale channels in a single save | the whole merchant is reconciled at once and never left half-applied |
| 03 | business owner | slug uniqueness checked before I submit | I never lose a form to a late clash |
| 04 | employee | my launchpad to show only the merchants I am assigned to | I never see another business's data |
| 05 | super admin | to see every organizer and merchant | I can support and audit across businesses |
5. Functional Requirements
| # | Requirement | Status | URD ref |
|---|---|---|---|
FR-001 | Onboarding creates organizer + default merchant + sale channels + owner access together, all-or-nothing. The creating user becomes Owner. | ✅ | URD-ORG-001..003 · URD-MER-001 · URD-SC-001 |
FR-002 | Organizer requires a multilingual name and a slug. A non-editable identifier is assigned on creation. | ✅ | URD-ORG-004..005 |
FR-003 | Organizer and merchant can be found by system id, falling back to slug (the displayed business code is not used for lookup). Owner can update the organizer profile. | ✅ | URD-ORG-006..007 · URD-MER-009 |
FR-004 | The Edit-merchant screen supports file attachments on the organizer. | ✅ | URD-ORG-008 |
FR-005 | Owner can create a merchant one at a time, or together with its categories and sale channels. | ✅ | URD-MER-002..004 |
FR-006 | Merchant slug is unique within its organizer. A slug check confirms organizer/merchant uniqueness before submit. | 🔶 | URD-MER-005 · URD-ORG-004 |
FR-007 | A merchant can be viewed with its channels and categories. Owner can update merchant information. | ✅ | URD-MER-006..007 |
FR-008 | On the Edit-merchant screen, an item with its details is updated, a new item is added, and an item marked for removal is removed. Removal is kept distinct from "leave unchanged". | ✅ | URD-MER-008 · URD-MER-015 |
FR-009 | Organizer and merchant lists, and their totals, are role-filtered (Admin sees all). | ✅ | URD-ORG-009 · URD-MER-010..011 · URD-ACC-004..005 |
FR-010 | Owner sees only their own organizer and merchants. • Owner can create, view, and update them. • The launchpad role filter rejects an invalid value (regular lists for a user with no grants return empty). | ✅ | URD-ACC-006..007 · URD-ACC-012 |
FR-011 | Launchpads return role-filtered summaries and overviews, including how many users hold each role. | ✅ | URD-ORG-009 · URD-MER-010 |
FR-012 | Organizer/merchant can be deactivated, reversibly. • The headquarters merchant can be re-designated. • Organizer/merchant can be archived read-only. | 🔶 | URD-ORG-010..012 |
FR-013 | All legal and financial attributes belong to the merchant - business type, tax identity, e-invoice details, finance accounts and wallets, currency - and never to the organizer. | ✅ | URD-MER-016 |
FR-014 | Every organizer has exactly one headquarters merchant - created at onboarding and recorded on the organizer in the same step. | ✅ | URD-MER-017 |
FR-015 | A merchant's sectors must be valid business sectors. Onboarding sets up the default sale channels and category templates for those sectors. | ✅ | URD-MER-018 |
FR-016 | The merchant is where tax identity is entered. The e-invoice profile then reuses it automatically, with no re-entry. | ✅ | URD-MER-019 |
FR-017 | Two onboarding paths exist. • Self-service onboarding runs as a single all-or-nothing operation and grants the calling user Owner with no license. • Administrative (aggregate) onboarding provisions a new owner user plus a license. | ✅ | URD-ORG-014..015 |
FR-018 | Administrative onboarding is a two-phase operation. • Phase 1 creates the organizer, headquarters merchant, and defaults locally. • Phase 2 provisions the owner user, owner access, and license. • On a phase-2 failure, the system compensates in reverse on a best-effort basis (each rollback step is logged). | ✅ | URD-ORG-015 |
FR-019 | Administrative onboarding is idempotent through an optional idempotency key supplied with the request. • On a repeat call after completion, the key replays the same result. • On a concurrent retry with the same key, the second call is rejected as in-progress. • A request without a key is not protected. | ✅ | URD-ORG-016 |
FR-020 | The license is issued against the new owner user. Its activation / contract code is echoed back to the caller, never stored. | ✅ | URD-ACC-013 |
FR-021 | Onboarding auto-creates a default set. • Exactly one headquarters merchant (derived slug, "Headquarter" name, headquarter flag, direct tax method). • Industry-specific default sale channels (F&B: Offline + Take away; Retail: Counter + Online; ticket / other: none). • Industry-specific category templates (F&B and Retail; ticket / other receive none). | ✅ | URD-ORG-017 · URD-MER-020 |
FR-022 | The owner grant is recorded as three access rows - membership in the organizer, membership in the merchant, and assignment of the Owner role. • The self-service path writes exactly three rows. • The administrative path reaches the same result with two membership rows plus the role assigned at owner-user creation. | ✅ | URD-ACC-014 |
FR-023 | Merchant default values cover three fields: business type, industry, and currency. • Business type: Household default / Business. • Industry: Retail / F&B default / Ticket / Other. • Currency: default VND. | ✅ | URD-MER-021 |
FR-024 | Onboarding progress is six ordered steps on the merchant (merchant details → sale channel → finance account → tax information → products → employees). • Each step's status is derived at read time from the data that exists, with no stored flags. • Deleting the data of a step returns it to not-complete. • The launchpad surfaces this as completed / total. | ✅ | URD-MER-022 |
FR-025 | Slug uniqueness is removal-aware - a removed slug is freed for reuse. • Administrative onboarding auto-appends a unique identifier suffix on slug collision. • There is no maximum number of merchants per organizer. | ✅ | URD-MER-023 |
FR-026 | Organizer / merchant list, count, and find results are filtered in the application from the caller's access grants (the central authorization engine does not currently gate these reads). | ✅ | URD-ACC-016 |
FR-027 | Administrative merchant creation branches on the caller. • A back-office admin scopes Owner to the new merchant. • An organizer owner links themselves. | ✅ | URD-ACC-015 |
FR-028 | Changing the headquarters merchant emits an event signalling the headquarters merchant changed to downstream consumers. | ✅ | URD-ORG-018 |
FR-029 | Organizer / merchant lists support free-text search and an excluded mode for the access-grant pickers. | ✅ | URD-ORG-009 · URD-MER-010 |
FR-030 | The merchant screen shows a six-counter summary. The organizer launchpad remembers and shows each entry's last visited time. | ✅ | URD-MER-010 |
5.1 Two onboarding paths
Onboarding has two distinct entry points with different ownership, completion, and provisioning models. Earlier PRD versions described only the self-service path. The Back Office screen of the administrative path is specified separately in PRD-BOP-001; this PRD keeps the model shared by both surfaces.
| Aspect | Self-service onboarding | Administrative (aggregate) onboarding |
|---|---|---|
| Driven by | The signing-up user (no organizer yet) | A back-office operator provisioning a client |
| Owner | The calling user becomes Owner | A new owner user is created |
| Atomicity | Single local all-or-nothing operation | Two-phase process: local phase 1 + remote phase 2, with compensation |
| License | None | A license is issued against the new owner user |
| Idempotency | None | An idempotency key (optional, supplied with the request) - replay completed, reject in-progress; without a key the call is unprotected |
| Request shape | The organizer name and slug plus the merchant's industry and optional sectors | Organizer + merchant + a new owner's details + license intent |
Both paths produce the same default structure (§5.2) and the same three-row owner access (FR-022).
5.2 Auto-created defaults
Every onboarding (either path) provisions a fixed, non-configurable default set:
| Default | Rule |
|---|---|
| Headquarters merchant | Exactly one, slug derived from the organizer slug, name "Headquarter", marked as headquarters, direct tax method; recorded on the organizer as its headquarters merchant |
| Sale channels | Industry-specific - F&B: Offline + Take away; Retail: Counter + Online; ticket / other: none - with slugs derived from the merchant slug |
| Category templates | F&B and Retail industries - default categories from the system category templates (matched by sector); ticket and other industries receive none |
| Owner grant | Three access rows - membership in the organizer, membership in the merchant, assignment of the Owner role (FR-022) |
5.3 Onboarding progress steps
After the structure exists, the merchant tracks six ordered onboarding steps, surfaced in the merchant launchpad as a completed / total count:
merchant details → sale channel → finance account → tax information → products → employees
The launchpad reports how many of the six are complete so an owner can see what remains before the merchant is fully set up.
5.4 Acceptance criteria
- Self-service onboarding: completing it yields the organizer + headquarters merchant + industry channels + three owner-access rows; forcing a failure at any step leaves no records behind.
- Administrative onboarding: re-calling with the same idempotency key after completion returns the stored result with no extra provisioning; two concurrent calls with the same key → the second is rejected as in-progress; a phase-2 failure revokes the license, deletes the owner user, and removes the merchant and organizer (verify the per-step compensation log).
- Combined merchant save: an item with details is updated; a new item is added; an item sent with only its id (removal marker) is removed; an item absent from the payload is left unchanged; a mid-save failure saves nothing.
- Slugs: creating an organizer with a duplicate slug is rejected; creating a merchant with a duplicate slug within the same organizer is rejected; a removed merchant frees its slug; administrative onboarding on a slug collision auto-suffixes and still succeeds.
- Launchpads & isolation: an employee sees only assigned merchants; an owner only their own organizers/merchants; Super Admin sees all; an invalid role value on the launchpad filter is explicitly rejected.
- Six steps: completing a step's data raises the completed count; deleting that data lowers it again (derived at read time, no stored flags).
- The pre-submit slug check in the UI rejects duplicate slugs before submit; the deactivate action works for organizers and merchants; the headquarters merchant can be re-designated from the UI; the server blocks writes to archived records.
6. Non-Functional Requirements
| Area | Requirement |
|---|---|
| All-or-nothing | Self-service onboarding and every combined merchant save complete fully or leave nothing behind; administrative onboarding is a two-phase process with best-effort compensation (§5.1) |
| Data integrity | Slug uniqueness is enforced (global for organizers, per-organizer for merchants, per-merchant for channels); nothing is permanently deleted - items are deactivated or archived and can be restored |
| Isolation & access | Every list, total, and change is role-filtered; owners see only their own organizer and merchants, employees only assigned merchants; Admin sees all; an unrecognized role is denied |
| Identity | Identifiers are assigned on creation and never change |
| Languages | Organizer/merchant names and user-facing labels are shown in multiple languages |
| Source of truth | Commerce is the source of truth. Two concrete flows propagate to invoicing: the merchant tax information is synced to the e-invoice profile on every save, and re-designating the headquarters merchant emits a notification to invoicing |
7. UX & Flows
Administrative aggregate onboarding - two-phase process & compensation. The administrative path spans three capabilities and compensates in reverse on a best-effort basis if the remote phase fails - each rollback step is logged so operations can handle the (rare) leftovers - so a half-provisioned client is not left behind.
Key screens live in the owner's admin app (client) and the Back Office: organizer create/edit pages, the merchant create/edit form, role-filtered organizer and merchant launchpads, and the merchant settings sections (languages, e-invoice, payment, finance wallet). For the Back Office onboarding screen, see PRD-BOP-001.
8. Data & Domain
| Concept | Role |
|---|---|
| Organizer | The virtual brand - multilingual name, globally-unique slug, status, place in a hierarchy |
| Merchant | The legal selling unit under an organizer - slug (unique per organizer), business type, currency, status |
| Sale channel | A selling channel within a merchant (dine-in, takeaway, delivery); slug unique per merchant |
| Category | A product grouping within a merchant |
| Access | A role-scoped grant of an organizer or merchant to a user; drives every role-filtered list |
Conceptual only - full data details and rules in the commerce domain model.
8.1 Organizer vs merchant - the principles
| Principle | Rule |
|---|---|
| Org is virtual | The organizer is a brand: name, slug, hierarchy, branding. It has no legal existence - no tax code, no e-invoice account, no wallet, no business type. |
| Merchant is legal | Every legal and financial attribute belongs to a merchant: business type (household or enterprise, which drives invoice rules), tax identity (tax code + registered address), e-invoice details, finance accounts and wallets, currency, industry, and physical location. |
| One headquarters merchant per org | Onboarding creates the organizer and its headquarters merchant together; an organizer never exists without at least this merchant. |
| Identity & slugs | A merchant's identifier is assigned on creation and never changes. Its slug is unique within the organizer among active merchants - removing a merchant frees its slug for reuse. |
| Sector-driven defaults | A merchant's sectors must be valid business sectors; onboarding sets up the default sale channels and the category templates for those sectors. |
| Ownership | The person who onboards becomes the Owner of the new organizer and its merchant. One user may own several organizers; a merchant never moves between organizers. |
| Hierarchy | Merchants may form a parent-child hierarchy within their organizer - child merchants under a flagship merchant, never across organizers. |
| Tax identity flows down | The tax code is entered once on the merchant and the e-invoice profile reuses it automatically - editing it on the merchant is the only supported path. |
9. Dependencies & Assumptions
Depends on
| # | Feature | What is depended on |
|---|---|---|
| 01 | User Management (CORE-01) | the owner and employees belong to the organizer and its merchants. |
| 02 | Permissions (CORE-02) | roles are scoped to an organizer or merchant; onboarding grants the owner access. |
Assumptions
| # | Assumption | What breaks if it is wrong |
|---|---|---|
| 01 | An authenticated user with no organizer drives onboarding. | A user who already has an organizer but still goes through onboarding gets a duplicate organizer/merchant instead of being routed into the structure they already have. |
| 02 | A set of roles exists so launchpads and lists can be filtered by role (with an Admin who sees all). | A missing or incomplete role set, or a missing Admin-sees-all role, leaves launchpads and lists with no basis to filter correctly - leaking data across businesses or wrongly blocking someone with a valid grant. |
| 03 | Slug input is normalized to a URL-friendly form before the uniqueness check. | Skipping or getting the normalization step wrong compares two strings in different forms, letting through slugs that clash on the actual URL but differ in punctuation, case, or whitespace. |
10. Release Plan & Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 for ORG / MER / ACC (per the URD feature catalog) |
| Rollout | All merchants; no feature flag |
| Migration | None - works on existing data; nothing is permanently deleted |
| Launch criteria | Onboarding creates organizer + merchant + channels + access all-or-nothing; the Edit-merchant screen adds, updates, and removes merchant items correctly; the slug check rejects duplicates; launchpads are role-filtered with no leakage between businesses |
| Monitoring | Onboarding success and failure rate, partial-save failures, slug-clash rejections, role-filter coverage |
References
- URD: Commerce - Organizer · Merchant · Access
- Builds on: Sale Channels · Categories
- Module: Commerce - URD
- Developer: @nx/commerce · domain model
Risks & Open Questions
| # | Risk / question | Mitigation / status |
|---|---|---|
| 01 | Partial failure mid-onboarding leaves a half-built business | Self-service: all-or-nothing. Administrative: two phases + best-effort compensation with a per-step log |
| 02 | The Edit-merchant screen removes the wrong item and drops data | Removal is an explicit marker, kept distinct from "leave unchanged"; the save is all-or-nothing |
| 03 | A slug clash surfaces only at submit | A dedicated slug check validates before submit |
| 04 | An invalid role value on the launchpad filter | Explicitly rejected; regular lists for a user with no grants return empty (by design) |
| 05 | No ownership transfer | Out of scope (Planned); documented as a constraint |
Frequently asked questions
| # | Question | Answer |
|---|---|---|
| 01 | Does onboarding create everything in one shot? | Self-service: yes - organizer, headquarters merchant, sale channels, and owner access are created all-or-nothing; nothing is saved on failure. The administrative path runs two phases with best-effort compensation (see §5.1). |
| 02 | How does the Edit-merchant screen decide add vs. update vs. remove? | An item with its details is updated, a new item is added, and an item marked for removal is removed - see this rule in §5. |
| 03 | Why check the slug before submit? | So a form is never lost to a clash discovered late. |
| 04 | Why do two users see different launchpads? | Every list is role-filtered - an owner sees only their own organizer and merchants, an employee only assigned merchants. Super Admin sees all. |
| 05 | Can a merchant be moved to another organizer? | No - cross-organizer sharing and ownership transfer are out of scope (Planned). |
| 06 | Why doesn't the organizer carry a tax code? | Because the organizer is a brand, not a legal seller. Tax identity, e-invoice accounts, wallets, and business type all belong to the merchant. One brand can cover several merchants with different tax registrations. |
| 07 | Can one user own several organizers? | Yes. What is fixed is the other direction: a merchant belongs to exactly one organizer, forever. |
| 08 | Why are there two onboarding paths? | Self-service onboarding sets the calling user up as Owner in one all-or-nothing operation with no license. Administrative onboarding (from the back office) provisions a brand-new owner user and a license, so it runs as a two-phase process that compensates fully if the remote phase fails. See §5.1. |
| 09 | What does the idempotency key do? | It makes administrative onboarding safe to retry - a completed request replays its stored result, and a concurrent retry of the same key is rejected as still in-progress, so a client is not provisioned twice. The key is optional - a call without one is not protected against double provisioning. See FR-019. |
| 10 | Is there a cap on merchants per organizer? | No - an organizer can hold any number of merchants (FR-025). |