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 03 · Building the checkout

Discounts at checkout

Automatic discounts and discount codes both land in the checkout's applied discounts and its discount total. How a code is added and removed, how to tell the shopper whether it helped, and what the checkout answers when it refuses one.

Who this is for. Developers adding a discount code field to a checkout page and showing discounts in its order summary. After this page you can apply and remove codes, show automatic discounts, and give the shopper the right message for every kind of code.

Automatic discounts and codes

A discount is one of two kinds:

  • Automatic: it has no code and applies whenever its rules match the order.
  • Code-based: it applies once the shopper adds one of its codes.

Both kinds show in the checkout's appliedDiscounts and add up in its discountTotal, which is in the checkout's currency like every other amount on it (Its currency, and the language of its lines). The codes a cart carries are copied onto the checkout when it is created, so a code the shopper added on the cart page is already there.

Discounts are worked out for the checkout's site, from discounts that are active and inside their from and to dates, in position order. A discount or rule marked final stops any discount after it from applying. Three details decide whether a given code can work:

  • Codes are stored in upper case, so a shopper can type one in any case.
  • A code whose maxUses is 0 cannot be used at all.
  • A person group condition checks the groups of the signed-in member, allowing or forbidding them. A guest has no groups, so a members-only discount needs the shopper to sign in first (Guest checkout and the captcha).

Adding and removing a code

A code is added with POST /cdna-api/checkout/<id>/discount-code and removed with DELETE /cdna-api/checkout/<id>/discount-code/<code>. Only one code per discount can be active at a time, so the production store's discount component removes any code already on the checkout before it adds a new one, and if that removal does not go through, tells the shopper a code is already applied instead of adding a second.

While the checkout is attached to its cart, the codes on the checkout are copied back onto the cart, and the cart worked out again, whenever the checkout changes. The cart and the checkout therefore always agree on which codes are active, and a shopper who goes back to the cart sees the code they added at checkout. Once a payment has split the checkout from its cart (From cart to order), its codes are its own.

Telling the shopper whether the code helped

A code that exists and is within its use limit is added to the checkout ("Discount code added to checkout") even when this order does not meet its dates or conditions. It then takes nothing off. So the message the checkout gives is not the message to show: compare discountTotal before and after, and tell the shopper what actually happened.

JavaScript checkout/components/discount-code-handler.js
const before = CheckoutState.getDiscountTotal();
const response = await Checkout.addDiscountCode(code);
const after = response?.data?.checkout?.discountTotal;
if (parseFloat(after ?? 0) <= parseFloat(before ?? 0)) {
	showMessage("This code doesn't apply to this order.");
} else {
	showMessage('Discount applied.');
}

CheckoutState and Checkout are the page's state object and its API layer (The single-page checkout).

When a code is refused

The checkout answers every code it cannot add with a 400 and one message, "Discount code does not exist": a code that is unknown, one that has reached its use limit, and one for a discount that already has another code on the checkout all receive it. Show the shopper one message that covers the three, and keep the field so they can try another code.

The codeThe checkout answersShow the shopper
Unknown, at its use limit, or for a discount that already has a code applied400, "Discount code does not exist""This code can't be used. Check it, or try another."
Exists, but this order does not meet its dates or conditions"Discount code added to checkout"; discountTotal unchanged"This code doesn't apply to this order."
Exists and applies"Discount code added to checkout"; discountTotal higher"Discount applied."

On the cart, the same cases carry messages of their own; see Changing the cart.

Showing discounts in the summary

Print discountTotal as its own line in the order summary, and the entries of appliedDiscounts beneath it, so a shopper sees both what they saved and why. The production store shows automatic discounts collapsed behind a toggle in the summary and expands them on a click: plain progressive disclosure over markup that is already on the page, with no API call. A <details> element does the same with no script at all.

A code that does not help yet

A code the checkout has accepted stays on it, even while it takes nothing off. Show it with a remove button and the note that it does not apply to this order, rather than as applied, so the shopper is never surprised by the total.

Go deeper

Was this page helpful?

Your answer goes to the docs team.