Каталог переменных окружения¶
Полный справочник env-переменных Payment Manager: типы, дефолты, обязательность, место использования и пример значения. Источник истины — src/shared/config.ts (Zod-схема). Этот документ — отражение схемы, а не её замена: при расхождении приоритет у кода.
Назначение¶
Payment Manager инициализирует конфигурацию один раз при старте процесса через envSchema.safeParse(process.env) в src/shared/config.ts. Любая невалидная или отсутствующая обязательная переменная приводит к немедленному падению с диагностическим сообщением (Invalid environment variables: ...).
Каталог ниже покрывает все 28 переменных, объявленных в config.ts, сгруппированных по функциональным областям, а также явно фиксирует расхождения с .env.example.
Таблица env vars¶
| Имя | Тип | Дефолт | Обязательная | Описание | Где используется | Пример |
|---|---|---|---|---|---|---|
NODE_ENV |
enum (development | test | production) |
development |
нет | Режим работы процесса; влияет на логирование, hot-reload и поведение тестов. | src/shared/config.ts, src/server.ts |
production |
PORT |
number |
3000 |
нет | TCP-порт HTTP-сервера Fastify (входящие /intents, admin endpoints, healthchecks). |
src/server.ts |
3000 |
DATABASE_URL |
string |
— | да | DSN PostgreSQL (через PgBouncer). PM использует search_path=pm,public; запись только в pm.*. |
src/db/client.ts, миграции Drizzle |
postgres://pm_user:pm_password@localhost:5433/onewallet |
REDIS_URL |
string |
— | да | Основной Redis для pub/sub (intent.{id}), кешей и временных структур координации. |
src/shared/redis.ts, intent streamer |
redis://localhost:6379 |
NOTIFICATIONS_REDIS_URL |
string |
— | нет | Отдельный Redis-инстанс для очереди уведомлений (stream.notifications.jobs). Если не задан — используется основной REDIS_URL. |
src/notifications/* |
redis://notifications-redis:6379 |
ADMIN_SECRET |
string (≥32) |
— | да | Bearer-токен для admin-эндпоинтов PM (диагностические запросы, ручные ремедиации). | src/admin/*, middleware requireAdmin |
change-me-to-a-strong-secret-32chars |
TB_ADDRESS |
string (≥10) |
— | да | Адрес TigerBeetle replica (формат host:port). PM — единственный клиент TB на write. |
src/ledger/tb-client.ts |
127.0.0.1:3001 |
OUTBOX_INTERVAL_MS |
number |
1000 |
нет | Период опроса таблицы pm.outbox воркером OutboxWorker. |
src/workers/outbox-worker.ts |
1000 |
IPPS_BASE_URL |
string (url) |
https://promptpay-api-sit.ipps.cloud |
нет | Базовый URL IPPS PromptPay eXchange (SIT/PROD). | src/psp/ipps/driver.ts |
https://promptpay-api.ipps.cloud |
IPPS_API_KEY |
string |
test-api-key |
нет | API-ключ IPPS, выдаётся командой интеграции. В продакшене обязателен. | src/psp/ipps/driver.ts |
sit-xxxxxxxxxxxxxxxx |
IPPS_PARTNER_ID |
string |
00 |
нет | Идентификатор партнёра IPPS. | src/psp/ipps/driver.ts |
00 |
IPPS_HTTP_TIMEOUT_MS |
number (>0) |
8000 |
нет | Таймаут HTTP-запросов к IPPS. | src/psp/ipps/driver.ts |
8000 |
WORKER_ROLES |
string→string[] (CSV) |
api,outbox-worker |
нет | Список ролей, которые запускает данный процесс. Допустимые значения: api, outbox-worker, psp-worker, invoice-expiry. |
src/server.ts (роутинг bootstrap) |
api,outbox-worker,psp-worker |
PSP_NAMES |
string→string[] (CSV) |
IPPS |
нет | Список PSP, обрабатываемых psp-worker-ролью. |
src/psp/worker.ts |
IPPS |
BALANCE_MONITOR_ENABLED |
boolean (строка 'true' | 'false') |
false |
нет | Включает фоновый мониторинг баланса IPPS-аккаунта (drift и low-balance алерты). | src/psp/ipps/balance-monitor.ts |
true |
PSP_POLL_INTERVAL_MS |
number (>0) |
500 |
нет | Интервал опроса очереди pm.psp_tx_map воркером PSP (FOR UPDATE SKIP LOCKED). |
src/psp/worker.ts |
500 |
PSP_LEASE_SEC |
number (>0) |
10 |
нет | Длительность аренды (lease) задачи PSP при первичной обработке. | src/psp/worker.ts |
10 |
PSP_RETRY_LEASE_SEC |
number (>0) |
30 |
нет | Длительность аренды при ретрае (более длинная — даёт время IPPS ответить inquiry). | src/psp/worker.ts |
30 |
PSP_MAX_RETRIES |
number (>0) |
3 |
нет | Максимум попыток PSP-операции перед переводом в DEAD. |
src/psp/worker.ts |
3 |
INVOICE_QR_SECRET |
string (≥32) |
— | да | Секрет HMAC-подписи QR-кодов merchant-инвойсов. Утечка = возможность подделки QR. | src/invoice/qr-signer.ts |
change-me-to-strong-invoice-qr-secret-min32chars |
INVOICE_DEFAULT_TTL_SECONDS |
number (>0) |
600 |
нет | TTL инвойса по умолчанию (10 минут), если merchant не указал явно. | src/invoice/create.ts |
600 |
INVOICE_MAX_TTL_SECONDS |
number (>0) |
3600 |
нет | Верхний предел TTL инвойса (защита от «вечных» инвойсов). | src/invoice/create.ts |
3600 |
INVOICE_EXPIRY_SWEEP_INTERVAL_MS |
number (>0) |
30000 |
нет | Период сканирования просроченных инвойсов воркером invoice-expiry. |
src/invoice/expiry-worker.ts |
30000 |
INVOICE_EXPIRY_BATCH_SIZE |
number (>0) |
100 |
нет | Размер пачки инвойсов, переводимых в EXPIRED за один проход sweep. |
src/invoice/expiry-worker.ts |
100 |
BALANCE_TICK_MS |
number (>0) |
60000 |
нет | Период тика balance-monitor (сравнение TB vs IPPS). | src/psp/ipps/balance-monitor.ts |
60000 |
IPPS_DRIFT_THRESHOLD_SATANG |
number (≥0) |
10000 (100 THB) |
нет | Допустимое расхождение баланса TB↔IPPS в сатангах; превышение => алерт. | src/psp/ipps/balance-monitor.ts |
10000 |
IPPS_LOW_BALANCE_THRESHOLD_SATANG |
number (≥0) |
100000 (1000 THB) |
нет | Порог «низкого баланса» IPPS-аккаунта в сатангах; ниже => алерт. | src/psp/ipps/balance-monitor.ts |
100000 |
SERVICE_SECRETS |
string→object (JSON) |
{} |
нет (де-факто да) | JSON-карта { serviceId: plainSecret } для верификации HMAC входящих запросов. Без записей PM отвергнет любые POST /intents. |
src/auth/hmac.ts, middleware verify |
{"auth-center":"...","nginx-gateway":"..."} |
Расхождения с .env.example¶
Открытый вопрос: ниже перечислены env-переменные, объявленные в
src/shared/config.ts, но отсутствующие в.env.example. Решение об обновлении.env.exampleпринимается отдельно — этот документ только фиксирует факт.
Объявлены в config.ts, но не упомянуты в .env.example:
NOTIFICATIONS_REDIS_URLTB_ADDRESS— критично: обязательная переменная без дефолта; локальный запуск без неё невозможен.OUTBOX_INTERVAL_MSWORKER_ROLESPSP_NAMESBALANCE_MONITOR_ENABLEDPSP_POLL_INTERVAL_MSPSP_LEASE_SECPSP_RETRY_LEASE_SECPSP_MAX_RETRIESBALANCE_TICK_MSIPPS_DRIFT_THRESHOLD_SATANGIPPS_LOW_BALANCE_THRESHOLD_SATANG
Упомянуты в .env.example, но отсутствуют в config.ts:
- расхождений нет.
Открытый вопрос:
SERVICE_SECRETSимеет дефолт'{}'вconfig.ts, но в.env.exampleприведён непустой набор dev-секретов. Без хотя бы одного валидного entry PM не сможет проверить HMAC ни одного клиента — фактически переменная обязательна для работы, хотя формально опциональна. Документировать ли это как breaking dependency — открытый вопрос.
Группы env по назначению¶
HMAC / Auth¶
SERVICE_SECRETS— JSON-карта серверных секретов:{"<serviceId>": "<plainSecret>"}. Используется middleware верификации HMAC при каждом входящем запросе (X-Service-Id,X-Timestamp,X-Signature). Минимум один entry должен быть задан, иначе ни один внешний сервис не сможет обратиться к PM.ADMIN_SECRET— отдельный bearer-токен для admin-эндпоинтов PM (диагностика, ручные ремедиации). Не связан с HMAC; передаётся в заголовкеAuthorization: Bearer <secret>. Минимум 32 символа.
Database / Redis / TigerBeetle¶
DATABASE_URL— PostgreSQL через PgBouncer. PM пишет только в схемуpm, читает изpublic(Auth Center). Миграции —drizzle-kit migrate.REDIS_URL— основной Redis для publish событийintent.{id}(Auth Center подписывается через nginx) и временных координационных структур.NOTIFICATIONS_REDIS_URL— опциональный отдельный Redis для очередиstream.notifications.jobs. Если не задан, PM использует основнойREDIS_URL.TB_ADDRESS— адрес TigerBeetle replica. PM — единственный клиент TB на write-операции (createAccounts, createTransfers, two-phase post/void). Read-only клиенты (Admin Panel) подключаются отдельно.
IPPS PSP¶
IPPS_BASE_URL— базовый URL IPPS PromptPay eXchange API (SIT для интеграции, PROD для боевого режима).IPPS_API_KEY— API-ключ, выдаётся командойintegration@ipps.cloud.IPPS_PARTNER_ID— идентификатор партнёра в системе IPPS.IPPS_HTTP_TIMEOUT_MS— таймаут HTTP-запросов; короткий таймаут защищает worker от зависших соединений (IPPS-операции не идемпотентны — таймаут != неуспех, см. NO-GO вCLAUDE.md).IPPS_DRIFT_THRESHOLD_SATANG— порог расхождения баланса между TB и IPPS, при превышении которого balance-monitor генерирует алерт. По умолчанию 10 000 сатангов = 100 THB.IPPS_LOW_BALANCE_THRESHOLD_SATANG— порог низкого баланса IPPS-аккаунта; ниже него — алерт оператору. По умолчанию 100 000 сатангов = 1000 THB.
Workers¶
WORKER_ROLES— CSV-список ролей, запускаемых процессом. Допустимые:api,outbox-worker,psp-worker,invoice-expiry. Один процесс может объединять несколько ролей; для масштабирования — разнести по отдельным деплоям.PSP_NAMES— список имён PSP, обрабатываемыхpsp-worker. Сейчас единственный реальный PSP —IPPS.BALANCE_MONITOR_ENABLED— флаг включения фонового monitor'а баланса IPPS (используетBALANCE_TICK_MS,IPPS_DRIFT_THRESHOLD_SATANG,IPPS_LOW_BALANCE_THRESHOLD_SATANG).BALANCE_TICK_MS— частота тика balance-monitor.OUTBOX_INTERVAL_MS— частота опросаpm.outboxворкером OutboxWorker. NO-GO: запускать одновременно более одного экземпляра OutboxWorker запрещено.PSP_POLL_INTERVAL_MS— частота опроса очередиpm.psp_tx_mapворкером PSP. Lock реализован черезFOR UPDATE SKIP LOCKED(см. NO-GO: IPPS не идемпотентен, нельзя ретраить transfer — только inquiry).PSP_LEASE_SEC— аренда задачи при первичной обработке. Короткая, чтобы быстро вернуть задачу в очередь при падении воркера.PSP_RETRY_LEASE_SEC— аренда при ретрае. Длиннее, потому что ретрай делает inquiry, а IPPS может отвечать с задержкой.PSP_MAX_RETRIES— лимит попыток PSP-операции. После исчерпания задача переводится в терминальный статусDEADи требует ручного разбора.
Invoice¶
INVOICE_QR_SECRET— секрет HMAC-подписи QR-кодов merchant-инвойсов. Минимум 32 символа. Утечка позволяет подделывать QR — хранить наравне с приватными ключами.INVOICE_DEFAULT_TTL_SECONDS— TTL по умолчанию (10 минут), применяется если merchant не указалttlSecondsпри создании.INVOICE_MAX_TTL_SECONDS— верхний предел TTL (1 час), защищает от создания «вечных» инвойсов.INVOICE_EXPIRY_SWEEP_INTERVAL_MS— период sweep'а воркеромinvoice-expiry: сканируетpm.invoiceсstatus=PENDING AND expires_at < now()и переводит вEXPIRED.INVOICE_EXPIRY_BATCH_SIZE— размер пачки за один sweep. Балансировка: маленькая пачка — меньше lock contention, большая — меньше overhead на цикл.
Связанные документы¶
docs/TECHNICAL.md— общее описание модулей.docs/INTEGRATIONS.md— детали по TigerBeetle / IPPS / Redis.docs/DEPLOYMENT.md— production env-конфиг и Docker.CLAUDE.md(root и проектный) — NO-GO правила, использующие эти env'ы.