Вспомогательные сервисы¶
Монорепозиторий Univex содержит три вспомогательных микросервиса: email, sms-gate и sumsub-integration. Каждый реализован как самостоятельный сервис со своей базой данных и транспортным слоем.
Email Service¶
Назначение¶
Сервис отправки электронных писем. Используется UnivexID для уведомлений: подтверждение регистрации, смена пароля, OTP-коды, уведомления о безопасности и т.д.
Архитектура¶
┌──────────────────────────────────────────────┐
│ gRPC Server │
│ SendMessage (приём запросов напрямую) │
└────────────────┬─────────────────────────────┘
│
┌────────────────▼─────────────────────────────┐
│ SQS Queue Worker │
│ Читает задания на отправку из очереди │
│ (асинхронная обработка) │
└────────────────┬─────────────────────────────┘
│
┌────────────────▼─────────────────────────────┐
│ SMTP Client │
│ Отправка письма через SMTP-сервер │
└──────────────────────────────────────────────┘
Сервис поддерживает два режима приёма задач:
- gRPC: синхронный вызов SendMessage — помещает задачу в очередь SQS.
- SQS Worker: асинхронный воркер читает задачи из очереди и выполняет отправку через SMTP.
База данных¶
2 миграции PostgreSQL:
| Таблица | Назначение |
|---|---|
email_messages |
История отправленных писем и их статусы |
email_templates |
Шаблоны писем |
gRPC-контракт¶
Прото-файл: proto/email_service.proto
service EmailService {
rpc SendMessage(SendMessageRequest) returns (SendMessageResponse);
}
message SendMessageRequest {
string email = 1; // Email-адрес получателя
string message = 2; // Тело письма (HTML или plain text)
string subject = 3; // Тема письма
}
message SendMessageResponse {
// Пустой ответ — успех подтверждается кодом статуса gRPC
}
Коды ошибок¶
| Код | Описание |
|---|---|
INVALID_ARGUMENT |
Не указан email, тема или тело письма |
INTERNAL |
Ошибка SMTP или внутренняя ошибка сервиса |
UNAVAILABLE |
Сервис недоступен |
Конфигурация¶
| Параметр | Описание |
|---|---|
DATABASE_URL |
DSN для подключения к PostgreSQL |
GRPC_PORT |
Порт gRPC-сервера |
SQS_QUEUE_URL |
URL очереди AWS SQS |
SMTP_HOST |
Хост SMTP-сервера |
SMTP_PORT |
Порт SMTP-сервера |
SMTP_USER |
Логин SMTP |
SMTP_PASSWORD |
Пароль SMTP |
SMTP_FROM |
Email-адрес отправителя |
SMS Gate¶
Назначение¶
Шлюз для отправки SMS-сообщений. Используется для двухфакторной аутентификации (2FA), подтверждения операций, а также уведомлений. Поддерживает несколько каналов доставки: AWS SNS и Telegram.
Архитектура¶
┌──────────────────────────────────────────────┐
│ gRPC Server │
│ SendMessage (помещает задачу в SQS) │
└────────────────┬─────────────────────────────┘
│
┌────────────────▼─────────────────────────────┐
│ SQS Queue Worker │
│ Читает задания на отправку из очереди │
└──────────┬────────────────┬──────────────────┘
│ │
┌──────────▼──────┐ ┌──────▼──────────────┐
│ AWS SNS │ │ Telegram Bot │
│ (SMS через │ │ (резервный канал) │
│ провайдеров) │ │ │
└─────────────────┘ └─────────────────────┘
Сервис использует AWS SNS как основной канал доставки SMS и Telegram-бота как резервный (или альтернативный) канал для доставки кодов пользователям, у которых привязан Telegram.
База данных¶
PostgreSQL. Таблица sms_messages хранит историю отправленных сообщений и их статусы.
gRPC-контракт¶
Прото-файл: proto/sms_gate.proto
service SMSGate {
rpc SendMessage(SendSMSRequest) returns (SendSMSResponse);
rpc ResendMessage(ResendSMSRequest) returns (ResendSMSResponse);
rpc GetStatus(GetSMSStatusRequest) returns (GetSMSStatusResponse);
}
message SendSMSRequest {
string phone = 1; // Номер телефона получателя (формат E.164)
string message = 2; // Текст SMS-сообщения
}
message SendSMSResponse {
string message_id = 1; // Идентификатор отправленного сообщения
}
message ResendSMSRequest {
string message_id = 1; // Идентификатор сообщения для повторной отправки
}
message ResendSMSResponse {
string message_id = 1; // Идентификатор нового сообщения
}
message GetSMSStatusRequest {
string message_id = 1; // Идентификатор сообщения
}
message GetSMSStatusResponse {
SMSStatus status = 1;
}
enum SMSStatus {
SMS_STATUS_UNSPECIFIED = 0;
SMS_STATUS_PENDING = 1; // Ожидает отправки
SMS_STATUS_SENT = 2; // Отправлено оператору
SMS_STATUS_DELIVERED = 3; // Доставлено получателю
SMS_STATUS_FAILED = 4; // Ошибка доставки
}
Коды ошибок¶
| Код | Описание |
|---|---|
INVALID_ARGUMENT |
Не указан телефон или текст сообщения |
NOT_FOUND |
Сообщение с указанным message_id не найдено |
INTERNAL |
Ошибка SMS-провайдера |
UNAVAILABLE |
Сервис недоступен |
Конфигурация¶
| Параметр | Описание |
|---|---|
DATABASE_URL |
DSN для подключения к PostgreSQL |
GRPC_PORT |
Порт gRPC-сервера |
SQS_QUEUE_URL |
URL очереди AWS SQS |
AWS_REGION |
Регион AWS |
AWS_ACCESS_KEY_ID |
Ключ доступа AWS |
AWS_SECRET_ACCESS_KEY |
Секретный ключ AWS |
TELEGRAM_BOT_TOKEN |
Токен Telegram-бота |
Sumsub Integration¶
Назначение¶
Сервис интеграции с платформой KYC-верификации Sumsub. Предоставляет gRPC-интерфейс для получения токена доступа к виджету верификации, а также принимает входящие вебхуки от Sumsub через встроенный HTTP-сервер на Fiber.
Архитектура¶
┌────────────────────────────────────────────────┐
│ gRPC Server │
│ GetAccessToken — токен для виджета │
│ GetApplicantInformation — данные заявителя │
│ GetDocumentImage — изображение документа │
└────────────────────┬───────────────────────────┘
│
┌────────────────────▼───────────────────────────┐
│ Sumsub API Client │
│ Обращение к Sumsub REST API │
└────────────────────────────────────────────────┘
┌────────────────────────────────────────────────┐
│ Fiber HTTP Server │
│ POST /webhook — приём событий от Sumsub │
│ (верификация подписи HMAC-SHA256) │
└────────────────────┬───────────────────────────┘
│
┌────────────────────▼───────────────────────────┐
│ Webhook Handler │
│ Обработка TerminalStatusEvent │
│ Обновление статуса KYC в БД │
└────────────────────────────────────────────────┘
Процесс KYC-верификации¶
┌──────────────┐ ┌──────────────┐ ┌───────────────┐
│ Клиент │ │ UnivexID │ │ Sumsub │
│ (браузер) │ │ Service │ │ Integration │
└──────┬───────┘ └──────┬───────┘ └───────┬───────┘
│ │ │
│ Запрос начать KYC │ │
│───────────────────────>│ │
│ │ GetAccessToken(userId) │
│ │────────────────────────>│
│ │ │
│ │ {access_token} │
│ │<────────────────────────│
│ {access_token} │ │
│<───────────────────────│ │
│ │ │
│ Инициализация │ │
│ Sumsub Widget │ │
│ с токеном │ │
│ │ │
│ Прохождение │ │
│ верификации │ │
│────────────────────────────────────────────────> │
│ │ │
│ POST /webhook (TerminalStatusEvent) │
│ │<────────────────────────│
База данных¶
1 миграция PostgreSQL: таблица applicants хранит данные заявителей Sumsub, привязанные к user_id.
gRPC-контракт¶
Прото-файл: proto/sumsub_integration.proto
service SumsubIntegration {
rpc GetAccessToken(GetAccessTokenRequest) returns (GetAccessTokenResponse);
rpc GetApplicantInformation(GetApplicantInformationRequest) returns (GetApplicantInformationResponse);
rpc GetDocumentImage(GetDocumentImageRequest) returns (GetDocumentImageResponse);
}
message GetAccessTokenRequest {
string user_id = 1; // Идентификатор пользователя в системе Univex
string level = 2; // Уровень верификации Sumsub (напр. "basic-kyc-level")
}
message GetAccessTokenResponse {
string access_token = 1; // Токен доступа для виджета Sumsub
int64 expires_at = 2; // Unix timestamp истечения токена
}
message GetApplicantInformationRequest {
string user_id = 1; // Идентификатор пользователя в системе Univex
}
message GetApplicantInformationResponse {
string applicant_id = 1; // Идентификатор заявки в Sumsub
string first_name = 2; // Имя из документа
string last_name = 3; // Фамилия из документа
string date_of_birth = 4; // Дата рождения (ISO 8601)
string country = 5; // Код страны (ISO 3166-1 alpha-2)
string kyc_status = 6; // Текущий статус верификации
}
message GetDocumentImageRequest {
string user_id = 1; // Идентификатор пользователя в системе Univex
string inspection_id = 2; // Идентификатор инспекции в Sumsub
string image_id = 3; // Идентификатор изображения
}
message GetDocumentImageResponse {
bytes image_data = 1; // Бинарные данные изображения
string content_type = 2; // MIME-тип (напр. "image/jpeg")
}
Webhook: TerminalStatusEvent¶
При получении события от Sumsub сервис:
- Верифицирует HMAC-SHA256 подпись запроса.
- Десериализует событие
TerminalStatusEvent. - Обновляет статус KYC заявителя в БД.
- При необходимости — инициирует следующие шаги (уведомление UnivexID).
message TerminalStatusEvent {
string applicant_id = 1; // Идентификатор заявки в Sumsub
string user_id = 2; // Идентификатор пользователя в Univex
string first_name = 3; // Имя из документа
string last_name = 4; // Фамилия из документа
string date_of_birth = 5; // Дата рождения (ISO 8601)
string country = 6; // Код страны (ISO 3166-1 alpha-2)
KYCResult result = 7; // Результат верификации
string reject_reason = 8; // Причина отказа (если result = REJECTED)
}
enum KYCResult {
KYC_RESULT_UNSPECIFIED = 0;
KYC_RESULT_APPROVED = 1;
KYC_RESULT_REJECTED = 2;
KYC_RESULT_PENDING = 3;
}
Коды ошибок gRPC¶
| Код | Описание |
|---|---|
INVALID_ARGUMENT |
Не указан user_id или уровень верификации |
NOT_FOUND |
Заявитель не найден |
INTERNAL |
Ошибка при обращении к Sumsub API |
UNAVAILABLE |
Sumsub API недоступен |
Конфигурация¶
| Параметр | Описание |
|---|---|
DATABASE_URL |
DSN для подключения к PostgreSQL |
GRPC_PORT |
Порт gRPC-сервера |
HTTP_PORT |
Порт Fiber HTTP-сервера (webhook) |
SUMSUB_APP_TOKEN |
API-токен Sumsub |
SUMSUB_SECRET_KEY |
Секретный ключ для подписи запросов и верификации webhook |
SUMSUB_BASE_URL |
Базовый URL Sumsub API |
Сводная таблица вспомогательных сервисов¶
| Сервис | Транспорт | БД | Внешние зависимости | RPC / Эндпоинты |
|---|---|---|---|---|
email |
gRPC + SQS Worker | PostgreSQL (2 миграции) | SMTP, AWS SQS | SendMessage |
sms-gate |
gRPC + SQS Worker | PostgreSQL | AWS SNS, AWS SQS, Telegram Bot | SendMessage, ResendMessage, GetStatus |
sumsub-integration |
gRPC + Fiber webhook | PostgreSQL (1 миграция) | Sumsub API | GetAccessToken, GetApplicantInformation, GetDocumentImage |