Перейти к содержанию

Конфигурация

Конфигурация модуля Loyalty разделена на три уровня:

  • Переменные окружения — инфраструктурные параметры развёртывания (подключение к БД, OIDC-провайдер, CORS, брокер сообщений, Redis, внешние интеграции). Задаются на уровне контейнера/деплоя.
  • Свойства приложения (application.yml, префикс loyalty.*, плюс customer.default.*) — бизнес-параметры и пороги подсистем: валюта баллов, системные счета, лимиты, расписания фоновых задач. Каждое свойство переопределяется переменной окружения по правилам Spring Boot (relaxed binding) — конкретная переменная указана в таблицах ниже.
  • Операционная конфигурация в БД (system_config) — узкий набор параметров, для которых значение, заданное администратором через /v1/admin/system-config, имеет приоритет над переменной окружения и действует без передеплоя (см. раздел «Операционная конфигурация в БД» ниже). Это исключение, а не общее правило — большинство параметров модуля такого переопределения не имеет.

Где искать конкретный параметр

Если параметр не встречается ни в одной таблице ниже — это либо строго env-only переменная инфраструктуры, либо расписание/лимит из смежного раздела руководства («Наблюдаемость» — метрики и оповещения; «Роли» — доступ; «Установка и развёртывание» — параметры чарта и ресурсы пода).

Обязательные переменные окружения

Минимальный набор, необходимый для запуска модуля в реальном контуре.

Переменная Назначение Обязательна Значение по умолчанию
LOYALTY_DB_URL JDBC-URL PostgreSQL да (есть fallback) jdbc:postgresql://…/billing
LOYALTY_DB_USERNAME Пользователь БД да (есть fallback) loyalty
LOYALTY_DB_PASSWORD Пароль БД да — (значения по умолчанию нет)
LOYALTY_OIDC_ISSUER_URI OIDC issuer (realm Keycloak) да — (без него приложение не стартует)
LOYALTY_OIDC_CLIENT_ID Keycloak-клиент, в resource_access которого лежат роли LOYALTY_* нет frontend

Пароль БД и issuer обязательны

Свойства LOYALTY_DB_PASSWORD и LOYALTY_OIDC_ISSUER_URI не имеют значения по умолчанию. Без пароля не поднимется пул соединений, без issuer-URI не инициализируется OAuth2 resource server — приложение завершится с ошибкой при старте.

База данных

URL, пользователь и пароль подключения к PostgreSQL. Пароль передаётся только через окружение (в реальном контуре — из секрета Kubernetes), в репозитории и конфиге не хранится. Схема версионируется и применяется самим приложением на старте через Liquibase (SPRING_LIQUIBASE_CONTEXTS, по умолчанию prod; демо-стенд задаёт demo, чтобы применить дополнительно сидовые changeset'ы). Размер пула соединений — LOYALTY_DB_POOL_SIZE (по умолчанию 30; денежная операция удерживает два соединения — своё и второе под распределённую блокировку), минимальный простой — LOYALTY_DB_POOL_MIN_IDLE (5), таймаут ожидания — LOYALTY_DB_POOL_TIMEOUT (5000 мс). Размер пула фоновых задач (@Scheduled) — LOYALTY_SCHEDULER_POOL_SIZE (по умолчанию 4).

OIDC / Keycloak

Модуль работает как OAuth2 resource server и валидирует JWT по issuer-uri. В едином контуре разработки используется общий realm с другими продуктами платформы (Core UI, Promo), чтобы один вход в Core UI давал токен, валидный для встроенного UI Loyalty.

Размер заголовка

Платформенные токены (омни-пользователь с ролями всех продуктов в одном JWT) велики, поэтому в конфигурации повышен лимит заголовка HTTP-запроса (server.max-http-request-header-size = 64KB). При самостоятельном развёртывании за прокси убедитесь, что аналогичный лимит выставлен и на прокси.

LOYALTY_DB_URL=jdbc:postgresql://loyalty-db:5432/billing
LOYALTY_DB_USERNAME=loyalty
LOYALTY_DB_PASSWORD=<из секрета>
LOYALTY_OIDC_ISSUER_URI=https://<keycloak>/auth/realms/<realm>
LOYALTY_OIDC_CLIENT_ID=frontend

Операционная конфигурация: валюта и системные счета

Бизнес-параметры балльного леджера задаются свойствами приложения. Они определяют валюту баллов и набор системных счетов двойной бухгалтерии, на которых отражаются начисления, списания, сгорание и корректировки.

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.points.currency — (правится в БД, см. «Операционная конфигурация в БД» ниже) Код валюты, представляющей баллы (единый источник истины — в том числе для бюджета акций и денежной оценки обязательств) PTS
loyalty.accrual.source-account LOYALTY_ACCRUAL_SOURCE_ACCOUNT Системный счёт-источник начислений (кредит) MRK
loyalty.redemption.sink-account LOYALTY_REDEMPTION_SINK_ACCOUNT Системный счёт-приёмник списанных баллов RDM
loyalty.expiry.breakage-account LOYALTY_EXPIRY_BREAKAGE_ACCOUNT Системный счёт, на который списываются сгоревшие баллы BRK
loyalty.correction.sink-account LOYALTY_CORRECTION_SINK_ACCOUNT Системный контрсчёт ручных корректировок (снятие оператором, отдельно от sink-account трат) CORR
customer.default.chart-of-accounts — План счетов клиентского счёта по коду валюты; для валюты без записи — 2110 у балльной и 2120 у денежной PTS: 2110, RUB: 2120
loyalty.currency.exchange.mapping — Сопоставление валют → счета пополнения/депозита для операций обмена PTS → MRK, RUB → RUB_REVENUE

Валюта баллов и номера системных счетов accrual, redemption, expiry и correction дополнительно переопределяются в БД (см. «Операционная конфигурация в БД» ниже) — значение из БД имеет приоритет над переменной окружения.

Пример из конфигурации приложения (значения по умолчанию):

loyalty:
  points:
    currency: PTS
  accrual:
    source-account: MRK
  redemption:
    sink-account: RDM
  expiry:
    breakage-account: BRK
  correction:
    sink-account: CORR
customer:
  default:
    chart-of-accounts:
      PTS: 2110
      RUB: 2120

Где физически живут системные счета

Перечисленные свойства — это «эталон»: миграции БД создают валюту баллов, планы счетов 2110 и 2120 и системные счета MRK, RDM, BRK (источник начислений, списание, сгорание) и CORR (корректировки), если их ещё нет. Изменение свойства влияет на то, какой счёт использует приложение при последующих операциях; уже созданные записи в БД при этом не удаляются.

Счета списания и сгорания по умолчанию разведены

Три системных счёта — источник начислений, списание и сгорание — по умолчанию разные: по номеру счёта-получателя отчёт по субсчёту делит ушедшие баллы на «списано» и «сгорело». Прежние проводки контура остаются на MRK, переопределения в БД не трогаются. Если на контуре счёт списания и счёт сгорания совпадают (заданы одним номером), сгорание от списания не отделяется: итоги субсчёта отдают признак expirySeparable: false, а сгоревшее учитывается как списанное.

Просмотр текущей операционной конфигурации

Действующие системные счета, валюты и единицы измерения доступны оператору только для чтения через админ-фасад справочников. Источником истины остаются соответствующие таблицы леджера.

Эндпоинт Назначение Роль
GET /v1/admin/dictionaries Реестр справочников (что доступно/редактируемо) LOYALTY_DICTIONARY_READ
GET /v1/admin/dictionaries/system-account/entries Системные счета (план счетов) LOYALTY_DICTIONARY_READ
GET /v1/admin/dictionaries/currency/entries Валюты, включая валюту баллов LOYALTY_DICTIONARY_READ
GET /v1/admin/dictionaries/unit/entries Единицы измерения LOYALTY_DICTIONARY_READ

Операционная конфигурация в БД

Узкий набор ключей может быть переопределён администратором через GET/PUT /v1/admin/system-config (роли LOYALTY_DICTIONARY_READ/WRITE) без передеплоя — значение из БД в этом случае имеет приоритет над переменной окружения (а для валюты баллов и срока резерва — над жёстко заданным значением, своей переменной окружения у них нет). Значения кэшируются в памяти реплики на срок loyalty.reference-cache.ttl (по умолчанию 30s, 0s выключает кэш); правка на своей реплике действует сразу, соседние увидят её не позже чем через TTL.

Ключ system_config Дефолт, пока ключ не задан в БД Назначение
points.currency PTS (жёстко в конфигурации, своей переменной окружения нет) Код валюты, представляющей баллы
accrual.source-account LOYALTY_ACCRUAL_SOURCE_ACCOUNT (MRK) Системный счёт-источник начислений
redemption.sink-account LOYALTY_REDEMPTION_SINK_ACCOUNT (RDM) Системный счёт-приёмник списанных баллов
expiry.breakage-account LOYALTY_EXPIRY_BREAKAGE_ACCOUNT (BRK) Системный счёт, на который списываются сгоревшие баллы
correction.sink-account LOYALTY_CORRECTION_SINK_ACCOUNT (CORR) Системный контрсчёт ручных корректировок
redemption.consume-order LOYALTY_REDEMPTION_CONSUME_ORDER (FEFO) Порядок списания лотов: FIFO | LIFO | FEFO (минимизирует breakage)
redemption.hold-ttl PT30M (жёстко в конфигурации, своей переменной окружения нет) Срок жизни резерва баллов (двухфазное списание), если вызывающий его не задал
program.timezone LOYALTY_PROGRAM_TIMEZONE (Europe/Moscow) Часовой пояс программы (IANA) — см. «Часовой пояс программы» ниже

Прочие свойства приложения (все параметры ниже, кроме перечисленных в этой таблице) такого переопределения не имеют — их меняют только через переменную окружения и передеплой.

Часовой пояс программы

Пояс программы (program.timezone) — единый пояс контура. В нём интерфейс вводит и показывает даты, а сервер режет сутки, недели и месяцы: календарный лимит участий «в день/неделю/месяц/год», итоги субсчёта за даты, дневные разрезы отчётов и мониторинга, подстраховка учётной даты (см. «Бизнес-день»). В нём же подписываются даты в текстах клиенту и именах выгружаемых файлов, по нему трактуются расписания бизнес-заданий — сгорания баллов, предупреждений о сгорании и выгрузок во внешние системы.

  • Допустимо только имя зоны IANA (Europe/Moscow, Asia/Yekaterinburg). PUT /v1/admin/system-config/program.timezone со значением вида Moscow или UTC+3 отклоняется 422 program_timezone_invalid; непригодный LOYALTY_PROGRAM_TIMEZONE останавливает старт приложения.
  • Значение из БД действует без перезапуска, но зона расписаний сгорания и предупреждений берётся на старте — после смены пояса в админке их нужно перезапустить.
  • Текущий пояс отдаёт GET /v1/program/timezone ({zone}) любому аутентифицированному пользователю.
  • Пояс среды исполнения — всегда UTC (TZ=UTC и -Duser.timezone=UTC в образе и чартах): партиции леджера, печати журнала аудита и технические расписания от пояса программы не зависят. Технические расписания без пометки «в поясе программы» (например, LOYALTY_AUDIT_SEAL_VERIFY_CRON, LOYALTY_TIER_REVIEW_CRON) трактуются в UTC.
  • Прошлое не пересчитывается: проставленные учётные даты и сроки лотов остаются как есть.

Лимиты списания по разрезам (с 26.0.0)

Поверх глобальной политики погашения можно задавать лимиты списания по разрезам (per-scope). Разрез задаёт, к какой категории операций применяется ограничение:

Разрез (scope) Применение лимита
TIER Лимит для клиентов конкретного уровня (тира)
SEGMENT Лимит для клиентов сегмента — по текущему составу сегментов, залитому из внешней системы (см. «Интеграции» → «Сегменты клиентов из внешней системы»); если клиент входит в несколько сегментов с лимитами, берётся самое строгое ограничение
CURRENCY Лимит по валюте баллов

Действует правило «частное бьёт общее», порядок проверки: TIER → SEGMENT → CURRENCY → правила списания активной конфигурации программы → глобальная политика погашения. Применяется первый найденный лимит. При выходе суммы списания за границы лимита операция отклоняется ответом 422 (redemption_below_min или redemption_above_max).

В интерфейсе лимиты настраиваются в разделе «Настройка ПЛ» → «Лимиты по разрезам». Управление лимитами требует роль LOYALTY_DICTIONARY_WRITE.

Общая база — правила конфигурации или глобальная политика

Разрезы не отменяют общих правил, а уточняют их для конкретных уровней, сегментов и валют. Если частного лимита нет, действуют правила списания активной конфигурации программы; если в конфигурации они не заданы — глобальная политика погашения, как и раньше.

Теги клиентов и метки акций

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.tags.max-bulk-targets LOYALTY_TAGS_MAX_BULK_TARGETS Предел клиентов в одном пакете присвоения/снятия тега 10000
loyalty.tags.max-batch-ids LOYALTY_TAGS_MAX_BATCH_IDS Предел ids в query-параметре пакетного чтения (ограничено длиной строки запроса) 200
loyalty.tags.max-lookup-values LOYALTY_TAGS_MAX_LOOKUP_VALUES Предел значений в POST /v1/tags/lookup (тело запроса); он же — предел числа тегов в ручных операциях «Добавить теги» и «Снять теги» 1000
loyalty.promotion-labels.max-bulk-targets LOYALTY_PROMOTION_LABELS_MAX_BULK_TARGETS То же для меток акций 10000
loyalty.promotion-labels.max-batch-ids LOYALTY_PROMOTION_LABELS_MAX_BATCH_IDS То же; он же — предел списка меток в массовом проставлении и снятии меток у акций (больше — 400 bulk_size_exceeded) 200
loyalty.promotion-labels.max-lookup-values LOYALTY_PROMOTION_LABELS_MAX_LOOKUP_VALUES То же 1000

Ручные операции

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.manual-operations.max-rows LOYALTY_MANUAL_OPERATIONS_MAX_ROWS Предел строк файла/списка ID на экране «Ручные операции» 1000000
loyalty.manual-operations.bulk-max-ids LOYALTY_MANUAL_OPERATIONS_BULK_MAX_IDS Предел ID у JSON-ручек (bulk-accruals/bulk-corrections/tier-bulk) и массовых статус-действий каталога; он же размер чанка, на которые режется файл при исполнении 1000
loyalty.manual-operations.scheduler.enabled LOYALTY_MANUAL_OPERATIONS_SCHEDULER_ENABLED Шедулер отложенного начисления/корректировки — без него принятый отложенный пакет не исполнится никогда true
loyalty.manual-operations.scheduler.tick LOYALTY_MANUAL_OPERATIONS_SCHEDULER_TICK Как часто искать просроченные пакеты PT30S
loyalty.manual-operations.scheduler.max-ahead LOYALTY_MANUAL_OPERATIONS_SCHEDULE_MAX_AHEAD Предельная отсрочка выполнения пакета P366D
loyalty.manual-operations.events.tick LOYALTY_MANUAL_OPERATIONS_EVENTS_TICK Шаг опроса счётчиков для потока прогресса пакета (SSE) PT1S
loyalty.manual-operations.events.heartbeat LOYALTY_MANUAL_OPERATIONS_EVENTS_HEARTBEAT Период комментария-пульса SSE (чтобы прокси не закрыл тихое соединение) PT15S
loyalty.manual-operations.resume.enabled LOYALTY_MANUAL_OPERATIONS_RESUME_ENABLED Подхват брошенных пакетов после падения реплики посреди выполнения true
loyalty.manual-operations.resume.tick LOYALTY_MANUAL_OPERATIONS_RESUME_TICK Как часто искать брошенные пакеты PT1M
loyalty.manual-operations.resume.stale-after LOYALTY_MANUAL_OPERATIONS_RESUME_STALE_AFTER Пауза без признака жизни, после которой пакет считается брошенным PT5M
loyalty.manual-operations.kafka.enabled LOYALTY_MANUAL_OPERATIONS_KAFKA_ENABLED Транспорт чанков пакета через Kafka вместо очереди в памяти приложения false
loyalty.manual-operations.kafka.topic LOYALTY_MANUAL_OPERATIONS_KAFKA_TOPIC Топик чанков loyalty.manual-operations.chunks
loyalty.manual-operations.kafka.group-id LOYALTY_MANUAL_OPERATIONS_KAFKA_GROUP Группа потребителя loyalty-billing-manual-operations
loyalty.manual-operations.kafka.concurrency LOYALTY_MANUAL_OPERATIONS_KAFKA_CONCURRENCY Потоков потребителя (не больше числа партиций топика) 1
loyalty.manual-operations.kafka.retry-attempts LOYALTY_MANUAL_OPERATIONS_KAFKA_RETRY_ATTEMPTS Повторов чанка при системном сбое, затем чанк закрывается прерванным 3
loyalty.manual-operations.kafka.retry-delay LOYALTY_MANUAL_OPERATIONS_KAFKA_RETRY_DELAY Пауза между повторами PT2S
loyalty.manual-operations.kafka.publish-timeout LOYALTY_MANUAL_OPERATIONS_KAFKA_PUBLISH_TIMEOUT Ожидание подтверждения брокера при публикации чанка на приёме PT10S

Причина «Прочее» — отдельное право

У всех трёх ручных операций (начисление, корректировка, перевод уровня) причина «Прочее» доступна только с ролью LOYALTY_OPERATION_REASON_OTHER_WRITE поверх права на саму операцию. Справочник причин операций требует LOYALTY_DICTIONARY_READ — без него форма любой из трёх операций сообщает «Справочник причин недоступен».

Ingress: отключите буферизацию ответа для потока прогресса

GET /v1/customers/operations/batches/{batchId}/events — поток событий SSE. Если ingress перед подом буферизует ответ (значение по умолчанию у большинства контроллеров), оператор не видит хода выполнения пакета до его завершения. Для пути /v1/customers/operations/batches/*/events отключите буферизацию (например, nginx.ingress.kubernetes.io/proxy-buffering: "off").

Рекомендуется подключение к Kafka

Без Kafka (LOYALTY_MANUAL_OPERATIONS_KAFKA_ENABLED=false, значение по умолчанию) пакеты ручных операций выполняются через очередь в памяти приложения в один поток. При перезапуске пода неисполненные чанки пакета пропадают вместе с процессом; их заново публикует подхват брошенных пакетов (loyalty.manual-operations.resume.*, включён по умолчанию) — на старте приложения и затем раз в resume.tick, если пакет не подаёт признаков жизни дольше resume.stale-after. Уже проведённые строки повторно не проводятся, но выполнение пакета прерывается и продолжается с задержкой. Для контуров, где используются ручные операции, включите транспорт через Kafka: LOYALTY_MANUAL_OPERATIONS_KAFKA_ENABLED=true, LOYALTY_KAFKA_BOOTSTRAP и SASL-учётные данные.

Сгорание баллов: предупреждения и сборщик просроченных резервов

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.expiry.cron LOYALTY_EXPIRY_CRON Плановое сгорание баллов (тот же прогон, что и ручной POST /v1/maintenance/points/expire); расписание — в поясе программы выключено (в примерах чарта — 0 30 0 * * *: баллы со сроком «до 23:59:59» сгорают в ту же ночь)
loyalty.expiry.batch-size LOYALTY_EXPIRY_BATCH_SIZE Сколько просроченных лотов сжигает один прогон; остаток разбирается следующим прогоном 5000
loyalty.expiry.notify-before LOYALTY_EXPIRY_NOTIFY_BEFORE Шаги предупреждения о сгорании — один или несколько сроков до сгорания через запятую (ISO-8601, например P30D,P7D,P1D); по каждому лоту и шагу — одно предупреждение. Непригодное значение (ноль, отрицательное, повтор) заменяется умолчанием P7D (в примерах чарта — P7D,P1D)
loyalty.expiry.notify-batch-size LOYALTY_EXPIRY_NOTIFY_BATCH_SIZE Сколько лотов забирает один прогон предупреждений 5000
loyalty.expiry.notify-cron LOYALTY_EXPIRY_NOTIFY_CRON Расписание прогона предупреждений о скором сгорании — в поясе программы выключено (в примерах чарта — 0 0 9 * * *, 09:00 по поясу программы)
loyalty.redemption.hold-expiry.cron LOYALTY_REDEMPTION_HOLD_EXPIRY_CRON Сборщик просроченных резервов баллов (hold без commit/cancel по истечении срока) выключено (на активных контурах включают на раз в минуту)

Прогон сгорания выполняется один на кластер (ручной вызов во время идущего прогона получает 409 points_expiry_in_progress), пачками в порядке «срок, лот», каждый счёт — в своей транзакции. Пробный прогон без изменений — POST /v1/maintenance/points/expire?dryRun=true; ручной запуск прогона предупреждений — POST /v1/maintenance/points/expiry-notice (роль LOYALTY_MAINTENANCE_WRITE, 409 points_expiry_notice_in_progress, если прогон уже идёт). Шаг, окно которого прошло к моменту начисления, пропускается: при единственном шаге P7D лот, начисленный меньше чем за неделю до срока, предупреждения не получит — поэтому для контура рекомендуется P7D,P1D. Предупреждения и сообщение о факте сгорания ставятся в очередь исходящих уведомлений (см. «Уведомления клиентам: очередь исходящих» ниже).

Первое включение сгорания на контуре

Рекомендуемый порядок: включить предупреждения (LOYALTY_EXPIRY_NOTIFY_CRON) → выждать самый длинный шаг LOYALTY_EXPIRY_NOTIFY_BEFORE → выполнить пробный прогон dryRun=true и оценить объём → задать LOYALTY_EXPIRY_CRON. Подробнее о поведении сгорания для оператора — в разделе «Сгорание баллов».

Акции: автоведение по периоду

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.promotion.lifecycle.enabled LOYALTY_PROMOTION_LIFECYCLE_ENABLED Автоматический перевод акции в «Активна» по дате начала и в «Завершена» по дате окончания периода false (включён на dev/qa; на prod включается вместе с выкаткой обновлённого каталога акций)
loyalty.promotion.lifecycle.interval LOYALTY_PROMOTION_LIFECYCLE_INTERVAL Как часто проверять акции на автопереход PT1M

Акции: согласование запуска и статистика переменных покупки

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.promotion.approval.enabled LOYALTY_PROMOTION_APPROVAL_ENABLED Согласование запуска акции по принципу «четырёх глаз»: запуск только согласованной акции, содержательная правка сбрасывает согласование. Выключенное — действия согласования отвечают 409 approval_disabled, запуск идёт без согласования false (в примерах чарта включено на dev и qa, выключено на prod)
loyalty.purchase-attributes.stats-retention LOYALTY_PURCHASEATTRIBUTES_STATSRETENTION Сколько хранится статистика переменных покупки для формы акции (запас сверх окна «7 дней», которое читает форма) P30D
loyalty.purchase-attributes.cleanup-cron LOYALTY_PURCHASEATTRIBUTES_CLEANUPCRON Расписание уборки этой статистики выключено

Право запуска акции — отдельная роль при любом значении настройки

Запуск акции (POST /v1/promotions/{id}/activate, POST /v1/promotions/bulk-activate) требует роль LOYALTY_PROMOTION_ACTIVATE независимо от того, включено ли согласование; решение по согласованию — роль LOYALTY_PROMOTION_APPROVE. Перед выкаткой версии с этими ролями повторно импортируйте роли Keycloak и выдайте LOYALTY_PROMOTION_ACTIVATE тем, кто раньше запускал акции правом LOYALTY_PROMOTION_WRITE, — иначе запуск ответит 403 access_denied. Экрана для переключения настройки в интерфейсе нет — это настройка развёртывания.

Флаг согласования задаётся и веб-интерфейсу

API значение LOYALTY_PROMOTION_APPROVAL_ENABLED не отдаёт, поэтому веб-интерфейс узнаёт о согласовании из своего runtime-конфига — переменной PROMOTION_APPROVAL_ENABLED пода loyalty-ui (см. «Runtime-конфиг веб-интерфейса» ниже). Задавайте обе переменные одинаково: в примерах значений чартов loyalty-ui они согласованы с бэкендом (включено на dev и qa, выключено на prod).

Асинхронный приём команд (intake) и брокер сообщений

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.intake.scheduler-enabled LOYALTY_INTAKE_SCHEDULER_ENABLED Фоновый разбор очереди приёма true
loyalty.intake.tick LOYALTY_INTAKE_TICK Как часто воркер разбирает очередь PT1S
loyalty.intake.batch-size LOYALTY_INTAKE_BATCH_SIZE Команд за один тик 50
loyalty.intake.lease LOYALTY_INTAKE_LEASE Срок аренды обработчика (истёк — команда снова доступна) PT5M
loyalty.intake.max-attempts LOYALTY_INTAKE_MAX_ATTEMPTS Число попыток исполнения до откладывания в разбор (PARKED) 10
loyalty.intake.backoff / .max-backoff LOYALTY_INTAKE_BACKOFF / LOYALTY_INTAKE_MAX_BACKOFF Нарастающая пауза между попытками PT2S / PT10M
loyalty.intake.done-ttl LOYALTY_INTAKE_DONE_TTL Срок хранения исполненных команд — он же окно дедупликации приёма P7D
loyalty.intake.reaper-tick LOYALTY_INTAKE_REAPER_TICK Как часто искать зависшую аренду (реплика упала между захватом и коммитом) PT1M
loyalty.intake.max-schedule LOYALTY_INTAKE_MAX_SCHEDULE Предельная отсрочка исполнения команды P366D
loyalty.intake.retention-tick LOYALTY_INTAKE_RETENTION_TICK Как часто чистить исполненные команды старше done-ttl PT1H
loyalty.intake.kafka.enabled LOYALTY_INTAKE_KAFKA_ENABLED Приём команд через Kafka-потребитель (поверх той же очереди, что и REST-приём) false
loyalty.intake.kafka.topic LOYALTY_INTAKE_KAFKA_TOPIC Топик команд loyalty.intake.commands
loyalty.intake.kafka.group-id LOYALTY_INTAKE_KAFKA_GROUP Группа потребителя loyalty-billing-intake
loyalty.intake.kafka.concurrency LOYALTY_INTAKE_KAFKA_CONCURRENCY Потоков потребителя 1
loyalty.intake.kafka.retry-attempts / .retry-delay LOYALTY_INTAKE_KAFKA_RETRY_ATTEMPTS / LOYALTY_INTAKE_KAFKA_RETRY_DELAY Повтор при сбое обработки сообщения 3 / PT2S

Общее подключение к брокеру (используется всеми Kafka-транспортами модуля: intake.kafka, manual-operations.kafka, а также отправкой событий смены уровня tier-events и уведомлений notifications — см. разделы ниже):

Переменная Назначение Значение по умолчанию
LOYALTY_KAFKA_BOOTSTRAP Адрес брокера localhost:9092
LOYALTY_KAFKA_SECURITY_PROTOCOL Протокол безопасности (платформенный брокер — SASL_PLAINTEXT) PLAINTEXT
LOYALTY_KAFKA_SASL_MECHANISM Механизм SASL SCRAM-SHA-512
LOYALTY_KAFKA_SASL_JAAS_CONFIG Строка JAAS учётной записи брокера (секрет) —

Оба Kafka-транспорта выключены по умолчанию

На контуре без брокера приложение стартует и работает как раньше: команды принимаются только синхронным REST-путём, а чанки пакетов ручных операций исполняются очередью внутри самого приложения. Включайте транспорт только после того, как на брокере заведены топик, ACL на топик и на группу потребителя, и получены учётные данные SASL — без права на группу поломка самая тихая из возможных: сообщения публикуются, читать их некому, а пакет/команда «вечно висит в очереди», выглядя штатно работающей системой.

В примерах значений чарта values.dev.yaml, values.qa.yaml и values.prod.yaml оба транспорта включены (LOYALTY_INTAKE_KAFKA_ENABLED=true, LOYALTY_MANUAL_OPERATIONS_KAFKA_ENABLED=true), а строка JAAS приходит из k8s-секрета loyalty-kafka (ключ sasl-jaas-config). Значение по умолчанию в самом приложении по-прежнему false — включение и откат выполняются через значения чарта контура. Если один брокер обслуживает несколько контуров, разведите на них топики (LOYALTY_INTAKE_KAFKA_TOPIC, LOYALTY_MANUAL_OPERATIONS_KAFKA_TOPIC, LOYALTY_TIER_EVENTS_TOPIC): иначе группы потребителей контуров станут общими и брокер будет делить партиции между их подами произвольно, а события смены уровня разных контуров смешаются в одном топике.

Кеш баланса (Redis)

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.balance-cache.enabled LOYALTY_BALANCE_CACHE_ENABLED Включение cache-aside кеша баланса в Redis false
loyalty.balance-cache.ttl LOYALTY_BALANCE_CACHE_TTL Срок жизни ключа — больше окна, за которое незавершённое изменение точно закончится 30s
loyalty.balance-cache.recheck LOYALTY_BALANCE_CACHE_RECHECK Как часто сверяться с БД, пока по клиенту есть незавершённые изменения 1s
loyalty.balance-cache.recent-keys LOYALTY_BALANCE_CACHE_RECENT_KEYS Предел памяти локального ограничителя сверок 100000

Подключение к Redis задаётся стандартными свойствами Spring Data Redis (spring.data.redis.* / SPRING_DATA_REDIS_*). Отказ Redis (недоступность, таймаут) не прерывает операцию — модуль откатывается на чтение баланса из БД.

Кеш справочников

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.reference-cache.ttl LOYALTY_REFERENCE_CACHE_TTL Срок жизни в памяти реплики для плана счетов, валют, субсчетов, БД-переопределений system_config, а также разрешения кода торговой точки из покупки в точку, город и регион и артикула в категории товара каталога; 0s выключает кэш 30s

Кэш не согласован между репликами: правку справочника соседняя реплика увидит не позже чем через TTL; на своей реплике изменение действует сразу (сброс кэша при записи). Исключение — разрешение кода торговой точки и артикула: оно сбросом при записи не обновляется, и правка точки, города или товара доходит до условий акций не позже чем через TTL на каждой реплике.

Rate limiting (ограничение частоты)

Ограничение частоты запросов покрывает весь интеграционный периметр одним фильтром (алгоритм GCRA), а не выборочные эндпоинты. Правила задаются списком loyalty.rate-limit.rules — выигрывает первое совпавшее правило, порядок значим.

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.rate-limit.enabled LOYALTY_RATE_LIMIT_ENABLED Аварийный выключатель всего ограничителя true
loyalty.rate-limit.trust-forwarded-for LOYALTY_RATE_LIMIT_TRUST_XFF Брать адрес клиента из X-Forwarded-For (выключайте, если под выставлен наружу напрямую — иначе заголовок подделывается) true
loyalty.rate-limit.max-keys-per-rule LOYALTY_RATE_LIMIT_MAX_KEYS Предел ключей на правило в памяти реплики (fail-open при переполнении — не должно отбивать трафик) 100000
loyalty.rate-limit.idle-eviction — Через сколько простоя ключ вычищается PT10M
loyalty.rate-limit.sweep-interval — Как часто идёт очистка простаивающих ключей PT5M

Готовые правила (капасити задаётся как target / N, где N — число реплик, так как состояние ограничителя in-memory на реплику):

Правило Путь Capacity/окно Режим Переменная capacity
pos-card-resolve GET /v1/integration/pos/cards/** 60/мин ENFORCE LOYALTY_RATE_LIMIT_POS_RESOLVE
checkscan-overview GET /v1/integration/checkscan/customers/** 60/мин ENFORCE LOYALTY_RATE_LIMIT_CHECKSCAN
integration-write POST/PUT/PATCH/DELETE /v1/integration/** 600/мин LOYALTY_RATE_LIMIT_INTEGRATION_MODE (деф. OBSERVE) LOYALTY_RATE_LIMIT_INTEGRATION_WRITE
integration-read GET /v1/integration/** 300/мин LOYALTY_RATE_LIMIT_INTEGRATION_MODE (деф. OBSERVE) LOYALTY_RATE_LIMIT_INTEGRATION_READ
events POST /v1/events/** 1200/мин LOYALTY_RATE_LIMIT_EVENTS_MODE (деф. OBSERVE) LOYALTY_RATE_LIMIT_EVENTS
bulk Массовые/пакетные операции (импорт акций, tier-bulk, сгорание, bulk-accruals/bulk-corrections, приём пакета файлом) 10/мин LOYALTY_RATE_LIMIT_BULK_MODE (деф. OBSERVE) LOYALTY_RATE_LIMIT_BULK
b2c /v1/b2c/** (ключ — субъект клиентского токена) 120/мин LOYALTY_RATE_LIMIT_B2C_MODE (деф. OBSERVE) LOYALTY_RATE_LIMIT_B2C

Режим OBSERVE перед ENFORCE

Новые правила заводятся в режиме OBSERVE: он считает метрику loyalty.ratelimit{outcome=observed} и пишет предупреждение в лог, но не отбивает запрос. Порог обкатывается на реальном трафике контура, затем переводится в ENFORCE соответствующей env-переменной режима. Правила pos-card-resolve и checkscan-overview — исторические, сразу в ENFORCE, их коды ответов (resolve_rate_limited, checkscan_overview_rate_limited) — часть контракта интеграций.

Загружаемые файлы

Свойство Переменная окружения Назначение Значение по умолчанию
spring.servlet.multipart.max-file-size LOYALTY_IMPORT_MAX_FILE_SIZE Предел размера одного загружаемого файла 20MB
spring.servlet.multipart.max-request-size LOYALTY_IMPORT_MAX_REQUEST_SIZE Предел размера всего multipart-запроса 21MB
loyalty.import.max-rows LOYALTY_IMPORT_MAX_ROWS Предел строк файла импорта клиентов 10000
loyalty.import.max-field-length LOYALTY_IMPORT_MAX_FIELD_LENGTH Предел длины значения поля 512
loyalty.import.max-problems LOYALTY_IMPORT_MAX_PROBLEMS Предел размера отчёта об ошибках (счётчики в ответе всегда полные) 200
loyalty.import.scan.enabled LOYALTY_IMPORT_SCAN_ENABLED Антивирусная проверка загружаемых файлов (fail-closed: включённая проверка без доступного сканера отказывает, а не пропускает файл) false

Загружаемый файл проверяется до разбора содержимого: сигнатура первых байт (не архив, не исполняемый файл), кодировка строго UTF-8 (без замены некорректных байт), затем — правила разбора CSV. У ручных операций свой отдельный предел строк — loyalty.manual-operations.max-rows (раздел выше), а не loyalty.import.max-rows: список ID — не импорт профилей клиентов.

CORS

Переменная Назначение Значение по умолчанию
LOYALTY_CORS_ALLOWED_ORIGINS Origin, которым разрешены cross-origin запросы к основному API (через запятую) — (не задано = запрещено)
LOYALTY_B2C_CORS_ALLOWED_ORIGINS То же для клиентского контура /v1/b2c/**; пусто — действует список выше —

Значения читаются типизированной настройкой (loyalty.security.cors-allowed-origins / .b2c-cors-allowed-origins), а не напрямую из окружения — видны в /actuator/configprops и подставляются в тестах.

Служебный предпросмотр B2C-кабинета сотрудником (роль LOYALTY_PROFILE_READ) открывает не произвольного клиента, а только одного, заданного настройкой loyalty.b2c.preview-external-id (LOYALTY_B2C_PREVIEW_EXTERNAL_ID, по умолчанию PLT-1002).

LOYALTY_CORS_ALLOWED_ORIGINS=https://core-ui.example.ru,https://loyalty-ui.example.ru

Идемпотентность: журнал с ограничением хранения

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.idempotency.retention LOYALTY_IDEMPOTENCY_RETENTION Окно повтора: сколько хранится строка журнала идемпотентности (полное тело ответа денежной операции) P7D
loyalty.idempotency.cleanup-cron LOYALTY_IDEMPOTENCY_CLEANUP_CRON Расписание уборки журнала старше окна повтора; пусто (-) — уборка выключена выключено

Запрет двойной проводки от журнала не зависит

Журнал идемпотентности — оптимизация (экономит повторную работу, возвращает прежний ответ на повторный вызов с тем же Idempotency-Key). Запрет двойной проводки после истечения окна держит бэкстоп ядра леджера (уникальность ссылки на проводку), поэтому уборка журнала безопасна даже без включённого расписания. На проде расписание стоит включать (например, раз в сутки в непиковый час) — иначе журнал растёт без ограничения.

SIEM: передача событий аудита

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.siem.enabled LOYALTY_SIEM_ENABLED Включение потока событий аудита во внешний SIEM false
loyalty.siem.host / .port LOYALTY_SIEM_HOST / LOYALTY_SIEM_PORT Адрес приёмника — / 514
loyalty.siem.transport LOYALTY_SIEM_TRANSPORT TCP (гарантия доставки) или UDP TCP
loyalty.siem.format LOYALTY_SIEM_FORMAT CEF (понимают все распространённые SIEM без парсера под продукт) или JSON CEF
loyalty.siem.tick LOYALTY_SIEM_TICK Период отправки — «близко к реальному времени» PT10S
loyalty.siem.batch-size LOYALTY_SIEM_BATCH_SIZE Записей журнала за один тик 500
loyalty.siem.timeout LOYALTY_SIEM_TIMEOUT Ожидание приёмника; истёк — попытка неудачна, курсор не двигается PT5S
loyalty.siem.lag LOYALTY_SIEM_LAG Отступ верхней границы выборки от текущего момента (транзакция становится видимой позже своей отметки времени) PT5S
loyalty.siem.facility LOYALTY_SIEM_FACILITY Категория источника syslog 13
loyalty.siem.product LOYALTY_SIEM_PRODUCT Имя продукта в заголовке события loyalty-billing

Буфер — сам журнал аудита

Поток не заводит отдельную очередь: буфером служит сам (durable) журнал аудита, хранится только курсор — докуда отдано. Пока приёмник недоступен, поток стоит и продолжается с того же места после починки; обрыв на полуслове даёт повтор последней пачки (SIEM дедуплицирует события, повтор безопаснее пропуска). Выключено по умолчанию: адрес приёмника знает только контур заказчика.

Аудит: защита журнала от подделки

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.audit.seal.enabled LOYALTY_AUDIT_SEAL_ENABLED Периодические печати журнала (цепочка отпечатков, делающая подделку записи обнаружимой) false
loyalty.audit.seal.tick LOYALTY_AUDIT_SEAL_TICK Как часто ставится печать. Вместе с lag задаёт окно lag + 2 × tick, старше которого непокрытая запись попадает в uncovered PT5M
loyalty.audit.seal.lag LOYALTY_AUDIT_SEAL_LAG Не печатать записи моложе этого возраста (верхняя граница печати обязана быть заведомо закоммиченной) PT1M
loyalty.audit.seal.key LOYALTY_AUDIT_SEAL_KEY Секрет HMAC для отпечатка. Обязателен, если enabled=true — без него старт падает —
loyalty.audit.seal.verify-cron LOYALTY_AUDIT_SEAL_VERIFY_CRON Ночная проверка целостности всей цепочки печатей; она же обновляет показатели loyalty.audit.seal.broken и loyalty.audit.seal.uncovered. Время — UTC 0 25 3 * * *

Включать только с секретом

Отпечаток — HMAC с секретом из окружения, а не голый хеш: журнал и цепочка печатей лежат в той же базе, которую защищают, и без секрета отпечаток пересчитывается общедоступным алгоритмом без всякой защиты. Включённая подсистема без LOYALTY_AUDIT_SEAL_KEY не поднимется — приложение остановится с понятной ошибкой при старте, а не отчитается «журнал цел», будучи неспособной это установить. Смена ключа обесценивает ранее поставленные печати — это долгоживущий секрет контура, а не предмет ротации.

Уровни: пересмотр и событие смены уровня

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.tier-review.cron LOYALTY_TIER_REVIEW_CRON Пересмотр уровней в личную дату клиента: понижение и продление периода происходят только здесь, здесь же снимается ручное назначение уровня. Расписание — в поясе среды исполнения (UTC) выключено (в примерах чарта задано только для dev — 0 20 3 * * *; для qa и prod не задано)
loyalty.tier-review.batch-size LOYALTY_TIER_REVIEW_BATCH_SIZE Клиентов в пачке захвата; каждый клиент — своя транзакция 500
loyalty.tier-review.max-duration LOYALTY_TIER_REVIEW_MAX_DURATION Предельная длительность одного запуска; остаток ждёт следующего PT1H
loyalty.tier-events.enabled LOYALTY_TIER_EVENTS_ENABLED Отправка события смены уровня tiers.customer.tier-changed в брокер false
loyalty.tier-events.topic LOYALTY_TIER_EVENTS_TOPIC Топик события loyalty.tiers.tier-changed
loyalty.tier-events.tick / .batch-size LOYALTY_TIER_EVENTS_TICK / LOYALTY_TIER_EVENTS_BATCH_SIZE Период и размер пачки отправителя PT5S / 200
loyalty.tier-events.publish-timeout LOYALTY_TIER_EVENTS_PUBLISH_TIMEOUT Ожидание подтверждения брокера; без подтверждения событие уйдёт следующим тиком PT10S
loyalty.tier-events.pending-ttl LOYALTY_TIER_EVENTS_PENDING_TTL Сколько неотправленное событие ждёт отправки, прежде чем его спишут без доставки P7D
loyalty.tier-events.retention LOYALTY_TIER_EVENTS_RETENTION Сколько отправленные и списанные события хранятся в очереди P7D

Без пересмотра уровни не понижаются

Пока LOYALTY_TIER_REVIEW_CRON не задан, понижение уровня и продление периода не выполняются — уровень клиента только растёт по покупкам. Задайте расписание на контуре, где действуют правила пересчёта уровней конфигурации программы. События смены уровня пишутся в очередь исходящих всегда и уходят после включения отправки; пролежавшие дольше pending-ttl не отправляются. Списание устаревших и уборку отправленных по retention выполняет сам отправитель, поэтому пока отправка выключена, очередь событий уровня (tier_event_outbox) только растёт.

Открытие счетов в новых валютах

У каждого клиента в статусе ACTIVE есть счёт в каждой включённой валюте. При создании включённой валюты и включении выключенной фоновая задача открывает счёт каждому активному клиенту; при выкладке версии разовый прогон ставится по всем уже включённым валютам.

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.account-opening.enabled LOYALTY_ACCOUNT_OPENING_ENABLED Фоновое открытие счетов true
loyalty.account-opening.tick LOYALTY_ACCOUNT_OPENING_TICK Период проверки очереди задач PT10S
loyalty.account-opening.batch-size LOYALTY_ACCOUNT_OPENING_BATCH_SIZE Клиентов в одной транзакции 1000
loyalty.account-opening.max-duration LOYALTY_ACCOUNT_OPENING_MAX_DURATION Предельная длительность одного тика PT50S

Задача безопасна на нескольких репликах и продолжается после перезапуска пода. Валюта, в которой счёт открыть не на что (нет базового субсчёта или плана счетов), заведению клиента не мешает: такие случаи считает показатель loyalty.accounts.opening.unopenable (см. «Наблюдаемость»), а задача откроет счета после исправления.

Сегменты клиентов из внешней системы

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.segments.max-members-per-request LOYALTY_SEGMENTS_MAX_MEMBERS_PER_REQUEST Предел участников в одной пачке заливки (больше — 413 segment_batch_too_large); предела числа клиентов в сегменте нет 10000
loyalty.segments.complete-statement-timeout LOYALTY_SEGMENTS_COMPLETE_STATEMENT_TIMEOUT Таймаут запроса, применяющего состав при завершении заливки 10m
loyalty.segments.history-retention LOYALTY_SEGMENTS_HISTORY_RETENTION Срок хранения истории входов и выходов клиентов P3Y
loyalty.segments.fill-retention LOYALTY_SEGMENTS_FILL_RETENTION Срок хранения журнала заливок P1Y
loyalty.segments.cleanup-cron LOYALTY_SEGMENTS_CLEANUP_CRON Уборка по срокам хранения (UTC) выключено (в примерах чарта — 0 50 3 * * *)

Приём сегментов описан в разделе «Интеграции» → «Сегменты клиентов из внешней системы».

Профиль клиента: источники данных карточки

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.client-profile.source-schemas LOYALTY_CLIENT_PROFILE_SOURCE_SCHEMAS Белый список схем БД (через запятую), из которых администратор выбирает таблицы-источники карточек и таблиц профиля клиента пусто — выбор источников выключен
loyalty.client-profile.max-bulk-ids LOYALTY_CLIENT_PROFILE_MAX_BULK_IDS Предел пакетного удаления полей и версий настройки карточки (больше — 400 bulk_size_exceeded) 200

Чтение чужих таблиц — только из белого списка

Источники карточки — таблицы смежных систем в той же базе. Приложение видит только схемы из LOYALTY_CLIENT_PROFILE_SOURCE_SCHEMAS и только то, на что у роли приложения в БД есть права; строки связываются с клиентом по строковой колонке external_id. Выдавайте роли приложения права на чтение ровно тех таблиц, которые нужны карточке.

Уведомления клиентам: очередь исходящих

Сообщения клиенту (предупреждения о сгорании, сообщение о факте сгорания, уведомления о начислении и списании) ставятся в очередь исходящих outbound_notification в той же транзакции, что и сам факт: откат операции уносит и сообщение, касса не ждёт канал доставки. Отправитель публикует созревшие сообщения событием notifications.customer.message-requested в брокер — дальше канал (SMS, push, e-mail) выбирает потребитель, например диаграмма RT Flow.

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.notifications.enabled LOYALTY_NOTIFICATIONS_ENABLED Публикация сообщений в брокер. Выключенная — сообщения копятся в очереди false
loyalty.notifications.topic LOYALTY_NOTIFICATIONS_TOPIC Топик события loyalty.notifications.customer-message
loyalty.notifications.send-hour LOYALTY_NOTIFICATIONS_SEND_HOUR Час отправки несрочных сообщений — в поясе клиента 9
loyalty.notifications.quiet-from / .quiet-to LOYALTY_NOTIFICATIONS_QUIET_FROM / LOYALTY_NOTIFICATIONS_QUIET_TO Тихие часы в поясе клиента 21 / 9
loyalty.notifications.tick / .batch-size LOYALTY_NOTIFICATIONS_TICK / LOYALTY_NOTIFICATIONS_BATCH_SIZE Период и размер пачки отправителя PT10S / 200
loyalty.notifications.max-attempts LOYALTY_NOTIFICATIONS_MAX_ATTEMPTS Попыток отправки до перевода сообщения в разбор 5
loyalty.notifications.publish-timeout LOYALTY_NOTIFICATIONS_PUBLISH_TIMEOUT Ожидание подтверждения брокера PT10S
loyalty.notifications.pending-ttl LOYALTY_NOTIFICATIONS_PENDING_TTL Срок годности неотправленного сообщения P7D
loyalty.notifications.valid-for LOYALTY_NOTIFICATIONS_VALID_FOR Срок годности ответных сообщений (начисление, списание) P1D
loyalty.notifications.retention LOYALTY_NOTIFICATIONS_RETENTION Сколько хранятся закрытые сообщения P90D
loyalty.notifications.housekeeping-tick LOYALTY_NOTIFICATIONS_HOUSEKEEPING_TICK Период уборки очереди (списание по сроку, удаление закрытого) — работает и при выключенной отправке PT1H
loyalty.notifications.purchase-city-window LOYALTY_NOTIFICATIONS_PURCHASE_CITY_WINDOW Окно «самого частого города покупок» для определения пояса клиента P90D

Пояс клиента определяется так: пояс из профиля (приходит из мастер-системы) → город профиля по справочнику городов → самый частый город покупок за окно → пояс программы. Отправляет очередь одна реплика; ответные сообщения уходят сразу, срочные (сгорание этой ночью) не ждут утра. Сообщение, не ушедшее за max-attempts попыток, переходит в разбор: GET /v1/admin/notifications (роль LOYALTY_NOTIFICATION_READ), вернуть в очередь или снять с основанием — POST /v1/admin/notifications/{id}/retry и .../discard (роль LOYALTY_NOTIFICATION_WRITE). Города профиля, по которым пояс не определился, показывает GET /v1/admin/timezones/unmatched-cities (роль LOYALTY_DICTIONARY_READ) — по нему дополняют справочник городов при внедрении. Экрана разбора очереди в интерфейсе нет.

Включайте отправку после настройки потребителя

Включайте LOYALTY_NOTIFICATIONS_ENABLED только после того, как на брокере заведён топик и потребитель готов доставлять сообщения. Сообщения, пролежавшие в очереди дольше pending-ttl, при включении не уходят разом, а списываются.

Мобильный кошелёк

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.wallet.enabled LOYALTY_WALLET_ENABLED Выпуск карты участника в мобильный кошелёк false
loyalty.wallet.pass-type-identifier LOYALTY_WALLET_PASS_TYPE_ID Pass Type ID сертификата Apple pass.ru.example.loyalty
loyalty.wallet.team-identifier LOYALTY_WALLET_TEAM_ID Team ID Apple Developer TEAMID0000
loyalty.wallet.google-issuer-id LOYALTY_WALLET_GOOGLE_ISSUER_ID Issuer ID эмитента Google Wallet 0000000000000
loyalty.wallet.origins LOYALTY_WALLET_ORIGINS Домены, которым разрешено сохранять карту в Google Кошелёк (через запятую) —

Подпись — заглушка

Пока сертификат Apple (Pass Type ID) и ключ сервисного аккаунта Google не подключены (секреты заказчика передаются адаптеру подписи отдельно, не через эти переменные), выпущенный пакет Apple структурно верен, но не подписан (заголовок ответа X-Loyalty-Pass-Signed: false), а ссылка «Сохранить в Google Кошелёк» не собирается вовсе. Включайте loyalty.wallet.enabled только вместе с реальным адаптером подписи.

Реферальная связь и контакты клиента

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.contact.phone.default-country LOYALTY_CONTACT_PHONE_COUNTRY Код страны для нормализации телефона 7
loyalty.contact.phone.canonical-form LOYALTY_CONTACT_PHONE_FORM Каноничная форма телефона: PLUS (+7XXXXXXXXXX) или LOCAL (8XXXXXXXXXX) PLUS

Реферальный код клиента генерируется приложением автоматически при создании клиента — отдельных настроек не требует.

Валюты и обмен баллов

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.currency.exchange.mapping — Соответствие валюта → счета пополнения/депозита для операций обмена (см. таблицу выше) PTS → MRK, RUB → RUB_REVENUE

Справочник валют — источник истины по курсу

Список валют (деньги и баллы одной записью, тип MONEY/POINTS), их состояние и история курса ведутся справочником /v1/admin/currencies (роли LOYALTY_DICTIONARY_READ/WRITE — устаревший /v1/admin/point-currencies работает поверх того же справочника для обратной совместимости). Курс валюты баллов — не переменная окружения, а запись справочника. Бюджет акций и денежная оценка обязательств в отчёте по баллам считаются по курсу валюты баллов программы (points.currency). Отдельного свойства валюты бюджета больше нет: loyalty.promotion.budget.point-currency и customer.default.currency не читаются, и если они заданы на контуре, их можно убрать. Пары обмена валют ведутся справочником /v1/admin/exchange-pairs (роли LOYALTY_DICTIONARY_READ/WRITE), смена курса пары пишется в журнал аудита.

Леджер: партиционирование

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.ledger.partitions.months-ahead LOYALTY_LEDGER_PARTITIONS_AHEAD На сколько месяцев вперёд держать нарезанные партиции 12
loyalty.ledger.partitions.warn-runway-days LOYALTY_LEDGER_PARTITIONS_WARN_DAYS Порог предупреждения о коротком запасе нарезки 90
loyalty.ledger.partitions.interval LOYALTY_LEDGER_PARTITIONS_INTERVAL Как часто проверять нарезку PT6H
loyalty.ledger.partitions.integrity-cron LOYALTY_LEDGER_INTEGRITY_CRON Ночная проверка уникальности идентификатора проводки и отсутствия лотов-сирот 0 15 3 * * *

Генератор идентификаторов (snowflake)

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.id-generator.snowflake.instance-id-bits — Разрядность номера реплики в идентификаторе 10
loyalty.id-generator.snowflake.sequence-bits — Разрядность порядкового номера внутри миллисекунды 12
loyalty.id-generator.snowflake.generator-epoch — Эпоха генератора (момент начала отсчёта) 2026-01-01T00:00:00.000Z
loyalty.id-generator.lease.ttl — Срок аренды слота instance-id; протухший слот переиспользуется PT10M
loyalty.id-generator.lease.heartbeat — Период продления аренды. Инвариант: ttl ≥ 3 × heartbeat (проверяется при старте) PT1M

Эти параметры затрагивают формат идентификаторов и совместимость с историческими данными — менять их на контуре с накопленными данными не следует.

Обмен данными с внешними системами

Свойство Переменная окружения Назначение Значение по умолчанию
loyalty.data-exchange.lag LOYALTY_DATA_EXCHANGE_LAG Отступ верхней границы ленты изменений и заданий выгрузки от текущего момента PT5M
loyalty.data-exchange.tick LOYALTY_DATA_EXCHANGE_TICK Как часто проверять, не пора ли запустить задание регламента (за тик берётся одно задание) PT30S
loyalty.data-exchange.dtp.enabled LOYALTY_DTP_ENABLED Переливка витрин в целевую БД потребителя (DTP) false
loyalty.data-exchange.dtp.target.url / .username / .password LOYALTY_DTP_TARGET_URL / LOYALTY_DTP_TARGET_USER / LOYALTY_DTP_TARGET_PASSWORD Подключение к целевой БД —
loyalty.data-exchange.dtp.processes.<витрина>.batch-size LOYALTY_DTP_BATCH Размер пачки при переливке (пример для DWH_TRANSACTIONS) 1000

Каталог витрин (datasets) и запрос вставки в целевую БД (merge-statement) задаются конфигурацией — это позволяет добавлять новые витрины без правки кода. Задания выгрузки в поставке засеяны выключенными (DWH_TRANSACTIONS, DWH_BALANCES, DWH_CUSTOMERS, ежечасное расписание) — включение требует настроенного приёмника. Расписание (cron) заданий трактуется в поясе программы.

Витрина проводок DWH_TRANSACTIONS отдаёт клиента и субсчёт по каждой ноге проводки — debitCustomerExternalId/creditCustomerExternalId и debitSubaccountCode/creditSubaccountCode (у системной ноги поле пустое): так в витрине видны обе стороны перевода баллов между клиентами. Запрос вставки в целевую БД при подключении приёмника задаётся под этот состав полей.

Запрос вставки DWH_TRANSACTIONS по умолчанию нужно заменить

Поставляемый в конфигурации приложения merge-statement витрины DWH_TRANSACTIONS ещё ссылается на прежние поля :customerExternalId и :subaccountCode, которых в витрине больше нет: с ним переливка этой витрины завершится ошибкой. Перед включением LOYALTY_DTP_ENABLED задайте собственный loyalty.data-exchange.dtp.processes.DWH_TRANSACTIONS.merge-statement под поля по ногам проводки и под таблицу приёмника.

Внешний справочник (dictionary-api) и MDM

Справочники работают в гибридном режиме: операционные справочники — локально (полный CRUD), а внешние soft-словари (мастер-данные) могут читаться из внешнего dictionary-api в режиме read-only. Сейчас ни один справочник продукта из dictionary-api не читается: товарные категории и товары — собственный каталог продукта (API /v1/admin/product-categories и /v1/admin/product-items с CSV-импортом; экрана в веб-интерфейсе пока нет), а сегменты клиентов приходят из внешней системы через интеграционный API (см. «Интеграции»).

Переменная Назначение Значение по умолчанию
LOYALTY_DICTIONARY_ENABLED Включение внешнего dictionary-api false
LOYALTY_DICTIONARY_BASE_URL Базовый URL внешнего dictionary-api —
LOYALTY_MDM_ADAPTER Источник golden-профиля клиента: local (встроенная заглушка) или dataocean (Data Ocean Governance MDM) local
LOYALTY_MDM_BASE_URL Базовый URL MDM (при dataocean) —
LOYALTY_MDM_ENTITY_SLUG Слаг сущности клиента (проектно-настраиваемый) customer
LOYALTY_MDM_EXTERNAL_SYSTEM Код системы-потребителя в MDM LOYALTY
LOYALTY_MDM_TOKEN_URL / _CLIENT_ID / _CLIENT_SECRET / _SCOPE Учётные данные OAuth2 client-credentials к MDM —

По умолчанию интеграция с dictionary-api выключена, и REMOTE-словари отдаются как available=false без обращения к сети. Недоступность MDM отдаётся как 503 mdm_unavailable, а не как «клиент не найден» — карточка клиента при этом показывает деградацию, а не ошибку 404.

Runtime-конфиг веб-интерфейса (loyalty-ui)

Веб-интерфейс читает конфигурацию из window._env_.loyalty, которую при старте контейнера рендерит entrypoint из переменных окружения пода (public/config.js в образе, файл перезаписывается — пересборка образа под контур не нужна).

Переменная Назначение Значение по умолчанию
API_URL Корень backend API (без хвостового /v1 дублирования — база должна оканчиваться на /v1) /loyalty/v1 (same-origin)
BASE_PATH basename роутера standalone-режима /loyalty
KEYCLOAK_URL Адрес Keycloak (только для standalone) —
KEYCLOAK_REALM Realm Keycloak (только для standalone) —
KEYCLOAK_CLIENT_ID Публичный клиент входа (общий для платформы — frontend) —
COMMIT_TAG Версия сборки (git-тег, подставляет CI); Core UI показывает её на странице «Помощь» — (пусто вне CI-сборки)
PROMOTION_APPROVAL_ENABLED Согласование запуска акции в интерфейсе: true — включено, пусто или иное значение — выключено. Задаётся в паре с LOYALTY_PROMOTION_APPROVAL_ENABLED бэкенда — (выключено; в примерах чарта — true на dev и qa, false на prod)

B2C_API_URL и B2C_TOKEN — код их читает, штатный entrypoint не рендерит

Рантайм-конфиг умеет принимать отдельный корень API (b2cApiUrl, по умолчанию — значение API_URL) и статический токен для отладочного доступа к кабинету (b2cToken), но штатный entrypoint-скрипт образа рендерит в config.js только API_URL, BASE_PATH, KEYCLOAK_*, COMMIT_TAG и PROMOTION_APPROVAL_ENABLED. Чтобы задать B2C_API_URL/B2C_TOKEN на реальном контуре, config.js нужно переопределить отдельно (доработать entrypoint или смонтировать готовый файл).

OIDC обязателен для standalone-режима

В standalone-режиме (без встраивания в Core UI) все три параметра KEYCLOAK_* обязательны — дефолтов в коде нет, их отсутствие интерфейс показывает как ошибку конфигурации при запуске. При встраивании в Core UI токен даёт хост-портал, и поля KEYCLOAK_* игнорируются.

Приоритет источников

flowchart LR
    ENV["Переменная окружения<br/>(LOYALTY_*)"] -->|переопределяет| YAML["Свойство приложения<br/>(loyalty.*)"]
    YAML -->|seed при старте| DB[("Таблицы леджера<br/>(валюты, план счетов,<br/>системные счета)")]
    DB -.->|"приоритет над env<br/>для узкого набора ключей"| CFG["system_config<br/>(правится в админке)"]

Действует стандартный порядок Spring Boot: переменная окружения переопределяет значение из файла конфигурации. Из итоговых значений на старте формируются (при отсутствии) записи в БД леджера. Отдельно от этой цепочки — узкий набор ключей system_config (см. «Операционная конфигурация в БД» выше), для которых значение из БД приоритетнее переменной окружения и правится без передеплоя.

Механики-как-параметры — в дорожной карте

Настраиваемые шаблоны механик акций («механики-как-параметры») в текущей версии не реализованы — набор механик закрыт кодом. Это отдельный пункт дорожной карты, не пересекающийся с операционной конфигурацией выше.