← All posts Published: March 15, 2026
The Kunming $60,000 QR code incident — and what it taught us
In November 2023, a hotpot diner in Kunming, China accidentally exposed her table's ordering QR code in a social-media photo. Strangers used it to submit ~$60,000 in phantom orders against her table. Here's what happened, why it happened, and how MobiTaste's default settings make it structurally impossible.
A diner in Kunming, China sat down for hotpot in November 2023 and did
something millions of people do every day: she posted a photo of her food
to WeChat Moments. Two things she didn’t notice were in the frame. First,
the table’s ordering QR code. Second, her phone wasn’t on the most
restrictive privacy setting.
Within hours, contacts had downloaded the photo, scanned the QR, and used
the menu to submit orders against her table. By the time the staff caught
on, the bill stood at roughly ¥430,000 — about $60,000 USD:
- 9,990 portions of shrimp paste
- 2,580 portions of squid
- 1,850 portions of fresh duck blood
The restaurant — to their credit — moved her party to a different table,
voided the order, and did not charge her. They then quietly added a
“distance limit” to their ordering software so the same trick wouldn’t
work again.
This story is now lightly famous in QR-menu industry circles. It’s a
useful one because it makes a structural problem concrete: a printed
QR code on a table is, by design, a permanent ordering credential.
Anyone with the photo can order from anywhere, at any time. If your
software trusts the QR alone, you trust everyone who’s ever seen the
table.
What every QR-menu vendor gets wrong (and right)
We surveyed the major players for our comparisons before
building MobiTaste. There are five common defences in the industry:
- Staff approval before kitchen — Adisyo, Menulux, QRKafe,
Restomenum and others default to this. Orders enter an approval
queue; a waiter taps Confirm before the kitchen sees the ticket.
This is what we picked too — it’s the cheapest, most universal
mitigation.
- Pay-on-order with card capture — Square for Restaurants, Lightspeed,
most US SMB tools. If you have to put a card down to submit, prank
orders are self-defeating.
- SMS-OTP + pre-authorised tab — me&u and GoTab. Phone verification +
a $0.01 pre-auth at first order, auto-captured at 3-hour idle.
- Pay-only platforms — Sunday, Qlub, Rooam Pay. They sidestep the
problem by not letting guests place orders at all; you only pay
a check that staff opened in the POS.
- Physical-presence binding — NFC tags, geofencing, dynamic QR. Real
adoption is limited because the trade-offs are steep.
The Kunming restaurant added option 5 (geofencing) reactively. We built
options 1, 4-style fail-safes, and per-session caps from the start.
How MobiTaste’s defaults remove this attack
For a fresh signup, the defaults are:
- Waiter approves the first order in every session. The first order
enters
PENDING_APPROVAL. Your waiter sees a card on their tablet
with the table, items, and total. One tap to approve and it hits
the kitchen, one tap to reject (with a reason) and it bounces.
After the first approval, subsequent orders at the same session
flow straight through.
- Hard limits per session. Even if a Kunming-style attempt slipped
past approval, the request would 429 long before it got close:
- max 50 items per single order
- max 200 items per session
- max 6 orders per session per hour
- max session value (defaults to 5,000 in the venue’s currency)
9,990 portions of shrimp paste 429 on row one.
- Session-bound + idle-expiring table sessions. Each table has a
TableSession object. New scans after the 90-minute idle window
open a brand-new session with zero approved orders — so even if
the same QR is re-scanned from another country, you’re back at
step zero (waiter approval required).
- Audit log on every order action. Place, approve, reject, status
change — all recorded with who did it. If you ever need to
reconstruct what happened, you can.
Concretely: take the Kunming attack as a script. From a different country,
re-scan the printed QR, place a 9,990-portion shrimp paste order.
POST /api/v1/guest/menu/{slug}/t/{token}/orders
→ HTTP 429 Too Many Requests
{ "error": "max_items_per_order", "limit": 50, ... }
If they trim it down to a small first order to slip past the cap, the
order enters PENDING_APPROVAL and waits for your waiter. Your waiter,
who is at the actual table and can see who is and isn’t sitting there,
taps Reject. The order is cancelled with a written reason that lands in
your audit log.
What we don’t do (yet)
We’re honest about limits. MobiTaste doesn’t currently:
- Verify phone numbers on first order (me&u’s approach — strongest
fraud defence but a UX tax).
- Take a card on file before letting orders through (the US-style
pre-auth — feasible only where guests carry credit cards).
- Enforce geofencing (TapTasty does this — false negatives in malls
and big hotels are a real problem we don’t want to ship around).
When the next Kunming-style incident becomes the standard threat for our
customers, we’ll have those layers ready. For 2026, server-side
approval + hard caps + session boundaries are the sweet spot for the
$10/mo segment — strong defence without the UX tax.
Try it
If you’d like to see the security defaults from the owner side, you can
start your 14-day trial and look at
the Settings page. The defaults are what every account starts with;
you can dial them up or down for your venue.