Skip to content
Log in
mDOS home

How we eliminated double charges architecturally

The Self-Service Engine, and why idempotent off-session payment routing is the single most under-discussed problem in hospitality SaaS.

Published 8 min read
How we eliminated double charges architecturally

A guest in a hotel taps "modify booking" on the in-room tablet at 11:47pm. The 3DS challenge takes 40 seconds. Their phone signal drops. They tap again. In most hospitality systems, they're charged twice and they're calling reception in the morning. In ours, they're charged once. Physically. Not "usually." Not "with monitoring." Architecturally.

This article is about the Self-Service modification engine that mDOS shipped to production this month, and why the payment-engineering work behind it is more commercially important than its operational utility suggests.

The problem most hospitality SaaS hasn't solved

Self-service modification (letting a guest change their own booking, order, or stay without calling reception) sounds like a feature. It is. But the engineering substrate it sits on is unforgiving in a way most product teams don't realise until they ship it and start getting chargebacks.

The base scenario: a guest needs to modify something that costs money. Maybe they're upgrading their room. Maybe they're adding a spa treatment to a stay that's already in progress. Maybe they're partially refunding a deposit because their party shrank from 12 to 10. Maybe they're changing dates on a flexible-rate booking and need to absorb the rate difference.

In all of these, the operational flow looks like this: identify the modification, calculate the financial delta (could be a charge, a refund, or both for complex partial-modification scenarios), route the payment, update the booking record, fire any downstream flows (loyalty adjustments, confirmation emails, ops notifications).

In most hospitality platforms, this flow is built sequentially, with each step calling out to a different system: the booking system updates first, then the payment processor is called, then the loyalty system is told about it, then the email system sends a confirmation. Each step has its own failure mode. Each step is its own opportunity for partial completion.

The specific failure mode that costs operators real money: the guest's connection drops between the payment call and the booking-system confirmation. From the guest's perspective, the request failed. The tablet is showing an error. They retry. The retry goes through. The original payment also went through, because it was in-flight when the connection dropped. The guest is charged twice. Reception finds out the next morning. The guest disputes the charge. The operator absorbs the loss, and the dispute fee, and the customer-service time, and the reputational damage.

This isn't a hypothetical. It's the most common payment-related customer-service ticket in hospitality SaaS. Most operators have just learned to live with it.

What mDOS did differently

The Self-Service engine treats every modification as a transaction with a deterministic idempotency key. The key is derived from the guest's intent: the booking being modified, the specific change being made, the financial delta being applied. If the same key arrives twice within a short window, the system recognises it as a retry and returns the original response rather than processing the modification again.

This sounds straightforward. It's not. The hard parts are:

Key derivation that survives partial information. The guest's first attempt may have partial form state. Their retry may have slightly different form state if they re-typed something. The key has to be specific enough to identify the intent uniquely, generic enough to match the retry, and unfakeable enough that a malicious user can't replay a successful payment.

Vaulted-card off-session charges. The first time a guest pays during a stay, the card is vaulted via Stripe's payment-method storage. Subsequent modifications during the same stay use the vaulted card to charge off-session: no card re-entry, no friction, no abandoned-modification rate. But off-session charges can trigger 3DS challenges that require the guest to confirm via their banking app, which means the system has to gracefully fall back to an on-session flow if the bank requires it.

Integer-pence accounting. Money is stored and settled in integer pence (or the equivalent base unit for non-GBP currencies). Balances, charges, and refunds never pass through floating-point. Where a single total must be apportioned across parts (allocating a basket-level discount across lines, or splitting a deposit), the proportional share is computed and rounded to whole pence at the point of allocation. Keeping the ledger in integer pence eliminates an entire class of rounding bugs that hospitality SaaS routinely ships (and quietly patches with reconciliation jobs).

Partial refund + upsell combination flows. A common scenario: a guest reduces their party size from 8 to 6, which means a partial refund of two deposits, but they also upgrade their menu choice, which is an additional charge. The system needs to net these correctly, apply the refund and the charge in a deterministic order, and surface the final delta to the guest with the correct breakdown. mDOS handles this as a single composite transaction; legacy systems handle it as two sequential transactions and hope nothing fails between them.

Why this matters commercially

The Self-Service engine took roughly six engineer-weeks to ship to production-grade. That's an enormous investment for what looks like a simple feature.

The commercial reasoning behind that investment was specific: enterprise hospitality acquirers and operators perform what's called payment-systems due diligence during procurement evaluation. The CTO will ask, "What happens when a guest's connection drops during a 3DS challenge?" If the answer is "we have monitoring" or "it's rare" or "we have a reconciliation job that runs nightly," the platform fails the due-diligence check.

If the answer is "the architecture prevents it: here's the idempotency-key derivation logic and the Playwright tests that prove it," the platform passes.

mDOS now passes. That single sentence is what unlocks a class of enterprise customer that wouldn't have considered the platform six months ago.

What it means for operators

For a multi-site operator running 200 venues, the Self-Service engine eliminates an estimated 3–7% of all payment-related customer-service tickets. The exact figure varies by venue type and guest demographic, but the pattern is consistent: every operator we've benchmarked against the legacy stack reports double-charges as a routine occurrence.

Eliminating that 3–7% has compounding effects. Customer-service hours drop. Chargeback rates drop. Refund-related accounting reconciliation work drops. Customer trust improves measurably, particularly among high-spend guests who are more likely to use self-service modification features. The operational ROI on the Self-Service engine across an estate at scale is meaningful enough to be a budget line in its own right.

For an acquirer evaluating mDOS as IP, the Self-Service engine signals something different: it signals that the engineering team chose to solve hard problems properly rather than ship faster with technical debt. This is what acquirers' technical due diligence is actually looking for: not feature counts, but evidence of architectural maturity in the unglamorous parts of the system.

The architectural pattern in one paragraph

Every modification originates as a guest intent. The intent is hashed into a deterministic idempotency key. The key is checked against a recent-requests table. If the key matches a recent successful request, the system returns the original response. If the key doesn't match, the system enters a transaction that handles the financial delta (charge, refund, or composite), the booking update, and the trigger fan-out as a single atomic operation. If any step fails, the entire transaction rolls back. If the guest retries the same intent within the idempotency window, they get the original successful response, even if their connection dropped at the worst possible moment.

This pattern is well-documented in payment-engineering literature. Stripe themselves recommend it. It's also almost universally absent from hospitality SaaS, because shipping it properly requires the kind of cross-system transactional discipline that fragmented architectures fundamentally can't provide.

mDOS can provide it because every step of the modification flow lives in the same Postgres database. The financial delta calculation, the booking update, the loyalty adjustment, the trigger fan-out: all of them are in the same transaction. The atomicity isn't a feature; it's a property of the unified architecture.

What to ask your current platform

If you're running a multi-site hospitality operation, three questions test whether your stack handles this class of problem.

How does your current platform handle a guest who taps "modify" twice during a 3DS challenge with intermittent connectivity? If the honest answer involves monitoring, manual reconciliation, or customer-service intervention, you're absorbing operational cost that the architecture should be eliminating.

What proportion of your monthly customer-service tickets relate to double-charges, missed refunds, or modification-related billing discrepancies? Most operators haven't measured this because they've accepted it as background noise.

What does your platform do when a vaulted off-session charge triggers an unexpected 3DS challenge? If the answer is "the charge fails and we have to ask the guest to come back through the regular checkout," you have a meaningful conversion-rate problem on every modification flow.

These questions aren't about features. They're about whether your platform's payment engineering is mature enough for enterprise scale.


The Self-Service engine shipped to production in May 2026. Technical documentation available under mutual NDA for in-flight evaluations.

See mDOS with your own business

A 30-minute call with one of our experts, built around what you want to fix first.

Book a demo

About the author

Ross Crawford
Founder, Mr Digital

Ross Crawford is the founder of Mr Digital and mDOS, and a Fellow of the Chartered Institute of Marketing (FCIM). He has worked in hospitality marketing since 2008, when he started at Fuller’s.

  • Fellow of the Chartered Institute of Marketing (FCIM)
Written by the people named above. Facts and figures are checked against their sources before publishing. Our editorial standards

Keep reading