EEltaCrew
Home › Blog

Why our lottery ticket QR scanner showed zero successes: a path you don't log looks like a failure

Published 2026-10-22Measured 2026-10-22About 5 min readelta · EltaCrew

In our lottery app's weekly report, 19 people had opened the QR screen and GA4 showed zero successful scans. The scanner wasn't broken. The path that real ticket QR codes took had no logging at all. How we found the measurement gap, what four similar apps do, why we switched to the bundled scanning engine, and the numbers one week after the fix.

19 people, zero successes

Our Korean lottery results app has a screen where you scan the QR code on a ticket to see whether it won. In an early-October weekly report, 19 people had opened that screen and GA4 recorded zero successful scans.

If none of 19 people could read a ticket, the scanner would be broken. But when we scanned tickets ourselves, it read them fine. The numbers didn't match what we saw in our hands, so we suspected the logging code before the scanner.

Real ticket QR codes left the app without a trace

Rereading the code that runs after a QR code is read, we found two paths:

Every real lottery ticket QR code is a URL. The LOTTO;ROUND= format was a sample we made early in development; no ticket carries it. So everyone who scanned a real ticket went down the first path to the browser, and nobody ever went down the second path, the only one that logged anything. "Zero successes" wasn't a failure. It was a gap in measurement.

What a ticket QR code looks like

Before changing anything, we checked the format on real tickets.

http://qr.dhlottery.co.kr/?v=1245q061415182834q…n000000000000…

Older tickets carry m.dhlottery.co.kr/?v=. Opening one today returns a 307 to qr.dhlottery.co.kr and the result page loads normally (checked October 15).

Logging first

To find where people drop off, we added an event to every branch before changing any behavior.

Branch Event
Lottery result URL success + URL shape
Pension lottery URL success + URL shape
Some other URL failure + domain
Not a lottery QR failure
Camera permission denied failure
Left without reading anything failure (no_read)

The URL shape replaces every digit with 9. Ticket numbers belong to the person who bought the ticket, so we don't send them to analytics; we only keep enough to tell whether the format matches.

lotto_url:qr.dhlottery.co.kr/?v=9999q999999999999q999999999999…

What four similar apps do

We installed four other apps that check lottery results by QR code, opened their APKs, and read the QR handling.

The catch with the unbundled scanning engine

ML Kit's barcode scanner comes in two versions. The bundled version ships the recognition model inside the APK; the unbundled version has Google Play services download the model separately.

Rereading the official documentation: the unbundled version downloads the model ahead of time only when the app is installed from the Play Store. Installed any other way, it starts downloading on the first scan, and requests made before the model arrives return no result. To the user, that's a camera pointed at a QR code with nothing happening.

We also publish this app in stores other than Google Play, so we switched to the bundled version. The APK grows by about 2.4 MB, but the first scan works no matter where the app came from. We also checked that the native library in the bundled version is 16 KB page aligned.

Other changes in the same release (1.0.76, October 15):

One week after the fix

QR screen events from 1.0.76 through October 22:

Event Users Count
Opened QR scanning 33 83
Success (lottery URL) 5 16
Left without reading 21 41
Camera permission denied 3 9
Other URL 1 1

Successes show up for the first time. Every successful URL shape in 1.0.76 was the older address (m.dhlottery.co.kr), so older tickets are being read too.

What we still don't know

Takeaways

The app in this post is our Korean lottery results and lucky store app (Google Play).

AndroidGA4ML KitQR codesMeasurement

Related

Tapping the logo in the map's corner crashed the app: Naver Map SDK and the AppCompat themeFour "You need to use a Theme.AppCompat theme" crashes showed up in Crashlytics for our lottery store map app. They came not from users but from the Play pre-launch report's test robot tapping the NAVER logo in the bottom-left corner of the map. The cause, a fix that wraps only the map instead of changing the whole app theme, and a second problem that appeared in dark mode.Requesting ads before the SDK finishes initializing: why the same crash came back three times after we added a guardAfter we first fixed the "MobileAds.initialize must be called" crash in our live apps, the same crash came back three more times in different shapes. We had a guard that waited for initialization to finish, but a flag set at the start, a flag set on consent alone, and new ad slots that skipped the guard all left gaps. The cause of each case and the results of checking all 23 apps.A crash from one line of image downsampling: inSampleSize starts at 0To save memory, we changed the document thumbnails in our QR scanner app to load downsampled. In the next version, the app closed every time the document list opened. The cause was that BitmapFactory.Options.inSampleSize defaults to 0. The code we changed, the crash data, the fix, and how we checked for the same mistake elsewhere.