Features · Checkout
A checkout that refuses to charge the wrong price
One call turns a cart into an order and tells the client how to pay. Retry it and you get the same order back; change a price underneath it and it answers 409 with the new figures instead of charging something the shopper never saw.
- 1 call from cart to order
- 11 gateway modules, plus cash on delivery
- 0 accounts a shopper needs
The call that places the order
The four things this checkout does that a buyer never sees and an operator depends on.
-
Single-call, idempotent checkout
POST /api/checkout/{code} takes the cart, contact, address, shipping rate and payment data, creates the order and returns what the client must do to pay. An Idempotency-Key retry resumes the same order instead of making a second.
core/checkout.go -
No silent repricing
A price that changed or stock that ran out returns 409 with per-line detail and the current values. The order is never charged at a figure the shopper did not see.
core/checkout.go · LineConflict -
Guest checkout, for good
An order needs an email, not an account; its access token is the guest’s handle. With the identity module a shopper can later claim a guest order into an account by presenting that token.
core/orders.go · ext/identity -
Operator-placed orders
Staff with orders.write place an order for a customer through the same checkout a shopper uses: the same stock reservation, numbering, discount checks and order.created event.
POST /api/admin/orders
Guests find their order
A guest has no account to sign in to, so the way back to an order is the order itself: a link in its emails, or its number and the email or phone it was placed with.
-
A link in the order emails
The “Order placed” and “Order shipped” emails end with a link to the storefront’s order page that opens the order with nothing to type. The texts carry it too; through MSG91 the store’s approved DLT template decides. Staff who manage orders copy the same link from the order’s Customer card to paste into a chat. It uses the store’s verified domain or its address under the base domain, and a store with neither sends no link.
core/storefront_url.go · orderTrackingURL -
Order number and email
Without the link, a guest opens their order with its number and the email or phone on it. A wrong email and an unknown number get the same answer, so nothing says which orders exist, and one visitor gets 20 tries an hour.
core/order_lookup.go · OpenWithContact -
Or a 4-digit code
A storefront can instead have a 4-digit code sent to the email or phone on the order. A code works for ten minutes and five tries, an order gets one a minute and five a day, and the answer is the same whether or not the order and contact matched.
core/order_lookup.go · RequestLookupCode
Abandoned checkouts
A basket somebody left is the order closest to happening. The cart-recovery module notices it within minutes, writes to the shopper, and stops the moment it becomes an order.
-
Noticed within minutes
A basket left for 10 minutes after the shopper typed an email — 30 if they never reached checkout — is recorded with its items and value as they were. The store sets both times. Baskets left before the module was installed are shown, and the reminders never go to them.
ext/cart-recovery · pass.go -
A sequence of reminders
Up to five “wait, then send” emails, one sequence for checkouts and one for carts. Each send is checked again first: nothing goes out if the basket became an order, was emptied, sold out, or the shopper is in it right now. The wording is the store’s, edited with the other emails. The three default reminders greet the shopper by first name when an earlier order gave it, and list each item with its price and the total.
ext/cart-recovery · sendStep -
One shopper, one sequence
A shopper who left one basket on a phone and another on a laptop hears about the newest only. Before each send, a basket stops with the reason “Newer basket” when the same email, compared without case, has a newer one still being chased or already bought. It stays on the screen with that reason.
ext/cart-recovery/pass.go · hasNewerBasket -
A link back to the basket
Each reminder links to the shopper’s basket on the storefront. With no storefront address set, the link uses the store’s own: its verified domain, or its address under the base domain. When the store’s panel address is set, the link passes through the store first, which counts the click.
GET /x/cart-recovery/r/{token} -
The order is credited
When the basket is bought, that order is credited to it. The figures keep apart orders a reminder came before and shoppers who came back on their own.
order.created · cart_id -
Staff can step in
Send a reminder now, copy the recovery link to paste into a chat, or stop the reminders with a reason. Every step is on the basket’s timeline with who took it.
/api/admin/x/cart-recovery/abandonments -
Recovery figures
Abandoned, potential revenue, reminders sent, clicks, recovered orders and revenue, the recovery rate, a funnel, and recovered revenue by day, week or month.
/api/admin/x/cart-recovery/analytics
Payments
-
11 gateway modules
Stripe, Razorpay, Cashfree, Adyen, Paddle, Lemon Squeezy, Creem, Hyperswitch, Helcim, Clover and RevenueCat, each configured from the admin’s payment methods screen.
ext/payments-*/ -
Cash on delivery by default
The built-in cod method confirms the order and takes stock off the shelf at once; payment stays pending until an operator marks it paid. A payment marked by mistake can be taken back, even after the order is cancelled, while money that really went back stays a refund. A store can switch cash on delivery off on its payment methods screen, and it is then offered nowhere.
core/payments.go · codProvider -
Checking a payment with the gatewayPartial
When a gateway’s confirmation never arrived, the order’s “Check with” button asks Stripe, Razorpay or Cashfree. A payment the gateway reports for the order’s total is recorded as the lost webhook would have recorded it, and nothing it did not report is recorded. Missing: the other gateways.
core/payments.go · CheckPayment -
Several gateways side by side
Any number of providers run at once, each at its own checkout route with a webhook route the core owns. Writing your own is a two-method interface.
core/ports.go · PaymentProvider
Signing in
With the identity module, shoppers sign in with an email and password, with a code texted to their phone, or either way into the same account.
-
Email, phone, or either
The store picks how shoppers sign in under Settings › Customer sign-in: email and password, the default; phone and a 4-digit code by SMS, with no password; or both. The engine refuses the way the store did not pick. Phone sign-in can be chosen once an SMS provider is sending, and texts its codes to Indian (+91) numbers. A code works for ten minutes and five tries, and a phone gets one a minute and five a day.
ext/identity/phone_login.go -
One account, either way in
On a store that allows both, a phone with no account lets the storefront ask “Already have an account?” after its code. The shopper signs in with their email and password and the phone joins that account, or opens a new one with it. A signed-in shopper can also add a phone with a code, or an email and password to a phone account. A phone is linked only by its code, and one that already opens another account is refused, not moved.
ext/identity/phone_login.go · ask_if_new -
Codes through MSG91
A store that texts through MSG91 sets a DLT template for the sign-in code and one for the order code, beside the order messages, under Notifications › Setup SMS. Sign-in codes go through MSG91’s OTP API, and each text MSG91 accepts is logged with its request id. When no template would carry a code, the shopper is told the shop cannot text sign-in codes yet, instead of waiting for one that never comes.
ext/notify-msg91/msg91.go -
Codes that fill themselves in
When the storefront names its address and it is the store’s verified domain, or its address under the base domain, the sign-in text ends with the line Chrome on Android reads, so the code fills itself in. Any other address is left out, so no other site can have a shopper’s code filled in. Through MSG91 the line appears once the store’s DLT template ends with it.
ext/identity/phone_login.go · ownSite
Customers and prices
-
Order history for shoppers
With the identity module, signed-in shoppers list and read their orders.
ext/identity -
Address bookPartial
Signed-in shoppers keep labelled addresses with a default. Missing: separate billing and shipping addresses, and staff editing a customer’s book.
ext/identity -
Discount codesPartial
Percentage, fixed or free shipping; scoped to the order, products, collections or a category; with minimum spend, dates, usage limits and once-per-email. A code the shop marks “Show in the basket’s offers” is listed for the basket, with what it takes off or how much more the basket needs. Missing: automatic discounts at checkout, more than one code per cart, buy-X-get-Y.
core/discounts.go -
Price listsPartial
A line’s price resolves by customer group, channel, date window, priority and quantity break. Staff who may set prices can agree a line’s price when they place an order, and it beats every list. Missing: a hook for your own pricing logic, and changing a price on an order already placed or in a shopper’s basket.
core/pricing.go
The product page and the basket
The engine works out each of these, so an offer price, a free-delivery figure or a sold count is the same for every client that reads it.
-
Add-ons at an offer price
A product offers up to ten add-ons in its Complete the set card, each another product’s variant with an optional offer price. The offer holds while the product is in the basket, one add-on for each, and never above the buyer’s own price. Take the product out and the add-on goes back to its own price. A withdrawn offer is a price change, which checkout refuses until the shopper has seen it.
core/upsells.go -
Suggestions
A product suggests up to 24 others, shown in a “You may also like” section of the product page that the theme editor moves or hides. The basket’s suggestions leave out what it already holds, and Basket suggestions, under Themes › Storefront extras, adds one collection’s products after them.
core/upsells.go · cross-sells -
Free-delivery progress
The basket says whether delivery is free, or how much more it needs, read from the shop’s own shipping rates for the destination or the store’s country. There is no second figure to keep in step.
core/upsells.go · FreeShipping -
Highlights and a free gift
Up to eight highlights and a free-gift line per product, typed in the product editor’s Highlights card, each line 80 characters at most. The gift is a note on the product page, not an item added to the basket.
core/product_extras.go -
Videos beside the pictures
The public product lists its pictures and videos in their order, so a storefront’s gallery can play a product video among the photos.
core/catalog.go · Media -
A sold count
Off until the shop turns it on under Themes › Storefront extras. It counts units on confirmed orders, leaving out cancelled, fully refunded and sample orders, and rounds down to tens, hundreds or thousands, so the figure shown is never more than the units sold. A product under the threshold, 25 unless the shop changes it, shows nothing. The count is refreshed every five minutes.
core/sold_counts.go
Stock, tax and shipping
-
Stock reservationPartial
Checkout reserves stock per location when the order is created and holds it until payment or until the unpaid-order window (24 hours by default) runs out. Missing: holding stock at the cart stage.
core/inventory.go · reserveStock -
Shipping in partsPartial
An order can ship in several parcels by line and quantity, each through any carrier module. Missing: several payments on one order.
core/fulfillment.go -
Stock allocationPartial
Each line is reserved at the first active location, by priority, that can cover it all. Missing: other strategies, and splitting one line across locations.
core/inventory.go · pickLocation -
Tax ratesPartial
Flat rates by country, state and category, or for one product, the most specific winning, prices tax-inclusive or not, each line’s tax recorded. A customer group can be tax-exempt, and its orders carry no tax. Missing: tax classes apart from categories and products, and per-channel rates.
core/taxes.go -
Shipping zones and ratesPartial
Zones by country and state, with named rates priced by basket subtotal (“free over 2,000”) or by parcel weight, each with an optional handling fee and delivery estimate. A rate can be kept for one customer group. Missing: postal-code zones.
core/shipping.go -
Only the countries you deliver to
Checkout’s address form is offered only the countries named by the store’s active zones that have a rate for everybody. A store with no rates, or with a priced zone that names no country, delivers everywhere. The engine still offers no delivery for an address no zone prices.
core/shipping.go · ShipsTo -
Address checksPartial
Checkout requires line one, city, postal code and country, and a module’s checkout guard can refuse an address before the order exists. Missing: format rules.
core/types.go · Address.Validate -
City and state from a PIN code
A six-digit Indian PIN code gives the city, as India Post’s district, and the state, from India Post’s All India Pincode Directory built into the engine, with nothing to set up. A PIN that India Post lists with no district or state is left out, and there the shopper types them.
core/postcodes.go
Storefront
-
A Svelte Commerce connectorPartial
@misiki/gocommerce-connector 0.16.0, published on npm, covers catalogue, cart, shipping rates, checkout and order lookup. Sign-in, the address book and order history need GoCommerce’s identity module.
misiki-in/gocommerce-connector -
Several carts at oncePartial
Carts are anonymous tokens, so a client can hold any number. Missing: carts owned by a customer account.
core/cart.go
Not in GoCommerce yet
Named here so nobody has to find out at launch. Each is absent from the code today.
- Recurring paymentsNo stored cards, subscriptions or off-session charges.
- Split paymentsAn order carries exactly one payment provider.
- Gift cards and store creditNo balance, issuing or redemption.
- Multi-currencyA store settles in one currency; channels carry none.
- Tax exemption for one customerExemption comes only through a customer group. No flag sits on a single customer or order.
- Tax or rate providersNo hook for Avalara-style tax or live carrier rates at checkout.
- Click-and-collectNo pickup option at checkout.
- Per-product quantity limitsA cart line is capped only by stock.
- Reorder for shoppersOnly a trade buyer in the b2b module can turn a past order into a new basket.
- Recovery by SMS or WhatsAppA basket carries an email and no phone number, so reminders go by email.
- Chasing a failed paymentOnce checkout runs the basket is an order, and nothing reopens it for another try.
- Merging customer accountsTwo accounts stay two. A phone that already opens one account is refused on another, not moved.
FAQ
Questions about checkout
What happens if a price changes while someone is checking out?
The checkout call answers 409 with the line that changed and its current price, and no order is charged. The client shows the new figure and the shopper decides. The engine never reprices an order silently.
Can shoppers buy without an account?
Yes. Guest checkout is a permanent guarantee in the core: an order needs an email, and the access token returned at checkout reads it back. A store can turn its Guest checkout switch off, and the storefront then asks shoppers to sign in first. Accounts are the identity module, added when you want them.
How does a guest find their order again?
Through the link in their order emails, which opens the order with nothing to type, or with the order number and the email or phone it was placed with. Neither needs an account.
Can shoppers sign in with a phone number?
Yes, with the identity module. The store chooses email and password, a code texted to the phone, or either, and a shopper who uses both ways keeps one account. Codes go to Indian (+91) numbers through the store’s SMS provider, such as MSG91 or Twilio.
Which payment gateways work in India?
Razorpay and Cashfree are modules, and cash on delivery is built in and on until the store switches it off. Hyperswitch and Stripe are also modules; whether a gateway serves your business is between you and it.
Can a cash-on-delivery order be refunded?
Not through the engine. The cod method has no refund call, so the API answers 409. The money goes back however you collected it, outside GoCommerce. Gateway orders refund through their gateway, except RevenueCat’s, which are refunded in RevenueCat.
Does GoCommerce send abandoned-cart emails?
Yes, with the cart-recovery module. A basket left for a few minutes is recorded, and the store’s own sequence of reminders goes out by email, each one checked again before it is sent. When the shopper buys, the order is credited to the basket and the reminders stop.
Does it calculate tax automatically?
From your own rate table: flat rates by country, state, product category or one product, applied at checkout and recorded per line. There is no connection to a tax service yet.
Read the code behind every tile
Each file named on this page is in the GoCommerce repository, MIT licensed. Run it and check.