Case study

From add to cart to refund

A store’s checkout is built around a small set of order states. Stock is held at checkout and committed at payment. Sales tax is decided by the ship-to address: since South Dakota v. Wayfair (2018), a state can require out-of-state sellers over its threshold to collect. Cards and PayPal are authorized at order and captured at shipping, Alipay is charged at once, ACH and checks ship once they clear, and purchase orders ship on approved credit. Before shipping an order is cancelled; after, it is returned, and refunds go to the original payment method. The company in this case study is fictional.

Last updated

Cart · stock · checkoutSales tax · payments · returns

Kestrel Supply sells office and workshop supplies to households and businesses across the US, from one warehouse in Ohio. Its old store takes cards only and charges one tax rate everywhere. Business buyers want to pay by purchase order and check, visitors from China ask for Alipay, and the accountant has a question about sales tax in other states. This is how we would build the new cart and checkout, one decision at a time.

45 + DChave a statewide sales tax; five states have none
$100,000a year of sales: the South Dakota threshold the Supreme Court upheld in 2018
30 daysto ship under the FTC rule when you promise no date
6ways to pay: cards, PayPal, Alipay, ACH, check and purchase order
Fictional company

Kestrel Supply and its numbers are fictional; the tax rates in the worked example are made up. The law and the payment facts are real and linked to their sources below. This is how we build stores, not legal or tax advice: check your own states with a tax professional.

The order lifecycle

An order moves through a small set of states, and every screen and email hangs off them.

Before any screen is drawn, we agree on the states an order can be in and the moves between them. Cards pass straight through; checks, bank debits and purchase orders wait for payment; a shortage sends the order to backorder; every path can end in a cancellation or a return.

Order states: cart, checkout, placed, picking, shipped, delivered; side states awaiting payment, backordered, cancelled and returnedCartadding itemsCheckoutaddress, taxPlacedstock heldPickingpayment OKShippedcard capturedDeliveredreturn windowHAPPY PATHCancelledvoid or refundAwaiting paymentcheck · ACH · PO creditBackorderedcustomer choosesReturnedRMA · inspect · refundnot paid yetclearedshort / restockedreturncustomer cancelsnever paidcustomer won’t wait: refund within 7 working days
Every order state, and what moves an order between them. Each arrow is one piece of code, one email to the customer and one test.

The cart

The cart is cheap and forgiving. The order it becomes is frozen.

Data model: customer, cart, cart line and product above; payment, order, order line and return belowCustomeremailtax_exempt_certpo_credit_limitCartcustomer or guestcurrencyexpires_atCart lineskuqtyprice_shownProduct (SKU)price_centstax_codeon_handreservedPaymentmethodstatusprovider_refidempotency_keyOrdernumberstatusship_tototal_centsOrder linesku, qtyunit_price_centstax_centstax_jurisdictionsReturnrmalinesreasonrefund_cents1 : N1 : NN : 11 : N1 : N1 : Ncheckout copies prices and taxfrozen at that moment
Eight tables carry the whole store. Checkout copies the cart into an order, with prices and tax fixed at that moment.
  • Money in whole cents. Store $19.99 as 1999, never as a floating-point number, so totals never drift by a cent.
  • A cart is not an order. Carts are cheap and get abandoned. Guests get a cart tied to a cookie; when they sign in, the two carts merge.
  • Show the price, re-check it at checkout. If a price changed while the item sat in the cart, say so before payment, not after.
  • Freeze the order. At checkout, copy each price, tax amount and the ship-to address onto the order. A placed order is never recalculated from today’s catalog.
  • Limits belong on the server. Maximum quantity, minimum order and who may buy on account are checked by the API, whatever the browser sends.

Out of stock

Hold stock at checkout, commit it at payment, and tell the customer the moment it runs out.

Flowchart: check stock when adding to the cart, again at checkout, and again when picking; each shortage has its own answerAdd to cartIn stock?Hold stockat checkoutStill instock?Paystock committedShort atthe shelf?ShipYESYESNONOBackorder dateor “notify me”NOTell them nowbefore paymentYESFTC rule: give a choicenew ship date, consent or cancelrefund within 7 working days
Three checks, three honest answers. The bottom-right box is the law, not good manners.
MomentWhat happens to stockWhat the customer sees
Adding to the cartNothing is heldShow "in stock", "only 3 left" or a backorder date. Offer "notify me" when it’s gone.
Starting checkoutStock held for a short time (we use 15 minutes)If it ran out meanwhile, tell the customer and fix the cart before they pay.
Payment acceptedStock committed to the orderThe hold becomes a firm allocation; the warehouse can pick it.
Short at the shelfOversold: counts were wrongThe FTC rule applies: a new date and the choice to wait or cancel, in writing.

The FTC’s Mail, Internet, or Telephone Order Merchandise Rule says you must have a reasonable basis to ship when you say, or within 30 days if you say nothing (50 days when the buyer applies for credit with you to pay). If you can’t, you must tell the customer the new date and let them agree to the delay or cancel. If they cancel, refund within seven working days, or within one billing cycle when you extended the credit yourself.

Checkout

Checkout is a conversation between four systems, and only one of them ever sees the card number.

Checkout sequence between the browser, our API, the tax engine and the payment provider; the card goes from the browser straight to the providerBrowserOur APITax enginePayment providerPlace order: cart, address, keyQuote tax: ship-to, tax codesRate per line and jurisdictionFinal total with taxCard typed into the provider’s fieldOne-time tokenConfirm: token + idempotency keyAuthorize the totalAuthorized, or declinedCommit stock · save order · log taxWebhook later: captured, refundedCard numbers never reach our server, which keeps the store in the shortest PCI self-assessment (SAQ A).
From Place order to a paid order. The amber arrows never touch our server: the card goes from the browser to the payment provider, and we get a token back. This shows the provider’s card fields in our page; with the off-site payment page below, the customer types the card on the provider’s own page instead.

Since March 31, 2025, an online store that sends all card data to a provider’s hosted fields or payment page validates with the January 2025 version of SAQ A, which adds one condition: the merchant confirms its site isn’t open to script attacks that could affect its e-commerce systems. In practice that means our strict Content Security Policy and no third-party scripts on the checkout page.

Off-site payments

The safest card number is one your store never has.

Hackers go where the card numbers are. If they never reach Kestrel’s website or server, a break-in yields order history and addresses, not cards. There are three ways to take a card, and we recommend the first: send the customer to the payment provider’s own page, the way PayPal and Alipay already work, and bring them back to a thank-you page when it’s paid.

Three ways to take a card: an off-site payment page and the provider’s fields keep card numbers off our server; a card form on our own server exposes them to hackersOff-site payment pageRECOMMENDEDCustomerbrowserProviderpayment pageOur serverorder onlycardorderpaidIF OUR SERVER IS HACKEDno card numbers to stealPCI: SAQ AProvider’s fields in our pageGOODCustomerprovider frameProvidercard fieldsOur serverorder onlycardorderpaidIF OUR SERVER IS HACKEDno card numbers to stealPCI: SAQ A, page locked downCard form on our serverAVOIDCustomerbrowserProviderAPIOur serversees cardscardIF OUR SERVER IS HACKEDevery card typed since the break-inPCI: the full standard
Where the card number travels, and what a hacker could take. Amber is card data. In the first two options it goes straight from the customer’s browser to the provider; only the third puts it on our server.
OptionWhat the customer seesOur security workOur pick
Off-site payment pageClicks Pay, lands on the provider’s secure page (its own web address), pays, and comes back to our thank-you page.The least: card numbers never touch our pages or servers. Shortest PCI self-assessment (SAQ A).Default for Kestrel
Provider’s card fields in our pageStays on our checkout page; the card boxes are small frames served by the provider.Also SAQ A, but the page around those frames must stay locked down against injected scripts.When the look matters
Card form on our own serverOur own form; the card number passes through our server to the provider.The full PCI standard, yearly, and every card typed since a break-in is exposed.Never
  • What we store: the provider’s payment ID, the card brand, the last four digits and the expiry date. Enough for receipts, support calls and refunds.
  • What we never store: the full card number or the security code. Saved cards for repeat buyers live in the provider’s vault, behind a token.
  • Refunds still work: a return or cancellation refunds by payment ID through the provider’s API. Nobody needs the card again.
  • The order is paid when the provider says so: by a signed webhook, never because the customer landed on the thank-you page.

Per Stripe’s integration security guide, a business that handles card numbers itself may have to meet more than 300 PCI DSS security controls, while the card brand, last four digits and expiry date that come back from a payment are outside PCI scope and safe to store.

Sales tax

Since 2018, where your customers live decides where you owe sales tax.

In South Dakota v. Wayfair (June 21, 2018, 5–4), the Supreme Court overruled the old physical-presence rule. A state may now require a seller with no office or warehouse there to collect its sales tax. South Dakota’s law, which the Court upheld, covered sellers with more than $100,000 of sales or 200 separate transactions into the state a year. Each state sets its own threshold: California’s is $500,000 of sales of goods a year.

Flowchart: does the ship-to state have a sales tax, do we have nexus there, is the buyer exempt; if yes, yes and no, collect tax by addressShip-toaddressState hassales tax?Nexus inthat state?Buyerexempt?Collect taxby addressYESYESNONONo state taxAK, DE, MT, NH, OR(Alaska towns may tax)NONot yetno office, staff or stock there,under its sales thresholdYESCharge $0keep the resale orexemption certificatestate + county + city+ district, per line
Do we collect tax on this order? Run for every order at checkout, by the ship-to address, not the billing address.
5

states have no statewide sales tax: Alaska, Delaware, Montana, New Hampshire and Oregon, per the Tax Foundation. Alaska’s cities and boroughs can still levy their own.

24

states have adopted the simplified, shared rules of the Streamlined Sales Tax Agreement, which makes registering and filing in them easier.

The rate isn’t one number per state. It stacks state, county, city and special district, and what is taxable changes too: some states tax shipping, some don’t; clothing, food and services each have their own rules. Business buyers who resell what they buy, and many nonprofits, give you an exemption certificate instead of paying tax, and you keep it on file in case of an audit. Orders placed through a marketplace are usually taxed by the marketplace, not by you.

Worked example (rates made up: 6% state + 1% county + 0.5% city = 7.5%)PriceDiscountTaxableTax
Desk chair$249.00−$24.90 coupon$224.10$16.81
Printer paper, 2 cases$85.00$85.00$6.38
Assembly service$60.00not taxable here$0.00
Shipping$15.00$15.00$1.13
Total: $384.10 + $24.32 tax = $408.42$324.10$24.32

Rounding each line gives $24.32; rounding the whole invoice ($324.10 × 7.5% = $24.3075) gives $24.31. Pick the method your states allow, use it everywhere, and store the tax per line and per jurisdiction: returns and tax filings both need it.

Payments

Six ways to pay, and each one decides when the box can leave the warehouse.

When money is secured and when goods ship: cards, PayPal and Alipay are secured at order; ACH and checks when they clear; purchase orders ship first and are paid about 30 days laterORDERPAYMENT CLEARSSHIPSINVOICE DUE, NET 30Cardsauthorize now, capture at shipPayPalauthorize now, capture at shipAlipaycharged at orderACH bank debitship once it settlesPaper checkship once it clearsPurchase orderapproved accounts onlywe carry the credit riskmoney securedgoods ship
When the money is safe, and when the goods ship. Purchase orders are the only method where the goods leave before the money is secured: that’s why they need a credit check and a limit.
MethodHow it worksWhen we shipRefunds and disputes
Cards: Visa, Mastercard, American Express, DiscoverThe payment provider’s own card fields sit in our page; our server only ever receives a token.Authorize at order, capture when it shipsRefund to the card. Chargebacks are possible for months.
PayPalA PayPal button; the customer pays from whatever is linked to their PayPal account. PayPal supports authorizing now and capturing later.Authorize at order, capture when it shipsRefund through PayPal.
Alipay and other local walletsRedirect to the wallet app, for customers from China and Chinese travelers. Through Stripe, Alipay is charged at once: no separate capture.Charged at order; refund if we cancelRefund within 90 days of payment (Stripe). No chargeback process.
ACH bank debit (e-check)The customer authorizes a one-time debit from a US bank account.Once the debit settlesRefund by ACH credit. A debit can still bounce.
Paper checkThe order waits in "Awaiting payment" until the check arrives and is deposited.Once the check clearsRefund by check or ACH.
Purchase orderApproved business accounts only, up to a credit limit. The invoice carries the buyer’s PO number and terms such as net 30.Right awayA credit memo against the invoice.

One payment provider can carry cards, wallets and foreign methods behind a single integration: per Stripe’s documentation, Alipay works for Stripe accounts in the US and many other countries, and customers see the amount in Chinese yuan. We turn on only the methods Kestrel’s customers actually ask for: every method is another refund path to test. PayPal Checkout is added on its own, with its own button and webhooks.

Cancellations and returns

Before it ships, cancel. After it ships, it’s a return. Both end in the same refund rules.

Flowchart: if not shipped, void an authorization or refund; if shipped, send a return label, inspect, then restock and refund or refund per policyCustomerwants outShippedyet?Card onlyauthorized?Void the holdno charge, no feeRelease stockorder cancelledRefundoriginal methodNOYESNOYESReturn labelRMA numberReceiveinspectResellable?Restockrefund item + its taxRefund per policyrestocking fee or vendor claimYESNO
What happens when a customer wants out. Void beats refund: an authorization that was never captured costs the customer nothing and us no fees.
  • Before it ships. Cancel at once. A card that was only authorized is voided: no charge, no refund to wait for. Stock goes back on the shelf.
  • After it ships. The customer asks for a return; we email a label and a return number (RMA), so the warehouse knows what is coming.
  • When it arrives. Inspect it. Resellable items go back into stock; damaged ones go to a vendor claim or a write-off.
  • The refund. Back to the original payment method, including the sales tax charged on the returned items. Shipping and restocking fees follow the written policy.
  • When we cancel. If we can’t ship, the refund is real money: under the FTC rule, store credit can’t replace it.

Rules we build by

Six rules that keep the money right

  1. Every payment call carries an idempotency key. A double click, a retry or a dropped connection charges the customer once.

  2. Trust webhooks, not the browser. The payment provider tells our server when money is captured, refunded or disputed; a closed tab changes nothing.

  3. Keep card numbers off your servers. Send customers to the provider’s payment page, or use its card fields: nothing to steal, and the shortest PCI self-assessment (SAQ A).

  4. Let a tax engine do the rates. Thousands of local rates change through the year. We keep the rules for nexus, exemptions and what we sell; the engine keeps the rates.

  5. Write the policy before the code. Return window, restocking fee, who pays return shipping, how cancellations work. The code follows the page the customer reads.

  6. Test the unhappy paths. A declined card, a webhook that arrives twice, stock that vanishes mid-checkout, half an order returned.

Building a store, or adding checkout to your site?

I plan and build them: the cart, the payment methods your customers use, sales tax, and the return rules, with every unhappy path tested. Every engagement starts with a free 30-minute call.

Sources