@mercurjs/docs 2.3.8-canary.2 → 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.
- package/content/learn/architecture.mdx +166 -263
- package/content/platform/attribute/concepts/attribute-types.mdx +73 -46
- package/content/platform/attribute/concepts/global-vs-inline.mdx +54 -32
- package/content/platform/attribute/concepts/variant-axes.mdx +54 -32
- package/content/platform/attribute/guides/attach-attributes-to-a-product.mdx +96 -53
- package/content/platform/attribute/guides/create-a-variant-axis.mdx +106 -57
- package/content/platform/attribute/guides/create-an-attribute.mdx +105 -56
- package/content/platform/attribute/overview.mdx +37 -33
- package/content/platform/attribute/reference/data-models.mdx +53 -38
- package/content/platform/attribute/reference/events.mdx +47 -24
- package/content/platform/attribute/reference/links.mdx +98 -27
- package/content/platform/attribute/reference/service.mdx +71 -32
- package/content/platform/attribute/reference/workflows.mdx +158 -28
- package/content/platform/catalog/concepts/master-products.mdx +39 -37
- package/content/platform/catalog/concepts/product-seller-allowlist.mdx +56 -35
- package/content/platform/catalog/concepts/status-lifecycle.mdx +55 -49
- package/content/platform/catalog/concepts/variants-categories-collections.mdx +55 -35
- package/content/platform/catalog/guides/allowlist-stores.mdx +100 -37
- package/content/platform/catalog/guides/create-a-master-product.mdx +124 -48
- package/content/platform/catalog/guides/publish-or-reject-a-product.mdx +120 -55
- package/content/platform/catalog/overview.mdx +39 -35
- package/content/platform/catalog/reference/data-models.mdx +154 -43
- package/content/platform/catalog/reference/events.mdx +41 -25
- package/content/platform/catalog/reference/links.mdx +196 -28
- package/content/platform/catalog/reference/service.mdx +57 -35
- package/content/platform/catalog/reference/workflows.mdx +124 -30
- package/content/platform/commission/concepts/order-commission-lines.mdx +54 -52
- package/content/platform/commission/concepts/rule-matching.mdx +53 -63
- package/content/platform/commission/concepts/rules-and-rates.mdx +61 -59
- package/content/platform/commission/guides/batch-update-rules.mdx +69 -41
- package/content/platform/commission/guides/create-a-rate.mdx +101 -53
- package/content/platform/commission/guides/refresh-order-commission-lines.mdx +104 -35
- package/content/platform/commission/overview.mdx +43 -35
- package/content/platform/commission/reference/data-models.mdx +59 -45
- package/content/platform/commission/reference/events.mdx +28 -34
- package/content/platform/commission/reference/links.mdx +42 -33
- package/content/platform/commission/reference/service.mdx +103 -28
- package/content/platform/commission/reference/workflows.mdx +101 -27
- package/content/platform/offer/concepts/pricing-and-inventory.mdx +63 -49
- package/content/platform/offer/concepts/shipping.mdx +28 -29
- package/content/platform/offer/concepts/what-is-an-offer.mdx +48 -45
- package/content/platform/offer/guides/bulk-create-offers.mdx +97 -61
- package/content/platform/offer/guides/create-an-offer.mdx +112 -60
- package/content/platform/offer/guides/manage-offer-inventory.mdx +78 -38
- package/content/platform/offer/overview.mdx +39 -32
- package/content/platform/offer/reference/data-models.mdx +77 -47
- package/content/platform/offer/reference/events.mdx +44 -18
- package/content/platform/offer/reference/links.mdx +92 -40
- package/content/platform/offer/reference/service.mdx +66 -31
- package/content/platform/offer/reference/workflows.mdx +146 -37
- package/content/platform/order-group/concepts/computed-totals.mdx +40 -37
- package/content/platform/order-group/concepts/order-splitting.mdx +41 -39
- package/content/platform/order-group/concepts/the-order-group.mdx +39 -32
- package/content/platform/order-group/guides/list-order-groups.mdx +96 -56
- package/content/platform/order-group/guides/retrieve-an-order-group.mdx +93 -42
- package/content/platform/order-group/guides/split-a-cart.mdx +115 -40
- package/content/platform/order-group/overview.mdx +42 -32
- package/content/platform/order-group/reference/data-models.mdx +48 -31
- package/content/platform/order-group/reference/events.mdx +29 -17
- package/content/platform/order-group/reference/links.mdx +90 -29
- package/content/platform/order-group/reference/service.mdx +55 -31
- package/content/platform/order-group/reference/workflows.mdx +83 -24
- package/content/platform/payout/concepts/account-lifecycle.mdx +64 -67
- package/content/platform/payout/concepts/accounts-and-onboarding.mdx +50 -40
- package/content/platform/payout/concepts/payout-pipeline.mdx +70 -73
- package/content/platform/payout/guides/create-a-payout-account.mdx +65 -41
- package/content/platform/payout/guides/process-a-provider-webhook.mdx +84 -38
- package/content/platform/payout/guides/start-provider-onboarding.mdx +90 -46
- package/content/platform/payout/overview.mdx +43 -34
- package/content/platform/payout/reference/data-models.mdx +63 -36
- package/content/platform/payout/reference/events.mdx +56 -30
- package/content/platform/payout/reference/links.mdx +52 -22
- package/content/platform/payout/reference/service.mdx +72 -33
- package/content/platform/payout/reference/workflows.mdx +80 -19
- package/content/platform/product-edit/concepts/change-actions.mdx +52 -45
- package/content/platform/product-edit/concepts/change-pipeline.mdx +60 -52
- package/content/platform/product-edit/concepts/status-and-auto-confirm.mdx +43 -46
- package/content/platform/product-edit/guides/confirm-or-decline-a-change.mdx +104 -56
- package/content/platform/product-edit/guides/edit-a-product.mdx +126 -46
- package/content/platform/product-edit/guides/request-a-revision.mdx +99 -39
- package/content/platform/product-edit/overview.mdx +41 -34
- package/content/platform/product-edit/reference/data-models.mdx +73 -47
- package/content/platform/product-edit/reference/events.mdx +39 -29
- package/content/platform/product-edit/reference/links.mdx +32 -24
- package/content/platform/product-edit/reference/service.mdx +45 -26
- package/content/platform/product-edit/reference/workflows.mdx +173 -31
- package/content/platform/review/concepts/product-vs-seller-reviews.mdx +42 -41
- package/content/platform/review/concepts/ratings-and-moderation.mdx +45 -44
- package/content/platform/review/concepts/the-review-model.mdx +52 -45
- package/content/platform/review/guides/compute-aggregate-ratings.mdx +62 -37
- package/content/platform/review/guides/create-a-review.mdx +109 -39
- package/content/platform/review/guides/moderate-a-review.mdx +83 -37
- package/content/platform/review/guides/respond-to-a-review.mdx +85 -40
- package/content/platform/review/overview.mdx +43 -33
- package/content/platform/review/reference/data-models.mdx +41 -27
- package/content/platform/review/reference/events.mdx +55 -37
- package/content/platform/review/reference/links.mdx +80 -22
- package/content/platform/review/reference/service.mdx +61 -33
- package/content/platform/review/reference/workflows.mdx +80 -24
- package/content/platform/store/concepts/lifecycle.mdx +68 -44
- package/content/platform/store/concepts/store-entity.mdx +45 -27
- package/content/platform/store/concepts/team.mdx +56 -35
- package/content/platform/store/guides/create-a-store.mdx +102 -35
- package/content/platform/store/guides/manage-the-team.mdx +122 -32
- package/content/platform/store/guides/moderate-a-store.mdx +107 -38
- package/content/platform/store/overview.mdx +41 -34
- package/content/platform/store/reference/data-models.mdx +132 -57
- package/content/platform/store/reference/events.mdx +42 -17
- package/content/platform/store/reference/links.mdx +67 -49
- package/content/platform/store/reference/service.mdx +105 -33
- package/content/platform/store/reference/workflows.mdx +304 -24
- package/content/references/panel-extensions/layouts.mdx +169 -0
- package/llms.txt +112 -111
- package/package.json +1 -1
|
@@ -1,50 +1,74 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Order group service methods"
|
|
3
3
|
sidebarTitle: "Service"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Seller module service methods for OrderGroup records, the filters they accept, and how they compute totals."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
Order
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Order group records live in the Seller module, so you read and write them through the Seller module service. Use it in subscribers, scheduled jobs, and custom steps when you need group rows without child orders. For child orders and their statuses, use the [read workflows](/platform/order-group/reference/workflows) instead.
|
|
8
|
+
|
|
9
|
+
## Resolve the service
|
|
10
|
+
|
|
11
|
+
Resolve the Seller module from the Medusa container with `MercurModules.SELLER`. There is no `ORDER_GROUP` module key.
|
|
11
12
|
|
|
12
13
|
```ts
|
|
13
14
|
import { MercurModules } from "@mercurjs/types"
|
|
14
15
|
|
|
15
16
|
const sellerModuleService = container.resolve(MercurModules.SELLER)
|
|
16
17
|
|
|
17
|
-
const [
|
|
18
|
-
customer_id: "
|
|
19
|
-
}
|
|
18
|
+
const [groups, count] = await sellerModuleService.listAndCountOrderGroups(
|
|
19
|
+
{ customer_id: "cus_01JD8Q1234567" },
|
|
20
|
+
{ take: 20 }
|
|
21
|
+
)
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
There is no `ORDER_GROUP` module key. The `OrderGroup` model is registered on
|
|
24
|
-
the Seller module, so its service methods live on `MercurModules.SELLER`.
|
|
25
|
-
</Note>
|
|
24
|
+
The call returns the customer’s 20 newest groups and the total number of groups that match.
|
|
26
25
|
|
|
27
|
-
##
|
|
26
|
+
## Read methods
|
|
28
27
|
|
|
29
|
-
The `OrderGroup
|
|
28
|
+
The Seller module overrides the generated read methods for `OrderGroup`. All three run one SQL query through `OrderGroupRepository`, which fills `seller_count` and `total`:
|
|
30
29
|
|
|
31
|
-
| Method |
|
|
30
|
+
| Method | Returns |
|
|
32
31
|
| --- | --- |
|
|
33
|
-
| `
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
| `deleteOrderGroups(ids)` | Delete one or more groups |
|
|
39
|
-
|
|
40
|
-
The list, count, and retrieve methods run through the module's order-group
|
|
41
|
-
repository, which aggregates each group's child orders to fill in the computed
|
|
42
|
-
`seller_count` and `total`. Supported filters include `id`, `customer_id`,
|
|
43
|
-
`seller_id`, `status`, `sales_channel_id`, `created_at`, `updated_at`, and `q`.
|
|
32
|
+
| `listOrderGroups(filters?, config?, sharedContext?)` | `Promise<OrderGroupDTO[]>` |
|
|
33
|
+
| `listAndCountOrderGroups(filters?, config?, sharedContext?)` | `Promise<[OrderGroupDTO[], number]>` |
|
|
34
|
+
| `retrieveOrderGroup(id, config?, sharedContext?)` | `Promise<OrderGroupDTO>` |
|
|
35
|
+
|
|
36
|
+
From `config`, the repository reads `take`, `skip`, and `order` only. It ignores `select` and `relations` and always returns every column plus the two computed fields.
|
|
44
37
|
|
|
45
38
|
<Warning>
|
|
46
|
-
|
|
47
|
-
need aggregated child-order status, and for the checkout split. The service
|
|
48
|
-
returns the group row and its computed totals but does **not** expand child
|
|
49
|
-
orders' payment/fulfillment status, emit events, or run compensation.
|
|
39
|
+
`retrieveOrderGroup` doesn’t throw when no group has the id: it resolves to `undefined`. Check the value before you use it, or use `getOrderGroupDetailWorkflow`, which throws `NOT_FOUND`.
|
|
50
40
|
</Warning>
|
|
41
|
+
|
|
42
|
+
## Write methods
|
|
43
|
+
|
|
44
|
+
The write methods are the ones `MedusaService` generates for the model:
|
|
45
|
+
|
|
46
|
+
| Method | Description |
|
|
47
|
+
| --- | --- |
|
|
48
|
+
| `createOrderGroups(data)` | Creates one group from a `CreateOrderGroupDTO`, or several from an array |
|
|
49
|
+
| `updateOrderGroups(data)` | Updates groups from `UpdateOrderGroupDTO` objects |
|
|
50
|
+
| `deleteOrderGroups(ids)` | Deletes groups by id |
|
|
51
|
+
| `softDeleteOrderGroups(ids)` | Sets `deleted_at` on groups |
|
|
52
|
+
| `restoreOrderGroups(ids)` | Clears `deleted_at` on groups |
|
|
53
|
+
|
|
54
|
+
Creating a group doesn’t create orders or links. Checkout does that in `completeCartWithSplitOrdersWorkflow`, so prefer the workflow over calling `createOrderGroups` yourself.
|
|
55
|
+
|
|
56
|
+
## Filters and sorting
|
|
57
|
+
|
|
58
|
+
The repository builds its own SQL and reads only the top-level keys below. It ignores every other key, including `$and` and `$or`.
|
|
59
|
+
|
|
60
|
+
| Filter | Matches |
|
|
61
|
+
| --- | --- |
|
|
62
|
+
| `id` | Group id, as a string or an array |
|
|
63
|
+
| `customer_id` | Customer id, as a string or an array |
|
|
64
|
+
| `cart_id` | Cart id, as a string or an array |
|
|
65
|
+
| `seller_id` | Groups with at least one child order from the seller |
|
|
66
|
+
| `status` | Groups with at least one child order in the status |
|
|
67
|
+
| `payment_status` | Groups with at least one child order in the derived payment status |
|
|
68
|
+
| `sales_channel_id` | Groups with at least one child order in the sales channel |
|
|
69
|
+
| `created_at`, `updated_at` | A date or an operator map: `$eq`, `$ne`, `$lt`, `$lte`, `$gt`, `$gte`, `$in`, `$nin`, `$like`, `$ilike` |
|
|
70
|
+
| `q` | Group id or customer id containing the text, or a display id such as `#G98`, `G98`, or `98` on the group or a child order |
|
|
71
|
+
|
|
72
|
+
The order-level filters only choose which groups match. `seller_count` and `total` still cover every order in each matching group. Pass `id` as a plain string or array: an `{ $in: [...] }` map isn’t read.
|
|
73
|
+
|
|
74
|
+
`config.order` sorts by `display_id`, `created_at`, or `updated_at`, with `ASC` or `DESC`. Other keys are ignored. Without a valid key, groups come back by `created_at` descending.
|
|
@@ -1,39 +1,98 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Order group workflows"
|
|
3
3
|
sidebarTitle: "Workflows"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Input, output, hooks, steps, and events of the checkout split and the order group read workflows."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
Group domain. Import workflows from `@mercurjs/core/workflows` and run them
|
|
9
|
-
against the Medusa container.
|
|
7
|
+
Three workflows and one step make up the order group surface: the checkout split that creates groups, and two read workflows that return groups with derived child order statuses. Import them from `@mercurjs/core/workflows` and run them with the Medusa container.
|
|
10
8
|
|
|
11
|
-
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
completeCartWithSplitOrdersWorkflow,
|
|
12
|
+
createOrderGroupStep,
|
|
13
|
+
getOrderGroupDetailWorkflow,
|
|
14
|
+
getOrderGroupsListWorkflow,
|
|
15
|
+
} from "@mercurjs/core/workflows"
|
|
16
|
+
```
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
## completeCartWithSplitOrdersWorkflow
|
|
19
|
+
|
|
20
|
+
Completes a cart by creating one order per seller under a new order group. Workflow id `complete-cart-with-split-orders`. Mercur’s cart completion route and `processPaymentWorkflow` both run it.
|
|
21
|
+
|
|
22
|
+
| Input | Type | Description |
|
|
14
23
|
| --- | --- | --- |
|
|
15
|
-
| `
|
|
24
|
+
| `cart_id` | `string` | The cart to complete |
|
|
25
|
+
|
|
26
|
+
It returns `{ order_group_id: string }`. If a group with the same `cart_id` already exists, the workflow skips the split and returns that group’s id.
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
The workflow stores its execution (`store: true`) and keeps it for 3 days. It holds a lock on the cart id with a 30-second wait timeout and a 2-minute time to live, so concurrent completions of one cart run one at a time.
|
|
18
29
|
|
|
19
|
-
|
|
30
|
+
### Hooks
|
|
31
|
+
|
|
32
|
+
| Hook | Receives | Use it to |
|
|
20
33
|
| --- | --- | --- |
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
34
|
+
| `validate` | `{ input, cart }` | Throw to stop the checkout before anything is created |
|
|
35
|
+
| `setOrderStatus` | `{ cart }` | Return `{ status?, status_by_seller_id? }` to set each child order’s initial status; the default is `pending` |
|
|
36
|
+
|
|
37
|
+
`SetOrderStatusHookResult` is exported with the workflow. `status_by_seller_id` is keyed by seller id and takes precedence over `status`.
|
|
38
|
+
|
|
39
|
+
### Steps
|
|
40
|
+
|
|
41
|
+
The workflow runs these steps in order. Steps 3 to 6 run only when no group exists for the cart:
|
|
42
|
+
|
|
43
|
+
1. `acquireLockStep` on the cart id, then a query for an existing group and the cart.
|
|
44
|
+
2. `validateCartPaymentsStep`, then the `validate` and `setOrderStatus` hooks.
|
|
45
|
+
3. `validateSellerCartItemsStep` and `validateSellerCartShippingStep`.
|
|
46
|
+
4. `createOrderGroupStep` and `createOrdersStep`, in parallel.
|
|
47
|
+
5. `createRemoteLinkStep`, `mirrorLineItemOfferLinksToOrderStep`, `updateCartsStep` to set `completed_at`, `reserveInventoryStep`, `registerUsageStep`, and two `emitEventStep` calls.
|
|
48
|
+
6. `authorizePaymentSessionStep`, then `splitCapturedCartPaymentWorkflow` and `refreshOrderCommissionLinesWorkflow` as steps.
|
|
49
|
+
7. `releaseLockStep` on the cart id.
|
|
50
|
+
|
|
51
|
+
### Events
|
|
52
|
+
|
|
53
|
+
| Event | Payload |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| `order.placed` | `{ id }`, once per child order |
|
|
56
|
+
| `order_group.created` | `{ id }` of the new group |
|
|
57
|
+
|
|
58
|
+
## getOrderGroupDetailWorkflow
|
|
59
|
+
|
|
60
|
+
Retrieves one order group with its child orders and sets `payment_status` and `fulfillment_status` on each order. Workflow id `get-order-group-detail`. Its input type is `GetOrderGroupDetailWorkflowInput`.
|
|
61
|
+
|
|
62
|
+
| Input | Type | Description |
|
|
63
|
+
| --- | --- | --- |
|
|
64
|
+
| `order_group_id` | `string` | The group to retrieve |
|
|
65
|
+
| `fields` | `string[]` | Fields to return, in `query.graph` syntax |
|
|
66
|
+
|
|
67
|
+
It returns the group. The workflow always adds `id`, `orders.id`, `orders.status`, `orders.version`, `orders.currency_code`, and `orders.items.*` to your fields, plus the payment collection and fulfillment fields it needs for the statuses. It throws `NOT_FOUND` when no group has the id.
|
|
68
|
+
|
|
69
|
+
The workflow removes some data it loaded for itself before it returns:
|
|
70
|
+
|
|
71
|
+
- **`orders.payment_collections`**: kept only when a requested field contains `payment_collections`
|
|
72
|
+
- **`orders.fulfillments`**: kept only when a requested field contains `fulfillments`
|
|
73
|
+
- **`orders.cart`**: kept only when a requested field starts with `orders.cart`
|
|
74
|
+
|
|
75
|
+
## getOrderGroupsListWorkflow
|
|
76
|
+
|
|
77
|
+
Lists order groups with their child orders and derived statuses. Workflow id `get-order-groups-list`. Its input type is `GetOrderGroupsListWorkflowInput`.
|
|
78
|
+
|
|
79
|
+
| Input | Type | Description |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `fields` | `string[]` | Fields to return, in `query.graph` syntax |
|
|
82
|
+
| `variables` | `object`, optional | Filters plus `skip`, `take`, and `order` |
|
|
83
|
+
| `sellerId` | `string \| string[]`, optional | Removes other sellers’ orders from each returned group |
|
|
84
|
+
|
|
85
|
+
It returns `{ rows, metadata }`, where `metadata` holds `count`, `skip`, and `take`. It adds the same child order fields as the detail workflow and removes the same data, and adds `orders.seller.id` when you pass `sellerId`.
|
|
86
|
+
|
|
87
|
+
`sellerId` trims orders but doesn’t filter groups. Pass `variables.seller_id` as well to return only groups where the seller has an order. The filter keys are listed in [Filters and sorting](/platform/order-group/reference/service#filters-and-sorting).
|
|
23
88
|
|
|
24
|
-
|
|
25
|
-
The read workflows always expand the group's child `orders`. Heavy relations
|
|
26
|
-
(`payment_collections`, `fulfillments`) are only kept in the response when you
|
|
27
|
-
request a matching field, and are used to derive each order's `payment_status`
|
|
28
|
-
and `fulfillment_status`.
|
|
29
|
-
</Note>
|
|
89
|
+
## createOrderGroupStep
|
|
30
90
|
|
|
31
|
-
|
|
91
|
+
Creates one `OrderGroup` record. Step id `create-order-group`.
|
|
32
92
|
|
|
33
|
-
|
|
|
93
|
+
| Input | Type | Description |
|
|
34
94
|
| --- | --- | --- |
|
|
35
|
-
| `
|
|
95
|
+
| `cart_id` | `string` | The completed cart |
|
|
96
|
+
| `customer_id` | `string \| null`, optional | The customer on the cart |
|
|
36
97
|
|
|
37
|
-
|
|
38
|
-
[Service reference](/platform/order-group/reference/service). To run side effects
|
|
39
|
-
when a group is created, see the [Event reference](/platform/order-group/reference/events).
|
|
98
|
+
It returns the created group. On rollback, it deletes the group with `deleteOrderGroups`. Use it in your own workflow only when you also create and link the child orders yourself.
|
|
@@ -1,75 +1,72 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Payout account and payout statuses"
|
|
3
3
|
sidebarTitle: "Account lifecycle"
|
|
4
|
-
description: "
|
|
4
|
+
description: "The statuses a payout account and a payout move through, and the provider webhooks that move them."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
| `
|
|
35
|
-
| `
|
|
36
|
-
| `
|
|
37
|
-
|
|
7
|
+
A payout account’s status says whether the seller can receive money, and a payout’s status says where one transfer stands. Neither is set by an operator. The provider decides, sends a webhook, and `processPayoutForWebhookWorkflow` writes the new status. Read this page to know when `createPayoutWorkflow` will accept an account, and what each webhook does.
|
|
8
|
+
|
|
9
|
+
## Account statuses
|
|
10
|
+
|
|
11
|
+
An account’s state is the `status` field on `PayoutAccount`, typed by the `PayoutAccountStatus` enum from `@mercurjs/types`. It defaults to `pending`, and the provider’s `createPayoutAccount` response can overwrite it.
|
|
12
|
+
|
|
13
|
+
| Status | Value | Meaning |
|
|
14
|
+
| --- | --- | --- |
|
|
15
|
+
| `PENDING` | `pending` | Created, setup not finished |
|
|
16
|
+
| `ACTIVE` | `active` | Can receive payouts |
|
|
17
|
+
| `RESTRICTED` | `restricted` | Missing information or a compliance issue |
|
|
18
|
+
| `REJECTED` | `rejected` | Permanently disabled by the provider |
|
|
19
|
+
|
|
20
|
+
Only `active` matters to the rest of the module. `createPayouts` throws `Account 'pacc_…' is not active` for any other status, so a payout never starts on a pending, restricted, or rejected account.
|
|
21
|
+
|
|
22
|
+
## Webhooks set the account status
|
|
23
|
+
|
|
24
|
+
The provider owns the account status, and a webhook carries each change. The provider’s `getWebhookActionAndData` turns a raw webhook into an action and the `pacc` id it applies to. `processPayoutForWebhookWorkflow` then runs the matching step.
|
|
25
|
+
|
|
26
|
+
<img className="block dark:hidden" src="/images/figures/payout-account-lifecycle-light.svg" alt="A Stripe account.updated webhook behind the process-payout-for-webhook workflow. The workflow maps account.activated to the activate-payout-account step and the Active status, account.restricted to Restricted, account.rejected to Rejected, and not_supported to no change." />
|
|
27
|
+
<img className="hidden dark:block" src="/images/figures/payout-account-lifecycle-dark.svg" alt="A Stripe account.updated webhook behind the process-payout-for-webhook workflow. The workflow maps account.activated to the activate-payout-account step and the Active status, account.restricted to Restricted, account.rejected to Rejected, and not_supported to no change." />
|
|
28
|
+
|
|
29
|
+
The workflow doesn’t check the current status before it writes. Any action can move an account from any status, so the provider’s view always wins:
|
|
30
|
+
|
|
31
|
+
| Action | Step | New status |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `account.activated` | `activate-payout-account` | `active` |
|
|
34
|
+
| `account.restricted` | `restrict-payout-account` | `restricted` |
|
|
35
|
+
| `account.rejected` | `reject-payout-account` | `rejected` |
|
|
36
|
+
| `not_supported` | none | unchanged |
|
|
37
|
+
|
|
38
|
+
Each step is `updatePayoutAccountStep`, renamed with `.config({ name })`. It saves the previous status and restores it if the workflow rolls back.
|
|
39
|
+
|
|
40
|
+
## How Stripe Connect maps account updates
|
|
41
|
+
|
|
42
|
+
The Stripe Connect provider handles only Stripe’s `account.updated` event, and it reads the `pacc` id from the Stripe account’s `metadata.account_id`. It picks the action in this order:
|
|
43
|
+
|
|
44
|
+
1. `account.rejected` when `requirements.disabled_reason` starts with `rejected.`
|
|
45
|
+
2. `account.activated` when every enabled `accountValidation` check passes
|
|
46
|
+
3. `account.restricted` in every other case
|
|
47
|
+
|
|
48
|
+
The default checks are `detailsSubmitted`, `chargesEnabled`, `payoutsEnabled`, and `noOutstandingRequirements`, all `true`, with no `requiredCapabilities`.
|
|
38
49
|
|
|
39
50
|
<Note>
|
|
40
|
-
|
|
41
|
-
payout for an account in any other status.
|
|
51
|
+
With Stripe Connect, an account can skip `pending` and go to `restricted` first. Stripe sends `account.updated` while the seller is still onboarding, and an account with outstanding requirements maps to `account.restricted`. It becomes `active` on a later update.
|
|
42
52
|
</Note>
|
|
43
53
|
|
|
44
|
-
##
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
<Tip>
|
|
66
|
-
A `RESTRICTED` account is not terminal. Once the seller resolves the
|
|
67
|
-
provider's requirements, the provider emits `account.activated` again and the
|
|
68
|
-
account returns to `ACTIVE`. `REJECTED` is the only permanent state.
|
|
69
|
-
</Tip>
|
|
70
|
-
|
|
71
|
-
## Payout status
|
|
72
|
-
|
|
73
|
-
An individual transfer carries its own `PayoutStatus` (`PENDING` → `PROCESSING`
|
|
74
|
-
→ `PAID`, or `FAILED` / `CANCELED`). Provider webhooks advance it through the
|
|
75
|
-
same workflow. See [The payout pipeline](/platform/payout/concepts/payout-pipeline).
|
|
54
|
+
## Payout statuses
|
|
55
|
+
|
|
56
|
+
Each payout carries its own `PayoutStatus`. The module creates the row as `pending`, then stores the status the provider’s `createPayout` returns.
|
|
57
|
+
|
|
58
|
+
| Status | Value | Set by |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `PENDING` | `pending` | Row creation |
|
|
61
|
+
| `PROCESSING` | `processing` | A `payout.processing` webhook |
|
|
62
|
+
| `PAID` | `paid` | The provider response or a `payout.paid` webhook |
|
|
63
|
+
| `FAILED` | `failed` | A `payout.failed` webhook |
|
|
64
|
+
| `CANCELED` | `canceled` | A `payout.canceled` webhook |
|
|
65
|
+
|
|
66
|
+
The Stripe Connect provider returns `paid` as soon as Stripe creates the transfer, and it maps no Stripe event to a `payout.*` action. The `payout.*` branches exist for providers that settle asynchronously.
|
|
67
|
+
|
|
68
|
+
## Related
|
|
69
|
+
|
|
70
|
+
- [Process a provider webhook](/platform/payout/guides/process-a-provider-webhook) to see the webhook path end to end
|
|
71
|
+
- [The payout pipeline](/platform/payout/concepts/payout-pipeline) for what happens once an account is active
|
|
72
|
+
- [Workflows reference](/platform/payout/reference/workflows) for every step name
|
|
@@ -1,70 +1,80 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
2
|
+
title: "Payout accounts and onboarding"
|
|
3
3
|
sidebarTitle: "Accounts & onboarding"
|
|
4
|
-
description: "
|
|
4
|
+
description: "How a seller connects to the payout provider, and where provider data such as account ids and onboarding links lives."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
where provider-specific data lives.
|
|
7
|
+
A payout account is a seller’s connection to the payout provider, and onboarding is the provider-side setup the seller completes before money can reach them. You need both before you can pay a seller. Mercur stores the provider’s own data on each record without reading it, so the same records work for any provider.
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## The payout account
|
|
11
10
|
|
|
12
|
-
A payout account is the
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
created for a seller through `createPayoutAccountWorkflow`, which also links the
|
|
16
|
-
account to the store.
|
|
11
|
+
A payout account is the record a payout is sent to. It’s the `PayoutAccount` data model (table `payout_account`, id prefix `pacc`), and the `seller_payout_account` link ties it to exactly one seller.
|
|
12
|
+
|
|
13
|
+
You create one with `createPayoutAccountWorkflow`. The workflow checks the seller has no account yet, asks the module to create the account, and links it to the seller:
|
|
17
14
|
|
|
18
15
|
```ts
|
|
16
|
+
import { createPayoutAccountWorkflow } from "@mercurjs/core/workflows"
|
|
17
|
+
|
|
19
18
|
const { result } = await createPayoutAccountWorkflow(container).run({
|
|
20
19
|
input: {
|
|
21
|
-
seller_id: "
|
|
22
|
-
|
|
23
|
-
data: { /* forwarded to the provider */ },
|
|
20
|
+
seller_id: "sel_01JD8Q1234567",
|
|
21
|
+
data: { country: "US" },
|
|
24
22
|
},
|
|
25
23
|
})
|
|
26
24
|
```
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
Inside the module, `createPayoutAccount` runs 3 writes in order:
|
|
27
|
+
|
|
28
|
+
1. Create the `PayoutAccount` row with your `data` and `context`
|
|
29
|
+
2. Call the provider’s `createPayoutAccount` with `data.account_id` set to the new `pacc` id and `context.idempotency_key` defaulting to the same id
|
|
30
|
+
3. Overwrite the row’s `data` and `status` with what the provider returns
|
|
31
|
+
|
|
32
|
+
If the provider call throws, the module deletes the new row and rethrows. The Stripe Connect provider returns `pending` and the full Stripe account object. The built-in `system` provider returns `active` and an empty object.
|
|
32
33
|
|
|
33
34
|
<Note>
|
|
34
|
-
A
|
|
35
|
-
validates that the seller doesn't already have one before creating it.
|
|
35
|
+
A seller can have only one payout account. `validateSellerPayoutAccountStep` queries `seller_payout_account` and fails with `Seller already has a payout account` when a row exists.
|
|
36
36
|
</Note>
|
|
37
37
|
|
|
38
38
|
## Onboarding
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
`onb`). The record is a one-to-one satellite of the payout account.
|
|
40
|
+
Onboarding is the provider’s setup flow: identity checks, bank details, and anything else the provider needs. Its state lives in the `Onboarding` data model (table `onboarding`, id prefix `onb`), a one-to-one child of the payout account.
|
|
41
|
+
|
|
42
|
+
`createOnboardingWorkflow` takes an `account_id` and calls the provider’s `createOnboarding`. The module merges the account’s stored `data` with the `data` you pass, so the provider sees both. With Stripe Connect, the account’s `data.id` is the Stripe account id, and you pass `refresh_url` and `return_url`:
|
|
44
43
|
|
|
45
44
|
```ts
|
|
46
|
-
|
|
45
|
+
import { createOnboardingWorkflow } from "@mercurjs/core/workflows"
|
|
46
|
+
|
|
47
|
+
const { result } = await createOnboardingWorkflow(container).run({
|
|
47
48
|
input: {
|
|
48
|
-
account_id: "
|
|
49
|
-
|
|
49
|
+
account_id: "pacc_01JD8Q1234567",
|
|
50
|
+
data: {
|
|
51
|
+
refresh_url: "https://vendor.example.com/payouts/refresh",
|
|
52
|
+
return_url: "https://vendor.example.com/payouts",
|
|
53
|
+
},
|
|
50
54
|
},
|
|
51
55
|
})
|
|
52
56
|
```
|
|
53
57
|
|
|
54
|
-
The
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
The result’s `data` holds what the provider returned. For Stripe Connect, that’s an account link object, and `data.url` is the hosted onboarding page. The workflow is safe to re-run:
|
|
59
|
+
|
|
60
|
+
- **First run**: creates the `Onboarding` record
|
|
61
|
+
- **Later runs**: update the same record with new provider data, which you need when a Stripe link expires
|
|
62
|
+
|
|
63
|
+
## Provider data and context
|
|
64
|
+
|
|
65
|
+
Every payout record has a `data` JSON field, and accounts and onboarding records also have `context`. They play different roles:
|
|
66
|
+
|
|
67
|
+
- **`data`**: the provider’s durable payload, such as the Stripe account, the account link, or the transfer
|
|
68
|
+
- **`context`**: per-call hints for the provider, today only `idempotency_key`
|
|
69
|
+
|
|
70
|
+
The module never interprets `data`. It passes it to the provider and stores what comes back. When you switch providers, the records keep the same shape.
|
|
58
71
|
|
|
59
|
-
|
|
72
|
+
<Warning>
|
|
73
|
+
A payout account’s `data` holds the raw provider object. For Stripe Connect, that’s the full `Stripe.Account`, so select only the fields you need before you return it from a public route.
|
|
74
|
+
</Warning>
|
|
60
75
|
|
|
61
|
-
|
|
62
|
-
provider-specific values live, such as the Stripe account id, onboarding URLs, or
|
|
63
|
-
transfer references. The module never interprets these fields. It forwards them
|
|
64
|
-
to and from the provider.
|
|
76
|
+
## Related
|
|
65
77
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
passed straight through the `IPayoutProvider` interface.
|
|
70
|
-
</Tip>
|
|
78
|
+
- [Account lifecycle](/platform/payout/concepts/account-lifecycle) for the statuses an account moves through after onboarding
|
|
79
|
+
- [Create a payout account](/platform/payout/guides/create-a-payout-account) for a full route example
|
|
80
|
+
- [Start provider onboarding](/platform/payout/guides/start-provider-onboarding) for the onboarding route
|