docs: revise commerce modules overview pages (#10738)
* revise API Key Module overview * revise auth module * support ref sidebar items * remove examples * revise cart module * revise currency * revise customer module * revise fulfillment module * revise inventory module * revise order module * revise payment * revise pricing module * revise product module * revise promotion module * revise region module * revise sales channel module * revise stock location module * revise store module * revise tax module * revise user module * lint content + fix snippets
This commit is contained in:
1 parent
c8f9938865
commit
ebca8fed28
112 files changed
+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" />
|
||||
+12
-12
@@ -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.
|
||||
+20
-20
@@ -45,8 +45,8 @@ To retrieve the API keys associated with a sales channel with [Query](!docs!/lea
|
||||
const { data: salesChannels } = await query.graph({
|
||||
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.
|
||||
+12
-12
@@ -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
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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,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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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,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,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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
Reference in new issue
Block a user