The customer paid for lunch, ate, left — an hour later a Stripe Connect webhook retry flipped the order to "failed" and prompted me to refund
Tomás (41), tapas bar owner in Lisbon's Alfama district, served a 12-top tourist group for lunch on Tuesday. €87 + tip via Apple Pay; 3DS step-up flashed "temporary error" then succeeded on retry. KDS marked paid, group ate, left. 48 minutes later Tomás's phone buzzed: "Order payment FAILED — refund may be required." Stripe Dashboard said `payment_intent: succeeded`, but thMenu admin showed `payment_status: failed`. Tomás trusted the louder signal, clicked refund — customer was long gone. Two weeks later, accountant's reconciliation showed €87 net loss: customer paid, ate, AND got refunded. Forensic: Stripe queued a 3DS-first-attempt `payment_failed` event because thMenu didn't acknowledge it during the retry window; 48 min later the retry delivered out-of-order, `flipPaymentStatus` blindly flipped an already-`paid` order to `failed`. **PR #646 batch VI F1** fix: `FLIP_GUARD` state-machine table (paid ← {pending, requires_action, processing, failed}; failed ← {pending, requires_action, processing} — paid FROZEN; refunded ← {paid}). Inline `AND ${guard}` on every UPDATE; `meta.changes === 0` → log + 200 OK (Stripe must stop retrying; error response triggers retry amplifying noise). Pattern: state machine MANDATORY on webhook sinks; every operator-visible status field needs forward-only state machine.