📊 Price Comparison Policy — At a Glance

Tourpik vs Peaktime · Tripon · as of 2026-07-28 · Ops manual: Master Guide →  |  한국어 · English · Tiếng Việt

① The Whole Pipeline

CollectOur rate sheet & snapshot
PK·TR API / extension
AlignSame checkin·nights·party
into buckets
Hidden priceCompetitor conditional discounts
→ notice badge only (not a price)
VerdictPer-night comparison
tiers & flags
Adjustvs competitor low
suggestions · guards
🎯 The base unit of comparison = "per-night price under the same conditions (checkin · nights · party)" — if conditions don't match, prices are never compared as-is

② Where the Three Prices Come From

TP Tourpik (us)

  • Rate sheet: admin API KV (net/sale) × FX rate
  • Official snapshot: KRW confirmed prices from product API → headline price
  • Stale detection: snapshot vs rate sheet by content comparison (price mismatch · missing keys)
  • When stale, the page waits for recollection and shows the fresh price. "Old snapshot" badge only on failure / 45s timeout

PK Peaktime

  • Detail API (real prices per nights & dates)
  • 1/2/3BR kept in separate buckets per residual (no sub-rate contamination)
  • Hidden-price rules parsed from detail pages (long-stay · early-bird)

TR Tripon

  • Rooms API (hotel-level lowest price in nature)
  • Annotated "based on cheapest room"
  • Cache invalidated immediately on recollection — fresh values shown right away
Competitor price source priorityMeaning
1️⃣ Condition-matched (bucket)Collected with the same checkin & nights — most trusted
2️⃣ LiveReal-time collected value
3️⃣ Auto-lowestLowest from another checkin — date shown plus "condition mismatch △" warning

③ Hidden-Price Decision Tree — Deciding whether to show a notice badge (it never produces a price)

1. Does this hotel have parsed discount rules? (long-stay N+ nights x% / early-bird N days x%)
NONo hidden price — compare the list price as-is YES↓ next
2. Is check-in inside the rule period (union of seasons) and clear of blackout/exclusion dates?
NONot applied YES↓ next
3. Conditions met? (nights ≥ minNights / booking lead ≥ leadDays)
NONot applied YES↓ next
4. Already baked into the list price? — the only valid evidence is the per-night comparison across nights
if the N-night unit price is 0.5%+ below the 1-night unit price for the same room & checkin, it's already applied
APPLIEDSkip — prevents double discounting NOT APPLIEDNotice badge only — "possible hidden discount −N% (unverified)". The price stays the displayed price
🚨🚨 Set by the CEO, 2026-08-15 — this tree does not produce a price: "NOT APPLIED" at step 4 proves only that the discount is not reflected in the displayed price; it never proves the discount exists. On 2026-08-15 this showed 269,919 KRW for a room the site listed at 317,552 KRW. So the tree's outcome is used for the notice badge only — prices, suggestions, and transmissions use only discounts measured on the checkout page.
🚨 Absolute rule: the strikethrough→sale discount (e.g. ↓10%) is an always-on marketing discount independent of nights. Never mark a rule "already applied" because the displayed rate looks similar — conditional discounts are added on top at the payment step (verified: identical 1/2/3-night unit prices via API + [long-stay benefit] line in the payment window).
Safety principle: ambiguous rules (conflicting rates for the same condition, unknown periods, …) are held entirely by the risk gate — not showing a hidden price is safer than over-discounting. Full audit (53 rules): 49 not-applied · 0 already-applied (2026-07-28).

④ Verdict — Who Is Cheaper, and by How Much

Our headline price (per night) vs each competitor's site-displayed price (the hidden-price notice is a badge and never changes this figure)
diff = ours − competitor low+N₩ overpriced / −N₩ cheaper / equal
Flag checks → priority tier assigned
T0
⚠ Data suspect

matching suspect · price outlier · breakfast mismatch · input-error suspect (≤ half of competitor low) · declared≠actual discount

T1
🔴 We're overpriced

diff > 0 — top adjustment priority

T2
⚪ Not comparable

price unknown · competitor not collected · capacity short (display only)

T3
🟢 Healthy

we are equal or cheaper

Room matching: auto-matching retired → stay-level comparison + manually confirmed mappings. room-sig normalization builds candidates; mismatches (bedroom count etc.) only raise suspect flags.

⑤ Price Adjustment (Workbench) — Suggestions and Guards

Competitor lowsite-displayed price basis
₩-mode (default)low − configured range
random per room (₩10 steps)
Net floor guardclamped 🟡 when below
Net+₩/% floor
SendLIVE only sends for real
others sealed (record only)
Markup prioritypromotion row > room row > hotel common  ·  new Sale = Net × (1+markup%)
Skippedrooms with no competitor price · unchecked rooms (per-row reasons: excluded / same as guard / no competitor / already at target)
Final defenseserver policy guard re-validates and blocks right before transmission

⑥ Verification Tools

ToolPurpose
npm testsyntax + schedule + locks + room-matching (room-sig) regression
scripts/audit-hidden-applied.jsfull audit of the hidden-price premise (1-night vs N-night unit prices)
scripts/refresh-hidden-promos.jsfull re-parse of hidden-price rules (dry-run default · --apply with backup · risk cleanup)
scripts/audit-room-matches.js · audit-official-products.js · check-curated-cond.mjsroom matching · our snapshots · curated condition consistency audits