docs: cache revalidation in next.js storefront + storefront totals (#11887)
* initial changes * generated sidebar
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user