docs: revise commerce modules overview pages (#10738)
* revise API Key Module overview * revise auth module * support ref sidebar items * remove examples * revise cart module * revise currency * revise customer module * revise fulfillment module * revise inventory module * revise order module * revise payment * revise pricing module * revise product module * revise promotion module * revise region module * revise sales channel module * revise stock location module * revise store module * revise tax module * revise user module * lint content + fix snippets
This commit is contained in:
1 parent
c8f9938865
commit
ebca8fed28
112 files changed
+9393
-7659
No files matched your search
@@ -1,306 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the API Key Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the API Key Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the API Key Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create an API Key
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const apiKeyModuleService = request.scope.resolve(
|
|
||||||
Modules.API_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
const apiKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: "Publishable API key",
|
|
||||||
type: "publishable",
|
|
||||||
created_by: "user_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
api_key: apiKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeApiKeyModule } from "@medusajs/medusa/api-key"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const apiKeyModuleService = await initializeApiKeyModule()
|
|
||||||
|
|
||||||
const apiKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: "Publishable API key",
|
|
||||||
type: "publishable",
|
|
||||||
created_by: "user_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
api_key: apiKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List API Keys
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const apiKeyModuleService = request.scope.resolve(
|
|
||||||
Modules.API_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
api_keys: await apiKeyModuleService.listApiKeys(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeApiKeyModule } from "@medusajs/medusa/api-key"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const apiKeyModuleService = await initializeApiKeyModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
api_keys: await apiKeyModuleService.listApiKeys(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Revoke an API Key
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts collapsibleLines="1-9" expandButtonLabel="Show Imports"
|
|
||||||
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: AuthenticatedMedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
) {
|
|
||||||
const apiKeyModuleService = request.scope.resolve(
|
|
||||||
Modules.API_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
const revokedKey = await apiKeyModuleService.revoke(request.params.id, {
|
|
||||||
revoked_by: request.auth_context.actor_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
api_key: revokedKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeApiKeyModule } from "@medusajs/medusa/api-key"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
id: string
|
|
||||||
user_id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const apiKeyModuleService = await initializeApiKeyModule()
|
|
||||||
|
|
||||||
const revokedKey = await apiKeyModuleService.revoke(params.id, {
|
|
||||||
revoked_by: params.user_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
api_key: revokedKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Verify or Authenticate Token
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const apiKeyModuleService = request.scope.resolve(
|
|
||||||
Modules.API_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
const authenticatedToken = await apiKeyModuleService.authenticate(
|
|
||||||
request.params.token
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
is_authenticated: !!authenticatedToken,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeApiKeyModule } from "@medusajs/medusa/api-key"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
token: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const apiKeyModuleService = await initializeApiKeyModule()
|
|
||||||
|
|
||||||
const authenticatedToken = await apiKeyModuleService.authenticate(
|
|
||||||
request.params.token
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
is_authenticated: !!authenticatedToken,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Roll API Key
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
|
||||||
import {
|
|
||||||
AuthenticatedMedusaRequest,
|
|
||||||
MedusaResponse,
|
|
||||||
} from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: AuthenticatedMedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
) {
|
|
||||||
const apiKeyModuleService = request.scope.resolve(
|
|
||||||
Modules.API_KEY
|
|
||||||
)
|
|
||||||
|
|
||||||
const revokedKey = await apiKeyModuleService.revoke(request.params.id, {
|
|
||||||
revoked_by: request.auth_context.actor_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
const newKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: revokedKey.title,
|
|
||||||
type: revokedKey.type,
|
|
||||||
created_by: revokedKey.created_by,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
api_key: newKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeApiKeyModule } from "@medusajs/medusa/api-key"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
id: string
|
|
||||||
user_id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const apiKeyModuleService = await initializeApiKeyModule()
|
|
||||||
|
|
||||||
const revokedKey = await apiKeyModuleService.revoke(params.id, {
|
|
||||||
revoked_by: params.user_id,
|
|
||||||
})
|
|
||||||
|
|
||||||
const newKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: revokedKey.title,
|
|
||||||
type: revokedKey.type,
|
|
||||||
created_by: revokedKey.created_by,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
api_key: newKey,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [API Key Module's main service reference](/references/api-key) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -37,8 +37,8 @@ To retrieve the sales channels of an API key with [Query](!docs!/learn/fundament
|
|||||||
const { data: apiKeys } = await query.graph({
|
const { data: apiKeys } = await query.graph({
|
||||||
entity: "api_key",
|
entity: "api_key",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channels.*"
|
"sales_channels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// apiKeys.sales_channels
|
// apiKeys.sales_channels
|
||||||
@@ -55,8 +55,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: apiKeys } = useQueryGraphStep({
|
const { data: apiKeys } = useQueryGraphStep({
|
||||||
entity: "api_key",
|
entity: "api_key",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channels.*"
|
"sales_channels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// apiKeys.sales_channels
|
// apiKeys.sales_channels
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
import { CodeTabs, CodeTab, ChildDocs } from "docs-ui"
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `API Key Module`,
|
title: `API Key Module`,
|
||||||
@@ -6,132 +6,153 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The API Key Module provides API-key-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the API Key Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use API Key Module's Service
|
Medusa has API-key related features available out-of-the-box through the API Key Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this API Key Module.
|
||||||
|
|
||||||
Use the API Key Module's main service by resolving from the Medusa container the resource `Modules.API_KEY`.
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## API Key Features
|
||||||
|
|
||||||
|
- [API Key Types and Management](./concepts/page.mdx): Manage API keys in your store. You can create both publishable and secret API keys for different use cases.
|
||||||
|
- [Token Verification](./concepts/page.mdx#token-verification): Verify tokens of secret API keys to authenticate users or actions.
|
||||||
|
- [Revoke Keys](./concepts/page.mdx#api-key-expiration): Revoke keys to disable their use permanently.
|
||||||
|
- Roll API Keys: Roll API keys by [revoking](/references/api-key/revoke) a key then [re-creating it](/references/api-key/createApiKeys).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use the API Key Module
|
||||||
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.API_KEY", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-api-key.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createApiKeyStep = createStep(
|
||||||
const apiKeyModuleService = container.resolve(
|
"create-api-key",
|
||||||
Modules.API_KEY
|
async ({}, { container }) => {
|
||||||
)
|
const apiKeyModuleService = container.resolve(Modules.API_KEY)
|
||||||
|
|
||||||
const apiKeys = await apiKeyModuleService.listApiKeys()
|
const apiKey = await apiKeyModuleService.createApiKeys({
|
||||||
})
|
title: "Publishable API key",
|
||||||
|
type: "publishable",
|
||||||
|
created_by: "user_123",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ apiKey }, apiKey.id)
|
||||||
|
},
|
||||||
|
async (apiKeyId, { container }) => {
|
||||||
|
const apiKeyModuleService = container.resolve(Modules.API_KEY)
|
||||||
|
|
||||||
|
await apiKeyModuleService.deleteApiKeys([apiKeyId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createApiKeyWorkflow = createWorkflow(
|
||||||
|
"create-api-key",
|
||||||
|
() => {
|
||||||
|
const { apiKey } = createApiKeyStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
apiKey,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createApiKeyWorkflow } from "../../workflows/create-api-key"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const apiKeyModuleService = request.scope.resolve(
|
const { result } = await createApiKeyWorkflow(req.scope)
|
||||||
Modules.API_KEY
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
api_keys: await apiKeyModuleService.listApiKeys(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createApiKeyWorkflow } from "../workflows/create-api-key"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const apiKeyModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.API_KEY
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createApiKeyWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const apiKeys = await apiKeyModuleService.listApiKeys()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createApiKeyWorkflow } from "../workflows/create-api-key"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createApiKeyWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="API Key" />
|
||||||
|
|
||||||
### API Key Types and Management
|
|
||||||
|
|
||||||
Manage API keys in your store. You can create both publishable and secret API keys for different use cases, such as:
|
|
||||||
|
|
||||||
- Publishable API Key associated with resources like sales channels.
|
|
||||||
- Authentication token for admin users to access Admin API Routes.
|
|
||||||
- Password reset tokens when a user or customer requests to reset their password.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const pubApiKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: "Publishable API key",
|
|
||||||
type: "publishable",
|
|
||||||
created_by: "user_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
const secretApiKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: "Authentication Key",
|
|
||||||
type: "secret",
|
|
||||||
created_by: "user_123",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Token Verification
|
|
||||||
|
|
||||||
Verify tokens of secret API keys to authenticate users or actions, such as verifying a password reset token.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const authenticatedToken = await apiKeyModuleService.authenticate("sk_123")
|
|
||||||
|
|
||||||
if (!authenticatedToken) {
|
|
||||||
console.error("Couldn't verify token")
|
|
||||||
} else {
|
|
||||||
console.log("Token verified successfully!")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Revoke Keys
|
|
||||||
|
|
||||||
Revoke keys to disable their use permenantly.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const revokedKey = await apiKeyModuleService.revoke("apk_1", {
|
|
||||||
revoked_by: "user_123",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Roll API Keys
|
|
||||||
|
|
||||||
Roll API keys by revoking a key then re-creating it.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const revokedKey = await apiKeyModuleService.revoke("apk_1", {
|
|
||||||
revoked_by: "user_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
const newKey = await apiKeyModuleService.createApiKeys({
|
|
||||||
title: revokedKey.title,
|
|
||||||
type: revokedKey.type,
|
|
||||||
created_by: revokedKey.created_by,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
@@ -1,444 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Auth Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Auth Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Auth Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Authenticate User
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
This example uses the [jsonwebtoken NPM package](https://www.npmjs.com/package/jsonwebtoken) to create the authentication token.
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts collapsibleLines="1-10" expandButtonLabel="Show Imports"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import {
|
|
||||||
IAuthModuleService,
|
|
||||||
AuthenticationInput,
|
|
||||||
} from "@medusajs/framework/types"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
import { MedusaError } from "@medusajs/framework/utils"
|
|
||||||
import jwt from "jsonwebtoken"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
const { success, authIdentity, location, error } =
|
|
||||||
await authModuleService.authenticate("emailpass", {
|
|
||||||
url: req.url,
|
|
||||||
headers: req.headers,
|
|
||||||
query: req.query,
|
|
||||||
body: req.body,
|
|
||||||
authScope: "admin",
|
|
||||||
protocol: req.protocol,
|
|
||||||
} as AuthenticationInput)
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
throw new MedusaError(MedusaError.Types.UNAUTHORIZED, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location) {
|
|
||||||
res.json({ location })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { jwtSecret } = req.scope.resolve("configModule").projectConfig.http
|
|
||||||
const token = jwt.sign(authIdentity, jwtSecret)
|
|
||||||
|
|
||||||
res.status(200).json({ token })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeAuthModule } from "@medusajs/medusa/auth"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const authModuleService = await initializeAuthModule()
|
|
||||||
const url = new URL(request.url)
|
|
||||||
|
|
||||||
const { success, authIdentity, location, error } =
|
|
||||||
await authModuleService.authenticate("emailpass", {
|
|
||||||
url: request.url,
|
|
||||||
headers: Object.fromEntries(request.headers),
|
|
||||||
query: Object.fromEntries(url.searchParams),
|
|
||||||
body: await request.json(),
|
|
||||||
authScope: "admin",
|
|
||||||
protocol: url.protocol,
|
|
||||||
} as AuthenticationInput)
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (location) {
|
|
||||||
return NextResponse.json({ location })
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = jwt.sign(authIdentity, "supersecret")
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
token,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Validate Callback
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
This example uses the [jsonwebtoken NPM package](https://www.npmjs.com/package/jsonwebtoken) to create the authentication token.
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts collapsibleLines="1-10" expandButtonLabel="Show Imports"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import {
|
|
||||||
IAuthModuleService,
|
|
||||||
AuthenticationInput,
|
|
||||||
} from "@medusajs/framework/types"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
import { MedusaError } from "@medusajs/framework/utils"
|
|
||||||
import jwt from "jsonwebtoken"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
const { success, authIdentity, error, successRedirectUrl } =
|
|
||||||
await authModuleService.validateCallback("google", {
|
|
||||||
url: req.url,
|
|
||||||
headers: req.headers,
|
|
||||||
query: req.query,
|
|
||||||
body: req.body,
|
|
||||||
authScope: "admin",
|
|
||||||
protocol: req.protocol,
|
|
||||||
} as AuthenticationInput)
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
throw new MedusaError(MedusaError.Types.UNAUTHORIZED, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const { jwtSecret } = req.scope.resolve("configModule").projectConfig.http
|
|
||||||
const token = jwt.sign(authIdentity, jwtSecret)
|
|
||||||
|
|
||||||
if (successRedirectUrl) {
|
|
||||||
const url = new URL(successRedirectUrl!)
|
|
||||||
url.searchParams.append("auth_token", token)
|
|
||||||
|
|
||||||
return res.redirect(url.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(200).json({ token })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts collapsibleLines="1-7" expandButtonLabel="Show Imports"
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeAuthModule } from "@medusajs/medusa/auth"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const authModuleService = await initializeAuthModule()
|
|
||||||
const url = new URL(request.url)
|
|
||||||
|
|
||||||
const { success, authIdentity, location, error } =
|
|
||||||
await authModuleService.authenticate("google", {
|
|
||||||
url: request.url,
|
|
||||||
headers: Object.fromEntries(request.headers),
|
|
||||||
query: Object.fromEntries(url.searchParams),
|
|
||||||
body: await request.json(),
|
|
||||||
authScope: "admin",
|
|
||||||
protocol: url.protocol,
|
|
||||||
} as AuthenticationInput)
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
const token = jwt.sign(authIdentity, "supersecret")
|
|
||||||
|
|
||||||
if (successRedirectUrl) {
|
|
||||||
const url = new URL(successRedirectUrl!)
|
|
||||||
url.searchParams.append("auth_token", token)
|
|
||||||
|
|
||||||
return NextResponse.redirect(url.toString())
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
token,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status: 200,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create Auth Identity
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
const authIdentity = await authModuleService.createAuthIdentities({
|
|
||||||
provider: "emailpass",
|
|
||||||
entity_id: "user@example.com",
|
|
||||||
scope: "admin",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ auth_identity: authIdentity })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeAuthModule } from "@medusajs/medusa/auth"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const authModuleService = await initializeAuthModule()
|
|
||||||
|
|
||||||
const authIdentity = await authModuleService.createAuthIdentities({
|
|
||||||
provider: "emailpass",
|
|
||||||
entity_id: "user@example.com",
|
|
||||||
scope: "admin",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
auth_identity: authIdentity,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Auth Identities
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
auth_identitys: await authModuleService.listAuthIdentities(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeAuthModule } from "@medusajs/medusa/auth"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const authModuleService = await initializeAuthModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
auth_identities: await authModuleService.listAuthIdentities(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Update an Auth Identity
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
const authIdentity = await authModuleService.updateAuthIdentites({
|
|
||||||
id: "authusr_123",
|
|
||||||
provider_metadata: {
|
|
||||||
test: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
auth_identity: authIdentity,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeAuthModule } from "@medusajs/medusa/auth"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const authModuleService = await initializeAuthModule()
|
|
||||||
|
|
||||||
const authIdentity = await authModuleService.updateAuthIdentites({
|
|
||||||
id: "authusr_123",
|
|
||||||
provider_metadata: {
|
|
||||||
test: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
auth_identity: authIdentity,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete an Auth Identity
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
await authModuleService.deleteAuthIdentities(["authusr_123"])
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeAuthModule } from "@medusajs/medusa/auth"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(request: Request, { params }: ContextType) {
|
|
||||||
const authModuleService = await initializeAuthModule()
|
|
||||||
|
|
||||||
await authModuleService.deleteAuthIdentities(["authusr_123"])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Auth Module's main service reference](/references/auth) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
import { CodeTabs, CodeTab, ChildDocs } from "docs-ui"
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `Auth Module`,
|
title: `Auth Module`,
|
||||||
@@ -6,132 +6,141 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Auth Module provides authentication-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Auth Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Auth Module's Service
|
Medusa has auth related features available out-of-the-box through the Auth Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Auth Module.
|
||||||
|
|
||||||
Use the Auth Module's main service by resolving from the Medusa container the resource `Modules.AUTH`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
## Auth Features
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
- [Basic User Authentication](./authentication-route/page.mdx#1-basic-authentication-flow): Authenticate users using their email and password credentials.
|
||||||
const authModuleService = container.resolve(
|
- [Third-Party and Social Authentication](./authentication-route/page.mdx#2-third-party-service-authenticate-flow): Authenticate users using third-party services and social platforms, such as [Google](./auth-providers/google/page.mdx) and [GitHub](./auth-providers/github/page.mdx).
|
||||||
Modules.AUTH
|
- [Authenticate Custom Actor Types](./create-actor-type/page.mdx): Create custom user or actor types, such as managers, authenticate them in your application, and guard routes based on the custom user types.
|
||||||
)
|
- [Custom Authentication Providers](/references/auth/provider): Integrate third-party services with custom authentication providors.
|
||||||
const authIdentitys = await authModuleService.listAuthIdentities()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const authModuleService = req.scope.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
authIdentitys: await authModuleService.listAuthIdentities(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const authModuleService = container.resolve(
|
|
||||||
Modules.AUTH
|
|
||||||
)
|
|
||||||
|
|
||||||
const authIdentitys = await authModuleService.listAuthIdentities()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## How to Use the Auth Module
|
||||||
|
|
||||||
### Basic User Authentication
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
Authenticate users using their email and password credentials.
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
```ts
|
For example:
|
||||||
const { success, authIdentity, error } = await authModuleService.authenticate(
|
|
||||||
|
export const highlights = [
|
||||||
|
["18", "Modules.AUTH", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/authenticate-user.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules, MedusaError } from "@medusajs/framework/utils"
|
||||||
|
import { MedusaRequest } from "@medusajs/framework/http"
|
||||||
|
import { AuthenticationInput } from "@medusajs/framework/types"
|
||||||
|
|
||||||
|
type Input = {
|
||||||
|
req: MedusaRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
const authenticateUserStep = createStep(
|
||||||
|
"authenticate-user",
|
||||||
|
async ({ req }: Input, { container }) => {
|
||||||
|
const authModuleService = container.resolve(Modules.AUTH)
|
||||||
|
|
||||||
|
const { success, authIdentity, error } = await authModuleService
|
||||||
|
.authenticate(
|
||||||
"emailpass",
|
"emailpass",
|
||||||
{
|
{
|
||||||
url: req.url,
|
url: req.url,
|
||||||
headers: req.headers,
|
headers: req.headers,
|
||||||
query: req.query,
|
query: req.query,
|
||||||
body: req.body,
|
body: req.body,
|
||||||
authScope: "admin",
|
authScope: "admin", // or custom actor type
|
||||||
protocol: req.protocol,
|
protocol: req.protocol,
|
||||||
} as AuthenticationInput
|
} as AuthenticationInput
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
// incorrect authentication details
|
// incorrect authentication details
|
||||||
throw new Error(error)
|
throw new MedusaError(
|
||||||
}
|
MedusaError.Types.UNAUTHORIZED,
|
||||||
```
|
error || "Incorrect authentication details"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
### Third-Party and Social Authentication
|
return new StepResponse({ authIdentity }, authIdentity?.id)
|
||||||
|
},
|
||||||
|
async (authIdentityId, { container }) => {
|
||||||
|
if (!authIdentityId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
The Auth Module supports a variety of authentication methods, such as authenticating with third-party services and social platforms.
|
const authModuleService = container.resolve(Modules.AUTH)
|
||||||
|
|
||||||
```ts
|
await authModuleService.deleteAuthIdentities([authIdentityId])
|
||||||
// in authentication API route
|
}
|
||||||
const { success, authIdentity, location } =
|
)
|
||||||
await authModuleService.authenticate("google", {
|
|
||||||
url: req.url,
|
|
||||||
headers: req.headers,
|
|
||||||
query: req.query,
|
|
||||||
body: req.body,
|
|
||||||
authScope: "admin",
|
|
||||||
protocol: req.protocol,
|
|
||||||
} as AuthenticationInput)
|
|
||||||
|
|
||||||
if (location) {
|
export const authenticateUserWorkflow = createWorkflow(
|
||||||
return res.json({ location })
|
"authenticate-user",
|
||||||
}
|
(input: Input) => {
|
||||||
|
const { authIdentity } = authenticateUserStep(input)
|
||||||
|
|
||||||
// in callback API route
|
return new WorkflowResponse({
|
||||||
const { success, authIdentity } = await authModuleService.validateCallback(
|
authIdentity,
|
||||||
"google",
|
})
|
||||||
{
|
}
|
||||||
url: req.url,
|
|
||||||
headers: req.headers,
|
|
||||||
query: req.query,
|
|
||||||
body: req.body,
|
|
||||||
authScope: "admin",
|
|
||||||
protocol: req.protocol,
|
|
||||||
} as AuthenticationInput
|
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
```ts title="API Route" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import type {
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { authenticateUserWorkflow } from "../../workflows/authenticate-user"
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) {
|
||||||
|
const { result } = await authenticateUserWorkflow(req.scope)
|
||||||
|
.run({
|
||||||
|
req,
|
||||||
|
})
|
||||||
|
|
||||||
|
res.send(result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Configure Auth Module
|
## Configure Auth Module
|
||||||
|
|
||||||
Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
The Auth Module accepts options for further configurations. Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Providers
|
||||||
|
|
||||||
|
Medusa provides the following authentication providers out-of-the-box. You can use them to authenticate admin users, customers, or custom actor types.
|
||||||
|
|
||||||
|
<ChildDocs showItems={["Providers"]} hideTitle />
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Auth" />
|
||||||
@@ -1,463 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Cart Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Cart Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Cart Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Cart
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const cart = await cartModuleService.createCarts({
|
|
||||||
currency_code: "usd",
|
|
||||||
shipping_address: {
|
|
||||||
address_1: "1512 Barataria Blvd",
|
|
||||||
country_code: "us",
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Shirt",
|
|
||||||
unit_price: 1000,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ cart })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
const cart = await cartModuleService.createCarts({
|
|
||||||
currency_code: "usd",
|
|
||||||
shipping_address: {
|
|
||||||
address_1: "1512 Barataria Blvd",
|
|
||||||
country_code: "us",
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
title: "Shirt",
|
|
||||||
unit_price: 1000,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
cart,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve Cart by ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const cart = await cartModuleService.retrieveCart("cart_123")
|
|
||||||
|
|
||||||
res.json({ cart })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
const cart = await cartModuleService.retrieveCart("cart_123")
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
cart,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add Item to Cart
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const lineItem = await cartModuleService.addLineItems({
|
|
||||||
cart_id: "cart_123",
|
|
||||||
title: "Shirt",
|
|
||||||
quantity: 2,
|
|
||||||
unit_price: 5000,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
line_item: lineItem,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
const lineItem = await cartModuleService.addLineItems({
|
|
||||||
cart_id: "cart_123",
|
|
||||||
title: "Shirt",
|
|
||||||
quantity: 2,
|
|
||||||
unit_price: 5000,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
line_item: lineItem,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add Shipping Method to Cart
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const shippingMethod = await cartModuleService.addShippingMethods({
|
|
||||||
cart_id: "cart_123",
|
|
||||||
name: "Custom shipping",
|
|
||||||
amount: 1000,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
shipping_method: shippingMethod,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
const shippingMethod = await cartModuleService.addShippingMethods({
|
|
||||||
cart_id: "cart_123",
|
|
||||||
name: "Custom shipping",
|
|
||||||
amount: 1000,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
shipping_method: shippingMethod,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add Item Adjustment Line
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const itemAdjustment = await cartModuleService.addLineItemAdjustments({
|
|
||||||
item_id: "cali_123",
|
|
||||||
amount: 500,
|
|
||||||
code: "50%OFF",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
adjustment: itemAdjustment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
const itemAdjustment = await cartModuleService.addLineItemAdjustments({
|
|
||||||
item_id: "cali_123",
|
|
||||||
amount: 500,
|
|
||||||
code: "50%OFF",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
adjustment: itemAdjustment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add Shipping Method Adjustment Line
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const shippingMethodAdjustment =
|
|
||||||
await cartModuleService.addShippingMethodAdjustments({
|
|
||||||
shipping_method_id: "casm_123",
|
|
||||||
amount: 500,
|
|
||||||
code: "FREESHIPPING",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
adjustment: shippingMethodAdjustment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
const shippingMethodAdjustment =
|
|
||||||
await cartModuleService.addShippingMethodAdjustments({
|
|
||||||
shipping_method_id: "casm_123",
|
|
||||||
amount: 500,
|
|
||||||
code: "FREESHIPPING",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
adjustment: shippingMethodAdjustment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Remove Line Item from Cart
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
await cartModuleService.deleteLineItems(["cali_123"])
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCartModule } from "@medusajs/medusa/cart"
|
|
||||||
|
|
||||||
export async function DELETE(request: Request) {
|
|
||||||
const cartModuleService = await initializeCartModule()
|
|
||||||
|
|
||||||
await cartModuleService.deleteLineItems(["cali_123"])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Remove Shipping Method from Cart
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
await cartModuleService.deleteShippingMethods(["casm_123"])
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
await cartModuleService.deleteShippingMethods(["casm_123"])
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Cart Module's main service reference](/references/cart) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -44,8 +44,8 @@ To retrieve the customer of a cart with [Query](!docs!/learn/fundamentals/module
|
|||||||
const { data: carts } = await query.graph({
|
const { data: carts } = await query.graph({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"customer.*"
|
"customer.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.order
|
// carts.order
|
||||||
@@ -62,8 +62,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: carts } = useQueryGraphStep({
|
const { data: carts } = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"customer.*"
|
"customer.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.order
|
// carts.order
|
||||||
@@ -93,8 +93,8 @@ To retrieve the order of a cart with [Query](!docs!/learn/fundamentals/module-li
|
|||||||
const { data: carts } = await query.graph({
|
const { data: carts } = await query.graph({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"order.*"
|
"order.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.order
|
// carts.order
|
||||||
@@ -111,8 +111,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: carts } = useQueryGraphStep({
|
const { data: carts } = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"order.*"
|
"order.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.order
|
// carts.order
|
||||||
@@ -186,8 +186,8 @@ To retrieve the payment collection of a cart with [Query](!docs!/learn/fundament
|
|||||||
const { data: carts } = await query.graph({
|
const { data: carts } = await query.graph({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"payment_collection.*"
|
"payment_collection.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.payment_collection
|
// carts.payment_collection
|
||||||
@@ -204,8 +204,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: carts } = useQueryGraphStep({
|
const { data: carts } = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"payment_collection.*"
|
"payment_collection.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.payment_collection
|
// carts.payment_collection
|
||||||
@@ -283,8 +283,8 @@ To retrieve the product, pass `product.*` in `fields`.
|
|||||||
const { data: lineItems } = await query.graph({
|
const { data: lineItems } = await query.graph({
|
||||||
entity: "line_item",
|
entity: "line_item",
|
||||||
fields: [
|
fields: [
|
||||||
"variant.*"
|
"variant.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// lineItems.variant
|
// lineItems.variant
|
||||||
@@ -301,8 +301,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: lineItems } = useQueryGraphStep({
|
const { data: lineItems } = useQueryGraphStep({
|
||||||
entity: "line_item",
|
entity: "line_item",
|
||||||
fields: [
|
fields: [
|
||||||
"variant.*"
|
"variant.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// lineItems.variant
|
// lineItems.variant
|
||||||
@@ -340,8 +340,8 @@ To retrieve the promotion of a line item adjustment, pass `promotion.*` in `fiel
|
|||||||
const { data: carts } = await query.graph({
|
const { data: carts } = await query.graph({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"promotions.*"
|
"promotions.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.promotions
|
// carts.promotions
|
||||||
@@ -358,8 +358,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: carts } = useQueryGraphStep({
|
const { data: carts } = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"promotions.*"
|
"promotions.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.promotions
|
// carts.promotions
|
||||||
@@ -429,8 +429,8 @@ To retrieve the region of a cart with [Query](!docs!/learn/fundamentals/module-l
|
|||||||
const { data: carts } = await query.graph({
|
const { data: carts } = await query.graph({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"region.*"
|
"region.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.region
|
// carts.region
|
||||||
@@ -447,8 +447,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: carts } = useQueryGraphStep({
|
const { data: carts } = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"region.*"
|
"region.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.region
|
// carts.region
|
||||||
@@ -474,8 +474,8 @@ To retrieve the sales channel of a cart with [Query](!docs!/learn/fundamentals/m
|
|||||||
const { data: carts } = await query.graph({
|
const { data: carts } = await query.graph({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channel.*"
|
"sales_channel.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.sales_channel
|
// carts.sales_channel
|
||||||
@@ -492,8 +492,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: carts } = useQueryGraphStep({
|
const { data: carts } = useQueryGraphStep({
|
||||||
entity: "cart",
|
entity: "cart",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channel.*"
|
"sales_channel.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// carts.sales_channel
|
// carts.sales_channel
|
||||||
|
|||||||
@@ -6,80 +6,52 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Cart Module provides cart-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Cart Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Cart Module's Service
|
Medusa has cart related features available out-of-the-box through the Cart Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Cart Module.
|
||||||
|
|
||||||
You can use the Cart Module's main service by resolving from the Medusa container the resource `Modules.CART`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
## Cart Features
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
- [Cart Management](./concepts/page.mdx): Store and manage carts, including their addresses, line items, shipping methods, and more.
|
||||||
const cartModuleService = container.resolve(
|
- [Apply Promotion Adjustments](./promotions/page.mdx): Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals.
|
||||||
Modules.CART
|
- [Apply Tax Lines](./tax-lines/page.mdx): Apply tax lines to line items and shipping methods.
|
||||||
)
|
- [Cart Scoping](./links-to-other-modules/page.mdx): When used in the Medusa application, Medusa creates links to other commerce modules, scoping a cart to a sales channel, region, and a customer.
|
||||||
|
|
||||||
const carts = await cartModuleService.listCarts()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const cartModuleService = req.scope.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
carts: await cartModuleService.listCarts(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const cartModuleService = container.resolve(
|
|
||||||
Modules.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const carts = await cartModuleService.listCarts()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## How to Use the Cart Module
|
||||||
|
|
||||||
### Cart Management
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
Store and manage carts, including their addresses, line items, shipping methods, and more.
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
```ts
|
For example:
|
||||||
const cart = await cartModuleService.createCarts({
|
|
||||||
|
export const highlights = [
|
||||||
|
["12", "Modules.CART", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/create-cart.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const createCartStep = createStep(
|
||||||
|
"create-cart",
|
||||||
|
async ({}, { container }) => {
|
||||||
|
const cartModuleService = container.resolve(Modules.CART)
|
||||||
|
|
||||||
|
const cart = await cartModuleService.createCarts({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
shipping_address: {
|
shipping_address: {
|
||||||
address_1: "1512 Barataria Blvd",
|
address_1: "1512 Barataria Blvd",
|
||||||
@@ -92,34 +64,107 @@ const cart = await cartModuleService.createCarts({
|
|||||||
quantity: 1,
|
quantity: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Apply Promotions
|
|
||||||
|
|
||||||
Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const lineAdjustments = await cartModuleService.addLineItemAdjustments({
|
|
||||||
item_id: "cali_123",
|
|
||||||
code: "50OFF",
|
|
||||||
amount: 500,
|
|
||||||
})
|
|
||||||
|
|
||||||
const shippingAdjustments =
|
|
||||||
await cartModuleService.addShippingMethodAdjustments({
|
|
||||||
shipping_method_id: "casm_123",
|
|
||||||
code: "FREESHIPPING",
|
|
||||||
amount: 1000,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ cart }, cart.id)
|
||||||
|
},
|
||||||
|
async (cartId, { container }) => {
|
||||||
|
if (!cartId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const cartModuleService = container.resolve(Modules.CART)
|
||||||
|
|
||||||
|
await cartModuleService.deleteCarts([cartId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createCartWorkflow = createWorkflow(
|
||||||
|
"create-cart",
|
||||||
|
() => {
|
||||||
|
const { cart } = createCartStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
cart,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cart Context and Scoping
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
A cart is scoped to a sales channel, region, and a customer.
|
<CodeTabs group="resource-types">
|
||||||
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
The Medusa application links the Cart Module to each of their respective modules, providing features like:
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import type {
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createCartWorkflow } from "../../workflows/create-cart"
|
||||||
|
|
||||||
- Checking product availability in a sales channel.
|
export async function GET(
|
||||||
- Retrieving pricing per region.
|
req: MedusaRequest,
|
||||||
- Applying promotions based on the customer's group.
|
res: MedusaResponse
|
||||||
|
) {
|
||||||
|
const { result } = await createCartWorkflow(req.scope)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
res.send(result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import {
|
||||||
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createCartWorkflow } from "../workflows/create-cart"
|
||||||
|
|
||||||
|
export default async function handleUserCreated({
|
||||||
|
event: { data },
|
||||||
|
container,
|
||||||
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createCartWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createCartWorkflow } from "../workflows/create-cart"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createCartWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Cart" />
|
||||||
@@ -1,111 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Currency Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Currency Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Currency Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## List Currencies
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const currencyModuleService = req.scope.resolve(
|
|
||||||
Modules.CURRENCY
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
currencies: await currencyModuleService.listCurrencies(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCurrencyModule } from "@medusajs/medusa/currency"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const currencyModuleService = await initializeCurrencyModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
currencies: await currencyModuleService.listCurrencies(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Currency by its Code
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const currencyModuleService = req.scope.resolve(
|
|
||||||
Modules.CURRENCY
|
|
||||||
)
|
|
||||||
|
|
||||||
const currency = await currencyModuleService.retrieveCurrency("usd")
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
currency,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCurrencyModule } from "@medusajs/medusa/currency"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const currencyModuleService = await initializeCurrencyModule()
|
|
||||||
|
|
||||||
const currency = await currencyModuleService.retrieveCurrency("usd")
|
|
||||||
|
|
||||||
return NextResponse.json({ currency })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Currency Module's main service reference](/references/currency) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -39,8 +39,8 @@ To retrieve the details of a store's currencies with [Query](!docs!/learn/fundam
|
|||||||
const { data: stores } = await query.graph({
|
const { data: stores } = await query.graph({
|
||||||
entity: "store",
|
entity: "store",
|
||||||
fields: [
|
fields: [
|
||||||
"supported_currencies.currency.*"
|
"supported_currencies.currency.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stores.supported_currencies
|
// stores.supported_currencies
|
||||||
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: stores } = useQueryGraphStep({
|
const { data: stores } = useQueryGraphStep({
|
||||||
entity: "store",
|
entity: "store",
|
||||||
fields: [
|
fields: [
|
||||||
"supported_currencies.currency.*"
|
"supported_currencies.currency.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stores.supported_currencies
|
// stores.supported_currencies
|
||||||
|
|||||||
@@ -6,91 +6,161 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Currency Module provides currency-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Currency Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Currency Module's Service
|
Medusa has currency related features available out-of-the-box through the Currency Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Currency Module.
|
||||||
|
|
||||||
You can use the Currency Module's main service by resolving from the Medusa container the resource `Modules.CURRENCY`.
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Currency Features
|
||||||
|
|
||||||
|
- [Currency Management and Retrieval](/references/currency/listAndCountCurrencies): This module adds all common currencies to your application and allows you to retrieve them.
|
||||||
|
- [Support Currencies in Modules](./links-to-other-modules/page.mdx): Other commerce modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use the Currency Module
|
||||||
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["13", "Modules.CURRENCY", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/retrieve-price-with-currency.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
transform,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const retrieveCurrencyStep = createStep(
|
||||||
const currencyModuleService = container.resolve(
|
"retrieve-currency",
|
||||||
Modules.CURRENCY
|
async ({}, { container }) => {
|
||||||
)
|
const currencyModuleService = container.resolve(Modules.CURRENCY)
|
||||||
|
|
||||||
const currencies = await currencyModuleService.listCurrencies()
|
const currency = await currencyModuleService
|
||||||
})
|
.retrieveCurrency("usd")
|
||||||
|
|
||||||
|
return new StepResponse({ currency })
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Input = {
|
||||||
|
price: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const retrievePriceWithCurrency = createWorkflow(
|
||||||
|
"create-currency",
|
||||||
|
(input: Input) => {
|
||||||
|
const { currency } = retrieveCurrencyStep()
|
||||||
|
|
||||||
|
const formattedPrice = transform({
|
||||||
|
input,
|
||||||
|
currency,
|
||||||
|
}, (data) => {
|
||||||
|
return `${data.currency.symbol}${data.input.price}`
|
||||||
|
})
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
formattedPrice,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { retrievePriceWithCurrency } from "../../workflows/retrieve-price-with-currency"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const currencyModuleService = req.scope.resolve(
|
const { result } = await retrievePriceWithCurrency(req.scope)
|
||||||
Modules.CURRENCY
|
.run({
|
||||||
)
|
price: 10,
|
||||||
|
|
||||||
res.json({
|
|
||||||
currencies: await currencyModuleService.listCurrencies(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
res.send(result)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const currencyModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.CURRENCY
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await retrievePriceWithCurrency(container)
|
||||||
|
.run({
|
||||||
|
price: 10,
|
||||||
|
})
|
||||||
|
|
||||||
const currencies = await currencyModuleService.listCurrencies()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"], ["9"], ["10"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await retrievePriceWithCurrency(container)
|
||||||
|
.run({
|
||||||
|
price: 10,
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Currency" />
|
||||||
|
|
||||||
### Currency Retrieval
|
|
||||||
|
|
||||||
List and retrieve currencies stored in your application.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const currency = await currencyModuleService.retrieveCurrency("usd")
|
|
||||||
```
|
|
||||||
|
|
||||||
### Support Currencies in Modules
|
|
||||||
|
|
||||||
Other commerce modules use currency codes in their data models or operations. Use the Currency Module to retrieve a currency code and its details.
|
|
||||||
|
|
||||||
An example with the Region Module:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const region = await regionModuleService.retrieveRegion("reg_123")
|
|
||||||
const currency = await currencyModuleService.retrieveCurrency(
|
|
||||||
region.currency_code
|
|
||||||
)
|
|
||||||
```
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Customer Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Customer Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Customer Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Customer
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const customerModuleService = request.scope.resolve(
|
|
||||||
Modules.CUSTOMER
|
|
||||||
)
|
|
||||||
|
|
||||||
const customer = await customerModuleService.createCustomers({
|
|
||||||
first_name: "Peter",
|
|
||||||
last_name: "Hayes",
|
|
||||||
email: "peter.hayes@example.com",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
customer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCustomerModule } from "@medusajs/medusa/customer"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const customerModuleService = await initializeCustomerModule()
|
|
||||||
|
|
||||||
const customer = await customerModuleService.createCustomers({
|
|
||||||
first_name: "Peter",
|
|
||||||
last_name: "Hayes",
|
|
||||||
email: "peter.hayes@example.com",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
customer,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create a Customer Group
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const customerModuleService = request.scope.resolve(
|
|
||||||
Modules.CUSTOMER
|
|
||||||
)
|
|
||||||
|
|
||||||
const customerGroup = await customerModuleService.createCustomerGroups({
|
|
||||||
name: "VIP",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
customer_group: customerGroup,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCustomerModule } from "@medusajs/medusa/customer"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const customerModuleService = await initializeCustomerModule()
|
|
||||||
|
|
||||||
const customerGroup = await customerModuleService.createCustomerGroups({
|
|
||||||
name: "VIP",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
customer_group: customerGroup,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add a Customer to a Group
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const customerModuleService = request.scope.resolve(
|
|
||||||
Modules.CUSTOMER
|
|
||||||
)
|
|
||||||
|
|
||||||
await customerModuleService.addCustomerToGroup({
|
|
||||||
customer_id: "cus_123",
|
|
||||||
customer_group_id: "cusgroup_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeCustomerModule } from "@medusajs/medusa/customer"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const customerModuleService = await initializeCustomerModule()
|
|
||||||
|
|
||||||
await customerModuleService.addCustomerToGroup({
|
|
||||||
customer_id: "cus_123",
|
|
||||||
customer_group_id: "cusgroup_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({}, { status: 200 })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Remove a Customer from a Group
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const customerModuleService = request.scope.resolve(
|
|
||||||
Modules.CUSTOMER
|
|
||||||
)
|
|
||||||
|
|
||||||
await customerModuleService.removeCustomerFromGroup({
|
|
||||||
customer_id: "cus_123",
|
|
||||||
customer_group_id: "cusgroup_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
|
|
||||||
import { initialize as initializeCustomerModule } from "@medusajs/medusa/customer"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const customerModuleService = await initializeCustomerModule()
|
|
||||||
|
|
||||||
await customerModuleService.removeCustomerFromGroup({
|
|
||||||
customer_id: "cus_123",
|
|
||||||
customer_group_id: "cusgroup_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({}, { status: 200 })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Customer Module's main service reference](/references/customer) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -276,9 +276,9 @@ You'll next execute the workflow in the hook handler.
|
|||||||
|
|
||||||
You can now consume the `customersCreated` hook, which is executed in the `createCustomersWorkflow` after the customer is created.
|
You can now consume the `customersCreated` hook, which is executed in the `createCustomersWorkflow` after the customer is created.
|
||||||
|
|
||||||
To consume the hook, create the file `src/workflow/hooks/customer-created.ts` with the following content:
|
To consume the hook, create the file `src/workflow/hooks/user-created.ts` with the following content:
|
||||||
|
|
||||||
```ts title="src/workflow/hooks/customer-created.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
```ts title="src/workflow/hooks/user-created.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { createCustomersWorkflow } from "@medusajs/medusa/core-flows"
|
import { createCustomersWorkflow } from "@medusajs/medusa/core-flows"
|
||||||
import {
|
import {
|
||||||
createCustomFromCustomerWorkflow,
|
createCustomFromCustomerWorkflow,
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ To retrieve a customer's carts with [Query](!docs!/learn/fundamentals/module-lin
|
|||||||
const { data: customers } = await query.graph({
|
const { data: customers } = await query.graph({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// customers.carts
|
// customers.carts
|
||||||
@@ -56,8 +56,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: customers } = useQueryGraphStep({
|
const { data: customers } = useQueryGraphStep({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// customers.carts
|
// customers.carts
|
||||||
@@ -83,8 +83,8 @@ To retrieve a customer's orders with [Query](!docs!/learn/fundamentals/module-li
|
|||||||
const { data: customers } = await query.graph({
|
const { data: customers } = await query.graph({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// customers.orders
|
// customers.orders
|
||||||
@@ -101,8 +101,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: customers } = useQueryGraphStep({
|
const { data: customers } = useQueryGraphStep({
|
||||||
entity: "customer",
|
entity: "customer",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// customers.orders
|
// customers.orders
|
||||||
|
|||||||
@@ -6,96 +6,154 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Customer Module provides customer-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Customer Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Customer Module's Service
|
Medusa has customer related features available out-of-the-box through the Customer Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Customer Module.
|
||||||
|
|
||||||
You can use the Customer Module's main service by resolving from the Medusa container the resource `Modules.CUSTOMER`.
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Customer Features
|
||||||
|
|
||||||
|
- [Customer Management](./customer-accounts/page.mdx): Store and manage guest and registered customers in your store.
|
||||||
|
- [Customer Organization](/references/customer/models): Organize customers into groups. This has a lot of benefits and supports many use cases, such as provide discounts for specific customer groups using the [Promotion Module](../promotion/page.mdx).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use the Customer Module
|
||||||
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.CUSTOMER", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-customer.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createCustomerStep = createStep(
|
||||||
const customerModuleService = container.resolve(
|
"create-customer",
|
||||||
Modules.CUSTOMER
|
async ({}, { container }) => {
|
||||||
)
|
const customerModuleService = container.resolve(Modules.CUSTOMER)
|
||||||
|
|
||||||
const customers = await customerModuleService.listCustomers()
|
const customer = await customerModuleService.createCustomers({
|
||||||
})
|
first_name: "Peter",
|
||||||
|
last_name: "Hayes",
|
||||||
|
email: "peter.hayes@example.com",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ customer }, customer.id)
|
||||||
|
},
|
||||||
|
async (customerId, { container }) => {
|
||||||
|
if (!customerId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const customerModuleService = container.resolve(Modules.CUSTOMER)
|
||||||
|
|
||||||
|
await customerModuleService.deleteCustomers([customerId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createCustomerWorkflow = createWorkflow(
|
||||||
|
"create-customer",
|
||||||
|
() => {
|
||||||
|
const { customer } = createCustomerStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
customer,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createCustomerWorkflow } from "../../workflows/create-customer"
|
||||||
|
|
||||||
export async function GET(request: MedusaRequest, res: MedusaResponse) {
|
export async function GET(
|
||||||
const customerModuleService = request.scope.resolve(
|
req: MedusaRequest,
|
||||||
Modules.CUSTOMER
|
res: MedusaResponse
|
||||||
)
|
) {
|
||||||
|
const { result } = await createCustomerWorkflow(req.scope)
|
||||||
|
.run()
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
customers: await customerModuleService.listCustomers(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createCustomerWorkflow } from "../workflows/create-customer"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const customerModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.CUSTOMER
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createCustomerWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const customers = await customerModuleService.listCustomers()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createCustomerWorkflow } from "../workflows/create-customer"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createCustomerWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Customer" />
|
||||||
|
|
||||||
### Customer Management
|
|
||||||
|
|
||||||
With the Customer Module, store and manage customers.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const customer = await customerModuleService.createCustomers({
|
|
||||||
first_name: "Peter",
|
|
||||||
last_name: "Hayes",
|
|
||||||
email: "peter.hayes@example.com",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Customer Organization
|
|
||||||
|
|
||||||
Organize customers into groups.
|
|
||||||
|
|
||||||
This has a lot of benefits and supports many use cases, such as provide discounts for specific customer groups using the Promotion Module.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const customerGroup = await customerModuleService.createCustomerGroups({
|
|
||||||
name: "VIP",
|
|
||||||
})
|
|
||||||
|
|
||||||
await customerModuleService.addCustomerToGroup({
|
|
||||||
customer_id: "cus_123",
|
|
||||||
customer_group_id: customerGroup.id,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
+12
-12
@@ -49,8 +49,8 @@ To retrieve the return, pass `return.*` in `fields`.
|
|||||||
const { data: fulfillments } = await query.graph({
|
const { data: fulfillments } = await query.graph({
|
||||||
entity: "fulfillment",
|
entity: "fulfillment",
|
||||||
fields: [
|
fields: [
|
||||||
"order.*"
|
"order.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// fulfillments.order
|
// fulfillments.order
|
||||||
@@ -67,8 +67,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: fulfillments } = useQueryGraphStep({
|
const { data: fulfillments } = useQueryGraphStep({
|
||||||
entity: "fulfillment",
|
entity: "fulfillment",
|
||||||
fields: [
|
fields: [
|
||||||
"order.*"
|
"order.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// fulfillments.order
|
// fulfillments.order
|
||||||
@@ -142,8 +142,8 @@ To retrieve the price set of a shipping option with [Query](!docs!/learn/fundame
|
|||||||
const { data: shippingOptions } = await query.graph({
|
const { data: shippingOptions } = await query.graph({
|
||||||
entity: "shipping_option",
|
entity: "shipping_option",
|
||||||
fields: [
|
fields: [
|
||||||
"price_set.*"
|
"price_set.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// shippingOptions.price_set
|
// shippingOptions.price_set
|
||||||
@@ -160,8 +160,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: shippingOptions } = useQueryGraphStep({
|
const { data: shippingOptions } = useQueryGraphStep({
|
||||||
entity: "shipping_option",
|
entity: "shipping_option",
|
||||||
fields: [
|
fields: [
|
||||||
"price_set.*"
|
"price_set.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// shippingOptions.price_set
|
// shippingOptions.price_set
|
||||||
@@ -245,8 +245,8 @@ To retrieve the stock location of a fulfillment provider, pass `locations.*` in
|
|||||||
const { data: fulfillmentSets } = await query.graph({
|
const { data: fulfillmentSets } = await query.graph({
|
||||||
entity: "fulfillment_set",
|
entity: "fulfillment_set",
|
||||||
fields: [
|
fields: [
|
||||||
"location.*"
|
"location.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// fulfillmentSets.location
|
// fulfillmentSets.location
|
||||||
@@ -263,8 +263,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: fulfillmentSets } = useQueryGraphStep({
|
const { data: fulfillmentSets } = useQueryGraphStep({
|
||||||
entity: "fulfillment_set",
|
entity: "fulfillment_set",
|
||||||
fields: [
|
fields: [
|
||||||
"location.*"
|
"location.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// fulfillmentSets.location
|
// fulfillmentSets.location
|
||||||
|
|||||||
@@ -6,80 +6,52 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Fulfillment Module provides fulfillment-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Fulfillment Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Fulfillment Module's Service
|
Medusa has fulfillment related features available out-of-the-box through the Fulfillment Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Fulfillment Module.
|
||||||
|
|
||||||
You can use the Fulfillment Module's main service by resolving from the Medusa container the resource `Modules.FULFILLMENT`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
## Fulfillment Features
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
- [Fulfillment Management](./item-fulfillment/page.mdx): Create fulfillments and keep track of their status, items, and more.
|
||||||
const fulfillmentModuleService = container.resolve(
|
- [Integrate Third-Party Fulfillment Providers](./fulfillment-provider/page.mdx): Create third-party fulfillment providers to provide customers with shipping options and fulfill their orders.
|
||||||
Modules.FULFILLMENT
|
- [Restrict By Location and Rules](./shipping-option/page.mdx): Shipping options can be restricted to specific geographical locations. You can also specify custom rules to restrict shipping options.
|
||||||
)
|
- [Support Different Fulfillment Forms](./concepts/page.mdx): Support various fulfillment forms, such as shipping or pick up.
|
||||||
|
|
||||||
const fulfillments = await fulfillmentModuleService.listFulfillments()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const fulfillmentModuleService = req.scope.resolve(
|
|
||||||
Modules.FULFILLMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
fulfillments: await fulfillmentModuleService.listFulfillments(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const fulfillmentModuleService = container.resolve(
|
|
||||||
Modules.FULFILLMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const fulfillments = await fulfillmentModuleService.listFulfillments()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## How to Use the Fulfillment Module
|
||||||
|
|
||||||
### Fulfillment Management
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
Create fulfillments and keep track of their status, items, and more.
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
```ts
|
For example:
|
||||||
const fulfillment = await fulfillmentModuleService.createFulfillment({
|
|
||||||
|
export const highlights = [
|
||||||
|
["12", "Modules.FULFILLMENT", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/create-fulfillment.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const createFulfillmentStep = createStep(
|
||||||
|
"create-fulfillment",
|
||||||
|
async ({}, { container }) => {
|
||||||
|
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
|
||||||
|
|
||||||
|
const fulfillment = await fulfillmentModuleService.createFulfillment({
|
||||||
location_id: "loc_123",
|
location_id: "loc_123",
|
||||||
provider_id: "webshipper",
|
provider_id: "webshipper",
|
||||||
delivery_address: {
|
delivery_address: {
|
||||||
@@ -97,71 +69,113 @@ const fulfillment = await fulfillmentModuleService.createFulfillment({
|
|||||||
],
|
],
|
||||||
labels: [],
|
labels: [],
|
||||||
order: {},
|
order: {},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ fulfillment }, fulfillment.id)
|
||||||
|
},
|
||||||
|
async (fulfillmentId, { container }) => {
|
||||||
|
if (!fulfillmentId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
|
||||||
|
|
||||||
|
await fulfillmentModuleService.deleteFulfillment(fulfillmentId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createFulfillmentWorkflow = createWorkflow(
|
||||||
|
"create-fulfillment",
|
||||||
|
() => {
|
||||||
|
const { fulfillment } = createFulfillmentStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
fulfillment,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Integrate Third-Party Fulfillment Providers
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
Use third-party fulfillment providers to provide customers with shipping options and fulfill their orders.
|
<CodeTabs group="resource-types">
|
||||||
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
const shippingOption = await fulfillmentModuleService.createShippingOptions({
|
import type {
|
||||||
// ...
|
MedusaRequest,
|
||||||
provider_id: "webshipper",
|
MedusaResponse,
|
||||||
})
|
} from "@medusajs/framework/http"
|
||||||
|
import { createFulfillmentWorkflow } from "../../workflows/create-fuilfillment"
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) {
|
||||||
|
const { result } = await createFulfillmentWorkflow(req.scope)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
res.send(result)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Restrict By Location and Rules
|
</CodeTab>
|
||||||
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
Shipping options can be restricted to specific geographical locations. You can also specify custom rules to restrict shipping options.
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import {
|
||||||
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createFulfillmentWorkflow } from "../workflows/create-fuilfillment"
|
||||||
|
|
||||||
```ts
|
export default async function handleUserCreated({
|
||||||
const serviceZone = await fulfillmentModuleService.createServiceZones({
|
event: { data },
|
||||||
name: "US",
|
container,
|
||||||
fulfillment_set_id: "fset_123",
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
geo_zones: [
|
const { result } = await createFulfillmentWorkflow(container)
|
||||||
{
|
.run()
|
||||||
type: "country",
|
|
||||||
country_code: "us",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
const shippingOption = await fulfillmentModuleService.createShippingOptions({
|
console.log(result)
|
||||||
name: "Express Shipping",
|
}
|
||||||
// restrict location
|
|
||||||
service_zone_id: serviceZone.id,
|
export const config: SubscriberConfig = {
|
||||||
// restrict by custom rules
|
event: "user.created",
|
||||||
rules: [
|
}
|
||||||
{
|
|
||||||
field: "customer_group",
|
|
||||||
operator: "eq",
|
|
||||||
value: "vip",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
// ...
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Support Different Fulfillment Forms
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
Support various fulfillment forms, such as shipping or pick up.
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createFulfillmentWorkflow } from "../workflows/create-fuilfillment"
|
||||||
|
|
||||||
```ts
|
export default async function myCustomJob(
|
||||||
const fulfillmentSets = await fulfillmentModuleService.createFulfillmentSets([
|
container: MedusaContainer
|
||||||
{
|
) {
|
||||||
name: "Shipping",
|
const { result } = await createFulfillmentWorkflow(container)
|
||||||
type: "shipping",
|
.run()
|
||||||
},
|
|
||||||
{
|
console.log(result)
|
||||||
name: "Pick-up",
|
}
|
||||||
type: "pick-up",
|
|
||||||
},
|
export const config = {
|
||||||
])
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Configure Fulfillment Module
|
## Configure Fulfillment Module
|
||||||
|
|
||||||
Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
The Fulfillment Module accepts options for further configurations. Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Fulfillment" />
|
||||||
@@ -1,526 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Inventory Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this document, you’ll find common examples of how you can use the Inventory Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Inventory Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create an Inventory Item
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const inventoryItem = await inventoryModuleService.createInventoryItems({
|
|
||||||
sku: request.body.sku,
|
|
||||||
title: request.body.title,
|
|
||||||
requires_shipping: request.body.requires_shipping,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ inventory_item: inventoryItem })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const inventoryItem = await inventoryModuleService.createInventoryItems({
|
|
||||||
sku: body.sku,
|
|
||||||
title: body.title,
|
|
||||||
requires_shipping: body.requires_shipping,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ inventory_item: inventoryItem })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Inventory Items
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const inventoryItems = await inventoryModuleService.listInventoryItems({})
|
|
||||||
|
|
||||||
res.json({ inventory_items: inventoryItems })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
|
|
||||||
const inventoryItems = await inventoryModuleService.listInventoryItems({})
|
|
||||||
|
|
||||||
return NextResponse.json({ inventory_items: inventoryItems })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve an Inventory Item by its ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const inventoryItem = await inventoryModuleService.retrieveInventoryItem(
|
|
||||||
request.params.id
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({ inventory_item: inventoryItem })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function GET(request: Request, { params }: ContextType) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
|
|
||||||
const inventoryItem = await inventoryModuleService.retrieveInventoryItem(
|
|
||||||
params.id
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ inventory_item: inventoryItem })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create an Inventory Level
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const inventoryLevel = await inventoryModuleService.createInventoryLevels({
|
|
||||||
inventory_item_id: request.body.inventory_item_id,
|
|
||||||
location_id: request.body.location_id,
|
|
||||||
stocked_quantity: request.body.quantity,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ inventory_level: inventoryLevel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const inventoryLevel = await inventoryModuleService.createInventoryLevels({
|
|
||||||
inventory_item_id: body.inventory_item_id,
|
|
||||||
location_id: body.location_id,
|
|
||||||
stocked_quantity: body.quantity,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ inventory_level: inventoryLevel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Adjust Inventory in Location
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const inventoryLevel = await inventoryModuleService.adjustInventory(
|
|
||||||
request.body.inventory_item_id,
|
|
||||||
request.body.location_id,
|
|
||||||
request.body.new_quantity
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({ inventory_level: inventoryLevel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const inventoryLevel = await inventoryModuleService.adjustInventory(
|
|
||||||
body.inventory_item_id,
|
|
||||||
body.location_id,
|
|
||||||
body.new_quantity
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ inventory_level: inventoryLevel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Confirm Inventory Item Availability
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
is_available: await inventoryModuleService.confirmInventory(
|
|
||||||
request.body.inventory_item_id,
|
|
||||||
[request.body.location_id],
|
|
||||||
request.body.required_quantity
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
is_available: await inventoryModuleService.confirmInventory(
|
|
||||||
body.inventory_item_id,
|
|
||||||
[body.location_id],
|
|
||||||
body.required_quantity
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create Reservation Item
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const reservationItem = await inventoryModuleService.createReservationItems({
|
|
||||||
inventory_item_id: request.body.inventory_item_id,
|
|
||||||
location_id: request.body.location_id,
|
|
||||||
quantity: request.body.reserved_quantity,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ reservation_item: reservationItem })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const reservationItem = await inventoryModuleService.createReservationItems({
|
|
||||||
inventory_item_id: body.inventory_item_id,
|
|
||||||
location_id: body.location_id,
|
|
||||||
quantity: body.reserved_quantity,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
reservation_item: reservationItem,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve Quantity Details
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
const reservedQuantity =
|
|
||||||
await inventoryModuleService.retrieveReservedQuantity(
|
|
||||||
request.params.inventory_item_id,
|
|
||||||
[request.params.location_id]
|
|
||||||
)
|
|
||||||
const stockedQuantity = await inventoryModuleService.retrieveStockedQuantity(
|
|
||||||
request.params.inventory_item_id,
|
|
||||||
[request.params.location_id]
|
|
||||||
)
|
|
||||||
const availableQuantity =
|
|
||||||
await inventoryModuleService.retrieveAvailableQuantity(
|
|
||||||
request.params.inventory_item_id,
|
|
||||||
[request.params.location_id]
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
reserved_quantity: reservedQuantity,
|
|
||||||
stocked_quantity: stockedQuantity,
|
|
||||||
available_quantity: availableQuantity,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
inventory_item_id: string
|
|
||||||
location_id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
|
|
||||||
const reservedQuantity =
|
|
||||||
await inventoryModuleService.retrieveReservedQuantity(
|
|
||||||
params.inventory_item_id,
|
|
||||||
[params.location_id]
|
|
||||||
)
|
|
||||||
const stockedQuantity = await inventoryModuleService.retrieveStockedQuantity(
|
|
||||||
params.inventory_item_id,
|
|
||||||
[params.location_id]
|
|
||||||
)
|
|
||||||
const availableQuantity =
|
|
||||||
await inventoryModuleService.retrieveAvailableQuantity(
|
|
||||||
params.inventory_item_id,
|
|
||||||
[params.location_id]
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
reserved_quantity: reservedQuantity,
|
|
||||||
stocked_quantity: stockedQuantity,
|
|
||||||
available_quantity: availableQuantity,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete a Reservation
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const inventoryModuleService = request.scope.resolve(
|
|
||||||
Modules.INVENTORY
|
|
||||||
)
|
|
||||||
|
|
||||||
await inventoryModuleService.deleteReservationItems([
|
|
||||||
request.params.reservation_id,
|
|
||||||
])
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { initialize as initializeInventoryModule } from "@medusajs/medusa/inventory"
|
|
||||||
|
|
||||||
type ContextType = {
|
|
||||||
params: {
|
|
||||||
reservation_id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function DELETE(request: Request, { params }: ContextType) {
|
|
||||||
const inventoryModuleService = await initializeInventoryModule({})
|
|
||||||
|
|
||||||
await inventoryModuleService.deleteReservationItems([params.reservation_id])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Inventory Module's main service reference](/references/inventory-next) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -42,8 +42,8 @@ To retrieve the product variants of an inventory item with [Query](!docs!/learn/
|
|||||||
const { data: inventoryItems } = await query.graph({
|
const { data: inventoryItems } = await query.graph({
|
||||||
entity: "inventory_item",
|
entity: "inventory_item",
|
||||||
fields: [
|
fields: [
|
||||||
"variants.*"
|
"variants.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// inventoryItems.variants
|
// inventoryItems.variants
|
||||||
@@ -60,8 +60,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: inventoryItems } = useQueryGraphStep({
|
const { data: inventoryItems } = useQueryGraphStep({
|
||||||
entity: "inventory_item",
|
entity: "inventory_item",
|
||||||
fields: [
|
fields: [
|
||||||
"variants.*"
|
"variants.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// inventoryItems.variants
|
// inventoryItems.variants
|
||||||
@@ -131,8 +131,8 @@ To retrieve the stock locations of an inventory level with [Query](!docs!/learn/
|
|||||||
const { data: inventoryLevels } = await query.graph({
|
const { data: inventoryLevels } = await query.graph({
|
||||||
entity: "inventory_level",
|
entity: "inventory_level",
|
||||||
fields: [
|
fields: [
|
||||||
"stock_locations.*"
|
"stock_locations.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// inventoryLevels.stock_locations
|
// inventoryLevels.stock_locations
|
||||||
@@ -149,8 +149,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: inventoryLevels } = useQueryGraphStep({
|
const { data: inventoryLevels } = useQueryGraphStep({
|
||||||
entity: "inventory_level",
|
entity: "inventory_level",
|
||||||
fields: [
|
fields: [
|
||||||
"stock_locations.*"
|
"stock_locations.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// inventoryLevels.stock_locations
|
// inventoryLevels.stock_locations
|
||||||
|
|||||||
@@ -6,130 +6,156 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Inventory Module provides inventory-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Inventory Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Inventory Module's Service
|
Medusa has inventory related features available out-of-the-box through the Inventory Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Inventory Module.
|
||||||
|
|
||||||
You can use the Inventory Module's main service by resolving from the Medusa container the resource `Modules.INVENTORY`.
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Inventory Features
|
||||||
|
|
||||||
|
- [Inventory Items Management](./concepts/page.mdx): Store and manage inventory of any stock-kept item, such as product variants.
|
||||||
|
- [Inventory Across Locations](./concepts/page.mdx#inventorylevel): Manage inventory levels across different locations, such as warehouses.
|
||||||
|
- [Reservation Management](./concepts/page.mdx#reservationitem): Reserve quantities of inventory items at specific locations for orders or other purposes.
|
||||||
|
- [Check Inventory Availability](/references/inventory-next/confirmInventory): Check whether an inventory item has the necessary quantity for purchase.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use the Inventory Module
|
||||||
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.INVENTORY", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-inventory-item.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createInventoryItemStep = createStep(
|
||||||
const inventoryModuleService = container.resolve(
|
"create-inventory-item",
|
||||||
Modules.INVENTORY
|
async ({}, { container }) => {
|
||||||
)
|
const inventoryModuleService = container.resolve(Modules.INVENTORY)
|
||||||
|
|
||||||
const inventoryItems = await inventoryModuleService.listInventoryItems({})
|
const inventoryItem = await inventoryModuleService.createInventoryItems({
|
||||||
})
|
sku: "SHIRT",
|
||||||
|
title: "Green Medusa Shirt",
|
||||||
|
requires_shipping: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ inventoryItem }, inventoryItem.id)
|
||||||
|
},
|
||||||
|
async (inventoryItemId, { container }) => {
|
||||||
|
if (!inventoryItemId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const inventoryModuleService = container.resolve(Modules.INVENTORY)
|
||||||
|
|
||||||
|
await inventoryModuleService.deleteInventoryItems([inventoryItemId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createInventoryItemWorkflow = createWorkflow(
|
||||||
|
"create-inventory-item-workflow",
|
||||||
|
() => {
|
||||||
|
const { inventoryItem } = createInventoryItemStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
inventoryItem,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createInventoryItemWorkflow } from "../../workflows/create-inventory-item"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const inventoryModuleService = request.scope.resolve(
|
const { result } = await createInventoryItemWorkflow(req.scope)
|
||||||
Modules.INVENTORY
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
inventory_items: await inventoryModuleService.listInventoryItems({}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createInventoryItemWorkflow } from "../workflows/create-inventory-item"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const inventoryModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.INVENTORY
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createInventoryItemWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const inventoryItems = await inventoryModuleService.listInventoryItems({})
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createInventoryItemWorkflow } from "../workflows/create-inventory-item"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createInventoryItemWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Inventory" />
|
||||||
|
|
||||||
### Inventory Items Management
|
|
||||||
|
|
||||||
Store and manage inventory of any stock-kept item, such as product variants.
|
|
||||||
|
|
||||||
Inventory items hold details of the underlying stock-kept item, as well as inventory details such as whether the item requires shipping.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const inventoryItem = await inventoryModuleService.createInventoryItems({
|
|
||||||
sku: "SHIRT",
|
|
||||||
title: "Green Medusa Shirt",
|
|
||||||
requires_shipping: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Inventory Across Locations
|
|
||||||
|
|
||||||
Inventory items' quantities are set per locations through inventory levels.
|
|
||||||
|
|
||||||
This gives you more flexibility in managing the quantity of a stock-kept item across different locations, such as different warehouses.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const inventoryLevel = await inventoryModuleService.createInventoryLevels([
|
|
||||||
{
|
|
||||||
inventory_item_id: "iitem_123",
|
|
||||||
location_id: "sloc_123",
|
|
||||||
stocked_quantity: 20,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
### Reservation Management
|
|
||||||
|
|
||||||
Reserve quantities of inventory items at specific locations for orders or other purposes.
|
|
||||||
|
|
||||||
The reserved quantity isn't considered for purchase, but can be deleted to revert the reservation.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const reservationItem = await inventoryModuleService.createReservationItems([
|
|
||||||
{
|
|
||||||
inventory_item_id: "iitem_123",
|
|
||||||
location_id: "sloc_123",
|
|
||||||
quantity: 10,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
### Check Inventory Availability
|
|
||||||
|
|
||||||
Check whether an inventory item has the necessary quantity for purchase.
|
|
||||||
|
|
||||||
Any reserved quantity is considered unavailable.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const isAvailable = await inventoryModuleService.confirmInventory(
|
|
||||||
inventoryItemId,
|
|
||||||
[locationId],
|
|
||||||
neededQuantity
|
|
||||||
)
|
|
||||||
```
|
|
||||||
@@ -47,8 +47,8 @@ To retrieve the customer of an order with [Query](!docs!/learn/fundamentals/modu
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"customer.*"
|
"customer.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.customer
|
// orders.customer
|
||||||
@@ -65,8 +65,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"customer.*"
|
"customer.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.customer
|
// orders.customer
|
||||||
@@ -96,8 +96,8 @@ To retrieve the cart of an order with [Query](!docs!/learn/fundamentals/module-l
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"cart.*"
|
"cart.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.cart
|
// orders.cart
|
||||||
@@ -114,8 +114,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"cart.*"
|
"cart.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.cart
|
// orders.cart
|
||||||
@@ -197,8 +197,8 @@ To retrieve the fulfillments of a return, pass `fulfillments.*` in `fields`.
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"fulfillments.*"
|
"fulfillments.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.fulfillments
|
// orders.fulfillments
|
||||||
@@ -215,8 +215,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"fulfillments.*"
|
"fulfillments.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.fulfillments
|
// orders.fulfillments
|
||||||
@@ -290,8 +290,8 @@ To retrieve the payment collections of an order, order exchange, or order claim
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"payment_collections.*"
|
"payment_collections.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.payment_collections
|
// orders.payment_collections
|
||||||
@@ -308,8 +308,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"payment_collections.*"
|
"payment_collections.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.payment_collections
|
// orders.payment_collections
|
||||||
@@ -388,8 +388,8 @@ To retrieve the product, pass `product.*` in `fields`.
|
|||||||
const { data: lineItems } = await query.graph({
|
const { data: lineItems } = await query.graph({
|
||||||
entity: "order_line_item",
|
entity: "order_line_item",
|
||||||
fields: [
|
fields: [
|
||||||
"variant.*"
|
"variant.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// lineItems.variant
|
// lineItems.variant
|
||||||
@@ -406,8 +406,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: lineItems } = useQueryGraphStep({
|
const { data: lineItems } = useQueryGraphStep({
|
||||||
entity: "order_line_item",
|
entity: "order_line_item",
|
||||||
fields: [
|
fields: [
|
||||||
"variant.*"
|
"variant.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// lineItems.variant
|
// lineItems.variant
|
||||||
@@ -435,8 +435,8 @@ To retrieve the promotion applied on an order with [Query](!docs!/learn/fundamen
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"promotion.*"
|
"promotion.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.promotion
|
// orders.promotion
|
||||||
@@ -453,8 +453,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"promotion.*"
|
"promotion.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.promotion
|
// orders.promotion
|
||||||
@@ -524,8 +524,8 @@ To retrieve the region of an order with [Query](!docs!/learn/fundamentals/module
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"region.*"
|
"region.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.region
|
// orders.region
|
||||||
@@ -542,8 +542,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"region.*"
|
"region.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.region
|
// orders.region
|
||||||
@@ -569,8 +569,8 @@ To retrieve the sales channel of an order with [Query](!docs!/learn/fundamentals
|
|||||||
const { data: orders } = await query.graph({
|
const { data: orders } = await query.graph({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channel.*"
|
"sales_channel.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.sales_channel
|
// orders.sales_channel
|
||||||
@@ -587,8 +587,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: orders } = useQueryGraphStep({
|
const { data: orders } = useQueryGraphStep({
|
||||||
entity: "order",
|
entity: "order",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channel.*"
|
"sales_channel.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// orders.sales_channel
|
// orders.sales_channel
|
||||||
|
|||||||
@@ -6,79 +6,53 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Order Module provides order-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Order Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Order Module's Service
|
Medusa has order related features available out-of-the-box through the Order Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Order Module.
|
||||||
|
|
||||||
You can use the Order Module's main service by resolving from the Medusa container the resource `Modules.ORDER`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
## Order Features
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
- [Order Management](./concepts/page.mdx): Store and manage your orders to retrieve, create, cancel, and perform other operations.
|
||||||
const orderModuleService = container.resolve(
|
- Draft Orders: Allow merchants to create orders on behalf of their customers as draft orders that later are transformed to regular orders.
|
||||||
Modules.ORDER
|
- [Apply Promotion Adjustments](./promotion-adjustments/page.mdx): Apply promotions or discounts to the order's items and shipping methods by adding adjustment lines that are factored into their subtotals.
|
||||||
)
|
- [Apply Tax Lines](./tax-lines/page.mdx): Apply tax lines to an order's line items and shipping methods.
|
||||||
const orders = await orderModuleService.listOrders()
|
- [Returns](./return/page.mdx), [Edits](./edit/page.mdx), [Exchanges](./exchange/page.mdx), and [Claims](./claim/page.mdx): Make [changes](./order-change/page.mdx) to an order to edit, return, or exchange its items, with [version-based control](./order-versioning/page.mdx) over the order's timeline.
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const orderModuleService = req.scope.resolve(
|
|
||||||
Modules.ORDER
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
orders: await orderModuleService.listOrders(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const orderModuleService = container.resolve(
|
|
||||||
Modules.ORDER
|
|
||||||
)
|
|
||||||
|
|
||||||
const orders = await orderModuleService.listOrders()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## How to Use the Order Module
|
||||||
|
|
||||||
### Order Management
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
Store and manage your orders to retrieve, create, cancel, and perform other operations.
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
```ts
|
For example:
|
||||||
const order = await orderModuleService.createOrders({
|
|
||||||
|
export const highlights = [
|
||||||
|
["12", "Modules.ORDER", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/create-draft-order.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const createDraftOrderStep = createStep(
|
||||||
|
"create-order",
|
||||||
|
async ({}, { container }) => {
|
||||||
|
const orderModuleService = container.resolve(Modules.ORDER)
|
||||||
|
|
||||||
|
const draftOrder = await orderModuleService.createOrders({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@@ -93,51 +67,108 @@ const order = await orderModuleService.createOrders({
|
|||||||
amount: 3000,
|
amount: 3000,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Draft Orders
|
|
||||||
|
|
||||||
Allow merchants to create orders on behalf of their customers as draft orders that later are transformed to regular orders.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const draftOrder = await orderModuleService.createOrders({
|
|
||||||
currency_code: "usd",
|
|
||||||
// other details...
|
|
||||||
status: "draft",
|
status: "draft",
|
||||||
is_draft_order: true,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Apply Promotions
|
|
||||||
|
|
||||||
Apply promotions or discounts to the order's items and shipping methods by adding adjustment lines that are factored into their subtotals.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const lineAdjustments = await orderModuleService.createOrderLineItemAdjustments({
|
|
||||||
item_id: "cali_123",
|
|
||||||
code: "50OFF",
|
|
||||||
amount: 500,
|
|
||||||
})
|
|
||||||
|
|
||||||
const shippingAdjustments =
|
|
||||||
await orderModuleService.createOrderShippingMethodAdjustments({
|
|
||||||
shipping_method_id: "casm_123",
|
|
||||||
code: "FREESHIPPING",
|
|
||||||
amount: 1000,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ draftOrder }, draftOrder.id)
|
||||||
|
},
|
||||||
|
async (draftOrderId, { container }) => {
|
||||||
|
if (!draftOrderId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const orderModuleService = container.resolve(Modules.ORDER)
|
||||||
|
|
||||||
|
await orderModuleService.deleteOrders([draftOrderId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createDraftOrderWorkflow = createWorkflow(
|
||||||
|
"create-draft-order",
|
||||||
|
() => {
|
||||||
|
const { draftOrder } = createDraftOrderStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
draftOrder,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Returns, Exchanges, and Claims
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
Return or exchange items, with version-based control over the order's timeline.
|
<CodeTabs group="resource-types">
|
||||||
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
const orderReturn = await orderModuleService.createReturn({
|
import type {
|
||||||
order_id: "order_123",
|
MedusaRequest,
|
||||||
items: [{
|
MedusaResponse,
|
||||||
id: "orditem_123",
|
} from "@medusajs/framework/http"
|
||||||
quantity: 1,
|
import { createDraftOrderWorkflow } from "../../workflows/create-draft-order"
|
||||||
}],
|
|
||||||
})
|
export async function GET(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) {
|
||||||
|
const { result } = await createDraftOrderWorkflow(req.scope)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
res.send(result)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import {
|
||||||
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createDraftOrderWorkflow } from "../workflows/create-draft-order"
|
||||||
|
|
||||||
|
export default async function handleUserCreated({
|
||||||
|
event: { data },
|
||||||
|
container,
|
||||||
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createDraftOrderWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createDraftOrderWorkflow } from "../workflows/create-draft-order"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createDraftOrderWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Order" />
|
||||||
@@ -1,359 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Payment Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Payment Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Payment Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Payment Collection
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const paymentModuleService = req.scope.resolve(
|
|
||||||
Modules.PAYMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const paymentCollection = await paymentModuleService.createPaymentCollections(
|
|
||||||
{
|
|
||||||
region_id: "reg_123",
|
|
||||||
currency_code: "usd",
|
|
||||||
amount: 4000,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
payment_collection: paymentCollection,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePaymentModule } from "@medusajs/medusa/payment"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const paymentModuleService = await initializePaymentModule()
|
|
||||||
|
|
||||||
const paymentCollection = await paymentModuleService.createPaymentCollections(
|
|
||||||
{
|
|
||||||
region_id: "reg_123",
|
|
||||||
currency_code: "usd",
|
|
||||||
amount: 4000,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
payment_collection: paymentCollection,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create Payment Session
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type" isCodeCodeTabs={true}>
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const paymentModuleService = req.scope.resolve(
|
|
||||||
Modules.PAYMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const paymentSession = await paymentModuleService.createPaymentSession(
|
|
||||||
"pay_col_123",
|
|
||||||
{
|
|
||||||
currency_code: "usd",
|
|
||||||
provider_id: "system",
|
|
||||||
amount: 4000,
|
|
||||||
data: {},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
payment_session: paymentSession,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePaymentModule } from "@medusajs/medusa/payment"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const paymentModuleService = await initializePaymentModule()
|
|
||||||
|
|
||||||
const paymentSession = await paymentModuleService.createPaymentSession(
|
|
||||||
"pay_col_123",
|
|
||||||
{
|
|
||||||
currency_code: "usd",
|
|
||||||
provider_id: "system",
|
|
||||||
amount: 4000,
|
|
||||||
data: {},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
payment_session: paymentSession,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Payment Sessions of Payment Collection
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type" isCodeCodeTabs={true}>
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const paymentModuleService = req.scope.resolve(
|
|
||||||
Modules.PAYMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const paymentSessions = await paymentModuleService.listPaymentSessions({
|
|
||||||
payment_collection_id: ["pay_col_123"],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
payment_sessions: paymentSessions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePaymentModule } from "@medusajs/medusa/payment"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const paymentModuleService = await initializePaymentModule()
|
|
||||||
|
|
||||||
const paymentSessions = await paymentModuleService.listPaymentSessions({
|
|
||||||
payment_collection_id: ["pay_col_123"],
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
payment_sessions: paymentSessions,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Authorize Payment Session
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type" isCodeCodeTabs={true}>
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const paymentModuleService = req.scope.resolve(
|
|
||||||
Modules.PAYMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const payment = await paymentModuleService.authorizePaymentSession(
|
|
||||||
"payses_123",
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
payment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePaymentModule } from "@medusajs/medusa/payment"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const paymentModuleService = await initializePaymentModule()
|
|
||||||
|
|
||||||
const payment = await paymentModuleService.authorizePaymentSession(
|
|
||||||
"payses_123",
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
payment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Payments of Payment Session
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type" isCodeCodeTabs={true}>
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const paymentModuleService = req.scope.resolve(
|
|
||||||
Modules.PAYMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const payments = await paymentModuleService.listPayments({
|
|
||||||
session_id: "payses_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
payments,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePaymentModule } from "@medusajs/medusa/payment"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const paymentModuleService = await initializePaymentModule()
|
|
||||||
|
|
||||||
const payments = await paymentModuleService.listPayments({
|
|
||||||
session_id: "payses_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
payments,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Capture Payment
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type" isCodeCodeTabs={true}>
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const paymentModuleService = req.scope.resolve(
|
|
||||||
Modules.PAYMENT
|
|
||||||
)
|
|
||||||
|
|
||||||
const payment = await paymentModuleService.capturePayment({
|
|
||||||
payment_id: "pay_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
payment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePaymentModule } from "@medusajs/medusa/payment"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const paymentModuleService = await initializePaymentModule()
|
|
||||||
|
|
||||||
const payment = await paymentModuleService.capturePayment({
|
|
||||||
payment_id: "pay_123",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
payment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Payment Module's main service reference](/references/payment) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -39,8 +39,8 @@ To retrieve the cart associated with the payment collection with [Query](!docs!/
|
|||||||
const { data: paymentCollections } = await query.graph({
|
const { data: paymentCollections } = await query.graph({
|
||||||
entity: "payment_collection",
|
entity: "payment_collection",
|
||||||
fields: [
|
fields: [
|
||||||
"cart.*"
|
"cart.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// paymentCollections.cart
|
// paymentCollections.cart
|
||||||
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: paymentCollections } = useQueryGraphStep({
|
const { data: paymentCollections } = useQueryGraphStep({
|
||||||
entity: "payment_collection",
|
entity: "payment_collection",
|
||||||
fields: [
|
fields: [
|
||||||
"cart.*"
|
"cart.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// paymentCollections.cart
|
// paymentCollections.cart
|
||||||
@@ -131,8 +131,8 @@ To retrieve the order of a payment collection with [Query](!docs!/learn/fundamen
|
|||||||
const { data: paymentCollections } = await query.graph({
|
const { data: paymentCollections } = await query.graph({
|
||||||
entity: "payment_collection",
|
entity: "payment_collection",
|
||||||
fields: [
|
fields: [
|
||||||
"order.*"
|
"order.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// paymentCollections.order
|
// paymentCollections.order
|
||||||
@@ -149,8 +149,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: paymentCollections } = useQueryGraphStep({
|
const { data: paymentCollections } = useQueryGraphStep({
|
||||||
entity: "payment_collection",
|
entity: "payment_collection",
|
||||||
fields: [
|
fields: [
|
||||||
"order.*"
|
"order.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// paymentCollections.order
|
// paymentCollections.order
|
||||||
@@ -224,8 +224,8 @@ To retrieve the regions of a payment provider with [Query](!docs!/learn/fundamen
|
|||||||
const { data: paymentProviders } = await query.graph({
|
const { data: paymentProviders } = await query.graph({
|
||||||
entity: "payment_provider",
|
entity: "payment_provider",
|
||||||
fields: [
|
fields: [
|
||||||
"regions.*"
|
"regions.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// paymentProviders.regions
|
// paymentProviders.regions
|
||||||
@@ -242,8 +242,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: paymentProviders } = useQueryGraphStep({
|
const { data: paymentProviders } = useQueryGraphStep({
|
||||||
entity: "payment_provider",
|
entity: "payment_provider",
|
||||||
fields: [
|
fields: [
|
||||||
"regions.*"
|
"regions.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// paymentProviders.regions
|
// paymentProviders.regions
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
import { CodeTabs, CodeTab, ChildDocs } from "docs-ui"
|
||||||
import { Table } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
title: `Payment Module`,
|
title: `Payment Module`,
|
||||||
@@ -7,130 +6,170 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Payment Module provides payment-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Payment Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Payment Module's Service
|
Medusa has payment related features available out-of-the-box through the Payment Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Payment Module.
|
||||||
|
|
||||||
You can use the Payment Module's main service by resolving from the Medusa container the resource `Modules.PAYMENT`.
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Payment Features
|
||||||
|
|
||||||
|
- [Authorize, Capture, and Refund Payments](./payment/page.mdx): Authorize, capture, and refund payments for a single resource.
|
||||||
|
- [Payment Collection Management](./payment-collection/page.mdx): Store and manage all payments of a single resources, such as a cart, in payment collections.
|
||||||
|
- [Integrate Third-Party Payment Providers](./payment-provider/page.mdx): Use payment providers like [Stripe](./payment-provider/stripe/page.mdx) to handle and process payments, or integrate custom payment providers.
|
||||||
|
- [Handle Webhook Events](./webhook-events/page.mdx): Handle webhook events from third-party providers and process the associated payment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use the Payment Module
|
||||||
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.PAYMENT", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-payment-collection.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createPaymentCollectionStep = createStep(
|
||||||
const paymentModuleService = container.resolve(
|
"create-payment-collection",
|
||||||
Modules.PAYMENT
|
async ({}, { container }) => {
|
||||||
)
|
const paymentModuleService = container.resolve(Modules.PAYMENT)
|
||||||
|
|
||||||
const payment_collections =
|
const paymentCollection = await paymentModuleService.createPaymentCollections({
|
||||||
await paymentModuleService.listPaymentCollections()
|
region_id: "reg_123",
|
||||||
})
|
currency_code: "usd",
|
||||||
|
amount: 5000,
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ paymentCollection }, paymentCollection.id)
|
||||||
|
},
|
||||||
|
async (paymentCollectionId, { container }) => {
|
||||||
|
if (!paymentCollectionId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const paymentModuleService = container.resolve(Modules.PAYMENT)
|
||||||
|
|
||||||
|
await paymentModuleService.deletePaymentCollections([paymentCollectionId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createPaymentCollectionWorkflow = createWorkflow(
|
||||||
|
"create-payment-collection",
|
||||||
|
() => {
|
||||||
|
const { paymentCollection } = createPaymentCollectionStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
paymentCollection,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createPaymentCollectionWorkflow } from "../../workflows/create-payment-collection"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const paymentModuleService = req.scope.resolve(
|
const { result } = await createPaymentCollectionWorkflow(req.scope)
|
||||||
Modules.PAYMENT
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
payment_collections: await paymentModuleService.listPaymentCollections(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const paymentModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.PAYMENT
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createPaymentCollectionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const payment_collections =
|
console.log(result)
|
||||||
await paymentModuleService.listPaymentCollections()
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createPaymentCollectionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
---
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Add Payment Functionalities to Any Resource
|
|
||||||
|
|
||||||
The Payment Module provides payment functionalities that allow you to process payment of any resource, such as a cart.
|
|
||||||
|
|
||||||
All payment processing starts with creating a payment collection, which carries information for authorized, captured, and refunded payments for a single resource.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const paymentCollection = await paymentModuleService.createPaymentCollections({
|
|
||||||
region_id: "reg_123",
|
|
||||||
currency_code: "usd",
|
|
||||||
amount: 5000,
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Authorize, Capture, and Refund Payment
|
|
||||||
|
|
||||||
Receive and handle payments, including authorizing, capturing, and refunding payment.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await paymentModuleService.capturePayment({
|
|
||||||
payment_id: "pay_1",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Integrate Third-Party Payment Providers
|
|
||||||
|
|
||||||
Use payment providers like Stripe to handle and process payments.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const payment = await paymentModuleService.createPaymentSession("pay_col_1", {
|
|
||||||
provider_id: "stripe",
|
|
||||||
amount: 1000,
|
|
||||||
currency_code: "usd",
|
|
||||||
data: {
|
|
||||||
// necessary data for the payment provider
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Handle Webhook Events
|
|
||||||
|
|
||||||
The Payment Module allows you to handle webhook events from third-party providers and process the associated payment.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await paymentModuleService.processEvent({
|
|
||||||
provider: "stripe",
|
|
||||||
payload: {
|
|
||||||
// webhook payload
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Configure Payment Module
|
## Configure Payment Module
|
||||||
|
|
||||||
Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
The Payment Module accepts options for further configurations. Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Providers
|
||||||
|
|
||||||
|
Medusa provides the following payment providers out-of-the-box. You can use them to process payments for orders, returns, and other resources.
|
||||||
|
|
||||||
|
<ChildDocs showItems={["Providers"]} hideTitle />
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Payment" />
|
||||||
@@ -1,395 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Pricing Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this document, you’ll find common examples of how you can use the Pricing Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Pricing Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Price Set
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSet = await pricingModuleService.createPriceSets([
|
|
||||||
{
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
currency_code: "eur",
|
|
||||||
amount: 10,
|
|
||||||
rules: {
|
|
||||||
region_id: "reg_123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
res.json({ price_set: priceSet })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePricingModule } from "@medusajs/medusa/pricing"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const pricingModuleService = await initializePricingModule()
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const priceSet = await pricingModuleService.createPriceSets([
|
|
||||||
{
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
currency_code: "eur",
|
|
||||||
amount: 10,
|
|
||||||
rules: {
|
|
||||||
region_id: "reg_123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
return NextResponse.json({ price_set: priceSet })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Price Sets
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSets = await pricingModuleService.listPriceSets()
|
|
||||||
|
|
||||||
res.json({ price_sets: priceSets })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePricingModule } from "@medusajs/medusa/pricing"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const pricingModuleService = await initializePricingModule()
|
|
||||||
|
|
||||||
const priceSets = await pricingModuleService.listPriceSets()
|
|
||||||
|
|
||||||
return NextResponse.json({ price_sets: priceSets })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Price Set by its ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSet = await pricingModuleService.retrievePriceSet("pset_123")
|
|
||||||
|
|
||||||
res.json({ price_set: priceSet })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePricingModule } from "@medusajs/medusa/pricing"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const pricingModuleService = await initializePricingModule()
|
|
||||||
|
|
||||||
const priceSet = await pricingModuleService.retrievePriceSet("pset_123")
|
|
||||||
|
|
||||||
return NextResponse.json({ price_set: priceSet })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add Prices with Rules
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSet = await pricingModuleService.addPrices({
|
|
||||||
priceSetId: "pset_123",
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 500,
|
|
||||||
currency_code: "USD",
|
|
||||||
rules: {
|
|
||||||
region_id: "reg_123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ price_set: priceSet })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePricingModule } from "@medusajs/medusa/pricing"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const pricingModuleService = await initializePricingModule()
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const priceSet = await pricingModuleService.addPrices({
|
|
||||||
priceSetId: "pset_123",
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 500,
|
|
||||||
currency_code: "USD",
|
|
||||||
rules: {
|
|
||||||
region_id: "reg_123",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ price_set: priceSet })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create Price List
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { IPricingModuleService } from "@medusajs/framework/types"
|
|
||||||
import { PriceListType } from "@medusajs/framework/utils"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceLists = await pricingModuleService.createPriceLists([
|
|
||||||
{
|
|
||||||
title: "My Sale",
|
|
||||||
description: "This is my sale",
|
|
||||||
type: PriceListType.SALE,
|
|
||||||
starts_at: Date.parse("01/10/2023").toString(),
|
|
||||||
ends_at: Date.parse("31/10/2023").toString(),
|
|
||||||
rules: {
|
|
||||||
region_id: ["reg_123", "reg_321"],
|
|
||||||
},
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 400,
|
|
||||||
currency_code: "EUR",
|
|
||||||
price_set_id: "pset_124",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
res.json({ price_lists: priceLists })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
import { PriceListType } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
import { initialize as initializePricingModule } from "@medusajs/medusa/pricing"
|
|
||||||
import { PriceListType } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const pricingModuleService = await initializePricingModule()
|
|
||||||
|
|
||||||
const priceLists = await pricingModuleService.createPriceLists([
|
|
||||||
{
|
|
||||||
title: "My Sale",
|
|
||||||
description: "This is my sale",
|
|
||||||
type: PriceListType.SALE,
|
|
||||||
starts_at: Date.parse("01/10/2023").toString(),
|
|
||||||
ends_at: Date.parse("31/10/2023").toString(),
|
|
||||||
rules: {
|
|
||||||
region_id: ["reg_123", "reg_321"],
|
|
||||||
},
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 400,
|
|
||||||
currency_code: "EUR",
|
|
||||||
price_set_id: "pset_124",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
return NextResponse.json({ price_lists: priceLists })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Calculate Prices For a Currency
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const price = await pricingModuleService.calculatePrices(
|
|
||||||
{
|
|
||||||
id: ["pset_123"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: {
|
|
||||||
currency_code: "eur",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({ price })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePricingModule } from "@medusajs/medusa/pricing"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const pricingModuleService = await initializePricingModule()
|
|
||||||
|
|
||||||
const price = await pricingModuleService.calculatePrices(
|
|
||||||
{
|
|
||||||
id: ["pset_123"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
context: {
|
|
||||||
currency_code: "eur",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ price })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Pricing Module's main service reference](/references/pricing) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -36,8 +36,8 @@ To retrieve the shipping option of a price set with [Query](!docs!/learn/fundame
|
|||||||
const { data: priceSets } = await query.graph({
|
const { data: priceSets } = await query.graph({
|
||||||
entity: "price_set",
|
entity: "price_set",
|
||||||
fields: [
|
fields: [
|
||||||
"shipping_option.*"
|
"shipping_option.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// priceSets.shipping_option
|
// priceSets.shipping_option
|
||||||
@@ -54,8 +54,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: priceSets } = useQueryGraphStep({
|
const { data: priceSets } = useQueryGraphStep({
|
||||||
entity: "price_set",
|
entity: "price_set",
|
||||||
fields: [
|
fields: [
|
||||||
"shipping_option.*"
|
"shipping_option.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// priceSets.shipping_option
|
// priceSets.shipping_option
|
||||||
@@ -133,8 +133,8 @@ To retrieve the variant of a price set with [Query](!docs!/learn/fundamentals/mo
|
|||||||
const { data: priceSets } = await query.graph({
|
const { data: priceSets } = await query.graph({
|
||||||
entity: "price_set",
|
entity: "price_set",
|
||||||
fields: [
|
fields: [
|
||||||
"variant.*"
|
"variant.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// priceSets.variant
|
// priceSets.variant
|
||||||
@@ -151,8 +151,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: priceSets } = useQueryGraphStep({
|
const { data: priceSets } = useQueryGraphStep({
|
||||||
entity: "price_set",
|
entity: "price_set",
|
||||||
fields: [
|
fields: [
|
||||||
"variant.*"
|
"variant.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// priceSets.variant
|
// priceSets.variant
|
||||||
|
|||||||
@@ -6,82 +6,53 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Pricing Module provides pricing-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Pricing Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Pricing Module's Service
|
Medusa has pricing related features available out-of-the-box through the Pricing Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Pricing Module.
|
||||||
|
|
||||||
You can use the Pricing Module's main service by resolving from the Medusa container the resource `Modules.PRICING`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
## Pricing Features
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
- [Price Management](./concepts/page.mdx): Store and manage prices of a resource, such as a product or a variant.
|
||||||
const pricingModuleService = container.resolve(
|
- [Advanced Rule Engine](./price-rules/page.mdx): Create prices with custom rules to condition prices based on different contexts.
|
||||||
Modules.PRICING
|
- [Price Lists](./concepts/page.mdx#price-list): Group prices and apply them only in specific conditions with price lists.
|
||||||
)
|
- [Price Calculation Strategy](./price-calculation/page.mdx): Retrieve the best price in a given context and for the specified rule values.
|
||||||
|
- [Tax-Inclusive Pricing](./tax-inclusive-pricing/page.mdx): Calculate prices with taxes included in the price, and Medusa will handle calculating the taxes automatically.
|
||||||
const priceSets = await pricingModuleService.listPriceSets()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const pricingModuleService = request.scope.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
price_sets: await pricingModuleService.listPriceSets(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const pricingModuleService = container.resolve(
|
|
||||||
Modules.PRICING
|
|
||||||
)
|
|
||||||
|
|
||||||
const priceSets = await pricingModuleService.listPriceSets()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## How to Use the Pricing Module
|
||||||
|
|
||||||
### Price Management
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
Store the prices of a resource and manage them through the main service's methods.
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
Prices are grouped in a price set, allowing you to add more than one price for a resource based on different conditions, such as currency code.
|
For example:
|
||||||
|
|
||||||
```ts
|
export const highlights = [
|
||||||
const priceSet = await pricingModuleService.createPriceSets({
|
["12", "Modules.PRICING", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/create-price-set.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const createPriceSetStep = createStep(
|
||||||
|
"create-price-set",
|
||||||
|
async ({}, { container }) => {
|
||||||
|
const pricingModuleService = container.resolve(Modules.PRICING)
|
||||||
|
|
||||||
|
const priceSet = await pricingModuleService.createPriceSets({
|
||||||
prices: [
|
prices: [
|
||||||
{
|
{
|
||||||
amount: 500,
|
amount: 500,
|
||||||
@@ -95,68 +66,107 @@ const priceSet = await pricingModuleService.createPriceSets({
|
|||||||
rules: {},
|
rules: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
```
|
|
||||||
|
|
||||||
### Advanced Rule Engine
|
return new StepResponse({ priceSet }, priceSet.id)
|
||||||
|
|
||||||
Create prices with custom rules. This gives you more flexibility in how you condition prices, filter them, and ensure the best prices are retrieved for custom contexts.
|
|
||||||
|
|
||||||
```ts highlights={[["8"]]}
|
|
||||||
const priceSet = await pricingModuleService.addPrices({
|
|
||||||
priceSetId: "pset_123",
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 500,
|
|
||||||
currency_code: "EUR",
|
|
||||||
rules: {
|
|
||||||
region_id: "reg_123",
|
|
||||||
},
|
},
|
||||||
},
|
async (priceSetId, { container }) => {
|
||||||
],
|
if (!priceSetId) {
|
||||||
})
|
return
|
||||||
```
|
}
|
||||||
|
const pricingModuleService = container.resolve(Modules.PRICING)
|
||||||
|
|
||||||
### Price Lists
|
await pricingModuleService.deletePriceSets([priceSetId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Group prices and apply them only in specific conditions with price lists.
|
export const createPriceSetWorkflow = createWorkflow(
|
||||||
|
"create-price-set",
|
||||||
|
() => {
|
||||||
|
const { priceSet } = createPriceSetStep()
|
||||||
|
|
||||||
You can also use them to override existing prices for specified conditions, or create a sale.
|
return new WorkflowResponse({
|
||||||
|
priceSet,
|
||||||
```ts
|
})
|
||||||
const priceList = await pricingModuleService.createPriceLists([
|
|
||||||
{
|
|
||||||
title: "My Sale",
|
|
||||||
description: "Sale on selected items.",
|
|
||||||
type: "sale",
|
|
||||||
starts_at: Date.parse("01/10/2023").toString(),
|
|
||||||
ends_at: Date.parse("31/10/2023").toString(),
|
|
||||||
rules: {
|
|
||||||
region_id: ["reg_123", "reg_321"],
|
|
||||||
},
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
amount: 400,
|
|
||||||
currency_code: "EUR",
|
|
||||||
price_set_id: "pset_123",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
### Price Calculation Strategy
|
|
||||||
|
|
||||||
Retrieve the best price in a given context and for the specified rule values.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const price = await pricingModuleService.calculatePrices(
|
|
||||||
{ id: ["pset_123"] },
|
|
||||||
{
|
|
||||||
context: {
|
|
||||||
currency_code: "EUR",
|
|
||||||
region_id: "reg_123",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import type {
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createPriceSetWorkflow } from "../../workflows/create-price-set"
|
||||||
|
|
||||||
|
export async function GET(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) {
|
||||||
|
const { result } = await createPriceSetWorkflow(req.scope)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
res.send(result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import {
|
||||||
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createPriceSetWorkflow } from "../workflows/create-price-set"
|
||||||
|
|
||||||
|
export default async function handleUserCreated({
|
||||||
|
event: { data },
|
||||||
|
container,
|
||||||
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createPriceSetWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createPriceSetWorkflow } from "../workflows/create-price-set"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createPriceSetWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Pricing" />
|
||||||
@@ -1,325 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Product Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Product Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Product Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create Product
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const products = await productModuleService.createProducts([
|
|
||||||
{
|
|
||||||
title: "Medusa Shirt",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
title: "Color",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
title: "Black Shirt",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
value: "Black",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
res.json({ products })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeProductModule } from "@medusajs/medusa/product"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const productModuleService = await initializeProductModule()
|
|
||||||
|
|
||||||
const products = await productModuleService.createProducts([
|
|
||||||
{
|
|
||||||
title: "Medusa Shirt",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
title: "Color",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
variants: [
|
|
||||||
{
|
|
||||||
title: "Black Shirt",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
value: "Black",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
return NextResponse.json({ products })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Products
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const products = await productModuleService.listProducts()
|
|
||||||
|
|
||||||
res.json({ products })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeProductModule } from "@medusajs/medusa/product"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const productModuleService = await initializeProductModule()
|
|
||||||
|
|
||||||
const products = await productModuleService.listProducts()
|
|
||||||
|
|
||||||
return NextResponse.json({ products })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Product by its ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const product = await productModuleService.retrieveProduct(request.params.id)
|
|
||||||
|
|
||||||
res.json({ product })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeProductModule } from "@medusajs/medusa/product"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Record<string, any> }
|
|
||||||
) {
|
|
||||||
const { id } = params
|
|
||||||
const productModuleService = await initializeProductModule()
|
|
||||||
|
|
||||||
const product = await productModuleService.retrieveProduct(id)
|
|
||||||
|
|
||||||
return NextResponse.json({ product })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Product by its Handle
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const data = await productModuleService.listProducts({
|
|
||||||
handle: "shirt",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ product: data[0] })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeProductModule } from "@medusajs/medusa/product"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const productModuleService = await initializeProductModule()
|
|
||||||
|
|
||||||
const data = await productModuleService.listProducts({
|
|
||||||
handle: "shirt",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ product: data[0] })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve Categories
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const categories = await productModuleService.listProductCategories()
|
|
||||||
|
|
||||||
res.json({ categories })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeProductModule } from "@medusajs/medusa/product"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const productModuleService = await initializeProductModule()
|
|
||||||
|
|
||||||
const categories = await productModuleService.listProductCategories()
|
|
||||||
|
|
||||||
return NextResponse.json({ categories })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve Category by Handle
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab value="medusa" label="Medusa API Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const data = await productModuleService.listProductCategories({
|
|
||||||
handle: request.params.handle,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ category: data[0] })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab value="nextjs" label="Next.js App Router">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeProductModule } from "@medusajs/medusa/product"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: Request,
|
|
||||||
{ params }: { params: Record<string, any> }
|
|
||||||
) {
|
|
||||||
const { handle } = params
|
|
||||||
const productModuleService = await initializeProductModule()
|
|
||||||
|
|
||||||
const data = await productModuleService.listProductCategories({
|
|
||||||
handle,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ category: data[0] })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Product Module's main service reference](/references/product) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -5,6 +5,7 @@ tags:
|
|||||||
- pricing
|
- pricing
|
||||||
- query
|
- query
|
||||||
- tax
|
- tax
|
||||||
|
- server
|
||||||
---
|
---
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ tags:
|
|||||||
- product
|
- product
|
||||||
- pricing
|
- pricing
|
||||||
- query
|
- query
|
||||||
|
- server
|
||||||
---
|
---
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ To retrieve the line items of a product, pass `line_items.*` in `fields`.
|
|||||||
const { data: variants } = await query.graph({
|
const { data: variants } = await query.graph({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"line_items.*"
|
"line_items.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.line_items
|
// variants.line_items
|
||||||
@@ -68,8 +68,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: variants } = useQueryGraphStep({
|
const { data: variants } = useQueryGraphStep({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"line_items.*"
|
"line_items.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.line_items
|
// variants.line_items
|
||||||
@@ -101,8 +101,8 @@ To retrieve the inventory items of a product variant with [Query](!docs!/learn/f
|
|||||||
const { data: variants } = await query.graph({
|
const { data: variants } = await query.graph({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"inventory_items.*"
|
"inventory_items.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.inventory_items
|
// variants.inventory_items
|
||||||
@@ -119,8 +119,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: variants } = useQueryGraphStep({
|
const { data: variants } = useQueryGraphStep({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"inventory_items.*"
|
"inventory_items.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.inventory_items
|
// variants.inventory_items
|
||||||
@@ -199,8 +199,8 @@ To retrieve a product's order line items, pass `order_items.*` in `fields`.
|
|||||||
const { data: variants } = await query.graph({
|
const { data: variants } = await query.graph({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"order_items.*"
|
"order_items.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.order_items
|
// variants.order_items
|
||||||
@@ -217,8 +217,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: variants } = useQueryGraphStep({
|
const { data: variants } = useQueryGraphStep({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"order_items.*"
|
"order_items.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.order_items
|
// variants.order_items
|
||||||
@@ -250,8 +250,8 @@ To retrieve the price set of a variant with [Query](!docs!/learn/fundamentals/mo
|
|||||||
const { data: variants } = await query.graph({
|
const { data: variants } = await query.graph({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"price_set.*"
|
"price_set.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.price_set
|
// variants.price_set
|
||||||
@@ -268,8 +268,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: variants } = useQueryGraphStep({
|
const { data: variants } = useQueryGraphStep({
|
||||||
entity: "variant",
|
entity: "variant",
|
||||||
fields: [
|
fields: [
|
||||||
"price_set.*"
|
"price_set.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// variants.price_set
|
// variants.price_set
|
||||||
@@ -343,8 +343,8 @@ To retrieve the sales channels of a product with [Query](!docs!/learn/fundamenta
|
|||||||
const { data: products } = await query.graph({
|
const { data: products } = await query.graph({
|
||||||
entity: "product",
|
entity: "product",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channels.*"
|
"sales_channels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// products.sales_channels
|
// products.sales_channels
|
||||||
@@ -361,8 +361,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: products } = useQueryGraphStep({
|
const { data: products } = useQueryGraphStep({
|
||||||
entity: "product",
|
entity: "product",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channels.*"
|
"sales_channels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// products.sales_channels
|
// products.sales_channels
|
||||||
|
|||||||
@@ -6,78 +6,50 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Product Module provides product-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Product Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Product Module's Service
|
Medusa has product related features available out-of-the-box through the Product Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Product Module.
|
||||||
|
|
||||||
You can use the Product Module's main service by resolving from the Medusa container the resource `Modules.PRODUCT`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
## Product Features
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
- [Products Management](/references/product/models/Product): Store and manage products. Products have custom options, such as color or size, and each variant in the product sets the value for these options.
|
||||||
const productModuleService = container.resolve(
|
- [Product Organization](/references/product/models): The Product Module provides different data models used to organize products, including categories, collections, tags, and more.
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const products = await productModuleService.listProducts()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(request: MedusaRequest, res: MedusaResponse) {
|
|
||||||
const productModuleService = request.scope.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
products: await productModuleService.listProducts(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const productModuleService = container.resolve(
|
|
||||||
Modules.PRODUCT
|
|
||||||
)
|
|
||||||
|
|
||||||
const products = await productModuleService.listProducts()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## How to Use the Product Module
|
||||||
|
|
||||||
### Products Management
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
Store and manage products. Products have custom options, such as color or size, and each variant in the product sets the value for these options.
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
```ts
|
For example:
|
||||||
const products = await productService.createProducts([
|
|
||||||
{
|
export const highlights = [
|
||||||
|
["12", "Modules.PRODUCT", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/create-product.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const createProductStep = createStep(
|
||||||
|
"create-product",
|
||||||
|
async ({}, { container }) => {
|
||||||
|
const productService = container.resolve(Modules.PRODUCT)
|
||||||
|
|
||||||
|
const product = await productService.createProducts({
|
||||||
title: "Medusa Shirt",
|
title: "Medusa Shirt",
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
@@ -93,27 +65,107 @@ const products = await productService.createProducts([
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ product }, product.id)
|
||||||
},
|
},
|
||||||
])
|
async (productId, { container }) => {
|
||||||
|
if (!productId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const productService = container.resolve(Modules.PRODUCT)
|
||||||
|
|
||||||
|
await productService.deleteProducts([productId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createProductWorkflow = createWorkflow(
|
||||||
|
"create-product",
|
||||||
|
() => {
|
||||||
|
const { product } = createProductStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
product,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Product Organization
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
The Product Module provides different data models used to organize products, including categories, collections, tags, and more.
|
<CodeTabs group="resource-types">
|
||||||
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
const category = await productService.createProductCategories({
|
import type {
|
||||||
name: "Shirts",
|
MedusaRequest,
|
||||||
})
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createProductWorkflow } from "../../workflows/create-product"
|
||||||
|
|
||||||
const products = await productService.updateProducts([
|
export async function GET(
|
||||||
{
|
req: MedusaRequest,
|
||||||
id: product.id,
|
res: MedusaResponse
|
||||||
categories: [
|
) {
|
||||||
{
|
const { result } = await createProductWorkflow(req.scope)
|
||||||
id: category.id,
|
.run()
|
||||||
},
|
|
||||||
],
|
res.send(result)
|
||||||
},
|
}
|
||||||
])
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import {
|
||||||
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createProductWorkflow } from "../workflows/create-product"
|
||||||
|
|
||||||
|
export default async function handleUserCreated({
|
||||||
|
event: { data },
|
||||||
|
container,
|
||||||
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createProductWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createProductWorkflow } from "../workflows/create-product"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createProductWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Product" />
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Promotion Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this document, you’ll find common examples of how you can use the Promotion Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Promotion Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Promotion
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const promotionModuleService = request.scope.resolve(
|
|
||||||
Modules.PROMOTION
|
|
||||||
)
|
|
||||||
|
|
||||||
const promotion = await promotionModuleService.createPromotions({
|
|
||||||
code: "10%OFF",
|
|
||||||
type: "standard",
|
|
||||||
application_method: {
|
|
||||||
type: "percentage",
|
|
||||||
target_type: "order",
|
|
||||||
value: "10",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ promotion })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePromotionModule } from "@medusajs/medusa/promotion"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const promotionModuleService = await initializePromotionModule()
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const promotion = await promotionModuleService.createPromotions({
|
|
||||||
code: "10%OFF",
|
|
||||||
type: "standard",
|
|
||||||
application_method: {
|
|
||||||
type: "percentage",
|
|
||||||
target_type: "order",
|
|
||||||
value: "10",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ promotion })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create a Campaign
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const promotionModuleService = request.scope.resolve(
|
|
||||||
Modules.PROMOTION
|
|
||||||
)
|
|
||||||
|
|
||||||
const campaign = await promotionModuleService.createCampaigns({
|
|
||||||
name: "Summer Discounts",
|
|
||||||
campaign_identifier: "G-123445",
|
|
||||||
starts_at: new Date("2024-05-02"),
|
|
||||||
ends_at: new Date("2024-07-20"),
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ campaign })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePromotionModule } from "@medusajs/medusa/promotion"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const promotionModuleService = await initializePromotionModule()
|
|
||||||
const body = await request.json()
|
|
||||||
|
|
||||||
const campaign = await promotionModuleService.createCampaigns({
|
|
||||||
name: "Summer Discounts",
|
|
||||||
campaign_identifier: "G-123445",
|
|
||||||
starts_at: new Date("2024-05-02"),
|
|
||||||
ends_at: new Date("2024-07-20"),
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ campaign })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create a Promotion with Flexible Rules
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const promotionModuleService = request.scope.resolve(
|
|
||||||
Modules.PROMOTION
|
|
||||||
)
|
|
||||||
|
|
||||||
const promotion = await promotionModuleService.createPromotions({
|
|
||||||
code: "10%OFF",
|
|
||||||
type: "standard",
|
|
||||||
application_method: {
|
|
||||||
type: "percentage",
|
|
||||||
target_type: "order",
|
|
||||||
value: "10",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
attribute: "customer_group_id",
|
|
||||||
operator: "eq",
|
|
||||||
values: ["VIP"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ promotion })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePromotionModule } from "@medusajs/medusa/promotion"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const promotionModuleService = await initializePromotionModule()
|
|
||||||
|
|
||||||
const promotion = await promotionModuleService.createPromotions({
|
|
||||||
code: "10%OFF",
|
|
||||||
type: "standard",
|
|
||||||
application_method: {
|
|
||||||
type: "percentage",
|
|
||||||
target_type: "order",
|
|
||||||
value: "10",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
attribute: "customer_group_id",
|
|
||||||
operator: "eq",
|
|
||||||
values: ["VIP"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ promotion })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Promotions
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const promotionModuleService = request.scope.resolve(
|
|
||||||
Modules.PROMOTION
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
promotions: await promotionModuleService.listPromotions(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializePromotionModule } from "@medusajs/medusa/promotion"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const promotionModuleService = await initializePromotionModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
promotions: await promotionModuleService.listPromotions(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Promotion Module's main service reference](/references/promotion) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -49,8 +49,8 @@ To retrieve the line item adjustments of a promotion, pass `line_item_adjustment
|
|||||||
const { data: promotions } = await query.graph({
|
const { data: promotions } = await query.graph({
|
||||||
entity: "promotion",
|
entity: "promotion",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// promotions.carts
|
// promotions.carts
|
||||||
@@ -67,8 +67,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: promotions } = useQueryGraphStep({
|
const { data: promotions } = useQueryGraphStep({
|
||||||
entity: "promotion",
|
entity: "promotion",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// promotions.carts
|
// promotions.carts
|
||||||
@@ -140,8 +140,8 @@ To retrieve the orders a promotion is applied on with [Query](!docs!/learn/funda
|
|||||||
const { data: promotions } = await query.graph({
|
const { data: promotions } = await query.graph({
|
||||||
entity: "promotion",
|
entity: "promotion",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// promotions.orders
|
// promotions.orders
|
||||||
@@ -158,8 +158,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: promotions } = useQueryGraphStep({
|
const { data: promotions } = useQueryGraphStep({
|
||||||
entity: "promotion",
|
entity: "promotion",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// promotions.orders
|
// promotions.orders
|
||||||
|
|||||||
@@ -6,130 +6,161 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Promotion Module provides promotion-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Promotion Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use the Promotion Module's Service
|
Medusa has promotion related features available out-of-the-box through the Promotion Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Promotion Module.
|
||||||
|
|
||||||
You can use the Promotion Module's main service by resolving from the Medusa container the resource `Modules.PROMOTION`.
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Promotion Features
|
||||||
|
|
||||||
|
- [Discount Functionalities](./concepts/page.mdx): A promotion discounts an amount or percentage of a cart's items, shipping methods, or the entire order.
|
||||||
|
- [Flexible Promotion Rules](./concepts/page.mdx#flexible-rules): A promotion has rules that restricts when the promotion is applied.
|
||||||
|
- [Campaign Management](./campaign/page.mdx): A campaign combines promotions under the same conditions, such as start and end dates, and budget configurations.
|
||||||
|
- [Apply Promotion on Carts and Orders](./actions/page.mdx): Apply promotions on carts and orders to discount items, shipping methods, or the entire order.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How to Use the Promotion Module
|
||||||
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.PROMOTION", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-promotion.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createPromotionStep = createStep(
|
||||||
const promotionModuleService = container.resolve(
|
"create-promotion",
|
||||||
Modules.PROMOTION
|
async ({}, { container }) => {
|
||||||
)
|
const promotionModuleService = container.resolve(Modules.PROMOTION)
|
||||||
|
|
||||||
const promotions = await promotionModuleService.listPromotions()
|
const promotion = await promotionModuleService.createPromotions({
|
||||||
})
|
code: "10%OFF",
|
||||||
|
type: "standard",
|
||||||
|
application_method: {
|
||||||
|
type: "percentage",
|
||||||
|
target_type: "order",
|
||||||
|
value: 10,
|
||||||
|
currency_code: "usd",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ promotion }, promotion.id)
|
||||||
|
},
|
||||||
|
async (promotionId, { container }) => {
|
||||||
|
if (!promotionId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const promotionModuleService = container.resolve(Modules.PROMOTION)
|
||||||
|
|
||||||
|
await promotionModuleService.deletePromotions(promotionId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createPromotionWorkflow = createWorkflow(
|
||||||
|
"create-promotion",
|
||||||
|
() => {
|
||||||
|
const { promotion } = createPromotionStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
promotion,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createPromotionWorkflow } from "../../workflows/create-cart"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const promotionModuleService = request.scope.resolve(
|
const { result } = await createPromotionWorkflow(req.scope)
|
||||||
Modules.PROMOTION
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
promotions: await promotionModuleService.listPromotions(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createPromotionWorkflow } from "../workflows/create-cart"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const promotionModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.PROMOTION
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createPromotionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const promotions = await promotionModuleService.listPromotions()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createPromotionWorkflow } from "../workflows/create-cart"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createPromotionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Promotion" />
|
||||||
|
|
||||||
### Discount Functionalities
|
|
||||||
|
|
||||||
A promotion discounts an amount or percentage of a cart's items, shipping methods, or the entire order.
|
|
||||||
|
|
||||||
The Promotion Module allows you to store and manage promotions.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const promotion = await promotionModuleService.createPromotions({
|
|
||||||
code: "10%OFF",
|
|
||||||
type: "standard",
|
|
||||||
application_method: {
|
|
||||||
type: "percentage",
|
|
||||||
target_type: "order",
|
|
||||||
value: "10",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Flexible Promotion Rules
|
|
||||||
|
|
||||||
A promotion has rules that restricts when it's applied.
|
|
||||||
|
|
||||||
For example, you can create a promotion that's only applied to VIP customers.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const promotion = await promotionModuleService.createPromotions({
|
|
||||||
code: "10%OFF",
|
|
||||||
type: "standard",
|
|
||||||
application_method: {
|
|
||||||
type: "percentage",
|
|
||||||
target_type: "order",
|
|
||||||
value: "10",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
attribute: "customer_group_id",
|
|
||||||
operator: "eq",
|
|
||||||
values: ["VIP"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Campaign Management
|
|
||||||
|
|
||||||
A campaign combines promotions under the same conditions, such as start and end dates.
|
|
||||||
|
|
||||||
A campaign can also have an identifier for tracking purposes, such as tracking through tools like Google Analytics.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const campaign = await promotionModuleService.createCampaigns({
|
|
||||||
name: "Summer Discounts",
|
|
||||||
campaign_identifier: "G-123445",
|
|
||||||
starts_at: new Date("2024-05-02"),
|
|
||||||
ends_at: new Date("2024-07-20"),
|
|
||||||
})
|
|
||||||
```
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Region Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Region Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Region Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Region
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const regionModuleService = req.scope.resolve(
|
|
||||||
Modules.REGION
|
|
||||||
)
|
|
||||||
|
|
||||||
const region = await regionModuleService.createRegions({
|
|
||||||
name: "Europe",
|
|
||||||
currency_code: "eur",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
region,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeRegionModule } from "@medusajs/medusa/region"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const regionModuleService = await initializeRegionModule()
|
|
||||||
|
|
||||||
const region = await regionModuleService.createRegions({
|
|
||||||
name: "Europe",
|
|
||||||
currency_code: "eur",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
region,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Regions
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const regionModuleService = req.scope.resolve(
|
|
||||||
Modules.REGION
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
regions: await regionModuleService.listRegions(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeRegionModule } from "@medusajs/medusa/region"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const regionModuleService = await initializeRegionModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
regions: await regionModuleService.listRegions(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Region by its ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const regionModuleService = req.scope.resolve(
|
|
||||||
Modules.REGION
|
|
||||||
)
|
|
||||||
|
|
||||||
const region = await regionModuleService.retrieveRegion("reg_123")
|
|
||||||
|
|
||||||
res.json({ region })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeRegionModule } from "@medusajs/medusa/region"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const regionModuleService = await initializeRegionModule()
|
|
||||||
|
|
||||||
const region = await regionModuleService.retrieveRegion("reg_123")
|
|
||||||
|
|
||||||
return NextResponse.json({ region })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Update a Region
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const regionModuleService = req.scope.resolve(
|
|
||||||
Modules.REGION
|
|
||||||
)
|
|
||||||
|
|
||||||
const region = await regionModuleService.updateRegions("reg_123", {
|
|
||||||
automatic_taxes: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ region })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeRegionModule } from "@medusajs/medusa/region"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const regionModuleService = await initializeRegionModule()
|
|
||||||
|
|
||||||
const region = await regionModuleService.updateRegions("reg_123", {
|
|
||||||
automatic_taxes: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ region })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete a Region
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const regionModuleService = req.scope.resolve(
|
|
||||||
Modules.REGION
|
|
||||||
)
|
|
||||||
|
|
||||||
await regionModuleService.deleteRegions("reg_123")
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeRegionModule } from "@medusajs/medusa/region"
|
|
||||||
|
|
||||||
export async function DELETE(request: Request) {
|
|
||||||
const regionModuleService = await initializeRegionModule()
|
|
||||||
|
|
||||||
await regionModuleService.deleteRegions("reg_123")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Region Module's main service reference](/references/region) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -39,8 +39,8 @@ To retrieve the carts of a region with [Query](!docs!/learn/fundamentals/module-
|
|||||||
const { data: regions } = await query.graph({
|
const { data: regions } = await query.graph({
|
||||||
entity: "region",
|
entity: "region",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// regions.carts
|
// regions.carts
|
||||||
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: regions } = useQueryGraphStep({
|
const { data: regions } = useQueryGraphStep({
|
||||||
entity: "region",
|
entity: "region",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// regions.carts
|
// regions.carts
|
||||||
@@ -84,8 +84,8 @@ To retrieve the orders of a region with [Query](!docs!/learn/fundamentals/module
|
|||||||
const { data: regions } = await query.graph({
|
const { data: regions } = await query.graph({
|
||||||
entity: "region",
|
entity: "region",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// regions.orders
|
// regions.orders
|
||||||
@@ -102,8 +102,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: regions } = useQueryGraphStep({
|
const { data: regions } = useQueryGraphStep({
|
||||||
entity: "region",
|
entity: "region",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// regions.orders
|
// regions.orders
|
||||||
@@ -133,8 +133,8 @@ To retrieve the payment providers of a region with [Query](!docs!/learn/fundamen
|
|||||||
const { data: regions } = await query.graph({
|
const { data: regions } = await query.graph({
|
||||||
entity: "region",
|
entity: "region",
|
||||||
fields: [
|
fields: [
|
||||||
"payment_providers.*"
|
"payment_providers.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// regions.payment_providers
|
// regions.payment_providers
|
||||||
@@ -151,8 +151,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: regions } = useQueryGraphStep({
|
const { data: regions } = useQueryGraphStep({
|
||||||
entity: "region",
|
entity: "region",
|
||||||
fields: [
|
fields: [
|
||||||
"payment_providers.*"
|
"payment_providers.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// regions.payment_providers
|
// regions.payment_providers
|
||||||
|
|||||||
@@ -6,125 +6,156 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Region Module provides region-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Region Module and how to use it in your application.
|
||||||
|
|
||||||
|
Medusa has region related features available out-of-the-box through the Region Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Region Module.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Region Features
|
||||||
|
|
||||||
|
- [Region Management](/references/region/models/Region): Manage regions in your store. You can create regions with different currencies and settings.
|
||||||
|
- [Multi-Currency Support](/references/region/models/Region): Each region has a currency. You can support multiple currencies in your store by creating multiple regions.
|
||||||
|
- [Different Settings Per Region](/references/region/models/Region): Each region has its own settings, such as what countries belong to a region or its tax settings.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## How to Use Region Module's Service
|
## How to Use Region Module's Service
|
||||||
|
|
||||||
You can use the Region Module's main service by resolving from the Medusa container the resource `Modules.REGION`.
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.REGION", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-region.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createRegionStep = createStep(
|
||||||
const regionModuleService = container.resolve(
|
"create-region",
|
||||||
Modules.REGION
|
async ({}, { container }) => {
|
||||||
)
|
const regionModuleService = container.resolve(Modules.REGION)
|
||||||
|
|
||||||
const regions = await regionModuleService.listRegions()
|
const region = await regionModuleService.createRegions({
|
||||||
})
|
name: "Europe",
|
||||||
|
currency_code: "eur",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ region }, region.id)
|
||||||
|
},
|
||||||
|
async (regionId, { container }) => {
|
||||||
|
if (!regionId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const regionModuleService = container.resolve(Modules.REGION)
|
||||||
|
|
||||||
|
await regionModuleService.deleteRegions([regionId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createRegionWorkflow = createWorkflow(
|
||||||
|
"create-region",
|
||||||
|
() => {
|
||||||
|
const { region } = createRegionStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
region,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createRegionWorkflow } from "../../workflows/create-region"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const regionModuleService = req.scope.resolve(
|
const { result } = await createRegionWorkflow(req.scope)
|
||||||
Modules.REGION
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
regions: await regionModuleService.listRegions(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createRegionWorkflow } from "../workflows/create-region"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const regionModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.REGION
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createRegionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const regions = await regionModuleService.listRegions()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createRegionWorkflow } from "../workflows/create-region"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createRegionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
---
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
## What is a Region?
|
|
||||||
|
|
||||||
A region represents the area you sell products in. Each region can cover multiple countries, but uses a single currency.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Region" />
|
||||||
|
|
||||||
### Region Management
|
|
||||||
|
|
||||||
You can manage your regions to create, update, retrieve, or delete them.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const region = await regionModuleService.createRegions({
|
|
||||||
name: "Europe",
|
|
||||||
currency_code: "eur",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multi-Currency Support
|
|
||||||
|
|
||||||
As each region has a currency, you can support multiple currencies in your store by creating multiple regions.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const regions = await regionModuleService.createRegions([
|
|
||||||
{
|
|
||||||
name: "Europe",
|
|
||||||
currency_code: "eur",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "United States of America",
|
|
||||||
currency_code: "usd",
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
### Different Settings Per Region
|
|
||||||
|
|
||||||
Each region has its own settings, such as what countries belong to a region or its tax settings. Each region has different tax rates, payment providers, and more provided by other commerce modules.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const regions = await regionModuleService.createRegions([
|
|
||||||
{
|
|
||||||
name: "Europe",
|
|
||||||
currency_code: "eur",
|
|
||||||
countries: ["dk", "de", "fr", "it", "pt"],
|
|
||||||
automatic_taxes: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "United States of America",
|
|
||||||
currency_code: "usd",
|
|
||||||
countries: ["us"],
|
|
||||||
payment_providers: ["stripe"],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Sales Channel Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Sales Channel Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Sales Channel Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Sales Channel
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
request.scope.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
const salesChannel = await salesChannelModuleService.createSalesChannels({
|
|
||||||
name: "B2B",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
sales_channel: salesChannel,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeSalesChannelModule } from "@medusajs/medusa/sales-channel"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const salesChannelModuleService = await initializeSalesChannelModule()
|
|
||||||
|
|
||||||
const salesChannel = await salesChannelModuleService.createSalesChannels({
|
|
||||||
name: "B2B",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ sales_channel: salesChannel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Sales Channels
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
request.scope.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
sales_channels: salesChannelModuleService.listSalesChannels(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeSalesChannelModule } from "@medusajs/medusa/sales-channel"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const salesChannelModuleService = await initializeSalesChannelModule()
|
|
||||||
|
|
||||||
const salesChannels = await salesChannelModuleService.listSalesChannels()
|
|
||||||
|
|
||||||
return NextResponse.json({ sales_channels: salesChannels })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Sales Channel by its ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
request.scope.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
const salesChannel = await salesChannelModuleService.retrieveSalesChannel(
|
|
||||||
"sc_123"
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
sales_channel: salesChannel,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeSalesChannelModule } from "@medusajs/medusa/sales-channel"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const salesChannelModuleService = await initializeSalesChannelModule()
|
|
||||||
|
|
||||||
const salesChannel = await salesChannelModuleService.retrieveSalesChannel(
|
|
||||||
"sc_123"
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({ sales_channel: salesChannel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Update a Sales Channel
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
request.scope.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
const salesChannel = await salesChannelModuleService.updateSalesChannels({
|
|
||||||
id: "sc_123",
|
|
||||||
description: "Sales channel for B2B customers",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
sales_channel: salesChannel,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeSalesChannelModule } from "@medusajs/medusa/sales-channel"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const salesChannelModuleService = await initializeSalesChannelModule()
|
|
||||||
|
|
||||||
const salesChannel = await salesChannelModuleService.updateSalesChannels({
|
|
||||||
id: "sc_123",
|
|
||||||
description: "Sales channel for B2B customers",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ sales_channel: salesChannel })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete a Sales Channel
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
request.scope.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
await salesChannelModuleService.deleteSalesChannels("sc_123")
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeSalesChannelModule } from "@medusajs/medusa/sales-channel"
|
|
||||||
|
|
||||||
export async function DELETE(request: Request) {
|
|
||||||
const salesChannelModuleService = await initializeSalesChannelModule()
|
|
||||||
|
|
||||||
await salesChannelModuleService.deleteSalesChannels("sc_123")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Sales Channel Module's main service reference](/references/sales-channel) provides a reference to all the methods available for use with examples for each.
|
|
||||||
+20
-20
@@ -45,8 +45,8 @@ To retrieve the API keys associated with a sales channel with [Query](!docs!/lea
|
|||||||
const { data: salesChannels } = await query.graph({
|
const { data: salesChannels } = await query.graph({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"publishable_api_keys.*"
|
"publishable_api_keys.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.publishable_api_keys
|
// salesChannels.publishable_api_keys
|
||||||
@@ -63,8 +63,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: salesChannels } = useQueryGraphStep({
|
const { data: salesChannels } = useQueryGraphStep({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"publishable_api_keys.*"
|
"publishable_api_keys.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.publishable_api_keys
|
// salesChannels.publishable_api_keys
|
||||||
@@ -134,8 +134,8 @@ To retrieve the carts of a sales channel with [Query](!docs!/learn/fundamentals/
|
|||||||
const { data: salesChannels } = await query.graph({
|
const { data: salesChannels } = await query.graph({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.carts
|
// salesChannels.carts
|
||||||
@@ -152,8 +152,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: salesChannels } = useQueryGraphStep({
|
const { data: salesChannels } = useQueryGraphStep({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"carts.*"
|
"carts.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.carts
|
// salesChannels.carts
|
||||||
@@ -179,8 +179,8 @@ To retrieve the orders of a sales channel with [Query](!docs!/learn/fundamentals
|
|||||||
const { data: salesChannels } = await query.graph({
|
const { data: salesChannels } = await query.graph({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.orders
|
// salesChannels.orders
|
||||||
@@ -197,8 +197,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: salesChannels } = useQueryGraphStep({
|
const { data: salesChannels } = useQueryGraphStep({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"orders.*"
|
"orders.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.orders
|
// salesChannels.orders
|
||||||
@@ -228,8 +228,8 @@ To retrieve the products of a sales channel with [Query](!docs!/learn/fundamenta
|
|||||||
const { data: salesChannels } = await query.graph({
|
const { data: salesChannels } = await query.graph({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"products.*"
|
"products.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.products
|
// salesChannels.products
|
||||||
@@ -246,8 +246,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: salesChannels } = useQueryGraphStep({
|
const { data: salesChannels } = useQueryGraphStep({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"products.*"
|
"products.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.products
|
// salesChannels.products
|
||||||
@@ -321,8 +321,8 @@ To retrieve the stock locations of a sales channel with [Query](!docs!/learn/fun
|
|||||||
const { data: salesChannels } = await query.graph({
|
const { data: salesChannels } = await query.graph({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"stock_locations.*"
|
"stock_locations.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.stock_locations
|
// salesChannels.stock_locations
|
||||||
@@ -339,8 +339,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: salesChannels } = useQueryGraphStep({
|
const { data: salesChannels } = useQueryGraphStep({
|
||||||
entity: "sales_channel",
|
entity: "sales_channel",
|
||||||
fields: [
|
fields: [
|
||||||
"stock_locations.*"
|
"stock_locations.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// salesChannels.stock_locations
|
// salesChannels.stock_locations
|
||||||
|
|||||||
@@ -6,68 +6,15 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Sales Channel Module provides sales-channel-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Sales Channel Module and how to use it in your application.
|
||||||
|
|
||||||
## How to Use Sales Channel Module's Service
|
Medusa has sales channel related features available out-of-the-box through the Sales Channel Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Sales Channel Module.
|
||||||
|
|
||||||
You can use the Sales Channel Module's main service by resolving from the Medusa container the resource `Modules.SALES_CHANNEL`.
|
<Note>
|
||||||
|
|
||||||
For example:
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
</Note>
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
container.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
const salesChannels = await salesChannelModuleService.listSalesChannels()
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="API Route" value="api-route">
|
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
request.scope.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
sales_channels: await salesChannelModuleService.listSalesChannels(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
|
||||||
const salesChannelModuleService =
|
|
||||||
container.resolve(Modules.SALES_CHANNEL)
|
|
||||||
|
|
||||||
const salesChannels = await salesChannelModuleService.listSalesChannels()
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## What's a Sales Channel?
|
## What's a Sales Channel?
|
||||||
|
|
||||||
@@ -81,31 +28,151 @@ Some use case examples for using a sales channel:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
## Sales Channel Features
|
||||||
|
|
||||||
### Sales Channel Management
|
- [Sales Channel Management](/references/sales-channel/models/SalesChannel): Manage sales channels in your store. Each sales channel has different meta information such as name or description, allowing you to easily differentiate between sales channels.
|
||||||
|
- [Product Availability](./links-to-other-modules/page.mdx): Medusa uses the Product and Sales Channel modules to allow merchants to specify a product's availability per sales channel.
|
||||||
|
- [Cart and Order Scoping](./links-to-other-modules/page.mdx): Carts, available through the Cart Module, are scoped to a sales channel. Paired with the product availability feature, you benefit from more features like allowing only products available in sales channel in a cart.
|
||||||
|
- [Inventory Availability Per Sales Channel](./links-to-other-modules/page.mdx): Medusa links sales channels to stock locations, allowing you to retrieve available inventory of products based on the specified sales channel.
|
||||||
|
|
||||||
Manage sales channels in your store. Each sales channel has different meta information such as name or description, allowing you to easily differentiate between sales channels.
|
---
|
||||||
|
|
||||||
```ts
|
## How to Use Sales Channel Module's Service
|
||||||
const salesChannels = await salesChannelModuleService.createSalesChannels([
|
|
||||||
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
export const highlights = [
|
||||||
|
["12", "Modules.SALES_CHANNEL", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
|
```ts title="src/workflows/create-sales-channel.ts" highlights={highlights}
|
||||||
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
|
const createSalesChannelStep = createStep(
|
||||||
|
"create-sales-channel",
|
||||||
|
async ({}, { container }) => {
|
||||||
|
const salesChannelModuleService = container.resolve(Modules.SALES_CHANNEL)
|
||||||
|
|
||||||
|
const salesChannels = await salesChannelModuleService.createSalesChannels([
|
||||||
{
|
{
|
||||||
name: "B2B",
|
name: "B2B",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Mobile App",
|
name: "Mobile App",
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
|
return new StepResponse({ salesChannels }, salesChannels.map((sc) => sc.id))
|
||||||
|
},
|
||||||
|
async (salesChannelIds, { container }) => {
|
||||||
|
if (!salesChannelIds) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const salesChannelModuleService = container.resolve(Modules.SALES_CHANNEL)
|
||||||
|
|
||||||
|
await salesChannelModuleService.deleteSalesChannels(
|
||||||
|
salesChannelIds
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createSalesChannelWorkflow = createWorkflow(
|
||||||
|
"create-sales-channel",
|
||||||
|
() => {
|
||||||
|
const { salesChannels } = createSalesChannelStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
salesChannels,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Product Availability
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
Medusa uses the Product and Sales Channel modules to allow merchants to specify a product's availability per sales channel.
|
<CodeTabs group="resource-types">
|
||||||
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
For example, B2B customers viewing products only see products in the B2B sales channel.
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import type {
|
||||||
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createSalesChannelWorkflow } from "../../workflows/create-sales-channel"
|
||||||
|
|
||||||
### Cart and Order Scoping
|
export async function GET(
|
||||||
|
req: MedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) {
|
||||||
|
const { result } = await createSalesChannelWorkflow(req.scope)
|
||||||
|
.run()
|
||||||
|
|
||||||
Carts, available through the Cart Module, are scoped to a sales channel. Paired with the product availability feature, you benefit from more features like allowing only products available in sales channel in a cart.
|
res.send(result)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Orders are also scoped to a sales channel due to the relation between the Sales Channel and Order modules.
|
</CodeTab>
|
||||||
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
|
import {
|
||||||
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createSalesChannelWorkflow } from "../workflows/create-sales-channel"
|
||||||
|
|
||||||
|
export default async function handleUserCreated({
|
||||||
|
event: { data },
|
||||||
|
container,
|
||||||
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createSalesChannelWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createSalesChannelWorkflow } from "../workflows/create-sales-channel"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createSalesChannelWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Sales Channel" />
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Stock Location Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this document, you’ll find common examples of how you can use the Stock Location Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Stock Location Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Stock Location
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const stockLocationModuleService =
|
|
||||||
request.scope.resolve(Modules.STOCK_LOCATION)
|
|
||||||
|
|
||||||
const stockLocation = await stockLocationModuleService.createStockLocations({
|
|
||||||
name: "Warehouse 1",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
stock_location: stockLocation,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStockLocationModule } from "@medusajs/medusa/stock-location"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const stockLocationModuleService = await initializeStockLocationModule({})
|
|
||||||
|
|
||||||
const stockLocation = await stockLocationModuleService.createStockLocations({
|
|
||||||
name: "Warehouse 1",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ stock_location: stockLocation })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Stock Locations
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const stockLocationModuleService =
|
|
||||||
request.scope.resolve(Modules.STOCK_LOCATION)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
stock_locations: await stockLocationModuleService.listStockLocations({}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStockLocationModule } from "@medusajs/medusa/stock-location"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const stockLocationModuleService = await initializeStockLocationModule({})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
stock_locations: await stockLocationModuleService.listStockLocations({}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Add Address to Stock Location
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const stockLocationModuleService =
|
|
||||||
request.scope.resolve(Modules.STOCK_LOCATION)
|
|
||||||
|
|
||||||
const stockLocation = await stockLocationModuleService.updateStockLocations({
|
|
||||||
id: "sloc_123",
|
|
||||||
address: {
|
|
||||||
country_code: "US",
|
|
||||||
city: "New York City",
|
|
||||||
address_1: "52 Stone St",
|
|
||||||
postal_code: "10004",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
stock_location: stockLocation,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStockLocationModule } from "@medusajs/medusa/stock-location"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const stockLocationModuleService = await initializeStockLocationModule({})
|
|
||||||
|
|
||||||
const stockLocation = await stockLocationModuleService.updateStockLocations({
|
|
||||||
id: "sloc_123",
|
|
||||||
address: {
|
|
||||||
country_code: "us",
|
|
||||||
city: "New York City",
|
|
||||||
address_1: "52 Stone St",
|
|
||||||
postal_code: "10004",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
stock_location: stockLocation,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete a Stock Location
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const stockLocationModuleService =
|
|
||||||
request.scope.resolve(Modules.STOCK_LOCATION)
|
|
||||||
|
|
||||||
await stockLocationModuleService.deleteStockLocations("sloc_123")
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStockLocationModule } from "@medusajs/medusa/stock-location"
|
|
||||||
|
|
||||||
export async function DELETE(request: Request) {
|
|
||||||
const stockLocationModuleService = await initializeStockLocationModule({})
|
|
||||||
|
|
||||||
await stockLocationModuleService.deleteStockLocations("sloc_123")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Stock Location Module's main service reference](/references/stock-location) provides a reference to all the methods available for use with examples for each.
|
|
||||||
+12
-12
@@ -54,8 +54,8 @@ To retrieve the fulfillment providers, pass `fulfillment_providers.*` in `fields
|
|||||||
const { data: stockLocations } = await query.graph({
|
const { data: stockLocations } = await query.graph({
|
||||||
entity: "stock_location",
|
entity: "stock_location",
|
||||||
fields: [
|
fields: [
|
||||||
"fulfillment_sets.*"
|
"fulfillment_sets.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stockLocations.fulfillment_sets
|
// stockLocations.fulfillment_sets
|
||||||
@@ -72,8 +72,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: stockLocations } = useQueryGraphStep({
|
const { data: stockLocations } = useQueryGraphStep({
|
||||||
entity: "stock_location",
|
entity: "stock_location",
|
||||||
fields: [
|
fields: [
|
||||||
"fulfillment_sets.*"
|
"fulfillment_sets.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stockLocations.fulfillment_sets
|
// stockLocations.fulfillment_sets
|
||||||
@@ -143,8 +143,8 @@ To retrieve the inventory levels of a stock location with [Query](!docs!/learn/f
|
|||||||
const { data: stockLocations } = await query.graph({
|
const { data: stockLocations } = await query.graph({
|
||||||
entity: "stock_location",
|
entity: "stock_location",
|
||||||
fields: [
|
fields: [
|
||||||
"inventory_levels.*"
|
"inventory_levels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stockLocations.inventory_levels
|
// stockLocations.inventory_levels
|
||||||
@@ -161,8 +161,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: stockLocations } = useQueryGraphStep({
|
const { data: stockLocations } = useQueryGraphStep({
|
||||||
entity: "stock_location",
|
entity: "stock_location",
|
||||||
fields: [
|
fields: [
|
||||||
"inventory_levels.*"
|
"inventory_levels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stockLocations.inventory_levels
|
// stockLocations.inventory_levels
|
||||||
@@ -192,8 +192,8 @@ To retrieve the sales channels of a stock location with [Query](!docs!/learn/fun
|
|||||||
const { data: stockLocations } = await query.graph({
|
const { data: stockLocations } = await query.graph({
|
||||||
entity: "stock_location",
|
entity: "stock_location",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channels.*"
|
"sales_channels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stockLocations.sales_channels
|
// stockLocations.sales_channels
|
||||||
@@ -210,8 +210,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: stockLocations } = useQueryGraphStep({
|
const { data: stockLocations } = useQueryGraphStep({
|
||||||
entity: "stock_location",
|
entity: "stock_location",
|
||||||
fields: [
|
fields: [
|
||||||
"sales_channels.*"
|
"sales_channels.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stockLocations.sales_channels
|
// stockLocations.sales_channels
|
||||||
|
|||||||
@@ -6,95 +6,150 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Stock Location Module provides stock-location-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Stock Location Module and how to use it in your application.
|
||||||
|
|
||||||
|
Medusa has stock location related features available out-of-the-box through the Stock Location Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Stock Location Module.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Stock Location Features
|
||||||
|
|
||||||
|
- [Stock Location Management](/references/stock-location-next/models): Store and manage stock locations. Medusa links stock locations with data models of other modules that require a location, such as the [Inventory Module's InventoryLevel](./links-to-other-modules/page.mdx).
|
||||||
|
- [Address Management](/references/stock-location-next/models/StockLocationAddress): Manage the address of each stock location.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## How to Use Stock Location Module's Service
|
## How to Use Stock Location Module's Service
|
||||||
|
|
||||||
You can use the Stock Location Module's main service by resolving from the Medusa container the resource `Modules.STOCK_LOCATION`.
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.STOCK_LOCATION", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-stock-location.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createStockLocationStep = createStep(
|
||||||
const stockLocationModuleService = container.resolve(
|
"create-stock-location",
|
||||||
Modules.STOCK_LOCATION
|
async ({}, { container }) => {
|
||||||
)
|
const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
|
||||||
|
|
||||||
const stockLocations = await stockLocationModuleService.listStockLocations({})
|
const stockLocation = await stockLocationModuleService.createStockLocations({
|
||||||
})
|
name: "Warehouse 1",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ stockLocation }, stockLocation.id)
|
||||||
|
},
|
||||||
|
async (stockLocationId, { container }) => {
|
||||||
|
if (!stockLocationId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
|
||||||
|
|
||||||
|
await stockLocationModuleService.deleteStockLocations([stockLocationId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createStockLocationWorkflow = createWorkflow(
|
||||||
|
"create-stock-location",
|
||||||
|
() => {
|
||||||
|
const { stockLocation } = createStockLocationStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({ stockLocation })
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createStockLocationWorkflow } from "../../workflows/create-stock-location"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const stockLocationModuleService =
|
const { result } = await createStockLocationWorkflow(req.scope)
|
||||||
request.scope.resolve(Modules.STOCK_LOCATION)
|
.run()
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
stock_locations: await stockLocationModuleService.listStockLocations({}),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createStockLocationWorkflow } from "../workflows/create-stock-location"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const stockLocationModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.STOCK_LOCATION
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createStockLocationWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const stockLocations = await stockLocationModuleService.listStockLocations({})
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createStockLocationWorkflow } from "../workflows/create-stock-location"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createStockLocationWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Stock Location" />
|
||||||
|
|
||||||
### Stock Location Management
|
|
||||||
|
|
||||||
Store and manage stock locations. Stock locations are associated with data models of other modules that require a location, such as the [Inventory Module's InventoryLevel](../inventory/concepts/page.mdx#inventory-level).
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const stockLocation = await stockLocationModuleService.createStockLocations({
|
|
||||||
name: "Warehouse 1",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Address Management
|
|
||||||
|
|
||||||
Manage the address of each stock location.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const stockLocation = await stockLocationModuleService.updateStockLocations({
|
|
||||||
id: "sloc_123",
|
|
||||||
address: {
|
|
||||||
country_code: "us",
|
|
||||||
city: "New York City",
|
|
||||||
address_1: "52 Stone St",
|
|
||||||
postal_code: "10004",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Store Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Store Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Store Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Store
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const storeModuleService = request.scope.resolve(
|
|
||||||
Modules.STORE
|
|
||||||
)
|
|
||||||
|
|
||||||
const store = await storeModuleService.createStores({
|
|
||||||
name: "My Store",
|
|
||||||
supported_currencies: [
|
|
||||||
{
|
|
||||||
currency_code: "usd",
|
|
||||||
is_default: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStoreModule } from "@medusajs/medusa/store"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const storeModuleService = await initializeStoreModule()
|
|
||||||
|
|
||||||
const store = await storeModuleService.createStores({
|
|
||||||
name: "My Store",
|
|
||||||
supported_currencies: [
|
|
||||||
{
|
|
||||||
currency_code: "usd",
|
|
||||||
is_default: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Stores
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const storeModuleService = request.scope.resolve(
|
|
||||||
Modules.STORE
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
stores: await storeModuleService.listStores(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStoreModule } from "@medusajs/medusa/store"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const storeModuleService = await initializeStoreModule()
|
|
||||||
|
|
||||||
const salesChannels = await storeModuleService.listStores()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
stores: await storeModuleService.list(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Retrieve a Store by its ID
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const storeModuleService = request.scope.resolve(
|
|
||||||
Modules.STORE
|
|
||||||
)
|
|
||||||
|
|
||||||
const store = await storeModuleService.retrieveStore("store_123")
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStoreModule } from "@medusajs/medusa/store"
|
|
||||||
|
|
||||||
export async function GET(request: Request, { params }: ContextType) {
|
|
||||||
const storeModuleService = await initializeStoreModule()
|
|
||||||
|
|
||||||
const store = await storeModuleService.retrieveStore("store_123")
|
|
||||||
|
|
||||||
return NextResponse.json({ store })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Update a Store
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const storeModuleService = request.scope.resolve(
|
|
||||||
Modules.STORE
|
|
||||||
)
|
|
||||||
|
|
||||||
const store = await storeModuleService.updateStores("store_123", {
|
|
||||||
name: "Change Store",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
store,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStoreModule } from "@medusajs/medusa/store"
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const storeModuleService = await initializeStoreModule()
|
|
||||||
|
|
||||||
const store = await storeModuleService.updateStores("store_123", {
|
|
||||||
name: "Change Store",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({ store })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete a Store
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
request: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const storeModuleService = request.scope.resolve(
|
|
||||||
Modules.STORE
|
|
||||||
)
|
|
||||||
|
|
||||||
await storeModuleService.deleteStores("store_123")
|
|
||||||
|
|
||||||
res.status(200)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeStoreModule } from "@medusajs/medusa/store"
|
|
||||||
|
|
||||||
export async function DELETE(request: Request, { params }: ContextType) {
|
|
||||||
const storeModuleService = await initializeStoreModule()
|
|
||||||
|
|
||||||
await storeModuleService.deleteStores("store_123")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Store Module's main service reference](/references/store) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -39,8 +39,8 @@ To retrieve the details of a store's currencies with [Query](!docs!/learn/fundam
|
|||||||
const { data: stores } = await query.graph({
|
const { data: stores } = await query.graph({
|
||||||
entity: "store",
|
entity: "store",
|
||||||
fields: [
|
fields: [
|
||||||
"supported_currencies.currency.*"
|
"supported_currencies.currency.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stores.supported_currencies
|
// stores.supported_currencies
|
||||||
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
|
|||||||
const { data: stores } = useQueryGraphStep({
|
const { data: stores } = useQueryGraphStep({
|
||||||
entity: "store",
|
entity: "store",
|
||||||
fields: [
|
fields: [
|
||||||
"supported_currencies.currency.*"
|
"supported_currencies.currency.*",
|
||||||
]
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// stores.supported_currencies
|
// stores.supported_currencies
|
||||||
|
|||||||
@@ -6,98 +6,154 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Store Module provides store-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Store Module and how to use it in your application.
|
||||||
|
|
||||||
|
Medusa has store related features available out-of-the-box through the Store Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Store Module.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Store Features
|
||||||
|
|
||||||
|
- [Store Management](/references/store/models/Store): Create and manage stores in your application.
|
||||||
|
- [Multi-Tenancy Support](/references/store/models/Store): Create multiple stores, each having its own configurations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## How to Use Store Module's Service
|
## How to Use Store Module's Service
|
||||||
|
|
||||||
You can use the Store Module's main service by resolving from the Medusa container the resource `Modules.STORE`.
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.STORE", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-store.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createStoreStep = createStep(
|
||||||
const storeModuleService: IStoreModuleService = container.resolve(
|
"create-store",
|
||||||
Modules.STORE
|
async ({}, { container }) => {
|
||||||
)
|
const storeModuleService = container.resolve(Modules.STORE)
|
||||||
|
|
||||||
const stores = await storeModuleService.listStores()
|
const store = await storeModuleService.createStores({
|
||||||
})
|
name: "My Store",
|
||||||
|
supported_currencies: [{
|
||||||
|
currency_code: "usd",
|
||||||
|
is_default: true,
|
||||||
|
}],
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ store }, store.id)
|
||||||
|
},
|
||||||
|
async (storeId, { container }) => {
|
||||||
|
if(!storeId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const storeModuleService = container.resolve(Modules.STORE)
|
||||||
|
|
||||||
|
await storeModuleService.deleteStores([storeId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createStoreWorkflow = createWorkflow(
|
||||||
|
"create-store",
|
||||||
|
() => {
|
||||||
|
const { store } = createStoreStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({ store })
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createStoreWorkflow } from "../../workflows/create-store"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
request: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const storeModuleService: IStoreModuleService = request.scope.resolve(
|
const { result } = await createStoreWorkflow(req.scope)
|
||||||
Modules.STORE
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
stores: await storeModuleService.listStores(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createStoreWorkflow } from "../workflows/create-store"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const storeModuleService: IStoreModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.STORE
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createStoreWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const stores = await storeModuleService.listStores()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createStoreWorkflow } from "../workflows/create-store"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createStoreWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Features
|
<CommerceModuleSections name="Store" />
|
||||||
|
|
||||||
### Store Management
|
|
||||||
|
|
||||||
A store holds the main configurations of your commerce store, such as supported currencies, default region and sales channel, and more.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const store = await storeModuleService.createStores({
|
|
||||||
name: "My Store",
|
|
||||||
supported_currency_codes: ["usd"],
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Multi-Tenancy Support
|
|
||||||
|
|
||||||
You can create multiple stores, each having its own configurations.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const stores = await storeModuleService.createStores([
|
|
||||||
{
|
|
||||||
name: "USA Store",
|
|
||||||
supported_currency_codes: ["usd"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Europe Store",
|
|
||||||
supported_currency_codes: ["eur"],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
@@ -1,344 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the Tax Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the Tax Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the Tax Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a Tax Region
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const taxModuleService = req.scope.resolve(
|
|
||||||
Modules.TAX
|
|
||||||
)
|
|
||||||
|
|
||||||
const taxRegion = await taxModuleService.createTaxRegions({
|
|
||||||
country_code: "us",
|
|
||||||
default_tax_rate: {
|
|
||||||
rate: 10,
|
|
||||||
name: "Default rate",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
tax_region: taxRegion,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeTaxModule } from "@medusajs/medusa/tax"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const taxModuleService = await initializeTaxModule()
|
|
||||||
|
|
||||||
const taxRegion = await taxModuleService.createTaxRegions({
|
|
||||||
country_code: "us",
|
|
||||||
default_tax_rate: {
|
|
||||||
rate: 10,
|
|
||||||
name: "Default rate",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
tax_region: taxRegion,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Tax Regions
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const taxModuleService = req.scope.resolve(
|
|
||||||
Modules.TAX
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
tax_regions: await taxModuleService.listTaxRegions(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeTaxModule } from "@medusajs/medusa/tax"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const taxModuleService = await initializeTaxModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
tax_regions: await taxModuleService.listTaxRegions(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Create Tax Rate with Rules
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const taxModuleService = req.scope.resolve(
|
|
||||||
Modules.TAX
|
|
||||||
)
|
|
||||||
|
|
||||||
const taxRate = await taxModuleService.createTaxRates({
|
|
||||||
tax_region_id: "txreg_123",
|
|
||||||
name: "Custom rate",
|
|
||||||
rate: 15,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
reference: "product_type",
|
|
||||||
reference_id: "ptyp_1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
reference: "product",
|
|
||||||
reference_id: "prod_123",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
tax_rate: taxRate,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeTaxModule } from "@medusajs/medusa/tax"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const taxModuleService = await initializeTaxModule()
|
|
||||||
|
|
||||||
const taxRate = await taxModuleService.createTaxRates({
|
|
||||||
tax_region_id: "txreg_123",
|
|
||||||
name: "Custom rate",
|
|
||||||
rate: 15,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
reference: "product_type",
|
|
||||||
reference_id: "ptyp_1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
reference: "product",
|
|
||||||
reference_id: "prod_123",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
tax_rate: taxRate,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Tax Rates
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const taxModuleService = req.scope.resolve(
|
|
||||||
Modules.TAX
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
tax_rates: await taxModuleService.listTaxRates(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeTaxModule } from "@medusajs/medusa/tax"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const taxModuleService = await initializeTaxModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
tax_rates: await taxModuleService.listTaxRates(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Get Tax Lines
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const taxModuleService = req.scope.resolve(
|
|
||||||
Modules.TAX
|
|
||||||
)
|
|
||||||
|
|
||||||
const taxLines = await taxModuleService.getTaxLines(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
id: "cali_123",
|
|
||||||
product_id: "prod_123",
|
|
||||||
unit_price: 1000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "casm_123",
|
|
||||||
shipping_option_id: "so_123",
|
|
||||||
unit_price: 2000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
address: {
|
|
||||||
country_code: "us",
|
|
||||||
postal_code: "123456",
|
|
||||||
},
|
|
||||||
customer: {
|
|
||||||
id: "cus_123",
|
|
||||||
email: "user@example.com",
|
|
||||||
customer_groups: ["VIP"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
tax_lines: taxLines,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeTaxModule } from "@medusajs/medusa/tax"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const taxModuleService = await initializeTaxModule()
|
|
||||||
|
|
||||||
const taxLines = await taxModuleService.getTaxLines(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
id: "cali_123",
|
|
||||||
product_id: "prod_123",
|
|
||||||
unit_price: 1000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "casm_123",
|
|
||||||
shipping_option_id: "so_123",
|
|
||||||
unit_price: 2000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
address: {
|
|
||||||
country_code: "us",
|
|
||||||
postal_code: "123456",
|
|
||||||
},
|
|
||||||
customer: {
|
|
||||||
id: "cus_123",
|
|
||||||
email: "user@example.com",
|
|
||||||
customer_groups: ["VIP"],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
tax_lines: taxLines,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [Tax Module's main service reference](/references/tax) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -6,142 +6,157 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The Tax Module provides tax-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the Tax Module and how to use it in your application.
|
||||||
|
|
||||||
|
Medusa has tax related features available out-of-the-box through the Tax Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this Tax Module.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## Tax Features
|
||||||
|
|
||||||
|
- [Tax Settings Per Region](./tax-region/page.mdx): Set different tax settings for each tax region.
|
||||||
|
- [Tax Rates and Rules](./tax-rates-and-rules/page.mdx): Manage each region's default tax rates and override them with conditioned tax rates.
|
||||||
|
- [Retrieve Tax Lines for carts and orders](./tax-calculation-with-provider/page.mdx): Calculate and retrieve the tax lines of a cart or order's line items and shipping methods with tax providers.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## How to Use Tax Module's Service
|
## How to Use Tax Module's Service
|
||||||
|
|
||||||
You can use the Tax Module's main service by resolving from the Medusa container the resource `Modules.TAX`.
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.TAX", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-tax-region.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createTaxRegionStep = createStep(
|
||||||
const taxModuleService = container.resolve(
|
"create-tax-region",
|
||||||
Modules.TAX
|
async ({}, { container }) => {
|
||||||
)
|
const taxModuleService = container.resolve(Modules.TAX)
|
||||||
|
|
||||||
const taxRegions = await taxModuleService.listTaxRegions()
|
const taxRegion = await taxModuleService.createTaxRegions({
|
||||||
})
|
country_code: "us",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ taxRegion }, taxRegion.id)
|
||||||
|
},
|
||||||
|
async (taxRegionId, { container }) => {
|
||||||
|
if (!taxRegionId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const taxModuleService = container.resolve(Modules.TAX)
|
||||||
|
|
||||||
|
await taxModuleService.deleteTaxRegions([taxRegionId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createTaxRegionWorkflow = createWorkflow(
|
||||||
|
"create-tax-region",
|
||||||
|
() => {
|
||||||
|
const { taxRegion } = createTaxRegionStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({ taxRegion })
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createTaxRegionWorkflow } from "../../workflows/create-tax-region"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const taxModuleService = req.scope.resolve(
|
const { result } = await createTaxRegionWorkflow(req.scope)
|
||||||
Modules.TAX
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
tax_regions: await taxModuleService.listTaxRegions(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createTaxRegionWorkflow } from "../workflows/create-tax-region"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const taxModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.TAX
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createTaxRegionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const taxRegions = await taxModuleService.listTaxRegions()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createTaxRegionWorkflow } from "../workflows/create-tax-region"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createTaxRegionWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
---
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### Tax Settings Per Region
|
|
||||||
|
|
||||||
Set different tax settings for each tax region.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const taxRegion = await taxModuleService.createTaxRegions({
|
|
||||||
country_code: "us",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tax Rates and Rules
|
|
||||||
|
|
||||||
Manage each region's default tax rates and override them with conditioned tax rates.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const taxRates = await taxModuleService.createTaxRates([
|
|
||||||
{
|
|
||||||
tax_region_id: "txreg_123",
|
|
||||||
name: "Default tax rate",
|
|
||||||
is_default: true,
|
|
||||||
rate: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tax_region_id: "txreg_123",
|
|
||||||
name: "Shirt product type",
|
|
||||||
is_default: false,
|
|
||||||
rate: 15,
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
reference: "product_type",
|
|
||||||
reference_id: "ptyp_1",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
```
|
|
||||||
|
|
||||||
### Retrieve Cart's Tax Lines
|
|
||||||
|
|
||||||
Calculate and retrieve the tax lines of a cart's line items and shipping methods with tax providers.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const taxLines = await taxModuleService.getTaxLines(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
id: "cali_123",
|
|
||||||
product_id: "prod_123",
|
|
||||||
unit_price: 1000,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "casm_123",
|
|
||||||
shipping_option_id: "so_123",
|
|
||||||
unit_price: 2000,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{
|
|
||||||
address: {
|
|
||||||
country_code: "us",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
You can use different tax providers for each region to handle tax-line retrieval differently.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Configure Tax Module
|
## Configure Tax Module
|
||||||
|
|
||||||
Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
The Tax Module accepts options for further configurations. Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="Tax" />
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
import { CodeTabs, CodeTab } from "docs-ui"
|
|
||||||
|
|
||||||
export const metadata = {
|
|
||||||
title: `Examples of the User Module`,
|
|
||||||
}
|
|
||||||
|
|
||||||
# {metadata.title}
|
|
||||||
|
|
||||||
In this guide, you’ll find common examples of how you can use the User Module in your application.
|
|
||||||
|
|
||||||
<Note>
|
|
||||||
|
|
||||||
You should only use the User Module's main service when implementing complex customizations. For common cases, check out [available workflows instead](../../../medusa-workflows-reference/page.mdx).
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
## Create a User
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
const user = await userModuleService.createUsers({
|
|
||||||
email: "user@example.com",
|
|
||||||
first_name: "John",
|
|
||||||
last_name: "Smith",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ user })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
const user = await userModuleService.createUsers({
|
|
||||||
email: "user@example.com",
|
|
||||||
first_name: "John",
|
|
||||||
last_name: "Smith",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
user,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## List Users
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function GET(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
users: await userModuleService.listUsers(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
users: await userModuleService.listUsers(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Update a User
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
const user = await userModuleService.updateUsers({
|
|
||||||
id: "user_123",
|
|
||||||
last_name: "Smith",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({ user })
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function POST(request: Request) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
const user = await userModuleService.updateUsers({
|
|
||||||
id: "user_123",
|
|
||||||
last_name: "Smith",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
user,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Delete a User
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function DELETE(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
await userModuleService.deleteUsers(["user_123"])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function DELETE(request: Request) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
await userModuleService.deleteUsers(["user_123"])
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Invite User
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
const invite = await userModuleService.createInvites({
|
|
||||||
email: "user2@example.com",
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
invite,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
const invite = await userModuleService.createInvites({
|
|
||||||
email: "user2@example.com",
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
invite,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Accept Invite
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
const invite = await userModuleService.validateInviteToken("secret_123")
|
|
||||||
|
|
||||||
const user = await userModuleService.createUsers({
|
|
||||||
email: invite.email,
|
|
||||||
metadata: invite.metadata,
|
|
||||||
})
|
|
||||||
|
|
||||||
await userModuleService.updateInvites({
|
|
||||||
id: invite.id,
|
|
||||||
accepted: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
user,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
const invite = await userModuleService.validateInviteToken("secret_123")
|
|
||||||
|
|
||||||
const user = await userModuleService.createUsers({
|
|
||||||
email: invite.email,
|
|
||||||
metadata: invite.metadata,
|
|
||||||
})
|
|
||||||
|
|
||||||
await userModuleService.updateInvites({
|
|
||||||
id: invite.id,
|
|
||||||
accepted: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
user,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Refresh Invite
|
|
||||||
|
|
||||||
<CodeTabs groupId="app-type">
|
|
||||||
<CodeTab label="Medusa API Router" value="medusa">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
|
||||||
|
|
||||||
export async function POST(
|
|
||||||
req: MedusaRequest,
|
|
||||||
res: MedusaResponse
|
|
||||||
): Promise<void> {
|
|
||||||
const userModuleService = req.scope.resolve(
|
|
||||||
Modules.USER
|
|
||||||
)
|
|
||||||
|
|
||||||
const invites = await userModuleService.refreshInviteTokens(["invite_123"])
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
invite: invites[0],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
<CodeTab label="Next.js App Router" value="nextjs">
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import { NextResponse } from "next/server"
|
|
||||||
|
|
||||||
import { initialize as initializeUserModule } from "@medusajs/medusa/user"
|
|
||||||
|
|
||||||
export async function POST(request: Request, { params }: ContextType) {
|
|
||||||
const userModuleService = await initializeUserModule()
|
|
||||||
|
|
||||||
const invites = await userModuleService.refreshInviteTokens(["invite_123"])
|
|
||||||
|
|
||||||
return NextResponse.json({
|
|
||||||
invite: invites[0],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</CodeTab>
|
|
||||||
</CodeTabs>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## More Examples
|
|
||||||
|
|
||||||
The [User Module's main service reference](/references/user) provides a reference to all the methods available for use with examples for each.
|
|
||||||
@@ -6,101 +6,160 @@ export const metadata = {
|
|||||||
|
|
||||||
# {metadata.title}
|
# {metadata.title}
|
||||||
|
|
||||||
The User Module provides user-related features in your Medusa and Node.js applications.
|
In this section of the documentation, you will find resources to learn more about the User Module and how to use it in your application.
|
||||||
|
|
||||||
|
Medusa has user related features available out-of-the-box through the User Module. A [module](!docs!/learn/fundamentals/modules) is a standalone package that provides features for a single domain. Each of Medusa's commerce features are placed in commerce modules, such as this User Module.
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
## User Features
|
||||||
|
|
||||||
|
- [User Management](./user-creation-flows/page.mdx): Store and manage users in your store.
|
||||||
|
- [Invite Users](./user-creation-flows/page.mdx#invite-users): Invite users to join your store and manage those invites.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## How to Use User Module's Service
|
## How to Use User Module's Service
|
||||||
|
|
||||||
You can use the User Module's main service by resolving from the Medusa container the resource `Modules.USER`.
|
In your Medusa application, you build flows around commerce modules. A flow is built as a [Workflow](!docs!/learn/fundamentals/workflows), which is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||||
|
|
||||||
|
You can build custom workflows and steps. You can also re-use Medusa's workflows and steps, which are provided by the `@medusajs/medusa/core-flows` package.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
<CodeTabs groupId="resource-type">
|
export const highlights = [
|
||||||
<CodeTab label="Workflow Step" value="workflow-step">
|
["12", "Modules.USER", "Resolve the module in a step."]
|
||||||
|
]
|
||||||
|
|
||||||
```ts title="src/workflows/hello-world/step1.ts"
|
```ts title="src/workflows/create-user.ts" highlights={highlights}
|
||||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
import {
|
||||||
|
createWorkflow,
|
||||||
|
WorkflowResponse,
|
||||||
|
createStep,
|
||||||
|
StepResponse,
|
||||||
|
} from "@medusajs/framework/workflows-sdk"
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
import { Modules } from "@medusajs/framework/utils"
|
||||||
|
|
||||||
const step1 = createStep("step-1", async (_, { container }) => {
|
const createUserStep = createStep(
|
||||||
const userModuleService = container.resolve(
|
"create-user",
|
||||||
Modules.USER
|
async ({}, { container }) => {
|
||||||
)
|
const userModuleService = container.resolve(Modules.USER)
|
||||||
|
|
||||||
const users = await userModuleService.listUsers()
|
const user = await userModuleService.createUsers({
|
||||||
})
|
email: "user@example.com",
|
||||||
|
first_name: "John",
|
||||||
|
last_name: "Smith",
|
||||||
|
})
|
||||||
|
|
||||||
|
return new StepResponse({ user }, user.id)
|
||||||
|
},
|
||||||
|
async (userId, { container }) => {
|
||||||
|
if (!userId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const userModuleService = container.resolve(Modules.USER)
|
||||||
|
|
||||||
|
await userModuleService.deleteUsers([userId])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const createUserWorkflow = createWorkflow(
|
||||||
|
"create-user",
|
||||||
|
() => {
|
||||||
|
const { user } = createUserStep()
|
||||||
|
|
||||||
|
return new WorkflowResponse({
|
||||||
|
user,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
|
||||||
|
|
||||||
|
<CodeTabs group="resource-types">
|
||||||
<CodeTab label="API Route" value="api-route">
|
<CodeTab label="API Route" value="api-route">
|
||||||
|
|
||||||
```ts title="src/api/store/custom/route.ts"
|
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
|
import type {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
MedusaRequest,
|
||||||
|
MedusaResponse,
|
||||||
|
} from "@medusajs/framework/http"
|
||||||
|
import { createUserWorkflow } from "../../workflows/create-user"
|
||||||
|
|
||||||
export async function GET(
|
export async function GET(
|
||||||
req: MedusaRequest,
|
req: MedusaRequest,
|
||||||
res: MedusaResponse
|
res: MedusaResponse
|
||||||
): Promise<void> {
|
) {
|
||||||
const userModuleService = req.scope.resolve(
|
const { result } = await createUserWorkflow(req.scope)
|
||||||
Modules.USER
|
.run()
|
||||||
)
|
|
||||||
|
|
||||||
res.json({
|
res.send(result)
|
||||||
users: await userModuleService.listUsers(),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
<CodeTab label="Subscriber" value="subscribers">
|
<CodeTab label="Subscriber" value="subscriber">
|
||||||
|
|
||||||
```ts title="src/subscribers/custom-handler.ts"
|
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
|
||||||
import { SubscriberArgs } from "@medusajs/framework"
|
import {
|
||||||
import { Modules } from "@medusajs/framework/utils"
|
type SubscriberConfig,
|
||||||
|
type SubscriberArgs,
|
||||||
|
} from "@medusajs/framework"
|
||||||
|
import { createUserWorkflow } from "../workflows/create-user"
|
||||||
|
|
||||||
export default async function subscriberHandler({ container }: SubscriberArgs) {
|
export default async function handleUserCreated({
|
||||||
const userModuleService = container.resolve(
|
event: { data },
|
||||||
Modules.USER
|
container,
|
||||||
)
|
}: SubscriberArgs<{ id: string }>) {
|
||||||
|
const { result } = await createUserWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
const users = await userModuleService.listUsers()
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config: SubscriberConfig = {
|
||||||
|
event: "user.created",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTab>
|
||||||
|
<CodeTab label="Scheduled Job" value="scheduled-job">
|
||||||
|
|
||||||
|
```ts title="src/jobs/run-daily.ts" highlights={[["7"], ["8"]]}
|
||||||
|
import { MedusaContainer } from "@medusajs/framework/types"
|
||||||
|
import { createUserWorkflow } from "../workflows/create-user"
|
||||||
|
|
||||||
|
export default async function myCustomJob(
|
||||||
|
container: MedusaContainer
|
||||||
|
) {
|
||||||
|
const { result } = await createUserWorkflow(container)
|
||||||
|
.run()
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
name: "run-once-a-day",
|
||||||
|
schedule: `0 0 * * *`,
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeTab>
|
</CodeTab>
|
||||||
</CodeTabs>
|
</CodeTabs>
|
||||||
|
|
||||||
---
|
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
### User Management
|
|
||||||
|
|
||||||
Manage and store your users through Create, Read, Update, and Delete (CRUD) operations:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const user = await userModuleService.createUsers({
|
|
||||||
email: "user@example.com",
|
|
||||||
first_name: "John",
|
|
||||||
last_name: "Smith",
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Invite Users
|
|
||||||
|
|
||||||
Invite users to join your store and manage those invites, with expiry and revalidation features.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const invite = await userModuleService.createInvites({
|
|
||||||
email: "user2@example.com",
|
|
||||||
})
|
|
||||||
|
|
||||||
// refresh token later
|
|
||||||
await userModuleService.refreshInviteTokens([invite.id])
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Configure User Module
|
## Configure User Module
|
||||||
|
|
||||||
Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
The User Module accepts options for further configurations. Refer to [this documentation](./module-options/page.mdx) for details on the module's options.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<CommerceModuleSections name="User" />
|
||||||
@@ -1,3 +1,9 @@
|
|||||||
|
---
|
||||||
|
tags:
|
||||||
|
- storefront
|
||||||
|
- payment
|
||||||
|
---
|
||||||
|
|
||||||
import { Prerequisites } from "docs-ui"
|
import { Prerequisites } from "docs-ui"
|
||||||
|
|
||||||
export const ogImage = "https://res.cloudinary.com/dza7lstvk/image/upload/v1734007558/Medusa%20Resources/integrations-stripe_qfnwtf.jpg"
|
export const ogImage = "https://res.cloudinary.com/dza7lstvk/image/upload/v1734007558/Medusa%20Resources/integrations-stripe_qfnwtf.jpg"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ tags:
|
|||||||
- publishable api key
|
- publishable api key
|
||||||
- api key
|
- api key
|
||||||
- storefront
|
- storefront
|
||||||
|
- sales channel
|
||||||
---
|
---
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
|
|||||||
@@ -0,0 +1,95 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { H2, Hr, useChildDocs } from "docs-ui"
|
||||||
|
import React, { useMemo } from "react"
|
||||||
|
|
||||||
|
type CommerceModuleSectionsProps = {
|
||||||
|
name: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CommerceModuleSections = ({
|
||||||
|
name,
|
||||||
|
}: CommerceModuleSectionsProps) => {
|
||||||
|
const components: (JSX.Element | JSX.Element[])[] = []
|
||||||
|
const { items: workflowItems, component: workflowsComponent } = useChildDocs({
|
||||||
|
showItems: ["Workflows"],
|
||||||
|
titleLevel: 3,
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
const { items: stepItems, component: stepsComponent } = useChildDocs({
|
||||||
|
showItems: ["Steps"],
|
||||||
|
titleLevel: 3,
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
|
||||||
|
const hideWorkflowsSection = useMemo(() => {
|
||||||
|
return !workflowItems?.default.length && !stepItems?.default.length
|
||||||
|
}, [workflowItems, stepItems])
|
||||||
|
|
||||||
|
const { items: serverGuideItems, component: serverGuidesComponent } =
|
||||||
|
useChildDocs({
|
||||||
|
showItems: ["Server Guides"],
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
if (serverGuideItems?.default.length) {
|
||||||
|
components.push(serverGuidesComponent)
|
||||||
|
}
|
||||||
|
const { items: storefrontGuideItems, component: storefrontGuidesComponent } =
|
||||||
|
useChildDocs({
|
||||||
|
showItems: ["Storefront Guides"],
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
if (storefrontGuideItems?.default.length) {
|
||||||
|
components.push(storefrontGuidesComponent)
|
||||||
|
}
|
||||||
|
const { items: adminGuideItems, component: adminGuidesComponent } =
|
||||||
|
useChildDocs({
|
||||||
|
showItems: ["Admin Guides"],
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
if (adminGuideItems?.default.length) {
|
||||||
|
components.push(adminGuidesComponent)
|
||||||
|
}
|
||||||
|
const { items: userGuideItems, component: userGuidesComponent } =
|
||||||
|
useChildDocs({
|
||||||
|
showItems: ["User Guides"],
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
if (userGuideItems?.default.length) {
|
||||||
|
components.push(userGuidesComponent)
|
||||||
|
}
|
||||||
|
const { items: referenceItems, component: referencesComponent } =
|
||||||
|
useChildDocs({
|
||||||
|
showItems: ["References"],
|
||||||
|
itemsPerRow: 2,
|
||||||
|
})
|
||||||
|
if (referenceItems?.default.length) {
|
||||||
|
components.push(referencesComponent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{!hideWorkflowsSection && (
|
||||||
|
<>
|
||||||
|
<H2 id="medusa-workflows-and-steps">Medusa Workflows and Steps</H2>
|
||||||
|
<p>
|
||||||
|
Medusa provides the following workflows and steps that use the{" "}
|
||||||
|
{name} Module. You can use these workflows and steps in your
|
||||||
|
customizations:
|
||||||
|
</p>
|
||||||
|
{workflowsComponent}
|
||||||
|
{stepsComponent}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{components.map((component, i) => (
|
||||||
|
<React.Fragment key={i}>
|
||||||
|
<>
|
||||||
|
{i !== 0 || !hideWorkflowsSection ? <Hr /> : null}
|
||||||
|
{component}
|
||||||
|
</>
|
||||||
|
</React.Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,12 +5,14 @@ import {
|
|||||||
TypeList,
|
TypeList,
|
||||||
WorkflowDiagram,
|
WorkflowDiagram,
|
||||||
} from "docs-ui"
|
} from "docs-ui"
|
||||||
|
import { CommerceModuleSections } from "../CommerceModuleSections"
|
||||||
|
|
||||||
const MDXComponents: MDXComponentsType = {
|
const MDXComponents: MDXComponentsType = {
|
||||||
...UiMdxComponents,
|
...UiMdxComponents,
|
||||||
a: Link,
|
a: Link,
|
||||||
TypeList,
|
TypeList,
|
||||||
WorkflowDiagram,
|
WorkflowDiagram,
|
||||||
|
CommerceModuleSections,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MDXComponents
|
export default MDXComponents
|
||||||
@@ -4,23 +4,20 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/auth/authentication-route/page.mdx": "2024-09-05T12:06:38.155Z",
|
"app/commerce-modules/auth/authentication-route/page.mdx": "2024-09-05T12:06:38.155Z",
|
||||||
"app/commerce-modules/auth/examples/page.mdx": "2024-10-15T15:02:13.794Z",
|
"app/commerce-modules/auth/examples/page.mdx": "2024-10-15T15:02:13.794Z",
|
||||||
"app/commerce-modules/auth/module-options/page.mdx": "2024-10-15T12:52:08.930Z",
|
"app/commerce-modules/auth/module-options/page.mdx": "2024-10-15T12:52:08.930Z",
|
||||||
"app/commerce-modules/auth/page.mdx": "2024-12-09T14:46:55.446Z",
|
"app/commerce-modules/auth/page.mdx": "2024-12-25T15:40:37.154Z",
|
||||||
"app/commerce-modules/cart/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/cart/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/cart/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/cart/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/cart/concepts/page.mdx": "2024-10-08T07:49:03.737Z",
|
"app/commerce-modules/cart/concepts/page.mdx": "2024-10-08T07:49:03.737Z",
|
||||||
"app/commerce-modules/cart/examples/page.mdx": "2024-10-15T14:59:11.331Z",
|
|
||||||
"app/commerce-modules/cart/promotions/page.mdx": "2024-10-08T07:54:31.120Z",
|
"app/commerce-modules/cart/promotions/page.mdx": "2024-10-08T07:54:31.120Z",
|
||||||
"app/commerce-modules/cart/tax-lines/page.mdx": "2024-10-08T07:57:19.168Z",
|
"app/commerce-modules/cart/tax-lines/page.mdx": "2024-10-08T07:57:19.168Z",
|
||||||
"app/commerce-modules/cart/page.mdx": "2024-12-09T14:47:07.204Z",
|
"app/commerce-modules/cart/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/currency/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/currency/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/currency/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/currency/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/currency/examples/page.mdx": "2024-10-15T14:59:18.466Z",
|
"app/commerce-modules/currency/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/currency/page.mdx": "2024-12-09T14:47:12.300Z",
|
|
||||||
"app/commerce-modules/customer/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/customer/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/customer/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/customer/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/customer/customer-accounts/page.mdx": "2024-10-08T12:20:44.769Z",
|
"app/commerce-modules/customer/customer-accounts/page.mdx": "2024-10-08T12:20:44.769Z",
|
||||||
"app/commerce-modules/customer/examples/page.mdx": "2024-10-15T14:59:26.644Z",
|
"app/commerce-modules/customer/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/customer/page.mdx": "2024-12-09T14:47:17.248Z",
|
|
||||||
"app/commerce-modules/fulfillment/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/fulfillment/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/fulfillment/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/fulfillment/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/fulfillment/concepts/page.mdx": "2024-06-19T13:02:16+00:00",
|
"app/commerce-modules/fulfillment/concepts/page.mdx": "2024-06-19T13:02:16+00:00",
|
||||||
@@ -28,13 +25,12 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/fulfillment/item-fulfillment/page.mdx": "2024-10-08T14:38:15.496Z",
|
"app/commerce-modules/fulfillment/item-fulfillment/page.mdx": "2024-10-08T14:38:15.496Z",
|
||||||
"app/commerce-modules/fulfillment/module-options/page.mdx": "2024-10-15T12:51:56.118Z",
|
"app/commerce-modules/fulfillment/module-options/page.mdx": "2024-10-15T12:51:56.118Z",
|
||||||
"app/commerce-modules/fulfillment/shipping-option/page.mdx": "2024-10-08T14:36:02.660Z",
|
"app/commerce-modules/fulfillment/shipping-option/page.mdx": "2024-10-08T14:36:02.660Z",
|
||||||
"app/commerce-modules/fulfillment/page.mdx": "2024-12-09T14:47:23.482Z",
|
"app/commerce-modules/fulfillment/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/inventory/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/inventory/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/inventory/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/inventory/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/inventory/concepts/page.mdx": "2024-10-08T15:11:27.634Z",
|
"app/commerce-modules/inventory/concepts/page.mdx": "2024-10-08T15:11:27.634Z",
|
||||||
"app/commerce-modules/inventory/examples/page.mdx": "2024-10-15T14:59:45.389Z",
|
|
||||||
"app/commerce-modules/inventory/inventory-in-flows/page.mdx": "2024-10-08T15:14:07.327Z",
|
"app/commerce-modules/inventory/inventory-in-flows/page.mdx": "2024-10-08T15:14:07.327Z",
|
||||||
"app/commerce-modules/inventory/page.mdx": "2024-12-09T14:47:28.150Z",
|
"app/commerce-modules/inventory/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/order/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/order/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/order/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/order/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/order/claim/page.mdx": "2024-10-09T10:11:12.090Z",
|
"app/commerce-modules/order/claim/page.mdx": "2024-10-09T10:11:12.090Z",
|
||||||
@@ -45,10 +41,9 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/order/return/page.mdx": "2024-10-09T10:19:40.731Z",
|
"app/commerce-modules/order/return/page.mdx": "2024-10-09T10:19:40.731Z",
|
||||||
"app/commerce-modules/order/tax-lines/page.mdx": "2024-10-09T10:22:49.335Z",
|
"app/commerce-modules/order/tax-lines/page.mdx": "2024-10-09T10:22:49.335Z",
|
||||||
"app/commerce-modules/order/transactions/page.mdx": "2024-10-09T10:23:36.485Z",
|
"app/commerce-modules/order/transactions/page.mdx": "2024-10-09T10:23:36.485Z",
|
||||||
"app/commerce-modules/order/page.mdx": "2024-12-09T14:47:32.415Z",
|
"app/commerce-modules/order/page.mdx": "2024-12-25T15:55:02.851Z",
|
||||||
"app/commerce-modules/payment/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/payment/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/payment/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/payment/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/payment/examples/page.mdx": "2024-10-15T14:59:55.208Z",
|
|
||||||
"app/commerce-modules/payment/module-options/page.mdx": "2024-10-15T12:51:40.574Z",
|
"app/commerce-modules/payment/module-options/page.mdx": "2024-10-15T12:51:40.574Z",
|
||||||
"app/commerce-modules/payment/payment/page.mdx": "2024-10-09T10:59:08.463Z",
|
"app/commerce-modules/payment/payment/page.mdx": "2024-10-09T10:59:08.463Z",
|
||||||
"app/commerce-modules/payment/payment-collection/page.mdx": "2024-10-09T10:56:49.510Z",
|
"app/commerce-modules/payment/payment-collection/page.mdx": "2024-10-09T10:56:49.510Z",
|
||||||
@@ -57,61 +52,52 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/payment/payment-provider/page.mdx": "2024-10-09T11:07:27.269Z",
|
"app/commerce-modules/payment/payment-provider/page.mdx": "2024-10-09T11:07:27.269Z",
|
||||||
"app/commerce-modules/payment/payment-session/page.mdx": "2024-10-09T10:58:00.960Z",
|
"app/commerce-modules/payment/payment-session/page.mdx": "2024-10-09T10:58:00.960Z",
|
||||||
"app/commerce-modules/payment/webhook-events/page.mdx": "2024-11-19T11:45:02.167Z",
|
"app/commerce-modules/payment/webhook-events/page.mdx": "2024-11-19T11:45:02.167Z",
|
||||||
"app/commerce-modules/payment/page.mdx": "2024-12-09T14:47:57.842Z",
|
"app/commerce-modules/payment/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/pricing/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/pricing/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/pricing/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/pricing/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/pricing/concepts/page.mdx": "2024-10-09T13:37:25.678Z",
|
"app/commerce-modules/pricing/concepts/page.mdx": "2024-10-09T13:37:25.678Z",
|
||||||
"app/commerce-modules/pricing/examples/page.mdx": "2024-12-09T13:04:57.713Z",
|
|
||||||
"app/commerce-modules/pricing/price-calculation/page.mdx": "2024-10-09T13:43:14.038Z",
|
"app/commerce-modules/pricing/price-calculation/page.mdx": "2024-10-09T13:43:14.038Z",
|
||||||
"app/commerce-modules/pricing/price-rules/page.mdx": "2024-10-09T13:38:47.112Z",
|
"app/commerce-modules/pricing/price-rules/page.mdx": "2024-10-09T13:38:47.112Z",
|
||||||
"app/commerce-modules/pricing/tax-inclusive-pricing/page.mdx": "2024-10-09T13:48:23.261Z",
|
"app/commerce-modules/pricing/tax-inclusive-pricing/page.mdx": "2024-10-09T13:48:23.261Z",
|
||||||
"app/commerce-modules/pricing/page.mdx": "2024-12-09T14:48:02.172Z",
|
"app/commerce-modules/pricing/page.mdx": "2024-12-25T15:55:02.851Z",
|
||||||
"app/commerce-modules/product/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/product/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/product/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/product/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/product/examples/page.mdx": "2024-10-09T13:59:32.887Z",
|
"app/commerce-modules/product/guides/price/page.mdx": "2024-12-25T15:10:37.730Z",
|
||||||
"app/commerce-modules/product/guides/price/page.mdx": "2024-12-19T16:37:40.491Z",
|
"app/commerce-modules/product/guides/price-with-taxes/page.mdx": "2024-12-25T15:10:40.879Z",
|
||||||
"app/commerce-modules/product/guides/price-with-taxes/page.mdx": "2024-12-19T16:37:48.597Z",
|
"app/commerce-modules/product/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/product/page.mdx": "2024-12-09T14:48:26.091Z",
|
|
||||||
"app/commerce-modules/promotion/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/promotion/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/promotion/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/promotion/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/promotion/actions/page.mdx": "2024-10-09T14:49:01.645Z",
|
"app/commerce-modules/promotion/actions/page.mdx": "2024-10-09T14:49:01.645Z",
|
||||||
"app/commerce-modules/promotion/application-method/page.mdx": "2024-06-26T07:55:59+00:00",
|
"app/commerce-modules/promotion/application-method/page.mdx": "2024-06-26T07:55:59+00:00",
|
||||||
"app/commerce-modules/promotion/campaign/page.mdx": "2024-05-29T11:08:06+00:00",
|
"app/commerce-modules/promotion/campaign/page.mdx": "2024-05-29T11:08:06+00:00",
|
||||||
"app/commerce-modules/promotion/concepts/page.mdx": "2024-10-09T14:50:50.255Z",
|
"app/commerce-modules/promotion/concepts/page.mdx": "2024-10-09T14:50:50.255Z",
|
||||||
"app/commerce-modules/promotion/examples/page.mdx": "2024-10-09T14:46:47.191Z",
|
"app/commerce-modules/promotion/page.mdx": "2024-12-25T15:55:02.850Z",
|
||||||
"app/commerce-modules/promotion/page.mdx": "2024-12-09T14:48:30.816Z",
|
|
||||||
"app/commerce-modules/region/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/region/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/region/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/region/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/region/examples/page.mdx": "2024-10-15T15:00:24.388Z",
|
"app/commerce-modules/region/page.mdx": "2024-12-25T15:55:02.851Z",
|
||||||
"app/commerce-modules/region/page.mdx": "2024-12-09T14:48:34.729Z",
|
|
||||||
"app/commerce-modules/sales-channel/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/sales-channel/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/sales-channel/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/sales-channel/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/sales-channel/examples/page.mdx": "2024-10-15T15:00:33.322Z",
|
|
||||||
"app/commerce-modules/sales-channel/publishable-api-keys/page.mdx": "2024-10-15T14:21:38.353Z",
|
"app/commerce-modules/sales-channel/publishable-api-keys/page.mdx": "2024-10-15T14:21:38.353Z",
|
||||||
"app/commerce-modules/sales-channel/page.mdx": "2024-12-09T14:48:38.646Z",
|
"app/commerce-modules/sales-channel/page.mdx": "2024-12-25T15:55:17.802Z",
|
||||||
"app/commerce-modules/stock-location/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/stock-location/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/stock-location/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/stock-location/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/stock-location/concepts/page.mdx": "2024-10-15T14:32:21.875Z",
|
"app/commerce-modules/stock-location/concepts/page.mdx": "2024-10-15T14:32:21.875Z",
|
||||||
"app/commerce-modules/stock-location/examples/page.mdx": "2024-10-15T15:00:41.265Z",
|
"app/commerce-modules/stock-location/page.mdx": "2024-12-25T16:09:26.781Z",
|
||||||
"app/commerce-modules/stock-location/page.mdx": "2024-12-09T14:48:42.516Z",
|
|
||||||
"app/commerce-modules/store/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/store/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/store/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/store/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/store/examples/page.mdx": "2024-10-15T15:00:47.283Z",
|
"app/commerce-modules/store/page.mdx": "2024-12-25T16:09:12.124Z",
|
||||||
"app/commerce-modules/store/page.mdx": "2024-12-09T14:48:46.363Z",
|
|
||||||
"app/commerce-modules/tax/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/tax/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/tax/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/tax/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/tax/examples/page.mdx": "2024-10-15T15:00:52.899Z",
|
|
||||||
"app/commerce-modules/tax/module-options/page.mdx": "2024-10-15T14:35:46.117Z",
|
"app/commerce-modules/tax/module-options/page.mdx": "2024-10-15T14:35:46.117Z",
|
||||||
"app/commerce-modules/tax/tax-calculation-with-provider/page.mdx": "2024-10-15T14:43:00.700Z",
|
"app/commerce-modules/tax/tax-calculation-with-provider/page.mdx": "2024-10-15T14:43:00.700Z",
|
||||||
"app/commerce-modules/tax/tax-rates-and-rules/page.mdx": "2024-10-15T14:38:06.889Z",
|
"app/commerce-modules/tax/tax-rates-and-rules/page.mdx": "2024-10-15T14:38:06.889Z",
|
||||||
"app/commerce-modules/tax/tax-region/page.mdx": "2024-10-15T14:36:47.028Z",
|
"app/commerce-modules/tax/tax-region/page.mdx": "2024-10-15T14:36:47.028Z",
|
||||||
"app/commerce-modules/tax/page.mdx": "2024-12-09T14:48:50.327Z",
|
"app/commerce-modules/tax/page.mdx": "2024-12-25T16:22:51.953Z",
|
||||||
"app/commerce-modules/user/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/user/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/user/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/user/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/user/examples/page.mdx": "2024-10-15T15:00:59.626Z",
|
|
||||||
"app/commerce-modules/user/module-options/page.mdx": "2024-09-30T08:43:53.171Z",
|
"app/commerce-modules/user/module-options/page.mdx": "2024-09-30T08:43:53.171Z",
|
||||||
"app/commerce-modules/user/user-creation-flows/page.mdx": "2024-10-15T14:51:37.311Z",
|
"app/commerce-modules/user/user-creation-flows/page.mdx": "2024-10-15T14:51:37.311Z",
|
||||||
"app/commerce-modules/user/page.mdx": "2024-12-09T14:48:54.225Z",
|
"app/commerce-modules/user/page.mdx": "2024-12-25T16:24:26.837Z",
|
||||||
"app/commerce-modules/page.mdx": "2024-12-23T14:38:21.064Z",
|
"app/commerce-modules/page.mdx": "2024-12-23T14:38:21.064Z",
|
||||||
"app/contribution-guidelines/docs/page.mdx": "2024-12-12T11:06:12.250Z",
|
"app/contribution-guidelines/docs/page.mdx": "2024-12-12T11:06:12.250Z",
|
||||||
"app/create-medusa-app/page.mdx": "2024-08-05T11:10:55+03:00",
|
"app/create-medusa-app/page.mdx": "2024-08-05T11:10:55+03:00",
|
||||||
@@ -209,11 +195,10 @@ export const generatedEditDates = {
|
|||||||
"app/commerce-modules/auth/auth-flows/page.mdx": "2024-09-05T08:50:11.671Z",
|
"app/commerce-modules/auth/auth-flows/page.mdx": "2024-09-05T08:50:11.671Z",
|
||||||
"app/commerce-modules/auth/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
"app/commerce-modules/auth/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||||
"app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2024-12-09T13:04:01.129Z",
|
"app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2024-12-09T13:04:01.129Z",
|
||||||
"app/commerce-modules/api-key/page.mdx": "2024-12-09T14:47:01.509Z",
|
"app/commerce-modules/api-key/page.mdx": "2024-12-25T15:55:02.846Z",
|
||||||
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-09T15:32:07.594Z",
|
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-25T13:26:27.176Z",
|
||||||
"app/architectural-modules/page.mdx": "2024-12-11T10:33:53.064Z",
|
"app/architectural-modules/page.mdx": "2024-12-11T10:33:53.064Z",
|
||||||
"app/architectural-modules/workflow-engine/redis/page.mdx": "2024-10-15T12:50:59.507Z",
|
"app/architectural-modules/workflow-engine/redis/page.mdx": "2024-10-15T12:50:59.507Z",
|
||||||
"app/commerce-modules/api-key/examples/page.mdx": "2024-10-15T14:58:58.994Z",
|
|
||||||
"app/architectural-modules/notification/sendgrid/page.mdx": "2024-10-15T12:51:25.569Z",
|
"app/architectural-modules/notification/sendgrid/page.mdx": "2024-10-15T12:51:25.569Z",
|
||||||
"app/commerce-modules/api-key/concepts/page.mdx": "2024-10-07T13:59:37.529Z",
|
"app/commerce-modules/api-key/concepts/page.mdx": "2024-10-07T13:59:37.529Z",
|
||||||
"app/architectural-modules/workflow-engine/page.mdx": "2024-05-28T13:25:03+03:00",
|
"app/architectural-modules/workflow-engine/page.mdx": "2024-05-28T13:25:03+03:00",
|
||||||
@@ -886,7 +871,7 @@ export const generatedEditDates = {
|
|||||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.authenticate/page.mdx": "2024-12-10T14:54:57.312Z",
|
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.authenticate/page.mdx": "2024-12-10T14:54:57.312Z",
|
||||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.validateCallback/page.mdx": "2024-12-10T14:54:57.318Z",
|
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.validateCallback/page.mdx": "2024-12-10T14:54:57.318Z",
|
||||||
"references/auth/interfaces/auth.AuthenticationResponse/page.mdx": "2024-12-09T13:21:36.233Z",
|
"references/auth/interfaces/auth.AuthenticationResponse/page.mdx": "2024-12-09T13:21:36.233Z",
|
||||||
"references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2024-12-09T13:21:36.205Z",
|
"references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2024-12-25T13:27:05.376Z",
|
||||||
"references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.refreshInviteTokensWorkflow/page.mdx": "2024-12-25T08:43:13.811Z",
|
"references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.refreshInviteTokensWorkflow/page.mdx": "2024-12-25T08:43:13.811Z",
|
||||||
"references/types/CommonTypes/types/types.CommonTypes.BatchMethodResponse/page.mdx": "2024-12-09T13:21:32.849Z",
|
"references/types/CommonTypes/types/types.CommonTypes.BatchMethodResponse/page.mdx": "2024-12-09T13:21:32.849Z",
|
||||||
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminAddReturnShipping/page.mdx": "2024-12-09T13:21:34.545Z",
|
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminAddReturnShipping/page.mdx": "2024-12-09T13:21:34.545Z",
|
||||||
@@ -986,7 +971,7 @@ export const generatedEditDates = {
|
|||||||
"references/order/IOrderModuleService/methods/order.IOrderModuleService.updateOrderChangeActions/page.mdx": "2024-12-23T12:30:30.337Z",
|
"references/order/IOrderModuleService/methods/order.IOrderModuleService.updateOrderChangeActions/page.mdx": "2024-12-23T12:30:30.337Z",
|
||||||
"references/types/ModulesSdkTypes/types/types.ModulesSdkTypes.RemoteQueryFunction/page.mdx": "2024-12-23T12:30:29.466Z",
|
"references/types/ModulesSdkTypes/types/types.ModulesSdkTypes.RemoteQueryFunction/page.mdx": "2024-12-23T12:30:29.466Z",
|
||||||
"references/types/types.CommonTypes/page.mdx": "2024-11-25T17:49:25.351Z",
|
"references/types/types.CommonTypes/page.mdx": "2024-11-25T17:49:25.351Z",
|
||||||
"app/storefront-development/publishable-api-keys/page.mdx": "2024-12-19T16:35:56.322Z",
|
"app/storefront-development/publishable-api-keys/page.mdx": "2024-12-25T16:00:18.474Z",
|
||||||
"references/api_key/types/api_key.ExpandScalar/page.mdx": "2024-09-17T00:10:59.563Z",
|
"references/api_key/types/api_key.ExpandScalar/page.mdx": "2024-09-17T00:10:59.563Z",
|
||||||
"references/api_key/types/api_key.FilterQuery/page.mdx": "2024-11-27T16:33:40.706Z",
|
"references/api_key/types/api_key.FilterQuery/page.mdx": "2024-11-27T16:33:40.706Z",
|
||||||
"references/api_key/types/api_key.FilterValue/page.mdx": "2024-09-17T00:10:59.571Z",
|
"references/api_key/types/api_key.FilterValue/page.mdx": "2024-09-17T00:10:59.571Z",
|
||||||
@@ -2146,12 +2131,12 @@ export const generatedEditDates = {
|
|||||||
"app/admin-components/layouts/single-column/page.mdx": "2024-10-07T11:16:06.435Z",
|
"app/admin-components/layouts/single-column/page.mdx": "2024-10-07T11:16:06.435Z",
|
||||||
"app/admin-components/layouts/two-column/page.mdx": "2024-10-07T11:16:10.092Z",
|
"app/admin-components/layouts/two-column/page.mdx": "2024-10-07T11:16:10.092Z",
|
||||||
"app/admin-components/components/forms/page.mdx": "2024-10-09T12:48:04.229Z",
|
"app/admin-components/components/forms/page.mdx": "2024-10-09T12:48:04.229Z",
|
||||||
"app/commerce-modules/auth/reset-password/page.mdx": "2024-11-27T13:33:55.940Z",
|
"app/commerce-modules/auth/reset-password/page.mdx": "2024-12-25T13:26:32.595Z",
|
||||||
"app/storefront-development/customers/reset-password/page.mdx": "2024-12-19T16:32:00.724Z",
|
"app/storefront-development/customers/reset-password/page.mdx": "2024-12-19T16:32:00.724Z",
|
||||||
"app/commerce-modules/api-key/links-to-other-modules/page.mdx": "2024-12-24T14:36:06.109Z",
|
"app/commerce-modules/api-key/links-to-other-modules/page.mdx": "2024-12-24T14:36:06.109Z",
|
||||||
"app/commerce-modules/cart/extend/page.mdx": "2024-12-11T09:05:37.041Z",
|
"app/commerce-modules/cart/extend/page.mdx": "2024-12-25T12:48:59.149Z",
|
||||||
"app/commerce-modules/cart/links-to-other-modules/page.mdx": "2024-12-24T14:46:49.847Z",
|
"app/commerce-modules/cart/links-to-other-modules/page.mdx": "2024-12-24T14:46:49.847Z",
|
||||||
"app/commerce-modules/customer/extend/page.mdx": "2024-12-16T14:11:47.517Z",
|
"app/commerce-modules/customer/extend/page.mdx": "2024-12-25T15:54:37.789Z",
|
||||||
"app/commerce-modules/fulfillment/links-to-other-modules/page.mdx": "2024-12-24T14:49:54.535Z",
|
"app/commerce-modules/fulfillment/links-to-other-modules/page.mdx": "2024-12-24T14:49:54.535Z",
|
||||||
"app/commerce-modules/inventory/links-to-other-modules/page.mdx": "2024-12-24T14:50:25.574Z",
|
"app/commerce-modules/inventory/links-to-other-modules/page.mdx": "2024-12-24T14:50:25.574Z",
|
||||||
"app/commerce-modules/pricing/links-to-other-modules/page.mdx": "2024-12-24T14:53:39.198Z",
|
"app/commerce-modules/pricing/links-to-other-modules/page.mdx": "2024-12-24T14:53:39.198Z",
|
||||||
@@ -5632,7 +5617,7 @@ export const generatedEditDates = {
|
|||||||
"app/recipes/commerce-automation/restock-notification/page.mdx": "2024-12-11T08:47:27.471Z",
|
"app/recipes/commerce-automation/restock-notification/page.mdx": "2024-12-11T08:47:27.471Z",
|
||||||
"app/troubleshooting/workflow-errors/page.mdx": "2024-12-11T08:44:36.598Z",
|
"app/troubleshooting/workflow-errors/page.mdx": "2024-12-11T08:44:36.598Z",
|
||||||
"app/integrations/guides/shipstation/page.mdx": "2024-12-23T07:48:47.719Z",
|
"app/integrations/guides/shipstation/page.mdx": "2024-12-23T07:48:47.719Z",
|
||||||
"app/nextjs-starter/guides/customize-stripe/page.mdx": "2024-12-12T12:46:33.999Z",
|
"app/nextjs-starter/guides/customize-stripe/page.mdx": "2024-12-25T14:48:55.877Z",
|
||||||
"references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflow/page.mdx": "2024-12-25T08:43:13.451Z",
|
"references/core_flows/Cart/Workflows_Cart/functions/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflow/page.mdx": "2024-12-25T08:43:13.451Z",
|
||||||
"references/core_flows/Cart/Workflows_Cart/variables/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflowId/page.mdx": "2024-12-17T16:57:22.044Z",
|
"references/core_flows/Cart/Workflows_Cart/variables/core_flows.Cart.Workflows_Cart.listShippingOptionsForCartWithPricingWorkflowId/page.mdx": "2024-12-17T16:57:22.044Z",
|
||||||
"references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.calculateShippingOptionsPricesStep/page.mdx": "2024-12-25T08:43:13.630Z",
|
"references/core_flows/Fulfillment/Steps_Fulfillment/functions/core_flows.Fulfillment.Steps_Fulfillment.calculateShippingOptionsPricesStep/page.mdx": "2024-12-25T08:43:13.630Z",
|
||||||
|
|||||||
@@ -127,10 +127,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/api-key/concepts/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/api-key/concepts/page.mdx",
|
||||||
"pathname": "/commerce-modules/api-key/concepts"
|
"pathname": "/commerce-modules/api-key/concepts"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/api-key/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/api-key/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/api-key/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/api-key/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/api-key/links-to-other-modules"
|
"pathname": "/commerce-modules/api-key/links-to-other-modules"
|
||||||
@@ -179,10 +175,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/auth/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/auth/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/auth/events"
|
"pathname": "/commerce-modules/auth/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/auth/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/auth/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/auth/module-options/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/auth/module-options/page.mdx",
|
||||||
"pathname": "/commerce-modules/auth/module-options"
|
"pathname": "/commerce-modules/auth/module-options"
|
||||||
@@ -203,10 +195,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/cart/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/cart/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/cart/events"
|
"pathname": "/commerce-modules/cart/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/cart/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/cart/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/cart/extend/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/cart/extend/page.mdx",
|
||||||
"pathname": "/commerce-modules/cart/extend"
|
"pathname": "/commerce-modules/cart/extend"
|
||||||
@@ -227,10 +215,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/cart/tax-lines/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/cart/tax-lines/page.mdx",
|
||||||
"pathname": "/commerce-modules/cart/tax-lines"
|
"pathname": "/commerce-modules/cart/tax-lines"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/currency/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/currency/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/currency/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/currency/links-to-other-modules"
|
"pathname": "/commerce-modules/currency/links-to-other-modules"
|
||||||
@@ -251,10 +235,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/customer/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/customer/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/customer/events"
|
"pathname": "/commerce-modules/customer/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/customer/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/customer/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/customer/extend/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/customer/extend/page.mdx",
|
||||||
"pathname": "/commerce-modules/customer/extend"
|
"pathname": "/commerce-modules/customer/extend"
|
||||||
@@ -307,10 +287,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/inventory/concepts/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/inventory/concepts/page.mdx",
|
||||||
"pathname": "/commerce-modules/inventory/concepts"
|
"pathname": "/commerce-modules/inventory/concepts"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/inventory/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/inventory/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/inventory/inventory-in-flows/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/inventory/inventory-in-flows/page.mdx",
|
||||||
"pathname": "/commerce-modules/inventory/inventory-in-flows"
|
"pathname": "/commerce-modules/inventory/inventory-in-flows"
|
||||||
@@ -383,10 +359,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules"
|
"pathname": "/commerce-modules"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/payment/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/payment/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/payment/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/payment/links-to-other-modules"
|
"pathname": "/commerce-modules/payment/links-to-other-modules"
|
||||||
@@ -435,10 +407,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/pricing/concepts/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/pricing/concepts/page.mdx",
|
||||||
"pathname": "/commerce-modules/pricing/concepts"
|
"pathname": "/commerce-modules/pricing/concepts"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/pricing/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/pricing/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/pricing/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/pricing/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/pricing/links-to-other-modules"
|
"pathname": "/commerce-modules/pricing/links-to-other-modules"
|
||||||
@@ -467,10 +435,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/product/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/product/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/product/events"
|
"pathname": "/commerce-modules/product/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/product/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/product/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/product/extend/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/product/extend/page.mdx",
|
||||||
"pathname": "/commerce-modules/product/extend"
|
"pathname": "/commerce-modules/product/extend"
|
||||||
@@ -511,10 +475,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/promotion/concepts/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/promotion/concepts/page.mdx",
|
||||||
"pathname": "/commerce-modules/promotion/concepts"
|
"pathname": "/commerce-modules/promotion/concepts"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/promotion/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/promotion/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/promotion/extend/page.mdx",
|
||||||
"pathname": "/commerce-modules/promotion/extend"
|
"pathname": "/commerce-modules/promotion/extend"
|
||||||
@@ -535,10 +495,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/region/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/region/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/region/events"
|
"pathname": "/commerce-modules/region/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/region/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/region/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/region/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/region/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/region/links-to-other-modules"
|
"pathname": "/commerce-modules/region/links-to-other-modules"
|
||||||
@@ -555,10 +511,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/sales-channel/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/sales-channel/events"
|
"pathname": "/commerce-modules/sales-channel/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/sales-channel/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/sales-channel/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/sales-channel/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/sales-channel/links-to-other-modules"
|
"pathname": "/commerce-modules/sales-channel/links-to-other-modules"
|
||||||
@@ -579,10 +531,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/stock-location/concepts/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/stock-location/concepts/page.mdx",
|
||||||
"pathname": "/commerce-modules/stock-location/concepts"
|
"pathname": "/commerce-modules/stock-location/concepts"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/stock-location/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/stock-location/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/stock-location/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/stock-location/links-to-other-modules"
|
"pathname": "/commerce-modules/stock-location/links-to-other-modules"
|
||||||
@@ -595,10 +543,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/store/admin-widget-zones/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/store/admin-widget-zones/page.mdx",
|
||||||
"pathname": "/commerce-modules/store/admin-widget-zones"
|
"pathname": "/commerce-modules/store/admin-widget-zones"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/store/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/store/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/store/links-to-other-modules/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/store/links-to-other-modules/page.mdx",
|
||||||
"pathname": "/commerce-modules/store/links-to-other-modules"
|
"pathname": "/commerce-modules/store/links-to-other-modules"
|
||||||
@@ -611,10 +555,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/tax/admin-widget-zones/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/tax/admin-widget-zones/page.mdx",
|
||||||
"pathname": "/commerce-modules/tax/admin-widget-zones"
|
"pathname": "/commerce-modules/tax/admin-widget-zones"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/tax/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/tax/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/tax/module-options/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/tax/module-options/page.mdx",
|
||||||
"pathname": "/commerce-modules/tax/module-options"
|
"pathname": "/commerce-modules/tax/module-options"
|
||||||
@@ -643,10 +583,6 @@ export const filesMap = [
|
|||||||
"filePath": "/www/apps/resources/app/commerce-modules/user/events/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/user/events/page.mdx",
|
||||||
"pathname": "/commerce-modules/user/events"
|
"pathname": "/commerce-modules/user/events"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/user/examples/page.mdx",
|
|
||||||
"pathname": "/commerce-modules/user/examples"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"filePath": "/www/apps/resources/app/commerce-modules/user/module-options/page.mdx",
|
"filePath": "/www/apps/resources/app/commerce-modules/user/module-options/page.mdx",
|
||||||
"pathname": "/commerce-modules/user/module-options"
|
"pathname": "/commerce-modules/user/module-options"
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -1,12 +1,9 @@
|
|||||||
import { generateEditedDates, generateSidebar } from "build-scripts"
|
import { generateEditedDates, generateSidebar } from "build-scripts"
|
||||||
import { generateTags } from "tags"
|
|
||||||
import { main as generateSlugChanges } from "./generate-slug-changes.mjs"
|
import { main as generateSlugChanges } from "./generate-slug-changes.mjs"
|
||||||
import { main as generateFilesMap } from "./generate-files-map.mjs"
|
import { main as generateFilesMap } from "./generate-files-map.mjs"
|
||||||
import { sidebar } from "../sidebar.mjs"
|
import { sidebar } from "../sidebar.mjs"
|
||||||
import path from "path"
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
await generateTags(path.resolve("..", "..", "packages", "tags"))
|
|
||||||
await generateSidebar(sidebar)
|
await generateSidebar(sidebar)
|
||||||
await generateSlugChanges()
|
await generateSlugChanges()
|
||||||
await generateFilesMap()
|
await generateFilesMap()
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const apiKeySidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/api-key/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -32,8 +31,44 @@ export const apiKeySidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_tags: "storefront+apiKey",
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_tags: "admin+apiKey",
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_tags: "userGuide+apiKey",
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_tags: "workflow+apiKey",
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_tags: "step+apiKey",
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ export const authSidebar = [
|
|||||||
title: "Module Options",
|
title: "Module Options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/auth/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -47,8 +46,11 @@ export const authSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Guides",
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+auth",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -68,8 +70,44 @@ export const authSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+auth",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+auth",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+auth",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+auth",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+auth",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "Providers",
|
title: "Providers",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -89,8 +127,9 @@ export const authSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,18 +11,12 @@ export const cartSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/cart/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "category",
|
||||||
path: "/commerce-modules/cart/extend",
|
|
||||||
title: "Extend Module",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "sub-category",
|
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -47,8 +41,58 @@ export const cartSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+cart",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "/commerce-modules/cart/extend",
|
||||||
|
title: "Extend Module",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+cart",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+cart",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+cart",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+cart",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+cart",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const currencySidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/currency/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "sub-category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -26,16 +25,59 @@ export const currencySidebar = [
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+currency",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+currency",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+currency",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+currency",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+currency",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+currency",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "sub-category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
path: "/references/currency",
|
path: "/references/currency",
|
||||||
title: "Main Service Reference",
|
title: "Main Service Reference",
|
||||||
isChildSidebar: true,
|
isChildSidebar: true,
|
||||||
childSidebarTitle: "Cart Module's Main Service Reference",
|
childSidebarTitle: "Currency Module's Main Service Reference",
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "category",
|
type: "category",
|
||||||
|
|||||||
@@ -11,18 +11,12 @@ export const customerSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/customer/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "category",
|
||||||
path: "/commerce-modules/customer/extend",
|
|
||||||
title: "Extend Module",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "sub-category",
|
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -37,8 +31,58 @@ export const customerSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+customer",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "/commerce-modules/customer/extend",
|
||||||
|
title: "Extend Module",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+customer",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+customer",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+customer",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+customer",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+customer",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -16,8 +16,12 @@ export const fulfillmentSidebar = [
|
|||||||
title: "Module Options",
|
title: "Module Options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -47,8 +51,11 @@ export const fulfillmentSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Guides",
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+fulfillment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -56,15 +63,51 @@ export const fulfillmentSidebar = [
|
|||||||
title: "Create Fulfillment Provider Module",
|
title: "Create Fulfillment Provider Module",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "ref",
|
||||||
path: "/integrations/guides/shipstation",
|
path: "/integrations/guides/shipstation",
|
||||||
title: "Integrate ShipStation",
|
title: "Integrate ShipStation",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+fulfillment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+fulfillment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+fulfillment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+fulfillment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+fulfillment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const inventorySidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/inventory/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -37,8 +36,51 @@ export const inventorySidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+inventory",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+inventory",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+inventory",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+inventory",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+inventory",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+inventory",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,8 +11,12 @@ export const orderSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "separator",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -72,8 +76,51 @@ export const orderSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+order",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+order",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+order",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+order",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+order",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+order",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ export const paymentSidebar = [
|
|||||||
title: "Module Options",
|
title: "Module Options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/payment/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -57,8 +56,11 @@ export const paymentSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Guides",
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+payment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -73,8 +75,44 @@ export const paymentSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Payment Providers",
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+payment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+payment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+payment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+payment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+payment",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Providers",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -84,8 +122,9 @@ export const paymentSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const pricingSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/pricing/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -47,8 +46,51 @@ export const pricingSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+pricing",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+pricing",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+pricing",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+pricing",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+pricing",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+pricing",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,18 +11,12 @@ export const productSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/product/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "category",
|
||||||
path: "/commerce-modules/product/extend",
|
|
||||||
title: "Extend Module",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "sub-category",
|
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -32,13 +26,68 @@ export const productSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Guides",
|
title: "Server Guides",
|
||||||
autogenerate_path: "/commerce-modules/product/guides",
|
autogenerate_tags: "server+product",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "/commerce-modules/product/extend",
|
||||||
|
title: "Extend Module",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "link",
|
||||||
|
path: "/commerce-modules/product/guides/price",
|
||||||
|
title: "Get Variant Prices",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "/commerce-modules/product/guides/price-with-taxes",
|
||||||
|
title: "Get Variant Price with Taxes",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+product",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+product",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+product",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+product",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+product",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,18 +11,12 @@ export const promotionSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/promotion/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "category",
|
||||||
path: "/commerce-modules/promotion/extend",
|
|
||||||
title: "Extend Module",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "sub-category",
|
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -52,8 +46,58 @@ export const promotionSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+promotion",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
path: "/commerce-modules/promotion/extend",
|
||||||
|
title: "Extend Module",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+promotion",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+promotion",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+promotion",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+promotion",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+promotion",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const regionSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/region/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -27,8 +26,51 @@ export const regionSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+region",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+region",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+region",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+region",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+region",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+region",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const salesChannelSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/sales-channel/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -32,8 +31,51 @@ export const salesChannelSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+salesChannel",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+salesChannel",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+salesChannel",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+salesChannel",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+salesChannel",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+salesChannel",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const stockLocationSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/stock-location/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -32,8 +31,51 @@ export const stockLocationSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+stockLocation",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+stockLocation",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+stockLocation",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+stockLocation",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+stockLocation",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+stockLocation",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -11,13 +11,12 @@ export const storeSidebar = [
|
|||||||
title: "Overview",
|
title: "Overview",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/store/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -27,8 +26,51 @@ export const storeSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+store",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+store",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+store",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+store",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+store",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+store",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -16,13 +16,12 @@ export const taxSidebar = [
|
|||||||
title: "Module Options",
|
title: "Module Options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/tax/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Concepts",
|
title: "Concepts",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -42,8 +41,11 @@ export const taxSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Guides",
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+tax",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -53,8 +55,44 @@ export const taxSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+tax",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+tax",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+tax",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+tax",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+tax",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -16,13 +16,14 @@ export const userSidebar = [
|
|||||||
title: "Module Options",
|
title: "Module Options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "separator",
|
||||||
path: "/commerce-modules/user/examples",
|
|
||||||
title: "Examples",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
title: "Guides",
|
title: "Server Guides",
|
||||||
|
autogenerate_tags: "server+user",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
@@ -32,8 +33,44 @@ export const userSidebar = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "sub-category",
|
type: "category",
|
||||||
|
title: "Storefront Guides",
|
||||||
|
autogenerate_tags: "storefront+user",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Admin Guides",
|
||||||
|
autogenerate_tags: "admin+user",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "User Guides",
|
||||||
|
autogenerate_tags: "userGuides+user",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Workflows",
|
||||||
|
autogenerate_tags: "workflow+user",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
|
title: "Steps",
|
||||||
|
autogenerate_tags: "step+user",
|
||||||
|
initialOpen: false,
|
||||||
|
autogenerate_as_ref: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "category",
|
||||||
title: "References",
|
title: "References",
|
||||||
|
initialOpen: false,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "link",
|
type: "link",
|
||||||
|
|||||||
@@ -88,23 +88,35 @@ async function getAutogeneratedSidebarItems(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getAutogeneratedTagSidebarItems(
|
async function getAutogeneratedTagSidebarItems(
|
||||||
tags: string
|
tags: string,
|
||||||
|
type: "link" | "ref",
|
||||||
|
existingChildren?: RawSidebarItem[]
|
||||||
): Promise<ItemsToAdd[]> {
|
): Promise<ItemsToAdd[]> {
|
||||||
const items: ItemsToAdd[] = []
|
const items: ItemsToAdd[] = []
|
||||||
|
|
||||||
const parsedTags = parseTags(tags)
|
const parsedTags = parseTags(tags)
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
...parsedTags.map(
|
...parsedTags
|
||||||
|
.filter((tagItem) => {
|
||||||
|
return existingChildren?.every((existingItem) => {
|
||||||
|
if (existingItem.type !== "link" && existingItem.type !== "ref") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingItem.path !== tagItem.path
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.map(
|
||||||
(tagItem) =>
|
(tagItem) =>
|
||||||
({
|
({
|
||||||
type: "link",
|
type,
|
||||||
...tagItem,
|
...tagItem,
|
||||||
}) as ItemsToAdd
|
}) as ItemsToAdd
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return sidebarAttachHrefCommonOptions(items)
|
return sidebarAttachHrefCommonOptions([...(existingChildren || []), ...items])
|
||||||
}
|
}
|
||||||
|
|
||||||
async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
|
async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
|
||||||
@@ -130,7 +142,9 @@ async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
|
|||||||
})
|
})
|
||||||
} else if (item.autogenerate_tags) {
|
} else if (item.autogenerate_tags) {
|
||||||
item.children = await getAutogeneratedTagSidebarItems(
|
item.children = await getAutogeneratedTagSidebarItems(
|
||||||
item.autogenerate_tags
|
item.autogenerate_tags,
|
||||||
|
item.autogenerate_as_ref ? "ref" : "link",
|
||||||
|
item.children
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
item.custom_autogenerate &&
|
item.custom_autogenerate &&
|
||||||
@@ -138,21 +152,29 @@ async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
|
|||||||
) {
|
) {
|
||||||
item.children = await customGenerators[item.custom_autogenerate]()
|
item.children = await customGenerators[item.custom_autogenerate]()
|
||||||
} else if (item.children) {
|
} else if (item.children) {
|
||||||
item.children = await Promise.all(
|
item.children = await checkItems(item.children)
|
||||||
item.children.map(async (childItem) => await checkItem(childItem))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return item
|
return item
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function checkItems(items: RawSidebarItem[]): Promise<RawSidebarItem[]> {
|
||||||
|
return (
|
||||||
|
await Promise.all(items.map(async (item) => await checkItem(item)))
|
||||||
|
).filter((item) => {
|
||||||
|
if (item.type !== "category" && item.type !== "sub-category") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return (item.children?.length || 0) > 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export async function generateSidebar(sidebar: RawSidebarItem[]) {
|
export async function generateSidebar(sidebar: RawSidebarItem[]) {
|
||||||
const path = await import("path")
|
const path = await import("path")
|
||||||
const { writeFileSync } = await import("fs")
|
const { writeFileSync } = await import("fs")
|
||||||
|
|
||||||
const normalizedSidebar = await Promise.all(
|
const normalizedSidebar = await checkItems(sidebar)
|
||||||
sidebar.map(async (item) => await checkItem(item))
|
|
||||||
)
|
|
||||||
|
|
||||||
const generatedDirPath = path.resolve("generated")
|
const generatedDirPath = path.resolve("generated")
|
||||||
|
|
||||||
|
|||||||
@@ -27,14 +27,11 @@ const getIntersectionTags = (tags: string): Tag => {
|
|||||||
.map((tagName) => getTagItems(tagName))
|
.map((tagName) => getTagItems(tagName))
|
||||||
.filter((tag) => tag !== undefined) as Tag[]
|
.filter((tag) => tag !== undefined) as Tag[]
|
||||||
|
|
||||||
if (!tagsToIntersect.length) {
|
if (tagsToIntersect.length < 2) {
|
||||||
|
// if there are less than 2 tags to intersect, return an empty array
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tagsToIntersect.length === 1) {
|
|
||||||
return tagsToIntersect[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
return tagsToIntersect[0].filter((tagItem) => {
|
return tagsToIntersect[0].filter((tagItem) => {
|
||||||
return tagsToIntersect
|
return tagsToIntersect
|
||||||
.slice(1)
|
.slice(1)
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ import React, { useMemo } from "react"
|
|||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import Link from "next/link"
|
import Link from "next/link"
|
||||||
import { SidebarItemLink } from "types"
|
import { SidebarItemLink } from "types"
|
||||||
import { CurrentItemsState, useSidebar, useSiteConfig } from "../../providers"
|
import {
|
||||||
|
CurrentItemsState,
|
||||||
|
isSidebarItemLink,
|
||||||
|
useSidebar,
|
||||||
|
useSiteConfig,
|
||||||
|
} from "../../providers"
|
||||||
import { Button } from "../Button"
|
import { Button } from "../Button"
|
||||||
import { TriangleRightMini } from "@medusajs/icons"
|
import { TriangleRightMini } from "@medusajs/icons"
|
||||||
|
|
||||||
@@ -29,16 +34,14 @@ export const Breadcrumbs = () => {
|
|||||||
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
|
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentPath =
|
const parentPath = isSidebarItemLink(item.parentItem)
|
||||||
item.parentItem?.type === "link"
|
|
||||||
? getLinkPath(item.parentItem)
|
? getLinkPath(item.parentItem)
|
||||||
: (item.parentItem?.type === "category" &&
|
: (item.parentItem?.type === "category" &&
|
||||||
breadcrumbOptions?.showCategories) ||
|
breadcrumbOptions?.showCategories) ||
|
||||||
item.parentItem?.type === "sub-category"
|
item.parentItem?.type === "sub-category"
|
||||||
? "#"
|
? "#"
|
||||||
: undefined
|
: undefined
|
||||||
const firstItemPath =
|
const firstItemPath = isSidebarItemLink(item.default[0])
|
||||||
item.default[0].type === "link"
|
|
||||||
? getLinkPath(item.default[0])
|
? getLinkPath(item.default[0])
|
||||||
: (item.default[0].type === "category" &&
|
: (item.default[0].type === "category" &&
|
||||||
breadcrumbOptions?.showCategories) ||
|
breadcrumbOptions?.showCategories) ||
|
||||||
@@ -76,7 +79,7 @@ export const Breadcrumbs = () => {
|
|||||||
breadcrumbOptions?.showCategories)
|
breadcrumbOptions?.showCategories)
|
||||||
) {
|
) {
|
||||||
tempBreadcrumbItems.set(
|
tempBreadcrumbItems.set(
|
||||||
sidebarActiveItem.parentItem.type === "link"
|
isSidebarItemLink(sidebarActiveItem.parentItem)
|
||||||
? getLinkPath(sidebarActiveItem.parentItem) || "#"
|
? getLinkPath(sidebarActiveItem.parentItem) || "#"
|
||||||
: "#",
|
: "#",
|
||||||
sidebarActiveItem.parentItem.chapterTitle ||
|
sidebarActiveItem.parentItem.chapterTitle ||
|
||||||
|
|||||||
@@ -1,193 +1,10 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { useMemo } from "react"
|
import React from "react"
|
||||||
import { Card, CardList, H2, useSidebar } from "../.."
|
import { useChildDocs, UseChildDocsProps } from "../.."
|
||||||
import { InteractiveSidebarItem, SidebarItem, SidebarItemLink } from "types"
|
|
||||||
import slugify from "slugify"
|
|
||||||
import { MDXComponents } from "../.."
|
|
||||||
|
|
||||||
const Hr = MDXComponents["hr"] as () => React.JSX.Element
|
export const ChildDocs = (props: UseChildDocsProps) => {
|
||||||
|
const { component } = useChildDocs(props)
|
||||||
|
|
||||||
type ChildDocsProps = {
|
return <>{component}</>
|
||||||
onlyTopLevel?: boolean
|
|
||||||
type?: "sidebar" | "item"
|
|
||||||
hideItems?: string[]
|
|
||||||
showItems?: string[]
|
|
||||||
hideTitle?: boolean
|
|
||||||
childLevel?: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ChildDocs = ({
|
|
||||||
onlyTopLevel = false,
|
|
||||||
hideItems = [],
|
|
||||||
showItems,
|
|
||||||
type = "sidebar",
|
|
||||||
hideTitle = false,
|
|
||||||
childLevel = 1,
|
|
||||||
}: ChildDocsProps) => {
|
|
||||||
const { currentItems, activeItem } = useSidebar()
|
|
||||||
const filterType = useMemo(() => {
|
|
||||||
return showItems !== undefined
|
|
||||||
? "show"
|
|
||||||
: hideItems.length > 0
|
|
||||||
? "hide"
|
|
||||||
: "all"
|
|
||||||
}, [showItems, hideItems])
|
|
||||||
|
|
||||||
const filterCondition = (item: SidebarItem): boolean => {
|
|
||||||
if (item.type === "separator") {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch (filterType) {
|
|
||||||
case "hide":
|
|
||||||
return (
|
|
||||||
(item.type !== "link" || !hideItems.includes(item.path)) &&
|
|
||||||
!hideItems.includes(item.title)
|
|
||||||
)
|
|
||||||
case "show":
|
|
||||||
return (
|
|
||||||
(item.type === "link" && showItems!.includes(item.path)) ||
|
|
||||||
showItems!.includes(item.title)
|
|
||||||
)
|
|
||||||
case "all":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const filterItems = (items: SidebarItem[]): SidebarItem[] => {
|
|
||||||
return items
|
|
||||||
.filter(filterCondition)
|
|
||||||
.map((item) => Object.assign({}, item))
|
|
||||||
.map((item) => {
|
|
||||||
if (
|
|
||||||
item.type !== "separator" &&
|
|
||||||
item.children &&
|
|
||||||
filterType === "hide"
|
|
||||||
) {
|
|
||||||
item.children = filterItems(item.children)
|
|
||||||
}
|
|
||||||
|
|
||||||
return item
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const filteredItems = useMemo(() => {
|
|
||||||
const targetItems =
|
|
||||||
type === "sidebar"
|
|
||||||
? currentItems
|
|
||||||
? Object.assign({}, currentItems)
|
|
||||||
: undefined
|
|
||||||
: {
|
|
||||||
default: [...(activeItem?.children || [])],
|
|
||||||
}
|
|
||||||
if (filterType === "all" || !targetItems) {
|
|
||||||
return targetItems
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
...targetItems,
|
|
||||||
default: filterItems(targetItems.default),
|
|
||||||
}
|
|
||||||
}, [currentItems, type, activeItem, filterItems])
|
|
||||||
|
|
||||||
const filterNonInteractiveItems = (
|
|
||||||
items: SidebarItem[] | undefined
|
|
||||||
): InteractiveSidebarItem[] => {
|
|
||||||
return (
|
|
||||||
(items?.filter(
|
|
||||||
(item) => item.type !== "separator"
|
|
||||||
) as InteractiveSidebarItem[]) || []
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getChildrenForLevel = (
|
|
||||||
item: InteractiveSidebarItem,
|
|
||||||
currentLevel = 1
|
|
||||||
): InteractiveSidebarItem[] | undefined => {
|
|
||||||
if (currentLevel === childLevel) {
|
|
||||||
return filterNonInteractiveItems(item.children)
|
|
||||||
}
|
|
||||||
if (!item.children) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const childrenResult: InteractiveSidebarItem[] = []
|
|
||||||
|
|
||||||
filterNonInteractiveItems(item.children).forEach((child) => {
|
|
||||||
const childChildren = getChildrenForLevel(child, currentLevel + 1)
|
|
||||||
|
|
||||||
if (!childChildren) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
childrenResult.push(...childChildren)
|
|
||||||
})
|
|
||||||
|
|
||||||
return childrenResult
|
|
||||||
}
|
|
||||||
|
|
||||||
const getTopLevelElms = (items?: SidebarItem[]) => {
|
|
||||||
return (
|
|
||||||
<CardList
|
|
||||||
items={
|
|
||||||
filterNonInteractiveItems(items).map((childItem) => {
|
|
||||||
const href =
|
|
||||||
childItem.type === "link"
|
|
||||||
? childItem.path
|
|
||||||
: childItem.children?.length
|
|
||||||
? (
|
|
||||||
childItem.children.find(
|
|
||||||
(item) => item.type === "link"
|
|
||||||
) as SidebarItemLink
|
|
||||||
)?.path
|
|
||||||
: "#"
|
|
||||||
return {
|
|
||||||
title: childItem.title,
|
|
||||||
href,
|
|
||||||
}
|
|
||||||
}) || []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getAllLevelsElms = (items?: SidebarItem[]) => {
|
|
||||||
const filteredItems = filterNonInteractiveItems(items)
|
|
||||||
return filteredItems.map((item, key) => {
|
|
||||||
const itemChildren = getChildrenForLevel(item)
|
|
||||||
const HeadingComponent = itemChildren?.length ? H2 : undefined
|
|
||||||
|
|
||||||
return (
|
|
||||||
<React.Fragment key={key}>
|
|
||||||
{HeadingComponent && (
|
|
||||||
<>
|
|
||||||
{!hideTitle && (
|
|
||||||
<HeadingComponent id={slugify(item.title)}>
|
|
||||||
{item.title}
|
|
||||||
</HeadingComponent>
|
|
||||||
)}
|
|
||||||
<CardList
|
|
||||||
items={
|
|
||||||
itemChildren?.map((childItem) => ({
|
|
||||||
title: childItem.title,
|
|
||||||
href: childItem.type === "link" ? childItem.path : "",
|
|
||||||
})) || []
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
{key !== filteredItems.length - 1 && <Hr />}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!HeadingComponent && item.type === "link" && (
|
|
||||||
<Card title={item.title} href={item.path} />
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const getElms = (items?: SidebarItem[]) => {
|
|
||||||
return onlyTopLevel ? getTopLevelElms(items) : getAllLevelsElms(items)
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{getElms(filteredItems?.default)}</>
|
|
||||||
}
|
}
|
||||||
@@ -120,3 +120,5 @@ export const MDXComponents: MDXComponentsType = {
|
|||||||
return <ZoomImg {...rest} />
|
return <ZoomImg {...rest} />
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const Hr = MDXComponents["hr"] as () => React.JSX.Element
|
||||||
@@ -30,6 +30,7 @@ export const SidebarItem = ({
|
|||||||
case "sub-category":
|
case "sub-category":
|
||||||
return <SidebarItemSubCategory item={item} {...props} />
|
return <SidebarItemSubCategory item={item} {...props} />
|
||||||
case "link":
|
case "link":
|
||||||
|
case "ref":
|
||||||
return <SidebarItemLink item={item} {...props} />
|
return <SidebarItemLink item={item} {...props} />
|
||||||
case "separator":
|
case "separator":
|
||||||
return <DottedSeparator />
|
return <DottedSeparator />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import React, { Suspense, useMemo, useRef } from "react"
|
import React, { Suspense, useMemo, useRef } from "react"
|
||||||
import { useSidebar } from "@/providers"
|
import { isSidebarItemLink, useSidebar } from "@/providers"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
import { Loading } from "@/components"
|
import { Loading } from "@/components"
|
||||||
import { SidebarItem } from "./Item"
|
import { SidebarItem } from "./Item"
|
||||||
@@ -139,7 +139,7 @@ export const Sidebar = ({
|
|||||||
const itemKey =
|
const itemKey =
|
||||||
item.type === "separator"
|
item.type === "separator"
|
||||||
? index
|
? index
|
||||||
: item.type === "link"
|
: isSidebarItemLink(item)
|
||||||
? `${item.path}-${index}`
|
? `${item.path}-${index}`
|
||||||
: `${item.title}-${index}`
|
: `${item.title}-${index}`
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from "./use-active-on-scroll"
|
export * from "./use-active-on-scroll"
|
||||||
|
export * from "./use-child-docs"
|
||||||
export * from "./use-click-outside"
|
export * from "./use-click-outside"
|
||||||
export * from "./use-collapsible"
|
export * from "./use-collapsible"
|
||||||
export * from "./use-collapsible-code-lines"
|
export * from "./use-collapsible-code-lines"
|
||||||
|
|||||||
@@ -0,0 +1,228 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import React, { useMemo } from "react"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardList,
|
||||||
|
H2,
|
||||||
|
H3,
|
||||||
|
H4,
|
||||||
|
Hr,
|
||||||
|
isSidebarItemLink,
|
||||||
|
useSidebar,
|
||||||
|
} from "../.."
|
||||||
|
import { InteractiveSidebarItem, SidebarItem, SidebarItemLink } from "types"
|
||||||
|
import slugify from "slugify"
|
||||||
|
import { MDXComponents } from "../.."
|
||||||
|
|
||||||
|
type HeadingComponent = (
|
||||||
|
props: React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
) => React.JSX.Element
|
||||||
|
|
||||||
|
export type UseChildDocsProps = {
|
||||||
|
onlyTopLevel?: boolean
|
||||||
|
type?: "sidebar" | "item"
|
||||||
|
hideItems?: string[]
|
||||||
|
showItems?: string[]
|
||||||
|
hideTitle?: boolean
|
||||||
|
titleLevel?: number
|
||||||
|
childLevel?: number
|
||||||
|
itemsPerRow?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useChildDocs = ({
|
||||||
|
onlyTopLevel = false,
|
||||||
|
hideItems = [],
|
||||||
|
showItems,
|
||||||
|
type = "sidebar",
|
||||||
|
hideTitle = false,
|
||||||
|
titleLevel = 2,
|
||||||
|
childLevel = 1,
|
||||||
|
itemsPerRow,
|
||||||
|
}: UseChildDocsProps) => {
|
||||||
|
const { currentItems, activeItem } = useSidebar()
|
||||||
|
const TitleHeaderComponent: HeadingComponent = useMemo(() => {
|
||||||
|
switch (titleLevel) {
|
||||||
|
case 3:
|
||||||
|
return H3
|
||||||
|
case 4:
|
||||||
|
return H4
|
||||||
|
case 5:
|
||||||
|
return MDXComponents["h5"] as HeadingComponent
|
||||||
|
case 6:
|
||||||
|
return MDXComponents["h6"] as HeadingComponent
|
||||||
|
default:
|
||||||
|
return H2
|
||||||
|
}
|
||||||
|
}, [titleLevel])
|
||||||
|
const filterType = useMemo(() => {
|
||||||
|
return showItems !== undefined
|
||||||
|
? "show"
|
||||||
|
: hideItems.length > 0
|
||||||
|
? "hide"
|
||||||
|
: "all"
|
||||||
|
}, [showItems, hideItems])
|
||||||
|
|
||||||
|
const filterCondition = (item: SidebarItem): boolean => {
|
||||||
|
if (item.type === "separator") {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch (filterType) {
|
||||||
|
case "hide":
|
||||||
|
return (
|
||||||
|
(!isSidebarItemLink(item) || !hideItems.includes(item.path)) &&
|
||||||
|
!hideItems.includes(item.title)
|
||||||
|
)
|
||||||
|
case "show":
|
||||||
|
return (
|
||||||
|
(isSidebarItemLink(item) && showItems!.includes(item.path)) ||
|
||||||
|
showItems!.includes(item.title)
|
||||||
|
)
|
||||||
|
case "all":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterItems = (items: SidebarItem[]): SidebarItem[] => {
|
||||||
|
return items
|
||||||
|
.filter(filterCondition)
|
||||||
|
.map((item) => Object.assign({}, item))
|
||||||
|
.map((item) => {
|
||||||
|
if (
|
||||||
|
item.type !== "separator" &&
|
||||||
|
item.children &&
|
||||||
|
filterType === "hide"
|
||||||
|
) {
|
||||||
|
item.children = filterItems(item.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredItems = useMemo(() => {
|
||||||
|
const targetItems =
|
||||||
|
type === "sidebar"
|
||||||
|
? currentItems
|
||||||
|
? Object.assign({}, currentItems)
|
||||||
|
: undefined
|
||||||
|
: {
|
||||||
|
default: [...(activeItem?.children || [])],
|
||||||
|
}
|
||||||
|
if (filterType === "all" || !targetItems) {
|
||||||
|
return targetItems
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...targetItems,
|
||||||
|
default: filterItems(targetItems.default),
|
||||||
|
}
|
||||||
|
}, [currentItems, type, activeItem, filterItems])
|
||||||
|
|
||||||
|
const filterNonInteractiveItems = (
|
||||||
|
items: SidebarItem[] | undefined
|
||||||
|
): InteractiveSidebarItem[] => {
|
||||||
|
return (
|
||||||
|
(items?.filter(
|
||||||
|
(item) => item.type !== "separator"
|
||||||
|
) as InteractiveSidebarItem[]) || []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getChildrenForLevel = (
|
||||||
|
item: InteractiveSidebarItem,
|
||||||
|
currentLevel = 1
|
||||||
|
): InteractiveSidebarItem[] | undefined => {
|
||||||
|
if (currentLevel === childLevel) {
|
||||||
|
return filterNonInteractiveItems(item.children)
|
||||||
|
}
|
||||||
|
if (!item.children) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const childrenResult: InteractiveSidebarItem[] = []
|
||||||
|
|
||||||
|
filterNonInteractiveItems(item.children).forEach((child) => {
|
||||||
|
const childChildren = getChildrenForLevel(child, currentLevel + 1)
|
||||||
|
|
||||||
|
if (!childChildren) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
childrenResult.push(...childChildren)
|
||||||
|
})
|
||||||
|
|
||||||
|
return childrenResult
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTopLevelElms = (items?: SidebarItem[]) => {
|
||||||
|
return (
|
||||||
|
<CardList
|
||||||
|
items={
|
||||||
|
filterNonInteractiveItems(items).map((childItem) => {
|
||||||
|
const href = isSidebarItemLink(childItem)
|
||||||
|
? childItem.path
|
||||||
|
: childItem.children?.length
|
||||||
|
? (
|
||||||
|
childItem.children.find((item) =>
|
||||||
|
isSidebarItemLink(item)
|
||||||
|
) as SidebarItemLink
|
||||||
|
)?.path
|
||||||
|
: "#"
|
||||||
|
return {
|
||||||
|
title: childItem.title,
|
||||||
|
href,
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
|
}
|
||||||
|
itemsPerRow={itemsPerRow}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getAllLevelsElms = (items?: SidebarItem[]) => {
|
||||||
|
const filteredItems = filterNonInteractiveItems(items)
|
||||||
|
return filteredItems.map((item, key) => {
|
||||||
|
const itemChildren = getChildrenForLevel(item)
|
||||||
|
const HeadingComponent = itemChildren?.length
|
||||||
|
? TitleHeaderComponent
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment key={key}>
|
||||||
|
{HeadingComponent && (
|
||||||
|
<>
|
||||||
|
{!hideTitle && (
|
||||||
|
<HeadingComponent id={slugify(item.title.toLowerCase())}>
|
||||||
|
{item.title}
|
||||||
|
</HeadingComponent>
|
||||||
|
)}
|
||||||
|
<CardList
|
||||||
|
items={
|
||||||
|
itemChildren?.map((childItem) => ({
|
||||||
|
title: childItem.title,
|
||||||
|
href: isSidebarItemLink(childItem) ? childItem.path : "",
|
||||||
|
})) || []
|
||||||
|
}
|
||||||
|
itemsPerRow={itemsPerRow}
|
||||||
|
/>
|
||||||
|
{key !== filteredItems.length - 1 && <Hr />}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{!HeadingComponent && isSidebarItemLink(item) && (
|
||||||
|
<Card title={item.title} href={item.path} />
|
||||||
|
)}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getElms = (items?: SidebarItem[]) => {
|
||||||
|
return onlyTopLevel ? getTopLevelElms(items) : getAllLevelsElms(items)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
items: filteredItems,
|
||||||
|
component: getElms(filteredItems?.default),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import React, {
|
|||||||
useMemo,
|
useMemo,
|
||||||
useState,
|
useState,
|
||||||
} from "react"
|
} from "react"
|
||||||
import { useSidebar } from "../Sidebar"
|
import { isSidebarItemLink, useSidebar } from "../Sidebar"
|
||||||
import { usePrevious } from "@uidotdev/usehooks"
|
import { usePrevious } from "@uidotdev/usehooks"
|
||||||
import { InteractiveSidebarItem, SidebarItem } from "types"
|
import { InteractiveSidebarItem, SidebarItem } from "types"
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
return children[0].type === "link"
|
return isSidebarItemLink(children[0])
|
||||||
? {
|
? {
|
||||||
...children[0],
|
...children[0],
|
||||||
parent: item,
|
parent: item,
|
||||||
@@ -69,7 +69,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
): SidebarItemWithParent[] | undefined => {
|
): SidebarItemWithParent[] | undefined => {
|
||||||
return item.children?.filter(
|
return item.children?.filter(
|
||||||
(childItem) =>
|
(childItem) =>
|
||||||
childItem.type === "link" ||
|
isSidebarItemLink(childItem) ||
|
||||||
(childItem.type !== "separator" &&
|
(childItem.type !== "separator" &&
|
||||||
getChildrenWithPages(childItem)?.length)
|
getChildrenWithPages(childItem)?.length)
|
||||||
) as SidebarItemWithParent[]
|
) as SidebarItemWithParent[]
|
||||||
@@ -95,7 +95,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
parent: item,
|
parent: item,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (item.type === "link") {
|
} else if (isSidebarItemLink(item)) {
|
||||||
foundItem = item
|
foundItem = item
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.type === "link") {
|
if (isSidebarItemLink(item)) {
|
||||||
foundItem = item
|
foundItem = item
|
||||||
} else if (item.children?.length) {
|
} else if (item.children?.length) {
|
||||||
const childItem = getNextItem(item.children, -1)
|
const childItem = getNextItem(item.children, -1)
|
||||||
@@ -139,7 +139,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
result.foundActive = currentItems.some((item, index) => {
|
result.foundActive = currentItems.some((item, index) => {
|
||||||
if (item.type === "link" && item.path === activePath) {
|
if (isSidebarItemLink(item) && item.path === activePath) {
|
||||||
if (index !== 0) {
|
if (index !== 0) {
|
||||||
result.prevItem = getPrevItem(currentItems, index)
|
result.prevItem = getPrevItem(currentItems, index)
|
||||||
}
|
}
|
||||||
@@ -161,8 +161,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
result.prevItem = childrenResult.prevItem
|
result.prevItem = childrenResult.prevItem
|
||||||
result.nextItem = childrenResult.nextItem
|
result.nextItem = childrenResult.nextItem
|
||||||
if (!result.prevItem) {
|
if (!result.prevItem) {
|
||||||
result.prevItem =
|
result.prevItem = isSidebarItemLink(item)
|
||||||
item.type === "link" ? item : getPrevItem(currentItems, index)
|
? item
|
||||||
|
: getPrevItem(currentItems, index)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result.nextItem && index !== currentItems.length - 1) {
|
if (!result.nextItem && index !== currentItems.length - 1) {
|
||||||
@@ -186,7 +187,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
result.prevItem
|
result.prevItem
|
||||||
? {
|
? {
|
||||||
title: result.prevItem.title,
|
title: result.prevItem.title,
|
||||||
link: result.prevItem.type === "link" ? result.prevItem.path : "",
|
link: isSidebarItemLink(result.prevItem)
|
||||||
|
? result.prevItem.path
|
||||||
|
: "",
|
||||||
parentTitle:
|
parentTitle:
|
||||||
result.prevItem.parent?.type !== "separator"
|
result.prevItem.parent?.type !== "separator"
|
||||||
? result.prevItem.parent?.title
|
? result.prevItem.parent?.title
|
||||||
@@ -198,7 +201,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
|
|||||||
result.nextItem
|
result.nextItem
|
||||||
? {
|
? {
|
||||||
title: result.nextItem.title,
|
title: result.nextItem.title,
|
||||||
link: result.nextItem.type === "link" ? result.nextItem.path : "",
|
link: isSidebarItemLink(result.nextItem)
|
||||||
|
? result.nextItem.path
|
||||||
|
: "",
|
||||||
parentTitle:
|
parentTitle:
|
||||||
result.nextItem.parent?.type !== "separator"
|
result.nextItem.parent?.type !== "separator"
|
||||||
? result.nextItem.parent?.title
|
? result.nextItem.parent?.title
|
||||||
|
|||||||
@@ -95,13 +95,26 @@ export type ActionType =
|
|||||||
|
|
||||||
type LinksMap = Map<string, SidebarItemLinkWithParent>
|
type LinksMap = Map<string, SidebarItemLinkWithParent>
|
||||||
|
|
||||||
|
export const isSidebarItemLink = (
|
||||||
|
item: SidebarItem | undefined,
|
||||||
|
checkRef = true
|
||||||
|
): item is SidebarItemLink => {
|
||||||
|
return (
|
||||||
|
item !== undefined &&
|
||||||
|
(item.type === "link" || (checkRef && item.type === "ref"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const areItemsEqual = (itemA: SidebarItem, itemB: SidebarItem): boolean => {
|
const areItemsEqual = (itemA: SidebarItem, itemB: SidebarItem): boolean => {
|
||||||
if (itemA.type === "separator" || itemB.type === "separator") {
|
if (itemA.type === "separator" || itemB.type === "separator") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const hasSameTitle = itemA.title === itemB.title
|
const hasSameTitle = itemA.title === itemB.title
|
||||||
const hasSamePath =
|
const hasSamePath =
|
||||||
itemA.type === "link" && itemB.type === "link" && itemA.path === itemB.path
|
isSidebarItemLink(itemA) &&
|
||||||
|
isSidebarItemLink(itemB) &&
|
||||||
|
itemA.type === itemB.type &&
|
||||||
|
itemA.path === itemB.path
|
||||||
|
|
||||||
return hasSameTitle || hasSamePath
|
return hasSameTitle || hasSamePath
|
||||||
}
|
}
|
||||||
@@ -116,8 +129,8 @@ const findItem = (
|
|||||||
if (i.type === "separator") {
|
if (i.type === "separator") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (areItemsEqual(item as SidebarItem, i) && i.type === "link") {
|
if (areItemsEqual(item as SidebarItem, i)) {
|
||||||
foundItem = i
|
foundItem = i as SidebarItemLink
|
||||||
} else if (checkChildren && i.children) {
|
} else if (checkChildren && i.children) {
|
||||||
foundItem = findItem(i.children, item)
|
foundItem = findItem(i.children, item)
|
||||||
if (foundItem && !foundItem.parentItem) {
|
if (foundItem && !foundItem.parentItem) {
|
||||||
@@ -143,7 +156,7 @@ const getLinksMap = (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.type === "link") {
|
if (isSidebarItemLink(item)) {
|
||||||
map.set(item.path, {
|
map.set(item.path, {
|
||||||
...item,
|
...item,
|
||||||
parentItem,
|
parentItem,
|
||||||
@@ -246,7 +259,7 @@ export const reducer = (
|
|||||||
: [...(i.children || []), ...items],
|
: [...(i.children || []), ...items],
|
||||||
loaded: parent.changeLoaded
|
loaded: parent.changeLoaded
|
||||||
? true
|
? true
|
||||||
: i.type === "link"
|
: isSidebarItemLink(i)
|
||||||
? i.loaded
|
? i.loaded
|
||||||
: true,
|
: true,
|
||||||
}
|
}
|
||||||
@@ -355,8 +368,8 @@ export const SidebarProvider = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isLinkActive = useCallback(
|
const isLinkActive = useCallback(
|
||||||
(item: SidebarItem, checkChildren = false): boolean => {
|
(item: SidebarItem, checkChildren = false, checkRef = true): boolean => {
|
||||||
if (item.type !== "link") {
|
if (!isSidebarItemLink(item, checkRef)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,24 +412,28 @@ export const SidebarProvider = ({
|
|||||||
if (item.type === "separator") {
|
if (item.type === "separator") {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (item.isChildSidebar && isLinkActive(item)) {
|
if (item.isChildSidebar && isLinkActive(item, false, false)) {
|
||||||
currentSidebar = item
|
currentSidebar = item
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!item.children?.length) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!currentSidebar && item.children?.length) {
|
|
||||||
const childSidebar =
|
const childSidebar =
|
||||||
getCurrentSidebar(item.children) ||
|
getCurrentSidebar(item.children) ||
|
||||||
(activePath
|
(activePath
|
||||||
? findItem(item.children, {
|
? findItem(item.children, {
|
||||||
path: activePath || undefined,
|
path: activePath,
|
||||||
type: "link",
|
type: "link",
|
||||||
})
|
})
|
||||||
: undefined)
|
: undefined)
|
||||||
|
|
||||||
if (childSidebar) {
|
currentSidebar = childSidebar
|
||||||
currentSidebar = childSidebar.isChildSidebar ? childSidebar : item
|
? childSidebar.isChildSidebar
|
||||||
}
|
? childSidebar
|
||||||
}
|
: item
|
||||||
|
: undefined
|
||||||
|
|
||||||
return currentSidebar !== undefined
|
return currentSidebar !== undefined
|
||||||
})
|
})
|
||||||
@@ -434,7 +451,7 @@ export const SidebarProvider = ({
|
|||||||
const previousSidebar = currentItems.previousSidebar || items
|
const previousSidebar = currentItems.previousSidebar || items
|
||||||
|
|
||||||
const backItem = previousSidebar.default.find(
|
const backItem = previousSidebar.default.find(
|
||||||
(item) => item.type === "link" && !item.isChildSidebar
|
(item) => isSidebarItemLink(item) && !item.isChildSidebar
|
||||||
) as SidebarItemLink
|
) as SidebarItemLink
|
||||||
|
|
||||||
if (!backItem) {
|
if (!backItem) {
|
||||||
@@ -472,7 +489,7 @@ export const SidebarProvider = ({
|
|||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
if (getScrolledTop(resolvedScrollableElement) === 0) {
|
if (getScrolledTop(resolvedScrollableElement) === 0) {
|
||||||
const firstItemPath =
|
const firstItemPath =
|
||||||
items.default.length && items.default[0].type === "link"
|
items.default.length && isSidebarItemLink(items.default[0])
|
||||||
? items.default[0].path
|
? items.default[0].path
|
||||||
: ""
|
: ""
|
||||||
setActivePath(firstItemPath)
|
setActivePath(firstItemPath)
|
||||||
@@ -548,8 +565,8 @@ export const SidebarProvider = ({
|
|||||||
) {
|
) {
|
||||||
const { children, ...parentItem } = currentSidebar
|
const { children, ...parentItem } = currentSidebar
|
||||||
const hasPreviousSidebar =
|
const hasPreviousSidebar =
|
||||||
currentItems?.previousSidebar?.parentItem?.type === "link" &&
|
isSidebarItemLink(currentItems?.previousSidebar?.parentItem) &&
|
||||||
parentItem.type === "link" &&
|
isSidebarItemLink(parentItem) &&
|
||||||
currentItems.previousSidebar.parentItem.path !== parentItem.path
|
currentItems.previousSidebar.parentItem.path !== parentItem.path
|
||||||
|
|
||||||
setCurrentItems({
|
setCurrentItems({
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const apiKey = [
|
export const apiKey = [
|
||||||
{
|
{
|
||||||
"title": "Use a Publishable API Key in the Storefront",
|
"title": "Use a Publishable API Key in the Storefront",
|
||||||
"path": "/app/storefront-development/publishable-api-keys"
|
"path": "/storefront-development/publishable-api-keys"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "createApiKeysStep",
|
"title": "createApiKeysStep",
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
export const auth = [
|
export const auth = [
|
||||||
{
|
{
|
||||||
"title": "Log-out Customer in Storefront",
|
"title": "Log-out Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/log-out"
|
"path": "/storefront-development/customers/log-out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Login Customer in Storefront",
|
"title": "Login Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/login"
|
"path": "/storefront-development/customers/login"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Register Customer in Storefront",
|
"title": "Register Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/register"
|
"path": "/storefront-development/customers/register"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Reset Customer Password in Storefront",
|
"title": "Reset Customer Password in Storefront",
|
||||||
"path": "/app/storefront-development/customers/reset-password"
|
"path": "/storefront-development/customers/reset-password"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve Customer in Storefront",
|
"title": "Retrieve Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/retrieve"
|
"path": "/storefront-development/customers/retrieve"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Third-Party or Social Login in Storefront",
|
"title": "Third-Party or Social Login in Storefront",
|
||||||
"path": "/app/storefront-development/customers/third-party-login"
|
"path": "/storefront-development/customers/third-party-login"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "setAuthAppMetadataStep",
|
"title": "setAuthAppMetadataStep",
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
export const cart = [
|
export const cart = [
|
||||||
{
|
{
|
||||||
"title": "Create Cart Context in Storefront",
|
"title": "Create Cart Context in Storefront",
|
||||||
"path": "/app/storefront-development/cart/context"
|
"path": "/storefront-development/cart/context"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Create Cart in Storefront",
|
"title": "Create Cart in Storefront",
|
||||||
"path": "/app/storefront-development/cart/create"
|
"path": "/storefront-development/cart/create"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Manage Cart's Items in Storefront",
|
"title": "Manage Cart's Items in Storefront",
|
||||||
"path": "/app/storefront-development/cart/manage-items"
|
"path": "/storefront-development/cart/manage-items"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve Cart in Storefront",
|
"title": "Retrieve Cart in Storefront",
|
||||||
"path": "/app/storefront-development/cart/retrieve"
|
"path": "/storefront-development/cart/retrieve"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Update Cart in Storefront",
|
"title": "Update Cart in Storefront",
|
||||||
"path": "/app/storefront-development/cart/update"
|
"path": "/storefront-development/cart/update"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 2: Enter Address",
|
"title": "Checkout Step 2: Enter Address",
|
||||||
"path": "/app/storefront-development/checkout/address"
|
"path": "/storefront-development/checkout/address"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 5: Complete Cart",
|
"title": "Checkout Step 5: Complete Cart",
|
||||||
"path": "/app/storefront-development/checkout/complete-cart"
|
"path": "/storefront-development/checkout/complete-cart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 1: Enter Email",
|
"title": "Checkout Step 1: Enter Email",
|
||||||
"path": "/app/storefront-development/checkout/email"
|
"path": "/storefront-development/checkout/email"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 4: Choose Payment Provider",
|
"title": "Checkout Step 4: Choose Payment Provider",
|
||||||
"path": "/app/storefront-development/checkout/payment"
|
"path": "/storefront-development/checkout/payment"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Payment with Stripe in React Storefront",
|
"title": "Payment with Stripe in React Storefront",
|
||||||
"path": "/app/storefront-development/checkout/payment/stripe"
|
"path": "/storefront-development/checkout/payment/stripe"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 3: Choose Shipping Method",
|
"title": "Checkout Step 3: Choose Shipping Method",
|
||||||
"path": "/app/storefront-development/checkout/shipping"
|
"path": "/storefront-development/checkout/shipping"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "addShippingMethodToCartStep",
|
"title": "addShippingMethodToCartStep",
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
export const customer = [
|
export const customer = [
|
||||||
{
|
{
|
||||||
"title": "Manage Customer Addresses in Storefront",
|
"title": "Manage Customer Addresses in Storefront",
|
||||||
"path": "/app/storefront-development/customers/addresses"
|
"path": "/storefront-development/customers/addresses"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Customer Context in Storefront",
|
"title": "Customer Context in Storefront",
|
||||||
"path": "/app/storefront-development/customers/context"
|
"path": "/storefront-development/customers/context"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Log-out Customer in Storefront",
|
"title": "Log-out Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/log-out"
|
"path": "/storefront-development/customers/log-out"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Login Customer in Storefront",
|
"title": "Login Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/login"
|
"path": "/storefront-development/customers/login"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Edit Customer Profile in Storefront",
|
"title": "Edit Customer Profile in Storefront",
|
||||||
"path": "/app/storefront-development/customers/profile"
|
"path": "/storefront-development/customers/profile"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Register Customer in Storefront",
|
"title": "Register Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/register"
|
"path": "/storefront-development/customers/register"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Reset Customer Password in Storefront",
|
"title": "Reset Customer Password in Storefront",
|
||||||
"path": "/app/storefront-development/customers/reset-password"
|
"path": "/storefront-development/customers/reset-password"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve Customer in Storefront",
|
"title": "Retrieve Customer in Storefront",
|
||||||
"path": "/app/storefront-development/customers/retrieve"
|
"path": "/storefront-development/customers/retrieve"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Third-Party or Social Login in Storefront",
|
"title": "Third-Party or Social Login in Storefront",
|
||||||
"path": "/app/storefront-development/customers/third-party-login"
|
"path": "/storefront-development/customers/third-party-login"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "findOrCreateCustomerStep",
|
"title": "findOrCreateCustomerStep",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const fulfillment = [
|
export const fulfillment = [
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 3: Choose Shipping Method",
|
"title": "Checkout Step 3: Choose Shipping Method",
|
||||||
"path": "/app/storefront-development/checkout/shipping"
|
"path": "/storefront-development/checkout/shipping"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "validateCartShippingOptionsStep",
|
"title": "validateCartShippingOptionsStep",
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
export * from "./product.js"
|
export * from "./product.js"
|
||||||
export * from "./tax.js"
|
export * from "./server.js"
|
||||||
export * from "./storefront.js"
|
export * from "./storefront.js"
|
||||||
export * from "./order.js"
|
|
||||||
export * from "./payment.js"
|
export * from "./payment.js"
|
||||||
|
export * from "./cart.js"
|
||||||
|
export * from "./order.js"
|
||||||
export * from "./stripe.js"
|
export * from "./stripe.js"
|
||||||
export * from "./fulfillment.js"
|
export * from "./fulfillment.js"
|
||||||
export * from "./customer.js"
|
export * from "./customer.js"
|
||||||
export * from "./auth.js"
|
export * from "./tax.js"
|
||||||
export * from "./pricing.js"
|
|
||||||
export * from "./product-collection.js"
|
|
||||||
export * from "./inventory.js"
|
export * from "./inventory.js"
|
||||||
export * from "./publishable-api-key.js"
|
export * from "./pricing.js"
|
||||||
export * from "./region.js"
|
|
||||||
export * from "./api-key.js"
|
export * from "./api-key.js"
|
||||||
export * from "./step.js"
|
export * from "./query.js"
|
||||||
|
export * from "./publishable-api-key.js"
|
||||||
|
export * from "./auth.js"
|
||||||
|
export * from "./region.js"
|
||||||
|
export * from "./product-collection.js"
|
||||||
export * from "./remote-link.js"
|
export * from "./remote-link.js"
|
||||||
export * from "./sales-channel.js"
|
|
||||||
export * from "./workflow.js"
|
export * from "./workflow.js"
|
||||||
export * from "./remote-query.js"
|
export * from "./sales-channel.js"
|
||||||
|
export * from "./product-category.js"
|
||||||
|
export * from "./step.js"
|
||||||
|
export * from "./promotion.js"
|
||||||
export * from "./event-bus.js"
|
export * from "./event-bus.js"
|
||||||
export * from "./store.js"
|
export * from "./store.js"
|
||||||
export * from "./logger.js"
|
|
||||||
export * from "./promotion.js"
|
|
||||||
export * from "./locking.js"
|
|
||||||
export * from "./file.js"
|
export * from "./file.js"
|
||||||
export * from "./user.js"
|
|
||||||
export * from "./product-category.js"
|
|
||||||
export * from "./stock-location.js"
|
export * from "./stock-location.js"
|
||||||
export * from "./cart.js"
|
export * from "./locking.js"
|
||||||
export * from "./query.js"
|
export * from "./user.js"
|
||||||
|
export * from "./remote-query.js"
|
||||||
|
export * from "./logger.js"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
export const inventory = [
|
export const inventory = [
|
||||||
{
|
{
|
||||||
"title": "Retrieve Product Variant's Inventory in Storefront",
|
"title": "Retrieve Product Variant's Inventory in Storefront",
|
||||||
"path": "/app/storefront-development/products/inventory"
|
"path": "/storefront-development/products/inventory"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "confirmInventoryStep",
|
"title": "confirmInventoryStep",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const order = [
|
export const order = [
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 5: Complete Cart",
|
"title": "Checkout Step 5: Complete Cart",
|
||||||
"path": "/app/storefront-development/checkout/complete-cart"
|
"path": "/storefront-development/checkout/complete-cart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "addOrderTransactionStep",
|
"title": "addOrderTransactionStep",
|
||||||
|
|||||||
@@ -1,15 +1,19 @@
|
|||||||
export const payment = [
|
export const payment = [
|
||||||
|
{
|
||||||
|
"title": "Customize the Stripe Integration in the Next.js Starter",
|
||||||
|
"path": "/nextjs-starter/guides/customize-stripe"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 5: Complete Cart",
|
"title": "Checkout Step 5: Complete Cart",
|
||||||
"path": "/app/storefront-development/checkout/complete-cart"
|
"path": "/storefront-development/checkout/complete-cart"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Checkout Step 4: Choose Payment Provider",
|
"title": "Checkout Step 4: Choose Payment Provider",
|
||||||
"path": "/app/storefront-development/checkout/payment"
|
"path": "/storefront-development/checkout/payment"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Payment with Stripe in React Storefront",
|
"title": "Payment with Stripe in React Storefront",
|
||||||
"path": "/app/storefront-development/checkout/payment/stripe"
|
"path": "/storefront-development/checkout/payment/stripe"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "createPaymentCollectionsStep",
|
"title": "createPaymentCollectionsStep",
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
export const pricing = [
|
export const pricing = [
|
||||||
{
|
{
|
||||||
"title": "Get Variant Prices",
|
"title": "Get Variant Prices",
|
||||||
"path": "/app/commerce-modules/product/guides/price"
|
"path": "/commerce-modules/product/guides/price"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Get Variant Price with Taxes",
|
"title": "Get Variant Price with Taxes",
|
||||||
"path": "/app/commerce-modules/product/guides/price-with-taxes"
|
"path": "/commerce-modules/product/guides/price-with-taxes"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Example: Show Sale Price",
|
"title": "Example: Show Sale Price",
|
||||||
"path": "/app/storefront-development/products/price/examples/sale-price"
|
"path": "/storefront-development/products/price/examples/sale-price"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Example: Show Variant's Price",
|
"title": "Example: Show Variant's Price",
|
||||||
"path": "/app/storefront-development/products/price/examples/show-price"
|
"path": "/storefront-development/products/price/examples/show-price"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Example: Show Price with Taxes",
|
"title": "Example: Show Price with Taxes",
|
||||||
"path": "/app/storefront-development/products/price/examples/tax-price"
|
"path": "/storefront-development/products/price/examples/tax-price"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve Product Variant's Prices in Storefront",
|
"title": "Retrieve Product Variant's Prices in Storefront",
|
||||||
"path": "/app/storefront-development/products/price"
|
"path": "/storefront-development/products/price"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "createShippingOptionsPriceSetsStep",
|
"title": "createShippingOptionsPriceSetsStep",
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
export const productCategory = [
|
export const productCategory = [
|
||||||
{
|
{
|
||||||
"title": "List Product Categories in Storefront",
|
"title": "List Product Categories in Storefront",
|
||||||
"path": "/app/storefront-development/products/categories/list"
|
"path": "/storefront-development/products/categories/list"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve Nested Categories in Storefront",
|
"title": "Retrieve Nested Categories in Storefront",
|
||||||
"path": "/app/storefront-development/products/categories/nested-categories"
|
"path": "/storefront-development/products/categories/nested-categories"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve a Category's Products in Storefront",
|
"title": "Retrieve a Category's Products in Storefront",
|
||||||
"path": "/app/storefront-development/products/categories/products"
|
"path": "/storefront-development/products/categories/products"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Retrieve a Category in Storefront",
|
"title": "Retrieve a Category in Storefront",
|
||||||
"path": "/app/storefront-development/products/categories/retrieve"
|
"path": "/storefront-development/products/categories/retrieve"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user