@mercurjs/docs 2.2.0-canary.40
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/README.md +24 -0
- package/content/blocks.mdx +209 -0
- package/content/learn/architecture.mdx +215 -0
- package/content/learn/attributes.mdx +89 -0
- package/content/learn/blocks.mdx +99 -0
- package/content/learn/commissions.mdx +175 -0
- package/content/learn/concepts.mdx +84 -0
- package/content/learn/installation.mdx +117 -0
- package/content/learn/introduction.mdx +71 -0
- package/content/learn/offers.mdx +78 -0
- package/content/learn/order-groups.mdx +114 -0
- package/content/learn/payouts.mdx +175 -0
- package/content/learn/product-requests.mdx +82 -0
- package/content/learn/products.mdx +79 -0
- package/content/learn/registry.mdx +155 -0
- package/content/learn/seller-members.mdx +62 -0
- package/content/learn/sellers.mdx +118 -0
- package/content/migration/from-1-x-to-2-0.mdx +152 -0
- package/content/migration/from-2-0-to-2-1.mdx +105 -0
- package/content/migration/overview.mdx +58 -0
- package/content/references/api/admin/commission-rates/batch-commission-rules.mdx +70 -0
- package/content/references/api/admin/commission-rates/create-commission-rate.mdx +95 -0
- package/content/references/api/admin/commission-rates/delete-commission-rate.mdx +40 -0
- package/content/references/api/admin/commission-rates/list-commission-rates.mdx +92 -0
- package/content/references/api/admin/commission-rates/retrieve-commission-rate.mdx +70 -0
- package/content/references/api/admin/commission-rates/update-commission-rate.mdx +84 -0
- package/content/references/api/admin/members/list-members.mdx +61 -0
- package/content/references/api/admin/offers/batch-create-offers.mdx +130 -0
- package/content/references/api/admin/offers/delete-offer.mdx +40 -0
- package/content/references/api/admin/offers/list-offers.mdx +106 -0
- package/content/references/api/admin/offers/retrieve-offer.mdx +82 -0
- package/content/references/api/admin/order-groups/list-order-groups.mdx +77 -0
- package/content/references/api/admin/order-groups/retrieve-order-group.mdx +56 -0
- package/content/references/api/admin/orders/get-order-commission-lines.mdx +65 -0
- package/content/references/api/admin/orders/get-order-order-group.mdx +56 -0
- package/content/references/api/admin/orders/list-orders.mdx +79 -0
- package/content/references/api/admin/payouts/list-payouts.mdx +75 -0
- package/content/references/api/admin/payouts/retrieve-payout.mdx +62 -0
- package/content/references/api/admin/product-attributes/create-attribute-value.mdx +76 -0
- package/content/references/api/admin/product-attributes/create-product-attribute.mdx +99 -0
- package/content/references/api/admin/product-attributes/delete-attribute-value.mdx +52 -0
- package/content/references/api/admin/product-attributes/delete-product-attribute.mdx +40 -0
- package/content/references/api/admin/product-attributes/list-product-attributes.mdx +91 -0
- package/content/references/api/admin/product-attributes/retrieve-product-attribute.mdx +81 -0
- package/content/references/api/admin/product-attributes/update-attribute-value.mdx +66 -0
- package/content/references/api/admin/product-attributes/update-product-attribute.mdx +76 -0
- package/content/references/api/admin/product-changes/cancel-product-change.mdx +68 -0
- package/content/references/api/admin/product-changes/confirm-product-change.mdx +47 -0
- package/content/references/api/admin/products/batch-product-attributes.mdx +96 -0
- package/content/references/api/admin/products/confirm-product.mdx +59 -0
- package/content/references/api/admin/products/create-product-variant.mdx +78 -0
- package/content/references/api/admin/products/create-product.mdx +169 -0
- package/content/references/api/admin/products/delete-product-variant.mdx +48 -0
- package/content/references/api/admin/products/delete-product.mdx +40 -0
- package/content/references/api/admin/products/list-product-variants.mdx +91 -0
- package/content/references/api/admin/products/list-products.mdx +109 -0
- package/content/references/api/admin/products/preview-product.mdx +78 -0
- package/content/references/api/admin/products/reject-product.mdx +59 -0
- package/content/references/api/admin/products/request-product-changes.mdx +59 -0
- package/content/references/api/admin/products/retrieve-product-variant.mdx +73 -0
- package/content/references/api/admin/products/retrieve-product.mdx +96 -0
- package/content/references/api/admin/products/set-product-sellers.mdx +47 -0
- package/content/references/api/admin/products/update-product-variant.mdx +89 -0
- package/content/references/api/admin/products/update-product.mdx +133 -0
- package/content/references/api/admin/sellers/add-seller-member.mdx +63 -0
- package/content/references/api/admin/sellers/approve-seller.mdx +54 -0
- package/content/references/api/admin/sellers/create-seller.mdx +100 -0
- package/content/references/api/admin/sellers/delete-member-invite.mdx +42 -0
- package/content/references/api/admin/sellers/delete-professional-details.mdx +52 -0
- package/content/references/api/admin/sellers/invite-seller-member.mdx +63 -0
- package/content/references/api/admin/sellers/list-member-invites.mdx +71 -0
- package/content/references/api/admin/sellers/list-seller-members.mdx +71 -0
- package/content/references/api/admin/sellers/list-seller-products.mdx +85 -0
- package/content/references/api/admin/sellers/list-sellers.mdx +96 -0
- package/content/references/api/admin/sellers/remove-seller-member.mdx +42 -0
- package/content/references/api/admin/sellers/resend-member-invite.mdx +55 -0
- package/content/references/api/admin/sellers/retrieve-seller.mdx +81 -0
- package/content/references/api/admin/sellers/suspend-seller.mdx +60 -0
- package/content/references/api/admin/sellers/terminate-seller.mdx +60 -0
- package/content/references/api/admin/sellers/unsuspend-seller.mdx +52 -0
- package/content/references/api/admin/sellers/unterminate-seller.mdx +52 -0
- package/content/references/api/admin/sellers/update-seller.mdx +83 -0
- package/content/references/api/admin/sellers/upsert-payment-details.mdx +88 -0
- package/content/references/api/admin/sellers/upsert-professional-details.mdx +78 -0
- package/content/references/api/admin/sellers/upsert-seller-address.mdx +91 -0
- package/content/references/api/admin.mdx +134 -0
- package/content/references/api/conventions.mdx +109 -0
- package/content/references/api/store/carts/add-line-item.mdx +115 -0
- package/content/references/api/store/carts/add-shipping-methods.mdx +84 -0
- package/content/references/api/store/carts/apply-promotions.mdx +83 -0
- package/content/references/api/store/carts/complete-cart.mdx +98 -0
- package/content/references/api/store/offers/list-offers.mdx +161 -0
- package/content/references/api/store/offers/retrieve-offer.mdx +112 -0
- package/content/references/api/store/order-groups/list-order-groups.mdx +105 -0
- package/content/references/api/store/order-groups/retrieve-order-group.mdx +78 -0
- package/content/references/api/store/product-attributes/list-product-attributes.mdx +134 -0
- package/content/references/api/store/product-attributes/retrieve-product-attribute.mdx +78 -0
- package/content/references/api/store/product-categories/list-product-categories.mdx +126 -0
- package/content/references/api/store/product-categories/retrieve-product-category.mdx +79 -0
- package/content/references/api/store/products/list-products.mdx +184 -0
- package/content/references/api/store/products/retrieve-product.mdx +107 -0
- package/content/references/api/store/search/search.mdx +136 -0
- package/content/references/api/store/sellers/list-sellers.mdx +105 -0
- package/content/references/api/store/sellers/retrieve-seller.mdx +62 -0
- package/content/references/api/store/shipping-options/list-shipping-options.mdx +74 -0
- package/content/references/api/store.mdx +67 -0
- package/content/references/api/vendor/members/accept-member-invite.mdx +65 -0
- package/content/references/api/vendor/members/retrieve-current-member.mdx +51 -0
- package/content/references/api/vendor/members/update-current-member.mdx +70 -0
- package/content/references/api/vendor/offers/batch-create-offers.mdx +123 -0
- package/content/references/api/vendor/offers/batch-offer-inventory-items.mdx +103 -0
- package/content/references/api/vendor/offers/create-offer.mdx +141 -0
- package/content/references/api/vendor/offers/delete-offer.mdx +51 -0
- package/content/references/api/vendor/offers/list-offers.mdx +125 -0
- package/content/references/api/vendor/offers/retrieve-offer.mdx +74 -0
- package/content/references/api/vendor/offers/update-offer.mdx +82 -0
- package/content/references/api/vendor/orders/cancel-fulfillment.mdx +63 -0
- package/content/references/api/vendor/orders/cancel-order.mdx +56 -0
- package/content/references/api/vendor/orders/complete-order.mdx +55 -0
- package/content/references/api/vendor/orders/create-fulfillment.mdx +76 -0
- package/content/references/api/vendor/orders/create-shipment.mdx +99 -0
- package/content/references/api/vendor/orders/get-order-commission-lines.mdx +66 -0
- package/content/references/api/vendor/orders/list-order-changes.mdx +75 -0
- package/content/references/api/vendor/orders/list-orders.mdx +103 -0
- package/content/references/api/vendor/orders/mark-fulfillment-delivered.mdx +62 -0
- package/content/references/api/vendor/orders/preview-order.mdx +65 -0
- package/content/references/api/vendor/orders/retrieve-order.mdx +84 -0
- package/content/references/api/vendor/payout-accounts/create-onboarding.mdx +68 -0
- package/content/references/api/vendor/payout-accounts/create-payout-account.mdx +59 -0
- package/content/references/api/vendor/payout-accounts/retrieve-payout-account-by-id.mdx +60 -0
- package/content/references/api/vendor/payout-accounts/retrieve-payout-account.mdx +77 -0
- package/content/references/api/vendor/payouts/list-payouts.mdx +90 -0
- package/content/references/api/vendor/payouts/retrieve-payout.mdx +63 -0
- package/content/references/api/vendor/product-attributes/list-product-attributes.mdx +135 -0
- package/content/references/api/vendor/product-attributes/retrieve-product-attribute.mdx +71 -0
- package/content/references/api/vendor/product-variants/list-variants.mdx +136 -0
- package/content/references/api/vendor/products/batch-product-attributes.mdx +104 -0
- package/content/references/api/vendor/products/cancel-product-change.mdx +62 -0
- package/content/references/api/vendor/products/create-product-variant.mdx +92 -0
- package/content/references/api/vendor/products/create-product.mdx +197 -0
- package/content/references/api/vendor/products/delete-product-variant.mdx +68 -0
- package/content/references/api/vendor/products/delete-product.mdx +62 -0
- package/content/references/api/vendor/products/list-product-variants.mdx +114 -0
- package/content/references/api/vendor/products/list-products.mdx +163 -0
- package/content/references/api/vendor/products/preview-product.mdx +61 -0
- package/content/references/api/vendor/products/retrieve-product-variant.mdx +76 -0
- package/content/references/api/vendor/products/retrieve-product.mdx +69 -0
- package/content/references/api/vendor/products/update-product-variant.mdx +104 -0
- package/content/references/api/vendor/products/update-product.mdx +148 -0
- package/content/references/api/vendor/sellers/create-seller.mdx +150 -0
- package/content/references/api/vendor/sellers/delete-professional-details.mdx +54 -0
- package/content/references/api/vendor/sellers/invite-seller-member.mdx +67 -0
- package/content/references/api/vendor/sellers/list-member-invites.mdx +82 -0
- package/content/references/api/vendor/sellers/list-seller-members.mdx +80 -0
- package/content/references/api/vendor/sellers/list-sellers.mdx +71 -0
- package/content/references/api/vendor/sellers/remove-seller-member.mdx +48 -0
- package/content/references/api/vendor/sellers/retrieve-current-seller-member.mdx +59 -0
- package/content/references/api/vendor/sellers/retrieve-current-seller.mdx +69 -0
- package/content/references/api/vendor/sellers/select-seller.mdx +44 -0
- package/content/references/api/vendor/sellers/update-current-seller.mdx +108 -0
- package/content/references/api/vendor/sellers/update-member-role.mdx +51 -0
- package/content/references/api/vendor/sellers/upsert-payment-details.mdx +74 -0
- package/content/references/api/vendor/sellers/upsert-professional-details.mdx +70 -0
- package/content/references/api/vendor/sellers/upsert-seller-address.mdx +81 -0
- package/content/references/api/vendor.mdx +150 -0
- package/content/references/configuration.mdx +155 -0
- package/content/references/modules/commission.mdx +106 -0
- package/content/references/modules/custom-fields.mdx +45 -0
- package/content/references/modules/media.mdx +55 -0
- package/content/references/modules/offer.mdx +64 -0
- package/content/references/modules/payout.mdx +121 -0
- package/content/references/modules/product-attribute.mdx +111 -0
- package/content/references/modules/product-edit.mdx +80 -0
- package/content/references/modules/search.mdx +112 -0
- package/content/references/modules/seller.mdx +175 -0
- package/content/references/overview.mdx +70 -0
- package/content/references/workflows/cart/add-seller-shipping-method-to-cart.mdx +44 -0
- package/content/references/workflows/cart/complete-cart-with-split-orders.mdx +36 -0
- package/content/references/workflows/cart/list-seller-shipping-options-for-cart.mdx +39 -0
- package/content/references/workflows/cart/update-cart-seller-promotions.mdx +42 -0
- package/content/references/workflows/commission/batch-commission-rules.mdx +45 -0
- package/content/references/workflows/commission/create-commission-rates.mdx +41 -0
- package/content/references/workflows/commission/delete-commission-rates.mdx +34 -0
- package/content/references/workflows/commission/refresh-order-commission-lines.mdx +30 -0
- package/content/references/workflows/commission/update-commission-rates.mdx +35 -0
- package/content/references/workflows/media/set-category-images.mdx +41 -0
- package/content/references/workflows/media/set-collection-images.mdx +41 -0
- package/content/references/workflows/member/accept-member-invite.mdx +33 -0
- package/content/references/workflows/member/add-seller-member.mdx +30 -0
- package/content/references/workflows/member/create-member-invites.mdx +34 -0
- package/content/references/workflows/member/delete-member-invite.mdx +24 -0
- package/content/references/workflows/member/remove-seller-member.mdx +28 -0
- package/content/references/workflows/member/resend-member-invite.mdx +28 -0
- package/content/references/workflows/member/update-member-role.mdx +28 -0
- package/content/references/workflows/member/update-member.mdx +35 -0
- package/content/references/workflows/offer/batch-offer-inventory-items.mdx +58 -0
- package/content/references/workflows/offer/create-offers.mdx +63 -0
- package/content/references/workflows/offer/delete-offers.mdx +35 -0
- package/content/references/workflows/offer/update-offers.mdx +51 -0
- package/content/references/workflows/order/cancel-order-fulfillment.mdx +34 -0
- package/content/references/workflows/order/confirm-claim-request.mdx +25 -0
- package/content/references/workflows/order/confirm-exchange-request.mdx +25 -0
- package/content/references/workflows/order/confirm-order-edit-request.mdx +25 -0
- package/content/references/workflows/order/confirm-return-receive.mdx +25 -0
- package/content/references/workflows/order/create-order-fulfillment.mdx +45 -0
- package/content/references/workflows/order-group/get-order-group-detail.mdx +29 -0
- package/content/references/workflows/order-group/get-order-groups-list.mdx +36 -0
- package/content/references/workflows/overview.mdx +72 -0
- package/content/references/workflows/payout/create-onboarding.mdx +36 -0
- package/content/references/workflows/payout/create-payout-account.mdx +33 -0
- package/content/references/workflows/payout/create-payout.mdx +30 -0
- package/content/references/workflows/payout/process-payout-for-webhook.mdx +32 -0
- package/content/references/workflows/product/confirm-products.mdx +46 -0
- package/content/references/workflows/product/create-products.mdx +63 -0
- package/content/references/workflows/product/link-sellers-to-product-category.mdx +40 -0
- package/content/references/workflows/product/link-sellers-to-product.mdx +40 -0
- package/content/references/workflows/product/reject-product.mdx +46 -0
- package/content/references/workflows/product/request-product-change.mdx +46 -0
- package/content/references/workflows/product-attribute/add-product-attributes-to-product.mdx +49 -0
- package/content/references/workflows/product-attribute/create-and-link-product-attributes-to-product.mdx +46 -0
- package/content/references/workflows/product-attribute/create-product-attribute-values.mdx +43 -0
- package/content/references/workflows/product-attribute/create-product-attributes.mdx +56 -0
- package/content/references/workflows/product-attribute/delete-product-attribute-values.mdx +30 -0
- package/content/references/workflows/product-attribute/delete-product-attributes.mdx +30 -0
- package/content/references/workflows/product-attribute/remove-product-attributes-from-product.mdx +29 -0
- package/content/references/workflows/product-attribute/update-product-attribute-values.mdx +44 -0
- package/content/references/workflows/product-attribute/update-product-attributes-on-product.mdx +41 -0
- package/content/references/workflows/product-attribute/update-product-attributes.mdx +48 -0
- package/content/references/workflows/product-attribute/upsert-product-attribute-values.mdx +43 -0
- package/content/references/workflows/product-edit/auto-confirm-product-change.mdx +39 -0
- package/content/references/workflows/product-edit/cancel-product-change.mdx +49 -0
- package/content/references/workflows/product-edit/confirm-product-change.mdx +57 -0
- package/content/references/workflows/product-edit/create-product-change.mdx +72 -0
- package/content/references/workflows/product-edit/reject-product-change.mdx +54 -0
- package/content/references/workflows/product-edit/stage-product-change.mdx +75 -0
- package/content/references/workflows/seller/approve-seller.mdx +34 -0
- package/content/references/workflows/seller/create-seller-account.mdx +59 -0
- package/content/references/workflows/seller/create-seller-defaults.mdx +22 -0
- package/content/references/workflows/seller/create-sellers.mdx +65 -0
- package/content/references/workflows/seller/delete-seller-professional-details.mdx +37 -0
- package/content/references/workflows/seller/delete-sellers.mdx +24 -0
- package/content/references/workflows/seller/invite-seller.mdx +28 -0
- package/content/references/workflows/seller/suspend-seller.mdx +35 -0
- package/content/references/workflows/seller/terminate-seller.mdx +35 -0
- package/content/references/workflows/seller/unsuspend-seller.mdx +34 -0
- package/content/references/workflows/seller/unterminate-seller.mdx +34 -0
- package/content/references/workflows/seller/update-seller-address.mdx +55 -0
- package/content/references/workflows/seller/update-seller-payment-details.mdx +52 -0
- package/content/references/workflows/seller/update-seller-professional-details.mdx +48 -0
- package/content/references/workflows/seller/update-sellers.mdx +57 -0
- package/content/resources/ai/llms.mdx +74 -0
- package/content/resources/ai/mcp.mdx +152 -0
- package/content/resources/ai/overview.mdx +97 -0
- package/content/resources/customization/custom-fields.mdx +182 -0
- package/content/resources/customization/extend-a-workflow.mdx +39 -0
- package/content/resources/customization/extending-panels.mdx +309 -0
- package/content/resources/deployment/medusa-cloud.mdx +167 -0
- package/content/resources/integrations/notifications.mdx +39 -0
- package/content/resources/integrations/search.mdx +122 -0
- package/content/resources/integrations/stripe-connect.mdx +395 -0
- package/content/resources/tutorials/add-a-block.mdx +74 -0
- package/content/resources/tutorials/attributes-and-variant-axes.mdx +111 -0
- package/content/resources/tutorials/build-a-block.mdx +134 -0
- package/content/resources/tutorials/configure-commissions.mdx +127 -0
- package/content/resources/tutorials/custom-api-route.mdx +142 -0
- package/content/resources/tutorials/custom-panel-page.mdx +78 -0
- package/content/resources/tutorials/first-marketplace.mdx +45 -0
- package/content/resources/tutorials/handle-product-requests.mdx +80 -0
- package/content/resources/tutorials/import-export-products.mdx +96 -0
- package/content/resources/tutorials/master-products-and-offers.mdx +89 -0
- package/content/resources/tutorials/recompose-a-page.mdx +129 -0
- package/content/resources/tutorials/replace-panel-components.mdx +116 -0
- package/content/resources/tutorials/seller-payouts-stripe.mdx +89 -0
- package/content/telemetry.mdx +67 -0
- package/content/tools/api-client.mdx +155 -0
- package/content/tools/cli.mdx +196 -0
- package/content/tools/dashboard-sdk.mdx +35 -0
- package/content/user-guide/admin/overview.mdx +23 -0
- package/content/user-guide/admin/stores.mdx +193 -0
- package/content/user-guide/vendor/overview.mdx +23 -0
- package/content/user-guide/vendor/stores.mdx +209 -0
- package/llms.txt +307 -0
- package/package.json +33 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Stripe Connect Integration"
|
|
3
|
+
description: "Set up Stripe Connect for marketplace payments and seller payouts — from Stripe Dashboard configuration to the full end-to-end payment lifecycle."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
A Mercur marketplace needs **two** Stripe integrations working together. The first is the standard Medusa payment provider, which charges customers at checkout. The second is the Mercur payout provider, which transfers funds from the platform to sellers after orders are fulfilled.
|
|
7
|
+
|
|
8
|
+
This guide covers both — setting up Stripe, configuring both providers, wiring up webhooks, and understanding how money flows from customer to seller.
|
|
9
|
+
|
|
10
|
+
<Tip>
|
|
11
|
+
This page is the configuration reference. For a hands-on walkthrough that takes one seller from onboarding to a settled payout, follow the [Set up seller payouts](/rc/resources/tutorials/seller-payouts-stripe) tutorial after completing the setup below.
|
|
12
|
+
</Tip>
|
|
13
|
+
|
|
14
|
+
<Info>
|
|
15
|
+
Mercur uses Stripe's [Separate Charges and Transfers](https://docs.stripe.com/connect/separate-charges-and-transfers) model. The platform collects the full payment from the customer, then creates transfers to each seller's connected account. This makes the platform the **Merchant of Record**, responsible for VAT, disputes, and chargebacks.
|
|
16
|
+
</Info>
|
|
17
|
+
|
|
18
|
+
## Prerequisites
|
|
19
|
+
|
|
20
|
+
- A [Stripe account](https://dashboard.stripe.com) with Connect enabled
|
|
21
|
+
- Stripe Secret API key and Publishable API key
|
|
22
|
+
- A running Mercur project ([installation guide](/rc/learn/installation))
|
|
23
|
+
- Node.js 20+
|
|
24
|
+
|
|
25
|
+
## Architecture overview
|
|
26
|
+
|
|
27
|
+
The payment lifecycle in a Mercur marketplace flows through two distinct Stripe integrations:
|
|
28
|
+
|
|
29
|
+
```mermaid
|
|
30
|
+
flowchart LR
|
|
31
|
+
C[Customer] -->|PaymentIntent| P[Platform Stripe Account]
|
|
32
|
+
P -->|Transfer| S1[Seller A Connected Account]
|
|
33
|
+
P -->|Transfer| S2[Seller B Connected Account]
|
|
34
|
+
S1 -->|Payout| B1[Seller A Bank]
|
|
35
|
+
S2 -->|Payout| B2[Seller B Bank]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
The two integrations serve different purposes:
|
|
39
|
+
|
|
40
|
+
| Integration | Package | Purpose | Webhook endpoint |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| Customer payments | `@medusajs/medusa/payment-stripe` | Charges customers at checkout | `/hooks/payment/stripe_stripe` |
|
|
43
|
+
| Seller payouts | `@mercurjs/payout-stripe-connect` | Transfers funds to sellers | `/hooks/payout` |
|
|
44
|
+
|
|
45
|
+
## Stripe Dashboard setup
|
|
46
|
+
|
|
47
|
+
<Steps>
|
|
48
|
+
<Step title="Enable Connect">
|
|
49
|
+
In your Stripe Dashboard, go to **Settings → Connect settings** and enable Connect. If you're starting fresh, Stripe will walk you through an onboarding flow to activate your platform account.
|
|
50
|
+
</Step>
|
|
51
|
+
<Step title="Find your API keys">
|
|
52
|
+
Go to **Developers → API keys**. You'll need:
|
|
53
|
+
|
|
54
|
+
- **Secret key** — starts with `sk_test_` (test mode) or `sk_live_` (production)
|
|
55
|
+
- **Publishable key** — starts with `pk_test_` or `pk_live_`
|
|
56
|
+
</Step>
|
|
57
|
+
<Step title="Set environment variables">
|
|
58
|
+
Add the following to your `.env` file:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
STRIPE_API_KEY=sk_test_...
|
|
62
|
+
STRIPE_WEBHOOK_SECRET=whsec_...
|
|
63
|
+
STRIPE_PAYOUT_WEBHOOK_SECRET=whsec_...
|
|
64
|
+
```
|
|
65
|
+
</Step>
|
|
66
|
+
</Steps>
|
|
67
|
+
|
|
68
|
+
## Configure Medusa payment provider
|
|
69
|
+
|
|
70
|
+
The Medusa payment provider handles customer-facing charges. Add it to your `medusa-config.ts`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
module.exports = defineConfig({
|
|
74
|
+
// ...
|
|
75
|
+
modules: [
|
|
76
|
+
{
|
|
77
|
+
resolve: "@medusajs/medusa/payment",
|
|
78
|
+
options: {
|
|
79
|
+
providers: [
|
|
80
|
+
{
|
|
81
|
+
resolve: "@medusajs/medusa/payment-stripe",
|
|
82
|
+
id: "stripe",
|
|
83
|
+
options: {
|
|
84
|
+
apiKey: process.env.STRIPE_API_KEY,
|
|
85
|
+
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
|
|
86
|
+
capture: false, // Use manual capture for marketplace flow
|
|
87
|
+
automatic_payment_methods: true,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Provider options
|
|
98
|
+
|
|
99
|
+
| Option | Type | Description |
|
|
100
|
+
|--------|------|-------------|
|
|
101
|
+
| `apiKey` | `string` | Stripe secret API key |
|
|
102
|
+
| `webhookSecret` | `string` | Signing secret for the payment webhook endpoint |
|
|
103
|
+
| `capture` | `boolean` | Set to `false` for manual capture (required for marketplace split flow) |
|
|
104
|
+
| `automatic_payment_methods` | `boolean` | Enable Stripe's automatic payment method detection |
|
|
105
|
+
| `paymentDescription` | `string` | Optional description shown on customer's bank statement |
|
|
106
|
+
|
|
107
|
+
<Info>
|
|
108
|
+
After configuring the provider, enable Stripe as a payment method in your Admin Panel: **Settings → Regions → Edit region → Payment providers → Stripe**.
|
|
109
|
+
</Info>
|
|
110
|
+
|
|
111
|
+
## Configure Mercur payout provider
|
|
112
|
+
|
|
113
|
+
The payout provider handles transfers from the platform to seller connected accounts. Add it alongside the payment provider in `medusa-config.ts`:
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
module.exports = defineConfig({
|
|
117
|
+
// ...
|
|
118
|
+
modules: [
|
|
119
|
+
// ... payment provider above
|
|
120
|
+
{
|
|
121
|
+
resolve: "@mercurjs/core/modules/payout",
|
|
122
|
+
options: {
|
|
123
|
+
providers: [
|
|
124
|
+
{
|
|
125
|
+
resolve: "@mercurjs/payout-stripe-connect",
|
|
126
|
+
id: "stripe-connect",
|
|
127
|
+
options: {
|
|
128
|
+
apiKey: process.env.STRIPE_API_KEY,
|
|
129
|
+
webhookSecret: process.env.STRIPE_PAYOUT_WEBHOOK_SECRET,
|
|
130
|
+
accountValidation: {
|
|
131
|
+
detailsSubmitted: true,
|
|
132
|
+
chargesEnabled: true,
|
|
133
|
+
payoutsEnabled: true,
|
|
134
|
+
noOutstandingRequirements: true,
|
|
135
|
+
requiredCapabilities: [],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Payout provider options
|
|
147
|
+
|
|
148
|
+
| Option | Type | Default | Description |
|
|
149
|
+
|--------|------|---------|-------------|
|
|
150
|
+
| `apiKey` | `string` | — | Stripe secret API key |
|
|
151
|
+
| `webhookSecret` | `string` | — | Signing secret for the payout webhook endpoint |
|
|
152
|
+
| `accountValidation` | `object` | See below | Controls when a connected account is considered `ACTIVE` |
|
|
153
|
+
|
|
154
|
+
### Account validation options
|
|
155
|
+
|
|
156
|
+
These options determine what conditions a Stripe connected account must meet before Mercur marks it as `ACTIVE` and allows payouts:
|
|
157
|
+
|
|
158
|
+
| Option | Type | Default | Description |
|
|
159
|
+
|--------|------|---------|-------------|
|
|
160
|
+
| `detailsSubmitted` | `boolean` | `true` | Require Stripe to mark onboarding details as submitted |
|
|
161
|
+
| `chargesEnabled` | `boolean` | `true` | Require the account to be enabled for charges |
|
|
162
|
+
| `payoutsEnabled` | `boolean` | `true` | Require the account to be enabled for payouts |
|
|
163
|
+
| `noOutstandingRequirements` | `boolean` | `true` | Treat any pending Stripe requirements as a restricted account |
|
|
164
|
+
| `requiredCapabilities` | `string[]` | `[]` | Require specific Stripe capabilities to be active (e.g. `["card_payments", "transfers"]`) |
|
|
165
|
+
|
|
166
|
+
### Payout module options
|
|
167
|
+
|
|
168
|
+
The payout module itself accepts timing options that control the capture and payout pipeline:
|
|
169
|
+
|
|
170
|
+
| Option | Type | Default | Description |
|
|
171
|
+
|--------|------|---------|-------------|
|
|
172
|
+
| `disabled` | `boolean` | `false` | Disable automatic capture checks and daily payout jobs |
|
|
173
|
+
| `authorizationWindowMs` | `number` | `604800000` (7 days) | How long a payment authorization remains valid |
|
|
174
|
+
| `sellerActionWindowMs` | `number` | `259200000` (72 hours) | How long sellers have to accept/fulfill before cancellation |
|
|
175
|
+
| `captureSafetyBufferMs` | `number` | `86400000` (24 hours) | Safety margin before authorization expiry — capture happens before `authorization expiry - buffer` |
|
|
176
|
+
| `requiredFulfillmentStatus` | `string` | `"fulfilled"` | Minimum fulfillment status before an order is eligible for capture |
|
|
177
|
+
|
|
178
|
+
## Set up webhooks
|
|
179
|
+
|
|
180
|
+
You need **two separate webhook endpoints** in Stripe — one for payment events, one for payout events.
|
|
181
|
+
|
|
182
|
+
<Warning>
|
|
183
|
+
These are two distinct webhook endpoints, each with its own signing secret. Do not combine them into a single endpoint.
|
|
184
|
+
</Warning>
|
|
185
|
+
|
|
186
|
+
<Steps>
|
|
187
|
+
<Step title="Add the payment webhook (Medusa)">
|
|
188
|
+
In the Stripe Dashboard, go to **Developers → Webhooks → Add endpoint**.
|
|
189
|
+
|
|
190
|
+
- **Endpoint URL**: `https://{your-backend-url}/hooks/payment/stripe_stripe`
|
|
191
|
+
- **Events to subscribe to**:
|
|
192
|
+
- `payment_intent.succeeded`
|
|
193
|
+
- `payment_intent.amount_capturable_updated`
|
|
194
|
+
- `payment_intent.payment_failed`
|
|
195
|
+
- `charge.refunded`
|
|
196
|
+
|
|
197
|
+
Copy the signing secret and set it as `STRIPE_WEBHOOK_SECRET` in your `.env`.
|
|
198
|
+
</Step>
|
|
199
|
+
<Step title="Add the payout webhook (Mercur)">
|
|
200
|
+
Add a second webhook endpoint in the Stripe Dashboard.
|
|
201
|
+
|
|
202
|
+
- **Endpoint URL**: `https://{your-backend-url}/hooks/payout`
|
|
203
|
+
- **Events to subscribe to**:
|
|
204
|
+
- `account.updated`
|
|
205
|
+
|
|
206
|
+
Copy the signing secret and set it as `STRIPE_PAYOUT_WEBHOOK_SECRET` in your `.env`.
|
|
207
|
+
</Step>
|
|
208
|
+
</Steps>
|
|
209
|
+
|
|
210
|
+
## How the payment flow works
|
|
211
|
+
|
|
212
|
+
Here's a concrete example. A customer buys items from two sellers:
|
|
213
|
+
|
|
214
|
+
- **Seller A**: €40 (product)
|
|
215
|
+
- **Seller B**: €30 (product)
|
|
216
|
+
- **Shipping**: €10
|
|
217
|
+
- **Cart total**: €80
|
|
218
|
+
|
|
219
|
+
### Step 1 — Authorize payment
|
|
220
|
+
|
|
221
|
+
At checkout, a single `PaymentIntent` is created for €80 with `capture_method: "manual"`. The customer authenticates once (SCA-compliant). No money moves yet — the funds are held on the customer's card.
|
|
222
|
+
|
|
223
|
+
### Step 2 — Split orders
|
|
224
|
+
|
|
225
|
+
Mercur's `completeCartWithSplitOrdersWorkflow` groups items by seller and creates separate orders — one for Seller A (€40) and one for Seller B (€30), plus shipping allocation.
|
|
226
|
+
|
|
227
|
+
### Step 3 — Seller acceptance and fulfillment
|
|
228
|
+
|
|
229
|
+
Each seller reviews and fulfills their order through the Vendor Portal. The payout module's `sellerActionWindowMs` (default: 72 hours) defines how long sellers have to act.
|
|
230
|
+
|
|
231
|
+
### Step 4 — Capture payment
|
|
232
|
+
|
|
233
|
+
Once orders meet the `requiredFulfillmentStatus` (default: `"fulfilled"`), the platform captures the authorized payment. The capture-check job runs automatically and respects the `captureSafetyBufferMs` to ensure capture happens before authorization expiry.
|
|
234
|
+
|
|
235
|
+
<Warning>
|
|
236
|
+
Card authorizations typically expire after **7 days**. The default configuration gives sellers 72 hours to fulfill, with a 24-hour safety buffer before capture. If your business requires longer seller action windows, consider whether the 7-day authorization window is sufficient.
|
|
237
|
+
</Warning>
|
|
238
|
+
|
|
239
|
+
### Step 5 — Commission calculation and transfers
|
|
240
|
+
|
|
241
|
+
After capture, Mercur calculates commission for each order and creates Stripe Transfers for the net amounts:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
Seller A net = €40 - commission
|
|
245
|
+
Seller B net = €30 - commission
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Each transfer is linked to the original charge via `source_transaction` and grouped by `transfer_group` (the order ID).
|
|
249
|
+
|
|
250
|
+
### Step 6 — Bank payouts
|
|
251
|
+
|
|
252
|
+
Stripe automatically pays out connected account balances to sellers' bank accounts on the configured payout schedule. Mercur tracks payout status changes via the `account.updated` webhook.
|
|
253
|
+
|
|
254
|
+
## Seller onboarding
|
|
255
|
+
|
|
256
|
+
When a seller creates a payout account, the following lifecycle begins:
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
PENDING → (Stripe onboarding) → ACTIVE
|
|
260
|
+
↓
|
|
261
|
+
RESTRICTED → ACTIVE (remediation)
|
|
262
|
+
↓
|
|
263
|
+
REJECTED (permanent)
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
1. **Account creation** — Mercur calls `stripe.accounts.create({ type: "express" })`, creating a Stripe Express connected account. The payout account starts in `PENDING` status.
|
|
267
|
+
|
|
268
|
+
2. **Onboarding link** — The seller receives a Stripe-hosted onboarding URL via `stripe.accountLinks.create()`. They complete identity verification, bank account setup, and any required compliance steps directly on Stripe.
|
|
269
|
+
|
|
270
|
+
3. **Webhook activation** — When the seller completes onboarding, Stripe sends an `account.updated` webhook. The provider evaluates the account against the `accountValidation` options and transitions the status:
|
|
271
|
+
- All validation checks pass → `ACTIVE`
|
|
272
|
+
- Missing requirements or disabled reason → `RESTRICTED`
|
|
273
|
+
- Disabled reason starts with `rejected.` → `REJECTED`
|
|
274
|
+
|
|
275
|
+
4. **Ongoing monitoring** — Stripe may send additional `account.updated` events if requirements change. The provider re-evaluates and updates the status accordingly.
|
|
276
|
+
|
|
277
|
+
For more details on payout accounts, balances, and transactions, see [Payout](/rc/learn/payouts).
|
|
278
|
+
|
|
279
|
+
## Transfers vs payouts
|
|
280
|
+
|
|
281
|
+
Stripe uses two distinct concepts for moving money, and it's important to understand the difference:
|
|
282
|
+
|
|
283
|
+
| | Transfer | Payout |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| **What it does** | Moves funds from platform balance to connected account balance | Moves funds from connected account balance to seller's bank account |
|
|
286
|
+
| **Speed** | Instant ledger movement | 1–3 business days (varies by country) |
|
|
287
|
+
| **Status lifecycle** | None — transfers are immediate | `pending` → `in_transit` → `paid` / `failed` |
|
|
288
|
+
| **Who triggers it** | Mercur (via `stripe.transfers.create()`) | Stripe (on the connected account's payout schedule) |
|
|
289
|
+
| **Mercur tracking** | Transfer created with status `PAID` immediately | Status tracked via webhooks |
|
|
290
|
+
|
|
291
|
+
When Mercur's `createPayout` method is called, the Stripe Connect provider creates a **Transfer** (platform → connected account). The actual bank payout (connected account → bank) happens automatically on Stripe's schedule.
|
|
292
|
+
|
|
293
|
+
## Refunds and reversals
|
|
294
|
+
|
|
295
|
+
Refunding a charge does **not** automatically reverse the associated transfers. These are two separate operations:
|
|
296
|
+
|
|
297
|
+
1. **Refund the PaymentIntent** — Returns funds to the customer's payment method
|
|
298
|
+
2. **Reverse the Transfer(s)** — Claws back funds from the connected account(s)
|
|
299
|
+
|
|
300
|
+
For a full refund of a multi-seller order, you would need to reverse each seller's transfer individually. For partial refunds, you need to calculate how much to reverse from each seller based on which items are being refunded.
|
|
301
|
+
|
|
302
|
+
<Info>
|
|
303
|
+
Refund and transfer reversal orchestration is an area where custom workflow logic may be needed depending on your business rules. See Stripe's documentation on [reversing transfers](https://docs.stripe.com/connect/separate-charges-and-transfers#reverse-transfers) for the API details.
|
|
304
|
+
</Info>
|
|
305
|
+
|
|
306
|
+
## EU/EEA considerations
|
|
307
|
+
|
|
308
|
+
### Merchant of Record
|
|
309
|
+
|
|
310
|
+
Using Separate Charges and Transfers makes the platform the Merchant of Record. This means:
|
|
311
|
+
|
|
312
|
+
- The platform collects and remits VAT
|
|
313
|
+
- The platform handles consumer disputes and chargebacks
|
|
314
|
+
- The platform is responsible for PSD2 compliance
|
|
315
|
+
|
|
316
|
+
### Strong Customer Authentication (SCA)
|
|
317
|
+
|
|
318
|
+
SCA is enforced at **authorization time**, not capture time. Since the customer authenticates once during checkout, no additional SCA step is needed when the platform captures later. The single authorization at checkout satisfies the SCA requirement for the entire order.
|
|
319
|
+
|
|
320
|
+
### Authorization windows
|
|
321
|
+
|
|
322
|
+
Card authorizations in Europe typically last **7 days**. Design your seller acceptance workflow to complete well within this window. The payout module options give you control over the timing:
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
{
|
|
326
|
+
resolve: "@mercurjs/core/modules/payout",
|
|
327
|
+
options: {
|
|
328
|
+
authorizationWindowMs: 7 * 24 * 60 * 60 * 1000, // 7 days
|
|
329
|
+
sellerActionWindowMs: 72 * 60 * 60 * 1000, // 72 hours
|
|
330
|
+
captureSafetyBufferMs: 24 * 60 * 60 * 1000, // 24 hours
|
|
331
|
+
// ...providers
|
|
332
|
+
},
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Testing
|
|
337
|
+
|
|
338
|
+
### Test mode
|
|
339
|
+
|
|
340
|
+
Use Stripe test mode keys (`sk_test_`, `pk_test_`) during development. All connected accounts and payments created in test mode are isolated from production.
|
|
341
|
+
|
|
342
|
+
**Test card number**: `4242 4242 4242 4242` (any future expiry, any CVC)
|
|
343
|
+
|
|
344
|
+
### Webhook forwarding with Stripe CLI
|
|
345
|
+
|
|
346
|
+
Since webhooks need to reach your local machine during development, use the [Stripe CLI](https://docs.stripe.com/stripe-cli) to forward events. You need **two separate listeners** — one for each webhook endpoint:
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Terminal 1 — Payment webhooks
|
|
350
|
+
stripe listen --forward-to localhost:9000/hooks/payment/stripe_stripe
|
|
351
|
+
|
|
352
|
+
# Terminal 2 — Payout webhooks
|
|
353
|
+
stripe listen --forward-to localhost:9000/hooks/payout
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
<Tip>
|
|
357
|
+
Each `stripe listen` process outputs its own webhook signing secret. Use these as your `STRIPE_WEBHOOK_SECRET` and `STRIPE_PAYOUT_WEBHOOK_SECRET` respectively during local development.
|
|
358
|
+
</Tip>
|
|
359
|
+
|
|
360
|
+
### Testing the full flow
|
|
361
|
+
|
|
362
|
+
1. Create a seller and complete Stripe's test onboarding flow
|
|
363
|
+
2. Add products from the seller to a cart
|
|
364
|
+
3. Complete checkout using the test card
|
|
365
|
+
4. Verify orders are split by seller in the Admin Panel
|
|
366
|
+
5. Fulfill the orders through the Vendor Portal
|
|
367
|
+
6. Confirm transfers appear in the Stripe Dashboard under the connected account
|
|
368
|
+
|
|
369
|
+
## FAQ
|
|
370
|
+
|
|
371
|
+
<AccordionGroup>
|
|
372
|
+
<Accordion title="Why do I need two webhook endpoints with two secrets?">
|
|
373
|
+
They belong to two different integrations: the payment webhook feeds Medusa's payment provider (charges, captures, refunds), while the payout webhook feeds Mercur's payout provider (connected-account status, transfer status). Each endpoint verifies its own signing secret — combining them silently breaks whichever side's signature doesn't match.
|
|
374
|
+
</Accordion>
|
|
375
|
+
<Accordion title="Why must capture be set to false on the payment provider?">
|
|
376
|
+
The marketplace flow authorizes at checkout and captures later, after sellers fulfill. Automatic capture would take the money before the split-order pipeline (fulfillment checks, commission, payouts) has run — `capture: false` hands that timing to the payout module's capture job.
|
|
377
|
+
</Accordion>
|
|
378
|
+
<Accordion title="A seller finished Stripe onboarding but is still not ACTIVE — why?">
|
|
379
|
+
Check the `accountValidation` options: by default the account must have details submitted, charges and payouts enabled, and **no outstanding requirements**. Stripe often adds follow-up requirements (e.g. extra KYC) after initial onboarding — the account shows as `RESTRICTED` until they're cleared.
|
|
380
|
+
</Accordion>
|
|
381
|
+
<Accordion title="Who handles VAT, disputes, and chargebacks?">
|
|
382
|
+
The platform. Separate Charges and Transfers makes the platform the Merchant of Record — see [EU/EEA considerations](#eueea-considerations) for what that entails.
|
|
383
|
+
</Accordion>
|
|
384
|
+
</AccordionGroup>
|
|
385
|
+
|
|
386
|
+
## Next steps
|
|
387
|
+
|
|
388
|
+
<CardGroup cols={2}>
|
|
389
|
+
<Card title="Set up seller payouts" href="/rc/resources/tutorials/seller-payouts-stripe">
|
|
390
|
+
Hands-on: one seller from onboarding to a settled payout.
|
|
391
|
+
</Card>
|
|
392
|
+
<Card title="Payout" href="/rc/learn/payouts">
|
|
393
|
+
The pipeline reference — statuses, jobs, and webhook events.
|
|
394
|
+
</Card>
|
|
395
|
+
</CardGroup>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Add a feature with a block"
|
|
3
|
+
description: "Install the reviews block end-to-end and see it live across the admin, vendor, and storefront surfaces."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Blocks are complete features — backend module, workflows, API routes, and panel UI — installed as **source code** into your project. This tutorial installs the `reviews` block and follows it across every surface it touches.
|
|
7
|
+
|
|
8
|
+
<Info>
|
|
9
|
+
**Blocks are copied, not installed as dependencies.** `add` writes the block's source files into your project via the aliases in `blocks.json` — you own and can edit every file afterwards. Updates are opt-in through `diff` and `add --overwrite`. When you outgrow the catalog, [build your own block](/rc/resources/tutorials/build-a-block).
|
|
10
|
+
</Info>
|
|
11
|
+
|
|
12
|
+
## Goal
|
|
13
|
+
|
|
14
|
+
Install reviews as a block and see it running everywhere it appears.
|
|
15
|
+
|
|
16
|
+
## Install the block
|
|
17
|
+
|
|
18
|
+
<Steps>
|
|
19
|
+
<Step title="Discover the block">
|
|
20
|
+
Search the registry and inspect what the block ships before installing:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bunx @mercurjs/cli@rc search --query reviews
|
|
24
|
+
bunx @mercurjs/cli@rc view reviews
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
`view` lists the block's files by target (API, admin, vendor) and its dependencies.
|
|
28
|
+
</Step>
|
|
29
|
+
<Step title="Install it">
|
|
30
|
+
```bash
|
|
31
|
+
bunx @mercurjs/cli@rc add reviews
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The CLI copies the source into the directories mapped by your `blocks.json` aliases and prints the block's post-install instructions — module registration, middlewares, migrations.
|
|
35
|
+
</Step>
|
|
36
|
+
<Step title="Run migrations and codegen">
|
|
37
|
+
The block introduced a reviews module, so generate and run its migrations, then refresh the typed route map:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
cd packages/api
|
|
41
|
+
bunx medusa db:generate reviews
|
|
42
|
+
bunx medusa db:migrate
|
|
43
|
+
bunx @mercurjs/cli@rc codegen
|
|
44
|
+
```
|
|
45
|
+
</Step>
|
|
46
|
+
<Step title="See it live">
|
|
47
|
+
Start the project — reviews now appear in the admin panel (moderation), the vendor portal (per-seller reviews), and the Store API (customer-facing review routes).
|
|
48
|
+
</Step>
|
|
49
|
+
</Steps>
|
|
50
|
+
|
|
51
|
+
## Verify
|
|
52
|
+
|
|
53
|
+
1. The block's files exist in your repo under the alias-mapped paths.
|
|
54
|
+
2. Migrations ran cleanly and the API boots.
|
|
55
|
+
3. The admin and vendor panels show their reviews pages; the store review endpoints respond.
|
|
56
|
+
4. `bunx @mercurjs/cli@rc diff reviews` reports no drift from the registry.
|
|
57
|
+
|
|
58
|
+
## FAQ
|
|
59
|
+
|
|
60
|
+
<AccordionGroup>
|
|
61
|
+
<Accordion title="What if I've already modified files the block wants to write?">
|
|
62
|
+
The CLI asks before overwriting existing files (or force it with `--overwrite`). If you've customized a page the block also ships, merge by hand — you're merging source, not resolving package versions.
|
|
63
|
+
</Accordion>
|
|
64
|
+
<Accordion title="How do I update a block later?">
|
|
65
|
+
`bunx @mercurjs/cli@rc diff reviews` shows what changed in the registry since you installed. Take updates with `add reviews --overwrite`, re-applying any local edits afterwards.
|
|
66
|
+
</Accordion>
|
|
67
|
+
</AccordionGroup>
|
|
68
|
+
|
|
69
|
+
## Next steps
|
|
70
|
+
|
|
71
|
+
<CardGroup cols={2}>
|
|
72
|
+
<Card title="Add a custom panel page" href="/rc/resources/tutorials/custom-panel-page" />
|
|
73
|
+
<Card title="Build your own block" href="/rc/resources/tutorials/build-a-block" />
|
|
74
|
+
</CardGroup>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Create attributes and variant axes"
|
|
3
|
+
description: "Build the attribute catalog: a filterable attribute, a variant axis backed by a native product option, and an inline product-scoped axis."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Attributes give the shared catalog structured, typed data — and for `multi_select` attributes, they can drive variant generation. This tutorial sets up the two kinds that matter most: a plain filterable attribute and a **variant axis**, which under the hood is a native Medusa global product option.
|
|
7
|
+
|
|
8
|
+
<Warning>
|
|
9
|
+
This is a release-candidate feature built on Medusa's global product options (Medusa 2.16 preview). Details may still shift before the stable release.
|
|
10
|
+
</Warning>
|
|
11
|
+
|
|
12
|
+
<Info>
|
|
13
|
+
**A variant axis IS a product option.** Attributes marked `is_variant_axis` aren't a parallel system bolted onto products — each one mirrors one-to-one onto a Medusa `ProductOption` and its values. Variants are then built with Medusa's standard machinery (`variants[].options`), exactly as in a plain Medusa project. Non-axis attributes never become options; they attach as plain value links. This is why only `multi_select` attributes can be axes — an axis needs an enumerable set of values to combine into variants.
|
|
14
|
+
</Info>
|
|
15
|
+
|
|
16
|
+
## What you'll build
|
|
17
|
+
|
|
18
|
+
- A global `Material` attribute (multi-select, filterable) used for storefront filtering.
|
|
19
|
+
- A global `Color` variant axis shared across the catalog.
|
|
20
|
+
- An inline, product-scoped `Fit` axis created on the fly from a product form.
|
|
21
|
+
|
|
22
|
+
## Global vs product-scoped
|
|
23
|
+
|
|
24
|
+
| Kind | Backed by | Appears in the global catalog? | Use for |
|
|
25
|
+
|------|-----------|-------------------------------|---------|
|
|
26
|
+
| Global attribute | Shared `ProductOption` (when axis) or value links | Yes | Data every product can use — Material, Color, Condition |
|
|
27
|
+
| Product-scoped attribute | Exclusive, product-owned option (when axis) | No | One-off fields or axes for a single product |
|
|
28
|
+
|
|
29
|
+
## Build the attribute catalog
|
|
30
|
+
|
|
31
|
+
<Steps>
|
|
32
|
+
<Step title="Create a filterable attribute">
|
|
33
|
+
In the Admin Panel, the operator owns the attribute catalog. Create **Material** as a global `multi_select` attribute with values like Cotton, Wool, Linen, and turn on `is_filterable`. Global attributes (`product_id = null`) can be attached to any product and linked to categories so the right attributes surface for the right product types.
|
|
34
|
+
|
|
35
|
+
Since it's not a variant axis, Material describes the product — it will never generate variants.
|
|
36
|
+
</Step>
|
|
37
|
+
<Step title="Create a variant axis">
|
|
38
|
+
Create **Color** the same way, but enable `is_variant_axis`. This is only allowed for `multi_select` attributes — the platform rejects the flag on any other type.
|
|
39
|
+
|
|
40
|
+
Because Color is a **global** axis, it's backed by one shared product option. Every product that uses it links to that option, restricted to the subset of values the product actually offers — so "Color" means the same thing across the whole catalog, while one product can offer only Red and Blue.
|
|
41
|
+
</Step>
|
|
42
|
+
<Step title="Attach attributes to a product">
|
|
43
|
+
Products manage attributes through one **batch** endpoint that adds, removes, and updates in a single request:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
curl -X POST "http://localhost:9000/vendor/products/prod_123/attributes/batch" \
|
|
47
|
+
-H "Authorization: Bearer <token>" \
|
|
48
|
+
-H "Content-Type: application/json" \
|
|
49
|
+
-d '{
|
|
50
|
+
"add": [
|
|
51
|
+
{ "id": "pattr_material", "value_ids": ["pattrval_cotton"] },
|
|
52
|
+
{ "id": "pattr_color", "value_ids": ["pattrval_red", "pattrval_blue"] }
|
|
53
|
+
]
|
|
54
|
+
}'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
At product create time, the same entry shape is passed as a unified `attributes[]` array. Because Color is an axis, selecting Red and Blue makes them available as variant options — variants are then defined with standard Medusa `variants[].options` mapping `"Color"` to `"Red"` or `"Blue"`.
|
|
58
|
+
</Step>
|
|
59
|
+
<Step title="Add an inline product-scoped axis">
|
|
60
|
+
Sometimes one product needs an axis that doesn't belong in the shared catalog. Define it **inline** by `title` instead of referencing an `id`:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
curl -X POST "http://localhost:9000/vendor/products/prod_123/attributes/batch" \
|
|
64
|
+
-H "Authorization: Bearer <token>" \
|
|
65
|
+
-H "Content-Type: application/json" \
|
|
66
|
+
-d '{
|
|
67
|
+
"add": [
|
|
68
|
+
{ "title": "Fit", "values": ["Slim", "Regular"], "is_variant_axis": true }
|
|
69
|
+
]
|
|
70
|
+
}'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
This creates a product-scoped attribute on the fly, backed by an **exclusive**, product-owned option. It won't appear in the global attribute list — it belongs to this product alone.
|
|
74
|
+
</Step>
|
|
75
|
+
</Steps>
|
|
76
|
+
|
|
77
|
+
<Note>
|
|
78
|
+
Attribute changes on products submitted by vendors flow through the same [change-request pipeline](/rc/learn/product-requests) as other product edits — `ATTRIBUTE_ADD` / `ATTRIBUTE_UPDATE` / `ATTRIBUTE_REMOVE` actions the operator reviews.
|
|
79
|
+
</Note>
|
|
80
|
+
|
|
81
|
+
## Verify
|
|
82
|
+
|
|
83
|
+
1. **Material** and **Color** appear in the Admin Panel's attribute catalog; **Fit** does not (it's product-scoped).
|
|
84
|
+
2. The product detail shows Material as descriptive data and Color/Fit as variant axes.
|
|
85
|
+
3. The product's variants combine the selected Color and Fit values, built from real product options.
|
|
86
|
+
4. The Store API exposes Material for filtering on product listings (`is_filterable`).
|
|
87
|
+
|
|
88
|
+
## FAQ
|
|
89
|
+
|
|
90
|
+
<AccordionGroup>
|
|
91
|
+
<Accordion title="Why can't a text or toggle attribute be a variant axis?">
|
|
92
|
+
An axis needs an enumerable, finite value set to combine into variants — Red/Blue × Slim/Regular. Free text has no enumerable values, and a toggle's two fixed values rarely describe purchasable variations. Only `multi_select` qualifies, and the platform enforces it.
|
|
93
|
+
</Accordion>
|
|
94
|
+
<Accordion title="What's the difference between a value link and an option?">
|
|
95
|
+
Non-axis attributes attach to a product as plain **value links** — descriptive data for display and filtering. Axis attributes are mirrored onto real Medusa **product options**, which participate in variant generation. Same authoring UI, structurally different underneath.
|
|
96
|
+
</Accordion>
|
|
97
|
+
<Accordion title="Can I promote a product-scoped attribute to a global one later?">
|
|
98
|
+
Not automatically — a product-scoped axis is backed by an exclusive option owned by that product. Create the global attribute in the catalog and re-attach products to it; treat inline attributes as intentionally local.
|
|
99
|
+
</Accordion>
|
|
100
|
+
</AccordionGroup>
|
|
101
|
+
|
|
102
|
+
## Next steps
|
|
103
|
+
|
|
104
|
+
<CardGroup cols={2}>
|
|
105
|
+
<Card title="Product Attributes" href="/rc/learn/attributes">
|
|
106
|
+
Types, fields, and the full attribute-to-option mapping.
|
|
107
|
+
</Card>
|
|
108
|
+
<Card title="Master products & offers" href="/rc/resources/tutorials/master-products-and-offers">
|
|
109
|
+
How sellers list against the variants your axes generate.
|
|
110
|
+
</Card>
|
|
111
|
+
</CardGroup>
|