Multi-axis earning: почему один loyalty-point убивает программу до запуска
Это второй пост Hospitality Loyalty Brief. Если первый объяснял зачем запускать retention-программу — этот объясняет, как её устроить так, чтобы она не сломалась через шесть месяцев после soft-launch.
Главная ошибка: «рубль = балл»
Стандартная схема, которую большинство hospitality-операторов копирует у банков и ритейла: за каждый потраченный рубль гость получает 1 балл; накопил 1 000 баллов — обменял на ночь. Понятно, измеримо, легко продать инвесторам.
И — катастрофически неправильно для отеля.
Причины три, и все три про физику гостиничного бизнеса:
- Один stay = разовая транзакция, но множественные поведения. Гость не просто платит за номер — он остаётся 3 ночи, обедает в ресторане, идёт в спа, оставляет отзыв, рекомендует друзьям. «Рубль = балл» видит только итоговый чек.
- ADR в разных сегментах различается в 5-8×. Studio за 6 000 и suite за 38 000 — внутри одной программы. Линейная шкала «рубль = балл» превращает программу в дискриминацию: премиальный гость получает в 6 раз больше за то же поведение, а массовый — копит баллы быстрее реального вклада в LTV.
- Сезонность ломает накопление. Resort с летним пиком: гость в августе получает 1 800 баллов за неделю, а в феврале — 400 за такую же. То же поведение, разный «доход» в программе — это feels arbitrary, не fair.
4-axis earning: что начисляется одновременно
В TTE.Loyalty каждая транзакция (PMS event) пробивает четыре независимые оси одновременно. Гость не выбирает «как считать» — система начисляет везде сразу.
Ось 1 · Monetary
Деньги, потраченные в собственности. Сегментированы по 6 категориям выручки: room / F&B / spa / ancillary / retail / meetings. У каждой свой tier-multiplier — потому что F&B-выручка стоит отелю меньше, чем room-выручка. Это правда — нужно её отражать в начислении.
monetary_points = Σ(category_revenue × category_multiplier × tier_bonus)
Пример: Silver-член провёл 3 ночи (room 24 000 ₽, F&B 4 200 ₽, spa 6 800 ₽).
Multipliers: room=1.0, F&B=1.4, spa=1.6. Tier-бонус Silver +15%.
points = (24000×1.0 + 4200×1.4 + 6800×1.6) × 1.15 ≈ 47 380.
Ось 2 · Volume
Число ночей и stays — не сумма, а количество. Именно это создаёт привычку. Гость, который останавливается 12 раз по 1 ночи, ценнее для retention, чем гость с одной 12-ночной поездкой за тот же money-spend: чаще touch, больше шансов на upsell, понятнее предпочтения.
volume_points = stays × stay_bonus + nights × night_bonus
Пример: те же 3 ночи = 1 stay = 3 nights × 100 + 1 stay × 800 = 1 100 баллов.
Ось 3 · Frequency
Не «сколько» — а «как часто». Эта ось награждает паттерн возврата: гость, который останавливается каждый квартал, получает frequency-бонус, который растёт нелинейно с серией визитов.
frequency_points = days_since_last_stay < THRESHOLD ? streak_bonus(streak) : 0
Эту ось ритейл не использует — потому что в ритейле частота визитов измеряется в днях, а в отеле — в кварталах. Без frequency-оси loyalty-программа не отличает «лояльного» гостя от «единоразового спендера».
Ось 4 · Engagement
Действия, не связанные напрямую с покупкой: оставил отзыв, заполнил профиль предпочтений, привёл друга через referral, поделился фото в соцсети с тегом отеля, ответил на пост-stay опрос. Это валюта будущей выручки — engagement-поведение коррелирует с repeat-rate на 0.6-0.7 в hospitality-данных McKinsey.
engagement_points = Σ(action × action_weight × decay_factor)
Пример: отзыв 5⭐ с фото (+1 200), заполненный профиль (+400), referral-приглашение, которое привело к stay (+5 000). Эти 6 600 баллов не связаны с одной транзакцией — это работа гостя на отель, которую программа должна признать.
Tier-квалификация: 5 типов, не один
Та же логика — для перехода между уровнями (Silver → Gold → Platinum). Один порог по сумме денег убивает программу через 18 месяцев: гости-«премиальные транжиры» застряли на Gold не двигаясь, а лояльные «частые недорогие» не дотягивают до Silver.
Альтернатива — квалификация через любой из 5 каналов:
- Spend-quals — суммарные траты за год
- Stay-quals — число stays за год
- Night-quals — число ночей за год
- Composite-quals — взвешенная комбинация (spend × stays)
- Behavioral-quals — для гостей, перешедших порог engagement-действий
Гость двигается на следующий tier, как только дотянул до порога любого из пяти. Резорт-оператор выбирает, какие три-четыре включить — и это уже tenant-конфигурация в админке.
Почему всё это умещается в одной транзакции
Главное архитектурное решение в TTE.Loyalty: все четыре оси и все пять квалификаций пересчитываются в одной DB-транзакции, когда из PMS приходит webhook о новом stay.
tenantTransaction(tenantId, async (tx) => {
const monetary = computeMonetaryAxis(stay, tx)
const volume = computeVolumeAxis(member, stay, tx)
const frequency = computeFrequencyAxis(member, stay, tx)
const engagement = computeEngagementAxis(member, tx)
await applyPoints(member, [monetary, volume, frequency, engagement], tx)
await recheckTierQuals(member, tx)
await writeAudit(tx, { actorType: 'system', action: 'earn' })
})
Это значит: либо все начисления и tier-checks прошли — либо ничего. Никаких «полу-начислений» при сбое. Никаких guests, которые «накопили в одной системе, а tier — в другой».
Что это даёт оператору
- Программа отражает реальную экономику. Гость с высоким engagement, но низким spend, получает признание — потому что он сделает для retention больше, чем «премиальный одиночка».
- Tier-mix не схлопывается. Распределение по уровням остаётся диверсифицированным, и это feels fair всему spectrum'у гостей.
- Sezon-проблема решается. Frequency-ось вознаграждает межсезонные возвраты — программа сама подталкивает гостей к нужным датам.
- Audit-trail на всё. Каждое начисление — отдельная строка в append-only audit_log с причиной. Никаких «непонятно откуда баллы».
Дальше
В следующем посте — про tier-design: как калибровать пороги, чтобы Silver-гости не «застревали», а distribution по tier'ам оставалась стабильной 18+ месяцев.