@mercurjs/docs 2.3.8-canary.3 → 2.3.8-canary.4

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.
Files changed (113) hide show
  1. package/content/learn/architecture.mdx +166 -263
  2. package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
  3. package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
  4. package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
  5. package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
  6. package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
  7. package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
  8. package/content/platform/attribute/overview.mdx +37 -33
  9. package/content/platform/attribute/reference/data-models.mdx +53 -38
  10. package/content/platform/attribute/reference/events.mdx +47 -24
  11. package/content/platform/attribute/reference/links.mdx +98 -27
  12. package/content/platform/attribute/reference/service.mdx +71 -32
  13. package/content/platform/attribute/reference/workflows.mdx +158 -28
  14. package/content/platform/catalog/concepts/master-products.mdx +39 -37
  15. package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
  16. package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
  17. package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
  18. package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
  19. package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
  20. package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
  21. package/content/platform/catalog/overview.mdx +39 -35
  22. package/content/platform/catalog/reference/data-models.mdx +154 -43
  23. package/content/platform/catalog/reference/events.mdx +41 -25
  24. package/content/platform/catalog/reference/links.mdx +196 -28
  25. package/content/platform/catalog/reference/service.mdx +57 -35
  26. package/content/platform/catalog/reference/workflows.mdx +124 -30
  27. package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
  28. package/content/platform/commission/concepts/rule-matching.mdx +53 -63
  29. package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
  30. package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
  31. package/content/platform/commission/guides/create-a-rate.mdx +101 -53
  32. package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
  33. package/content/platform/commission/overview.mdx +43 -35
  34. package/content/platform/commission/reference/data-models.mdx +59 -45
  35. package/content/platform/commission/reference/events.mdx +28 -34
  36. package/content/platform/commission/reference/links.mdx +42 -33
  37. package/content/platform/commission/reference/service.mdx +103 -28
  38. package/content/platform/commission/reference/workflows.mdx +101 -27
  39. package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
  40. package/content/platform/offer/concepts/shipping.mdx +28 -29
  41. package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
  42. package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
  43. package/content/platform/offer/guides/create-an-offer.mdx +112 -60
  44. package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
  45. package/content/platform/offer/overview.mdx +39 -32
  46. package/content/platform/offer/reference/data-models.mdx +77 -47
  47. package/content/platform/offer/reference/events.mdx +44 -18
  48. package/content/platform/offer/reference/links.mdx +92 -40
  49. package/content/platform/offer/reference/service.mdx +66 -31
  50. package/content/platform/offer/reference/workflows.mdx +146 -37
  51. package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
  52. package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
  53. package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
  54. package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
  55. package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
  56. package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
  57. package/content/platform/order-group/overview.mdx +42 -32
  58. package/content/platform/order-group/reference/data-models.mdx +48 -31
  59. package/content/platform/order-group/reference/events.mdx +29 -17
  60. package/content/platform/order-group/reference/links.mdx +90 -29
  61. package/content/platform/order-group/reference/service.mdx +55 -31
  62. package/content/platform/order-group/reference/workflows.mdx +83 -24
  63. package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
  64. package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
  65. package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
  66. package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
  67. package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
  68. package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
  69. package/content/platform/payout/overview.mdx +43 -34
  70. package/content/platform/payout/reference/data-models.mdx +63 -36
  71. package/content/platform/payout/reference/events.mdx +56 -30
  72. package/content/platform/payout/reference/links.mdx +52 -22
  73. package/content/platform/payout/reference/service.mdx +72 -33
  74. package/content/platform/payout/reference/workflows.mdx +80 -19
  75. package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
  76. package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
  77. package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
  78. package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
  79. package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
  80. package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
  81. package/content/platform/product-edit/overview.mdx +41 -34
  82. package/content/platform/product-edit/reference/data-models.mdx +73 -47
  83. package/content/platform/product-edit/reference/events.mdx +39 -29
  84. package/content/platform/product-edit/reference/links.mdx +32 -24
  85. package/content/platform/product-edit/reference/service.mdx +45 -26
  86. package/content/platform/product-edit/reference/workflows.mdx +173 -31
  87. package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
  88. package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
  89. package/content/platform/review/concepts/the-review-model.mdx +52 -45
  90. package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
  91. package/content/platform/review/guides/create-a-review.mdx +109 -39
  92. package/content/platform/review/guides/moderate-a-review.mdx +83 -37
  93. package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
  94. package/content/platform/review/overview.mdx +43 -33
  95. package/content/platform/review/reference/data-models.mdx +41 -27
  96. package/content/platform/review/reference/events.mdx +55 -37
  97. package/content/platform/review/reference/links.mdx +80 -22
  98. package/content/platform/review/reference/service.mdx +61 -33
  99. package/content/platform/review/reference/workflows.mdx +80 -24
  100. package/content/platform/store/concepts/lifecycle.mdx +68 -44
  101. package/content/platform/store/concepts/store-entity.mdx +45 -27
  102. package/content/platform/store/concepts/team.mdx +56 -35
  103. package/content/platform/store/guides/create-a-store.mdx +102 -35
  104. package/content/platform/store/guides/manage-the-team.mdx +122 -32
  105. package/content/platform/store/guides/moderate-a-store.mdx +107 -38
  106. package/content/platform/store/overview.mdx +41 -34
  107. package/content/platform/store/reference/data-models.mdx +132 -57
  108. package/content/platform/store/reference/events.mdx +42 -17
  109. package/content/platform/store/reference/links.mdx +67 -49
  110. package/content/platform/store/reference/service.mdx +105 -33
  111. package/content/platform/store/reference/workflows.mdx +304 -24
  112. package/llms.txt +111 -111
  113. package/package.json +1 -1
@@ -1,86 +1,95 @@
1
1
  ---
2
- title: "Payout"
2
+ title: "Payouts"
3
3
  sidebarTitle: "Overview"
4
- description: "Onboard sellers to a payment provider and settle their earnings automatically."
4
+ description: "Connect each seller to a payout provider, onboard them, and transfer their share of an order to their own account."
5
5
  ---
6
6
 
7
- Use Mercur to control how each seller is onboarded and settled, while the
8
- payment provider handles KYC.
7
+ Use Mercur to connect each seller to a payment provider and send them their share of every order.
9
8
 
10
- Settlement you control is the Payout domain. Once an order is placed and
11
- fulfilled, it splits each seller's share out to their own connected account, so
12
- funds are settled per seller rather than pooled. The seller's share is the order
13
- total minus commission. Onboarding is provider-driven, with the payment provider
14
- carrying KYC, and the domain ships with a pluggable provider interface and a
15
- **Stripe Connect** implementation out of the box. It also runs a fully automated
16
- capture-and-transfer pipeline driven by scheduled jobs and provider webhooks.
9
+ A payout is a transfer from the marketplace to one seller, for one order. The Payout module holds 3 records: the seller’s payout account, its onboarding state, and the payouts made to it. Every external call goes through a single payout provider. Stripe Connect ships as a package, and a built-in `system` provider runs when you register none.
10
+
11
+ <img className="block dark:hidden" src="/images/figures/payout-overview-light.svg" alt="A Payouts list in front of a Payout accounts list. Payout #12 for ACME is accented: an order total of € 47.00 minus € 4.70 commission gives a € 42.30 payout, marked Paid. Behind it, ACME's payout account is Active and GLOBEX's is Restricted." />
12
+ <img className="hidden dark:block" src="/images/figures/payout-overview-dark.svg" alt="A Payouts list in front of a Payout accounts list. Payout #12 for ACME is accented: an order total of € 47.00 minus € 4.70 commission gives a € 42.30 payout, marked Paid. Behind it, ACME's payout account is Active and GLOBEX's is Restricted." />
17
13
 
18
14
  <Note>
19
- **Provider-agnostic.** The module talks to exactly one registered payout
20
- provider through a single interface. Stripe Connect ships by default; any
21
- other processor is a drop-in implementation of the same `IPayoutProvider`
22
- contract.
15
+ Resolve the module service with `MercurModules.PAYOUT` from `@mercurjs/types`. Import the payout workflows from `@mercurjs/core/workflows`.
23
16
  </Note>
24
17
 
25
18
  ## Key features
26
19
 
27
- - **Pluggable provider interface:** one `IPayoutProvider` contract, with Stripe Connect included.
28
- - **Payout accounts and onboarding:** a per-seller account plus a provider onboarding record.
29
- - **Webhook-driven lifecycle:** account status (`PENDING` → `ACTIVE` ↔ `RESTRICTED` / `REJECTED`) tracks the provider.
30
- - **Automated pipeline:** a capture-check job, payment capture, a daily payout job, and transfer, with no manual steps.
31
- - **Commission-aware transfers:** each payout is the order total minus its commission lines.
32
- - **Tunable timing:** authorization window, seller-action window, capture buffer, and required fulfillment status.
20
+ The Payout module covers a seller’s connection to the provider and each transfer made through it:
21
+
22
+ - **One provider interface**: `IPayoutProvider` has 4 methods, and the module loads exactly one implementation
23
+ - **Stripe Connect provider**: `@mercurjs/payout-stripe-connect` creates Express accounts, onboarding links, and transfers
24
+ - **One account per store**: `createPayoutAccountWorkflow` refuses a second account for the same seller
25
+ - **Re-runnable onboarding**: `createOnboardingWorkflow` creates the onboarding record once and updates it after that
26
+ - **Webhook-driven statuses**: provider webhooks move accounts and payouts between statuses through `processPayoutForWebhookWorkflow`
27
+ - **Commission-aware amounts**: `createPayoutWorkflow` pays the order total minus the order’s commission lines
28
+
29
+ ## How it works
30
+
31
+ A seller moves from no account to a paid order in 5 steps:
32
+
33
+ 1. Run `createPayoutAccountWorkflow` for the seller. The provider creates a connected account.
34
+ 2. Run `createOnboardingWorkflow`. The provider returns onboarding data, such as a Stripe onboarding link.
35
+ 3. The provider posts webhooks to `/hooks/payout`, and the `payout-webhook` subscriber sets the account to `active`, `restricted`, or `rejected`.
36
+ 4. Run `createPayoutWorkflow` with an order id. It computes the seller’s share and asks the provider to transfer it.
37
+ 5. Later provider webhooks update the payout’s status.
38
+
39
+ <Warning>
40
+ Mercur doesn’t trigger step 4 for you. No scheduled job or subscriber in `@mercurjs/core` captures payments or runs `createPayoutWorkflow`. Call it from your own job, subscriber, or route. See [The payout pipeline](/platform/payout/concepts/payout-pipeline).
41
+ </Warning>
33
42
 
34
43
  ## Get started
35
44
 
36
- Learn how the domain fits together.
45
+ Learn how accounts, onboarding, and transfers fit together.
37
46
 
38
47
  <CardGroup cols={2}>
39
48
  <Card title="Accounts & onboarding" icon="id-card" href="/platform/payout/concepts/accounts-and-onboarding">
40
- The payout account, its onboarding record, and provider data.
49
+ The payout account, its onboarding record, and the provider data they carry.
41
50
  </Card>
42
51
  <Card title="Account lifecycle" icon="arrows-rotate" href="/platform/payout/concepts/account-lifecycle">
43
- Statuses and the webhook-driven transitions between them.
52
+ Account and payout statuses, and the webhooks that change them.
44
53
  </Card>
45
54
  <Card title="The payout pipeline" icon="arrows-turn-right" href="/platform/payout/concepts/payout-pipeline">
46
- Capture check, capture, daily payout, transfer, and the provider interface.
55
+ What ships, what you wire yourself, and the provider interface.
47
56
  </Card>
48
57
  </CardGroup>
49
58
 
50
59
  ## Examples
51
60
 
52
- Build against the Payout domain in your own code.
61
+ Build against payouts in your own server code.
53
62
 
54
63
  <CardGroup cols={2}>
55
64
  <Card title="Create a payout account" icon="plus" href="/platform/payout/guides/create-a-payout-account">
56
65
  Run `createPayoutAccountWorkflow` for a seller.
57
66
  </Card>
58
67
  <Card title="Start onboarding" icon="link" href="/platform/payout/guides/start-provider-onboarding">
59
- Kick off provider onboarding with `createOnboardingWorkflow`.
68
+ Create a provider onboarding link with `createOnboardingWorkflow`.
60
69
  </Card>
61
70
  <Card title="Process a webhook" icon="bell" href="/platform/payout/guides/process-a-provider-webhook">
62
- Turn a provider webhook into account and payout status updates.
71
+ Turn a provider webhook into account and payout status changes.
63
72
  </Card>
64
73
  </CardGroup>
65
74
 
66
75
  ## Resources
67
76
 
68
- Data models, workflows, service methods, and events for the Payout domain.
77
+ The data models, links, workflows, service methods, and events behind payouts.
69
78
 
70
79
  <CardGroup cols={2}>
71
80
  <Card title="Data models" icon="table" href="/platform/payout/reference/data-models">
72
- The `PayoutAccount`, `Onboarding`, and `Payout` entities.
81
+ `PayoutAccount`, `Onboarding`, and `Payout`, with every field.
73
82
  </Card>
74
83
  <Card title="Links" icon="link" href="/platform/payout/reference/links">
75
- How the Payout domain links to sellers and orders.
84
+ How payout records link to sellers and orders.
76
85
  </Card>
77
86
  <Card title="Workflows" icon="diagram-project" href="/platform/payout/reference/workflows">
78
- Account, onboarding, payout, and webhook workflows.
87
+ The 4 payout workflows, their steps, and their hooks.
79
88
  </Card>
80
89
  <Card title="Service" icon="gear" href="/platform/payout/reference/service">
81
- Module service methods for working with records directly.
90
+ Generated and provider-backed methods on the module service.
82
91
  </Card>
83
92
  <Card title="Events" icon="bell" href="/platform/payout/reference/events">
84
- Events that drive the payout pipeline.
93
+ The webhook event, the data-model events, and the reserved event names.
85
94
  </Card>
86
95
  </CardGroup>
@@ -1,61 +1,88 @@
1
1
  ---
2
- title: "Data models"
2
+ title: "Payout data models"
3
3
  sidebarTitle: "Data models"
4
- description: "The data models owned by the Payout domain."
4
+ description: "Every field of PayoutAccount, Onboarding, and Payout, plus the PayoutAccountStatus and PayoutStatus enums."
5
5
  ---
6
6
 
7
- The Payout domain is owned by the **Payout module**. This reference lists its
8
- data models and their fields. For the full module overview, see the
9
- [Payout overview](/platform/payout/overview).
7
+ The Payout module owns 3 data models: `PayoutAccount`, `Onboarding`, and `Payout`. They’re defined in `packages/core/src/modules/payout/models`, and the module service is registered as `MercurModules.PAYOUT`. Every model also gets Medusa’s `created_at`, `updated_at`, and `deleted_at` columns.
10
8
 
11
9
  ## PayoutAccount
12
10
 
13
- Table `payout_account`, id prefix `pacc`. A seller's connection to the payment
14
- provider. This is the record funds are transferred to.
11
+ A seller’s connection to the payout provider. Table `payout_account`, id prefix `pacc`.
15
12
 
16
- | Field | Type | Notes |
13
+ | Field | Type | Description |
17
14
  | --- | --- | --- |
18
- | `id` | text | Primary key |
19
- | `status` | enum | `PayoutAccountStatus`, default `pending` |
20
- | `data` | json | Provider-specific account data |
21
- | `context` | json | Nullable; per-request provider hints |
15
+ | `id` | `text` | Primary key, such as `pacc_01JD8Q1234567` |
16
+ | `status` | `enum` | `PayoutAccountStatus`, default `pending` |
17
+ | `data` | `json` | Provider account data, default `{}` |
18
+ | `context` | `json`, nullable | Provider context passed at creation |
22
19
 
23
- Relations: `onboarding` (one-to-one, nullable), `payouts` (one-to-many).
20
+ Relations:
21
+
22
+ - **`onboarding`**: `hasOne` `Onboarding`, nullable, mapped by `account`
23
+ - **`payouts`**: `hasMany` `Payout`, mapped by `account`
24
+
25
+ The seller is reached through the `seller_payout_account` link, not a column. See [Links](/platform/payout/reference/links).
24
26
 
25
27
  ## Onboarding
26
28
 
27
- Table `onboarding`, id prefix `onb`. Provider setup state for a payout account
28
- (for Stripe Connect, the onboarding link).
29
+ The provider’s onboarding state for one payout account. Table `onboarding`, id prefix `onb`.
29
30
 
30
- | Field | Type | Notes |
31
+ | Field | Type | Description |
31
32
  | --- | --- | --- |
32
- | `id` | text | Primary key |
33
- | `data` | json | Nullable; provider onboarding data |
34
- | `context` | json | Nullable; per-request provider hints |
35
- | `account_id` | text | Belongs to a `PayoutAccount` |
33
+ | `id` | `text` | Primary key, such as `onb_01JD8Q1234567` |
34
+ | `data` | `json`, nullable | Provider onboarding data, such as a Stripe account link |
35
+ | `context` | `json`, nullable | Provider context from the last onboarding call |
36
+ | `account_id` | `text` | Foreign key to `PayoutAccount` |
37
+
38
+ Relations:
36
39
 
37
- <Note>
38
- `Onboarding` is one-to-one with `PayoutAccount`. Re-running the onboarding
39
- workflow updates the existing record rather than creating a second one.
40
- </Note>
40
+ - **`account`**: `belongsTo` `PayoutAccount`, mapped by `onboarding`
41
41
 
42
42
  ## Payout
43
43
 
44
- Table `payout`, id prefix `pout`. A single transfer of a seller's earnings for
45
- one order.
44
+ One transfer to a seller’s payout account. Table `payout`, id prefix `pout`.
46
45
 
47
- | Field | Type | Notes |
46
+ | Field | Type | Description |
48
47
  | --- | --- | --- |
49
- | `id` | text | Primary key |
50
- | `display_id` | number | Auto-incrementing, human-readable |
51
- | `currency_code` | text | The payout's currency |
52
- | `amount` | bigNumber | Order total minus commission |
53
- | `data` | json | Nullable; provider transfer data |
54
- | `status` | enum | `PayoutStatus`, default `pending` |
55
- | `account_id` | text | Belongs to a `PayoutAccount` |
48
+ | `id` | `text` | Primary key, such as `pout_01JD8Q1234567` |
49
+ | `display_id` | `autoincrement` | Human-readable sequence number |
50
+ | `currency_code` | `text` | Currency of the transfer |
51
+ | `amount` | `bigNumber` | Amount sent to the seller |
52
+ | `data` | `json`, nullable | Provider transfer data, such as a Stripe transfer |
53
+ | `status` | `enum` | `PayoutStatus`, default `pending` |
54
+ | `account_id` | `text` | Foreign key to `PayoutAccount` |
55
+
56
+ Relations:
57
+
58
+ - **`account`**: `belongsTo` `PayoutAccount`, mapped by `payouts`
59
+
60
+ Indexes, both limited to rows where `deleted_at IS NULL`:
61
+
62
+ - **`IDX_payout_account_id_created_at`**: on `account_id`, `created_at`
63
+ - **`IDX_payout_status_created_at`**: on `status`, `created_at`
64
+
65
+ `createPayoutWorkflow` sets `amount` to the order total minus the order’s commission lines. The model itself doesn’t store an order id. The order travels to the provider as the idempotency key, and Stripe Connect stores it as the transfer’s `transfer_group` in `data`.
56
66
 
57
67
  ## Enums
58
68
 
59
- **`PayoutAccountStatus`:** `pending`, `active`, `restricted`, `rejected`.
69
+ Both enums are exported from `@mercurjs/types`.
70
+
71
+ `PayoutAccountStatus`:
72
+
73
+ | Member | Value |
74
+ | --- | --- |
75
+ | `PENDING` | `pending` |
76
+ | `ACTIVE` | `active` |
77
+ | `RESTRICTED` | `restricted` |
78
+ | `REJECTED` | `rejected` |
79
+
80
+ `PayoutStatus`:
60
81
 
61
- **`PayoutStatus`:** `pending`, `processing`, `paid`, `failed`, `canceled`.
82
+ | Member | Value |
83
+ | --- | --- |
84
+ | `PENDING` | `pending` |
85
+ | `PROCESSING` | `processing` |
86
+ | `PAID` | `paid` |
87
+ | `FAILED` | `failed` |
88
+ | `CANCELED` | `canceled` |
@@ -1,48 +1,74 @@
1
1
  ---
2
- title: "Event reference"
2
+ title: "Payout events"
3
3
  sidebarTitle: "Events"
4
- description: "Events that drive the payout pipeline, for subscribers and side effects."
4
+ description: "The payout webhook event, the data-model events the Payout module emits, and the reserved PayoutEvents names that nothing emits yet."
5
5
  ---
6
6
 
7
- The Payout domain is event-driven. Scheduled jobs emit events, and subscribers
8
- react to them to capture payments and transfer funds. Subscribe to these events
9
- to run your own side effects, such as notifications, ledger syncing, or follow-up
10
- workflows, instead of polling.
7
+ The Payout module has one event of its own, `payout.webhook_received`, which carries provider webhooks to the shipped subscriber. Medusa also emits a created, updated, or deleted event whenever a payout record changes. Subscribe to those for side effects such as seller notifications or ledger syncs.
11
8
 
12
- ```ts title="src/subscribers/payout-requested.ts"
9
+ ## Webhook event
10
+
11
+ The `POST /hooks/payout` route emits this event with a 5000 ms delay and 3 attempts.
12
+
13
+ | Event | Emitted when | Payload |
14
+ | --- | --- | --- |
15
+ | `payout.webhook_received` | The provider posts to `/hooks/payout` | `{ data, rawData, headers }` |
16
+
17
+ `data` is the parsed body, `rawData` is the raw body, and `headers` are the request headers. The `payout-webhook` subscriber, with subscriber id `payout-webhook-handler`, handles it and runs `processPayoutForWebhookWorkflow`. The event bus serializes `rawData` as a buffer object, and the subscriber turns it back into a `Buffer` before signature checks.
18
+
19
+ ## Data-model events
20
+
21
+ `PayoutModuleService` extends `MedusaService`, so Medusa emits an event for each row it creates, updates, or deletes. The name is the module name, the kebab-case model name, and the action. The payload is `{ id }`.
22
+
23
+ | Event | Emitted when |
24
+ | --- | --- |
25
+ | `payout.payout-account.created` | `createPayoutAccount` inserts an account |
26
+ | `payout.payout-account.updated` | The provider response is stored, or a webhook changes the status |
27
+ | `payout.payout-account.deleted` | The provider call fails and the new account is removed |
28
+ | `payout.onboarding.created` | `createOnboarding` runs for the first time on an account |
29
+ | `payout.onboarding.updated` | `createOnboarding` runs again on the same account |
30
+ | `payout.payout.created` | `createPayouts` inserts a `pending` payout |
31
+ | `payout.payout.updated` | The provider response is stored, or a webhook changes the status |
32
+ | `payout.payout.deleted` | The provider call fails and the new payout is removed |
33
+
34
+ An `updated` event doesn’t say which field changed. Retrieve the record in your subscriber and read its `status`.
35
+
36
+ ```ts title="src/subscribers/payout-account-updated.ts"
13
37
  import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
38
+ import { MercurModules, PayoutAccountStatus } from "@mercurjs/types"
14
39
 
15
- export default async function payoutRequestedHandler({
40
+ export default async function payoutAccountUpdated({
16
41
  event,
17
42
  container,
18
- }: SubscriberArgs<{ order_id: string }>) {
19
- const orderId = event.data.order_id
20
- // ...notify the seller, write to an external ledger, etc.
43
+ }: SubscriberArgs<{ id: string }>) {
44
+ const payoutService = container.resolve(MercurModules.PAYOUT)
45
+ const account = await payoutService.retrievePayoutAccount(event.data.id)
46
+
47
+ if (account.status === PayoutAccountStatus.RESTRICTED) {
48
+ const logger = container.resolve("logger")
49
+ logger.warn(`Payout account ${account.id} is restricted`)
50
+ }
21
51
  }
22
52
 
23
53
  export const config: SubscriberConfig = {
24
- event: "payout.requested",
54
+ event: "payout.payout-account.updated",
25
55
  }
26
56
  ```
27
57
 
28
- ## Pipeline events
58
+ This subscriber logs a warning each time an account lands in `restricted`, which is where you’d send the seller a reminder to finish onboarding.
29
59
 
30
- | Event | Emitted when | Handled by |
31
- | --- | --- | --- |
32
- | `order.capture_requested` | Capture check finds an order ready to capture | The payment-capture subscriber runs `capturePaymentWorkflow` |
33
- | `order.authorization_expired` | Capture check finds an authorization that already expired | Order is flagged so it isn't retried |
34
- | `payout.requested` | Daily job finds a captured order not yet paid out | The transfer subscriber runs `createPayoutWorkflow` |
60
+ ## Reserved event names
35
61
 
36
- ## Webhook events
62
+ `@mercurjs/types` exports a `PayoutEvents` enum. Nothing in `@mercurjs/core` emits these events or subscribes to them, and the enum carries a TODO to move them to order events.
37
63
 
38
- | Event | Emitted when | Handled by |
39
- | --- | --- | --- |
40
- | `payout.webhook_received` | A provider webhook arrives | The `payout-webhook` subscriber runs `processPayoutForWebhookWorkflow` |
41
-
42
- <Note>
43
- `order.capture_requested`, `order.authorization_expired`, and
44
- `payout.requested` are defined on the `PayoutEvents` enum in `@mercurjs/types`.
45
- The webhook subscriber resolves `payout.webhook_received` to a provider action
46
- before updating status. See
47
- [Account lifecycle](/platform/payout/concepts/account-lifecycle).
48
- </Note>
64
+ | Member | Name |
65
+ | --- | --- |
66
+ | `OrderCaptureRequested` | `order.capture_requested` |
67
+ | `OrderAuthorizationExpired` | `order.authorization_expired` |
68
+ | `PayoutRequested` | `payout.requested` |
69
+
70
+ You can emit them from your own payout trigger and subscribe to them in your project. See [The payout pipeline](/platform/payout/concepts/payout-pipeline).
71
+
72
+ ## Workflow hook
73
+
74
+ `createPayoutWorkflow` exposes a `payoutCreated` hook with `{ order_id, payout_id }`. Use it instead of `payout.payout.created` when you need the order id, because the data-model event carries only the payout id.
@@ -1,36 +1,66 @@
1
1
  ---
2
- title: "Links to other modules"
2
+ title: "Payout module links"
3
3
  sidebarTitle: "Links"
4
- description: "How the Payout domain links to sellers and orders across the marketplace."
4
+ description: "The 3 module links that connect payout accounts and payouts to sellers and orders."
5
5
  ---
6
6
 
7
- Modules in Mercur never reference each other directly. They connect through
8
- **module links**. The Payout module links to the Seller and Order modules. Once a
9
- link is defined, you retrieve related records with `query.graph` using the link
10
- alias.
7
+ The Payout module never references other modules directly. 3 module links in `packages/core/src/links` connect its records to sellers and orders. Read across them with `query.graph`, and query a link’s own table by its entry name.
8
+
9
+ | Link | Entry name | Cardinality | Created by |
10
+ | --- | --- | --- | --- |
11
+ | Seller ↔ payout account | `seller_payout_account` | One to one | `createPayoutAccountWorkflow` |
12
+ | Payout ↔ seller | `payout_seller` | Many payouts to one seller | `createPayoutWorkflow` |
13
+ | Order ↔ payout | `order_payout` | One order to many payouts | Nothing in core |
14
+
15
+ ## Seller and payout account
16
+
17
+ Defined in `seller-payout-account-link.ts` between `SellerModule.linkable.seller` and `PayoutModule.linkable.payoutAccount`. Neither side is a list, so a seller has one payout account and an account belongs to one seller. `validateSellerPayoutAccountStep` enforces the one-account rule before a new link is created.
11
18
 
12
19
  ```ts
13
- const { data: sellers } = await query.graph({
20
+ const { data: [seller] } = await query.graph({
14
21
  entity: "seller",
15
- fields: ["id", "name", "payout_account.*", "payouts.*"],
22
+ fields: ["id", "name", "payout_account.id", "payout_account.status"],
23
+ filters: { id: "sel_01JD8Q1234567" },
16
24
  })
17
25
  ```
18
26
 
19
- ## Sellers
27
+ ## Payout and seller
28
+
29
+ Defined in `payout-seller-link.ts` between `PayoutModule.linkable.payout`, marked `isList: true`, and `SellerModule.linkable.seller`. A seller has many payouts, and each payout has one seller. `createPayoutWorkflow` creates this link right after the payout.
20
30
 
21
- | Linked module | Relationship |
22
- | --- | --- |
23
- | **Seller** (account) | A store has **one** payout account (`seller` ↔ `payout_account`). |
24
- | **Seller** (payouts) | A store has **many** payouts (`seller` ↔ `payout`, list). |
31
+ ```ts
32
+ const { data: payouts } = await query.graph({
33
+ entity: "payout",
34
+ fields: ["id", "amount", "status", "seller.id", "seller.name"],
35
+ filters: { status: "paid" },
36
+ })
37
+ ```
25
38
 
26
- ## Orders
39
+ The Vendor API scopes payouts by querying the link table directly, with `entity: "payout_seller"` and filters on `seller_id` and `payout_id`.
27
40
 
28
- | Linked module | Relationship |
29
- | --- | --- |
30
- | **Order** | An order has **many** payouts (`order` ↔ `payout`, list). |
41
+ ## Order and payout
42
+
43
+ Defined in `order-payout-link.ts` between Medusa’s `OrderModule.linkable.order` and `PayoutModule.linkable.payout`, marked `isList: true`. An order can have many payouts.
44
+
45
+ <Warning>
46
+ No workflow in core creates `order_payout` rows, so `order.payouts` is empty by default. To use this link, create it in the `payoutCreated` hook of `createPayoutWorkflow`, which receives `order_id` and `payout_id`.
47
+ </Warning>
48
+
49
+ ```ts title="src/workflows/hooks/link-payout-to-order.ts"
50
+ import { createPayoutWorkflow } from "@mercurjs/core/workflows"
51
+ import { ContainerRegistrationKeys, Modules } from "@medusajs/framework/utils"
52
+ import { MercurModules } from "@mercurjs/types"
53
+
54
+ createPayoutWorkflow.hooks.payoutCreated(
55
+ async ({ order_id, payout_id }, { container }) => {
56
+ const link = container.resolve(ContainerRegistrationKeys.LINK)
57
+
58
+ await link.create({
59
+ [Modules.ORDER]: { order_id },
60
+ [MercurModules.PAYOUT]: { payout_id },
61
+ })
62
+ }
63
+ )
64
+ ```
31
65
 
32
- <Note>
33
- A payout is linked to both the order it settles and the seller it pays. The
34
- account link is one-to-one, while the seller-payouts and order-payouts links
35
- are lists.
36
- </Note>
66
+ Once the hook runs, read an order’s payouts with `fields: ["id", "payouts.id", "payouts.status"]` on the `order` entity.
@@ -1,53 +1,92 @@
1
1
  ---
2
- title: "Service reference"
2
+ title: "Payout module service"
3
3
  sidebarTitle: "Service"
4
- description: "The Payout module service: methods for working with records directly."
4
+ description: "Resolve the Payout module service, and use its generated data methods and its provider-backed methods."
5
5
  ---
6
6
 
7
- The Payout module exposes a service you can resolve from the Medusa container to
8
- read and write records directly, without going through a workflow. Use it inside
9
- custom services, subscribers, or scheduled jobs.
7
+ `PayoutModuleService` reads and writes payout records, and it’s the only code that calls the payout provider. Resolve it from the container in subscribers, jobs, steps, and routes. Prefer the [workflows](/platform/payout/reference/workflows) when you also need links or a rollback.
8
+
9
+ ## Resolve the service
10
+
11
+ The service is registered under `MercurModules.PAYOUT`, whose value is `"payout"`:
10
12
 
11
13
  ```ts
12
14
  import { MercurModules } from "@mercurjs/types"
13
15
 
14
- const payoutModuleService = container.resolve(MercurModules.PAYOUT)
16
+ const payoutService = container.resolve(MercurModules.PAYOUT)
15
17
 
16
- const [payouts, count] = await payoutModuleService.listAndCountPayouts({
17
- status: "paid",
18
- })
18
+ const [payouts, count] = await payoutService.listAndCountPayouts(
19
+ { status: "paid" },
20
+ { take: 20, order: { created_at: "DESC" } }
21
+ )
19
22
  ```
20
23
 
21
24
  ## Generated methods
22
25
 
23
- Each data model gets a standard set of auto-generated methods. For `Payout`:
26
+ The service extends `MedusaService({ Onboarding, Payout, PayoutAccount })`, so each model gets Medusa’s standard methods:
24
27
 
25
- | Method | Description |
28
+ | Model | Methods |
26
29
  | --- | --- |
27
- | `retrievePayout(id, config?)` | Retrieve a payout by id |
28
- | `listPayouts(filters?, config?)` | List payouts matching filters |
29
- | `listAndCountPayouts(filters?, config?)` | List payouts with a total count |
30
- | `updatePayouts(data)` | Update one or more payouts |
31
- | `deletePayouts(ids)` | Delete one or more payouts |
30
+ | `PayoutAccount` | `retrievePayoutAccount`, `listPayoutAccounts`, `listAndCountPayoutAccounts`, `createPayoutAccounts`, `updatePayoutAccounts`, `deletePayoutAccounts`, `softDeletePayoutAccounts`, `restorePayoutAccounts` |
31
+ | `Onboarding` | `retrieveOnboarding`, `listOnboardings`, `listAndCountOnboardings`, `createOnboardings`, `updateOnboardings`, `deleteOnboardings`, `softDeleteOnboardings`, `restoreOnboardings` |
32
+ | `Payout` | `retrievePayout`, `listPayouts`, `listAndCountPayouts`, `updatePayouts`, `deletePayouts`, `softDeletePayouts`, `restorePayouts` |
33
+
34
+ `createPayouts` isn’t in the `Payout` row because the service overrides it. The generated create and update methods write rows only. They never call the provider.
35
+
36
+ ## Provider-backed methods
37
+
38
+ These methods call the payout provider as part of the write.
39
+
40
+ ### createPayoutAccount
41
+
42
+ ```ts
43
+ createPayoutAccount(input: CreatePayoutAccountDTO): Promise<PayoutAccountDTO>
44
+ ```
45
+
46
+ Creates the row, calls the provider with `data.account_id` set to the new id, then stores the provider’s `data` and `status`. Deletes the row if the provider throws. `input` takes optional `data` and `context`.
47
+
48
+ ### createOnboarding
49
+
50
+ ```ts
51
+ createOnboarding(input: CreateOnboardingDTO): Promise<OnboardingDTO>
52
+ ```
53
+
54
+ Calls the provider with the account’s `data` merged with `input.data`, then creates the onboarding record or updates the existing one. `input` takes `account_id` and optional `data` and `context`.
32
55
 
33
- The same set exists for `PayoutAccount` and `Onboarding` (e.g.
34
- `retrievePayoutAccount`, `listPayoutAccounts`, `updateOnboardings`).
56
+ ### createPayouts
35
57
 
36
- ## Provider-integrated methods
58
+ ```ts
59
+ createPayouts(input: CreatePayoutDTO): Promise<PayoutDTO>
60
+ ```
61
+
62
+ Takes one payout, not an array. Throws a `NOT_ALLOWED` error when the account isn’t `active`. Otherwise it creates a `pending` row, calls the provider, and stores the provider’s `data` and `status`. Deletes the row if the provider throws. `input` takes `account_id`, `amount`, `currency_code`, and optional `data` and `context`.
63
+
64
+ ### getWebhookActionAndData
65
+
66
+ ```ts
67
+ getWebhookActionAndData(
68
+ input: PayoutWebhookActionInput
69
+ ): Promise<PayoutWebhookResult>
70
+ ```
71
+
72
+ Passes `{ data, rawData, headers }` to the provider and returns its action and Mercur id. It doesn’t change any record.
37
73
 
38
- These overrides call the configured provider as part of the operation. Prefer
39
- them over the raw generated creates.
74
+ ### getOptions
40
75
 
41
- | Method | Description |
76
+ ```ts
77
+ getOptions(): PayoutModuleOptions
78
+ ```
79
+
80
+ Returns the module options merged over `PAYOUT_MODULE_OPTION_DEFAULTS`. Core reads none of the timing options, so use them in your own payout trigger. See [The payout pipeline](/platform/payout/concepts/payout-pipeline).
81
+
82
+ ## Idempotency keys
83
+
84
+ Each provider-backed method sets `context.idempotency_key` before it calls the provider, unless you pass one:
85
+
86
+ | Method | Default key |
42
87
  | --- | --- |
43
- | `createPayoutAccount(input)` | Create the account and the provider's connected account |
44
- | `createOnboarding(input)` | Create or update onboarding via the provider |
45
- | `createPayouts(input)` | Initiate a transfer through the provider (account must be `ACTIVE`) |
46
- | `getWebhookActionAndData(input)` | Ask the provider to parse a raw webhook payload |
47
- | `getOptions()` | Read the module's configured options (with defaults applied) |
48
-
49
- <Warning>
50
- Prefer [workflows](/platform/payout/reference/workflows) for anything that
51
- touches the provider or links records. The generated `updatePayouts` /
52
- `updatePayoutAccounts` write status directly and do **not** call the provider.
53
- </Warning>
88
+ | `createPayoutAccount` | The new `pacc` id |
89
+ | `createOnboarding` | The `account_id` |
90
+ | `createPayouts` | The new `pout` id |
91
+
92
+ `createPayoutWorkflow` passes the order id, which replaces the `pout` default. Stripe Connect forwards the key to Stripe on account and transfer creation.