Case study
- Retail
- POS
- Java
ICA checkout
Checkout and self-checkout behaviour on a Java point-of-sale stack running in ICA grocery stores. The lane is the same software whether a cashier drives it or the shopper does — the same basket, the same promotions, the same path to payment.
Work delivered inside a Nordic retail-software house.
- Java
- Spring
- REST
- Payments

- Role
- Checkout and self-checkout engineering
- Built with
- Java · Spring · REST · Payments
The challenge
Staffed tills, self-checkout and self-scan have to share one price list, one promotion engine and one payment path. A shopper who scans a multibuy at a self-checkout has to be charged what the staffed till two metres away would have charged, or the store has a queue at the customer service desk instead of a working lane. Age checks and weight checks fail closed: where the lane cannot be sure, it stops and asks for a person rather than guessing.
What we built
One basket, one set of rules
Basket, pricing and promotions come from the same engine whichever lane the shopper is standing in. A multibuy, a mix-and-match or a member price behaves identically at a staffed till and at a self-checkout, because it is the same code deciding.
Tender, and the paths after it
Card and the other tender types, over REST to the payment services. Partial payment, cancellation, and the reversal path for the case where a tender authorises and the transaction then cannot complete.
Age-restricted items stop the lane
An item that needs an age check holds the transaction until an attendant approves it. There is no timeout that quietly lets it through — failing closed is the requirement, not the fallback.
Weight checks fail closed too
The security scale has to agree with the basket before payment completes. A mismatch calls an attendant rather than accepting it and moving on.
Attendant override, on the record
Everything an attendant can approve is an explicit action against the transaction rather than a way around it, so the lane's own log says who allowed what.
A failure stays in its lane
A terminal, a scale or a printer going down takes out one lane and nothing else. The store keeps trading on the rest while that one is looked at.
Technical approach
Java services around the existing checkout engine rather than a second implementation for self-service — the moment self-checkout gets its own pricing path the two drift, and the shopper is the one who discovers it. REST out to the payment services and the store systems. Failures are contained at the edge, so a device fault is a lane out of service rather than a store out of service. Hardware-agnostic against the terminals already installed: lane hardware is not something a software change gets to replace.
The product

Got something like this to build?
Start a conversation