docs: update create payment collection route usage in store development (#8736)

This commit is contained in:
Shahed Nasser authored and GitHub committed 2024-08-27 10:35:22 +02:00
1 parent 5996089c9c
commit b36e2c3129
2 files changed
+243 -234

No files matched your search

@@ -36,113 +36,122 @@ export const fetchHighlights = [
["7", "fetch", "Retrieve available payment providers."], ["7", "fetch", "Retrieve available payment providers."],
["18", "selectPaymentProvider", "This function is executed when the customer submits their chosen payment provider."], ["18", "selectPaymentProvider", "This function is executed when the customer submits their chosen payment provider."],
["25", "fetch", "Create a payment collection for the cart when it doesn't have one."], ["25", "fetch", "Create a payment collection for the cart when it doesn't have one."],
["47", "fetch", "Initialize the payment session in the payment collection for the chosen provider."], ["44", "fetch", "Initialize the payment session in the payment collection for the chosen provider."],
["65", "fetch", "Retrieve the cart again to update its data."], ["62", "fetch", "Retrieve the cart again to update its data."],
["76", "getPaymentUi", "This function shows the necessary UI based on the selected payment provider."], ["73", "getPaymentUi", "This function shows the necessary UI based on the selected payment provider."],
["77", "activePaymentSession", "The active session is the first in the payment collection's sessions."], ["74", "activePaymentSession", "The active session is the first in the payment collection's sessions."],
["83", "", "Test which payment provider is chosen based on the prefix of the provider ID."], ["80", "", "Test which payment provider is chosen based on the prefix of the provider ID."],
["84", `"pp_stripe_"`, "Check if the chosen provider is Stripe."], ["81", `"pp_stripe_"`, "Check if the chosen provider is Stripe."],
["88", `"pp_system_default"`, "Check if the chosen provider is the default systen provider."], ["85", `"pp_system_default"`, "Check if the chosen provider is the default systen provider."],
["90", "default", "Handle unrecognized providers."], ["87", "default", "Handle unrecognized providers."],
["94", "handlePayment", "The function that handles the payment process using the above functions."]
] ]
```ts highlights={fetchHighlights} ```ts highlights={fetchHighlights}
// assuming the cart is previously fetched // assuming the cart is previously fetched
const cart = { const cart = {
// cart object... // cart object...
} }
const retrievePaymentProviders = async () => { const retrievePaymentProviders = async () => {
const { payment_providers } = await fetch( const { payment_providers } = await fetch(
`http://localhost:9000/store/payment-providers?region_id=${ `http://localhost:9000/store/payment-providers?region_id=${
cart.region_id cart.region_id
}`, { }`, {
credentials: "include",
})
.then((res) => res.json())
return payment_providers
}
const selectPaymentProvider = async (
selectedPaymentProviderId: string
) => {
let paymentCollectionId = cart.payment_collection?.id
if (!paymentCollectionId) {
// create payment collection
const { payment_collection } = await fetch(
`http://localhost:9000/store/payment-collections`,
{
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
cart_id: cart.id,
region_id: cart.region_id,
currency_code: cart.currency_code,
amount: cart.total,
}),
}
)
.then((res) => res.json())
paymentCollectionId = payment_collection.id
}
// initialize payment session
await fetch(`http://localhost:9000/store/payment-collections/${
paymentCollectionId
}/payment-sessions`,
{
credentials: "include", credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
provider_id: selectedPaymentProviderId,
}),
}) })
.then((res) => res.json()) .then((res) => res.json())
// re-fetch cart return payment_providers
const { }
cart: updatedCart,
} = await fetch( const selectPaymentProvider = async (
`http://localhost:9000/store/carts/${cart.id}`, selectedPaymentProviderId: string
) => {
let paymentCollectionId = cart.payment_collection?.id
if (!paymentCollectionId) {
// create payment collection
const { payment_collection } = await fetch(
`http://localhost:9000/store/payment-collections`,
{ {
credentials: "include", credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
cart_id: cart.id,
}),
} }
) )
.then((res) => res.json()) .then((res) => res.json())
return updatedCart paymentCollectionId = payment_collection.id
} }
const getPaymentUi = () => { // initialize payment session
const activePaymentSession = cart?.payment_collection?. await fetch(`http://localhost:9000/store/payment-collections/${
payment_sessions?.[0] paymentCollectionId
if (!activePaymentSession) { }/payment-sessions`,
return {
} credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
provider_id: selectedPaymentProviderId,
}),
})
.then((res) => res.json())
switch(true) { // re-fetch cart
case activePaymentSession.provider_id.startsWith("pp_stripe_"): const {
// TODO handle Stripe UI cart: updatedCart,
return "You chose stripe!" } = await fetch(
case activePaymentSession.provider_id `http://localhost:9000/store/carts/${cart.id}`,
.startsWith("pp_system_default"): {
return "You chose manual payment! No additional actions required." credentials: "include",
default:
return `You chose ${
activePaymentSession.provider_id
} which is in development.`
} }
)
.then((res) => res.json())
return updatedCart
}
const getPaymentUi = () => {
const activePaymentSession = cart?.payment_collection?.
payment_sessions?.[0]
if (!activePaymentSession) {
return
} }
```
switch(true) {
case activePaymentSession.provider_id.startsWith("pp_stripe_"):
// TODO handle Stripe UI
return "You chose stripe!"
case activePaymentSession.provider_id
.startsWith("pp_system_default"):
return "You chose manual payment! No additional actions required."
default:
return `You chose ${
activePaymentSession.provider_id
} which is in development.`
}
}
const handlePayment = () => {
retrievePaymentProviders()
// ... customer chooses payment provider
// const providerId = ...
selectPaymentProvider(providerId)
getPaymentUi()
}
```
</CodeTab> </CodeTab>
<CodeTab label="React" value="react"> <CodeTab label="React" value="react">
@@ -164,171 +173,168 @@ export const highlights = [
["160", "getPaymentUi", "If a provider is chosen, render its UI."] ["160", "getPaymentUi", "If a provider is chosen, render its UI."]
] ]
```tsx highlights={highlights} ```tsx highlights={highlights}
"use client" // include with Next.js 13+ "use client" // include with Next.js 13+
import { useCallback, useEffect, useState } from "react" import { useCallback, useEffect, useState } from "react"
import { useCart } from "../../../providers/cart" import { useCart } from "../../../providers/cart"
import { HttpTypes } from "@medusajs/types" import { HttpTypes } from "@medusajs/types"
export default function CheckoutPaymentStep() { export default function CheckoutPaymentStep() {
const { cart, setCart } = useCart() const { cart, setCart } = useCart()
const [paymentProviders, setPaymentProviders] = useState< const [paymentProviders, setPaymentProviders] = useState<
HttpTypes.StorePaymentProvider[] HttpTypes.StorePaymentProvider[]
>([]) >([])
const [ const [
selectedPaymentProvider, selectedPaymentProvider,
setSelectedPaymentProvider, setSelectedPaymentProvider,
] = useState<string | undefined>() ] = useState<string | undefined>()
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
useEffect(() => { useEffect(() => {
if (!cart) { if (!cart) {
return return
} }
fetch(`http://localhost:9000/store/payment-providers?region_id=${ fetch(`http://localhost:9000/store/payment-providers?region_id=${
cart.region_id cart.region_id
}`, { }`, {
credentials: "include", credentials: "include",
}) })
.then((res) => res.json()) .then((res) => res.json())
.then(({ payment_providers }) => { .then(({ payment_providers }) => {
setPaymentProviders(payment_providers) setPaymentProviders(payment_providers)
setSelectedPaymentProvider( setSelectedPaymentProvider(
cart.payment_collection?.payment_sessions?.[0]?.id cart.payment_collection?.payment_sessions?.[0]?.id
) )
}) })
}, [cart]) }, [cart])
const handleSelectProvider = async ( const handleSelectProvider = async (
e: React.MouseEvent<HTMLButtonElement, MouseEvent> e: React.MouseEvent<HTMLButtonElement, MouseEvent>
) => { ) => {
e.preventDefault() e.preventDefault()
if (!cart || !selectedPaymentProvider) { if (!cart || !selectedPaymentProvider) {
return return
} }
setLoading(false) setLoading(false)
let paymentCollectionId = cart.payment_collection?.id let paymentCollectionId = cart.payment_collection?.id
if (!paymentCollectionId) { if (!paymentCollectionId) {
// create payment collection // create payment collection
const { payment_collection } = await fetch( const { payment_collection } = await fetch(
`http://localhost:9000/store/payment-collections`, `http://localhost:9000/store/payment-collections`,
{
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
cart_id: cart.id,
region_id: cart.region_id,
currency_code: cart.currency_code,
amount: cart.total,
}),
}
)
.then((res) => res.json())
paymentCollectionId = payment_collection.id
}
// initialize payment session
await fetch(`http://localhost:9000/store/payment-collections/${
paymentCollectionId
}/payment-sessions`,
{
credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
provider_id: selectedPaymentProvider,
}),
})
.then((res) => res.json())
// re-fetch cart
const {
cart: updatedCart,
} = await fetch(
`http://localhost:9000/store/carts/${cart.id}`,
{ {
credentials: "include", credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
cart_id: cart.id,
}),
} }
) )
.then((res) => res.json()) .then((res) => res.json())
setCart(updatedCart) paymentCollectionId = payment_collection.id
setLoading(false)
} }
const getPaymentUi = useCallback(() => { // initialize payment session
const activePaymentSession = cart?.payment_collection?. await fetch(`http://localhost:9000/store/payment-collections/${
payment_sessions?.[0] paymentCollectionId
if (!activePaymentSession) { }/payment-sessions`,
return {
} credentials: "include",
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
provider_id: selectedPaymentProvider,
}),
})
.then((res) => res.json())
switch(true) { // re-fetch cart
case activePaymentSession.provider_id.startsWith("pp_stripe_"): const {
return ( cart: updatedCart,
<span> } = await fetch(
You chose stripe! `http://localhost:9000/store/carts/${cart.id}`,
{/* TODO add stripe UI */} {
</span> credentials: "include",
)
case activePaymentSession.provider_id
.startsWith("pp_system_default"):
return (
<span>
You chose manual payment! No additional actions required.
</span>
)
default:
return (
<span>
You chose {activePaymentSession.provider_id} which is
in development.
</span>
)
} }
} , [cart])
return (
<div>
<form>
<select
value={selectedPaymentProvider}
onChange={(e) => setSelectedPaymentProvider(e.target.value)}
>
{paymentProviders.map((provider) => (
<option
key={provider.id}
value={provider.id}
>
{provider.id}
</option>
))}
</select>
<button
disabled={loading}
onClick={async (e) => {
await handleSelectProvider(e)
}}
>
Submit
</button>
</form>
{getPaymentUi()}
</div>
) )
.then((res) => res.json())
setCart(updatedCart)
setLoading(false)
} }
```
const getPaymentUi = useCallback(() => {
const activePaymentSession = cart?.payment_collection?.
payment_sessions?.[0]
if (!activePaymentSession) {
return
}
switch(true) {
case activePaymentSession.provider_id.startsWith("pp_stripe_"):
return (
<span>
You chose stripe!
{/* TODO add stripe UI */}
</span>
)
case activePaymentSession.provider_id
.startsWith("pp_system_default"):
return (
<span>
You chose manual payment! No additional actions required.
</span>
)
default:
return (
<span>
You chose {activePaymentSession.provider_id} which is
in development.
</span>
)
}
} , [cart])
return (
<div>
<form>
<select
value={selectedPaymentProvider}
onChange={(e) => setSelectedPaymentProvider(e.target.value)}
>
{paymentProviders.map((provider) => (
<option
key={provider.id}
value={provider.id}
>
{provider.id}
</option>
))}
</select>
<button
disabled={loading}
onClick={async (e) => {
await handleSelectProvider(e)
}}
>
Submit
</button>
</form>
{getPaymentUi()}
</div>
)
}
```
</CodeTab> </CodeTab>
</CodeTabs> </CodeTabs>
@@ -340,3 +346,5 @@ In the example above, you:
1. Check whether the cart has a payment collection. If not, create one using the [Create Payment Collection API route](!api!/store#payment-collections_postpaymentcollections). 1. Check whether the cart has a payment collection. If not, create one using the [Create Payment Collection API route](!api!/store#payment-collections_postpaymentcollections).
2. Initialize the payment session for the chosen payment provider using the [Initialize Payment Session API route](!api!/store#payment-collections_postpaymentcollectionsidpaymentsessions). 2. Initialize the payment session for the chosen payment provider using the [Initialize Payment Session API route](!api!/store#payment-collections_postpaymentcollectionsidpaymentsessions).
- Once the cart has a payment session, you optionally render the UI to perform additional actions. For example, if the customer chose stripe, you can show them the card form to enter their credit card. - Once the cart has a payment session, you optionally render the UI to perform additional actions. For example, if the customer chose stripe, you can show them the card form to enter their credit card.
In the `Fetch API` example, the `handlePayment` function implements this flow.
@@ -153,6 +153,7 @@ export const MDXComponents: MDXComponentsType = {
<li <li
className={clsx( className={clsx(
"text-medusa-fg-subtle [&:not(:last-child)]:mb-docs_0.5", "text-medusa-fg-subtle [&:not(:last-child)]:mb-docs_0.5",
"[&_ol]:mt-docs_0.5 [&_ul]:mt-docs_0.5",
className className
)} )}
{...props} {...props}