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:
Shahed Nasser authored and GitHub committed 2024-12-26 10:32:16 +02:00
1 parent c8f9938865
commit ebca8fed28
112 files changed
+9362 -7628

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 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])
}
) )
const apiKeys = await apiKeyModuleService.listApiKeys() 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
The Auth Module supports a variety of authentication methods, such as authenticating with third-party services and social platforms.
```ts
// 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) {
return res.json({ location })
} }
// in callback API route return new StepResponse({ authIdentity }, authIdentity?.id)
const { success, authIdentity } = await authModuleService.validateCallback( },
"google", async (authIdentityId, { container }) => {
{ if (!authIdentityId) {
url: req.url, return
headers: req.headers, }
query: req.query,
body: req.body, const authModuleService = container.resolve(Modules.AUTH)
authScope: "admin",
protocol: req.protocol, await authModuleService.deleteAuthIdentities([authIdentityId])
} as AuthenticationInput }
)
export const authenticateUserWorkflow = createWorkflow(
"authenticate-user",
(input: Input) => {
const { authIdentity } = authenticateUserStep(input)
return new WorkflowResponse({
authIdentity,
})
}
) )
``` ```
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,79 +6,51 @@ 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.
For example:
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)
```ts
const cart = await cartModuleService.createCarts({ const cart = await cartModuleService.createCarts({
currency_code: "usd", currency_code: "usd",
shipping_address: { shipping_address: {
@@ -93,33 +65,106 @@ const cart = await cartModuleService.createCarts({
}, },
], ],
}) })
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,
})
}
)
``` ```
### Apply Promotions You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals. <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 lineAdjustments = await cartModuleService.addLineItemAdjustments({ import type {
item_id: "cali_123", MedusaRequest,
code: "50OFF", MedusaResponse,
amount: 500, } from "@medusajs/framework/http"
}) import { createCartWorkflow } from "../../workflows/create-cart"
const shippingAdjustments = export async function GET(
await cartModuleService.addShippingMethodAdjustments({ req: MedusaRequest,
shipping_method_id: "casm_123", res: MedusaResponse
code: "FREESHIPPING", ) {
amount: 1000, const { result } = await createCartWorkflow(req.scope)
}) .run()
res.send(result)
}
``` ```
### Cart Context and Scoping </CodeTab>
<CodeTab label="Subscriber" value="subscriber">
A cart is scoped to a sales channel, region, and a customer. ```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"
The Medusa application links the Cart Module to each of their respective modules, providing features like: export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createCartWorkflow(container)
.run()
- Checking product availability in a sales channel. console.log(result)
- Retrieving pricing per region. }
- Applying promotions based on the customer's group.
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 currency = await currencyModuleService
.retrieveCurrency("usd")
return new StepResponse({ currency })
}
) )
const currencies = await currencyModuleService.listCurrencies() 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 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])
}
) )
const customers = await customerModuleService.listCustomers() 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,
})
```
@@ -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,79 +6,51 @@ 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.
For example:
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)
```ts
const fulfillment = await fulfillmentModuleService.createFulfillment({ const fulfillment = await fulfillmentModuleService.createFulfillment({
location_id: "loc_123", location_id: "loc_123",
provider_id: "webshipper", provider_id: "webshipper",
@@ -98,70 +70,112 @@ const fulfillment = await fulfillmentModuleService.createFulfillment({
labels: [], labels: [],
order: {}, order: {},
}) })
```
### Integrate Third-Party Fulfillment Providers return new StepResponse({ fulfillment }, fulfillment.id)
},
async (fulfillmentId, { container }) => {
if (!fulfillmentId) {
return
}
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
Use third-party fulfillment providers to provide customers with shipping options and fulfill their orders. await fulfillmentModuleService.deleteFulfillment(fulfillmentId)
}
)
```ts export const createFulfillmentWorkflow = createWorkflow(
const shippingOption = await fulfillmentModuleService.createShippingOptions({ "create-fulfillment",
// ... () => {
provider_id: "webshipper", const { fulfillment } = createFulfillmentStep()
return new WorkflowResponse({
fulfillment,
}) })
}
)
``` ```
### Restrict By Location and Rules You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
Shipping options can be restricted to specific geographical locations. You can also specify custom rules to restrict shipping options. <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 serviceZone = await fulfillmentModuleService.createServiceZones({ import type {
name: "US", MedusaRequest,
fulfillment_set_id: "fset_123", MedusaResponse,
geo_zones: [ } from "@medusajs/framework/http"
{ import { createFulfillmentWorkflow } from "../../workflows/create-fuilfillment"
type: "country",
country_code: "us",
},
],
})
const shippingOption = await fulfillmentModuleService.createShippingOptions({ export async function GET(
name: "Express Shipping", req: MedusaRequest,
// restrict location res: MedusaResponse
service_zone_id: serviceZone.id, ) {
// restrict by custom rules const { result } = await createFulfillmentWorkflow(req.scope)
rules: [ .run()
{
field: "customer_group", res.send(result)
operator: "eq", }
value: "vip",
},
],
// ...
})
``` ```
### Support Different Fulfillment Forms </CodeTab>
<CodeTab label="Subscriber" value="subscriber">
Support various fulfillment forms, such as shipping or pick up. ```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 fulfillmentSets = await fulfillmentModuleService.createFulfillmentSets([ event: { data },
{ container,
name: "Shipping", }: SubscriberArgs<{ id: string }>) {
type: "shipping", const { result } = await createFulfillmentWorkflow(container)
}, .run()
{
name: "Pick-up", console.log(result)
type: "pick-up", }
},
]) 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 { createFulfillmentWorkflow } from "../workflows/create-fuilfillment"
export default async function myCustomJob(
container: MedusaContainer
) {
const { result } = await createFulfillmentWorkflow(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).
--- ---
## 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 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])
}
) )
const inventoryItems = await inventoryModuleService.listInventoryItems({}) 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,
}) })
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,
})
}
)
``` ```
### Apply Promotions You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
Apply promotions or discounts to the order's items and shipping methods by adding adjustment lines that are factored into their subtotals. <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 lineAdjustments = await orderModuleService.createOrderLineItemAdjustments({ import type {
item_id: "cali_123", MedusaRequest,
code: "50OFF", MedusaResponse,
amount: 500, } from "@medusajs/framework/http"
}) import { createDraftOrderWorkflow } from "../../workflows/create-draft-order"
const shippingAdjustments = export async function GET(
await orderModuleService.createOrderShippingMethodAdjustments({ req: MedusaRequest,
shipping_method_id: "casm_123", res: MedusaResponse
code: "FREESHIPPING", ) {
amount: 1000, const { result } = await createDraftOrderWorkflow(req.scope)
}) .run()
res.send(result)
}
``` ```
### Returns, Exchanges, and Claims </CodeTab>
<CodeTab label="Subscriber" value="subscriber">
Return or exchange items, with version-based control over the order's timeline. ```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"
```ts export default async function handleUserCreated({
const orderReturn = await orderModuleService.createReturn({ event: { data },
order_id: "order_123", container,
items: [{ }: SubscriberArgs<{ id: string }>) {
id: "orditem_123", const { result } = await createDraftOrderWorkflow(container)
quantity: 1, .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 paymentCollection = await paymentModuleService.createPaymentCollections({
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])
}
) )
const payment_collections = export const createPaymentCollectionWorkflow = createWorkflow(
await paymentModuleService.listPaymentCollections() "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,81 +6,52 @@ 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:
export const highlights = [
["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)
```ts
const priceSet = await pricingModuleService.createPriceSets({ const priceSet = await pricingModuleService.createPriceSets({
prices: [ prices: [
{ {
@@ -96,67 +67,106 @@ const priceSet = await pricingModuleService.createPriceSets({
}, },
], ],
}) })
```
### 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)
await pricingModuleService.deletePriceSets([priceSetId])
}
)
export const createPriceSetWorkflow = createWorkflow(
"create-price-set",
() => {
const { priceSet } = createPriceSetStep()
return new WorkflowResponse({
priceSet,
}) })
```
### Price Lists
Group prices and apply them only in specific conditions with price lists.
You can also use them to override existing prices for specified conditions, or create a sale.
```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([
}, },
}, },
], ],
},
])
```
### Product Organization
The Product Module provides different data models used to organize products, including categories, collections, tags, and more.
```ts
const category = await productService.createProductCategories({
name: "Shirts",
}) })
const products = await productService.updateProducts([ return new StepResponse({ product }, product.id)
{
id: product.id,
categories: [
{
id: category.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,
})
}
)
``` ```
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 { createProductWorkflow } from "../../workflows/create-product"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result } = await createProductWorkflow(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 { 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 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)
}
) )
const promotions = await promotionModuleService.listPromotions() 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 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])
}
) )
const regions = await regionModuleService.listRegions() 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.
@@ -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,13 +28,41 @@ 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. ---
## How to Use Sales Channel Module's Service
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)
```ts
const salesChannels = await salesChannelModuleService.createSalesChannels([ const salesChannels = await salesChannelModuleService.createSalesChannels([
{ {
name: "B2B", name: "B2B",
@@ -96,16 +71,108 @@ const salesChannels = await salesChannelModuleService.createSalesChannels([
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.
@@ -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 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])
}
) )
const stockLocations = await stockLocationModuleService.listStockLocations({}) 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 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])
}
) )
const stores = await storeModuleService.listStores() 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 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])
}
) )
const taxRegions = await taxModuleService.listTaxRegions() 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 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])
}
) )
const users = await userModuleService.listUsers() 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
+27 -42
View File
@@ -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
-3
View File
@@ -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()
+40 -5
View File
@@ -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",
+47 -8
View File
@@ -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",
+54 -10
View File
@@ -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",
+46 -4
View File
@@ -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",
+54 -10
View File
@@ -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",
+48 -5
View File
@@ -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",
+47 -5
View File
@@ -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",
+49 -2
View File
@@ -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",
+48 -9
View File
@@ -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",
+47 -5
View File
@@ -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",
+62 -13
View File
@@ -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",
+54 -10
View File
@@ -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",
+47 -5
View File
@@ -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",
+47 -5
View File
@@ -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",
+47 -5
View File
@@ -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",
+47 -5
View File
@@ -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",
+45 -7
View File
@@ -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",
+43 -6
View File
@@ -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
View File
@@ -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 -1
View File
@@ -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",
+6 -6
View File
@@ -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",
+11 -11
View File
@@ -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",
+9 -9
View File
@@ -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 -1
View File
@@ -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",
+18 -17
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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",
+7 -3
View File
@@ -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",
+6 -6
View File
@@ -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