GET /api/public/crm/purchase-config — Конфигурация способов оплаты (витрина)
Витрина карточки объекта недвижимости без авторизации запрашивает конфигурацию способов оплаты застройщика
(по developerId либо propertyObjectId) — чтобы отрисовать варианты 100% оплаты, ипотеки и пресетов
рассрочки и параметры калькулятора.
Назначение
Публичная read-модель конфигурации способов оплаты застройщика для карточки объекта недвижимости витрины:
доступность и параметры 100% оплаты и ипотеки + список пресетов рассрочки с промежуточными платежами — чтобы
фронт отрисовал варианты оплаты и параметры калькулятора. Отдаётся без авторизации. Только чтение; дом
полей и семантика — purchase-program.md. Расчёт графика платежей —
отдельный метод (preview-payment-schedule.md /
get-payment-schedule.md), не этот endpoint. Привилегированный админ-аналог того же
агрегата — get-purchase-program.md (под crm.purchase-programs.manage).
Metadata
| Поле | Значение |
|---|---|
| Сервис/контекст | crm |
| API area | public (гейт витрины: анонимно) |
| Feature group | purchase-programs |
| Статус | draft |
| Документ | docs/06-services/crm/api/public/get-purchase-config.md |
Актор и доступ
| Проверка | Значение |
|---|---|
| Actor type | Anonymous (публичная карточка объекта; JWT не требуется) |
| Auth policy / Permissions | none (публичный read-only метод); rate limit по IP |
| Scope (RBAC) | не применяется (актор — вне tenant; tenant резолвится из параметра, не из JWT) |
| Record-level | не применяется |
| Tenant isolation | tenant не из JWT: DeveloperCompanyId резолвится из developerId (напрямую) либо propertyObjectId (через chessboard, шаг 3); агрегат фильтруется явным TenantId = resolvedDeveloperId |
| Audit | read (не sensitive); PII отсутствует (числовая конфигурация оплаты) |
Метод публичный: актор Anonymous, JWT не требуется, permissions/scope/record-level не применяются.
Tenant не берётся из claims — резолвится из developerId (напрямую) либо propertyObjectId (через
chessboard, шаг 3). Кодов авторизации (401/403) у метода нет.
HTTP-контракт
| Поле | Значение |
|---|---|
| Method | GET |
| Route | /api/public/crm/purchase-config?developerId={guid} или ?propertyObjectId={guid} |
| Success status | 200 |
| Idempotency header | не применяется (безопасный GET) |
| Correlation | сквозной trace через заголовок traceparent |
Входные данные / параметры запроса
Ровно один из developerId/propertyObjectId (взаимоисключающие входы витрины: карточка объекта знает
propertyObjectId, листинг застройщика — developerId).
| Поле | Источник | Тип | Обязательность | Валидация / Ограничения | Безопасность |
|---|---|---|---|---|---|
DeveloperId | query | Guid? | Условно | задан ровно один из DeveloperId/PropertyObjectId; != Guid.Empty; трактуется как DeveloperCompanyId | — |
PropertyObjectId | query | Guid? | Условно | задан ровно один из DeveloperId/PropertyObjectId; != Guid.Empty; резолвится в chessboard → DeveloperCompanyId (шаг 3); тенант вычисляется, не из claims | — |
Модель ответа PurchaseConfigModel
Публичная проекция агрегата; дом полей и семантика — purchase-program.md
(enum MilestoneKind; правило расчёта графика). Внутренние поля админ-таба (Id программы, Version,
UpdatedAt, audit) наружу не отдаются. Все enum сериализуются строкой.
| Поле | Тип | Обязательность | Источник | Masking | Описание |
|---|---|---|---|---|---|
DeveloperId | Guid | Да | resolved (query/chessboard, шаг 3) | — | Застройщик-владелец конфигурации (эхо резолва). |
FullPayment | FullPaymentConfigModel | Да | PurchaseProgramEntity | — | Условия 100% оплаты. |
Mortgage | MortgageConfigModel | Да | PurchaseProgramEntity | — | Условия ипотеки. |
InstallmentPresets | IReadOnlyList<InstallmentPresetConfigModel> | Да (может быть пустым) | live InstallmentPresetEntity (шаг 3) | — | Пресеты рассрочки, SortOrder asc. |
InstallmentPresets — именованная проекция-массив ограниченной кардинальности (не страничная выборка).
FullPaymentConfigModel
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
IsAvailable | bool | Да | IsFullPaymentEnabled | Доступна ли 100% оплата у застройщика. |
DiscountPercent | decimal | Да | FullPaymentDiscountPercent | Скидка при 100% оплате, % (0..100). |
MortgageConfigModel
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
IsAvailable | bool | Да | IsMortgageEnabled | Доступна ли ипотека у застройщика. |
AnnualRatePercent | decimal | Да | MortgageAnnualRatePercent | Годовая ставка для калькулятора, % (0..100). |
TermMonths | int | Да | MortgageTermMonths | Срок ипотеки, месяцы (1..600). |
InstallmentPresetConfigModel
Проекция InstallmentPresetEntity (дом — purchase-program.md).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
Id | Guid | Да | Id | Идентификатор пресета (для выбора способа оплаты при брони/сделке; Deal.InstallmentPresetId). |
Name | string | Да | Name | Название («Рассрочка 12 месяцев»). |
InitialPaymentPercent | decimal | Да | InitialPaymentPercent | Первоначальный взнос, % от цены (0..100). |
MonthlyFixedPayment | decimal | Да | MonthlyFixedPayment | Фиксированный ежемесячный платёж, RUB (>= 0). |
TermMonths | int | Да | TermMonths | Срок рассрочки, месяцы (1..600). |
DiscountPercent | decimal | Да | DiscountPercent | Скидка пресета от цены объекта, % (0..100). |
MilestonePayments | IReadOnlyList<MilestonePaymentModel> | Да (может быть пустым) | typed jsonb MilestonePayments.Items | Промежуточные обязательные платежи. |
SortOrder | int | Да | SortOrder | Порядок показа опций (>= 0). |
MilestonePaymentModel
Проекция элемента typed jsonb MilestonePayment (ContractVersion = 1, дом —
purchase-program.md); 1:1.
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
Month | int | Да | Month | Месяц платежа (1..TermMonths). |
Kind | MilestoneKind (enum строкой) | Да | Kind | percent / fixed / remainder. |
Value | decimal | Да | Value | Значение по Kind (percent: %; fixed: RUB; remainder: 0). |
Алгоритм
- Контекст и доступ. Публичный метод; актор
Anonymous(JWT не требуется). Tenant в контексте отсутствует — резолвится на шаге 3. - Проверка входа и предусловий. Задан ровно один из
DeveloperId/PropertyObjectIdи он!= Guid.Empty(ни одного / оба / пустой →VALIDATION_FAILED). Существование объекта/агрегата — на шаге 3. Допустимость состояния не применяется (конфигурация без статусной машины, читается всегда). Scope/record-level не применяются (аноним, публичное чтение). - Что читаем. Порядок: сначала резолв тенанта, затем агрегат.
- резолв
DeveloperCompanyId: если заданpropertyObjectId— lookup объекта вchessboard→ нет →CRM_PROPERTY_OBJECT_NOT_FOUND; из ответа берёмDeveloperCompanyId. Если заданdeveloperId—resolvedDeveloperId = developerId(внешний вызов не нужен); - живая программа покупки тенанта (единственная живая, INV-PPR-1); фильтр по тенанту — явный
(
TenantId == resolvedDeveloperId). Пусто →CRM_PURCHASE_PROGRAM_NOT_FOUND(застройщик ещё не сидирован миграцией); - живые пресеты рассрочки программы (
SortOrder asc → lower(Name) asc), с ихMilestonePaymentsиз typed jsonb.
- резолв
- Бизнес-правила и маппинг. Переходов состояний нет (чтение). Маппинг поле-в-поле
program → PurchaseConfigModel(FullPayment={IsFullPaymentEnabled, FullPaymentDiscountPercent};Mortgage={IsMortgageEnabled, MortgageAnnualRatePercent, MortgageTermMonths};DeveloperId = resolvedDeveloperId);presets → InstallmentPresetConfigModel[];MilestonePayments.Items → MilestonePaymentModel[]. EnumKindсериализуется строкой. Внутренние поля (Idпрограммы,Version,UpdatedAt, audit) в публичную проекцию не мапятся. Расчётов графика нет. - Результат.
PurchaseConfigModel→ HTTP200; при отказе — соответствующий код. Программа и пресеты согласованы на момент запроса.
Диаграмма
Побочные эффекты
| Тип | Детали |
|---|---|
| Внешние вызовы | chessboard lookup объекта по PropertyObjectId → DeveloperCompanyId (только если задан propertyObjectId; timeout 2 c, retry 1); при developerId — нет |
Ошибки и failure modes
| Код | HTTP status | Условие | Сообщение | Retry |
|---|---|---|---|---|
VALIDATION_FAILED | 400 | не задан либо заданы оба developerId/propertyObjectId, либо Guid.Empty | «Укажите застройщика или объект» | no retry |
CRM_PROPERTY_OBJECT_NOT_FOUND | 404 | propertyObjectId не найден в chessboard при резолве тенанта | «Объект не найден» | no retry |
CRM_PURCHASE_PROGRAM_NOT_FOUND | 404 | у застройщика нет живого агрегата (не сидирован миграцией) | «Программа покупки не найдена» | no retry |
Авторизационных кодов (401/403) нет — метод публичный.
Совместимость и наблюдаемость
- Публичная конфигурация оплаты для карточки объекта, без авторизации (вход по застройщику/объекту). Новые
optional поля
PurchaseConfigModel/дочерних моделей — additive (без breaking); удаление/переименование/ смена типа поля или значения enumMilestoneKind— breaking (migration + rollout). Enum сериализуется строкой; клиент обязан толерантно обрабатывать неизвестное значение. - Изменения конфигурации поднимают доменное
PurchaseProgramChangedв командах upsert (purchase-program.md); у публичного чтения интеграционных событий нет. - Logs:
purchase-config viewed(resolvedDeveloperId,propertyObjectId?, счётчик пресетов; PII отсутствует); metrics:crm_purchase_config_requests_total{result}, latency p95 < 1 c (SLO чтений); traces — сквозной trace черезtraceparent; dashboard Grafanapin-crm.
Acceptance Criteria
- AC-1 (вход по застройщику). Дано: у застройщика есть живая программа покупки. Когда: запрос с
developerId. Тогда:200сFullPayment/Mortgage-конфигом иInstallmentPresets(поSortOrder asc), без обращения кchessboard. - AC-2 (вход по объекту). Дано: объект существует в
chessboard. Когда: запрос сpropertyObjectId. Тогда:DeveloperCompanyIdрезолвится из объекта,DeveloperIdв ответе = резолву,200. - AC-3 (оба/ни одного входа). Дано: заданы оба
developerIdиpropertyObjectIdлибо ни одного (либоGuid.Empty). Когда: запрос. Тогда: отказVALIDATION_FAILED(400). - AC-4 (объект не найден). Дано:
propertyObjectIdотсутствует вchessboard. Когда: запрос. Тогда: отказCRM_PROPERTY_OBJECT_NOT_FOUND(404). - AC-5 (программа не сидирована). Дано: у застройщика нет живого агрегата программы. Когда: запрос. Тогда:
отказ
CRM_PURCHASE_PROGRAM_NOT_FOUND(404). - AC-6 (анонимный доступ). Дано: запрос без JWT. Когда: валидный вход. Тогда:
200(авторизация не требуется, кодов401/403нет); внутренние поля (Id/Version/UpdatedAt/audit) в ответ не попадают. - AC-7 (пустой список пресетов). Дано: у программы нет живых пресетов рассрочки. Когда: запрос. Тогда:
200,InstallmentPresets = [](не ошибка).
Обратные ссылки
Автоссылки: где используется этот документ.
- registries (1): Реестр API-методов PIN
Связанные документы
- Сущности: purchase-program.md (дом полей
PurchaseProgramEntity/InstallmentPresetEntity, jsonbMilestonePayments, enumMilestoneKind/PaymentMethod, INV-PPR-1, правило расчёта графика), deal.md (потребительPaymentMethod/InstallmentPresetId). - Привилегированный аналог: get-purchase-program.md; расчёт графика — preview-payment-schedule.md, get-payment-schedule.md; смежный публичный вход — create-consultation-request.md; карта областей — README.
- tenancy — ADR-0052; simplicity — ADR-0056.