← Back to the merchant guide Selling

Carts & bundles

One checkout, 2–10 products, one payment. Buyers tick what they want, every item delivers on its own, and the price is worked out on the server rather than in their browser. This page covers what a buyer sees, the two bundle discount rules and how they differ, how to build one, and when a cart is the wrong shape.

Two names, one thing. In the dashboard this offer type is labelled Bundle. This page calls it a cart, and keeps the word bundle for one of the two pricing rules a cart can carry — the fixed bundle, one price for the complete set. So a cart may or may not have a bundle price on it. Where you see “Bundle” in the dashboard, read “cart”. Selling one thing with add-ons? See bumps & upsells.

01 — The idea

What a cart is

A cart offer is one checkout page carrying several products. The buyer ticks the ones they want, and pays once. They get one receipt, one invoice with a line per item, and each item delivers on its own.

It is the right shape when someone should be able to choose — a course plus optional workbooks, a set of templates people buy in different combinations, a launch where three things go out together.

Products
2–10each listed once
Quantities
1–99per item, if you allow it
Discount rules
Oneper cart, two kinds
Payment
Card & PayPalone charge for the basket

02 — Their side

What the buyer sees

One page, one decision at a time, one payment. Nothing is charged for an item they did not tick, and nothing is delivered for one either.

The anatomy of a cart checkoutOne checkout card listing three products with tick-boxes: two ticked and charged, one left unticked and greyed out. Below them a discount hint, a subtotal, a tax line, and a single Pay button for the whole basket. The anatomy — one page, several products, one payment Everything the buyer decides happens on a single checkout. The maths happens on the server. Fernwood & Co. — Creator Bundle CART OFFER The Joinery Course nine lessons · lifetime access NZ$49 Measured Drawings Pack qty 1–3 — quantities are allowed, and they count NZ$19 Bench Review Call left unticked — never charged, never delivered NZ$99 Buy 3 or more items and save 15% — static, honest, no timer Subtotal NZ$68.00 Tax — by the buyer's location at checkout Pay NZ$68.00 WHAT THE BROWSER SENDS POST /quote { "items": [ { product, qty }, … ] } ← no prices. ever. WHAT THE SERVER DOES · looks up YOUR prices · applies the one discount rule · computes tax per line · ignores any number the page sent a forged total is not argued with — it is unread AFTER PAYMENT one receipt · one invoice, a line per item each item delivers on its own link
Two items ticked and one left alone. The untouched item is never charged and never delivered — there is no partial state to reconcile later, because the basket the buyer sees is the basket the server prices.

03 — The part that matters

The price is not in the browser

When a buyer ticks a box, their browser tells us which products they picked — never what those products cost. The total is worked out on the server, from your prices, every time.

That is testable rather than reassuring. Send the quote endpoint a selection with a total of one cent attached and a fabricated discount of NZ$999, and the correct total comes back: the amounts in the request are ignored because they are never read. Ask for more of something than you allowed, or for a product that is not in the cart, and it refuses and says which.

The price is not in the browserA forged quote request carrying fabricated amounts branches into three fates: the amounts are never read because they are not on the endpoint's allowlist, the impossible selections are refused with a stated reason, and only the buyer's choices survive. From those choices the server rebuilds the total from its own rows and that number is what Stripe is asked to charge. The price is not in the browser A crafted request meets three fates — unread, refused, or honoured as a choice. None of them sets a price. A CRAFTED REQUEST POST …/{slug}/quote { "items": [ {"product_id":"p_joinery", "qty": 40, "unit_price_cents": 1}, {"product_id":"p_not_mine"}], "total_cents": 1, "discount_cents": 99900, "country": "NZ" } FATE 1 — NEVER READ "total_cents" "discount_cents" "unit_price_cents" Not on the endpoint's allowlist, so they are never in the validated data. CartPricer reads product_id and qty from a selection, and nothing else. FATE 2 — REFUSED, AND TOLD WHY That item isn't part of this offer — refresh the page and pick again. unknown product_id Only {max_qty} of “Measured Drawings Pack” per order. qty > max_qty Each item can only appear once — change its quantity instead. duplicate product_id Select at least one item to pay. empty selection FATE 3 — HONOURED AS A CHOICE, NEVER AS A NUMBER product_id · qty → checked against the offer's own items and max_qty coupon → a code to look up, never a discount amount country · postal_code · tax_id → tax inputs, declared by the buyer WHAT THE SERVER REBUILDS, FROM ITS OWN ROWS the same pricer runs on /quote, the Stripe intent and the PayPal create 1 — lines products.price_cents × qty 2 — bundle rule the offer's discount rule row 3 — coupon the coupons row for that code 4 — tax Stripe Tax, calculated per line total_cents = subtotal - bundle - coupon + tax ↓ written to orders.total_cents ↓ charged as the Stripe amount Nothing the browser sends decides a price — so the worst a determined buyer can do is get a correct one. The price is server-authored; the tax jurisdiction is not — country, postal code and tax id are buyer-declared, and nothing checks an IP.
A cart quote request carrying a fabricated total, discount and unit price meets three separate fates: the amounts are dropped because they are not on the endpoint's validation allowlist, the impossible selections are refused with a stated reason, and only the buyer's choices — which products, how many, a coupon code and the tax fields — survive. From those choices CartPricer rebuilds the total in a fixed order, lines then bundle rule then coupon then tax, reading the price from the merchant's own product rows; that figure is written to the order and is the amount Stripe is asked to charge. The one thing the buyer still controls is the tax jurisdiction: country, postal code and tax id are self-declared and nothing verifies them.
Why this is worth a section. A checkout that trusts the browser can be edited by anyone who opens developer tools. Every cart total, every quantity limit and every discount is decided server-side, so the worst a determined buyer can do is get a correct price.

04 — Bundle pricing

The two discount rules

A cart carries one discount rule, and there are two kinds. They sound similar and they count completely differently — this is the thing to get right.

The two discount rules count differentlyThe same basket — three copies of one NZ$19 product — sent through both rules. The percent rule counts units, so three copies qualify. The fixed-bundle rule needs one of every product, so three copies of one do not. Two discount rules — and they count on different axes Same buyer, same basket: three copies of the NZ$19 drawings pack. Watch what each rule does with it. THE BASKET Drawings Pack × 3 one product, three units — NZ$57 “% off when they buy several” counts UNITS — quantities included 3 units ≥ 3 → rule fires NZ$57 − 15% = NZ$48.45 discount granted — no bundle in sight good for moving volume “one price for the whole set” needs ONE OF EVERY product 3 copies of one thing ≠ the set → no fire NZ$57 − nothing = NZ$57.00 same basket, zero discount good for selling the bundle Pick by the outcome you mean — the checkout does what you configured, not what you meant.
Same basket, two rules, two answers. "% off when they buy several" counts units, so three copies of one product qualifies. "One price for the whole set" needs one of every product, so three copies of one does not.

"Take any three, save 15%"

A percent threshold: pick a minimum number of items and a percentage. It counts units, so three copies of one product hits a three-item threshold. Use it when you want the basket to get bigger — it rewards quantity without caring which things.

"All five for NZ$199"

A fixed bundle: one price for the complete set. It needs one of every product in the cart, so it is an all-or-nothing decision rather than something a buyer edges towards. Use it when the set is the product and you want to anchor against the separate total.

The server refuses a bundle price that is not a discount. Set a fixed bundle at or above what the items cost separately and it will not save — because it would not be a bundle, it would be a surcharge with a friendly name. The message tells you both numbers.

Nothing can take a charge below NZ$0.50, which is Stripe's minimum rather than a GoCushy opinion. It sits underneath both rules.

05 — Doing it

Building one

Two ways in, and neither is the lesser one. Both write the same offer and both are held to the same server-side rules.

Two ways to build the same cartThe dashboard wizard and a sentence to your agent, side by side, both producing the same offer under the same server-side rules. Two front doors, one cart — the wizard and the sentence Both create the same offer under the same server-side rules. Neither is second-class. IN THE DASHBOARD New offer One-time Subscription Multi-product cart ● The Joinery Course · NZ$49 · delivery ✓ Drawings Pack · NZ$19 · delivery ✓ + Add product Bundle discount — “% off when they buy several” counts items, not different products — 3 of one thing qualifies won't go live if an item can't deliver — refused at creation, not discovered by a stranded buyer OR SAY IT TO YOUR AI “Make a cart offer called Creator Bundle with my course at NZ$49, the workbook at NZ$19 and a call at NZ$99 — 15% off if they take all three.” the same live cart, either way SAME RULES AT BOTH DOORS 2–10 one-time products · one currency no bumps (the items are the bumps) card + PayPal · every item must deliver two doors that disagreed about what a cart is would be worse than one door
The dashboard wizard and a sentence to your agent produce the same offer. Use whichever suits the moment — the wizard when you are thinking in a list, the sentence when you already know what you want.
Say this to your agent Make a cart called "Creator Bundle" with my Joinery Course at NZ$49, the Measured Drawings pack at NZ$19, and the Bench Review call at NZ$99. Let people buy up to three drawings packs. If they take three or more items, take 15% off.

Ask it to read the cart back afterwards if you want to check what it built, or open the offer in your dashboard and look at the items there.

06 — After the sale

Delivery, per item

Each product in a cart delivers on its own. The course link goes to the people who bought the course; the drawings link to the people who bought the drawings. Nobody receives something they did not pay for, and nobody misses something they did.

That is also why every item needs its own delivery — a link, an on-screen access note, or webhook delivery — before the cart can go live. A cart with one undelivered item is the same failure as a single offer that takes money and delivers nothing, just harder to spot.

Delivery, per itemThe New offer dialog with type Bundle selected, showing three per-item delivery blocks: one with a delivery link filled, one with an access note filled, and one with both empty which is refused by the wizard. To the right, what the empty one would cost: the merchant-side warning chip on the Offers row, the buyer who paid and received nothing, and the same refusal returned to an AI. Delivery, per item New offer → Bundle. Every product carries its own delivery, and step 1 will not pass until each one does. New offer 1 · Basics 2 · Checkout page 3 · Bump & upsell 4 · Selling rules What are you selling? One-time Course, ebook, templates — paid once Subscription Membership billed monthly or yearly Bundle 2–10 products on one page, one payment Delivery (how buyers get access — set at least one) Set at least one — a delivery link or an on-screen access note. 1. The Joinery Course Delivery link (or an access note below) https://members.fernwood.co/joinery Access note (shown on the thank-you page) 2. Measured Drawings Pack Delivery link (or an access note below) https://… Drawings are emailed with your receipt. 3. Bench Review call Delivery link (or an access note below) https://… Access note (shown on the thank-you page) Add a delivery link or an access note — every cart item delivers on its own. WHAT THE EMPTY ONE COSTS OFFERS Fernwood & Co. — Starter Bundle ⚠ set delivery Bench Review call — no delivery set, so buyers who select it receive nothing. Set a delivery link or access note on the product itself (ask your AI), or set up webhook delivery. THE BUYER jane@example.com ticks all three items, pays once. Two arrive. Bench Review call has nowhere to send her — no link, no note, no webhook. THE SAME REFUSAL, TOLD TO AN AI POST /v1/offers/{id}/resume → 422 delivery_required Setting this offer live would take money and deliver nothing— Bench Review call has no delivery link or access note. Ask the seller how buyers get access and set it with update_product (delivery_url or delivery_note), or add an order.paid webhook if they fulfil themselves — then resume it. No bundle goes live with a blank item — the refusal lands on step 1, not on a buyer. Exception: one active order.paid webhook makes every field here optional — Delivery::accountAutoDelivers short-circuits the gate account-wide, so a webhook seller sees no warning chip and no refusal.
In gocushy's New offer wizard, choosing the Bundle type replaces the single Delivery link and Access note fields with one delivery block per product, and step 1 will not advance until each block has a link or a note: the third item here, with both fields blank, is refused with "Add a delivery link or an access note — every cart item delivers on its own." Had it shipped blank, the Offers row would carry a "⚠ set delivery" chip warning that "buyers who select it receive nothing", and a buyer who ticked all three would be charged once and receive only two. The same rule answers an AI: POST /v1/offers/{id}/resume returns 422 delivery_required with "Setting this offer live would take money and deliver nothing". The one exception is an account with an active order.paid webhook, which makes every one of these fields optional.
One receipt, one invoice, a line per item. Sequential invoice numbering, your business details, tax broken out per line — the things an accountant expects, from a single payment.

07 — The rules

Limits

RuleWhy
2–10 productsBelow two it is a single offer; above ten the page stops being a checkout and becomes a catalogue.
Each product appears onceUse a quantity allowance (up to 99) to sell multiples of one thing.
One discount rule per cartTwo rules on one basket means the buyer cannot tell which applied, and neither can you.
One-time products onlyCarts do not carry subscriptions yet.
No order bumps on a cartA cart already is a page of tick-boxes; add the product as another item.
Card and PayPalPayPal takes a cart as a single order for the total. Airwallex, the currency menu and the crypto rail stay off a cart for now.
Every item must deliverChecked before the cart can go live, and named item by item if something is missing.

A cart can carry a post-purchase upsell chain — see bumps and upsells — it just cannot carry bumps on the page itself.

08 — Restraint

When not to

When there is really one thing you want them to buy. A cart asks the buyer to make several decisions. If you know which one you want them to make, a single offer with a bump asks for one and gets out of the way.

When the extras are afterthoughts. Ten half-finished products on one page do not add up to a bundle. They add up to a page a buyer leaves.

09 — When it looks wrong

If something looks wrong

The cart will not go live

Almost always an item without delivery. The refusal names the item — give it a link, an access note, or webhook delivery.

The bundle price will not save

A fixed bundle has to be cheaper than the items bought separately. The message shows both numbers so you can see the gap.

A buyer says the discount did not apply

Check which rule you set. A percent threshold counts units; a fixed bundle needs one of every product. That distinction is the most common surprise.

The Airwallex or currency options are missing

Expected on a cart — a cart has no presentment currency to offer, so the Airwallex element and the currency menu stay off it. Card and PayPal are always there, and USDC on Base appears once the crypto rail is enabled for your account. A single offer still has the full set of rails.

Related

Bumps & upsells · Products & offers · See a live one