AUTH-POLICIES — Руководство по политикам аутентификации¶
Таблица pm.auth_policies реализует policy-based step-up authentication: решение о нужном уровне
авторизации принимается динамически на основе контекста транзакции (сумма, новый получатель,
накопленный дневной объём), а не хардкодится в логике интента.
Справочник полей¶
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
id |
BIGSERIAL | авто | Первичный ключ, генерируется автоматически |
scope |
VARCHAR(80) | ✅ | Область применения (см. ниже) |
condition |
JSONB | ✅ | Условия срабатывания (см. ниже) |
required_step_up |
VARCHAR(20) | ✅ | Требуемый уровень аутентификации |
reason_code |
VARCHAR(50) | ✅ | Код причины, возвращается в ответе API |
priority |
INTEGER | ✅ (default 100) | Порядок проверки: меньше = первее |
active |
BOOLEAN | ✅ (default true) | Включено/выключено без удаления строки |
created_at |
TIMESTAMPTZ | авто | Дата создания |
updated_at |
TIMESTAMPTZ | авто | Дата последнего обновления |
Поле scope — область применения¶
global — применяется ко всем платежам
app:{appId} — только платежи из конкретного mini-app
merchant:{merchantId} — только платежи конкретному мерчанту
При оценке движок загружает все три скоупа сразу и сортирует по priority.
Можно создавать правила с одинаковым приоритетом в разных скоупах — они конкурируют между собой.
Поле required_step_up — уровни аутентификации¶
| Значение | Смысл |
|---|---|
NONE |
Подтверждение не требуется |
PIN |
Ввод PIN-кода |
OTP |
Одноразовый код (SMS/email) |
BIOMETRIC |
Биометрия (Face ID / Touch ID) |
KYC_UPLIFT |
Повышение уровня KYC пользователя |
Поле condition — поддерживаемые ключи JSONB¶
Все ключи соединяются AND — все указанные условия должны выполняться одновременно.
| Ключ | Тип значения | Описание |
|---|---|---|
amount_lt |
number | Сумма строго меньше указанного значения |
amount_gte |
number | Сумма больше или равно указанному значению |
amount_lte |
number | Сумма меньше или равно указанному значению |
daily_cumulative_gte |
number | Дневной оборот пользователя (settled DEBIT) ≥ значения |
new_payee |
boolean | true — мерчант ранее не получал платежи от этого пользователя |
currency |
string | Фиксирует валюту, например "THB" |
channel |
string | Канал платежа: INTERNAL_P2P, IPPS_TRANSFER, MERCHANT_INVOICE |
Важно: суммы хранятся в наименьших единицах валюты. Для THB: 1 бат = 100 сатангов → 100 бат =
10000, 5 000 бат =500000, 50 000 бат =5000000.
Поле priority — логика приоритетов¶
Движок берёт первое совпавшее правило и останавливается. Если нужно, чтобы строгое правило перекрывало мягкое — дай ему меньший номер.
priority 1 ← проверяется первым, наивысший приоритет
priority 5
priority 10
...
priority 100 ← default, самый низкий приоритет
Создание правил — примеры SQL¶
Микроплатежи не требуют подтверждения¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('global', '{"amount_lt": 10000}', 'NONE', 'micro_amount', 10);
PIN для новых получателей с суммой ≥ 100 бат¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('global', '{"new_payee": true, "amount_gte": 10000}', 'PIN', 'new_payee', 15);
Биометрия для крупных сумм (≥ 5 000 бат)¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('global', '{"amount_gte": 500000}', 'BIOMETRIC', 'large_amount', 30);
KYC при превышении дневного лимита (50 000 бат)¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('global', '{"daily_cumulative_gte": 5000000}', 'KYC_UPLIFT', 'daily_limit', 5);
Особое правило только для одного mini-app¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('app:my-app-id', '{"amount_gte": 50000}', 'PIN', 'app_threshold', 25);
Строгое правило для конкретного мерчанта¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('merchant:42', '{"amount_gte": 100000}', 'BIOMETRIC', 'merchant_strict', 5);
PIN только для платежей через IPPS (не для внутренних P2P)¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES ('global', '{"channel": "IPPS_TRANSFER", "amount_gte": 10000}', 'PIN', 'ipps_standard', 20);
Комбинированное условие: новый получатель + крупная сумма + валюта¶
INSERT INTO pm.auth_policies (scope, condition, required_step_up, reason_code, priority)
VALUES (
'global',
'{"new_payee": true, "amount_gte": 100000, "currency": "THB"}',
'BIOMETRIC',
'new_payee_large_thb',
8
);
Редактирование правил¶
Отключить правило (без удаления)¶
Включить обратно¶
Изменить порог суммы в condition¶
Изменить требуемый уровень аутентификации¶
Изменить приоритет¶
Удалить правило¶
Типичные ошибки¶
Перекрывающиеся диапазоны без учёта приоритета:
Политика A (priority 20): {"amount_gte": 10000, "amount_lt": 500000} → PIN
Политика B (priority 10): {"amount_gte": 50000} → BIOMETRIC
Платёж 100 000 → сработает B (priority 10), несмотря на то что A тоже подходит.
Пустой condition: {} — сработает на любой платёж.
Если такое нужно (catch-all), ставь максимальный priority (например, 999).
new_payee без merchantId в запросе — если вызывающий не передаёт merchantId,
флаг isNewPayee всегда будет false, и правило с new_payee: true никогда не сработает.
Немедленное применение изменений¶
Изменения вступают в силу немедленно — движок читает таблицу при каждом вызове
POST /policies/evaluate, кэширования нет. Это удобно для оперативной реакции на фрод,
но означает что ошибочное правило с priority: 1 и condition: {} мгновенно потребует
step-up от всех пользователей.
Связанные файлы¶
| Файл | Описание |
|---|---|
src/limits/evaluate-policy.ts |
Логика оценки политик |
src/policies/routes.ts |
HTTP endpoint POST /policies/evaluate |
src/shared/schema.ts |
Drizzle-схема таблицы и типы |
drizzle/migrations/0008_auth_policies.sql |
Миграция + базовые seed-данные |
test/limits/evaluate-policy.test.ts |
Unit-тесты логики |
test/policies/routes.test.ts |
Интеграционные тесты endpoint |