Skip to main content

Overview

A cart is everything that happens before a purchase is final: adding items, entering an address, choosing delivery, and paying. Completing a cart creates an Order — a separate, permanent record. The two are kept apart because they want opposite things. A cart changes constantly, tolerates half-finished states, and is often abandoned. An order is a financial record: it must not change quietly, and it has to keep saying what the customer actually agreed to pay.

Cart attributes

Every amount comes in two forms: total is the raw value ("135.60") and display_total is formatted for the currency ("$135.60"). Render the display_ one.
Money is sent as a string, not a number. JavaScript numbers can’t represent every decimal exactly — 0.1 + 0.2 gives 0.30000000000000004 — which is not something you want inside a price. Let Spree do the arithmetic and render the formatted strings.

Creating a cart and adding items

Every change returns the whole cart with new totals, so you never need a second request to refresh the summary. When a guest signs in, attach their cart to the account:

Checkout requirements

A cart tells you what it still needs before it can be completed:
Response
Every entry has the same four fields. Branch on codemessage is translated prose meant for a person, and step is only a grouping hint. This is what lets you design your own checkout. Spree tells you what’s missing; you decide how and when to ask for it — one long page, a few steps, or whatever suits your storefront. Seven checks run on every cart read:
An empty requirements array does not guarantee completion will succeed. What a cart reports is the advisory set. Completing one additionally checks stock (out_of_stock, discontinued), negotiated quantity rules (quantity_rule_violated) and the guest-checkout policy (guest_checkout_not_allowed) — each of which has to load line items, so they are held back to keep reading a cart cheap. Treat a failed completion as a normal path, not an exception.

Checkout steps

A cart also reports a step list, so you can render a progress indicator:
Spree has no checkout state machine. The list is computed from the cart’s own data, and nothing on the server refuses a write because of where the customer appears to be — you can set an address, choose delivery and attach a payment in any order. current_step is simply the step of the first unmet requirement. Five steps are built in, and three only appear when they apply: So a free digital order reports address then complete. A storefront that hardcodes five steps will show two that never apply.
Requirements use one grouping name that is not a step: cart. Missing line items, the order minimum and stock problems are filed under it, and there is no cart entry in the step list — current_step reports those as address. If you group requirements by step to place them on screens, give cart a home.
Checkout introspection lives on the cart, not the order. An order has already been placed, so current_step and requirements do not appear on it. To add your own step or requirement, see Customizing checkout.

Checkout

1

Add the customer's details

Store SDK
2

Choose delivery

Each fulfillment offers delivery rates. Pick one per fulfillment.
Store SDK
3

Take payment

For providers like Stripe, create a payment session and confirm it with their own SDK.
Store SDK
4

Complete the cart

Store SDK
You get back an Order.
Discount codes, gift cards and store credit can be applied any time before completion:

What completion guarantees

Completing a cart moves real money, so Spree is careful about it: Double submission is safe. A customer double-clicking “Place order” gets the same order back rather than a second charge. This holds even when your app runs on several servers. An interrupted completion recovers. If something dies after the customer was charged, trying again finishes the job instead of charging twice. Totals are worked out at the last moment. The amount charged is calculated when the cart is completed, not taken from an earlier request — so a price or promotion that changed while the customer was reviewing can’t lead to the wrong charge. If a cart can’t be completed you get told why — a payment failure, or unmet requirements — and the cart is left alone so the customer can fix it and try again.

Abandoned carts

Carts emit cart.created, cart.updated and cart.deleted events, which also reach webhooks — enough to drive abandonment email without polling for changes. Completed carts are kept rather than deleted, and an order records which cart it came from, so you can measure conversion. Abandoned carts are cleared out on a schedule you control; carts with a payment in progress are never removed.