How a shop’s checkout software handles lost connections without losing money or making duplicate sales · Muhammad Faisal · Week ending October 23, 2026
Imagine a busy shop at closing time. A queue of customers is waiting, and the internet goes down. If the checkout software stops, the shop loses sales and customers lose patience. This week I worked on making the IQ-POS desktop app keep selling offline, then catch up safely once the connection returns.
This post explains how, in plain language. You do not need a technical background.
What IQ-POS is
IQ-POS is software for shopkeepers. It handles billing at the counter, stock, receipts, supplier records and customer credit. This week’s work focused on the Windows desktop version, which runs on the shop’s own computer.
The problem in one sentence
A sale made while offline must be stored safely, protected from prying eyes, and sent to the server exactly once, even if the connection drops again halfway.
The five challenges at a glance
| Challenge | Everyday comparison | What we built |
| Keep unsent sales safe | A locked drawer under the counter | Encrypted local storage, with a separate key for each shop |
| Record what was agreed | A sealed, signed receipt | An unchangeable sale record with prices, totals and payment terms |
| Send each sale only once | A numbered cheque | A unique key the server uses to spot repeats |
| Avoid two things at once | One cashier at the till | A worker that sends sales one at a time |
| Show what is happening | A tray labelled sent and waiting | A status panel with a Retry button |
1. A locked drawer for unsent sales
When the internet is down, sales have to wait somewhere on the shop computer. That storage must be private, because it holds customer and payment details.
What we did. Offline data is stored in a small local database and encrypted. Encryption scrambles information so that only someone with the right key can read it. A master key is created for the installation and protected by Windows itself. From it, each shop gets its own separate key.
Why it works. Think of one building with many shop units, each with its own lock. Even if someone copies a locked box from one unit, it will not open in another. A tampered copy also fails to open, because the encryption checks that the contents have not been changed.
A deliberate detail. Signing out clears the cached product and customer lists, but keeps any sales still waiting to be sent. A shopkeeper should never lose a sale just because they logged out.
2. A sealed record of each sale
When a sale happens offline, the app does not just note a total. It saves a complete, unchangeable record of the agreement: the shop, the customer, each product, the agreed prices, the stock it saw at that moment, the totals, how it was paid, and the local time it was completed. It also gets a receipt reference so the customer has something to hold.
What is allowed offline. Cash and customer credit work offline. Cash must cover the full total. Credit needs a valid due date and must leave a balance still owed. Card payments are refused with a clear message that a connection is required.
Why it works. A sealed receipt cannot be quietly edited later. If anyone questions the sale, the record shows exactly what was agreed at the counter.
3. Send each sale exactly once
This is the part that surprises most people. Sending a sale is risky in a particular way. Suppose the app sends it, the server records it, but the reply is lost on the way back. The app thinks it failed and tries again. Without protection, the shop now has the same sale twice.
What we did. Every sale carries a unique key, a bit like a cheque number. The server remembers the keys it has seen for each retailer. If the same sale arrives again, the server recognizes the key and returns the original result instead of creating a second sale.
A safe, separate door. The server received a new dedicated entrance for these sales. It checks the money adds up and saves everything in one all-or-nothing step, which databases call a transaction. The existing web checkout was left untouched, so nothing already working could break.
4. One at a time
A computer can try to do many things at once, and that can cause clashes. The sending worker claims one waiting sale at a time and records the result. If the app is closed halfway through a send, the next start moves that sale back to waiting. Thanks to the unique key, trying again is safe.
5. Show what is happening
A shopkeeper needs to see the state of things without reading logs. The sidebar shows whether the computer is online, how many sales are waiting, and which receipts have a conflict. Sending starts when the app opens online, again when the connection returns, and on demand with a button.
If a sale cannot be applied, it is marked as a conflict instead of being silently dropped. The only action offered is Retry, which keeps the process simple and safe. The screen never sees the scrambled data or the keys, only the receipt number and its state.
The cash drawer
Many shops use a USB cash drawer. The desktop app can list the connected ports, remember the chosen one, run a test, and open the drawer after a successful cash payment. It opens whenever the cash part of a payment is above zero, including mixed payments, but never for card-only or credit-only sales. The screen cannot send arbitrary commands to the device, only the approved action.
The same idea for online orders
The customer side of the app faced a similar problem. When a customer places a cash-on-delivery order and the connection times out, the order may already exist. Placing it again with a fresh key could create a duplicate.
So the app keeps the same key for every retry and locks the delivery details while the outcome is uncertain. If the server already has the order, it replies with that same order. The saved attempt is kept only for that customer, in temporary storage, and removed once the order is finished.
A related rule applies to reading data: simple look-ups are retried up to twice after a network problem, but anything that moves money is never retried automatically.
What is not yet proven
Good engineering means saying what has not been tested. The checkout work is marked conditionally complete. Real tests with live accounts are still outstanding: a completed sale, a stock conflict, a duplicate completion attempt, and two different shops trying to reach each other’s data. They are listed as release blockers, and moving on does not remove them.
There were also build problems. One runtime download was only partly unpacked, and the installer failed on a computer with very little free disk space. Both were diagnosed and have clear fixes.
What I learned
- A timeout is not a failure. Retry with the same key, never a new one.
- The server stays the final authority on money. The app reloads the bill after every change.
- Give each shop its own lock, so one leak cannot expose everyone.
- Record what was agreed at the moment of sale, in a form that cannot be edited.
- Show people the state of the system, and offer only safe actions.
- Keep a list of what is not yet verified, and do not delete items from it just because work moved on.
A short glossary
| Term | Plain meaning |
| Offline-first | Software that keeps working when the internet is unavailable |
| Encryption | Scrambling information so only someone with the key can read it |
| Idempotency key | A unique number that lets a server recognize a repeated request |
| Transaction | A group of saves that either all happen or none do |
| Conflict | A sale the server could not apply as sent, needing attention |
| Release blocker | A test that must pass before the software is released |
What comes next
Next week I plan to run the live tests with two approved shop accounts, free the disk space needed to build the Windows installer, test the offline sales on the packaged app, and finish the customer screens for order tracking and profile.