Restaurant DX InsightsPublished: September 14, 2026
By Rezabo Editorial Team

Thailand Restaurant No-Show Prevention: PromptPay Deposit Automation That Cuts Food Waste to Zero

Thailand Restaurant No-Show Prevention: PromptPay Deposit Automation That Cuts Food Waste to Zero

BLUF: What to decide before Friday prep starts

If a Bangkok high-check venue takes more than six Friday-Saturday dinner no-shows a month, the loss is not the uncollected cancellation fee. The loss is already-prepped food cost plus a table that can no longer be resold. Waste falls toward zero only when the kitchen list is built from paid, confirmed covers, not from held names. A static PromptPay QR plus a LINE slip screenshot cannot stop amount edits or reused transfers during a 19:00 rush. Issue a reservation-ID dynamic QR, promote the seat only on a bank or PSP payment event, auto-release unpaid inventory before the prep cutoff, and route non-+66 guests to Visa / Mastercard / JCB. The shops that actually stop waste freeze Friday 16:00 prep volume to paid reservations only.

1. Count the loss as discarded prep plus a dead table, not as a missing course check

A 12-seat counter in Phrom Phong or Thong Lo, or a yakiniku / kaiseki room that holds six-top private rooms, will make the wrong call if it prices no-shows as lost top-line only. Three costs hit at the same time.

  1. Discarded prep. Air-flown fish and chilled wagyu do not hold quality overnight. At a 38% to 45% food cost, a four-top on a 5,500 THB tasting menu is 22,000 THB of revenue and roughly 8,000 THB of product already off the board.
  2. Dead inventory. Omakase almost never restocks an 18:00 walk-in after a silent no-show. A Friday 20:00 private room has demand on paper, but the resale window is closed once the party simply does not appear.
  3. Fixed kitchen labor. Prep is finished before the absence is known. Floor hours can be cut. Prep hours cannot.

Trade press repeats 15% to 20% no-show on reservations, with weekends near 30%. Measured on Bangkok high-check Friday-Saturday dinners without a deposit, the live range is closer to 12% to 22%. On a 12-seat counter, eight weekend slots a month is enough to write off 30,000 to 50,000 THB of product, and 80,000 to 150,000 THB once contribution on unsold seats is included. Extra phone confirms do not change that math if the kitchen still preps against held bookings. The operating rule that cuts waste is narrower: by the prep cutoff (14:00 to 16:00 in most tasting-menu kitchens), only paid reservations go on the list, and unpaid holds auto-open two hours before that cutoff.

Card-guarantee products sold to North American and Japanese restaurants do not translate cleanly. TableCheck-class “no-show policy” tools often authorize a card and capture later. That is not a Thai PromptPay deposit. PromptPay has no tokenization, no recurring charge, and no chargeback. Thai diners pay with K PLUS, SCB Easy, Krungthai NEXT, and Bangkok Bank. Asking them to leave a Visa PAN on file is how booking completion falls, even when the policy looks fair on paper.

2. Operators and diners describe the same failure on Reddit and X

The breakage pattern is identical in floor notes and in public threads.

On the floor (LINE + static QR + slip photo). At Friday 19:00 a host is squinting at K PLUS and SCB Easy screenshots. A slip from another date, a painted amount, or a friend’s transfer reused as “proof” is hard to catch while two more parties are waiting. Until a human marks the row “paid,” the table stays held. Phone plus chat plus a paper diary then double-book the same private room.

On the diner side. Threads on r/ThailandTourism treat a 1,000 THB per cover prepayment at Bangkok high-check rooms as a show guarantee, not a cover charge, and most locals accept that framing when it is written on the booking screen. Complaints cluster elsewhere. r/JapaneseFood and TableCheck users regularly report “I read same-day cancel as free, then I was charged in full” and “I cannot tell an authorization from a capture.” r/Thailand keeps a running set of posts from visitors who cannot pay a personal PromptPay QR because they have no Thai bank account. Workarounds such as DeepPocket often cap around 500 THB per day, which is useless for a tasting-menu deposit. On X, Bangkok night venues that switched to deposits split into two stories in the same week: members who pay once on the official site and skip the next deposit, and first-time guests who abandon the booking the moment a surprise prepayment appears.

Diners do not reject deposits as a class. They reject (a) copy that never says the money is protecting a seat and perishable prep, (b) a refund deadline missing from the booking screen, (c) a mix of card auth and cash QR with no receipt of what was actually taken, and (d) no path for a guest without a Thai bank.

Xendit’s 2026 hospitality note matches the floor: hotels, spas, and restaurants still chase bank-transfer screenshots for deposits. That workflow is slow, error-prone, and a poor first impression. Payment links over LINE help. For F&B they are incomplete unless the paid event writes the reservation ledger and the kitchen list in the same transaction.

3. Feature comparison: five ways to “take a deposit”

“Take a deposit” is a slogan. The implementation decides monthly waste and host minutes.

Dimension LINE + static QR + slip review Card auth hold (foreign ledgers) Omise / Xendit PromptPay Stripe PromptPay Rezabo dynamic QR + policy automation
Guest rail Personal or corporate account QR; guest types the amount Visa / Mastercard / JCB; weak for Thai bank-app payers Dynamic PromptPay QR Dynamic PromptPay QR in Stripe Checkout PromptPay as default; tourists auto-routed to cards
Cost per paid deposit Bank fee near 0 under 5,000 THB; labor is the real cost Card 3.25% to 3.65% plus VAT 7% (~35 to 39 THB on 1,000) Omise 1.65% plus VAT 7% (~17.7 THB on 1,000) Stripe processing plus standard payout timing Ledger is flat monthly; rail fees billed separately
Seat confirm trigger Human flips “held” to “confirmed”; delayed at peak Auth success holds the seat; staff confuse hold vs capture charge.complete / paid webhook payment_intent.succeeded Reservation ID match auto-confirms; unpaid auto-releases
Refund path Staff send back to the guest’s PromptPay; account typos remain Auth release is fast; captured refunds take days Omise: void/refund not supported. Xendit docs: Refunds Not Available Yes, but Stripe emails the guest for a refund account; status requires_action In-policy refunds from the console; PSP-blocked rails fall back to outbound transfer
Forgery / amount mismatch Edited slip images are not blocked Strong on cards; Thai completion drops first Amount and expiry are bound in the QR Amount bound in the QR Amount + reservation ID in the QR; OCR only if the webhook is late
QR / hold lifetime Paper QR never expires; amount can be changed by the payer Auth window is issuer-dependent Default 24 hours; cannot extend past 24h Payment expires if unpaid Re-issue jobs sized to lead time (same-day 2h, advance 24h cycles)
Monthly shape Software 0; waste and labor dominate Base fee + per-cover + card fees (some public quotes ~USD 250/month) Gateway % plus in-house engineering Stripe fees plus refund operations 1,900 THB flat, unlimited reservations, plus rail costs

Read the table as a ranking of operating risk, not of sticker price. Static QR is cheap per transfer and expensive per mismatch. Card-first foreign ledgers reconcile well and lose Thai diners. PSP PromptPay gives you a dynamic QR in days, then surprises finance when Friday’s in-policy cancel cannot be refunded through the same API. Stripe will refund PromptPay, but only after the guest replies to an email with the originating bank account. That is not a 19:00 host action.

4. Rate limits, amount units, and refund constraints to read before you sign

Sales decks say “PromptPay QR, instant, refundable.” The published limits disagree. Treat the following as contract due diligence, not trivia.

Constraint Bank of Thailand PromptPay (account-to-account) Omise PromptPay Xendit QR PromptPay Stripe PromptPay
Amount unit on the API Bank-specific; often THB major units Satang (1 THB = 100). 1,000 THB is 100000 THB major units in most docs Smallest currency unit
Min / max per charge Bank and KYC tier; retail caps often 50,000 to 500,000 THB/day Min 20 THB, max 150,000 THB Min 1 THB, max 700,000 THB Max 2,000,000 THB
QR / intent lifetime Static merchant QR has no expiry Default 24 hours. expires_at may shorten, not extend past 24h Channel-dependent; do not assume two-week survival Unpaid PaymentIntent expires; no capture-later
Recurring / tokenization No No Not available No
Chargebacks / disputes None (push payment) None None No dispute support
Refunds via API Outbound transfer to the guest PromptPay ID Cannot void or refund Official channel table: Refunds Not Available. Marketing pages mention voiding and “refunds coming in 2026.” Contract to the docs. Yes / partial yes, after the guest submits a refund account by email
Settlement Instant to the merchant account Charge paid on scan; merchant payout per Omise cycle Docs: instant for TH Stripe standard payout timing (not instant to the shop’s Thai account)
Payer-side bank fee <=5,000 THB: free. 5,001-30,000: <2 THB. 30,001-100,000: <5 THB. Above: <10 THB. Most retail banks waive this. Guest pays bank fee; merchant pays 1.65% + VAT Merchant MDR per contract (hospitality pages quote ~2.5% on QR in some packages) Merchant Stripe fee
Webhook duplicate risk Statement polling if you have a corporate API charge.complete can arrive late and twice for one charge.id Paid events retry; idempotency is your problem payment_intent.succeeded can retry
Tourist coverage Requires a Thai bank app Same Same Same; pair with cards

Three traps sit inside that matrix.

Satang vs baht. Send 1000 to an Omise PromptPay charge and you have issued a 10 THB QR, or you have failed min-amount validation. Send 10000000 and you have issued 100,000 THB. This is the most common first-week production incident in Thai PSP integrations.

24-hour QR on a 14-day booking. A charge created at reservation time is dead 13 days before service. If the product does not re-issue a fresh QR at T-72h and T-24h, completion falls and the host goes back to LINE chasing.

Refund copy that contradicts the PSP. Omise’s PromptPay guide states that charges cannot be voided or refunded through Omise. Xendit’s channel page marks Refunds as Not Available even while a 2026 blog claims voiding. Stripe refunds enter requires_action until the diner answers an email. If your booking screen promises “instant refund if you cancel before 20:00 the day prior,” you have written a policy your rail cannot execute.

5. The primary path is a reservation-ID payment event, not slip OCR

Product pages love computer vision on transfer slips. Build the ledger around the bank event first.

Dynamic merchant-presented QR (Thai QR Payment / EMVCo). Each reservation carries a locked amount and a reference. The guest does not type the amount. That single difference removes most mismatches. Receive on a corporate Tax ID merchant QR, not the GM’s mobile number.

Payment detection. The source of truth is an ITMX credit notification or a PSP webhook (charge.complete on Omise, paid QR events on Xendit, payment_intent.succeeded on Stripe). The reservation ledger promotes held to confirmed only when that event matches the reservation ID. Slip OCR is a backup for webhook delay, not the control.

Unpaid release and prep sync. Same-day holds expire in two hours. Advance holds expire in 24 hours and get a fresh QR. A LINE reminder fires two hours before expiry. The kitchen cover count is confirmed rows only.

Tourist fork and POS offset. If the phone is not +66, or the guest picks a card, route to Visa / Mastercard / JCB. At check, the POS subtracts the captured deposit. Manual subtraction breaks on party-size changes and on the 10% service charge. Write a 15-minute no-show grace (BTS delay, traffic) into house policy, then release to walk-in.

6. Implementation snippets: webhook, ledger, kitchen cutoff

Amounts, idempotency, and cutoff queries are where in-house builds fail after the QR “works in sandbox.”

const SATANG = 100;
const OMISE_MIN = 2_000;      // 20 THB
const OMISE_MAX = 15_000_000; // 150,000 THB

export function toSatang(thb: number): number {
  const satang = Math.round(thb * SATANG);
  if (satang < OMISE_MIN || satang > OMISE_MAX) {
    throw new Error(`PromptPay ${satang} satang is outside Omise 20-150,000 THB`);
  }
  return satang;
}

/** Omise rejects expires_at more than 24h from creation. */
export function qrExpiresAt(now: Date, sameDay: boolean): Date {
  const ms = sameDay ? 2 * 60 * 60 * 1000 : 24 * 60 * 60 * 1000;
  return new Date(now.getTime() + ms);
}

export type DepositPolicy = {
  days: Array<"fri" | "sat">;
  meal: "dinner";
  minPartySize: number;
  minCourseThb: number;
  perCoverThb: number;
  kitchenCutoff: "16:00";
};

Omise-style webhook payload (fields abbreviated; amount is satang; refundable is false):

{
  "object": "event",
  "key": "charge.complete",
  "id": "evnt_test_5nxyzpromptpay01",
  "data": {
    "object": "charge",
    "id": "chrg_test_5nxyzpromptpay01",
    "amount": 100000,
    "currency": "thb",
    "status": "successful",
    "paid": true,
    "refundable": false,
    "metadata": {
      "reservation_id": "rsv_bkk_20260821_1930_t12"
    },
    "source": {
      "type": "promptpay",
      "scannable_code": { "type": "qr" }
    }
  }
}

Apply the payment once. A retry of the same charge.id must not confirm a second table.

BEGIN;

INSERT INTO payments (charge_id, reservation_id, amount_satang, paid_at)
VALUES ('chrg_test_5nxyzpromptpay01', 'rsv_bkk_20260821_1930_t12', 100000, now())
ON CONFLICT (charge_id) DO NOTHING;

UPDATE reservations r
SET status = 'confirmed',
    deposit_paid_at = now(),
    paid_charge_id = 'chrg_test_5nxyzpromptpay01'
WHERE r.id = 'rsv_bkk_20260821_1930_t12'
  AND r.status = 'held'
  AND EXISTS (
    SELECT 1 FROM payments p
    WHERE p.charge_id = 'chrg_test_5nxyzpromptpay01'
      AND p.reservation_id = r.id
  );

COMMIT;

/* Kitchen list at Friday 16:00: paid covers only */
SELECT service_date, SUM(party_size) AS prep_covers
FROM reservations
WHERE service_date = DATE '2026-08-21'
  AND meal = 'dinner'
  AND status = 'confirmed'
  AND deposit_paid_at IS NOT NULL
GROUP BY service_date;

/* Unpaid holds past TTL become resale inventory */
UPDATE reservations
SET status = 'released', released_reason = 'deposit_expired'
WHERE status = 'held'
  AND deposit_due_at < now();

Issue and re-issue from the booking service, not from a host chat macro:

mutation IssueDepositQr($input: IssueDepositInput!) {
  issueDepositQr(input: $input) {
    reservationId
    amountSatang
    expiresAt
    qrPayload
    touristFallbackUrl
  }
}

If you poll a corporate bank statement API instead of a PSP webhook, cap the worker. Many Thai corporate connections are effectively tens of requests per minute, not hundreds. Poll on a per-merchant cursor, never per open QR in a tight loop during Friday service.

7. Operational traps that keep waste on the prep list

Contract and floor landmines

  • Blanket deposits. Charging weekday lunch and two-top a la carte kills completion. Start with Friday-Saturday dinner, parties of six or more, private rooms, and courses above 4,000 THB.
  • GM’s personal PromptPay number. Mixes corporate sales into a personal account. Revenue Department matching on PP.30 becomes unexplainable. Receive on a Tax ID merchant QR.
  • PSP “we can refund” talk. Omise documents PromptPay as non-refundable. Xendit marks Refunds Not Available. Stripe needs the guest’s bank details by email. Write the policy to the rail you actually have, including outbound PromptPay for in-window cancels.
  • One QR for a two-week-out booking. 24-hour expiry is a product constraint, not a guest habit. Re-issue or you will train hosts to collect slips again.
  • Silent party-size rules. Six booked, four arrive: is the two-cover remainder refunded or kept? If it is not on the booking screen, every check becomes a negotiation.
  • VAT timing. Decide with the accountant whether VAT 7% is recognized at deposit, or the cash sits as unearned revenue until service or no-show. Ad hoc receipts break PP.30.
  • Kitchen still prepping held rows. A working payment flow with a prep list still bound to held does not cut waste. Share one cutoff clock between the ledger and the pass.
  • Confirming on a slip photo. Reference-matched payment events are the original. Photos are backup. Manager override must write an audit row.
  • No card fork. PromptPay-only checkout drops tourists and regional corporates. Keep Visa / Mastercard / JCB beside the QR.
  • Opening a table while the iPad is offline because “they said they paid.” The paid flag lives in the cloud. Paper promises during an outage create double seats.

Merchant onboarding is slower than a sandbox QR. Dynamic commercial QR needs company registration, Tax ID, store photos, and a bank account whose legal name matches the trade name. A mismatch stops review. Singapore PayNow and other cross-border QR schemes are not a substitute for every tourist. Keep the card rail.

8. Pricing pitfalls and first-year TCO

Static QR looks cheapest on a price list. Add waste, labor, and refund work, and the ranking reverses.

Assumptions: 12-seat Phrom Phong counter, 5,500 THB average course, 40% food cost, 80 Friday-Saturday dinner covers in scope, 1,000 THB deposit per cover.

  • No-deposit no-show rate: 18% (14.4 covers)
  • Discarded prep: 14.4 × 5,500 × 0.40 = 31,680 THB/month
  • Lost contribution on unsold seats: 14.4 × 5,500 × 0.60 = 47,520 THB/month
  • Friday-Saturday direct loss: ~79,200 THB/month, 950,400 THB/year

If a scoped deposit drops no-shows to 2%, discarded prep falls to 3,520 THB/month. The annual gap on waste alone is about 338,000 THB. If released tables actually resell, the annual gap clears 700,000 THB.

Annual cost item Static QR + human match Card ledger + per-cover In-house PSP connect Rezabo flat ledger
Software / book 0 Base + per cover (often 4,000 to 8,000 THB/month; some quotes ~USD 250/month) Build 80,000 to 150,000 plus upkeep 3,500 × 12 = 42,000 THB
Payment fees ~0 bank 80 × 1,000 × 3.65% × 12 ≈ 35,000 80 × 1,000 × 1.65% × 1.07 × 12 ≈ 16,970 Rail cost only, booked by channel
Match labor ~4 min × 80 × 12 at 90 THB/hour ≈ 5,760 Low Engineer time on webhook retries Exception handling only
Refund operations Manual outbound, account errors Card refund days + inbox No refund API; manual outbound remains In-policy from the console; outbound where the PSP blocks
Residual waste Stays high if matching fails at peak Low, if completion does not collapse Low after the build is complete Low if the policy stays scoped
Year-1 TCO shape Waste can exceed 700,000 Fees + per-cover + lost Thai bookings Capex dominates year 1 42,000 + rail fees + residual waste

Three procurement mistakes repeat.

  1. Per-cover reservation software bills the deposit program as volume grows. A busy month is punished twice: more deposits to collect, and a larger software invoice.
  2. A 1.65% quote hides refund labor. If the API cannot refund, every in-window cancel is a host with a clipboard.
  3. “Connected to PromptPay” is not “Friday 19:00 unpaid inventory opens with no human.” Re-issue, card fork, POS offset, idempotent webhooks, and kitchen cutoff are separate jobs. Count them in the build estimate.

Rezabo’s 1,900 THB/month is the unlimited reservation ledger, not the bank or PSP fee. There is no per-cover booking commission, so Friday-Saturday volume does not inflate software OPEX. Against hundreds of thousands of baht in waste and lost contribution, the ledger line is an order of magnitude smaller. Do not over-claim the payment fee delta. The operating question is whether 20:00 Friday still has a confirmed empty four-top.

9. Roll out on a narrow scope in three weeks

  • Week 1. Scope Friday-Saturday dinner private rooms and courses at 4,000 THB or more per person. Put “full refund until 20:00 the day before; after that the deposit covers prep; on arrival it applies to the check” on the booking screen in Thai, English, and Japanese. Register the kitchen cutoff on the ledger.
  • Week 2. Turn on unpaid reminders and auto-release. Stop the GM opening a bank app at 16:00 Friday. Prep lists are confirmed covers only.
  • Week 3. Codify party-size changes (reduce until 24 hours out with a difference refund; increase issues a new QR), plus house waivers for BTS closures and flooding. Audit POS offset on live receipts. Drill the 15-minute no-show grace with the floor.

Run 90 days on that scope. Expand to holiday eves and weekday banquets of eight or more only after Friday-Saturday no-shows sit under 3%. Starting with every reservation is the abandonment pattern already visible on X.

10. Questions owners ask before they turn it on

Q1. How large should the deposit be? For high-check Bangkok rooms, 500 THB per cover on Friday-Saturday regular seats and 1,000 THB per cover on private rooms and tasting menus is the going rate. Start at 15% to 25% of the course. Full prepayment lowers first-visit completion.

Q2. How do tourists pay? Non-+66 numbers, or an explicit card choice, route to international cards. Do not hard-wire PromptPay as the only rail.

Q3. Who pays refund fees on an in-window cancel? Direct PromptPay from the shop account is effectively free under 5,000 THB on the BOT schedule. PSP PromptPay often cannot refund in-API, so the shop owns the outbound transfer cost and the staff time. Write that into the policy before the first Friday.

Q4. Can we confirm on a slip image alone? No, as a rule. Reference-matched payment events are the original. Images are backup. Exceptions need a manager log.

Q5. Will booking volume fall? It falls when every seat is charged. When the scope is narrow and the refund clock is on the screen, what falls is the “hold it just in case” booking. Realized Friday-Saturday covers usually rise because unpaid inventory returns in time to sell.

Q6. What if the webhook is late and the guest is at the door? Poll the charge or the merchant statement, then confirm. Do not seat on a screenshot during a 120-second delay unless a manager override is logged. Underpayment should issue a residual QR, not a whispered “pay the rest later.”

11. Metrics that prove waste actually fell

Metric No deposit / human match Scoped automated deposit Why it matters
Friday-Saturday no-show rate 12% to 22% 1% to 3% Lead indicator for waste and dead inventory
In-scope food waste 30,000 to 50,000 THB/month 0 to 4,000 THB/month Tests whether prep equals paid covers
Minutes to match a payment 3 to 5 per booking Exceptions only Friday peak floor time
Resale of unpaid holds Near 0 (discovered at 20:00) Over 50% if released before 18:00 Tests TTL and reminders
Completion on out-of-scope lunch Should be unchanged A drop means the scope is too wide Side-effect watch
Unconfirmed rows at kitchen cutoff Normal today 0 is the waste-zero condition Direct control
Food waste does not fall because you chased more guests. It falls when payment and seat confirmation are the same event, and the prep list is synchronized to paid reservations only.

Closing

Thai restaurants do not stop no-shows by collecting a polite “see you at 19:30.” They take a financial commitment on the rail guests already use, PromptPay dynamic QR, confirm the seat on the payment event, and return unpaid inventory to sale before a human is in the loop. The kitchen receives paid covers only. What to avoid is equally specific: blanket deposits, personal account QR, slip-only matching, contracts signed without reading PromptPay refund limits, and prep against held rows.

Keep the scope on Friday-Saturday and high-check inventory, print the refund clock on the booking screen, and leave a card path for tourists. Shops that hold those three conditions watch monthly discarded prep fall toward zero. What remains is exception handling. Ledger cost sits near 42,000 THB a year. Avoidable waste and lost contribution sit in the hundreds of thousands. The spread is not a rounding difference on MDR. It is whether Friday 20:00 still has a silent empty four-top.

To map deposit rules, refund deadlines, and POS offset onto your floor mix, talk to the local team from the button below.

Free Download (PDF)Ver 2.6 Live

Bangkok Restaurant No-Show Prevention Checklist (15 Rules)

An operational field guide for dining venues in Thailand: sync kitchen prep strictly with dynamic PromptPay QR deposits and automated LINE reminders to eliminate food waste.

  • Strict synchronization of Friday 16:00 kitchen prep with paid bookings only
  • Dynamic QR verification replacing static slip matching and slip forgery
  • High-retention refund deadline rules and seamless tourist card routing
Download Free Checklist (Get via LINE / PDF)

Delivered instantly via LINE Official Account (Instant 3-sec access)

1-Month Free Trial Available

Restaurant Booking & CRM Made Simple & Smart.

Born from Bangkok dining floors, Rezabo enables new floor staff to allocate tables and share VIP notes in 5 seconds with zero double-booking.

Explore Rezabo Features & Pricing (Free Trial)
← Back to Knowledge Articles