Профиль: валидация, календарь, ИНН 12 цифр, email, DaData адреса, банки из BANK_IP, подсказка ИНН (ФНС)

- Backend: N8N_AUTH_WEBHOOK из env (fallback), банки из BANK_IP, эндпоинт
  /api/v1/profile/dadata/address для подсказок адресов (FORMA_DADATA_*).
- Config: bank_ip, bank_api_url, forma_dadata_api_key, forma_dadata_secret.
- Frontend Profile: DatePicker для даты рождения, ИНН 12 цифр + ссылка на ФНС,
  валидация email, чекбокс «Совпадает с адресом регистрации», AutoComplete
  адресов через DaData, Select банков из /api/v1/banks/nspk (bankId/bankName).

Подробности в CHANGELOG_PROFILE_VALIDATION.md.
This commit is contained in:
Fedor
2026-02-27 18:31:41 +03:00
parent b5c31b43dd
commit c39b12630e
6 changed files with 379 additions and 56 deletions

View File

@@ -2,6 +2,7 @@
Конфигурация приложения
"""
import os
import json
from pathlib import Path
from pydantic_settings import BaseSettings
from typing import List, Optional
@@ -138,9 +139,17 @@ class Settings(BaseSettings):
aviationstack_base_url: str = "http://api.aviationstack.com/v1"
# ============================================
# NSPK BANKS API
# NSPK BANKS API (и альтернативный BANK_IP из .env)
# ============================================
nspk_banks_api_url: str = "https://qr.nspk.ru/proxyapp/c2bmembers.json"
bank_ip: str = "http://212.193.27.93/api/payouts/dictionaries/nspk-banks"
bank_api_url: str = "http://212.193.27.93/api/payouts/dictionaries/nspk-banks"
# ============================================
# DADATA (подсказки адресов в форме профиля)
# ============================================
forma_dadata_api_key: str = "" # FORMA_DADATA_API_KEY
forma_dadata_secret: str = "" # FORMA_DADATA_SECRET
# ============================================
# SMS SERVICE (SigmaSMS)
@@ -221,11 +230,21 @@ class Settings(BaseSettings):
# ============================================
# MAX (мессенджер) — Mini App auth
# ============================================
max_bot_token: str = "" # Токен бота MAX для проверки initData WebApp
max_bot_token: str = "" # Токен бота MAX (один бот)
max_bot_tokens: str = "" # Мультибот: JSON {"bot_id": "token", ...}. Если задан — используется вместо max_bot_token.
def get_max_bot_tokens(self) -> List[tuple]:
"""Список (bot_id, token) для проверки подписи MAX initData. Один токен — [('default', token)]."""
token = (self.max_bot_token or "").strip()
"""Список (bot_id, token) для проверки подписи MAX initData. Из MAX_BOT_TOKENS (JSON) или [('default', MAX_BOT_TOKEN)]."""
s = (self.max_bot_tokens or os.environ.get("MAX_BOT_TOKENS") or "").strip()
if s:
try:
d = json.loads(s)
out = [(k, str(v).strip()) for k, v in d.items() if v and str(v).strip()]
if out:
return out
except Exception:
pass
token = (self.max_bot_token or os.environ.get("MAX_BOT_TOKEN") or "").strip()
if token:
return [("default", token)]
return []