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
+9465 -7731

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({
entity: "api_key",
fields: [
"sales_channels.*"
]
"sales_channels.*",
],
})
// apiKeys.sales_channels
@@ -55,8 +55,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: apiKeys } = useQueryGraphStep({
entity: "api_key",
fields: [
"sales_channels.*"
]
"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 = {
title: `API Key Module`,
@@ -6,132 +6,153 @@ export const metadata = {
# {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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.API_KEY", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-api-key.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const apiKeyModuleService = container.resolve(
Modules.API_KEY
)
const createApiKeyStep = createStep(
"create-api-key",
async ({}, { container }) => {
const apiKeyModuleService = container.resolve(Modules.API_KEY)
const apiKeys = await apiKeyModuleService.listApiKeys()
})
const apiKey = await apiKeyModuleService.createApiKeys({
title: "Publishable API key",
type: "publishable",
created_by: "user_123",
})
return new StepResponse({ apiKey }, apiKey.id)
},
async (apiKeyId, { container }) => {
const apiKeyModuleService = container.resolve(Modules.API_KEY)
await apiKeyModuleService.deleteApiKeys([apiKeyId])
}
)
export const createApiKeyWorkflow = createWorkflow(
"create-api-key",
() => {
const { apiKey } = createApiKeyStep()
return new WorkflowResponse({
apiKey,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createApiKeyWorkflow } from "../../workflows/create-api-key"
export async function GET(
request: MedusaRequest,
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const apiKeyModuleService = request.scope.resolve(
Modules.API_KEY
)
) {
const { result } = await createApiKeyWorkflow(req.scope)
.run()
res.json({
api_keys: await apiKeyModuleService.listApiKeys(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createApiKeyWorkflow } from "../workflows/create-api-key"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createApiKeyWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const apiKeyModuleService = container.resolve(
Modules.API_KEY
)
console.log(result)
}
const apiKeys = await apiKeyModuleService.listApiKeys()
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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,
})
```
<CommerceModuleSections name="API Key" />
@@ -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 = {
title: `Auth Module`,
@@ -6,132 +6,141 @@ export const metadata = {
# {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>
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
</Note>
## Auth Features
- [Basic User Authentication](./authentication-route/page.mdx#1-basic-authentication-flow): Authenticate users using their email and password credentials.
- [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).
- [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.
---
## How to Use the Auth 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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["18", "Modules.AUTH", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
```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"
const step1 = createStep("step-1", async (_, { container }) => {
const authModuleService = container.resolve(
Modules.AUTH
)
const authIdentitys = await authModuleService.listAuthIdentities()
})
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",
{
url: req.url,
headers: req.headers,
query: req.query,
body: req.body,
authScope: "admin", // or custom actor type
protocol: req.protocol,
} as AuthenticationInput
)
if (!success) {
// incorrect authentication details
throw new MedusaError(
MedusaError.Types.UNAUTHORIZED,
error || "Incorrect authentication details"
)
}
return new StepResponse({ authIdentity }, authIdentity?.id)
},
async (authIdentityId, { container }) => {
if (!authIdentityId) {
return
}
const authModuleService = container.resolve(Modules.AUTH)
await authModuleService.deleteAuthIdentities([authIdentityId])
}
)
export const authenticateUserWorkflow = createWorkflow(
"authenticate-user",
(input: Input) => {
const { authIdentity } = authenticateUserStep(input)
return new WorkflowResponse({
authIdentity,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
```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
): Promise<void> {
const authModuleService = req.scope.resolve(
Modules.AUTH
)
) {
const { result } = await authenticateUserWorkflow(req.scope)
.run({
req,
})
res.json({
authIdentitys: await authModuleService.listAuthIdentities(),
})
res.send(result)
}
```
</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
### Basic User Authentication
Authenticate users using their email and password credentials.
```ts
const { success, authIdentity, 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) {
// incorrect authentication details
throw new Error(error)
}
```
### 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
const { success, authIdentity } = await authModuleService.validateCallback(
"google",
{
url: req.url,
headers: req.headers,
query: req.query,
body: req.body,
authScope: "admin",
protocol: req.protocol,
} as AuthenticationInput
)
```
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## 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({
entity: "cart",
fields: [
"customer.*"
]
"customer.*",
],
})
// carts.order
@@ -62,8 +62,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: carts } = useQueryGraphStep({
entity: "cart",
fields: [
"customer.*"
]
"customer.*",
],
})
// 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({
entity: "cart",
fields: [
"order.*"
]
"order.*",
],
})
// carts.order
@@ -111,8 +111,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: carts } = useQueryGraphStep({
entity: "cart",
fields: [
"order.*"
]
"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({
entity: "cart",
fields: [
"payment_collection.*"
]
"payment_collection.*",
],
})
// carts.payment_collection
@@ -204,8 +204,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: carts } = useQueryGraphStep({
entity: "cart",
fields: [
"payment_collection.*"
]
"payment_collection.*",
],
})
// carts.payment_collection
@@ -283,8 +283,8 @@ To retrieve the product, pass `product.*` in `fields`.
const { data: lineItems } = await query.graph({
entity: "line_item",
fields: [
"variant.*"
]
"variant.*",
],
})
// lineItems.variant
@@ -301,8 +301,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: lineItems } = useQueryGraphStep({
entity: "line_item",
fields: [
"variant.*"
]
"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({
entity: "cart",
fields: [
"promotions.*"
]
"promotions.*",
],
})
// carts.promotions
@@ -358,8 +358,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: carts } = useQueryGraphStep({
entity: "cart",
fields: [
"promotions.*"
]
"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({
entity: "cart",
fields: [
"region.*"
]
"region.*",
],
})
// carts.region
@@ -447,8 +447,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: carts } = useQueryGraphStep({
entity: "cart",
fields: [
"region.*"
]
"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({
entity: "cart",
fields: [
"sales_channel.*"
]
"sales_channel.*",
],
})
// carts.sales_channel
@@ -492,8 +492,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: carts } = useQueryGraphStep({
entity: "cart",
fields: [
"sales_channel.*"
]
"sales_channel.*",
],
})
// carts.sales_channel
@@ -6,120 +6,165 @@ export const metadata = {
# {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>
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
</Note>
## Cart Features
- [Cart Management](./concepts/page.mdx): Store and manage carts, including their addresses, line items, shipping methods, and more.
- [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.
- [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.
---
## How to Use the Cart 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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.CART", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```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 step1 = createStep("step-1", async (_, { container }) => {
const cartModuleService = container.resolve(
Modules.CART
)
const createCartStep = createStep(
"create-cart",
async ({}, { container }) => {
const cartModuleService = container.resolve(Modules.CART)
const carts = await cartModuleService.listCarts()
})
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 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,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createCartWorkflow } from "../../workflows/create-cart"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const cartModuleService = req.scope.resolve(
Modules.CART
)
) {
const { result } = await createCartWorkflow(req.scope)
.run()
res.json({
carts: await cartModuleService.listCarts(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<CodeTab label="Subscriber" value="subscriber">
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import {
type SubscriberConfig,
type SubscriberArgs,
} from "@medusajs/framework"
import { createCartWorkflow } from "../workflows/create-cart"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createCartWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const cartModuleService = container.resolve(
Modules.CART
)
console.log(result)
}
const carts = await cartModuleService.listCarts()
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).
---
## Features
### Cart Management
Store and manage carts, including their addresses, line items, shipping methods, and more.
```ts
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,
},
],
})
```
### Apply Promotions
Apply promotions or discounts to line items and shipping methods by adding adjustment lines that are factored into their subtotals.
```ts
const lineAdjustments = await cartModuleService.addLineItemAdjustments({
item_id: "cali_123",
code: "50OFF",
amount: 500,
})
const shippingAdjustments =
await cartModuleService.addShippingMethodAdjustments({
shipping_method_id: "casm_123",
code: "FREESHIPPING",
amount: 1000,
})
```
### Cart Context and Scoping
A cart is scoped to a sales channel, region, and a customer.
The Medusa application links the Cart Module to each of their respective modules, providing features like:
- Checking product availability in a sales channel.
- Retrieving pricing per region.
- Applying promotions based on the customer's group.
<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({
entity: "store",
fields: [
"supported_currencies.currency.*"
]
"supported_currencies.currency.*",
],
})
// stores.supported_currencies
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: stores } = useQueryGraphStep({
entity: "store",
fields: [
"supported_currencies.currency.*"
]
"supported_currencies.currency.*",
],
})
// stores.supported_currencies
@@ -6,91 +6,161 @@ export const metadata = {
# {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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["13", "Modules.CURRENCY", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/retrieve-price-with-currency.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
transform,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const currencyModuleService = container.resolve(
Modules.CURRENCY
)
const retrieveCurrencyStep = createStep(
"retrieve-currency",
async ({}, { container }) => {
const currencyModuleService = container.resolve(Modules.CURRENCY)
const currencies = await currencyModuleService.listCurrencies()
})
const currency = await currencyModuleService
.retrieveCurrency("usd")
return new StepResponse({ currency })
}
)
type Input = {
price: number
}
export const retrievePriceWithCurrency = createWorkflow(
"create-currency",
(input: Input) => {
const { currency } = retrieveCurrencyStep()
const formattedPrice = transform({
input,
currency,
}, (data) => {
return `${data.currency.symbol}${data.input.price}`
})
return new WorkflowResponse({
formattedPrice,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<CodeTabs group="resource-types">
<CodeTab label="API Route" value="api-route">
```ts title="src/api/workflow/route.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/framework/http"
import { retrievePriceWithCurrency } from "../../workflows/retrieve-price-with-currency"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const currencyModuleService = req.scope.resolve(
Modules.CURRENCY
)
) {
const { result } = await retrievePriceWithCurrency(req.scope)
.run({
price: 10,
})
res.json({
currencies: await currencyModuleService.listCurrencies(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<CodeTab label="Subscriber" value="subscriber">
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"], ["13"], ["14"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import {
type SubscriberConfig,
type SubscriberArgs,
} from "@medusajs/framework"
import { retrievePriceWithCurrency } from "../workflows/retrieve-price-with-currency"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await retrievePriceWithCurrency(container)
.run({
price: 10,
})
export default async function subscriberHandler({ container }: SubscriberArgs) {
const currencyModuleService = container.resolve(
Modules.CURRENCY
)
console.log(result)
}
const currencies = await currencyModuleService.listCurrencies()
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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
)
```
<CommerceModuleSections name="Currency" />
@@ -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.
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 {
createCustomFromCustomerWorkflow,
@@ -38,8 +38,8 @@ To retrieve a customer's carts with [Query](!docs!/learn/fundamentals/module-lin
const { data: customers } = await query.graph({
entity: "customer",
fields: [
"carts.*"
]
"carts.*",
],
})
// customers.carts
@@ -56,8 +56,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: customers } = useQueryGraphStep({
entity: "customer",
fields: [
"carts.*"
]
"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({
entity: "customer",
fields: [
"orders.*"
]
"orders.*",
],
})
// customers.orders
@@ -101,8 +101,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: customers } = useQueryGraphStep({
entity: "customer",
fields: [
"orders.*"
]
"orders.*",
],
})
// customers.orders
@@ -6,96 +6,154 @@ export const metadata = {
# {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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.CUSTOMER", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-customer.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const customerModuleService = container.resolve(
Modules.CUSTOMER
)
const createCustomerStep = createStep(
"create-customer",
async ({}, { container }) => {
const customerModuleService = container.resolve(Modules.CUSTOMER)
const customers = await customerModuleService.listCustomers()
})
const customer = await customerModuleService.createCustomers({
first_name: "Peter",
last_name: "Hayes",
email: "peter.hayes@example.com",
})
return new StepResponse({ customer }, customer.id)
},
async (customerId, { container }) => {
if (!customerId) {
return
}
const customerModuleService = container.resolve(Modules.CUSTOMER)
await customerModuleService.deleteCustomers([customerId])
}
)
export const createCustomerWorkflow = createWorkflow(
"create-customer",
() => {
const { customer } = createCustomerStep()
return new WorkflowResponse({
customer,
})
}
)
```
</CodeTab>
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
<CodeTabs group="resource-types">
<CodeTab label="API Route" value="api-route">
```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 { createCustomerWorkflow } from "../../workflows/create-customer"
```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
) {
const { result } = await createCustomerWorkflow(req.scope)
.run()
export async function GET(request: MedusaRequest, res: MedusaResponse) {
const customerModuleService = request.scope.resolve(
Modules.CUSTOMER
)
res.json({
customers: await customerModuleService.listCustomers(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createCustomerWorkflow } from "../workflows/create-customer"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createCustomerWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const customerModuleService = container.resolve(
Modules.CUSTOMER
)
console.log(result)
}
const customers = await customerModuleService.listCustomers()
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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,
})
```
<CommerceModuleSections name="Customer" />
@@ -49,8 +49,8 @@ To retrieve the return, pass `return.*` in `fields`.
const { data: fulfillments } = await query.graph({
entity: "fulfillment",
fields: [
"order.*"
]
"order.*",
],
})
// fulfillments.order
@@ -67,8 +67,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: fulfillments } = useQueryGraphStep({
entity: "fulfillment",
fields: [
"order.*"
]
"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({
entity: "shipping_option",
fields: [
"price_set.*"
]
"price_set.*",
],
})
// shippingOptions.price_set
@@ -160,8 +160,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: shippingOptions } = useQueryGraphStep({
entity: "shipping_option",
fields: [
"price_set.*"
]
"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({
entity: "fulfillment_set",
fields: [
"location.*"
]
"location.*",
],
})
// fulfillmentSets.location
@@ -263,8 +263,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: fulfillmentSets } = useQueryGraphStep({
entity: "fulfillment_set",
fields: [
"location.*"
]
"location.*",
],
})
// fulfillmentSets.location
@@ -6,162 +6,176 @@ export const metadata = {
# {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>
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
</Note>
## Fulfillment Features
- [Fulfillment Management](./item-fulfillment/page.mdx): Create fulfillments and keep track of their status, items, and more.
- [Integrate Third-Party Fulfillment Providers](./fulfillment-provider/page.mdx): Create third-party fulfillment providers to provide customers with shipping options and fulfill their orders.
- [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.
---
## How to Use the Fulfillment 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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.FULFILLMENT", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```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 step1 = createStep("step-1", async (_, { container }) => {
const fulfillmentModuleService = container.resolve(
Modules.FULFILLMENT
)
const createFulfillmentStep = createStep(
"create-fulfillment",
async ({}, { container }) => {
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
const fulfillments = await fulfillmentModuleService.listFulfillments()
})
const fulfillment = await fulfillmentModuleService.createFulfillment({
location_id: "loc_123",
provider_id: "webshipper",
delivery_address: {
country_code: "us",
city: "Strongsville",
address_1: "18290 Royalton Rd",
},
items: [
{
title: "Shirt",
sku: "SHIRT",
quantity: 1,
barcode: "123456",
},
],
labels: [],
order: {},
})
return new StepResponse({ fulfillment }, fulfillment.id)
},
async (fulfillmentId, { container }) => {
if (!fulfillmentId) {
return
}
const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT)
await fulfillmentModuleService.deleteFulfillment(fulfillmentId)
}
)
export const createFulfillmentWorkflow = createWorkflow(
"create-fulfillment",
() => {
const { fulfillment } = createFulfillmentStep()
return new WorkflowResponse({
fulfillment,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createFulfillmentWorkflow } from "../../workflows/create-fuilfillment"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const fulfillmentModuleService = req.scope.resolve(
Modules.FULFILLMENT
)
) {
const { result } = await createFulfillmentWorkflow(req.scope)
.run()
res.json({
fulfillments: await fulfillmentModuleService.listFulfillments(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createFulfillmentWorkflow } from "../workflows/create-fuilfillment"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createFulfillmentWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const fulfillmentModuleService = container.resolve(
Modules.FULFILLMENT
)
console.log(result)
}
const fulfillments = await fulfillmentModuleService.listFulfillments()
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>
---
## Features
### Fulfillment Management
Create fulfillments and keep track of their status, items, and more.
```ts
const fulfillment = await fulfillmentModuleService.createFulfillment({
location_id: "loc_123",
provider_id: "webshipper",
delivery_address: {
country_code: "us",
city: "Strongsville",
address_1: "18290 Royalton Rd",
},
items: [
{
title: "Shirt",
sku: "SHIRT",
quantity: 1,
barcode: "123456",
},
],
labels: [],
order: {},
})
```
### Integrate Third-Party Fulfillment Providers
Use third-party fulfillment providers to provide customers with shipping options and fulfill their orders.
```ts
const shippingOption = await fulfillmentModuleService.createShippingOptions({
// ...
provider_id: "webshipper",
})
```
### Restrict By Location and Rules
Shipping options can be restricted to specific geographical locations. You can also specify custom rules to restrict shipping options.
```ts
const serviceZone = await fulfillmentModuleService.createServiceZones({
name: "US",
fulfillment_set_id: "fset_123",
geo_zones: [
{
type: "country",
country_code: "us",
},
],
})
const shippingOption = await fulfillmentModuleService.createShippingOptions({
name: "Express Shipping",
// restrict location
service_zone_id: serviceZone.id,
// restrict by custom rules
rules: [
{
field: "customer_group",
operator: "eq",
value: "vip",
},
],
// ...
})
```
### Support Different Fulfillment Forms
Support various fulfillment forms, such as shipping or pick up.
```ts
const fulfillmentSets = await fulfillmentModuleService.createFulfillmentSets([
{
name: "Shipping",
type: "shipping",
},
{
name: "Pick-up",
type: "pick-up",
},
])
```
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## 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({
entity: "inventory_item",
fields: [
"variants.*"
]
"variants.*",
],
})
// inventoryItems.variants
@@ -60,8 +60,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: inventoryItems } = useQueryGraphStep({
entity: "inventory_item",
fields: [
"variants.*"
]
"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({
entity: "inventory_level",
fields: [
"stock_locations.*"
]
"stock_locations.*",
],
})
// inventoryLevels.stock_locations
@@ -149,8 +149,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: inventoryLevels } = useQueryGraphStep({
entity: "inventory_level",
fields: [
"stock_locations.*"
]
"stock_locations.*",
],
})
// inventoryLevels.stock_locations
@@ -6,130 +6,156 @@ export const metadata = {
# {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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.INVENTORY", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-inventory-item.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const inventoryModuleService = container.resolve(
Modules.INVENTORY
)
const createInventoryItemStep = createStep(
"create-inventory-item",
async ({}, { container }) => {
const inventoryModuleService = container.resolve(Modules.INVENTORY)
const inventoryItems = await inventoryModuleService.listInventoryItems({})
})
const inventoryItem = await inventoryModuleService.createInventoryItems({
sku: "SHIRT",
title: "Green Medusa Shirt",
requires_shipping: true,
})
return new StepResponse({ inventoryItem }, inventoryItem.id)
},
async (inventoryItemId, { container }) => {
if (!inventoryItemId) {
return
}
const inventoryModuleService = container.resolve(Modules.INVENTORY)
await inventoryModuleService.deleteInventoryItems([inventoryItemId])
}
)
export const createInventoryItemWorkflow = createWorkflow(
"create-inventory-item-workflow",
() => {
const { inventoryItem } = createInventoryItemStep()
return new WorkflowResponse({
inventoryItem,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createInventoryItemWorkflow } from "../../workflows/create-inventory-item"
export async function GET(
request: MedusaRequest,
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const inventoryModuleService = request.scope.resolve(
Modules.INVENTORY
)
) {
const { result } = await createInventoryItemWorkflow(req.scope)
.run()
res.json({
inventory_items: await inventoryModuleService.listInventoryItems({}),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createInventoryItemWorkflow } from "../workflows/create-inventory-item"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createInventoryItemWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const inventoryModuleService = container.resolve(
Modules.INVENTORY
)
console.log(result)
}
const inventoryItems = await inventoryModuleService.listInventoryItems({})
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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
)
```
<CommerceModuleSections name="Inventory" />
@@ -47,8 +47,8 @@ To retrieve the customer of an order with [Query](!docs!/learn/fundamentals/modu
const { data: orders } = await query.graph({
entity: "order",
fields: [
"customer.*"
]
"customer.*",
],
})
// orders.customer
@@ -65,8 +65,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"customer.*"
]
"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({
entity: "order",
fields: [
"cart.*"
]
"cart.*",
],
})
// orders.cart
@@ -114,8 +114,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"cart.*"
]
"cart.*",
],
})
// orders.cart
@@ -197,8 +197,8 @@ To retrieve the fulfillments of a return, pass `fulfillments.*` in `fields`.
const { data: orders } = await query.graph({
entity: "order",
fields: [
"fulfillments.*"
]
"fulfillments.*",
],
})
// orders.fulfillments
@@ -215,8 +215,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"fulfillments.*"
]
"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({
entity: "order",
fields: [
"payment_collections.*"
]
"payment_collections.*",
],
})
// orders.payment_collections
@@ -308,8 +308,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"payment_collections.*"
]
"payment_collections.*",
],
})
// orders.payment_collections
@@ -388,8 +388,8 @@ To retrieve the product, pass `product.*` in `fields`.
const { data: lineItems } = await query.graph({
entity: "order_line_item",
fields: [
"variant.*"
]
"variant.*",
],
})
// lineItems.variant
@@ -406,8 +406,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: lineItems } = useQueryGraphStep({
entity: "order_line_item",
fields: [
"variant.*"
]
"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({
entity: "order",
fields: [
"promotion.*"
]
"promotion.*",
],
})
// orders.promotion
@@ -453,8 +453,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"promotion.*"
]
"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({
entity: "order",
fields: [
"region.*"
]
"region.*",
],
})
// orders.region
@@ -542,8 +542,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"region.*"
]
"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({
entity: "order",
fields: [
"sales_channel.*"
]
"sales_channel.*",
],
})
// orders.sales_channel
@@ -587,8 +587,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: orders } = useQueryGraphStep({
entity: "order",
fields: [
"sales_channel.*"
]
"sales_channel.*",
],
})
// orders.sales_channel
@@ -6,138 +6,169 @@ export const metadata = {
# {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>
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
</Note>
## Order Features
- [Order Management](./concepts/page.mdx): Store and manage your orders to retrieve, create, cancel, and perform other operations.
- Draft Orders: Allow merchants to create orders on behalf of their customers as draft orders that later are transformed to regular orders.
- [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.
- [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.
---
## How to Use the Order 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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.ORDER", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```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 step1 = createStep("step-1", async (_, { container }) => {
const orderModuleService = container.resolve(
Modules.ORDER
)
const orders = await orderModuleService.listOrders()
})
const createDraftOrderStep = createStep(
"create-order",
async ({}, { container }) => {
const orderModuleService = container.resolve(Modules.ORDER)
const draftOrder = await orderModuleService.createOrders({
currency_code: "usd",
items: [
{
title: "Shirt",
quantity: 1,
unit_price: 3000,
},
],
shipping_methods: [
{
name: "Express shipping",
amount: 3000,
},
],
status: "draft",
})
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,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createDraftOrderWorkflow } from "../../workflows/create-draft-order"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const orderModuleService = req.scope.resolve(
Modules.ORDER
)
) {
const { result } = await createDraftOrderWorkflow(req.scope)
.run()
res.json({
orders: await orderModuleService.listOrders(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<CodeTab label="Subscriber" value="subscriber">
```ts title="src/subscribers/user-created.ts" highlights={[["11"], ["12"]]} collapsibleLines="1-6" expandButtonLabel="Show Imports"
import {
type SubscriberConfig,
type SubscriberArgs,
} from "@medusajs/framework"
import { createDraftOrderWorkflow } from "../workflows/create-draft-order"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createDraftOrderWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const orderModuleService = container.resolve(
Modules.ORDER
)
console.log(result)
}
const orders = await orderModuleService.listOrders()
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).
---
## Features
### Order Management
Store and manage your orders to retrieve, create, cancel, and perform other operations.
```ts
const order = await orderModuleService.createOrders({
currency_code: "usd",
items: [
{
title: "Shirt",
quantity: 1,
unit_price: 3000,
},
],
shipping_methods: [
{
name: "Express shipping",
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",
is_draft_order: true,
})
```
### Apply Promotions
Apply promotions or discounts to the order's items and shipping methods by adding adjustment lines that are factored into their subtotals.
```ts
const lineAdjustments = await orderModuleService.createOrderLineItemAdjustments({
item_id: "cali_123",
code: "50OFF",
amount: 500,
})
const shippingAdjustments =
await orderModuleService.createOrderShippingMethodAdjustments({
shipping_method_id: "casm_123",
code: "FREESHIPPING",
amount: 1000,
})
```
### Returns, Exchanges, and Claims
Return or exchange items, with version-based control over the order's timeline.
```ts
const orderReturn = await orderModuleService.createReturn({
order_id: "order_123",
items: [{
id: "orditem_123",
quantity: 1,
}],
})
```
<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({
entity: "payment_collection",
fields: [
"cart.*"
]
"cart.*",
],
})
// paymentCollections.cart
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: paymentCollections } = useQueryGraphStep({
entity: "payment_collection",
fields: [
"cart.*"
]
"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({
entity: "payment_collection",
fields: [
"order.*"
]
"order.*",
],
})
// paymentCollections.order
@@ -149,8 +149,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: paymentCollections } = useQueryGraphStep({
entity: "payment_collection",
fields: [
"order.*"
]
"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({
entity: "payment_provider",
fields: [
"regions.*"
]
"regions.*",
],
})
// paymentProviders.regions
@@ -242,8 +242,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: paymentProviders } = useQueryGraphStep({
entity: "payment_provider",
fields: [
"regions.*"
]
"regions.*",
],
})
// paymentProviders.regions
@@ -1,5 +1,4 @@
import { CodeTabs, CodeTab } from "docs-ui"
import { Table } from "docs-ui"
import { CodeTabs, CodeTab, ChildDocs } from "docs-ui"
export const metadata = {
title: `Payment Module`,
@@ -7,130 +6,170 @@ export const metadata = {
# {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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.PAYMENT", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-payment-collection.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const paymentModuleService = container.resolve(
Modules.PAYMENT
)
const createPaymentCollectionStep = createStep(
"create-payment-collection",
async ({}, { container }) => {
const paymentModuleService = container.resolve(Modules.PAYMENT)
const payment_collections =
await paymentModuleService.listPaymentCollections()
})
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])
}
)
export const createPaymentCollectionWorkflow = createWorkflow(
"create-payment-collection",
() => {
const { paymentCollection } = createPaymentCollectionStep()
return new WorkflowResponse({
paymentCollection,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createPaymentCollectionWorkflow } from "../../workflows/create-payment-collection"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const paymentModuleService = req.scope.resolve(
Modules.PAYMENT
)
) {
const { result } = await createPaymentCollectionWorkflow(req.scope)
.run()
res.json({
payment_collections: await paymentModuleService.listPaymentCollections(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createPaymentCollectionWorkflow } from "../workflows/create-payment-collection"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createPaymentCollectionWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const paymentModuleService = container.resolve(
Modules.PAYMENT
)
console.log(result)
}
const payment_collections =
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>
</CodeTabs>
---
## 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
},
})
```
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## 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({
entity: "price_set",
fields: [
"shipping_option.*"
]
"shipping_option.*",
],
})
// priceSets.shipping_option
@@ -54,8 +54,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: priceSets } = useQueryGraphStep({
entity: "price_set",
fields: [
"shipping_option.*"
]
"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({
entity: "price_set",
fields: [
"variant.*"
]
"variant.*",
],
})
// priceSets.variant
@@ -151,8 +151,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: priceSets } = useQueryGraphStep({
entity: "price_set",
fields: [
"variant.*"
]
"variant.*",
],
})
// priceSets.variant
@@ -6,157 +6,167 @@ export const metadata = {
# {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>
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
</Note>
## Pricing Features
- [Price Management](./concepts/page.mdx): Store and manage prices of a resource, such as a product or a variant.
- [Advanced Rule Engine](./price-rules/page.mdx): Create prices with custom rules to condition prices based on different contexts.
- [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.
---
## How to Use the Pricing 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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.PRICING", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```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 step1 = createStep("step-1", async (_, { container }) => {
const pricingModuleService = container.resolve(
Modules.PRICING
)
const createPriceSetStep = createStep(
"create-price-set",
async ({}, { container }) => {
const pricingModuleService = container.resolve(Modules.PRICING)
const priceSets = await pricingModuleService.listPriceSets()
})
const priceSet = await pricingModuleService.createPriceSets({
prices: [
{
amount: 500,
currency_code: "USD",
},
{
amount: 400,
currency_code: "EUR",
min_quantity: 0,
max_quantity: 4,
rules: {},
},
],
})
return new StepResponse({ priceSet }, priceSet.id)
},
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,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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(
request: MedusaRequest,
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const pricingModuleService = request.scope.resolve(
Modules.PRICING
)
) {
const { result } = await createPriceSetWorkflow(req.scope)
.run()
res.json({
price_sets: await pricingModuleService.listPriceSets(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createPriceSetWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const pricingModuleService = container.resolve(
Modules.PRICING
)
console.log(result)
}
const priceSets = await pricingModuleService.listPriceSets()
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).
---
## Features
### Price Management
Store the prices of a resource and manage them through the main service's methods.
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.
```ts
const priceSet = await pricingModuleService.createPriceSets({
prices: [
{
amount: 500,
currency_code: "USD",
},
{
amount: 400,
currency_code: "EUR",
min_quantity: 0,
max_quantity: 4,
rules: {},
},
],
})
```
### Advanced Rule Engine
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",
},
},
],
})
```
### 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",
},
}
)
```
<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
- query
- tax
- server
---
export const metadata = {
@@ -4,6 +4,7 @@ tags:
- product
- pricing
- query
- server
---
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({
entity: "variant",
fields: [
"line_items.*"
]
"line_items.*",
],
})
// variants.line_items
@@ -68,8 +68,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: variants } = useQueryGraphStep({
entity: "variant",
fields: [
"line_items.*"
]
"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({
entity: "variant",
fields: [
"inventory_items.*"
]
"inventory_items.*",
],
})
// variants.inventory_items
@@ -119,8 +119,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: variants } = useQueryGraphStep({
entity: "variant",
fields: [
"inventory_items.*"
]
"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({
entity: "variant",
fields: [
"order_items.*"
]
"order_items.*",
],
})
// variants.order_items
@@ -217,8 +217,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: variants } = useQueryGraphStep({
entity: "variant",
fields: [
"order_items.*"
]
"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({
entity: "variant",
fields: [
"price_set.*"
]
"price_set.*",
],
})
// variants.price_set
@@ -268,8 +268,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: variants } = useQueryGraphStep({
entity: "variant",
fields: [
"price_set.*"
]
"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({
entity: "product",
fields: [
"sales_channels.*"
]
"sales_channels.*",
],
})
// products.sales_channels
@@ -361,8 +361,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: products } = useQueryGraphStep({
entity: "product",
fields: [
"sales_channels.*"
]
"sales_channels.*",
],
})
// products.sales_channels
@@ -6,114 +6,166 @@ export const metadata = {
# {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>
Learn more about why modules are isolated in [this documentation](!docs!/learn/fundamentals/modules/isolation).
</Note>
## Product Features
- [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.
- [Product Organization](/references/product/models): The Product Module provides different data models used to organize products, including categories, collections, tags, and more.
---
## How to Use the Product 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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.PRODUCT", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```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 step1 = createStep("step-1", async (_, { container }) => {
const productModuleService = container.resolve(
Modules.PRODUCT
)
const createProductStep = createStep(
"create-product",
async ({}, { container }) => {
const productService = container.resolve(Modules.PRODUCT)
const products = await productModuleService.listProducts()
})
const product = await productService.createProducts({
title: "Medusa Shirt",
options: [
{
title: "Color",
values: ["Black", "White"],
},
],
variants: [
{
title: "Black Shirt",
options: {
Color: "Black",
},
},
],
})
return new StepResponse({ product }, product.id)
},
async (productId, { container }) => {
if (!productId) {
return
}
const productService = container.resolve(Modules.PRODUCT)
await productService.deleteProducts([productId])
}
)
export const createProductWorkflow = createWorkflow(
"create-product",
() => {
const { product } = createProductStep()
return new WorkflowResponse({
product,
})
}
)
```
</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">
```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"
```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
) {
const { result } = await createProductWorkflow(req.scope)
.run()
export async function GET(request: MedusaRequest, res: MedusaResponse) {
const productModuleService = request.scope.resolve(
Modules.PRODUCT
)
res.json({
products: await productModuleService.listProducts(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createProductWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const productModuleService = container.resolve(
Modules.PRODUCT
)
console.log(result)
}
const products = await productModuleService.listProducts()
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).
---
## Features
### Products Management
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.
```ts
const products = await productService.createProducts([
{
title: "Medusa Shirt",
options: [
{
title: "Color",
values: ["Black", "White"],
},
],
variants: [
{
title: "Black Shirt",
options: {
Color: "Black",
},
},
],
},
])
```
### 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([
{
id: product.id,
categories: [
{
id: category.id,
},
],
},
])
```
<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({
entity: "promotion",
fields: [
"carts.*"
]
"carts.*",
],
})
// promotions.carts
@@ -67,8 +67,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: promotions } = useQueryGraphStep({
entity: "promotion",
fields: [
"carts.*"
]
"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({
entity: "promotion",
fields: [
"orders.*"
]
"orders.*",
],
})
// promotions.orders
@@ -158,8 +158,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: promotions } = useQueryGraphStep({
entity: "promotion",
fields: [
"orders.*"
]
"orders.*",
],
})
// promotions.orders
@@ -6,130 +6,161 @@ export const metadata = {
# {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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.PROMOTION", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-promotion.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const promotionModuleService = container.resolve(
Modules.PROMOTION
)
const createPromotionStep = createStep(
"create-promotion",
async ({}, { container }) => {
const promotionModuleService = container.resolve(Modules.PROMOTION)
const promotions = await promotionModuleService.listPromotions()
})
const promotion = await promotionModuleService.createPromotions({
code: "10%OFF",
type: "standard",
application_method: {
type: "percentage",
target_type: "order",
value: 10,
currency_code: "usd",
},
})
return new StepResponse({ promotion }, promotion.id)
},
async (promotionId, { container }) => {
if (!promotionId) {
return
}
const promotionModuleService = container.resolve(Modules.PROMOTION)
await promotionModuleService.deletePromotions(promotionId)
}
)
export const createPromotionWorkflow = createWorkflow(
"create-promotion",
() => {
const { promotion } = createPromotionStep()
return new WorkflowResponse({
promotion,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createPromotionWorkflow } from "../../workflows/create-cart"
export async function GET(
request: MedusaRequest,
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const promotionModuleService = request.scope.resolve(
Modules.PROMOTION
)
) {
const { result } = await createPromotionWorkflow(req.scope)
.run()
res.json({
promotions: await promotionModuleService.listPromotions(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createPromotionWorkflow } from "../workflows/create-cart"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createPromotionWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const promotionModuleService = container.resolve(
Modules.PROMOTION
)
console.log(result)
}
const promotions = await promotionModuleService.listPromotions()
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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"),
})
```
<CommerceModuleSections name="Promotion" />
@@ -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({
entity: "region",
fields: [
"carts.*"
]
"carts.*",
],
})
// regions.carts
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: regions } = useQueryGraphStep({
entity: "region",
fields: [
"carts.*"
]
"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({
entity: "region",
fields: [
"orders.*"
]
"orders.*",
],
})
// regions.orders
@@ -102,8 +102,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: regions } = useQueryGraphStep({
entity: "region",
fields: [
"orders.*"
]
"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({
entity: "region",
fields: [
"payment_providers.*"
]
"payment_providers.*",
],
})
// regions.payment_providers
@@ -151,8 +151,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: regions } = useQueryGraphStep({
entity: "region",
fields: [
"payment_providers.*"
]
"payment_providers.*",
],
})
// regions.payment_providers
@@ -6,125 +6,156 @@ export const metadata = {
# {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
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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.REGION", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-region.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const regionModuleService = container.resolve(
Modules.REGION
)
const createRegionStep = createStep(
"create-region",
async ({}, { container }) => {
const regionModuleService = container.resolve(Modules.REGION)
const regions = await regionModuleService.listRegions()
})
const region = await regionModuleService.createRegions({
name: "Europe",
currency_code: "eur",
})
return new StepResponse({ region }, region.id)
},
async (regionId, { container }) => {
if (!regionId) {
return
}
const regionModuleService = container.resolve(Modules.REGION)
await regionModuleService.deleteRegions([regionId])
}
)
export const createRegionWorkflow = createWorkflow(
"create-region",
() => {
const { region } = createRegionStep()
return new WorkflowResponse({
region,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createRegionWorkflow } from "../../workflows/create-region"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const regionModuleService = req.scope.resolve(
Modules.REGION
)
) {
const { result } = await createRegionWorkflow(req.scope)
.run()
res.json({
regions: await regionModuleService.listRegions(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createRegionWorkflow } from "../workflows/create-region"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createRegionWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const regionModuleService = container.resolve(
Modules.REGION
)
console.log(result)
}
const regions = await regionModuleService.listRegions()
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>
</CodeTabs>
---
## What is a Region?
A region represents the area you sell products in. Each region can cover multiple countries, but uses a single currency.
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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"],
},
])
```
<CommerceModuleSections name="Region" />
@@ -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({
entity: "sales_channel",
fields: [
"publishable_api_keys.*"
]
"publishable_api_keys.*",
],
})
// salesChannels.publishable_api_keys
@@ -63,8 +63,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: salesChannels } = useQueryGraphStep({
entity: "sales_channel",
fields: [
"publishable_api_keys.*"
]
"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({
entity: "sales_channel",
fields: [
"carts.*"
]
"carts.*",
],
})
// salesChannels.carts
@@ -152,8 +152,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: salesChannels } = useQueryGraphStep({
entity: "sales_channel",
fields: [
"carts.*"
]
"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({
entity: "sales_channel",
fields: [
"orders.*"
]
"orders.*",
],
})
// salesChannels.orders
@@ -197,8 +197,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: salesChannels } = useQueryGraphStep({
entity: "sales_channel",
fields: [
"orders.*"
]
"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({
entity: "sales_channel",
fields: [
"products.*"
]
"products.*",
],
})
// salesChannels.products
@@ -246,8 +246,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: salesChannels } = useQueryGraphStep({
entity: "sales_channel",
fields: [
"products.*"
]
"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({
entity: "sales_channel",
fields: [
"stock_locations.*"
]
"stock_locations.*",
],
})
// salesChannels.stock_locations
@@ -339,8 +339,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: salesChannels } = useQueryGraphStep({
entity: "sales_channel",
fields: [
"stock_locations.*"
]
"stock_locations.*",
],
})
// salesChannels.stock_locations
@@ -6,68 +6,15 @@ export const metadata = {
# {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">
<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>
---
</Note>
## What's a Sales Channel?
@@ -81,31 +28,151 @@ Some use case examples for using a sales channel:
---
## Features
## Sales Channel Features
### Sales Channel Management
- [Sales Channel Management](/references/sales-channel/models/SalesChannel): Manage sales channels in your store. Each sales channel has different meta information such as name or description, allowing you to easily differentiate between sales channels.
- [Product Availability](./links-to-other-modules/page.mdx): Medusa uses the Product and Sales Channel modules to allow merchants to specify a product's availability per sales channel.
- [Cart and Order Scoping](./links-to-other-modules/page.mdx): Carts, available through the Cart Module, are scoped to a sales channel. Paired with the product availability feature, you benefit from more features like allowing only products available in sales channel in a cart.
- [Inventory Availability Per Sales Channel](./links-to-other-modules/page.mdx): Medusa links sales channels to stock locations, allowing you to retrieve available inventory of products based on the specified sales channel.
Manage sales channels in your store. Each sales channel has different meta information such as name or description, allowing you to easily differentiate between sales channels.
---
```ts
const salesChannels = await salesChannelModuleService.createSalesChannels([
{
name: "B2B",
## 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)
const salesChannels = await salesChannelModuleService.createSalesChannels([
{
name: "B2B",
},
{
name: "Mobile App",
},
])
return new StepResponse({ salesChannels }, salesChannels.map((sc) => sc.id))
},
{
name: "Mobile App",
},
])
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">
```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"
For example, B2B customers viewing products only see products in the B2B sales channel.
export async function GET(
req: MedusaRequest,
res: MedusaResponse
) {
const { result } = await createSalesChannelWorkflow(req.scope)
.run()
### Cart and Order Scoping
res.send(result)
}
```
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.
</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"
Orders are also scoped to a sales channel due to the relation between the Sales Channel and Order modules.
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({
entity: "stock_location",
fields: [
"fulfillment_sets.*"
]
"fulfillment_sets.*",
],
})
// stockLocations.fulfillment_sets
@@ -72,8 +72,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: stockLocations } = useQueryGraphStep({
entity: "stock_location",
fields: [
"fulfillment_sets.*"
]
"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({
entity: "stock_location",
fields: [
"inventory_levels.*"
]
"inventory_levels.*",
],
})
// stockLocations.inventory_levels
@@ -161,8 +161,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: stockLocations } = useQueryGraphStep({
entity: "stock_location",
fields: [
"inventory_levels.*"
]
"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({
entity: "stock_location",
fields: [
"sales_channels.*"
]
"sales_channels.*",
],
})
// stockLocations.sales_channels
@@ -210,8 +210,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: stockLocations } = useQueryGraphStep({
entity: "stock_location",
fields: [
"sales_channels.*"
]
"sales_channels.*",
],
})
// stockLocations.sales_channels
@@ -6,95 +6,150 @@ export const metadata = {
# {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
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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.STOCK_LOCATION", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-stock-location.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const stockLocationModuleService = container.resolve(
Modules.STOCK_LOCATION
)
const createStockLocationStep = createStep(
"create-stock-location",
async ({}, { container }) => {
const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
const stockLocations = await stockLocationModuleService.listStockLocations({})
})
const stockLocation = await stockLocationModuleService.createStockLocations({
name: "Warehouse 1",
})
return new StepResponse({ stockLocation }, stockLocation.id)
},
async (stockLocationId, { container }) => {
if (!stockLocationId) {
return
}
const stockLocationModuleService = container.resolve(Modules.STOCK_LOCATION)
await stockLocationModuleService.deleteStockLocations([stockLocationId])
}
)
export const createStockLocationWorkflow = createWorkflow(
"create-stock-location",
() => {
const { stockLocation } = createStockLocationStep()
return new WorkflowResponse({ stockLocation })
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createStockLocationWorkflow } from "../../workflows/create-stock-location"
export async function GET(
request: MedusaRequest,
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const stockLocationModuleService =
request.scope.resolve(Modules.STOCK_LOCATION)
) {
const { result } = await createStockLocationWorkflow(req.scope)
.run()
res.json({
stock_locations: await stockLocationModuleService.listStockLocations({}),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createStockLocationWorkflow } from "../workflows/create-stock-location"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createStockLocationWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const stockLocationModuleService = container.resolve(
Modules.STOCK_LOCATION
)
console.log(result)
}
const stockLocations = await stockLocationModuleService.listStockLocations({})
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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",
},
})
```
<CommerceModuleSections name="Stock Location" />
@@ -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({
entity: "store",
fields: [
"supported_currencies.currency.*"
]
"supported_currencies.currency.*",
],
})
// stores.supported_currencies
@@ -57,8 +57,8 @@ import { useQueryGraphStep } from "@medusajs/medusa/core-flows"
const { data: stores } = useQueryGraphStep({
entity: "store",
fields: [
"supported_currencies.currency.*"
]
"supported_currencies.currency.*",
],
})
// stores.supported_currencies
@@ -6,98 +6,154 @@ export const metadata = {
# {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
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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.STORE", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-store.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const storeModuleService: IStoreModuleService = container.resolve(
Modules.STORE
)
const createStoreStep = createStep(
"create-store",
async ({}, { container }) => {
const storeModuleService = container.resolve(Modules.STORE)
const stores = await storeModuleService.listStores()
})
const store = await storeModuleService.createStores({
name: "My Store",
supported_currencies: [{
currency_code: "usd",
is_default: true,
}],
})
return new StepResponse({ store }, store.id)
},
async (storeId, { container }) => {
if(!storeId) {
return
}
const storeModuleService = container.resolve(Modules.STORE)
await storeModuleService.deleteStores([storeId])
}
)
export const createStoreWorkflow = createWorkflow(
"create-store",
() => {
const { store } = createStoreStep()
return new WorkflowResponse({ store })
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createStoreWorkflow } from "../../workflows/create-store"
export async function GET(
request: MedusaRequest,
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const storeModuleService: IStoreModuleService = request.scope.resolve(
Modules.STORE
)
) {
const { result } = await createStoreWorkflow(req.scope)
.run()
res.json({
stores: await storeModuleService.listStores(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createStoreWorkflow } from "../workflows/create-store"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createStoreWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const storeModuleService: IStoreModuleService = container.resolve(
Modules.STORE
)
console.log(result)
}
const stores = await storeModuleService.listStores()
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>
</CodeTabs>
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## Features
### 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"],
},
])
```
<CommerceModuleSections name="Store" />
@@ -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}
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
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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.TAX", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-tax-region.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const taxModuleService = container.resolve(
Modules.TAX
)
const createTaxRegionStep = createStep(
"create-tax-region",
async ({}, { container }) => {
const taxModuleService = container.resolve(Modules.TAX)
const taxRegions = await taxModuleService.listTaxRegions()
})
const taxRegion = await taxModuleService.createTaxRegions({
country_code: "us",
})
return new StepResponse({ taxRegion }, taxRegion.id)
},
async (taxRegionId, { container }) => {
if (!taxRegionId) {
return
}
const taxModuleService = container.resolve(Modules.TAX)
await taxModuleService.deleteTaxRegions([taxRegionId])
}
)
export const createTaxRegionWorkflow = createWorkflow(
"create-tax-region",
() => {
const { taxRegion } = createTaxRegionStep()
return new WorkflowResponse({ taxRegion })
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createTaxRegionWorkflow } from "../../workflows/create-tax-region"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const taxModuleService = req.scope.resolve(
Modules.TAX
)
) {
const { result } = await createTaxRegionWorkflow(req.scope)
.run()
res.json({
tax_regions: await taxModuleService.listTaxRegions(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createTaxRegionWorkflow } from "../workflows/create-tax-region"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createTaxRegionWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const taxModuleService = container.resolve(
Modules.TAX
)
console.log(result)
}
const taxRegions = await taxModuleService.listTaxRegions()
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>
</CodeTabs>
---
## 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.
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## 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}
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
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:
<CodeTabs groupId="resource-type">
<CodeTab label="Workflow Step" value="workflow-step">
export const highlights = [
["12", "Modules.USER", "Resolve the module in a step."]
]
```ts title="src/workflows/hello-world/step1.ts"
import { createStep } from "@medusajs/framework/workflows-sdk"
```ts title="src/workflows/create-user.ts" highlights={highlights}
import {
createWorkflow,
WorkflowResponse,
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { Modules } from "@medusajs/framework/utils"
const step1 = createStep("step-1", async (_, { container }) => {
const userModuleService = container.resolve(
Modules.USER
)
const createUserStep = createStep(
"create-user",
async ({}, { container }) => {
const userModuleService = container.resolve(Modules.USER)
const users = await userModuleService.listUsers()
})
const user = await userModuleService.createUsers({
email: "user@example.com",
first_name: "John",
last_name: "Smith",
})
return new StepResponse({ user }, user.id)
},
async (userId, { container }) => {
if (!userId) {
return
}
const userModuleService = container.resolve(Modules.USER)
await userModuleService.deleteUsers([userId])
}
)
export const createUserWorkflow = createWorkflow(
"create-user",
() => {
const { user } = createUserStep()
return new WorkflowResponse({
user,
})
}
)
```
</CodeTab>
<CodeTab label="API Route" value="api-route">
You can then execute the workflow in your custom API routes, scheduled jobs, or subscribers:
```ts title="src/api/store/custom/route.ts"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { Modules } from "@medusajs/framework/utils"
<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 { createUserWorkflow } from "../../workflows/create-user"
export async function GET(
req: MedusaRequest,
res: MedusaResponse
): Promise<void> {
const userModuleService = req.scope.resolve(
Modules.USER
)
) {
const { result } = await createUserWorkflow(req.scope)
.run()
res.json({
users: await userModuleService.listUsers(),
})
res.send(result)
}
```
</CodeTab>
<CodeTab label="Subscriber" value="subscribers">
<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 { createUserWorkflow } from "../workflows/create-user"
```ts title="src/subscribers/custom-handler.ts"
import { SubscriberArgs } from "@medusajs/framework"
import { Modules } from "@medusajs/framework/utils"
export default async function handleUserCreated({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
const { result } = await createUserWorkflow(container)
.run()
export default async function subscriberHandler({ container }: SubscriberArgs) {
const userModuleService = container.resolve(
Modules.USER
)
console.log(result)
}
const users = await userModuleService.listUsers()
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>
</CodeTabs>
---
## 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])
```
Learn more about workflows in [this documentation](!docs!/learn/fundamentals/workflows).
---
## 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"
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
- api key
- storefront
- sales channel
---
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,
WorkflowDiagram,
} from "docs-ui"
import { CommerceModuleSections } from "../CommerceModuleSections"
const MDXComponents: MDXComponentsType = {
...UiMdxComponents,
a: Link,
TypeList,
WorkflowDiagram,
CommerceModuleSections,
}
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/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/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/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/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/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/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-09T14:47:12.300Z",
"app/commerce-modules/currency/page.mdx": "2024-12-25T15:55:02.850Z",
"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/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-09T14:47:17.248Z",
"app/commerce-modules/customer/page.mdx": "2024-12-25T15:55:02.850Z",
"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/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/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/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/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/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/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/page.mdx": "2024-07-03T19:27:13+03:00",
"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/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/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/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/payment/page.mdx": "2024-10-09T10:59:08.463Z",
"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-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/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/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/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-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/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/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-19T16:37:40.491Z",
"app/commerce-modules/product/guides/price-with-taxes/page.mdx": "2024-12-19T16:37:48.597Z",
"app/commerce-modules/product/page.mdx": "2024-12-09T14:48:26.091Z",
"app/commerce-modules/product/guides/price/page.mdx": "2024-12-25T15:10:37.730Z",
"app/commerce-modules/product/guides/price-with-taxes/page.mdx": "2024-12-25T15:10:40.879Z",
"app/commerce-modules/product/page.mdx": "2024-12-25T15:55:02.850Z",
"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/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/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/examples/page.mdx": "2024-10-09T14:46:47.191Z",
"app/commerce-modules/promotion/page.mdx": "2024-12-09T14:48:30.816Z",
"app/commerce-modules/promotion/page.mdx": "2024-12-25T15:55:02.850Z",
"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/examples/page.mdx": "2024-10-15T15:00:24.388Z",
"app/commerce-modules/region/page.mdx": "2024-12-09T14:48:34.729Z",
"app/commerce-modules/region/page.mdx": "2024-12-25T15:55:02.851Z",
"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/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/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/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/examples/page.mdx": "2024-10-15T15:00:41.265Z",
"app/commerce-modules/stock-location/page.mdx": "2024-12-09T14:48:42.516Z",
"app/commerce-modules/stock-location/page.mdx": "2024-12-25T16:09:26.781Z",
"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/examples/page.mdx": "2024-10-15T15:00:47.283Z",
"app/commerce-modules/store/page.mdx": "2024-12-09T14:48:46.363Z",
"app/commerce-modules/store/page.mdx": "2024-12-25T16:09:12.124Z",
"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/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/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-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/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/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/contribution-guidelines/docs/page.mdx": "2024-12-12T11:06:12.250Z",
"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/_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/api-key/page.mdx": "2024-12-09T14:47:01.509Z",
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-09T15:32:07.594Z",
"app/commerce-modules/api-key/page.mdx": "2024-12-25T15:55:02.846Z",
"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/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/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",
@@ -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.validateCallback/page.mdx": "2024-12-10T14:54:57.318Z",
"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/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",
@@ -986,7 +971,7 @@ export const generatedEditDates = {
"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/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.FilterQuery/page.mdx": "2024-11-27T16:33:40.706Z",
"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/two-column/page.mdx": "2024-10-07T11:16:10.092Z",
"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/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/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/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",
@@ -5632,7 +5617,7 @@ export const generatedEditDates = {
"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/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/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",
@@ -127,10 +127,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/api-key/concepts/page.mdx",
"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",
"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",
"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",
"pathname": "/commerce-modules/auth/module-options"
@@ -203,10 +195,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/cart/events/page.mdx",
"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",
"pathname": "/commerce-modules/cart/extend"
@@ -227,10 +215,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/cart/tax-lines/page.mdx",
"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",
"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",
"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",
"pathname": "/commerce-modules/customer/extend"
@@ -307,10 +287,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/inventory/concepts/page.mdx",
"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",
"pathname": "/commerce-modules/inventory/inventory-in-flows"
@@ -383,10 +359,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/page.mdx",
"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",
"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",
"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",
"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",
"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",
"pathname": "/commerce-modules/product/extend"
@@ -511,10 +475,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/promotion/concepts/page.mdx",
"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",
"pathname": "/commerce-modules/promotion/extend"
@@ -535,10 +495,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/region/events/page.mdx",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"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",
"pathname": "/commerce-modules/tax/module-options"
@@ -643,10 +583,6 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/commerce-modules/user/events/page.mdx",
"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",
"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 { generateTags } from "tags"
import { main as generateSlugChanges } from "./generate-slug-changes.mjs"
import { main as generateFilesMap } from "./generate-files-map.mjs"
import { sidebar } from "../sidebar.mjs"
import path from "path"
async function main() {
await generateTags(path.resolve("..", "..", "packages", "tags"))
await generateSidebar(sidebar)
await generateSlugChanges()
await generateFilesMap()
+40 -5
View File
@@ -11,13 +11,12 @@ export const apiKeySidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/api-key/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+47 -8
View File
@@ -16,13 +16,12 @@ export const authSidebar = [
title: "Module Options",
},
{
type: "link",
path: "/commerce-modules/auth/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
type: "link",
@@ -47,8 +46,11 @@ export const authSidebar = [
],
},
{
type: "sub-category",
title: "Guides",
type: "category",
title: "Server Guides",
autogenerate_tags: "server+auth",
initialOpen: false,
autogenerate_as_ref: true,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
@@ -89,8 +127,9 @@ export const authSidebar = [
],
},
{
type: "sub-category",
type: "category",
title: "References",
initialOpen: false,
children: [
{
type: "link",
+54 -10
View File
@@ -11,18 +11,12 @@ export const cartSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/cart/examples",
title: "Examples",
type: "separator",
},
{
type: "link",
path: "/commerce-modules/cart/extend",
title: "Extend Module",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+46 -4
View File
@@ -11,13 +11,12 @@ export const currencySidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/currency/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
title: "References",
initialOpen: false,
children: [
{
type: "link",
path: "/references/currency",
title: "Main Service Reference",
isChildSidebar: true,
childSidebarTitle: "Cart Module's Main Service Reference",
childSidebarTitle: "Currency Module's Main Service Reference",
children: [
{
type: "category",
+54 -10
View File
@@ -11,18 +11,12 @@ export const customerSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/customer/examples",
title: "Examples",
type: "separator",
},
{
type: "link",
path: "/commerce-modules/customer/extend",
title: "Extend Module",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+48 -5
View File
@@ -16,8 +16,12 @@ export const fulfillmentSidebar = [
title: "Module Options",
},
{
type: "sub-category",
type: "separator",
},
{
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
type: "link",
@@ -47,8 +51,11 @@ export const fulfillmentSidebar = [
],
},
{
type: "sub-category",
title: "Guides",
type: "category",
title: "Server Guides",
autogenerate_tags: "server+fulfillment",
initialOpen: false,
autogenerate_as_ref: true,
children: [
{
type: "link",
@@ -56,15 +63,51 @@ export const fulfillmentSidebar = [
title: "Create Fulfillment Provider Module",
},
{
type: "link",
type: "ref",
path: "/integrations/guides/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",
initialOpen: false,
children: [
{
type: "link",
+47 -5
View File
@@ -11,13 +11,12 @@ export const inventorySidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/inventory/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+49 -2
View File
@@ -11,8 +11,12 @@ export const orderSidebar = [
title: "Overview",
},
{
type: "sub-category",
type: "separator",
},
{
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+48 -9
View File
@@ -16,13 +16,12 @@ export const paymentSidebar = [
title: "Module Options",
},
{
type: "link",
path: "/commerce-modules/payment/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
type: "link",
@@ -57,8 +56,11 @@ export const paymentSidebar = [
],
},
{
type: "sub-category",
title: "Guides",
type: "category",
title: "Server Guides",
autogenerate_tags: "server+payment",
initialOpen: false,
autogenerate_as_ref: true,
children: [
{
type: "link",
@@ -73,8 +75,44 @@ export const paymentSidebar = [
],
},
{
type: "sub-category",
title: "Payment Providers",
type: "category",
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: [
{
type: "link",
@@ -84,8 +122,9 @@ export const paymentSidebar = [
],
},
{
type: "sub-category",
type: "category",
title: "References",
initialOpen: false,
children: [
{
type: "link",
+47 -5
View File
@@ -11,13 +11,12 @@ export const pricingSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/pricing/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+62 -13
View File
@@ -11,18 +11,12 @@ export const productSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/product/examples",
title: "Examples",
type: "separator",
},
{
type: "link",
path: "/commerce-modules/product/extend",
title: "Extend Module",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
type: "link",
@@ -32,13 +26,68 @@ export const productSidebar = [
],
},
{
type: "sub-category",
title: "Guides",
autogenerate_path: "/commerce-modules/product/guides",
type: "category",
title: "Server Guides",
autogenerate_tags: "server+product",
initialOpen: false,
autogenerate_as_ref: true,
children: [
{
type: "link",
path: "/commerce-modules/product/extend",
title: "Extend Module",
},
{
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: "sub-category",
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",
initialOpen: false,
children: [
{
type: "link",
+54 -10
View File
@@ -11,18 +11,12 @@ export const promotionSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/promotion/examples",
title: "Examples",
type: "separator",
},
{
type: "link",
path: "/commerce-modules/promotion/extend",
title: "Extend Module",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+47 -5
View File
@@ -11,13 +11,12 @@ export const regionSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/region/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+47 -5
View File
@@ -11,13 +11,12 @@ export const salesChannelSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/sales-channel/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+47 -5
View File
@@ -11,13 +11,12 @@ export const stockLocationSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/stock-location/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+47 -5
View File
@@ -11,13 +11,12 @@ export const storeSidebar = [
title: "Overview",
},
{
type: "link",
path: "/commerce-modules/store/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+45 -7
View File
@@ -16,13 +16,12 @@ export const taxSidebar = [
title: "Module Options",
},
{
type: "link",
path: "/commerce-modules/tax/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
type: "category",
title: "Concepts",
initialOpen: false,
children: [
{
type: "link",
@@ -42,8 +41,11 @@ export const taxSidebar = [
],
},
{
type: "sub-category",
title: "Guides",
type: "category",
title: "Server Guides",
autogenerate_tags: "server+tax",
initialOpen: false,
autogenerate_as_ref: true,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
+43 -6
View File
@@ -16,13 +16,14 @@ export const userSidebar = [
title: "Module Options",
},
{
type: "link",
path: "/commerce-modules/user/examples",
title: "Examples",
type: "separator",
},
{
type: "sub-category",
title: "Guides",
type: "category",
title: "Server Guides",
autogenerate_tags: "server+user",
initialOpen: false,
autogenerate_as_ref: true,
children: [
{
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",
initialOpen: false,
children: [
{
type: "link",
@@ -88,23 +88,35 @@ async function getAutogeneratedSidebarItems(
}
async function getAutogeneratedTagSidebarItems(
tags: string
tags: string,
type: "link" | "ref",
existingChildren?: RawSidebarItem[]
): Promise<ItemsToAdd[]> {
const items: ItemsToAdd[] = []
const parsedTags = parseTags(tags)
items.push(
...parsedTags.map(
(tagItem) =>
({
type: "link",
...tagItem,
}) as ItemsToAdd
)
...parsedTags
.filter((tagItem) => {
return existingChildren?.every((existingItem) => {
if (existingItem.type !== "link" && existingItem.type !== "ref") {
return true
}
return existingItem.path !== tagItem.path
})
})
.map(
(tagItem) =>
({
type,
...tagItem,
}) as ItemsToAdd
)
)
return sidebarAttachHrefCommonOptions(items)
return sidebarAttachHrefCommonOptions([...(existingChildren || []), ...items])
}
async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
@@ -130,7 +142,9 @@ async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
})
} else if (item.autogenerate_tags) {
item.children = await getAutogeneratedTagSidebarItems(
item.autogenerate_tags
item.autogenerate_tags,
item.autogenerate_as_ref ? "ref" : "link",
item.children
)
} else if (
item.custom_autogenerate &&
@@ -138,21 +152,29 @@ async function checkItem(item: RawSidebarItem): Promise<RawSidebarItem> {
) {
item.children = await customGenerators[item.custom_autogenerate]()
} else if (item.children) {
item.children = await Promise.all(
item.children.map(async (childItem) => await checkItem(childItem))
)
item.children = await checkItems(item.children)
}
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[]) {
const path = await import("path")
const { writeFileSync } = await import("fs")
const normalizedSidebar = await Promise.all(
sidebar.map(async (item) => await checkItem(item))
)
const normalizedSidebar = await checkItems(sidebar)
const generatedDirPath = path.resolve("generated")
@@ -27,14 +27,11 @@ const getIntersectionTags = (tags: string): Tag => {
.map((tagName) => getTagItems(tagName))
.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 []
}
if (tagsToIntersect.length === 1) {
return tagsToIntersect[0]
}
return tagsToIntersect[0].filter((tagItem) => {
return tagsToIntersect
.slice(1)
@@ -4,7 +4,12 @@ import React, { useMemo } from "react"
import clsx from "clsx"
import Link from "next/link"
import { SidebarItemLink } from "types"
import { CurrentItemsState, useSidebar, useSiteConfig } from "../../providers"
import {
CurrentItemsState,
isSidebarItemLink,
useSidebar,
useSiteConfig,
} from "../../providers"
import { Button } from "../Button"
import { TriangleRightMini } from "@medusajs/icons"
@@ -29,22 +34,20 @@ export const Breadcrumbs = () => {
tempBreadcrumbItems = getBreadcrumbsOfItem(item.previousSidebar)
}
const parentPath =
item.parentItem?.type === "link"
? getLinkPath(item.parentItem)
: (item.parentItem?.type === "category" &&
breadcrumbOptions?.showCategories) ||
item.parentItem?.type === "sub-category"
? "#"
: undefined
const firstItemPath =
item.default[0].type === "link"
? getLinkPath(item.default[0])
: (item.default[0].type === "category" &&
breadcrumbOptions?.showCategories) ||
item.default[0].type === "sub-category"
? "#"
: undefined
const parentPath = isSidebarItemLink(item.parentItem)
? getLinkPath(item.parentItem)
: (item.parentItem?.type === "category" &&
breadcrumbOptions?.showCategories) ||
item.parentItem?.type === "sub-category"
? "#"
: undefined
const firstItemPath = isSidebarItemLink(item.default[0])
? getLinkPath(item.default[0])
: (item.default[0].type === "category" &&
breadcrumbOptions?.showCategories) ||
item.default[0].type === "sub-category"
? "#"
: undefined
const breadcrumbPath = parentPath || firstItemPath || "/"
@@ -76,7 +79,7 @@ export const Breadcrumbs = () => {
breadcrumbOptions?.showCategories)
) {
tempBreadcrumbItems.set(
sidebarActiveItem.parentItem.type === "link"
isSidebarItemLink(sidebarActiveItem.parentItem)
? getLinkPath(sidebarActiveItem.parentItem) || "#"
: "#",
sidebarActiveItem.parentItem.chapterTitle ||
@@ -1,193 +1,10 @@
"use client"
import React, { useMemo } from "react"
import { Card, CardList, H2, useSidebar } from "../.."
import { InteractiveSidebarItem, SidebarItem, SidebarItemLink } from "types"
import slugify from "slugify"
import { MDXComponents } from "../.."
import React from "react"
import { useChildDocs, UseChildDocsProps } from "../.."
const Hr = MDXComponents["hr"] as () => React.JSX.Element
export const ChildDocs = (props: UseChildDocsProps) => {
const { component } = useChildDocs(props)
type ChildDocsProps = {
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)}</>
return <>{component}</>
}
@@ -120,3 +120,5 @@ export const MDXComponents: MDXComponentsType = {
return <ZoomImg {...rest} />
},
}
export const Hr = MDXComponents["hr"] as () => React.JSX.Element
@@ -30,6 +30,7 @@ export const SidebarItem = ({
case "sub-category":
return <SidebarItemSubCategory item={item} {...props} />
case "link":
case "ref":
return <SidebarItemLink item={item} {...props} />
case "separator":
return <DottedSeparator />
@@ -1,7 +1,7 @@
"use client"
import React, { Suspense, useMemo, useRef } from "react"
import { useSidebar } from "@/providers"
import { isSidebarItemLink, useSidebar } from "@/providers"
import clsx from "clsx"
import { Loading } from "@/components"
import { SidebarItem } from "./Item"
@@ -139,7 +139,7 @@ export const Sidebar = ({
const itemKey =
item.type === "separator"
? index
: item.type === "link"
: isSidebarItemLink(item)
? `${item.path}-${index}`
: `${item.title}-${index}`
return (
+1
View File
@@ -1,4 +1,5 @@
export * from "./use-active-on-scroll"
export * from "./use-child-docs"
export * from "./use-click-outside"
export * from "./use-collapsible"
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,
useState,
} from "react"
import { useSidebar } from "../Sidebar"
import { isSidebarItemLink, useSidebar } from "../Sidebar"
import { usePrevious } from "@uidotdev/usehooks"
import { InteractiveSidebarItem, SidebarItem } from "types"
@@ -56,7 +56,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
return undefined
}
return children[0].type === "link"
return isSidebarItemLink(children[0])
? {
...children[0],
parent: item,
@@ -69,7 +69,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
): SidebarItemWithParent[] | undefined => {
return item.children?.filter(
(childItem) =>
childItem.type === "link" ||
isSidebarItemLink(childItem) ||
(childItem.type !== "separator" &&
getChildrenWithPages(childItem)?.length)
) as SidebarItemWithParent[]
@@ -95,7 +95,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
parent: item,
}
}
} else if (item.type === "link") {
} else if (isSidebarItemLink(item)) {
foundItem = item
}
@@ -115,7 +115,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
return false
}
if (item.type === "link") {
if (isSidebarItemLink(item)) {
foundItem = item
} else if (item.children?.length) {
const childItem = getNextItem(item.children, -1)
@@ -139,7 +139,7 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
}
result.foundActive = currentItems.some((item, index) => {
if (item.type === "link" && item.path === activePath) {
if (isSidebarItemLink(item) && item.path === activePath) {
if (index !== 0) {
result.prevItem = getPrevItem(currentItems, index)
}
@@ -161,8 +161,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
result.prevItem = childrenResult.prevItem
result.nextItem = childrenResult.nextItem
if (!result.prevItem) {
result.prevItem =
item.type === "link" ? item : getPrevItem(currentItems, index)
result.prevItem = isSidebarItemLink(item)
? item
: getPrevItem(currentItems, index)
}
if (!result.nextItem && index !== currentItems.length - 1) {
@@ -186,7 +187,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
result.prevItem
? {
title: result.prevItem.title,
link: result.prevItem.type === "link" ? result.prevItem.path : "",
link: isSidebarItemLink(result.prevItem)
? result.prevItem.path
: "",
parentTitle:
result.prevItem.parent?.type !== "separator"
? result.prevItem.parent?.title
@@ -198,7 +201,9 @@ export const PaginationProvider = ({ children }: PaginationProviderProps) => {
result.nextItem
? {
title: result.nextItem.title,
link: result.nextItem.type === "link" ? result.nextItem.path : "",
link: isSidebarItemLink(result.nextItem)
? result.nextItem.path
: "",
parentTitle:
result.nextItem.parent?.type !== "separator"
? result.nextItem.parent?.title
@@ -95,13 +95,26 @@ export type ActionType =
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 => {
if (itemA.type === "separator" || itemB.type === "separator") {
return false
}
const hasSameTitle = itemA.title === itemB.title
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
}
@@ -116,8 +129,8 @@ const findItem = (
if (i.type === "separator") {
return false
}
if (areItemsEqual(item as SidebarItem, i) && i.type === "link") {
foundItem = i
if (areItemsEqual(item as SidebarItem, i)) {
foundItem = i as SidebarItemLink
} else if (checkChildren && i.children) {
foundItem = findItem(i.children, item)
if (foundItem && !foundItem.parentItem) {
@@ -143,7 +156,7 @@ const getLinksMap = (
return
}
if (item.type === "link") {
if (isSidebarItemLink(item)) {
map.set(item.path, {
...item,
parentItem,
@@ -246,7 +259,7 @@ export const reducer = (
: [...(i.children || []), ...items],
loaded: parent.changeLoaded
? true
: i.type === "link"
: isSidebarItemLink(i)
? i.loaded
: true,
}
@@ -355,8 +368,8 @@ export const SidebarProvider = ({
}
const isLinkActive = useCallback(
(item: SidebarItem, checkChildren = false): boolean => {
if (item.type !== "link") {
(item: SidebarItem, checkChildren = false, checkRef = true): boolean => {
if (!isSidebarItemLink(item, checkRef)) {
return false
}
@@ -399,24 +412,28 @@ export const SidebarProvider = ({
if (item.type === "separator") {
return false
}
if (item.isChildSidebar && isLinkActive(item)) {
if (item.isChildSidebar && isLinkActive(item, false, false)) {
currentSidebar = item
return true
}
if (!item.children?.length) {
return false
}
if (!currentSidebar && item.children?.length) {
const childSidebar =
getCurrentSidebar(item.children) ||
(activePath
? findItem(item.children, {
path: activePath || undefined,
type: "link",
})
: undefined)
const childSidebar =
getCurrentSidebar(item.children) ||
(activePath
? findItem(item.children, {
path: activePath,
type: "link",
})
: undefined)
if (childSidebar) {
currentSidebar = childSidebar.isChildSidebar ? childSidebar : item
}
}
currentSidebar = childSidebar
? childSidebar.isChildSidebar
? childSidebar
: item
: undefined
return currentSidebar !== undefined
})
@@ -434,7 +451,7 @@ export const SidebarProvider = ({
const previousSidebar = currentItems.previousSidebar || items
const backItem = previousSidebar.default.find(
(item) => item.type === "link" && !item.isChildSidebar
(item) => isSidebarItemLink(item) && !item.isChildSidebar
) as SidebarItemLink
if (!backItem) {
@@ -472,7 +489,7 @@ export const SidebarProvider = ({
const handleScroll = () => {
if (getScrolledTop(resolvedScrollableElement) === 0) {
const firstItemPath =
items.default.length && items.default[0].type === "link"
items.default.length && isSidebarItemLink(items.default[0])
? items.default[0].path
: ""
setActivePath(firstItemPath)
@@ -548,8 +565,8 @@ export const SidebarProvider = ({
) {
const { children, ...parentItem } = currentSidebar
const hasPreviousSidebar =
currentItems?.previousSidebar?.parentItem?.type === "link" &&
parentItem.type === "link" &&
isSidebarItemLink(currentItems?.previousSidebar?.parentItem) &&
isSidebarItemLink(parentItem) &&
currentItems.previousSidebar.parentItem.path !== parentItem.path
setCurrentItems({
+1 -1
View File
@@ -1,7 +1,7 @@
export const apiKey = [
{
"title": "Use a Publishable API Key in the Storefront",
"path": "/app/storefront-development/publishable-api-keys"
"path": "/storefront-development/publishable-api-keys"
},
{
"title": "createApiKeysStep",
+6 -6
View File
@@ -1,27 +1,27 @@
export const auth = [
{
"title": "Log-out Customer in Storefront",
"path": "/app/storefront-development/customers/log-out"
"path": "/storefront-development/customers/log-out"
},
{
"title": "Login Customer in Storefront",
"path": "/app/storefront-development/customers/login"
"path": "/storefront-development/customers/login"
},
{
"title": "Register Customer in Storefront",
"path": "/app/storefront-development/customers/register"
"path": "/storefront-development/customers/register"
},
{
"title": "Reset Customer Password in Storefront",
"path": "/app/storefront-development/customers/reset-password"
"path": "/storefront-development/customers/reset-password"
},
{
"title": "Retrieve Customer in Storefront",
"path": "/app/storefront-development/customers/retrieve"
"path": "/storefront-development/customers/retrieve"
},
{
"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",
+11 -11
View File
@@ -1,47 +1,47 @@
export const cart = [
{
"title": "Create Cart Context in Storefront",
"path": "/app/storefront-development/cart/context"
"path": "/storefront-development/cart/context"
},
{
"title": "Create Cart in Storefront",
"path": "/app/storefront-development/cart/create"
"path": "/storefront-development/cart/create"
},
{
"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",
"path": "/app/storefront-development/cart/retrieve"
"path": "/storefront-development/cart/retrieve"
},
{
"title": "Update Cart in Storefront",
"path": "/app/storefront-development/cart/update"
"path": "/storefront-development/cart/update"
},
{
"title": "Checkout Step 2: Enter Address",
"path": "/app/storefront-development/checkout/address"
"path": "/storefront-development/checkout/address"
},
{
"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",
"path": "/app/storefront-development/checkout/email"
"path": "/storefront-development/checkout/email"
},
{
"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",
"path": "/app/storefront-development/checkout/payment/stripe"
"path": "/storefront-development/checkout/payment/stripe"
},
{
"title": "Checkout Step 3: Choose Shipping Method",
"path": "/app/storefront-development/checkout/shipping"
"path": "/storefront-development/checkout/shipping"
},
{
"title": "addShippingMethodToCartStep",
+9 -9
View File
@@ -1,39 +1,39 @@
export const customer = [
{
"title": "Manage Customer Addresses in Storefront",
"path": "/app/storefront-development/customers/addresses"
"path": "/storefront-development/customers/addresses"
},
{
"title": "Customer Context in Storefront",
"path": "/app/storefront-development/customers/context"
"path": "/storefront-development/customers/context"
},
{
"title": "Log-out Customer in Storefront",
"path": "/app/storefront-development/customers/log-out"
"path": "/storefront-development/customers/log-out"
},
{
"title": "Login Customer in Storefront",
"path": "/app/storefront-development/customers/login"
"path": "/storefront-development/customers/login"
},
{
"title": "Edit Customer Profile in Storefront",
"path": "/app/storefront-development/customers/profile"
"path": "/storefront-development/customers/profile"
},
{
"title": "Register Customer in Storefront",
"path": "/app/storefront-development/customers/register"
"path": "/storefront-development/customers/register"
},
{
"title": "Reset Customer Password in Storefront",
"path": "/app/storefront-development/customers/reset-password"
"path": "/storefront-development/customers/reset-password"
},
{
"title": "Retrieve Customer in Storefront",
"path": "/app/storefront-development/customers/retrieve"
"path": "/storefront-development/customers/retrieve"
},
{
"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",
+1 -1
View File
@@ -1,7 +1,7 @@
export const fulfillment = [
{
"title": "Checkout Step 3: Choose Shipping Method",
"path": "/app/storefront-development/checkout/shipping"
"path": "/storefront-development/checkout/shipping"
},
{
"title": "validateCartShippingOptionsStep",
+18 -17
View File
@@ -1,31 +1,32 @@
export * from "./product.js"
export * from "./tax.js"
export * from "./server.js"
export * from "./storefront.js"
export * from "./order.js"
export * from "./payment.js"
export * from "./cart.js"
export * from "./order.js"
export * from "./stripe.js"
export * from "./fulfillment.js"
export * from "./customer.js"
export * from "./auth.js"
export * from "./pricing.js"
export * from "./product-collection.js"
export * from "./tax.js"
export * from "./inventory.js"
export * from "./publishable-api-key.js"
export * from "./region.js"
export * from "./pricing.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 "./sales-channel.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 "./store.js"
export * from "./logger.js"
export * from "./promotion.js"
export * from "./locking.js"
export * from "./file.js"
export * from "./user.js"
export * from "./product-category.js"
export * from "./stock-location.js"
export * from "./cart.js"
export * from "./query.js"
export * from "./locking.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 = [
{
"title": "Retrieve Product Variant's Inventory in Storefront",
"path": "/app/storefront-development/products/inventory"
"path": "/storefront-development/products/inventory"
},
{
"title": "confirmInventoryStep",
+1 -1
View File
@@ -1,7 +1,7 @@
export const order = [
{
"title": "Checkout Step 5: Complete Cart",
"path": "/app/storefront-development/checkout/complete-cart"
"path": "/storefront-development/checkout/complete-cart"
},
{
"title": "addOrderTransactionStep",
+7 -3
View File
@@ -1,15 +1,19 @@
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",
"path": "/app/storefront-development/checkout/complete-cart"
"path": "/storefront-development/checkout/complete-cart"
},
{
"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",
"path": "/app/storefront-development/checkout/payment/stripe"
"path": "/storefront-development/checkout/payment/stripe"
},
{
"title": "createPaymentCollectionsStep",
+6 -6
View File
@@ -1,27 +1,27 @@
export const pricing = [
{
"title": "Get Variant Prices",
"path": "/app/commerce-modules/product/guides/price"
"path": "/commerce-modules/product/guides/price"
},
{
"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",
"path": "/app/storefront-development/products/price/examples/sale-price"
"path": "/storefront-development/products/price/examples/sale-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",
"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",
"path": "/app/storefront-development/products/price"
"path": "/storefront-development/products/price"
},
{
"title": "createShippingOptionsPriceSetsStep",
@@ -1,18 +1,18 @@
export const productCategory = [
{
"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",
"path": "/app/storefront-development/products/categories/nested-categories"
"path": "/storefront-development/products/categories/nested-categories"
},
{
"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",
"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