xMatix
Sign in Request demo
xMatix
PRODUCTS
SalesField SalesCRMRewardsClaimsInventoryProcurementWarehouse ManagementField ServiceServiceSupportTelephony & MessagingFinance & AccountingPayrollExpense ManagementCommercePortalsAnalytics & ReportingData StudioMobile AppSee all products →
PLATFORM
Platform overviewApp BuilderAutomationIntegrationsSecurity & GovernanceChange ManagementDevelopers
SENSE AI
Sense AI overviewSense AssistSense ControlSense VisionAI StudioTrust & governanceIn Claude & ChatGPTUse cases
SOLUTIONS
FMCG & DistributionManufacturing & Dealer NetworksAutomotive & DealershipsPharma & HealthcareConsumer DurablesAgri-InputsBuilding MaterialsService NetworksWarehousing & 3PLFinancial AccountingERP SoftwareIndia GST ComplianceUAE VAT & e-InvoicingSaudi ZATCA & VATAll solutions →
RESOURCES
Knowledge CenterDeveloper & CLIBlogGuidesWhat is xMatix?Company facts
COMPANY
AboutCareersPartnersEventsContactAuthorsLegal
Sign in Request demo
BLOG · ENGINEERING

The checkout that freezes the deal before the money moves

Every commerce system has to answer one question: what happens when the price changes between "pay" and "paid". Freezing the deal before the money moves turns a whole class of failures into one that cannot happen.

· Platform & Domain Architecture · · 7 min read

There is a moment in every online sale that looks instantaneous and is not. A shopper taps Pay. A gateway redirects them somewhere. They authenticate, or they hesitate, or their bank sends a one-time code that takes ninety seconds to arrive. Then they come back — and the system has to decide what the order costs.

Between those two instants, the world moved. Someone repriced the item. A colleague published a new catalogue. The last unit in stock went to a faster shopper. A coupon hit its cap. None of that is exotic; on a busy day it is ordinary.

So: what do you charge, and what do you promise?

The two answers most systems give

The common design validates after the payment. Take the money, then check stock and price, and if something moved, unwind it. The result is a refund queue and a support conversation that starts with "we took your money but".

The other common design validates before and hopes the gap is short. It usually is. When it isn't, you get the same refund queue, less often, and with less certainty about which orders were affected.

Both share a hidden assumption: that placing an order after payment is an operation that can fail on commercial grounds. Once you accept that, you are committed to building the machinery for handling it — reconciliation, partial refunds, apology emails, and a permanent class of edge cases that only reproduce under load.

The third answer: make the failure impossible

xMatix takes a different position. At the moment a shopper initiates payment — not at add-to-cart, not at address entry, but at payment-init — the platform does four things in one step:

  • Reprices the basket from scratch, through the same engine that prices every other order in the system.
  • Reserves the stock, on channels whose policy is to refuse overselling.
  • Holds the coupon, so a scarce code cannot be spent twice while this shopper is authenticating.
  • Hashes and locks the result — the commercial snapshot — with an expiry.

Only then does it ask the gateway for money.

The consequence is worth stating precisely, because it is the whole point: a captured payment can always be placed. Order placement converts a snapshot that was already validated. It does not re-derive prices, re-check stock, or re-evaluate coupons, because all of that happened while the shopper still had their finger over the button.

Which means every checkout failure in the system is, by construction, a pre-payment outcome. Basket empty. Price changed. Out of stock. Coupon exhausted. Payment declined. Address invalid. Read that list again and notice what it does not contain: any state in which the customer has been charged and does not have an order.

What this costs you

Honesty requires the trade-off. Freezing has a price, and it is paid in three places.

Reservations expire, and expiry has to be real. If a shopper abandons a payment, the stock they were holding must come back — promptly and without a human noticing. That means a job, and a job that runs reliably enough that you can trust the sellable figure. A reservation system you cannot trust is worse than none, because it silently under-sells.

The lock has a shelf life. A snapshot cannot be honoured forever; if a shopper wanders off for an hour and returns, the deal is stale and they are repriced. That is a slightly worse experience than pretending otherwise, and it is the correct one.

You must reprice at the right moment. Freeze too early — at add-to-cart — and you are quoting stale prices for as long as the basket lives. Freeze too late — after capture — and you are back to refunds. Payment-init is the only instant where the shopper has committed and the money has not yet moved.

The pattern generalises

The same reasoning shows up in a second place in the same system, and the symmetry is not an accident.

Coupon budgets could be enforced by refusing a shopper the moment a campaign crosses its cap. That is arithmetically correct and commercially stupid: the shopper did nothing wrong, and they experience a rejection caused by other people's purchases seconds earlier. So the order completes and the redemption is flagged as having crossed the budget. A human reviews the overspend afterwards, which is a conversation you can have — unlike the one where a customer is refused at checkout for reasons invisible to them.

Both decisions come from the same instinct: work out where a system is allowed to fail, and push every failure to the side of that line where it is cheap and explicable.

What it looks like in the records

A design like this is only trustworthy if you can audit it. Every payment attempt in xMatix carries its own append-only event trail — initiated, intent created, redirected, webhook received, signature verified, captured — written from the gateway's own signed callbacks rather than reconstructed later. When support is asked what happened to a payment, the answer is a sequence of recorded facts with timestamps, not an inference from the current state of a row.

And when a webhook never arrives — because networks are what they are — a reconciliation job polls the gateway for anything not in a terminal state. The captured payment that nobody told us about still becomes an order.

The generalisable bit

Most commerce architecture debates are about layering: headless or not, one service or several, which framework. Those matter less than people think.

What matters is where you draw the line between "can still fail" and "cannot fail any more", and whether the money crosses that line before or after the promise does. Get that ordering right and a whole category of operational pain never gets built. Get it wrong and no amount of good engineering downstream will fully clean it up — you will just get very good at refunds.

← All posts
See it on your business.
Request a demo