diff --git a/docs/commerce/5.x/development/cart.md b/docs/commerce/5.x/development/cart.md index 07730002c..7a3785416 100644 --- a/docs/commerce/5.x/development/cart.md +++ b/docs/commerce/5.x/development/cart.md @@ -66,6 +66,15 @@ To see what cart information you can use in your templates, take a look at the [ Once a cart is completed and becomes an order, accessing the current cart via either method starts this process over. +### Whose Cart Is Returned + +The cart Commerce returns depends on who is asking for it: + +- A **guest** (not signed in) gets the cart referenced by their cart cookie. +- A **signed-in user** gets the cart referenced by their cookie, and it’s automatically claimed under their account (its customer and email are set to match) the first time they access it. + +For privacy, a cart that belongs to a registered user is only returned to that same user while they’re signed in. If a guest—or a _different_ signed-in user—ends up with that cart’s cookie, Commerce quietly starts them a fresh cart instead. The one exception is a session that has been authorized to use the cart, which happens when it was [loaded with a valid link](#load-a-cart). + ## Displaying Cart Contents Your store should provide enough information about the contents of a customer’s cart for them to shop and check out confidently. @@ -608,17 +617,36 @@ Read more about the [`redirectInput()` Twig function](/5.x/reference/twig/functi ### Load a Cart -Commerce provides a [`commerce/cart/load-cart`](../reference/controller-actions.md#get-post-cart-load-cart) endpoint for loading an existing cart into a cookie for the current customer. +Commerce provides a [`commerce/cart/load-cart`](../reference/controller-actions.md#get-post-cart-load-cart) endpoint for loading an existing cart into the current customer’s session, referenced by its [number](../system/orders-carts.md#order-number). + +You can have the customer interact with the endpoint by [navigating to a URL](#loading-a-cart-with-a-url) or by [submitting a form](#loading-a-cart-with-a-form). + +#### How Carts Are Protected -You can have the user interact with the endpoint by [navigating to a URL](#loading-a-cart-with-a-url) or by [submitting a form](#loading-a-cart-with-a-form). Either way, an existing cart number is required. +An _empty_ cart—one with no email address and no shipping or billing address—can be loaded by anyone who has its number. -If there are issues loading the cart, an error message will be flashed to the user. +As soon as a cart has an email or an address on it, Commerce protects it. Loading a protected cart requires **one** of the following: + +- a valid, unexpired **load-cart token**, passed as the `code` parameter; or +- being signed in as the cart’s own customer. + +If neither is true, the customer is sent to an [email challenge](#recovering-a-protected-cart), where they can request a fresh recovery link by email. + +This table summarizes the outcome of a `load-cart` request for a **protected** cart: + +| Signed-in status | Valid `code` token? | Outcome | +| --- | --- | --- | +| Signed in as the cart’s customer | Not required | Cart loads | +| Signed in as a _different_ user | Yes | Cart loads, and is reassigned to the signed-in user | +| Signed in as a _different_ user | No | Redirected to the email challenge | +| Guest (not signed in) | Yes | Cart loads; the guest can view and edit it | +| Guest (not signed in) | No | Redirected to the email challenge | ::: tip -If the desired cart belongs to a user, that user must be logged in to load it into a browser cookie. +When a signed-in user loads a cart that isn’t already theirs (with a valid token), the cart is **reassigned** to them—its customer and email are updated to match their account. A guest who loads a cart keeps it as-is; if they later sign in or register, it’s reassigned to their account at that point. ::: -The [`loadCartRedirectUrl`](../configure.md#loadcartredirecturl) setting determines where the customer will be sent by default after the cart has been loaded. +The [`loadCartRedirectUrl`](../configure.md#loadcartredirecturl) setting determines where the customer is sent by default after a cart is loaded. If there are issues loading the cart, an error message is flashed to the customer. ### Loading a Cart with a URL @@ -628,31 +656,43 @@ Store managers can get this URL by navigating in the control panel to This link includes a secure, expiring token, so it works even for a [protected cart](#how-carts-are-protected) that belongs to a signed-out customer. -This example sets a `loadCartUrl` variable to an absolute URL the customer can access to load their cart. We’re assuming a `cart` object already exists for the cart that should be loaded: +To build a shareable link programmatically (without the involvement of a store manager), use the carts service’s method, which mints a tokenized URL for a given cart: ::: code ```twig -{% set loadCartUrl = actionUrl( - 'commerce/cart/load-cart', - { number: cart.number } -) %} +{% set loadCartUrl = craft.commerce.carts.getLoadCartUrl(cart) %} ``` ```php -$loadCartUrl = craft\helpers\UrlHelper::actionUrl( - 'commerce/cart/load-cart', - ['number' => $cart->number] -); +use craft\commerce\Plugin as Commerce; + +$loadCartUrl = Commerce::getInstance()->getCarts()->getLoadCartUrl($cart); ``` ::: +The token embedded in the URL is valid for the duration of the [`loadCartUrlExpiry`](../configure.md#loadcarturlexpiry) setting (seven days, by default). + ::: tip -This URL can be presented to the user however you’d like. It’s particularly useful in an email that allows the customer to retrieve an abandoned cart. +This URL can be presented to the customer however you’d like. It’s particularly useful in an email that allows the customer to retrieve an abandoned cart—even if they aren’t signed in. +::: + +::: warning +If you construct a load-cart URL by hand with only a `number` (and no `code` token), it will still load an [empty cart](#how-carts-are-protected)—but a protected cart will send the customer to the [email challenge](#recovering-a-protected-cart) instead of loading. Use `getLoadCartUrl()` to generate links for protected carts. ::: +### Recovering a Protected Cart + +When a customer tries to load a [protected cart](#how-carts-are-protected) without a valid token—for example, by following an expired or hand-built link—Commerce sends them to an **email challenge** rather than exposing the cart to the wrong person. + +The flow works like this: + +1. The customer confirms they’d like to recover the cart. Commerce emails a fresh, tokenized [load-cart link](#loading-a-cart-with-a-url) to the address already stored on the cart. +2. The customer sees a confirmation page with their email address partially masked. +3. Following the link from their inbox loads the cart into their session. + +Because the recovery link is only ever sent to the cart’s _own_ email address, customers can recover a cart on a new device or browser without it ever being publicly accessible. + ### Loading a Cart with a Form Send a GET or POST action request with a `number` parameter referencing the cart you’d like to load. When posting the form data, you can include a specific [redirect location](/5.x/development/forms.md#after-a-post-request) like you can with most any other Craft form. diff --git a/docs/commerce/5.x/reference/controller-actions.md b/docs/commerce/5.x/reference/controller-actions.md index 756e7bb8b..06ceaa352 100644 --- a/docs/commerce/5.x/reference/controller-actions.md +++ b/docs/commerce/5.x/reference/controller-actions.md @@ -150,6 +150,7 @@ Loads a cookie for the specified cart. Param | Description ----- | ----------- `number` | Required cart number to be loaded. +`code` | A secure load-cart token, required to load a [protected cart](/5.x/development/cart.md#how-carts-are-protected) when not signed in as its customer. Generate one with . #### Response