docs: cache revalidation in next.js storefront + storefront totals (#11887)

* initial changes

* generated sidebar
This commit is contained in:
Shahed Nasser
2025-03-18 12:20:09 +02:00
committed by GitHub
parent ff3885500c
commit 2b2b65f5f7
18 changed files with 647 additions and 83 deletions
@@ -92,10 +92,10 @@ module.exports = defineConfig({
options: {
providers: [
// add providers here...
]
}
}
]
],
},
},
],
})
```
@@ -35,10 +35,10 @@ module.exports = defineConfig({
// and you have other Locking Module Providers registered.
is_default: true,
},
]
}
}
]
],
},
},
],
})
```
@@ -75,10 +75,10 @@ module.exports = defineConfig({
id: "locking-postgres",
is_default: true,
},
]
}
}
]
],
},
},
],
})
```
@@ -41,12 +41,12 @@ module.exports = defineConfig({
is_default: true,
options: {
redisUrl: process.env.LOCKING_REDIS_URL,
}
},
},
]
}
}
]
],
},
},
],
})
```
@@ -257,12 +257,12 @@ module.exports = defineConfig({
is_default: true,
options: {
// ...
}
},
},
]
}
}
]
],
},
},
],
})
```
@@ -0,0 +1,88 @@
export const metadata = {
title: `Revalidate Cache in Next.js Starter Storefront`,
}
# {metadata.title}
In this guide, you'll learn about the general approach to revalidating cache in the Next.js Starter Storefront when data is updated in the Medusa application.
## Approach Overview
By default, the data that the Next.js Starter Storefront retrieves from the Medusa application is cached in the browser. This cache is used to improve the performance and speed of the storefront.
In some cases, you may need to revalidate the cache in the storefront when data is updated in the Medusa application. For example, when a product variant's price is updated in the Medusa application, you may want to revalidate the cache in the storefront to reflect the updated price.
You're free to choose the approach that works for your use case, custom requirements, and tech stack. The approach that Medusa recommends is:
1. Create a [subscriber](!docs!/learn/fundamentals/events-and-subscribers) in the Medusa application that listens for the event that triggers the data update. For example, you can listen to the `product.updated` event.
2. In the subscriber, send a request to a custom endpoint in the Next.js Starter Storefront to trigger the cache revalidation.
3. Create the custom endpoint in the Next.js Starter Storefront that listens for the request from the subscriber and revalidates the cache.
<Note title="Tip">
Refer to the [Events Reference](../../../events-reference/page.mdx) for a full list of events that the Medusa application emits.
</Note>
---
## Example: Revalidating Cache for Product Update
Consider you want to revalidate the cache in the Next.js Starter Storefront whenever a product is updated.
Start by creating the following subscriber in the Medusa application:
```ts
import type {
SubscriberArgs,
SubscriberConfig,
} from "@medusajs/framework"
export default async function productUpdatedHandler({
event: { data },
container,
}: SubscriberArgs<{ id: string }>) {
// send request to next.js storefront to revalidate cache
await fetch(`${process.env.STOREFRONT_URL}/api/revalidate?tags=products`)
}
export const config: SubscriberConfig = {
event: "product.updated",
}
```
In the subscriber, you send a request to the custom endpoint `/api/revalidate` in the Next.js Starter Storefront. The request includes the query parameter `tags=product-${data.id}` to specify the cache that needs to be revalidated.
<Note>
Make sure to set the `STOREFRONT_URL` environment variable in the Medusa application to the URL of the Next.js Starter Storefront.
</Note>
Then, create in the Next.js Starter Storefront the custom endpoint that listens for the request and revalidates the cache:
```ts title="src/app/api/revalidate/route.ts"
import { NextRequest, NextResponse } from "next/server"
import { revalidateTag } from "next/cache"
import { getCacheTag } from "../../../lib/data/cookies"
export async function GET(req: NextRequest) {
const searchParams = req.nextUrl.searchParams
const tags = searchParams.get("tags") as string
if (!tags) {
return NextResponse.json({ error: "No tags provided" }, { status: 400 })
}
const tagsArray = tags.split(",")
await Promise.all(
tagsArray.map(async (tag) => {
const cacheTag = await getCacheTag(tag)
// revalidate cache for the tag
revalidateTag(cacheTag)
})
)
return NextResponse.json({ message: "Revalidated" }, { status: 200 })
}
```
@@ -585,7 +585,7 @@ export const vendorWorkflowHighlights = [
```ts title="src/workflows/marketplace/create-vendor/index.ts" highlights={vendorWorkflowHighlights}
import {
createWorkflow,
WorkflowResponse
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
setAuthAppMetadataStep,
@@ -617,7 +617,7 @@ const createVendorWorkflow = createWorkflow(
const vendorAdminData = transform({
input,
vendor
vendor,
}, (data) => {
return {
...data.input.admin,
@@ -701,13 +701,13 @@ export const vendorRouteSchemaHighlights = [
```ts title="src/api/vendors/route.ts" highlights={vendorRouteSchemaHighlights}
import {
AuthenticatedMedusaRequest,
MedusaResponse
MedusaResponse,
} from "@medusajs/framework/http"
import { MedusaError } from "@medusajs/framework/utils"
import { z } from "zod"
import createVendorWorkflow, {
CreateVendorWorkflowInput
} from "../../workflows/marketplace/create-vendor";
CreateVendorWorkflowInput,
} from "../../workflows/marketplace/create-vendor"
export const PostVendorCreateSchema = z.object({
name: z.string(),
@@ -716,8 +716,8 @@ export const PostVendorCreateSchema = z.object({
admin: z.object({
email: z.string(),
first_name: z.string().optional(),
last_name: z.string().optional()
}).strict()
last_name: z.string().optional(),
}).strict(),
}).strict()
type RequestBody = z.infer<typeof PostVendorCreateSchema>
@@ -750,7 +750,7 @@ export const POST = async (
input: {
...vendorData,
authIdentityId: req.auth_context.auth_identity_id,
} as CreateVendorWorkflowInput
} as CreateVendorWorkflowInput,
})
res.json({
@@ -788,7 +788,7 @@ You define middlewares in Medusa in the `src/api/middlewares.ts` special file. S
import {
defineMiddlewares,
authenticate,
validateAndTransformBody
validateAndTransformBody,
} from "@medusajs/framework/http"
import { PostVendorCreateSchema } from "./vendors/route"
@@ -960,12 +960,12 @@ import { CreateProductWorkflowInputDTO } from "@medusajs/framework/types"
import {
createWorkflow,
transform,
WorkflowResponse
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
createProductsWorkflow,
createRemoteLinkStep,
useQueryGraphStep
useQueryGraphStep,
} from "@medusajs/medusa/core-flows"
import { MARKETPLACE_MODULE } from "../../../modules/marketplace"
import { Modules } from "@medusajs/framework/utils"
@@ -988,22 +988,22 @@ const createVendorProductWorkflow = createWorkflow(
const productData = transform({
input,
stores
stores,
}, (data) => {
return {
products: [{
...data.input.product,
sales_channels: [
{
id: data.stores[0].default_sales_channel_id
}
]
}]
id: data.stores[0].default_sales_channel_id,
},
],
}],
}
})
const createdProducts = createProductsWorkflow.runAsStep({
input: productData
input: productData,
})
// TODO link vendor and products
@@ -1029,23 +1029,23 @@ const { data: vendorAdmins } = useQueryGraphStep({
entity: "vendor_admin",
fields: ["vendor.id"],
filters: {
id: input.vendor_admin_id
}
id: input.vendor_admin_id,
},
}).config({ name: "retrieve-vendor-admins" })
const linksToCreate = transform({
input,
createdProducts,
vendorAdmins
vendorAdmins,
}, (data) => {
return data.createdProducts.map((product) => {
return {
[MARKETPLACE_MODULE]: {
vendor_id: data.vendorAdmins[0].vendor.id
vendor_id: data.vendorAdmins[0].vendor.id,
},
[Modules.PRODUCT]: {
product_id: product.id
}
product_id: product.id,
},
}
})
})
@@ -1056,12 +1056,12 @@ const { data: products } = useQueryGraphStep({
entity: "product",
fields: ["*", "variants.*"],
filters: {
id: createdProducts[0].id
}
id: createdProducts[0].id,
},
}).config({ name: "retrieve-products" })
return new WorkflowResponse({
product: products[0]
product: products[0],
})
```
@@ -1086,8 +1086,8 @@ Create the file `src/api/vendors/products/route.ts` with the following content:
```ts title="src/api/vendors/products/route.ts"
import {
AuthenticatedMedusaRequest,
MedusaResponse
} from "@medusajs/framework/http";
MedusaResponse,
} from "@medusajs/framework/http"
import {
HttpTypes,
} from "@medusajs/framework/types"
@@ -1101,12 +1101,12 @@ export const POST = async (
.run({
input: {
vendor_admin_id: req.auth_context.actor_id,
product: req.validatedBody
}
product: req.validatedBody,
},
})
res.json({
product: result.product
product: result.product,
})
}
```
@@ -1133,9 +1133,9 @@ export default defineMiddlewares({
method: ["POST"],
middlewares: [
validateAndTransformBody(AdminCreateProduct),
]
}
]
],
},
],
})
```
@@ -1202,7 +1202,7 @@ To create the API route that retrieves the vendors products, add the followin
```ts title="src/api/vendors/products/route.ts"
// other imports...
import {
ContainerRegistrationKeys
ContainerRegistrationKeys,
} from "@medusajs/framework/utils"
export const GET = async (
@@ -1217,13 +1217,13 @@ export const GET = async (
filters: {
id: [
// ID of the authenticated vendor admin
req.auth_context.actor_id
req.auth_context.actor_id,
],
},
})
res.json({
products: vendorAdmin.vendor.products
products: vendorAdmin.vendor.products,
})
}
```
@@ -1339,8 +1339,8 @@ const groupVendorItemsStep = createStep(
entity: "product",
fields: ["vendor.*"],
filters: {
id: [item.product_id]
}
id: [item.product_id],
},
})
const vendorId = product.vendor?.id
@@ -1350,12 +1350,12 @@ const groupVendorItemsStep = createStep(
}
vendorsItems[vendorId] = [
...(vendorsItems[vendorId] || []),
item
item,
]
}))
return new StepResponse({
vendorsItems
vendorsItems,
})
}
)
@@ -1815,7 +1815,7 @@ export const getOrderHighlights = [
]
```ts title="src/api/vendors/orders/route.ts" highlights={getOrderHighlights}
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http";
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { ContainerRegistrationKeys } from "@medusajs/framework/utils"
import { getOrdersListWorkflow } from "@medusajs/medusa/core-flows"
@@ -1829,8 +1829,8 @@ export const GET = async (
entity: "vendor_admin",
fields: ["vendor.orders.*"],
filters: {
id: [req.auth_context.actor_id]
}
id: [req.auth_context.actor_id],
},
})
const { result: orders } = await getOrdersListWorkflow(req.scope)
@@ -1854,14 +1854,14 @@ export const GET = async (
],
variables: {
filters: {
id: vendorAdmin.vendor.orders.map((order) => order.id)
}
}
}
id: vendorAdmin.vendor.orders.map((order) => order.id),
},
},
},
})
res.json({
orders
orders,
})
}
```
@@ -0,0 +1,140 @@
---
tags:
- cart
- storefront
---
import { CodeTabs, CodeTab, Table } from "docs-ui"
export const metadata = {
title: `Show Cart Totals`,
}
# {metadata.title}
In this guide, you'll learn how to show the cart totals in the checkout flow. This is usually shown as part of the checkout and cart pages.
## Cart Total Fields
The `Cart` object has various fields related to its totals, which you can check out in the [Store API reference](!api!/store#carts_cart_schema).
The fields that are most commonly used are:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Field</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`subtotal`
</Table.Cell>
<Table.Cell>
The cart's subtotal excluding taxes and shipping, and including discounts.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`discount_total`
</Table.Cell>
<Table.Cell>
The total discounts or promotions applied to the cart.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`shipping_total`
</Table.Cell>
<Table.Cell>
The total shipping cost.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`tax_total`
</Table.Cell>
<Table.Cell>
The total tax amount.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`total`
</Table.Cell>
<Table.Cell>
The total amount of the cart including all taxes, shipping, and discounts.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
---
## Example: React Storefront
Here's an example of how you can show the cart totals in a React component:
export const highlights = [
["3", "useCart", "The `useCart` hook was defined in the Cart React Context documentation."],
["8", "formatPrice", "A function to format a price using the `Intl.NumberFormat` API."],
["23", "formatPrice", "Show the cart's subtotal"],
["27", "formatPrice", "Show the total discounts"],
["31", "formatPrice", "Show the shipping total"],
["35", "formatPrice", "Show the tax total"],
["39", "formatPrice", "Show the total amount"],
]
```tsx highlights={highlights}
"use client" // include with Next.js 13+
import { useCart } from "../../../providers/cart"
export default function CartTotals() {
const { cart } = useCart()
const formatPrice = (amount: number): string => {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: cart?.currency_code,
})
.format(amount)
}
return (
<div>
{!cart && <span>Loading...</span>}
{cart && (
<ul>
<li>
<span>Subtotal (excl. shipping & taxes)</span>
<span>{formatPrice(cart.subtotal ?? 0)}</span>
</li>
<li>
<span>Discounts</span>
<span>{formatPrice(cart.discount_total ?? 0)}</span>
</li>
<li>
<span>Shipping</span>
<span>{formatPrice(cart.shipping_total ?? 0)}</span>
</li>
<li>
<span>Taxes</span>
<span>{formatPrice(cart.tax_total ?? 0)}</span>
</li>
<li>
<span>Total</span>
<span>{formatPrice(cart.total ?? 0)}</span>
</li>
</ul>
)}
</div>
)
}
```
In the example, you first retrieve the cart using the [Cart Context](../context/page.mdx). Then, you define the [formatPrice](../retrieve/page.mdx#format-prices) function to format the total amounts.
Finally, you render the cart totals in a list, showing the subtotal, discounts, shipping, taxes, and the total amount.
@@ -0,0 +1,248 @@
---
tags:
- order
- storefront
---
import { CodeTabs, CodeTab, Table } from "docs-ui"
export const metadata = {
title: `Order Confirmation in Storefront`,
}
# {metadata.title}
After the customer completes the checkout process and places an order, you can show an order confirmation page to display the order details.
In this guide, you'll learn how to show the different order details on the order confirmation page.
## Retrieve Order Details
To show the order details, you need to retrieve the order by sending a request to the [Get an Order API route](!api!store#orders_getordersid).
You need the order's ID to retrieve the order. You can pass it from the [complete cart step](../complete-cart/page.mdx) or store it in the `localStorage`.
The following example assumes you already have the order ID:
<CodeTabs group="store-request">
<CodeTab label="Fetch API" value="fetch">
```ts
// orderId is the order ID which you can get from the complete cart step
fetch(`http://localhost:9000/store/orders/${orderId}`, {
credentials: "include",
headers: {
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",
},
})
.then((res) => res.json())
.then(({ order }) => {
// use order...
console.log(order)
})
```
</CodeTab>
<CodeTab label="React" value="react">
```tsx
"use client" // include with Next.js 13+
import { HttpTypes } from "@medusajs/types"
import { useEffect } from "react"
import { useState } from "react"
export function OrderConfirmation({ id }: { id: string }) {
const [order, setOrder] = useState<HttpTypes.StoreOrder | undefined>()
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch(`http://localhost:9000/store/orders/${id}`, {
credentials: "include",
headers: {
"x-publishable-api-key": process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY || "temp",
},
})
.then((res) => res.json())
.then(({ order: dataOrder }) => {
setOrder(dataOrder)
setLoading(false)
})
}, [id])
return (
<div>
{loading && <span>Loading...</span>}
{!loading && order && (
<div>
<h1>Order Confirmation</h1>
<p>Order ID: {order.id}</p>
<p>Order Date: {order.created_at.toLocaleString()}</p>
<p>Order Customer: {order.email}</p>
{/* TODO show more info */}
</div>
)}
</div>
)
}
```
</CodeTab>
</CodeTabs>
In the above example, you retrieve the order's details from the [Get an Order API route](!api!store#orders_getordersid). Then, in the React example, you show the order details like the order ID, order date, and customer email.
The rest of this guide will expand on the React example to show more order details.
<Note title="Tip">
Refer to the [Order schema in the API reference](!api!/store#orders_order_schema) for all the available order fields.
</Note>
---
## Show Order Items
An order has an `items` field that contains the order items. You can show the order items on the order confirmation page.
For example, add to the React component a `formatPrice` function to format prices with the order's currency:
```tsx
const formatPrice = (amount: number): string => {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency: order?.currency_code,
})
.format(amount)
}
```
Since this is the same function used to format the prices of products and cart totals, you can define the function in one place and re-use it where necessary. In that case, make sure to pass the currency code as a parameter.
Then, you can show the order items in a list:
```tsx
return (
<div>
{loading && <span>Loading...</span>}
{!loading && order && (
<div>
{/* ... */}
<p>
<span>Order Items</span>
<ul>
{order.items?.map((item) => (
<li key={item.id}>
{item.title} - {item.quantity} x {formatPrice(item.unit_price)}
</li>
))}
</ul>
</p>
{/* TODO show more details */}
</div>
)}
</div>
)
```
In the above example, you show the order items in a list, displaying the item's title, quantity, and unit price formatted with the `formatPrice` function.
---
## Show Order Totals
An order has various fields for the order totals, which you can check out in the [Order schema in the Store API reference](https://docs.medusajs.com/api/store#orders_order_schema). The most commonly used fields are:
<Table>
<Table.Header>
<Table.Row>
<Table.HeaderCell>Field</Table.HeaderCell>
<Table.HeaderCell>Description</Table.HeaderCell>
</Table.Row>
</Table.Header>
<Table.Body>
<Table.Row>
<Table.Cell>
`subtotal`
</Table.Cell>
<Table.Cell>
The order's subtotal excluding taxes and shipping, and including discounts.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`discount_total`
</Table.Cell>
<Table.Cell>
The total discounts or promotions applied to the order.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`shipping_total`
</Table.Cell>
<Table.Cell>
The total shipping cost.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`tax_total`
</Table.Cell>
<Table.Cell>
The total tax amount.
</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell>
`total`
</Table.Cell>
<Table.Cell>
The total amount of the order including all taxes, shipping, and discounts.
</Table.Cell>
</Table.Row>
</Table.Body>
</Table>
You can show these totals on the order confirmation page. For example:
```tsx
return (
<div>
{loading && <span>Loading...</span>}
{!loading && order && (
<div>
{/* ... */}
<div>
<span>Order Totals</span>
<ul>
<li>
<span>Subtotal (excl. shipping & taxes)</span>
<span>{formatPrice(order.subtotal ?? 0)}</span>
</li>
<li>
<span>Discounts</span>
<span>{formatPrice(order.discount_total ?? 0)}</span>
</li>
<li>
<span>Shipping</span>
<span>{formatPrice(order.shipping_total ?? 0)}</span>
</li>
<li>
<span>Taxes</span>
<span>{formatPrice(order.tax_total ?? 0)}</span>
</li>
<li>
<span>Total</span>
<span>{formatPrice(order.total ?? 0)}</span>
</li>
</ul>
</div>
</div>
)}
</div>
)
```
In the above example, you show the order totals in a list, displaying the subtotal, discounts, shipping, taxes, and total amount formatted with the [formatPrice function](#show-order-items).
@@ -1,5 +1,3 @@
import { ChildDocs } from "docs-ui"
export const metadata = {
title: `Checkout in Storefront`,
}
@@ -10,12 +8,16 @@ Once a customer finishes adding products to cart, they go through the checkout f
The checkout flow is composed of five steps:
1. **Email:** Enter customer email. For logged-in customer, you can pre-fill it.
2. **Address:** Enter shipping/billing address details.
3. **Shipping**: Choose a shipping method.
4. **Payment:** Choose a payment provider.
5. **Complete Cart:** Perform any payment action necessary (for example, enter card details), complete the cart, and place the order.
1. [Email](./email/page.mdx): Enter customer email. For logged-in customer, you can pre-fill it.
2. [Address](./address/page.mdx): Enter shipping/billing address details.
3. [Shipping](./shipping/page.mdx): Choose a shipping method.
4. [Payment](./payment/page.mdx): Choose a payment provider.
5. [Complete Cart](./complete-cart/page.mdx): Perform any payment action necessary (for example, enter card details), complete the cart, and place the order.
You can combine steps based on your desired checkout flow.
You can combine steps or change their order based on your desired checkout flow. Once the customer places the order, you can show them an [order confirmation page](./order-confirmation/page.mdx).
<ChildDocs type="item" onlyTopLevel={true} />
<Note title="Tip">
Refer to the [Express Checkout Tutorial](../guides/express-checkout/page.mdx) for a complete example of a different checkout flow.
</Note>
+4 -1
View File
@@ -6057,5 +6057,8 @@ export const generatedEditDates = {
"references/modules/event/page.mdx": "2025-03-17T15:24:03.021Z",
"references/modules/file_service/page.mdx": "2025-03-17T15:24:03.025Z",
"references/modules/notification_service/page.mdx": "2025-03-17T15:24:05.164Z",
"references/notification_service/interfaces/notification_service.INotificationModuleService/page.mdx": "2025-03-17T15:24:05.173Z"
"references/notification_service/interfaces/notification_service.INotificationModuleService/page.mdx": "2025-03-17T15:24:05.173Z",
"app/nextjs-starter/guides/revalidate-cache/page.mdx": "2025-03-18T08:47:59.628Z",
"app/storefront-development/cart/totals/page.mdx": "2025-03-18T09:20:59.533Z",
"app/storefront-development/checkout/order-confirmation/page.mdx": "2025-03-18T09:44:14.561Z"
}
@@ -891,6 +891,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/nextjs-starter/guides/customize-stripe/page.mdx",
"pathname": "/nextjs-starter/guides/customize-stripe"
},
{
"filePath": "/www/apps/resources/app/nextjs-starter/guides/revalidate-cache/page.mdx",
"pathname": "/nextjs-starter/guides/revalidate-cache"
},
{
"filePath": "/www/apps/resources/app/nextjs-starter/page.mdx",
"pathname": "/nextjs-starter"
@@ -1039,6 +1043,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/storefront-development/cart/retrieve/page.mdx",
"pathname": "/storefront-development/cart/retrieve"
},
{
"filePath": "/www/apps/resources/app/storefront-development/cart/totals/page.mdx",
"pathname": "/storefront-development/cart/totals"
},
{
"filePath": "/www/apps/resources/app/storefront-development/cart/update/page.mdx",
"pathname": "/storefront-development/cart/update"
@@ -1055,6 +1063,10 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/storefront-development/checkout/email/page.mdx",
"pathname": "/storefront-development/checkout/email"
},
{
"filePath": "/www/apps/resources/app/storefront-development/checkout/order-confirmation/page.mdx",
"pathname": "/storefront-development/checkout/order-confirmation"
},
{
"filePath": "/www/apps/resources/app/storefront-development/checkout/page.mdx",
"pathname": "/storefront-development/checkout"
@@ -1195,6 +1195,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
"path": "https://docs.medusajs.com/resources/storefront-development/cart/retrieve",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"title": "Show Cart Totals",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/totals",
"children": []
},
{
"loaded": true,
"isPathHref": true,
@@ -5957,6 +5965,14 @@ const generatedgeneratedCommerceModulesSidebarSidebar = {
"title": "Implement Express Checkout with Medusa",
"path": "https://docs.medusajs.com/resources/storefront-development/guides/express-checkout",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "ref",
"title": "Order Confirmation in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/checkout/order-confirmation",
"children": []
}
]
},
@@ -293,6 +293,14 @@ const generatedgeneratedStorefrontDevelopmentSidebarSidebar = {
"path": "/storefront-development/cart/manage-items",
"title": "Manage Line Items",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/storefront-development/cart/totals",
"title": "Show Totals",
"children": []
}
]
},
@@ -359,6 +367,14 @@ const generatedgeneratedStorefrontDevelopmentSidebarSidebar = {
"path": "/storefront-development/checkout/complete-cart",
"title": "5. Complete Cart",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/storefront-development/checkout/order-confirmation",
"title": "Show Order Confirmation",
"children": []
}
]
},
@@ -756,7 +756,7 @@ const generatedgeneratedToolsSidebarSidebar = {
"loaded": true,
"isPathHref": true,
"type": "category",
"title": "Payment",
"title": "How-to Guides",
"initialOpen": true,
"children": [
{
@@ -766,6 +766,14 @@ const generatedgeneratedToolsSidebarSidebar = {
"path": "/nextjs-starter/guides/customize-stripe",
"title": "Customize Stripe Integration",
"children": []
},
{
"loaded": true,
"isPathHref": true,
"type": "link",
"path": "/nextjs-starter/guides/revalidate-cache",
"title": "Revalidate Cache",
"children": []
}
]
}
@@ -179,6 +179,11 @@ export const storefrontDevelopmentSidebar = [
path: "/storefront-development/cart/manage-items",
title: "Manage Line Items",
},
{
type: "link",
path: "/storefront-development/cart/totals",
title: "Show Totals",
},
],
},
{
@@ -224,6 +229,11 @@ export const storefrontDevelopmentSidebar = [
path: "/storefront-development/checkout/complete-cart",
title: "5. Complete Cart",
},
{
type: "link",
path: "/storefront-development/checkout/order-confirmation",
title: "Show Order Confirmation",
},
],
},
{
+6 -1
View File
@@ -100,7 +100,7 @@ export const toolsSidebar = [
},
{
type: "category",
title: "Payment",
title: "How-to Guides",
initialOpen: true,
children: [
{
@@ -108,6 +108,11 @@ export const toolsSidebar = [
path: "/nextjs-starter/guides/customize-stripe",
title: "Customize Stripe Integration",
},
{
type: "link",
path: "/nextjs-starter/guides/revalidate-cache",
title: "Revalidate Cache",
},
],
},
],
+4
View File
@@ -27,6 +27,10 @@ export const cart = [
"title": "Retrieve Cart in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/retrieve"
},
{
"title": "Show Cart Totals",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/totals"
},
{
"title": "Update Cart in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/update"
+4
View File
@@ -43,6 +43,10 @@ export const order = [
"title": "Checkout Step 5: Complete Cart",
"path": "https://docs.medusajs.com/resources/storefront-development/checkout/complete-cart"
},
{
"title": "Order Confirmation in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/checkout/order-confirmation"
},
{
"title": "Implement Express Checkout with Medusa",
"path": "https://docs.medusajs.com/resources/storefront-development/guides/express-checkout"
+8
View File
@@ -19,6 +19,10 @@ export const storefront = [
"title": "Retrieve Cart in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/retrieve"
},
{
"title": "Show Cart Totals",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/totals"
},
{
"title": "Update Cart in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/cart/update"
@@ -35,6 +39,10 @@ export const storefront = [
"title": "Checkout Step 1: Enter Email",
"path": "https://docs.medusajs.com/resources/storefront-development/checkout/email"
},
{
"title": "Order Confirmation in Storefront",
"path": "https://docs.medusajs.com/resources/storefront-development/checkout/order-confirmation"
},
{
"title": "Checkout Step 4: Choose Payment Provider",
"path": "https://docs.medusajs.com/resources/storefront-development/checkout/payment"