@mercurjs/docs 2.3.0 → 2.3.2-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,286 +1,336 @@
1
1
  ---
2
2
  title: "Architecture"
3
- description: "How the Mercur enterprise marketplace platform is built: its layers, building blocks, and how the pieces fit together."
3
+ description: "How Mercur is put together: the runtime layers, the boundary between the marketplace platform and your own code, and the zones you extend."
4
4
  ---
5
5
 
6
- Mercur is the open-source enterprise marketplace platform, built on
7
- [Medusa](https://medusajs.com). It is composable, API-first, and AI-native, and
8
- it runs on infrastructure you own.
6
+ Mercur is an open-source marketplace platform. It gives you a multi-vendor
7
+ commerce backend, an admin panel, and a vendor portal that you deploy on your own
8
+ infrastructure and change at the source level.
9
9
 
10
- Mercur is not a standalone application, and it is not something you assemble from
11
- scratch. Medusa provides the commerce engine, such as products, pricing, carts,
12
- orders, payments, and fulfillment. Mercur adds the marketplace layer on top:
13
- sellers, commissions, order splitting, payouts, and a governed change pipeline,
14
- along with an admin panel and a vendor portal. Operators run the marketplace with
15
- role-based access control and an auditable change history, on a codebase they own
16
- outright.
10
+ It is not a hosted service you integrate with, and it is not a starter kit you
11
+ finish yourself. Mercur is a running system on day one, with defined seams for
12
+ the parts every marketplace does differently: onboarding rules, commission logic,
13
+ approval policy, back-office integrations, and the screens your teams work in.
17
14
 
18
- This page explains how the platform is structured and how its parts fit together.
15
+ This page describes those layers and those seams. If you are evaluating Mercur,
16
+ the two questions it answers are **what runs where** and **where does my code go**.
19
17
 
20
- ## High-level architecture
18
+ ## Design principles
21
19
 
22
- Mercur is layered. Each layer owns one responsibility and talks only to the layer
23
- beneath it, so you can reason about, extend, or replace any layer on its own.
20
+ Everything below follows from four decisions.
24
21
 
25
- ```mermaid
26
- graph TD
27
- subgraph Frontend Layer
28
- A[Admin Panel]
29
- B[Vendor Portal]
30
- C[Storefront]
31
- end
22
+ - **You run it.** MIT-licensed, PostgreSQL, Node. No vendor in the request path, no GMV fee, no proprietary runtime. Self-host, or deploy to any cloud that runs a Node process and a database.
23
+ - **Extension over configuration.** Instead of hundreds of settings, the platform exposes typed extension zones: workflow hooks, API routes, modules, custom fields, pages, and widgets. When a zone isn't enough, the source is yours to change.
24
+ - **Marketplace on top of commerce, not instead of it.** Products, carts, orders, payments, and fulfillment are handled by [Medusa](https://medusajs.com), a mature commerce engine. Mercur adds only what multi-vendor requires: sellers, offers, order splitting, commissions, payouts, and governance.
25
+ - **Governance is structural.** Role scoping, the product change pipeline, and exact-precision money arithmetic are properties of the architecture, not features you enable.
32
26
 
33
- subgraph API Layer
34
- D["/admin/*"]
35
- E["/vendor/*"]
36
- F["/store/*"]
37
- end
27
+ ## The layers
28
+
29
+ Mercur is a single deployable backend with three API surfaces, plus two panel
30
+ applications that are ordinary clients of those APIs.
38
31
 
39
- subgraph Marketplace Layer - Mercur
40
- G[Modules · Workflows · Links · Subscribers · Events]
32
+ ```mermaid
33
+ graph TD
34
+ subgraph Clients
35
+ A[Admin Panel<br/>React]
36
+ B[Vendor Portal<br/>React]
37
+ C[Storefront<br/>any framework]
38
+ D[Your services<br/>ERP · PIM · agents]
41
39
  end
42
40
 
43
- subgraph Commerce Layer - Medusa
44
- H[Products · Orders · Carts · Payments · Fulfillment]
41
+ subgraph "Backend one Node process"
42
+ E["/admin/*"]
43
+ F["/vendor/*"]
44
+ G["/store/*"]
45
+ H[Marketplace domain<br/>sellers · offers · commissions · payouts · governance]
46
+ I[Commerce domain<br/>products · carts · orders · payments · fulfillment]
45
47
  end
46
48
 
47
- I[(PostgreSQL)]
49
+ J[(PostgreSQL)]
50
+ K[Redis · event bus, workflow engine, cache]
51
+ L[Providers<br/>payment · payout · fulfillment · notification · search]
48
52
 
49
- A --> D
50
- B --> E
51
- C --> F
52
- D --> G
53
- E --> G
54
- F --> G
53
+ A --> E
54
+ B --> F
55
+ C --> G
56
+ D --> E
57
+ E --> H
58
+ F --> H
55
59
  G --> H
56
60
  H --> I
61
+ I --> J
62
+ H --> J
63
+ H --> K
64
+ H --> L
57
65
  ```
58
66
 
59
- ### Commerce layer
67
+ ### Data
68
+
69
+ One PostgreSQL database. Every domain owns its own tables and never reaches into
70
+ another's; relationships across domains are declared explicitly as links (see
71
+ [Links](#links)). That isolation is what makes a domain replaceable, and what
72
+ keeps a schema change local instead of platform-wide.
60
73
 
61
- Medusa provides the core commerce engine: products, pricing, carts, orders,
62
- payments, fulfillment, promotions, and inventory. Mercur does not replace any of
63
- it. Mercur builds on top through Medusa's extension model, using custom modules,
64
- links, workflows, and API routes. This is the one place the word framework
65
- applies. Medusa is the commerce framework, and Mercur is the platform you run on
66
- it.
74
+ Redis backs the event bus, the workflow engine, and caching in production. In
75
+ development both fall back to in-memory implementations, so a laptop needs
76
+ nothing but Postgres.
67
77
 
68
- ### Marketplace layer
78
+ ### Domain
69
79
 
70
- This is where Mercur's own code lives, packaged as the `@mercurjs/core` plugin. It
71
- adds marketplace modules such as Seller, Commission, Offer, Payout, Product
72
- Attribute, and Product Edit, plus the workflows that coordinate marketplace
73
- operations like order splitting, product approvals, and commission calculation.
74
- Links connect these modules to Medusa's core entities without modifying the
75
- original models.
80
+ The domain layer is where marketplace behaviour lives: seller accounts and
81
+ members, offers against a shared product catalog, commission rules and their
82
+ resolution, payout accounts and transfers, order groups, the product change
83
+ pipeline. It calls into the commerce domain for anything commerce already does.
76
84
 
77
- ### API layer
85
+ Two things about this layer matter to an architect:
78
86
 
79
- Mercur exposes three sets of HTTP endpoints, one per audience.
87
+ - **It is not a wrapper.** Mercur does not proxy or re-implement commerce endpoints. Marketplace concepts are first-class records with their own lifecycle, joined to commerce records through links.
88
+ - **Your own domains sit beside it.** A module you write, a module Mercur ships, and a module Medusa ships are the same kind of object, registered the same way, with the same access to the container, the event bus, and the workflow engine. There is no privileged inner ring.
80
89
 
81
- | API | Path | Purpose |
82
- | ---------- | ----------- | -------------------------------------------------------------------------- |
83
- | **Admin** | `/admin/*` | Platform administration: manage sellers, configure commission rates, view payouts. |
84
- | **Vendor** | `/vendor/*` | Seller operations: manage products, orders, fulfillment, shipping, inventory, payouts. |
85
- | **Store** | `/store/*` | Storefront: browse sellers, manage carts, check out with order splitting. |
90
+ ### API
86
91
 
87
- Each route is composed of a request handler, middleware, query configuration, and
88
- Zod validators. The middleware is where access control lives, so every vendor
89
- request is scoped to its own seller's data before the handler runs. See the
90
- [API conventions](/references/api/conventions) for authentication and scoping.
92
+ Three HTTP surfaces, one per audience, separated because their authorization
93
+ models differ, not because their data does.
91
94
 
92
- ### Panels and clients
95
+ | Surface | Path | Audience | Scoping |
96
+ | ---------- | ----------- | --------------------- | ------------------------------------------------------------------ |
97
+ | **Admin** | `/admin/*` | Marketplace operators | Platform-wide, gated by role |
98
+ | **Vendor** | `/vendor/*` | Sellers | Every request is narrowed to the caller's seller before the handler |
99
+ | **Store** | `/store/*` | Customers, storefront | Public catalog and the caller's own cart, orders, and account |
93
100
 
94
- Three interfaces consume the APIs.
101
+ A route is a thin adapter: middleware authenticates and scopes, a Zod validator
102
+ checks the payload, and the handler runs a workflow for writes or the query
103
+ engine for reads. Business logic does not live in routes, which is why adding
104
+ one is cheap and overriding one is safe. See the
105
+ [API conventions](/references/api/conventions).
95
106
 
96
- - **Admin Panel:** a React application on Medusa UI. Operators approve sellers, set commission rates, and monitor payouts across the whole marketplace.
97
- - **Vendor Portal:** a React application for sellers to manage products, orders, fulfillment, and payouts, scoped to their own store.
98
- - **Storefront:** the customer-facing application. Build it with any frontend that consumes the Store API.
107
+ ### Clients
99
108
 
100
- Both panels talk to the API through `@mercurjs/client`, a fully typed fetch
101
- wrapper generated from the real route definitions, so requests and responses stay
102
- in sync with the backend.
109
+ The admin panel and the vendor portal are separate React applications, not a
110
+ templated back office. They talk to the backend through `@mercurjs/client`, a
111
+ typed fetch wrapper generated from the real route definitions — including routes
112
+ you add — so a backend change that breaks a caller fails at `tsc`, not in
113
+ production.
103
114
 
104
- ## Building blocks of the marketplace layer
115
+ The storefront is deliberately not shipped as a fixed application. Anything that
116
+ speaks HTTP consumes the Store API; a reference Next.js storefront is available
117
+ to start from.
105
118
 
106
- The marketplace layer is assembled from four Medusa-native primitives. Together
107
- they keep the platform composable: each piece is small, explicit, and replaceable.
119
+ ## The building blocks
120
+
121
+ Four primitives compose the domain layer. You use the same four to extend it.
108
122
 
109
123
  ### Modules
110
124
 
111
- A module encapsulates the data models and business logic for one domain, such as
112
- Seller or Commission. Each module is self-contained, with its own models, service,
113
- and migrations. Modules never reference each other directly. They communicate
114
- through links and workflows, which keeps domains decoupled.
115
- [Learn about modules](/resources/best-practices/modules).
125
+ A module owns one domain: its data models, its service, its migrations. Modules
126
+ never import each other. That constraint is what lets you swap Mercur's
127
+ commission logic for your own, or drop a module you don't use, without a
128
+ refactor rippling outward. [Modules →](/resources/best-practices/modules)
116
129
 
117
130
  ### Links
118
131
 
119
- A link defines a relationship between a Mercur module and a Medusa core entity
120
- without modifying either model. For example, the product-seller link connects a
121
- Medusa `Product` to a Mercur `Seller` and acts as the allowlist of who may sell
122
- what. Dozens of links wire the marketplace layer into the commerce layer.
123
- [Learn about module links](/resources/best-practices/module-links).
132
+ A link declares a relationship between records in two modules without either
133
+ module knowing about the other. The productseller link, for example, is what
134
+ allowlists which sellers may sell a given product expressed as a link rather
135
+ than a foreign key, so neither the product model nor the seller model is
136
+ modified. Your modules link to built-in entities exactly the same way.
137
+ [Links →](/resources/best-practices/module-links)
124
138
 
125
139
  ### Workflows
126
140
 
127
- A workflow orchestrates a multi-step operation that spans modules. Workflows
128
- support compensation, which rolls back automatically on failure, and hooks, which
129
- are the extension points you inject custom logic into. The central one is
130
- `completeCartWithSplitOrdersWorkflow`, which validates a cart, splits it by
131
- seller, creates an order for each, allocates payment, and calculates commissions.
132
- [Learn about workflows](/resources/best-practices/workflows).
141
+ A workflow is a multi-step operation with automatic compensation: if step five
142
+ fails, steps one through four roll back. Anything that crosses domains or must
143
+ not half-happen is a workflow seller approval, payout transfer, and above all
144
+ cart completion, which validates the cart, splits it by seller, creates an order
145
+ each, allocates payment, and computes commission lines as one atomic unit.
146
+
147
+ Workflows also expose **hooks**, which is the primary backend extension zone.
148
+ [Workflows →](/resources/best-practices/workflows)
149
+
150
+ ### Events and subscribers
133
151
 
134
- ### Subscribers and events
152
+ Workflows emit events; subscribers react asynchronously. Notifications, webhooks
153
+ to your systems, search indexing, and payout side effects all hang off this,
154
+ which keeps the transactional path short and lets you add behaviour without
155
+ touching the workflow that triggered it.
156
+ [Subscribers and jobs →](/resources/best-practices/subscribers-and-jobs)
135
157
 
136
- Workflows emit events. Subscribers listen and run asynchronous side effects, such
137
- as sending notifications, calling webhooks, or transferring payouts. This keeps
138
- the core workflows focused while the platform reacts to change.
139
- [Learn about subscribers and jobs](/resources/best-practices/subscribers-and-jobs).
158
+ ## Extension zones
140
159
 
141
- ## Enterprise governance by design
160
+ This is the part that determines what a build actually costs. Mercur exposes six
161
+ zones. Each one is typed, each one is additive, and none of them require forking
162
+ or patching platform code.
142
163
 
143
- Governance lives in the architecture, not in a bolt-on. The same primitives that
144
- make the platform composable also make it governable.
164
+ ```mermaid
165
+ graph LR
166
+ subgraph Backend
167
+ A[Workflow hooks<br/>inject steps into existing flows]
168
+ B[API routes<br/>add or override endpoints]
169
+ C[Modules and links<br/>add domains]
170
+ D[Custom fields<br/>extra data on built-in entities]
171
+ end
172
+ subgraph Panels
173
+ E[Pages<br/>new routes]
174
+ F[Widgets<br/>components in named zones]
175
+ G[Custom field UI<br/>forms · rows · columns]
176
+ H[Navigation<br/>reorder · relabel · hide]
177
+ end
178
+ ```
145
179
 
146
- - **Role-based access control.** `withMercur()` registers a roles module, so vendor requests are scoped to their own seller by default. Operators and sellers each see only what their role permits.
147
- - **An auditable change pipeline.** Every product edit is captured as an immutable `ProductChange` record: who changed what, and who approved it. Low-risk edits auto-confirm, and the rest wait for operator review.
148
- - **Financial accuracy.** All commission arithmetic uses BigNumber with arbitrary precision, so split payments and payouts stay exact to the cent.
149
- - **A governed surface for AI agents.** The typed client, exposed workflows, and `llms.txt` give AI agents structured contracts to build against, inside the same role and review guardrails as human users. Agents extend the platform. They do not bypass its governance.
150
- - **You own the deployment.** Mercur is MIT-licensed and runs on infrastructure you control. Blocks ship as source code, so you own every line, with no hosted vendor in the request path and no commission on gross merchandise value.
180
+ ### 1. Workflow hooks change what happens
151
181
 
182
+ The default way to change platform behaviour. A hook is a declared point inside
183
+ an existing workflow where you register your own step. Your step runs inside the
184
+ original transaction and participates in its rollback, so you are not
185
+ reimplementing the flow to add one rule.
152
186
 
153
- ## How a multi-vendor order flows
187
+ ```ts src/workflows/hooks/seller-approved.ts
188
+ import { approveSellerWorkflow } from "@mercurjs/core/workflows"
189
+
190
+ approveSellerWorkflow.hooks.sellerApproved(
191
+ async ({ seller_id }, { container }) => {
192
+ await container.resolve("erp").createVendorAccount(seller_id)
193
+ }
194
+ )
195
+ ```
154
196
 
155
- A single customer cart can hold items from many sellers. Order splitting is where
156
- the marketplace, commerce, commission, and payout layers work together.
157
-
158
- 1. **Customer adds items** from multiple sellers to one cart (Store API).
159
- 2. **Cart completion** triggers the split-order workflow (marketplace layer).
160
- 3. Items are **grouped by seller**, and a separate order is created for each (commerce and marketplace layers).
161
- 4. **Commission lines** are calculated per order from the matching rates (Commission module).
162
- 5. **Payment is split** proportionally across the seller orders (commerce layer).
163
- 6. Each seller's order is **credited to its payout account** after commission (Payout module).
164
- 7. **Events are emitted**, triggering notifications, webhook calls, and other side effects (subscribers).
165
- 8. Sellers **manage their orders** through the Vendor Portal (Vendor API).
166
- 9. The operator **monitors everything** through the Admin Panel (Admin API).
167
-
168
- ## Technology stack
169
-
170
- | Layer | Technology |
171
- | -------------------- | --------------------------------------------------- |
172
- | Runtime | Node.js 20+, TypeScript |
173
- | Commerce framework | Medusa v2 |
174
- | Database | PostgreSQL |
175
- | Frontend | React 18, React Router, Vite |
176
- | Data fetching | TanStack React Query |
177
- | UI components | Medusa UI, Radix UI |
178
- | Form handling | React Hook Form, Zod |
179
- | Tables | TanStack React Table |
180
- | Build | Turborepo (monorepo), Bun (package manager), tsup |
181
- | Internationalization | i18next |
182
-
183
- ## Core plugin layout
184
-
185
- `@mercurjs/core` is the package that holds all marketplace logic. It is structured
186
- as a standard Medusa plugin.
197
+ Reach for a hook when the answer is "the standard flow, plus something."
198
+ [Extend a workflow →](/resources/customization/extend-a-workflow)
199
+
200
+ ### 2. API routes add endpoints
201
+
202
+ Drop a `route.ts` under `src/api/**` and the endpoint exists. Routes nest under
203
+ the built-in ones, so a new resource can hang off an existing entity rather than
204
+ living off to the side.
187
205
 
188
206
  ```
189
- core/src/
190
- ├── modules/ # Data models and services
191
- │ ├── seller/ # Seller registration, profiles, members, order groups
192
- │ ├── commission/ # Commission rates, rules, calculation
193
- │ ├── offer/ # Seller listings against the shared product catalog
194
- │ ├── payout/ # Payout accounts, onboarding, payouts
195
- │ ├── product-attribute/ # Typed attribute catalog and values
196
- │ ├── product-edit/ # Product change requests and audit trail
197
- │ └── ... # Media, custom fields, and more
198
- ├── links/ # Relationships between modules
199
- ├── workflows/ # Multi-step business processes
200
- │ ├── seller/ # Seller lifecycle workflows
201
- │ ├── cart/ # Cart completion with order splitting
202
- │ ├── commission/ # Commission rate and line management
203
- │ ├── payout/ # Payout processing and crediting
204
- │ ├── offer/ # Offer lifecycle
205
- │ ├── product/ # Product approval and seller linking
206
- │ ├── product-edit/ # Change-request lifecycle
207
- │ ├── order-group/ # Order group operations
208
- │ └── ... # Attributes, shipping, inventory, promotions
209
- ├── api/ # HTTP route handlers
210
- │ ├── admin/ # Admin API routes
211
- │ ├── vendor/ # Vendor API routes
212
- │ ├── store/ # Store API routes
213
- │ └── hooks/ # Webhook handlers
214
- ├── subscribers/ # Event listeners
215
- ├── providers/ # Third-party provider integrations
216
- └── jobs/ # Scheduled background tasks
207
+ src/api/vendor/sellers/documents/route.ts → GET/POST /vendor/sellers/documents
208
+ src/api/vendor/sellers/documents/[id]/route.ts → GET/DELETE /vendor/sellers/documents/:id
217
209
  ```
218
210
 
219
- ## Distribution: blocks you own
211
+ Your routes get the same treatment as the built-in ones: vendor requests are
212
+ already scoped to the caller's seller by the surrounding middleware, and codegen
213
+ picks the route up so it appears on the typed client with real request and
214
+ response types.
215
+
216
+ [Create an API route →](/resources/best-practices/api-routes)
220
217
 
221
- Mercur ships features as blocks, not as an opaque dependency. The CLI copies
222
- source code directly into your project, so a block is a self-contained piece of
223
- functionality: a module, a workflow, an API route, or a UI extension.
218
+ ### 3. Modules and links add domains
224
219
 
225
- This is what code ownership means in practice.
220
+ When the data has its own lifecycle — subscriptions, RMAs, contracts, quotas —
221
+ it is a module, not a field. You define the models and service, link the module
222
+ to the built-in entities it relates to, and it becomes queryable in the same
223
+ graph as everything else. This is how substantial vertical features get built,
224
+ and it is the same mechanism Mercur's own domains use.
226
225
 
227
- - **You own every line** of code in your project.
228
- - **You can modify any block** to fit your business requirements.
229
- - **There are no hidden abstractions** or version conflicts.
230
- - **Updates are explicit.** You diff against the registry and apply the changes you want.
226
+ ### 4. Custom fields add data to built-in entities
231
227
 
232
- The CLI (`@mercurjs/cli@latest`) scaffolds projects, installs blocks, searches the
233
- registry, and compares local changes against upstream.
228
+ For one plain property on an existing record — `is_featured` on a product,
229
+ `tier` on a customer you declare the field in configuration and the storage,
230
+ link, and migration are generated for you.
234
231
 
235
- ## Workflow example
232
+ ```ts medusa-config.ts
233
+ {
234
+ resolve: "@mercurjs/core/modules/custom-fields",
235
+ options: {
236
+ customFields: {
237
+ Product: { is_featured: { type: "boolean", nullable: true } },
238
+ },
239
+ },
240
+ }
241
+ ```
236
242
 
237
- Workflows coordinate multi-step operations with automatic rollback on failure.
238
- Here is a simplified example.
243
+ One row per parent, no lifecycle of its own; anything more is a module.
244
+ [Custom fields →](/resources/best-practices/custom-fields)
239
245
 
240
- ```typescript
241
- import {
242
- createWorkflow,
243
- createStep,
244
- StepResponse,
245
- WorkflowResponse,
246
- } from "@medusajs/framework/workflows-sdk"
247
- import { MercurModules } from "@mercurjs/types"
246
+ ### 5. Panel pages and widgets — change what teams see
248
247
 
249
- const validateSellerStep = createStep(
250
- "validate-seller",
251
- async ({ seller_id }: { seller_id: string }, { container }) => {
252
- const sellerService = container.resolve(MercurModules.SELLER)
253
- const seller = await sellerService.retrieveSeller(seller_id)
248
+ The panels are extended by file convention. A file's folder decides what it does,
249
+ and there is no manifest to maintain.
254
250
 
255
- if (seller.status !== "open") {
256
- throw new Error("Seller is not active")
257
- }
251
+ | Path | Adds |
252
+ | ------------------------ | ------------------------------------- |
253
+ | `src/routes/**/page.tsx` | A new page and its route |
254
+ | `src/widgets/**` | A component in a named zone on a built-in page |
255
+ | `src/custom-fields/**` | Form fields, detail rows, and list columns on a built-in model |
256
+ | `src/_navigation.ts` | Sidebar order, labels, and visibility |
258
257
 
259
- return new StepResponse(seller)
260
- }
261
- )
258
+ A widget targets a slot on an existing page, which is how you put a payout
259
+ summary on the order detail screen without owning that screen:
262
260
 
263
- const createProductForSellerWorkflow = createWorkflow(
264
- "create-product-for-seller",
265
- (input: { seller_id: string }) => {
266
- const seller = validateSellerStep({ seller_id: input.seller_id })
261
+ ```tsx apps/vendor/src/widgets/payout-summary.tsx
262
+ export const config = defineWidgetConfig({ zone: "orders.detail.side.after" })
263
+ ```
267
264
 
268
- // Additional steps: create product, link to seller, and so on.
265
+ Zone ids, model names, and field ids are generated per panel into
266
+ `extension-targets.d.ts`, so a mistargeted extension fails type-checking instead
267
+ of silently not rendering.
268
+ [Panel extensions →](/references/panel-extensions/overview)
269
269
 
270
- return new WorkflowResponse({ seller })
271
- }
272
- )
270
+ ### 6. Blocks install features as source
271
+
272
+ Larger features ship as blocks. The CLI copies the source into your project
273
+ rather than adding a dependency, so a block is yours from the moment it lands:
274
+ readable, debuggable, and modifiable without waiting on an upstream release.
275
+ Updates are explicit — `diff` against the registry and take what you want.
276
+
277
+ ```bash
278
+ bunx @mercurjs/cli@latest add reviews
273
279
  ```
274
280
 
275
- ## Design principles
281
+ [Blocks →](/learn/blocks)
282
+
283
+ ### Choosing a zone
284
+
285
+ | You want to… | Use |
286
+ | --------------------------------------------------- | ----------------------- |
287
+ | Add a rule or side effect to an existing operation | Workflow hook |
288
+ | Expose new data or a new operation over HTTP | API route |
289
+ | Model something with its own lifecycle | Module and link |
290
+ | Store one extra property on a built-in entity | Custom field |
291
+ | Show or capture something in the panels | Page, widget, or custom-field UI |
292
+ | Adopt a whole feature and own it | Block |
293
+
294
+ ## Governance
276
295
 
277
- These principles explain why the architecture looks the way it does.
296
+ Multi-vendor means untrusted writers, so the guarantees operators need are built
297
+ into the layers rather than layered on.
278
298
 
279
- - **Enterprise is the noun, composable is the how.** Mercur is a marketplace platform first. Composability, open source, and AI-nativeness are how it becomes a better enterprise choice than a closed platform, not a step down from one.
280
- - **Modular over monolithic.** Each marketplace feature is a separate module you can install, modify, or replace on its own. You do not need all of Mercur to benefit from it.
281
- - **Explicit over implicit.** Relationships are declared through links, not buried in service code. Workflows make multi-step operations visible and debuggable. API routes are file-based and predictable.
282
- - **Extensible over configurable.** Instead of hundreds of config flags, Mercur gives you extension points. Workflows have hooks, providers are pluggable, and models extend through Medusa. When configuration is not enough, you change the source you own.
283
- - **Commerce-aware.** Mercur does not reinvent commerce. It delegates products, pricing, orders, payments, and fulfillment to Medusa and focuses on the marketplace logic that multi-vendor systems need.
299
+ - **Scoping before handling.** Vendor requests are narrowed to the caller's seller in middleware, ahead of any handler. A route cannot accidentally leak across sellers, including a route you add.
300
+ - **An immutable change pipeline.** Product edits are recorded as `ProductChange` entries who changed what, who approved it, when. Low-risk edits auto-confirm; the rest queue for review. The audit trail is the mechanism, not a log written beside it.
301
+ - **Exact money.** Commission and payout arithmetic uses arbitrary-precision numbers throughout, so splitting a payment across sellers stays exact.
302
+ - **Agents inside the same guardrails.** The typed client, exposed workflows, `llms.txt`, and the MCP server give AI agents structured contracts subject to the same roles and the same review pipeline as human users.
303
+
304
+ ## How a multi-vendor order flows
305
+
306
+ One cart, several sellers. This is the path that touches every layer.
307
+
308
+ 1. The customer adds items from multiple sellers to a single cart (Store API).
309
+ 2. Cart completion starts the split-order workflow.
310
+ 3. Items are grouped by seller and an order is created per seller, all under one order group.
311
+ 4. Commission lines are computed per order from the matching rules.
312
+ 5. Payment is allocated proportionally across the seller orders.
313
+ 6. Each seller's share is credited to its payout account, net of commission.
314
+ 7. Events fire: notifications, webhooks, indexing, and any hook you registered.
315
+ 8. Sellers fulfill their own orders in the vendor portal; the operator sees all of it in the admin panel.
316
+
317
+ Steps 2 through 6 are a single workflow. If any of it fails, none of it happened.
318
+
319
+ ## Stack
320
+
321
+ | Concern | Technology |
322
+ | ------------------ | -------------------------------------------- |
323
+ | Runtime | Node.js 20+, TypeScript |
324
+ | Commerce engine | Medusa v2 |
325
+ | Database | PostgreSQL |
326
+ | Events, cache | Redis (in-memory fallback in development) |
327
+ | Panels | React 18, Vite, Medusa UI, TanStack Query |
328
+ | Validation, forms | Zod, React Hook Form |
329
+ | Build | Turborepo, Bun, tsup |
330
+
331
+ Mercur's own code is distributed as `@mercurjs/core`, a Medusa plugin registered
332
+ by `withMercur()` in your `medusa-config.ts`. That packaging matters when you
333
+ upgrade; it does not change any of the above.
284
334
 
285
335
  ## Next steps
286
336
 
@@ -288,13 +338,13 @@ These principles explain why the architecture looks the way it does.
288
338
  <Card title="Platform modules" href="/platform/store/overview">
289
339
  Data models, workflows, and events for each marketplace domain.
290
340
  </Card>
291
- <Card title="Blocks" href="/learn/blocks">
292
- How features ship as source code you own, not an opaque dependency.
341
+ <Card title="Panel extensions" href="/references/panel-extensions/overview">
342
+ Pages, widgets, custom fields, and navigation.
343
+ </Card>
344
+ <Card title="Extend a workflow" href="/resources/customization/extend-a-workflow">
345
+ Inject a step into an existing flow through a hook.
293
346
  </Card>
294
347
  <Card title="API reference" href="/references/api/conventions">
295
- Authentication, seller scoping, and the Admin, Vendor, and Store APIs.
296
- </Card>
297
- <Card title="Panel extensions" href="/references/panel-extensions/overview">
298
- Extend the admin and vendor panels without forking them.
348
+ Authentication, seller scoping, and the three API surfaces.
299
349
  </Card>
300
350
  </CardGroup>
@@ -51,12 +51,17 @@ Order groups are created automatically during checkout by the `completeCartWithS
51
51
  5. **Link everything** — The workflow creates links between:
52
52
  - Each order and the order group
53
53
  - Each order and its seller
54
- - Each order and the cart's payment collection
55
54
  - Each seller and the customer (if new relationship)
56
55
  6. **Finalize** — Inventory is reserved, payment is authorized, and the `order_group.created` event is emitted
57
56
 
58
57
  <Info>
59
- Payment collections are linked at the **individual order level**, not on the order group. This is a deliberate design decision each per-seller order gets its own link to the payment collection, which enables per-order capture and refund flows.
58
+ A single payment collection is shared across all of a cart's split orders and stays on the **cart**, not on each order. Split orders are **not** linked directly to the payment collection (Medusa's `order payment_collection` link is one-to-one on the payment-collection side). Read the payment collection through the cart:
59
+
60
+ ```text
61
+ order → cart → payment_collection
62
+ ```
63
+
64
+ Order-group and vendor order reads normalize `cart.payment_collection` back onto `payment_collections` and recompute `payment_status` from it, so consumers keep the familiar shape. When requesting fields on vendor order routes, make sure the request does not replace route defaults that already include `cart.payment_collection.*` (a `fields=` list containing bare fields replaces defaults in Medusa; use `+`-prefixed fields to merge, or include the `cart.payment_collection.*` paths explicitly).
60
65
  </Info>
61
66
 
62
67
  ## Events
package/llms.txt CHANGED
@@ -7,7 +7,7 @@ package (`node_modules/@mercurjs/docs/`).
7
7
 
8
8
  ## Learn — concepts and getting started
9
9
 
10
- - [Architecture](content/learn/architecture.mdx) — How the Mercur enterprise marketplace platform is built: its layers, building blocks, and how the pieces fit together.
10
+ - [Architecture](content/learn/architecture.mdx) — How Mercur is put together: the runtime layers, the boundary between the marketplace platform and your own code, and the zones you extend.
11
11
  - [Product Attributes](content/learn/attributes.mdx) — Typed, filterable product attributes on top of Medusa's native product options — including variant axes shared across the catalog.
12
12
  - [Blocks](content/learn/blocks.mdx) — Self-contained pieces of marketplace functionality that get copied into your project as source code.
13
13
  - [Commission](content/learn/commissions.mdx) — How marketplace commissions work in Mercur — rates, rules, the matching algorithm, and commission lines.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mercurjs/docs",
3
- "version": "2.3.0",
3
+ "version": "2.3.2-canary.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/mercurjs/mercur",