Skip to content
FeaturesPricingAffiliateBlogHelpAboutContact
Get StartedSign In
Blog

Insights & updates

Expert articles on restaurant digitalization, customer experience, and operational efficiency.

222 articles

🗺️
industry8 min read

Opening a Restaurant in a Tourist-Heavy Area: The Role of Digital Menus

Multilingual requirements, currency display, tourist QR habits, and Google Maps integration — a strategic view for restaurants in tourist destinations.

Read More
👨‍👩‍👧tips
2026-06-127 min read

Menu Strategy for Families with Children

Kids' menu design, allergen sensitivity, portion sizing, and parental trust — what 'family-friendly' actually means in practice, with concrete tactics.

Read More
guides
2026-06-118 min read

How to Design an Accessible Digital Menu for Customers with Disabilities

WCAG standards, screen reader compatibility, colorblind-friendly palettes, and large-text options — what an accessible digital menu actually looks like, with concrete examples.

Read More
⏱️industry
2026-06-108 min read

How Wait Times Affect Customer Satisfaction: A Data Analysis

Order taking, food prep, and check waiting each impact satisfaction differently. A data-driven look at where digital tools save measurable minutes and recover lost revenue.

Read More
🔬tips
2026-06-097 min read

Competitor Analysis: How to Audit Rival Restaurant Digital Menus

What to look for, price benchmarking, missing allergen info, photo quality, and UX gaps — a practical playbook for rival restaurant menu analysis.

Read More
📅guides
2026-06-088 min read

Content Calendar for Your Restaurant: 12-Month Blog and Social Media Plan

Monthly themes, seasonal content, holiday campaigns, and menu announcement timing — the practical 12-month restaurant content calendar.

Read More
📲tips
2026-06-077 min read

Influencer Restaurant Marketing: How QR Menu Becomes a Weapon

Turn influencer visits into ongoing UGC — how digital menu design, sharable QR links, and a content-ready experience compound on every drop-in.

Read More
✉️guides
2026-06-067 min read

Email Marketing for Restaurants: How to Announce Menu Updates

List building, seasonal menu announcements, personalized campaigns, and open-rate tactics — the playbook for email marketing that actually drives bookings.

Read More
📍guides
2026-06-057 min read

How to Enhance Your Google Business Profile with Your Digital Menu

Menu link, dish photos, update cadence, and Q&A optimization — the complete guide to making Google Business Profile work with your digital menu.

Read More
🔍guides
2026-06-048 min read

Restaurant SEO 101: Basic Steps to Rank Higher on Google

Google Business Profile, local SEO, menu content indexing, and schema.org RestaurantMenu markup — the foundation steps for ranking restaurants on Google.

Read More
📸tips
2026-06-037 min read

Menu Visuals for Social Media: Designing for Instagram

Photo composition, color harmony, customer share motivation, and UGC value — the practical playbook for Instagram-worthy menu and dish design.

Read More
📖tips
2026-06-027 min read

Storytelling in Menus: Give Your Dishes a Personality

Use origin, supplier names, family recipes, and regional roots to turn menu items into stories — driving emotional connection and higher price tolerance.

Read More
industry
2026-06-016 min read

Does "Chefs Recommendation" Badge Really Increase Sales?

Social proof mechanism, which dishes deserve it, how overuse neutralizes the effect. The practical use of a chef recommendation badge.

Read More
🎨industry
2026-05-316 min read

Which Color Gets More Orders? Menu Color Psychology

Do red and yellow increase appetite? Green and health perception. Brand alignment. Cultural color differences.

Read More
✍️tips
2026-05-306 min read

How Menu Descriptions Affect Sales: Appetite-Inducing Copywriting Techniques

Sensory words, provenance, technique highlighting. The rules of menu description writing that actually shift conversion.

Read More
💰industry
2026-05-297 min read

Price Psychology in Menus: Why Customers Choose the More Expensive Option

Anchor pricing, charm endings (.99), currency-symbol effect, embedding prices in copy. The menu pricing psychology that actually works.

Read More
📊guides
2026-05-287 min read

What Is Menu Engineering? The Science of Selling More

Stars, cash cows, puzzles, dogs. Price anchoring, visual hierarchy, profitability analysis. A working guide to menu engineering.

Read More
📅tips
2026-05-276 min read

How Often Should You Update Your Restaurant Menu?

Seasonal rotation, inflation pressure, dropping low performers, trend tracking. A practical cadence for restaurant menu updates.

Read More
🍽️guides
2026-05-266 min read

Digital Menus for Fine Dining Restaurants: Without Compromising Luxury

Aesthetic design, minimalist UI, storytelling, sommelier notes. What a luxury restaurant digital menu should do — and should never do.

Read More
🔐industry
2026-05-2512 min read

My employee was under a password brute-force attack — pw_failures + staff lockout (PR #548 PP H4)

Tom Newcastle Quayside 39-yo 14-yr Newcastle Brown Ale House 55-cover traditional British pub Sunday roast + Newcastle Brown Ale tap + house-cured pork pies thMenu Pro 16 months. Monday 09:15 admin panel amber security banner 6200 failed password attempts past 24 hours review team staff of 6 not capable 6200 attempts. Audit log every attempt same staff email tom@brownalehouse.uk 60 distinct /24 subnets botnet brute-force single target Tom admin email distributed IP pool bypassing IP-based rate limit. Tom password strong 16 chars password manager attacker failed 6200 attempts continuing eventual success or rate-limit bypass. Support email 6200 attempts 60 IPs brute-force attack does thMenu have per-account lockout. Engineering reproduce 6200×60/24h ~103/IP ~4/hour under Worker IP-rate-limit 10/5min never triggered botnet. 3 wrong theories (1) tighten IP-rate-limit 10→5/5min legitimate users pain 5 wrong attempts locked tight attacker scales botnet 60→100 2-3 attempts/5min under threshold anti-pattern; (2) CAPTCHA hurts legitimate UX every login CAPTCHA-solving services $1/1000 solves doesn't stop determined; (3) IP-allowlist multi-location senseless Tom phone + 2 location tablets + home WiFi maintenance overhead. Correct pattern per-account lockout PIN already shipped PR #336 + D1_OPS 0062 pin_failures same pattern password but table didn't exist lockout not shipped. Forensic apps/web-admin/src/app/api/staff/route.ts PIN flow after PR #336 (1) email + restaurant_id pull pin_failures D1 query SELECT window_start > now-30min; (2) attempts >= 10 429 account_locked_30min IP rotation doesn't help (restaurant_id, email_lc) tuple key; (3) successful PIN verify delete pin_failures atomic UPSERT attempts+=1 or 1 if window>30min. Same flow password endpoint WASN'T THERE only Cloudflare Worker IP-rate-limit pw_failures table didn't exist. 90-day audit deepened 23 restaurants same pattern 30-50 IPs /24 subnets 100-200 attempts per IP Tom upper end 60 IPs 103/IP. PR #548 batch PP H4 3-layer fix Layer 1 D1_OPS migration 0076 pw_failures table schema pin_failures mirror restaurant_id + email_lc + window_start + attempts + last_attempt PK + idx_pw_failures_window remote applied. Layer 2 password lockout helper apps/web-admin/src/app/api/staff/route.ts PIN flow mirror (1) SELECT attempts; (2) >= 10 429 account_locked_30min i18n locale-aware; (3) atomic UPSERT 30-min sliding window; (4) successful password verify DELETE. Layer 3 CLAUDE.md doc + sibling sweep PIN lockout PR #336 + PW lockout PR #548 PP H4 symmetric future staff auth surface forced apply pattern docs + PR template checkbox. Production audit 23 affected restaurants personal outreach pw_failures lockout active password manager 1-3 attempts lockout doesn't affect. Post-deploy 7-day brute-force attempts dropped 93% botnet operators abandoned lockout resets aggression 0 successful breaches. Tom Hall of Fame + 3-month Pro tier credit Twitter 2.3k Newcastle small restaurants protection. Ali Kutahya Merkez Ataturk Bulvari Kutahya Lokumu + Cini Kafe 35-cover Turkish-delight + Turkish-coffee + tile-craft 13-yr 5847 attempts 47 IPs aggressive parallel disclosure same fix + 3-month Pro credit + Hall of Fame. Pattern authentication endpoints PIN password magic-link OAuth must enforce per-account lockout ALONGSIDE IP rate-limit botnet IP rotation bypasses IP rate-limit per-account lockout can't bypassed keyed off single target email. Sibling sweep PIN PR #336 + PW PR #548 PP H4 + magic-link PR #335 + customer_email_links TTL + send-magic-link PR #626 GGG F5 Gmail-alias normalize + OAuth-PKCE PR #526 HH atomic rotation + affiliate OTP PR #646 VI F3 atomic TOCTOU. Implementation auth endpoint identify + D1 _failures table create + atomic UPSERT sliding 30-min window + successful auth DELETE + lockout threshold PIN 10 password 10 magic-link 5 sends/15min + i18n locale-aware message + PR template checkbox + quarterly auth-surface audit + brute-force pattern detect 5000+/24h alarm. PR #548 reference.

Read More
🔗industry
2026-05-2512 min read

Google review URL field missing safeHref javascript scheme stored XSS — review_url (PR #548 PP C1)

Cameron Edinburgh Stockbridge 38-yo 9-yr independent web-application security consulting 4-yr NCC Group + 5-yr solo UK + Nordic SaaS audits. Q1 2026 reading thMenu open-source repo auditing URL-field safeHref coverage parity PR #334 shipped safeHref restaurants.{wifi_url, logo_url, cover_url} + social_links.url grep additional URL fields Pro-tier google_reviews_config widget table review_url Leave us a Google Review CTA customer menu PR #334 safeHref sweep missed this table. Lab repro test thMenu Pro account admin panel Google Reviews review_url javascript:alert('xss') Save 200 OK no scheme validation. Test restaurant menu URL Leave us a Google review button click alert pop-up browser parsed javascript URL executed against restaurant menu origin stored XSS vector customer cookies + session + geolocation attacker-controllable menu.thmenu.com CSP unsafe-inline pre-PR #347 inheritance execution not blocked. Attacker compromise admin account javascript:fetch evil.example cookie harvest. data:text/html<script>alert(1)</script> also accepted data: URLs not in safeHref allowlist should be rejected. Cameron writeup security@thmenu.com google_reviews_config.review_url PUT missing safeHref javascript: data: file: schemes accepted CVSS 8.4 HIGH operator-side stored XSS customer-side cookie + session theft + cross-restaurant pivot PR #334 sweep missed sibling coverage gap. Threat model (a) compromised admin javascript payload customer XSS; (b) malicious sub-operator Pro tier staff menu edit access same vector; (c) social engineering Hey paste this code into review URL boost Google ranking operator pastes javascript:fetch. Engineering 30 minutes reproduce 3 wrong theories (1) sanitize rendering DOMPurify review_url URL field not HTML DOMPurify HTML context no URL scheme validation javascript:alert passes through; (2) tighten CSP menu.thmenu.com unsafe-inline inline styles + scripts customer menu UX tier-1 SaaS javascript scheme not blocked CSP source allowlist execute from direct user click wrong angle; (3) frontend regex filter client-side bypassable curl/Postman PUT endpoint direct server-side validation required. Correct pattern safeHref helper http(s) + mailto + tel scheme allowlist + 500-char max length PR #334 helper applied. Forensic apps/web-admin/src/app/api/google-reviews-config/route.ts PUT handler review_url written DB as-is menu page render <a href={config.review_url}>{button_label}</a> href attribute injected without scheme validation. apps/web-admin/src/lib/sanitize.ts:safeHref PR #334 origin http(s)/mailto/tel allowlist javascript:/data:/file:/blob:/vbscript: rejected PR #334 shipped restaurants + social_links google_reviews_config missed. brands.logo_url same coverage gap PR #661 batch XI F3 blog #1136 same sibling-surface-sweep miss pattern. PR #548 batch PP C1 3-layer fix Layer 1 safeHref validation google_reviews_config PUT safeHref(review_url, maxLen 500) 422 invalid_review_url javascript: data: file: rejected clear error message. Layer 2 URL field sibling-surface sweep grep monorepo PUT endpoints (a) restaurants.{wifi_url,logo_url,cover_url} PR #334; (b) social_links.url PR #334; (c) google_reviews_config.review_url PP C1; (d) brands.logo_url XI F3; (e) affiliate_postback_url PR #609 CCC-B dual-secret rotation already safeHref; (f) custom_domains.cname_target internal-only; (g) menu.cover_image_url + products.image_url R2 prefix regex. Layer 3 CI grep guard .github/workflows/ci.yml grep -rn href={.*\.url pattern Did you use safeHref warning. Production audit DB scan SELECT review_url NOT LIKE https/http 3 restaurants malformed self-set by curious operators pasted bookmarklets malformed Google Maps deep links no real attacker payloads. 3 affected restaurants personal email + manual cleanup review_url null operator re-paste legitimate Google Reviews URL. 90-day Cloudflare access log + Sentry breadcrumb audit 0 third-party attacker reproductions operator-curiosity-set never exploited. Cameron €2000 Wise CVSS 8.4 + Hall of Fame + advisory board LinkedIn 5.1k UK web-application security disclosure response benchmark. Seyma Sanliurfa Eyyubiye 36-yo 7-yr ex-Garanti BBVA AppSec parallel disclosure €1800 Turkish security community blog 2.2k. Pattern every operator-controlled URL field rendered customer-side writable via PUT MUST route safeHref helper http(s) + mailto + tel allowlist + 500 char max + javascript:/data:/file:/blob:/vbscript: explicit reject. Sibling sweep canonical (a) safeHref helper application; (b) DB schema TEXT NOT NULL DEFAULT or nullable; (c) PUT validation + 422 invalid_url; (d) render-side defensive double-check pre-fix raw values; (e) CI grep guard. Implementation URL field migration / PR review safeHref-sweep checkbox + all PUT endpoints URL fields safeHref + frontend form defensive validation + render-side fallback null legacy + production existing-data audit + manual cleanup + CI grep guard + PR template checkbox. PR #548 reference.

Read More
💳industry
2026-05-2513 min read

Stripe payment-intent endpoint no auth session_id ownership bypass — PP C2 (PR #548)

Aoife Manchester Northern Quarter 37-yo 8-yr independent payment security research practice 5-yr Klarna platform security + 3-yr solo EU + UK payment processor integration audits @aoife-paysec weekly research notes Stripe/Adyen/Mollie/Worldpay integration patterns. Q1 2026 auditing thMenu customer-side payment flow POST /api/stripe/payment-intent endpoint from-table order customer Stripe Elements card details client POST create PaymentIntent body session_id + amount + currency zero authentication + no session_id ownership check + amount client-controllable. 3-prong threat (1) session ID enumeration + PI forgery URL leak screenshot social engineering attacker knows victim session_id creates PI victim Stripe Elements card charge attacker PI; (2) amount manipulation client body amount not validated orders.total_cents server-side this endpoint independent; (3) DoS amplification no auth rate-limit IP-level botnet distributed millions PI Stripe API quota burn thMenu account risk flag. Lab repro 2 test accounts A+B account A from-table order session_id grabbed network tab account B POST session_id A amount 1 currency gbp 200 OK client_secret no ownership check. Writeup CVSS 8.6 HIGH recommended fix session_id ownership verify via table_sessions.host_device_id + server-side amount derive from order_id + per-IP 10/min rate-limit PR #318 pattern. Engineering 45 minutes reproduce 3 wrong theories (1) Stripe Elements client-side validation server-side not needed wrong Stripe Elements card UI component doesn't guarantee PaymentIntent metadata integrity server-side mandatory; (2) orders.total amount already server-side computed correct incomplete /api/stripe/payment-intent separate endpoint independent body needs own amount validation; (3) session_id already secret-ish UUID v4 wrong customer-visible URL network tab QR-scan history not predictable but observable ownership check separate validation layer. Correct triad session_id ownership + server-side amount derive + per-IP rate-limit. Forensic apps/web-menu/src/app/api/stripe/payment-intent/route.ts no validation Platinum tier customer-side payment early-design auth/ownership TODO skipped production deploy table_sessions.host_device_id field PR M20 fix session-host-device verification table-session/join ownership check shipped payment-intent same pattern not applied. Production audit 90-day 28000 requests 0 exploit patterns hypothetically exploitable never realized. PR #548 batch PP C2 3-layer fix Layer 1 session_id ownership verification SELECT 1 FROM table_sessions WHERE id=? AND host_device_id=? AND expires_at>now AND restaurant_id=? host_device_id cookie table-session/join PR #335 magic-link pattern 403 session_ownership_mismatch. Layer 2 amount server-side derive body amount IGNORED SELECT total_cents currency FROM orders WHERE table_session_id=? AND status='pending' derived Stripe.create client manipulation impossible. Layer 3 per-IP rate-limit 10/min checkRateLimit endpointId stripe-pi-create limit 10 windowMs 60000 daily-salted IP hash + session_id composite key legitimate 1-3 PI per order bounds botnet PI-bomb. Bonus structured audit log console.log event pi_created session_id + amount + restaurant_id + host_device_id + caller_ip Logpush + Sentry SOC 2 evidence + suspicious-pattern detection. Post-deploy 30-day 24000 PI legitimate 0 session_ownership_mismatch Stripe Elements correct session_id 0 rate-limit hits stable. Aoife €2800 Wise CVSS 8.6 + Hall of Fame + advisory board LinkedIn 5.6k UK payment security community disclosure response benchmark Manchester represents. Cagri Bursa Mudanya 35-yo 8-yr ex-Garanti BBVA AppSec @cagri-paysec parallel disclosure €2400 Turkish payment security community blog 2.7k. Pattern payment-affecting endpoints (PaymentIntent create, refund, tip-adjust, dispute) ALWAYS 3-prong hardening (a) ownership verify; (b) server-side amount derive client body IGNORED; (c) per-IP rate-limit + structured audit log. Sibling sweep /api/stripe/payment-intent PP C2 + /api/orders/[id]/tip PR #326 tip cap + rate-limit + /api/orders/[id]/refund PR #328 cumulative + race-guard + /api/orders PR #11 H10 + idempotency-key + rate-limit + /api/loyalty/redeem PR #311 atomic + IP hash + /api/promo/apply PR #507 atomic + rate-limit. Implementation payment endpoint identify + ownership verify + amount derive + per-IP rate-limit 10/min payment default + structured audit log + Stripe idempotency-key PR #661 XI F2 + PR template checkbox + quarterly grep audit. PR #548 reference.

Read More
🔑industry
2026-05-2513 min read

I double-redeemed an OAuth authorization_code — atomic consume + family-revoke (PR #548 PP H1)

Stefan Berlin Friedrichshain 36-yo 8-yr independent SaaS integration development practice 5-yr Zalando API team OAuth 2.0 + OIDC + PKCE + 3-yr solo European SaaS audit @stefan-oauth German-language OAuth/OIDC research blog. Q1 2026 thMenu Phase 2 OAuth pre-PR #526 HH refresh-token rotation authorization_code grant flow focus RFC 6749 §4.1.2 + OAuth 2.0 Security BCP §4.10 atomic consume code redeemed once repeat-redeem authorization grant family tokens revoke. POST /api/oauth/token authorization_code grant handler non-atomic consume + no family-revoke. Lab repro test OAuth client client_id + client_secret + PKCE code_verifier authorize endpoint authorization_code /token POST grant_type=authorization_code + code + code_verifier 200 OK access_token + refresh_token first redeem success then same code re-POST 200 OK different access_token + different refresh_token same code double-redeemed. MITM attacker authorization_code intercept browser history + referer leak + extension scope abuse races legitimate client to /token both sides access_tokens Pro tier admin operations victim scope. Family-revoke missing RFC 6749 BCP §4.14 refresh-token rotation family-revoke shipped PR #526 HH F1 authorization_code side not present both refresh_tokens remain parallel. Stefan writeup CVSS 8.1 HIGH atomic UPDATE oauth_authorization_codes SET redeemed_at=now WHERE code=? AND redeemed_at IS NULL + meta.changes=0 detection + family-revoke oauth_refresh_tokens WHERE auth_grant_family_id=? sibling PR #526 HH F1 refresh-token rotation pattern. Engineering 1 hour reproduce 3 wrong theories (1) PKCE code_verifier prevents double-redeem PKCE intercept defense not double-redeem prevention; (2) 60-second code expiry enough race-to-redeem 60sec window atomic consume required; (3) DELETE-then-INSERT not atomic but fast enough race conditions D1 transaction commits atomic UPDATE inline filter required CLAUDE.md §17. Correct fix atomic UPDATE + meta.changes=0 400 invalid_grant + family-revoke cascade. Forensic apps/web-admin/src/app/api/oauth/token/route.ts SELECT-then-UPDATE anti-pattern two parallel /token requests same code SELECT redeemed_at IS NULL both decide valid both issue tokens UPDATE last-writer-wins two access_tokens + two refresh_tokens. oauth_authorization_codes auth_grant_family_id lacked family-revoke not possible RFC 6749 BCP §4.10 violation. PR #548 batch PP H1 3-layer fix Layer 1 atomic UPDATE inline filter UPDATE oauth_authorization_codes SET redeemed_at=? WHERE code=? AND redeemed_at IS NULL meta.changes=0 already redeemed potential attack. Layer 2 family-revoke cascade auth_grant_family_id column added code issued random UUID /token refresh tokens issued link auth_grant_family_id familyRevoke async SELECT family_id + UPDATE oauth_refresh_tokens SET revoked_at WHERE auth_grant_family_id console.warn [BEACON:oauth_family_revoke]. Layer 3 Sentry beacon + audit log Logpush + SOC 2 + intrusion detection signal. Production audit 90-day /api/oauth/token grant_type=authorization_code 3200 requests 0 double-redeem patterns Phase 2 OAuth narrow beta 12 integration partners no attacker exploited. Post-deploy 30-day 4100 authorization_code redeems 0 double-redeem legitimate clients only redeem once family-revoke flow lab-tested double-redeem simulation 2nd 400 invalid_grant + 1st tokens revoked + Sentry beacon. Stefan €2800 Wise CVSS 8.1 + Hall of Fame + advisory board LinkedIn 5.2k European OAuth integration community disclosure-response benchmark RFC 6749 BCP §4.10 compliant. Ezgi Ankara ODTU Teknokent 33-yo 5-yr ex-Trendyol API team @ezgi-oauth parallel discovery €2200 Turkish OAuth integration community blog 2.4k. Pattern OAuth 2.0 token endpoints authorization_code + refresh_token redemption ALWAYS atomic UPDATE inline filter + double-redeem family-revoke cascade RFC 6749 BCP §4.10 + §4.14 compliance. Sibling sweep oauth_authorization_codes PP H1 + oauth_refresh_tokens PR #526 HH F1 + oauth_access_tokens 15min short-lived family_id cascade + affiliate_otp_codes PR #646 VI F3 + customer_email_links PR #335 + oauth_pkce_challenges PR #526 HH F1. Implementation single-use token table identify + UPDATE inline filter atomic consume WHERE token=? AND redeemed_at IS NULL + meta.changes=0 400 invalid_grant + family_id column + cascade revoke + Sentry beacon + PR template checkbox + RFC 6749 BCP compliance audit quarterly. PR #548 reference.

Read More
⚖️industry
2026-05-2513 min read

Two super-admins approved and rejected the same payout — status race-guard (PR #548 PP H5)

Lena Berlin Mitte Synaltix EU office 32-yo EU affiliate payout lead ops engineer 8-yr operations 5-yr Delivery Hero finance ops + 3-yr Synaltix thMenu Phase 1-3 affiliate payout review + anomaly + Wise dispatch superadmin panel manual. Monday 09:00 47 pending payout requests KYC review complete + Wise quote pulled awaiting final sign-off. Senior colleague Kerem parallel-reviews. 09:14 Lena payout #47828 Approve clicks same moment Kerem Reject same row two requests parallel thMenu admin API. 5 seconds UI refresh status rejected but Lena approved Wise transfer dispatch log Lena approve dispatchWiseTransfer + Wise quote+create €310 affiliate bank money flowing thMenu DB status rejected audit chaos. Engineering reproduce two parallel curl PUT endpoint different status. 3 wrong theories (1) UI optimistic lock reviewing badge helpful UX race fix not two superadmins miss/ignore race still UI-only back-end correctness; (2) DB transaction isolation level Cloudflare D1 SQLite serialized exists each independent UPDATE own mini-transaction statement-level atomicity inline race-guard; (3) frontend debounce 1 sec single user race two different users. Correct fix race-guarded UPDATE WHERE status='requested' + meta.changes=0 detection 409 conflict + state machine PAYOUT_TRANSITIONS lookup. Forensic apps/web-superadmin/src/app/api/payouts/[id]/route.ts no SELECT no race-guard two parallel (1) Lena approve UPDATE WHERE id=47828 status=approved + dispatchWiseTransfer €310 wire fired; (2) Kerem reject UPDATE WHERE id=47828 status=rejected + Lena updated_at + status OVERWRITE no dispatchWiseTransfer call. Final state rejected reviewed_by=kerem Wise transfer ALREADY FIRED €310 audit chaos. Manual reconcile Wise cancelTransfer pending settlement window reversal success status requested KYC re-review docs OK approved + Wise re-fired 24 hours affiliate €310. Production audit 90-day race-pattern 3 incidents Lena + 2 others similar manual reconciles. PR #548 batch PP H5 3-layer fix Layer 1 race-guarded UPDATE WHERE status='requested' meta.changes=0 SELECT current 409 conflict current_status + current_reviewer UI 409 catch refresh-and-retry. Layer 2 side-effect dispatch ONLY after meta.changes>0 if approved + meta.changes>0 dispatchWiseTransfer status flip + Wise atomic spurious fires impossible. Layer 3 PAYOUT_TRANSITIONS const requested:[approved,rejected] approved:[processing,cancelled] rejected:[] processing:[paid,failed] paid:[] failed:[requested] retry cancelled:[requested] retry state machine 422 illegal_transition sibling PR #603 BBB F3 reservation. Bonus audit log per state transition INSERT payout_audit_log payout_id + prior_status + new_status + actor_id + transition_at + reason + dashboard widget. 3 affected manual reconcile (a) Lena/Kerem Monday Wise reverse + KYC re-review €310; (b) Aysu/Mehmet 2 months prior Wise IBAN invalid return cancel + reissue new IBAN; (c) Aysu/Mehmet 1 month prior double approve Wise idempotency-key single transfer manual reconcile. Post-deploy 30-day 0 race incidents 4 race-guard 409 triggers superadmins refresh-and-retry state-machine compliant 0 spurious Wise. Lena Synaltix internal Slack PR #548 PP H5 review queue safe parallel superadmins 409 coordinated SOC 2 finance audit transparent audit log Berlin coffee engineering. Tugrul Istanbul Maslak Synaltix HQ 29-yo TR/MENA affiliate payout lead ops ex-Hepsiburada finance ops 6-yr parallel #47811 TR queue same Monday after PR #548 deploy queue safe. Pattern financial state-change endpoints (payout approve/reject + refund + dispute + transfer + commission) MUST use UPDATE inline filter WHERE prior_status race-guard + side-effect dispatch ONLY after meta.changes>0 + state machine TRANSITIONS lookup. Sibling sweep /api/payouts/[id]/approve|reject PP H5 + affiliate_commissions PR #378 + /api/orders/[id]/refund PR #328 + /api/dispute/[id]/resolve PR #585 XX F1 + /api/wise/transfer-confirm PR #593 ZZ F1 + /api/orders/[id]/status PR #329. Implementation financial state-change identify + race-guarded UPDATE + meta.changes 409 + side-effect ONLY after success + TRANSITIONS state machine + audit log + UI 409 catch refresh-and-retry + PR template checkbox + quarterly financial endpoint audit. PR #548 reference.

Read More