1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500---
title: Ecommerce Frontend
label: Frontend
order: 30
desc: Add ecommerce functionality to your Payload CMS application with this plugin.
keywords: plugins, ecommerce, stripe, plugin, payload, cms, shop, payments
---
The package provides a set of React utilities to help you manage your ecommerce frontend. These include context providers, hooks, and components to handle carts, products, and payments.
The following hooks and components are available:
| Hook / Component | Description |
| -------------------- | ------------------------------------------------------------------------------ |
| `EcommerceProvider` | A context provider to wrap your application and provide the ecommerce context. |
| `useCart` | A hook to manage the cart state and actions. |
| `useAddresses` | A hook to fetch and manage addresses. |
| `usePayments` | A hook to manage the checkout process. |
| `useCurrency` | A hook to format prices based on the selected currency. |
| `useEcommerceConfig` | A hook to access the ecommerce configuration (collection slugs, API settings). |
| `useEcommerce` | A hook that encompasses all of the above in one. |
### EcommerceProvider
The `EcommerceProvider` component is used to wrap your application and provide the ecommerce context. It takes the following props:
| Prop | Type | Description |
| ------------------ | ------------------ | ----------------------------------------------------------------------------------------------------------- |
| `addressesSlug` | `string` | The slug of the addresses collection. Defaults to `addresses`. |
| `api` | `object` | API configuration for the internal fetches of the provider. [More](#api) |
| `cartsSlug` | `string` | The slug of the carts collection. Defaults to `carts`. |
| `children` | `ReactNode` | The child components that will have access to the ecommerce context. |
| `currenciesConfig` | `CurrenciesConfig` | Configuration for supported currencies. See [Currencies](./plugin#currencies). |
| `customersSlug` | `string` | The slug of the customers collection. Defaults to `users`. |
| `debug` | `boolean` | Enable or disable debug mode. This will send more information to the console. |
| `enableVariants` | `boolean` | Enable or disable product variants support. Defaults to `true`. |
| `paymentMethods` | `PaymentMethod[]` | An array of payment method adapters for the client side. See [Payment adapters](./plugin#payment-adapters). |
| `syncLocalStorage` | `boolean` `object` | Whether to sync the cart ID to local storage. Defaults to `true`. Takes an object for configuration |
Example usage:
```tsx
import { EcommerceProvider } from '@payloadcms/plugin-ecommerce/client/react'
// Import any payment adapters you want to use on the client side
import { stripeAdapterClient } from '@payloadcms/plugin-ecommerce/payments/stripe'
import { USD, EUR } from '@payloadcms/plugin-ecommerce'
export const Providers = () => (
<EcommerceProvider
enableVariants={true}
currenciesConfig={{
supportedCurrencies: [USD, EUR],
defaultCurrency: 'USD',
}}
>
{children}
</EcommerceProvider>
)
```
#### api
The `api` prop is used to configure the API settings for the internal fetches of the provider. It takes an object with the following properties:
| Property | Type | Description |
| ----------------- | -------- | ----------------------------------------------------------------- |
| `apiRoute` | `string` | The base route for accessing the Payload API. Defaults to `/api`. |
| `serverURL` | `string` | The full URL of your Payload server. |
| `cartsFetchQuery` | `object` | Additional query parameters to include when fetching the cart. |
#### cartsFetchQuery
The `cartsFetchQuery` property allows you to specify additional query parameters to include when fetching the cart. This can be useful for including related data or customizing the response. This accepts:
| Property | Type | Description |
| ---------- | -------------- | --------------------------------------------------------------- |
| `depth` | `string` | Defaults to 0. [See Depth](../queries/depth) |
| `select` | `SelectType` | Select parameters. [See Select](../queries/select) |
| `populate` | `PopulateType` | Populate parameters. [See Populate](../queries/select#populate) |
Example usage:
```tsx
<EcommerceProvider
api={{
cartsFetchQuery: {
depth: 2, // Include related data up to 2 levels deep
},
}}
>
{children}
</EcommerceProvider>
```
#### syncLocalStorage
The `syncLocalStorage` prop is used to enable or disable syncing the cart ID to local storage. This allows the cart to persist across page reloads and sessions. It defaults to `true`.
You can also provide an object with the following properties for more configuration:
| Property | Type | Description |
| -------- | -------- | ---------------------------------------------------------------------------- |
| `key` | `string` | The key to use for storing the cart ID in local storage. Defaults to `cart`. |
### useCart
The `useCart` hook is used to manage the cart state and actions. It provides methods to add, remove, and update items in the cart, as well as to fetch the current cart state. It has the following properties:
| Property | Type | Description |
| --------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| `addItem` | `(item: CartItemInput, quantity?: number) => void` | Method to add an item to the cart, optionally accepts a quantity to add multiple at once. |
| `cart` | `Cart` `null` | The current cart state. Null or undefined if it doesn't exist. |
| `clearCart` | `() => void` | Method to clear the cart. |
| `decrementItem` | `(item: IDType) => void` | Method to decrement the quantity of an item. Will remove it entirely if it reaches 0. |
| `incrementItem` | `(item: IDType) => void` | Method to increment the quantity of an item. |
| `isLoading` | `boolean` | Boolean indicating if any async operation is in progress (e.g., adding/removing items). |
| `removeItem` | `(item: IDType) => void` | Method to remove an item from the cart. |
Example usage:
```tsx
import { useCart } from '@payloadcms/plugin-ecommerce/client/react'
const CartComponent = () => {
const {
addItem,
cart,
clearCart,
decrementItem,
incrementItem,
isLoading,
removeItem,
} = useCart()
// Your component logic here
// Use isLoading to show loading states in your UI
}
```
### useAddresses
The `useAddresses` hook is used to fetch and manage addresses. It provides methods to create, update, and delete addresses, as well as to fetch the list of addresses. It has the following properties:
| Property | Type | Description |
| --------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| `addresses` | `Address[]` | The list of addresses, if any are available for the current user. |
| `createAddress` | `(data: Address) => Promise<Address>` | Method to create a new address. |
| `isLoading` | `boolean` | Boolean indicating if any async operation is in progress (e.g., creating/updating address). |
| `updateAddress` | `(addressID: IDType, data: Partial<Address>) => Promise<Address>` | Method to update an existing address by ID. |
Example usage:
```tsx
import { useAddresses } from '@payloadcms/plugin-ecommerce/client/react'
const AddressesComponent = () => {
const { addresses, createAddress, isLoading, updateAddress } = useAddresses()
// Your component logic here
// Use isLoading to show loading states while creating or updating addresses
}
```
### usePayments
The `usePayments` hook is used to manage the checkout process. It provides methods to initiate payments, confirm orders, and handle payment status. It has the following properties:
| Property | Type | Description |
| ----------------------- | -------------------------- | --------------------------------------------------------------------------------------------- |
| `confirmOrder` | `(args) => Promise<Order>` | Method to confirm an order by ID. [More](#confirmOrder) |
| `initiatePayment` | `(args) => Promise<void>` | Method to initiate a payment for an order. [More](#initiatePayment) |
| `isLoading` | `boolean` | Boolean indicating if any async operation is in progress (e.g., initiating/confirming order). |
| `paymentMethods` | `PaymentMethod[]` | The list of available payment methods. |
| `selectedPaymentMethod` | `PaymentMethod` | The currently selected payment method, if any. |
Example usage:
```tsx
import { usePayments } from '@payloadcms/plugin-ecommerce/client/react'
const CheckoutComponent = () => {
const {
confirmOrder,
initiatePayment,
isLoading,
paymentMethods,
selectedPaymentMethod,
} = usePayments()
// Your component logic here
// Use isLoading to show loading states during payment initiation or order confirmation
}
```
#### confirmOrder
Use this method to confirm an order by its ID. It requires the payment method ID and will return the order ID.
```ts
try {
const data = await confirmOrder('stripe', {
additionalData: {
paymentIntentID: paymentIntent.id,
customerEmail,
},
})
// Return type will contain `orderID`
// use data to redirect to your order page
} catch (error) {
// handle error
}
```
If the payment gateway requires additional confirmations offsite then you will need another landing page to handle that. For example with Stripe you may need to use a callback URL, just make sure the relevant information is routed back.
<Banner type="info">
This will mark the transaction as complete in the backend and create the order
for the user.
</Banner>
#### initiatePayment
Use this method to initiate a payment for an order. It requires the cart ID and the payment method ID. Depending on the payment method, additional data may be required. Depending on the payment method used you may need to provide additional data, for example with Stripe:
```ts
try {
const data = await initiatePayment('stripe', {
additionalData: {
customerEmail,
billingAddress,
shippingAddress,
},
})
} catch (error) {
// handle error
}
```
This function will hit the Payload API endpoint for `/stripe/initiate` and return the payment data required to complete the payment on the client side, which by default will include a `client_secret` to complete the payment with Stripe.js. The next step is to call the `confirmOrder` once payment is confirmed on the client side by Stripe.
<Banner type="info">
At this step the cart is verified and a transaction is created in the backend
with the address details provided. No order is created yet until you call
`confirmOrder`, which should be done after payment is confirmed on the client
side or via webhooks if you opt for that approach instead.
</Banner>
### useCurrency
The `useCurrency` hook is used to format prices based on the selected currency. It provides methods to format prices and to get the current currency. It has the following properties:
| Property | Type | Description |
| ------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `currenciesConfig` | `CurrenciesConfig` | The configuration for supported currencies. Directly matching the config provided to the Context Provider. [More](#ecommerceprovider) |
| `currency` | `Currency` | The currently selected currency. |
| `formatPrice` | `(amount: number) => string` | Method to format a price based on the selected currency. |
| `setCurrency` | `(currencyCode: string) => void` | Method to set the current currency by code. It will update all price formats when used in conjunction with the `formatPrice` utility. |
`formatPrice` in particular is very helpful as all prices are stored as integers to avoid any potential issues with decimal calculations, therefore on the frontend you can use this utility to format your price accounting for the currency and decimals. Example usage:
```tsx
import { useCurrency } from '@payloadcms/plugin-ecommerce/client/react'
const PriceComponent = ({ amount }) => {
const { currenciesConfig, currency, setCurrency } = useCurrency()
return <div>{formatPrice(amount)}</div>
}
```
### useEcommerceConfig
The `useEcommerceConfig` hook provides access to the ecommerce configuration. This is useful when you need to build custom API calls or queries using the correct collection slugs and API settings.
| Property | Type | Description |
| --------------- | -------- | -------------------------------------- |
| `addressesSlug` | `string` | The slug for the addresses collection. |
| `cartsSlug` | `string` | The slug for the carts collection. |
| `customersSlug` | `string` | The slug for the customers collection. |
| `api.apiRoute` | `string` | The base API route (e.g., `/api`). |
Example usage:
```tsx
import { useEcommerceConfig } from '@payloadcms/plugin-ecommerce/client/react'
const CustomComponent = () => {
const { cartsSlug, customersSlug, api } = useEcommerceConfig()
// Build custom API URLs
const cartsEndpoint = `${api.apiRoute}/${cartsSlug}`
// Your component logic here
}
```
### useEcommerce
The `useEcommerce` hook encompasses all of the above hooks in one. It provides access to the cart, addresses, and payments hooks, along with a unified `isLoading` state that tracks any async operations across all these features. It also includes the `config` property for accessing collection slugs and API settings.
Example usage:
```tsx
import { useEcommerce } from '@payloadcms/plugin-ecommerce/client/react'
const EcommerceComponent = () => {
const {
cart,
addresses,
clearSession,
config,
isLoading,
selectedPaymentMethod,
} = useEcommerce()
// Your component logic here
// isLoading tracks loading states for cart, addresses, and payment operations
// config provides access to collection slugs and API settings
}
```
## Hooks
The ecommerce provider exposes several hooks for handling authentication events, session management, and cart operations. These hooks are accessible via `useEcommerce()`.
### onLogin
Called after a successful login to properly set up cart state. This hook handles:
- Fetching the authenticated user's data
- Merging any guest cart items into the user's existing cart
- Transferring a guest cart to the user if they don't have one
- Clearing guest cart secrets (authenticated users don't need them)
```tsx
import { useEcommerce } from '@payloadcms/plugin-ecommerce/client/react'
const LoginForm = () => {
const { onLogin } = useEcommerce()
const handleLogin = async (credentials) => {
// Perform your login logic
const response = await fetch('/api/users/login', {
method: 'POST',
body: JSON.stringify(credentials),
})
if (response.ok) {
// Set up ecommerce state after successful login
await onLogin()
}
}
return <form onSubmit={handleLogin}>{/* form fields */}</form>
}
```
### onLogout
Called during logout to clear all ecommerce session data. Currently this is just an alias for `clearSession()` but named for semantic clarity when used in logout flows and it could change in the future.
```tsx
import { useEcommerce } from '@payloadcms/plugin-ecommerce/client/react'
const LogoutButton = () => {
const { onLogout } = useEcommerce()
const handleLogout = async () => {
// Perform your logout logic
await fetch('/api/users/logout', { method: 'POST' })
// Clear ecommerce session data
onLogout()
}
return <button onClick={handleLogout}>Logout</button>
}
```
### clearSession
Clears all ecommerce session data from state and localStorage. This includes:
- Cart data and cart ID
- Cart secret (for guest carts)
- User addresses
- User state
Use this when you need to reset the ecommerce state, such as during logout or when switching users.
```tsx
import { useEcommerce } from '@payloadcms/plugin-ecommerce/client/react'
const ResetButton = () => {
const { clearSession } = useEcommerce()
return <button onClick={clearSession}>Reset Session</button>
}
```
### mergeCart
Merges items from a source cart into a target cart. This is useful when you need to manually merge a guest cart into an authenticated user's cart, or when implementing custom cart merge logic.
| Parameter | Type | Description |
| -------------- | -------- | --------------------------------------------------------- |
| `targetCartID` | `string` | The ID of the cart to merge items into |
| `sourceCartID` | `string` | The ID of the cart to merge items from |
| `sourceSecret` | `string` | The secret for the source cart (required for guest carts) |
When items are merged:
- Matching items (same product and variant) have their quantities combined
- Non-matching items are added to the target cart
- The source cart is deleted after successful merge
```tsx
import { useEcommerce } from '@payloadcms/plugin-ecommerce/client/react'
const MergeCartsButton = () => {
const { mergeCart, isLoading } = useEcommerce()
const handleMerge = async () => {
try {
const mergedCart = await mergeCart(
'user-cart-id',
'guest-cart-id',
'guest-cart-secret',
)
console.log('Merged cart:', mergedCart)
} catch (error) {
console.error('Failed to merge carts:', error)
}
}
return (
<button onClick={handleMerge} disabled={isLoading}>
Merge Carts
</button>
)
}
```
### refreshCart
Fetches the latest cart data from the server and updates the local state. Use this when you need to ensure the cart is in sync with the server, such as after external cart modifications.
```tsx
import { useEcommerce } from '@payloadcms/plugin-ecommerce/client/react'
const RefreshCartButton = () => {
const { refreshCart, isLoading } = useEcommerce()
return (
<button onClick={refreshCart} disabled={isLoading}>
Refresh Cart
</button>
)
}
```
## Session Management
The provider includes built-in session management for handling user authentication flows. This ensures cart data, addresses, and user state are properly managed when users log in or out.
### Handling Login
When a user logs in, call `onLogin()` to set up the ecommerce state. This automatically:
1. Fetches the authenticated user's data
2. Merges any guest cart items into the user's existing cart (if both exist)
3. Transfers the guest cart to the user (if they don't have an existing cart)
4. Clears guest cart secrets (authenticated users don't need them)
```tsx
const handleLogin = async (credentials) => {
const response = await fetch('/api/users/login', {
method: 'POST',
body: JSON.stringify(credentials),
})
if (response.ok) {
await onLogin() // Set up ecommerce state
}
}
```
### Handling Logout
When a user logs out, call `onLogout()` or `clearSession()` to clear all ecommerce data:
```tsx
const handleLogout = async () => {
await fetch('/api/users/logout', { method: 'POST' })
onLogout() // or clearSession()
}
```
Both methods clear cart data, cart ID, cart secret, addresses, and user state from memory and localStorage.