Skip to content
All work

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
A row of orange supermarket self-checkout terminals in a dim store, screens dark, an empty basket sitting on each bagging shelf.
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

A self-checkout operator window: the basket on the left with four grocery lines and a total in Swedish kronor, and on the right the payment panel — card terminal ready, a mobile payment QR option, an age check waiting for approval, and a weight check that has passed.
Reconstructed operator view — not the vendor console.