Wongnai POS / Ocha Integration: Unify Sales and Guest Data, End Cashier Double-Entry

Wongnai POS / Ocha Integration: Unify Sales and Guest Data, End Cashier Double-Entry
BLUF: Sales and guest history survive only on one reservation ID
In a Bangkok full-service room, a booking ledger that sits apart from Wongnai POS or Ocha does not cost “a few seconds at the till.” It costs checks that do not match the booking and settled spend that never lands on the guest card. Wongnai POS (the Android line) and FoodStory (the same company’s iPad line) ingest LINE MAN orders well. They do not publish a TableCheck-grade public POS API for a third-party reservation engine. Ocha at 799 THB a month will take payment offline and print a Thai tax invoice, and official help states that the membership module cannot connect to outside software. Integration is finished only when Seated opens a folio, payment-complete releases the table, and the settled amount plus line items attach to the reservation ID. Shops that kill double-entry are the shops that close table-ID maps, daily settlement deductions, and the 10% service plus 7% VAT recomputation in the contract, before anyone taps “connect.”
1. Count the loss as mismatch plus invisible LTV, not as typing seconds
Sukhumvit and Phrom Phong izakaya, yakiniku, and kaiseki rooms of 28 to 40 seats keep the booking book of record in a LINE Official Account plus paper or an iPad grid. The payment book of record is Wongnai POS or Ocha. Friday 19:00, the host confirms “T-04, four covers, Course B, nut exclusion” on the ledger. The cashier re-types the same facts into the POS. While that re-key remains, three layers fire on the same cover.
- Payment mismatch. A table-number typo drops the course onto the neighbouring four-top. The host cuts the party to three on the ledger and leaves four on the POS. A 1,000 THB PromptPay deposit is subtracted by hand, or 10% service is applied after 7% VAT instead of before. Voiding in front of the guest becomes the Friday cashier queue.
- Dead table time. The check closes and the floor does not hear it. Bussing starts late. The loss is real only on Friday night, when the next party is already at the door. A host who looks for an empty chair before calling the waitlist has already missed the resale window.
- Guest LTV cut. POS membership keys on a phone number. The ledger’s regular keys on a LINE user ID. One person remains two IDs. A guest who spends 80,000 THB a year walks in and the till shows no lifetime. The next course offer and the allergy file never meet the settled check.
Place the arithmetic on a 40-seat Phrom Phong room, 1,100 covers a month, 1,850 THB check average, Friday-Saturday dinner at 18 tables times 2.1 turns.
- Re-key per seating: table number, covers, course SKU, free-text allergy. About 95 seconds. 1,100 covers is about 29 hours. At 90 THB an hour, labour is 2,610 THB a month.
- Billing incidents: four a month, 1,400 THB of discount or re-fire each, 5,600 THB a month, 67,200 THB a year.
- Friday-Saturday seat lag: 7 minutes after payment before the grid turns green. About 30% of those tables have a waiting party. At 1,850 THB and 85 minutes dwell, eight peak nights run about 14,000 THB a month, 168,000 THB a year.
- LTV cut: 35 regulars above 50,000 THB a year treated as first-time walk-ins, with repeat and check down 10%. About 175,000 THB a year.
The 2,610 THB labour line is not a board decision. The body of the loss is unsold second sits and spend that never reaches the card. If you KPI typing time, the meeting returns to “which POS is cheaper per month.” Watch the Friday 20:00 course that landed on the wrong table, and the regular who spent 80,000 THB last year and booked somewhere else this month.
2. Floor notes, Reddit, and X describe the same split
Shop memos and public posts name one structure.
On the floor (ledger and till on separate books). Friday 18:50, LINE confirms “four covers, Course B, 19:00, window.” At 19:05 the host marks Seated on the iPad. The cashier, in a rush, types “4.” The course opens on POS Table 4, which is counter 4. At 19:40 the window table arrives at the till with an empty folio. During the void and transfer, the next party waits at the door.
On X (vendor operations, not menu taste). In May 2026 a FoodStory POS user (Wongnai Media’s iPad line) posted that outage diagnosis was pushed back onto the shop and that support did not move. In May 2025 the same family of products signalled a shift of incident intake from phone to chat. Rooms that used to isolate a frozen till by voice now sit in a chat queue while Friday night dies. Bangkok operator groups repeat a second pattern: Wongnai POS works, and first-time setup assumes Thai copy. A Japanese GM who only read the English UI goes live with table labels and tax classes that do not match the floor.
On Reddit (the structure, product names swapped). r/restaurantowners keeps posting the three-tablet stack: POS, booking site, delivery tablet, same order typed twice. r/KitchenConfidential’s stock story is the floor change that never hits the POS, so the kitchen ticket and the check disagree. Operators are not refusing a brand. They are refusing four gaps: (a) Seated does not open a folio, (b) payment-complete does not return to table state, (c) membership ID is not the same key as reservation ID, (d) during an outage the cloud and the terminal each claim to be the book of record.
The operating implication is simple. Buying Wongnai POS or Ocha is not the same event as killing double-entry on the booking ledger. LINE MAN dropping into the kitchen does not prove that an in-house tasting course will drop onto the till.
3. Feature, price, and constraint matrix
“POS integration” is a slogan. Implementation decides monthly re-key volume and how complete the guest card is. List prices below are public figures from July to August 2026, mostly exclusive of VAT 7%. Daily headline rates move with campaigns.
| Dimension | Paper / LINE ledger + hand POS | Wongnai POS (Android line) | FoodStory POS (same company, iPad line) | Ocha POS | StoreHub | Rezabo ledger + POS adapter |
|---|---|---|---|---|---|---|
| Product job | Booking and payment are two books of record | Door into the LINE MAN stack. Single shop to under five sites | Full-service and buffet iPad POS | Low monthly till. Offline and weight-based charging | Retail plus F&B, stock and e-commerce sync | Booking, seating, CRM as book of record. Does not replace POS |
| Public software price | 0 | Headline about 14 THB/day. Promo 9 THB/day. Year about 3,300 to 5,100 THB | QSR 1,590 / full-service 1,990 / buffet 2,290 THB per month (ex-VAT) | Own device 799 THB/month (about 8,900 on annual). Sunmi bundle 250 THB/month | 990 / 1,999 / 4,800 THB/month. Long commit used to shrink the sticker | Ledger 1,900 THB/month, unlimited reservations. POS monthly stays on the existing contract |
| Hardware you see | Existing till | Terminal from 8,900. Promo Mini 11,900, one screen 13,900, two screens 15,900. Deposit 1,000 THB | iPad 9 about 12,056. Star mPOP about 18,599 | Bundles 17,990 to 30,990 (three months of software included) | Hardware unpublished | Floor iPad. Reuse the POS terminal |
| Pricing trap | Mismatch and LTV cut are the cost | Daily fee deducted from merchant settlement. Deposit is interest-free. Returned 30 to 45 days after device return | Printer count capped by plan. Buffet lives on the iPad line | 799 is software only. Membership cannot connect outside | 24- to 36-month contracts used to cut the apparent monthly | Flat ledger. Payment rails and POS vendor fees sit on other rows |
| Bind to reservations | Hand entry. Drops at peak | No public in-house reservation API spec. LINE MAN auto-intake is the product | Same. Inventory, staff order, EDC sit thicker on the iPad line | Folios and tables are native. Reservation-engine connect is not a public standard | E-commerce stock, not a booking ledger | Seated opens the folio. Payment releases the table. Settled sales attach to reservation ID |
| Where guest data lives | LINE threads vs POS members, split | POS members plus Wongnai shop page. LINE booking ID is a different key | CRM thicker on the iPad line | Members stay inside Ocha. Official help: no external software connect | Loyalty from mid plan up | Shop-tenant guest card. Phone, LINE, and reservation ID join to one person |
| Offline | Paper works. Same-day match remains | Cloud assumed. Line quality becomes till speed | Cloud assumed | Vendor states offline payment and tax invoices | Vendor states offline continue | Seating keeps running. POS disconnect queues events. Reconnect is idempotent |
| Delivery | Separate tablet, typed twice | LINE MAN is native | LINE MAN native. Channel-specific prices exist | Help still lists delivery as unconnected (later Scan-to-Order pages disagree) | Own Beep fleet plus e-commerce | In-house booking is the main line. Do not break existing POS delivery intake |
Read the table as one sentence. Wongnai POS and FoodStory are not rivals. They are Wongnai Media (LINE MAN Wongnai) Android and iPad lines. 14 THB a day is the Android headline. A full-service room that actually quotes will often land on the iPad line near 1,990 THB a month. Ocha at 799 THB is cheap as a till. For a shop that wants guest LTV back on the booking card, “membership cannot connect outside” hits first. StoreHub at 990 THB is an entry for stock and e-commerce. It is rarely why a Bangkok private-room house buys. Rezabo’s 1,900 THB is not POS rent. It is the cost of keeping Wongnai or Ocha in place and joining booking events to payment events on one key.
4. Double-entry dies on three event binds, not on a logo row
Vendor decks stop at “we connect by API.” Design the book of record for each event first.
Event 1. Seated opens the folio. The moment the ledger marks Seated, an empty check appears on the mapped POS table. Course SKU, covers, allergy codes, and deposit balance ride in the payload. The cashier does not re-type the table number. Course changes after seating happen on the POS and write back on settle.
Event 2. Payment-complete releases the table. Cash, PromptPay, card, or split close is received by webhook or by a bounded poll. The ledger moves to “paid / bussing” and offers the table to the waitlist. Spoken “they just got up” is not a book of record. Split tender and partial return release the table only on the final settled amount. A mid-check partial payment must not open the chair.
Event 3. Settled sales attach to the guest card. Tax-inclusive total, line items, discount, tender, service charge, and VAT attach to reservation ID and guest ID. Build the match table for POS member number, LINE user ID, and phone before go-live. Unmatched checks land in an “unnamed sales” queue for the GM the next morning. If unnamed rows are deleted at month-end, LTV does not come back.
A connection that misses any of the three is a dashboard with logos. Next-morning CSV is useful for cover-count versus check-count. It cannot release a Friday table and it cannot inject a course at seating. Do not mix those jobs.
type PosAdapter = "WONGNAI_ANDROID" | "FOODSTORY_IPAD" | "OCHA";
interface SeatedOpenCheckCommand {
idempotencyKey: string; // reservationId + seatedAt ISO
reservationId: string;
posTableId: string; // internal POS id, never "T-04" display
covers: number;
courseSku: string | null;
allergyCodes: string[]; // codes, not free text
depositInclusiveSatang: number;
guestId: string | null;
}
async function openCheckOnSeated(
adapter: PosAdapter,
cmd: SeatedOpenCheckCommand
): Promise<{ checkId: string; duplicate: boolean }> {
const existing = await findCheckByIdempotency(cmd.idempotencyKey);
if (existing) return { checkId: existing.checkId, duplicate: true };
return postPartnerOpenCheck(adapter, cmd);
}
The timeout path retries the same idempotencyKey. Two Seated taps, or a LINE retry, must open one folio.
{
"event": "pos.payment.closed",
"event_id": "evt_7f3c9a21",
"occurred_at": "2026-08-14T20:47:11+07:00",
"shop_id": "shp_thonglo_04",
"check_id": "chk_88421",
"pos_table_id": "tbl_12",
"reservation_id": "rsv_19c0e4",
"tender": ["promptpay", "card"],
"amounts_satang": {
"food_ex_vat": 720000,
"service_10pct": 72000,
"vat_7pct": 55440,
"deposit_applied": 100000,
"settled_inclusive": 747440
},
"line_items": [
{ "sku": "COURSE_B", "qty": 3, "allergy_codes": ["NUT"] }
]
}
Receive that payload at-least-once. The same check_id must release a table once.
query GuestSpendOnReservation($reservationId: ID!) {
reservation(id: $reservationId) {
id
guest {
id
lineUserId
phones
ltvInclusiveSatang
}
seatedCheck {
checkId
settledInclusiveSatang
serviceSatang
vatSatang
unnamed: matchStatus
}
}
}
mutation AttachSettledCheck($input: AttachSettledCheckInput!) {
attachSettledCheck(input: $input) {
guestId
unnamed
}
}
Floor traps (contract and operations)
- Table-number spelling drift. Ledger T-01, POS “Table 1,” and the floor’s “number 1” are three strings. Bind on a 1:1 internal
table_idmap, not on display names. A host who rewrites the map by hand on a private-room combine night will open Saturday lunch folios on the wrong chair. - Android line quoted as if it were the iPad line. Terminal 8,900 THB and 14 THB a day are Android-line figures. Buffet, deep inventory, staff order, and EDC sit thicker on FoodStory. Start an all-you-can-eat yakiniku on the Android line and you will migrate plans mid-contract.
- Daily deduction and the 1,000 THB deposit. Subscription-style Android plans deduct the daily fee from merchant settlement. The 1,000 THB device deposit is interest-free and returns 30 to 45 days after the terminal comes back. Accounting that books “POS rent” monthly will not find a matching line; they will find settlement net of a daily take.
- Ocha membership cannot leave Ocha. Official help states the member module does not connect to outside programs. If a salesperson says “API two-way CRM,” treat that as export or as manual work unless the contract lists events. The same help still lists QR order and delivery as unsupported, while later Scan-to-Order pages say otherwise. Write the in-scope events on paper.
- Ocha operator transfer. A 1 August 2026 notice moved the service to SCommerce (Thailand) Co., Ltd. Get price and API continuity in writing. Assume adapter client IDs will be reissued after transfer, and put the re-issue steps in the GM runbook.
- Deposit versus service-and-VAT order. Holding 1,000 THB per cover on the reservation, then letting POS apply 10% then 7% on the ex-VAT course, produces a remainder that cashiers “fix.” Offset against the tax-inclusive settled amount. Write the satang rounding rule in one line and copy the POS rule, not the ledger’s.
- Webhook replay. Payment-complete arrives late and twice on the same
check_id. Releasing the table twice seats the next party on an occupied chair. The receiver rule is one check ID, one release. - Do not take the booking book down with the POS. Stopping reservation intake because the till cloud is dark is backwards. During a POS outage, pay on paper or on the POS alone. Rejoin on
check_idafter reconnect. Do not cancel already-seated parties.
5. Plan traps and first-year TCO
On sticker monthly, hand-entry on the existing POS looks cheapest, Ocha next, Wongnai Android cheapest still on a daily rate. Add mismatch cost and the cost of re-collecting guest history, and the order changes.
Assumptions (40-seat Phrom Phong, 1,100 covers a month, 1,850 THB check, Friday-Saturday seat lag and billing incidents at the conservative figures in section 1):
- Billing incidents: 67,200 THB a year
- Friday-Saturday missed resale: 168,000 THB a year
- Regulars down 10% LTV: 175,000 THB a year
- Re-key labour: 31,320 THB a year
- Split-stack cost, if all four land: about 440,000 THB a year
They do not all land at full value every year. Billing plus seat lag already clears 230,000 THB. Then add software.
| Cost line (annual) | Hand-entry (keep current POS) | Wongnai Android alone | FoodStory iPad alone | Ocha alone | Rezabo flat + existing POS |
|---|---|---|---|---|---|
| Software | 0 | 9 to 14 THB/day × 365 = about 3,300 to 5,100 | 1,990 × 12 = 23,880 (full-service, ex-VAT) | 799 × 12 = 9,588 (own device) | 19,000 (annual plan, 2 months free) plus existing POS year |
| Hardware / deposit | 0 (existing) | Terminal 8,900 to 15,900 plus 1,000 deposit (no interest) | iPad plus printer about 30,000 | Bundle about 18,000 to 31,000, or own device 0 | No extra hardware (reuse) |
| Re-key labour | about 31,000 | Remains if booking unbound | Remains if booking unbound | Remains if booking unbound | Near zero if Seated-open runs |
| Billing incidents and seat lag | about 230,000 remains | LINE MAN improves. In-house booking re-key remains | Same | Offline resilience is high. Booking bind is separate work | Seat lag falls first if pay-to-release runs |
| Re-collecting guest LTV | Ask again every year | POS members accrue. LINE bookings stay split | CRM is thicker. Reservation-ID join is separate | Members stay inside. Cannot export by API | Settled sales attach to the card |
| First-year TCO, order of magnitude | Software 0 + split 440,000 | Device + daily fee + most of the split | Software 24,000 + hardware + most of the split | Software about 10,000 + closed membership | Ledger 42,000 + existing POS + remaining unnamed sales |
Four traps sit under the stickers. First, 9 THB or 14 THB a day is a headline for a single-site QSR whose main line is LINE MAN. A full-service room that papers that number through finance often discovers it needed the 1,990 THB iPad line. Second, daily deduction nets against settlement, so “POS rent” never appears as a monthly AP line. Third, Ocha at about 8,900 THB a year is cheap as a till, and the work to return members to the booking card is a separate budget. Fourth, a shop-written webhook quote that ignores table maps, splits, tax order, replay, offline queues, and post-transfer credential rotation routinely clears 80,000 to 150,000 THB in year one.
Do not over-claim Rezabo’s number. 1,900 THB a month is the unlimited reservation ledger. Wongnai or Ocha software does not disappear. The value is that booking volume does not turn the ledger into a per-cover meter, and that Seated, payment, and sales-attach are not re-implemented by the shop. Against split cost above 230,000 THB a year, a 19,000 THB ledger line is one order of magnitude smaller.
6. API and infrastructure limits to close before kickoff
Public API presence. TableCheck publishes POS-facing API documentation. Wongnai POS / FoodStory are built around LINE MAN intake and the merchant console. A third-party booking ledger cannot subscribe to the same event grain from a public spec. Connect through a partner contract, an official connector, or a permitted export. If someone says “REST, two-way” in a meeting, take the event list (seated, course inject, split, close, VOID) and the SLA on paper.
Ocha constraint surface. Help pages list no external membership connect, no dynamic QR payment, no QR order, and no delivery connect. Later Scan-to-Order and PromptPay pages disagree. The adapter treats only vendor-guaranteed events as book of record. Do not scrape unpublished screens. Write OAuth client re-issue after operator transfer into the runbook.
Money and tax. Thai full-service arithmetic is commonly ex-VAT course, plus 10% service, plus 7% VAT on the combined base. Decide first whether the reservation deposit is tax-inclusive. Round satang (0.01 THB) the way the POS rounds. If the ledger rounds half-up and the POS truncates, daily reconciliation drifts by a few baht and the floor stops trusting the bind and returns to hand entry.
Identifiers. Do not mix reservation ID, POS check_id, table_id, guest_id, and payment txn_id. Normalise phones: leading 0 versus +66. Moving a folio opened on a secretary’s number onto the principal guest is a logged host action, not a Friday rewrite of the match table.
Transport. Seated payloads carry reservation_id + seated_at as the idempotency key. A timeout retry must not open a second folio. On POS offline (Ocha’s strong case), flush the unsent queue in time order after reconnect. The ledger keeps seating and intake up while the till is dark.
Kitchen printers. Firing ESC/POS course tickets from the ledger onto TCP 9100 doubles print with the POS kitchen route. Print book of record stays on the POS. The ledger hands course SKU to the POS. The POS prints. Allergies travel as codes and print as a red band, not as a novel in the special-instructions field.
Partner adapters that do exist still sit inside unpublished envelopes. Use the following as a planning ceiling, not as a published Wongnai SLA. Confirm every row in the partner statement of work.
| Constraint | Planning envelope | Failure if you ignore it |
|---|---|---|
| Seated open-check POST | 1 request/sec per shop, burst 5, HTTP 429 retry with jitter | Duplicate folios if retry has no idempotency key |
| Payment webhook delivery | At-least-once, typically 3 retries across 15 minutes | Double table release on the same check_id |
| Guest match lookup at walk-in | 60 requests/minute per shop | Friday 19:00 lookup times out, host types the phone again |
| Polling fallback (no webhook) | 15 to 30 second interval, never 1 second | Source IP throttled; grid stays red after payment |
| LINE MAN order import | Native on Wongnai. Not an in-house reservation event | Naive merge overwrites reservation_id on a delivery ticket |
| Export / CSV pull | Once per closing, not per cover | Used as a fake real-time bind; Friday release still manual |
| Signature window | HMAC body + timestamp, reject skew over 5 minutes | Replay of an old close event opens a clean table at lunch |
CREATE TABLE pos_check_bind (
reservation_id TEXT PRIMARY KEY,
check_id TEXT NOT NULL UNIQUE,
pos_table_id TEXT NOT NULL,
adapter TEXT NOT NULL,
seated_at TIMESTAMPTZ NOT NULL,
closed_at TIMESTAMPTZ,
settled_satang BIGINT,
unnamed BOOLEAN NOT NULL DEFAULT TRUE,
idempotency_key TEXT NOT NULL UNIQUE
);
/* Daily cover vs check recon: unnamed rows are a GM queue, not a delete. */
SELECT
DATE(seated_at AT TIME ZONE 'Asia/Bangkok') AS biz_date,
COUNT(*) FILTER (WHERE closed_at IS NOT NULL) AS closed_checks,
COUNT(*) FILTER (WHERE unnamed) AS unnamed_sales,
SUM(settled_satang) FILTER (WHERE NOT unnamed) AS attached_satang
FROM pos_check_bind
WHERE seated_at >= DATE '2026-08-01' AND seated_at < DATE '2026-09-01'
GROUP BY 1
ORDER BY 1;
A morning with unnamed_sales above zero is a join job, not a “sync is green” screenshot.
7. Freeze the table map first, then spend three weeks
- Week 1. Copy every floor chair onto the POS internal ID. Tabulate combine tables and private rooms as exceptions. Scope is Friday-Saturday dinner courses only. Walk-ins may stay POS-origin. Match deposit offset and tax order on one test folio.
- Week 2. Run Seated-open and payment-close on live hardware. Exercise split tender, cover drop, VOID, and partial return at least three times each. Fire the payment webhook twice by hand and confirm the table does not double-release. At 16:00 Friday, stop the GM’s verbal check “did you tell the cashier the table number.”
- Week 3. Turn on settled-sales attach. Make the unnamed-sales morning queue a GM duty. Confirm LINE MAN (or existing delivery) tickets do not overwrite in-house reservation IDs. One-page outage rule: pay on POS alone, rejoin on
check_idafter reconnect.
Widen to all dayparts only after Friday-Saturday course mismatch sits at zero for two weeks. Auto-opening every two-top at lunch on day one explodes table-map exceptions at peak.
8. Questions the GM should close before signing
Q1. Do we have to throw away Wongnai POS or Ocha?
No. Payment, tax invoices, and LINE MAN intake stay on the current POS. What you throw away is re-typing table number and course at every seating.
Q2. Is the Android line enough for a full-service room?
If you run buffet, deep inventory, staff order, or EDC, quote the iPad line (FoodStory). Do not paper an Android daily rate through finance as if it were that quote.
Q3. Will Ocha member points flow back onto the booking card by themselves?
Official help says no external connect. If you need two-way point balances, keep points inside Ocha or design a separate booking-side benefit. Do not decide on a slide titled “CRM integration.”
Q4. If the internet dies, do bookings stop?
They must not. The ledger keeps running. Ocha can still take payment offline. After reconnect, flush unsent events with idempotency. Do not open the same chair twice on paper while the line is down.
Q5. Will joining POS cut reservation volume?
Guests see that they do not repeat the course at the table and that the cashier queue is shorter. Completion rate drops when you spread deposits or membership capture onto every chair, not because the till is joined.
Q6. Do we need an engineer on the floor?
The GM and the floor own the table map and the test folios. Auth keys, webhook signatures, and tax recon live with the implementation side. Resident maintenance that depends on screen scraping breaks on every app update and every operator transfer.
Close: join reservation IDs, not logos
Wongnai POS and Ocha already run Bangkok in-house payment. The missing piece is not another till. It is the bind to booking events. Daily 14 THB headlines, 799 THB offline resilience, a closed membership API, daily settlement deduction, table-number spelling drift: close those in the contract and the map, then automate only Seated, payment, and sales-attach. Double-entry ends after that, not after a logo appears on a status page.
Keep the current Wongnai POS or Ocha. Run seating and payment-complete on one key. Start from the table map and one test folio, not from a generic “we have an API.”
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
Delivered instantly via LINE Official Account (Instant 3-sec access)