Guide 08 · Developer docs

Commerce

A Core dna store is a set of records and an API that moves a purchase from cart to order: products described in every language and priced in every currency the store sells in, a cart, a checkout that becomes an order, invoices, payments, tax and shipping. The site builds the storefront on top: the cart and checkout pages, their scripts, and the customer emails.

L2–L3
Unit 02 · Carts, cookies and the cache

The cart record and its cookie

A cart is a database record found through a cookie, not session data. What it holds and how long it lives, the cookie's attributes and the paths that lose a cart, what happens to a guest cart when the shopper signs in, the restore link, and abandoned carts.

Who this is for. The commerce implementer and the developer who gets the "my cart emptied" ticket. Afterwards you know where a cart lives, what finds it again, and the order in which to check the causes when it goes missing.

A cart is a record

A cart is a row in the database with an id and a uuid, which is what the browser and the API use to name it. Because it is a record and not session data, it survives the session, can be restored from a link, can be merged into an account at sign-in, and can be inspected by staff. It holds:

  • Items: each with the SKU (productCode), the product's name, a quantity, the amount, discount and tax, whether it is still available, and optional attributes such as an engraving (item attributes).
  • Discount codes, and the discounts that applied.
  • Totals: the sum of the available items, less discounts, plus tax. A cart has no shipping; that arrives with the checkout.
  • A currency, set by the first item added and cleared when the cart is emptied (currencies).
  • Messages: short-lived notices such as "Item has been added" (cart messages).
  • A person, once the shopper is signed in or has given an email address. A person has at most one cart.

When a cart is created, and when it goes

A cart is saved the first time something needs it: an item being added, the /cart page, or a template reading $_cart. Until then a visitor has no cart, and GET /cdna-api/cart answers 404.

A background clean-up deletes carts that have not changed for Lifetime (Carts.lifetime, 15552000 seconds, which is 180 days) and do not belong to a member: a guest's cart, with no person or with a person who has no login. A signed-in member's cart is kept however old it is. When an order is placed, the items bought leave the cart, and a cart left empty is deleted (From checkout to order).

The browser finds its cart through a cookie named cart, whose value is the cart's uuid. Its attributes:

AttributeValueConsequence
SecureYesNever sent over plain HTTP: a site served without HTTPS starts a new cart on every request
HttpOnlyYesScripts cannot read it; ask the API for the cart instead
SameSiteLaxSent on top-level navigation from another site, so a shopper arriving from an email keeps their cart
DomainNone (this host only)Not shared between www.example.com and example.com, or between staging and live
ExpiresCarts.lifetime from the last time it was set (180 days)Set again on every request that loads the cart through the cookie, so an active shopper's cookie does not run out

The cookie is cleared when the visitor signs out. API requests that name a cart by its uuid (/cdna-api/cart/<uuid>/…) neither read nor set it.

"My cart emptied": the diagnosis

Check the causes in this order; the first three explain most reports:

  1. A different hostname. The shopper moved between www and the bare domain, or a link sent them to another hostname of the site. Redirect every hostname to one.
  2. Plain HTTP somewhere. A page or environment served over http:// never stores the cookie.
  3. They signed out, or are signed in on one device and not another. Sign-out clears the cookie; a member's cart comes back when they sign in again.
  4. They bought it. Items bought leave the cart; a cart left empty is deleted.
  5. A different browser, a private window, or cleared cookies. A guest's cart can then only come back through its restore link.
  6. It expired: a guest cart untouched for 180 days.

To see which, open the browser's developer tools and compare the cart cookie before and after the moment it empties, and note the hostname and scheme in the address bar. If the uuid is unchanged, ask the API for that cart (GET /cdna-api/cart/<uuid>): a 404 means the record was deleted, a cart with no items means it was emptied or bought.

Signing in with a cart

Which cart a member has is settled on the first request after sign-in that loads the cart (the cart page, a cart API call, a template reading $_cart):

  • The member has no cart yet: the guest cart in the cookie becomes theirs, items and all.
  • The member already has a cart (from an earlier visit or another device): the guest cart's items are added to it, and the cookie is pointed at the member's cart. The guest cart itself is left behind and later cleaned up.

When both carts have items, the merge follows these rules:

  • Quantities add up. Two of a product in the guest cart and one in the member's cart become three, on one line.
  • The guest cart's currency wins if it had items. Member items with no price in that currency become unavailable.
  • Item attributes are not carried over. Guest lines arrive without them, and so join any line of the same product that has no attributes.
  • Items that can no longer be bought are skipped, and the "Item has been added" messages the merge would produce are removed, so the shopper is not told.

If attributes matter on a store, such as engraving or a chosen size note, ask shoppers to sign in before they customise, or tell them to check their cart after signing in.

The restore link

GET /cart/restore/<uuid> points the visitor's cookie at that cart and redirects to /cart, keeping the link's query string (campaign tags included). It is the link to put in an abandoned-cart email; a cart's getRestoreUrl() gives it as a full URL. A signed-in shopper who opens it takes the restored cart as their own, and their previous cart stops being theirs. The link opens the cart for whoever holds it, so send it only to the cart's owner, by email, and never print it on a page.

Abandoned carts

A background job looks for carts that are abandoned: changed longer ago than Abandoned Threshold (Carts.abandonedThreshold, 3600 seconds), holding at least one item, belonging to a person with an email address, with no checkout in progress, and not reported already. Each one fires the cart's Abandoned event once, which a job turns into an email with the restore link; Jobs and events covers the job. Any later change to the cart makes it eligible again.

A guest cart has no person until the site gives it one. Send the shopper's email address as soon as they type it, for example from a newsletter box or the first step of checkout:

HTTP
PUT /cdna-api/cart
Content-Type: application/json
Accept: application/json

{"person": {"email": "jo@example.com"}}

An address that belongs to a member's account, or to a person who already has another cart, is refused with "This email is already associated with another cart"; a body without an email is refused with "The 'person' field must be an object containing an email address". A refused address leaves the cart without a person, so it is never reported as abandoned.

The header's cart count, which must not be rendered into a cached page, has its own page: The cart count on a cached page.

Go deeper

Was this page helpful?

Your answer goes to the docs team.