HeadlinesBriefing favicon HeadlinesBriefing.com

QR Codes That Route to the Appropriate App Store

Hacker News •
×

When I announced Poker Nexus earlier this week, I mentioned in passing that it deploys "a separate shortlink host for printed links." This post covers what that host does and why it's built the way it is. The goal was simple. I wanted business cards with a QR code on the back that says "Download." The trouble is that a QR code encodes exactly one URL, while "download the app" means three different places depending on who scans it: an iPhone should land on the App Store listing, an Android phone on the Google Play listing, and everything else (a laptop, a crawler, a device I can't identify) should land on the website. Printed cards also can't be updated once they're in someone's wallet, so the URL on them needs to keep working even if the destinations change later. Both problems point to the same answer: print a URL I control, and let the server decide where each request goes. The cards point at `https://go.pokernexus.com/app`.

I made the QR code itself with my QR Code Generator. It runs entirely in the browser and exports a plain SVG or PNG, which is what a print shop wants. A short URL produces a less dense code, and a less dense code has larger modules that scan more reliably at business card size. `go.pokernexus.com/app` is short enough to stay at a low QR version. Putting a logo in the middle covers some of the modules, so the code has to lean on error correction to make up for them. The generator bumps the level to H (roughly 30% recoverable) as soon as a center icon is added, which is what let the Poker Nexus club sit in the middle without breaking scans.

The host is served by a small standalone Hono app. Every place it can send someone is described as a `Destination`: a URL, plus a flag that says whether the incoming query string should be copied onto it. The routing table maps each path to a `Target`, which is either a fixed `Destination` or a function that picks one based on the request. The decision is made on the `User-Agent` header alone. Every branch that isn't a confident match falls through to the website. That asymmetry is intentional. A wrong answer that lands on the website is still a working page, while a wrong answer that lands in a store is an install prompt for a device that can't run the app.

The order of those checks is easy to overlook. Google's smartphone crawlers identify as phones and append their own name, so the Android crawler's user agent contains `Android` and the iOS one contains `iPhone`. Bots are checked first to ensure they land on the website, not the store.