Накладення електронного підпису за допомогою електронного підпису Приват24
Головна ідея: розробити Python-сервіс, який дозволяє користувачам підписувати документи за допомогою електронного підпису ПриватБанку / SmartID / Приват24 із подальшим збереженням документа, файлу підпису, статусу підписання, журналу дій і результату перевірки підпису.
Критично важливо: система не повинна зберігати пароль користувача до КЕП, приватний ключ або секрети підпису. Користувач підтверджує підписання у Приват24 / SmartID, а Python-сервіс зберігає тільки результат підписання, технічний статус, audit log і файл підпису.
Важливо: точні API endpoint-и, криптографічні формати, правила шифрування запитів, параметри сесії та callback потрібно брати з офіційної технічної документації ПриватБанку / SmartID, яку надають після підключення до сервісу.
Технічний стек: Python 3.11+, FastAPI, PostgreSQL, SQLAlchemy, Alembic, httpx, Pydantic, Celery/RQ/APScheduler, Redis, Docker, S3-compatible file storage.
Управлінський результат: відповідальна особа повинна бачити, які документи очікують підпису через Приват24 / SmartID, які підписані, які відхилені, які прострочені, які мають помилки підписання, які потребують повтору або ручної перевірки.
1. Мета
Метою задачі є створення Python-сервісу для накладення електронного підпису за допомогою КЕП ПриватБанку / SmartID / Приват24.
Сервіс повинен забезпечити:
- створення заявки на підписання документа;
- підготовку документа до підпису;
- розрахунок hash документа;
- створення сесії підписання;
- передачу документа або hash у сервіс підписання;
- ініціацію підтвердження підпису користувачем у Приват24 / SmartID;
- отримання результату підписання;
- збереження файлу підпису;
- збереження підписаного контейнера, якщо він повертається сервісом;
- перевірку підпису;
- перевірку цілісності документа;
- оновлення статусу документа в K2 ERP або іншій системі;
- журналювання всіх подій;
- контроль помилок;
- dashboard для відповідальних осіб.
2. Область застосування
Інтеграція може використовуватись для:
- договорів;
- актів виконаних робіт;
- рахунків;
- заяв;
- анкет;
- кадрових документів;
- первинних документів;
- податкових і бухгалтерських документів;
- документів ЕДО;
- документів K2 ERP;
- документів CRM;
- документів особистого кабінету клієнта;
- підтвердження юридично значущих дій користувача.
3. Що таке SmartID / електронний підпис Приват24 у межах інтеграції
SmartID у межах цього ТЗ розглядається як хмарний КЕП ПриватБанку, який користувач створює та використовує через Приват24.
| Параметр | Опис |
|---|---|
| Тип сервісу | Хмарний кваліфікований електронний підпис. |
| Канал користувача | Приват24 / Приват24 для бізнесу / SmartID. |
| Основний сценарій | Користувач підтверджує підписання у Приват24, а Python-сервіс отримує результат. |
| Результат | Файл підпису, підписаний об'єкт або інший результат згідно з API SmartID. |
| Кінцева система | K2 ERP / CRM / документообіг / сайт / мобільний застосунок. |
ПриватБанк описує SmartID як КЕП, що може використовуватись для підписання документів, звітів, підтвердження особистості й отримання послуг онлайн. :contentReference[oaicite:1]{index=1}
4. Передумови
Для реалізації задачі необхідно отримати:
- доступ до сервісу SmartID / хмарного КЕП ПриватБанку;
- офіційну технічну документацію API;
- тестове середовище, якщо доступне;
- ідентифікатор партнера / клієнта API;
- технічні ключі або сертифікати сервісу;
- правила авторизації;
- правила шифрування запитів;
- правила отримання сесії підписання;
- правила формування запиту на підпис;
- правила отримання результату;
- правила перевірки підпису;
- допустимі формати документів;
- максимальний розмір документа;
- callback URL або polling-сценарій;
- контакт технічної підтримки ПриватБанку.
Критично важливо: якщо офіційний API SmartID недоступний для конкретного бізнес-сценарію, потрібно передбачити альтернативний режим: користувач підписує документ вручну у Приват24 / SmartID, а система приймає підписаний файл або p7s на завантаження та виконує перевірку підпису.
5. Варіанти реалізації
5.1. Варіант 1. Пряма API-інтеграція зі SmartID
Python-сервіс напряму інтегрується з API SmartID.
| Параметр | Опис |
|---|---|
| Підходить для | Автоматизованого підписання документів у бізнес-процесі K2 ERP. |
| Переваги | Контроль статусів, сесій, результатів і callback. |
| Обмеження | Потрібен офіційний доступ до API, технічна документація і тестове середовище. |
| Рекомендація | Основний production-сценарій, якщо API доступний. |
5.2. Варіант 2. Ручне підписання у Приват24 + завантаження підпису в систему
Користувач сам підписує документ через Приват24 або SmartID, а потім завантажує підписаний документ / файл підпису в K2 ERP.
| Параметр | Опис |
|---|---|
| Підходить для | MVP без прямого API або резервного сценарію. |
| Переваги | Не потребує повної інтеграції з API SmartID. |
| Обмеження | Менше автоматизації, більше ручних дій. |
| Рекомендація | Використовувати як fallback-сценарій. |
5.3. Варіант 3. Комбінована схема
Система підтримує обидва режими:
- автоматичне створення сесії підписання через SmartID API;
- ручне завантаження підписаного документа, якщо API недоступне або користувач підписав документ поза системою.
Рекомендовано для K2 ERP: реалізувати основний режим через API SmartID, а також резервний режим ручного завантаження p7s / підписаного контейнера з подальшою перевіркою.
6. Основні сценарії інтеграції
6.1. Підписання одного документа
Користувач відкриває документ у K2 ERP або на сайті та натискає кнопку «Підписати через Приват24 / SmartID».
Система:
- створює запис документа;
- створює версію документа;
- розраховує hash;
- створює заявку на підпис;
- створює сесію SmartID;
- очікує підтвердження користувачем;
- отримує результат підписання;
- зберігає підпис;
- перевіряє підпис;
- змінює статус документа на VERIFIED.
6.2. Підписання пакета документів
Користувач підписує декілька документів в одному бізнес-процесі.
Система повинна:
- створити пакет документів;
- перевірити всі документи;
- створити окрему заявку на кожен документ або одну пакетну заявку, якщо це підтримується API;
- отримати результат по кожному документу;
- показати частково підписані або помилкові документи;
- не втратити статус окремого документа.
6.3. Підписання документа клієнтом
Клієнт отримує посилання на документ.
Система:
- відкриває сторінку підписання;
- показує коротку інформацію про документ;
- запускає сценарій підпису через Приват24 / SmartID;
- клієнт підтверджує підписання;
- система отримує результат;
- документ стає підписаним клієнтом.
6.4. Підписання документа співробітником
Співробітник компанії підписує внутрішній документ.
Система:
- створює задачу на підпис;
- показує її у списку задач K2 ERP;
- контролює строк підписання;
- нагадує про прострочення;
- зберігає аудит дій.
6.5. Ручне завантаження підпису
Користувач підписує документ поза системою, наприклад у Приват24, і завантажує результат.
Система:
- приймає файл підпису або підписаний контейнер;
- перевіряє hash вихідного документа;
- перевіряє підпис;
- визначає підписанта;
- змінює статус документа;
- зберігає результат перевірки.
7. Основні сутності
| Сутність | Опис |
|---|---|
| Signature Integration | Налаштування підключення до SmartID / Приват24. |
| Document | Документ, який потрібно підписати. |
| Document Version | Версія документа, яка передана на підпис. |
| Signature Request | Заявка на підписання. |
| Signature Session | Сесія взаємодії зі SmartID. |
| Signer | Підписант. |
| Callback Event | Подія, отримана від сервісу підпису. |
| Signature File | Файл підпису або підписаний контейнер. |
| Verification Result | Результат перевірки підпису. |
| Audit Event | Подія журналу. |
8. User Story
8.1. Користувач підписує документ
Як користувач, я хочу натиснути кнопку «Підписати через Приват24 / SmartID», щоб підписати документ без завантаження приватного ключа в систему.
8.2. Менеджер контролює підписання
Як менеджер, я хочу бачити статус підписання документа, щоб знати, чи клієнт або співробітник підписав документ.
8.3. Адміністратор перевіряє помилки
Як адміністратор, я хочу бачити callback-и, помилки API та технічний журнал, щоб швидко знаходити причини невдалого підписання.
8.4. Керівник бачить dashboard
Як керівник, я хочу бачити кількість документів на підписі, підписаних, відхилених і прострочених, щоб контролювати документообіг.
9. Статуси документа
| Статус | Код | Опис | Колір |
|---|---|---|---|
| Чернетка | DRAFT | Документ створений, але ще не готовий до підпису. | Сірий |
| Готовий до підпису | READY_TO_SIGN | Документ перевірено і можна створювати заявку. | Блакитний |
| Очікує підпису | WAITING_SIGNATURE | Створено заявку на підпис. | Жовтий |
| Підписується | SIGNING | Користувач проходить підтвердження у Приват24 / SmartID. | Блакитний |
| Підписано | SIGNED | Підпис успішно отримано і збережено. | Зелений |
| Підпис перевірено | VERIFIED | Підпис пройшов перевірку. | Зелений |
| Відхилено користувачем | DECLINED_BY_USER | Користувач не підтвердив підписання. | Помаранчевий |
| Прострочено | EXPIRED | Строк сесії підписання минув. | Помаранчевий |
| Помилка підписання | SIGN_ERROR | Помилка під час підписання. | Червоний |
| Помилка перевірки | VERIFY_ERROR | Підпис отримано, але перевірка не пройдена. | Червоний |
| Ручна перевірка | MANUAL_REVIEW | Потрібна перевірка адміністратором. | Фіолетовий |
10. Статуси сесії підписання
| Статус | Код | Опис | Колір |
|---|---|---|---|
| Створюється | CREATING | Система створює сесію підписання. | Блакитний |
| Активна | ACTIVE | Сесія створена та очікує дії користувача. | Жовтий |
| Очікує підтвердження | WAITING_USER_CONFIRMATION | Користувач має підтвердити підпис у Приват24 / SmartID. | Жовтий |
| Очікує результат | WAITING_RESULT | Підписання підтверджено, система очікує результат. | Жовтий |
| Завершена | COMPLETED | Сесія завершена успішно. | Зелений |
| Відхилена | DECLINED | Користувач відхилив дію. | Помаранчевий |
| Прострочена | EXPIRED | Сесія не завершена у строк. | Помаранчевий |
| Помилка | ERROR | Технічна помилка. | Червоний |
11. Єдина логіка кольорів
| Колір | HTML | Значення | Де використовується |
|---|---|---|---|
| Зелений | #c8e6c9 | Успішно: підписано, перевірено, завершено. | Dashboard, список документів, картка документа. |
| Блакитний | #bbdefb | Операція виконується. | Створення сесії, підписання. |
| Жовтий | #fff9c4 | Очікування дії користувача або результату. | Очікує підпису, активна сесія. |
| Помаранчевий | #ffcc80 | Потрібна дія або є ризик. | Відхилено, прострочено. |
| Червоний | #ef9a9a | Помилка або негативний результат. | Помилка підписання, помилка перевірки. |
| Фіолетовий | #f3e5f5 | Ручна перевірка або нестандартний сценарій. | MANUAL_REVIEW. |
| Сірий | #eeeeee | Чернетка або архів. | DRAFT, archived. |
12. Архітектура рішення
12.1. Загальна схема
K2 ERP / CRM / Website
|
| 1. Документ на підпис
v
Python Privat24 / SmartID Signature Service
|
| 2. Валідація, hash, створення заявки
v
SmartID Adapter
|
| 3. API SmartID / ПриватБанк
v
Приват24 / SmartID
|
| 4. Користувач підтверджує підписання
v
Callback або polling Python-сервісу
|
| 5. Результат підписання
v
Signature Storage + Verification Service
|
| 6. Збереження, перевірка, статус
v
K2 ERP / Dashboard / Документообіг
12.2. Основні компоненти Python-сервісу
| Компонент | Опис |
|---|---|
| API Layer | REST API для створення заявок на підпис. |
| Document Service | Робота з документами та версіями. |
| Signature Request Service | Створення заявки на підписання. |
| SmartID Client | Python-клієнт для API SmartID. |
| Confirmation Service | Керує сесією підтвердження підпису користувачем. |
| Callback Controller | Прийом callback, якщо API його підтримує. |
| Polling Worker | Періодична перевірка статусу, якщо callback не використовується. |
| Signature Storage | Зберігання підпису, контейнера, документа. |
| Verification Service | Перевірка підпису та цілісності. |
| Status Sync Service | Оновлення статусів у K2 ERP. |
| Audit Logger | Журнал подій, callback-ів, помилок. |
| Dashboard API | Дані для керівника та відповідальних осіб. |
13. SmartID Client
13.1. Призначення
SmartID Client — це Python-клас або пакет, який інкапсулює роботу з API ПриватБанку / SmartID.
13.2. Основні методи
class SmartIDSignatureClient:
def check_connection(self) -> "ConnectionStatus":
pass
def get_service_certificate(self) -> "ServiceCertificateResponse":
pass
def create_session(self, payload: "CreateSessionPayload") -> "SignatureSessionResponse":
pass
def create_signature_request(self, session_id: str, payload: "SignaturePayload") -> "SignatureRequestResponse":
pass
def get_session_status(self, session_id: str) -> "SignatureSessionStatusResponse":
pass
def get_signature_result(self, session_id: str) -> "SignatureResultResponse":
pass
def cancel_session(self, session_id: str) -> "CancelSessionResponse":
pass
Важливо: назви методів у Python-клієнті є внутрішньою абстракцією. Реальні endpoint-и, шифрування, payload і response потрібно взяти з офіційної документації ПриватБанку / SmartID.
14. Конфігурація
from pydantic_settings import BaseSettings
class SmartIDSignatureSettings(BaseSettings):
base_url: str
partner_id: str
partner_secret: str | None = None
service_certificate_path: str | None = None
private_key_path: str | None = None
callback_url: str | None = None
timeout_seconds: int = 30
retry_count: int = 3
retry_backoff_seconds: int = 5
verify_ssl: bool = True
session_ttl_minutes: int = 15
max_document_size_mb: int = 10
Приклад `.env`:
SMARTID_BASE_URL=https://acsk.privatbank.ua/cloud/api/back SMARTID_PARTNER_ID=******** SMARTID_PARTNER_SECRET=******** SMARTID_SERVICE_CERTIFICATE_PATH=/run/secrets/smartid_service_cert.pem SMARTID_PRIVATE_KEY_PATH=/run/secrets/smartid_private_key.pem SMARTID_CALLBACK_URL=https://example.com/api/v1/smartid/callback SMARTID_TIMEOUT_SECONDS=30 SMARTID_RETRY_COUNT=3 SMARTID_SESSION_TTL_MINUTES=15 SMARTID_MAX_DOCUMENT_SIZE_MB=10
Заборонено: зберігати partner_secret, приватні ключі, токени, callback secrets, паролі КЕП або інші секрети у коді, Git-репозиторії, frontend-змінних або відкритих логах.
15. API Python-сервісу
15.1. Створення інтеграції
POST /api/v1/smartid-signature/integrations
15.2. Перевірка підключення
POST /api/v1/smartid-signature/integrations/{integration_id}/check-connection
15.3. Створення документа
POST /api/v1/smartid-signature/documents
15.4. Створення заявки на підпис
POST /api/v1/smartid-signature/documents/{document_id}/signature-requests
15.5. Отримання статусу заявки
GET /api/v1/smartid-signature/signature-requests/{request_id}/status
15.6. Callback від SmartID
POST /api/v1/smartid-signature/callback
15.7. Завантаження підписаного документа
GET /api/v1/smartid-signature/documents/{document_id}/signed-file
15.8. Завантаження файлу підпису
GET /api/v1/smartid-signature/documents/{document_id}/signature-file
15.9. Ручне завантаження підпису
POST /api/v1/smartid-signature/documents/{document_id}/upload-signature
15.10. Перевірка підпису
POST /api/v1/smartid-signature/documents/{document_id}/verify
15.11. Dashboard
GET /api/v1/smartid-signature/dashboard?date_from=2026-05-01&date_to=2026-05-31
16. Приклад запиту на створення заявки на підпис
{
"external_document_id": "K2-DOC-2026-000123",
"idempotency_key": "K2-DOC-2026-000123-smartid-sign-v1",
"document_type": "CONTRACT",
"document_name": "Договір поставки №123",
"document_number": "123",
"document_date": "2026-05-07",
"file_id": "file-001",
"file_name": "contract_123.pdf",
"file_mime_type": "application/pdf",
"signer": {
"external_signer_id": "CLIENT-001",
"full_name": "Іван Петренко",
"phone": "+380671112233",
"email": "client@example.com",
"tax_id": "1234567890"
},
"callback_context": {
"k2_entity": "contract",
"k2_entity_id": "contract-001"
},
"expires_at": "2026-05-07T14:30:00+03:00"
}
17. Валідація документа перед підписом
Перед створенням заявки система повинна перевірити:
- наявність external_document_id;
- наявність idempotency_key;
- наявність файлу документа;
- файл доступний у сховищі;
- файл не порожній;
- розмір файлу не перевищує ліміт;
- MIME type дозволений;
- документ не був змінений після створення заявки;
- hash документа збережений;
- підписант визначений;
- строк підписання не минув;
- документ ще не підписаний цим підписантом;
- бізнес-процес дозволяє підписання;
- користувач має право ініціювати підписання;
- телефон або ідентифікатор підписанта відповідає даним користувача, якщо це потрібно для SmartID-сценарію.
Критично важливо: якщо документ змінено після створення заявки на підпис, попередня заявка повинна бути скасована або переведена в статус INVALIDATED. Не можна підписувати старий hash для нової версії документа.
18. Hash документа і версії
Для кожного документа потрібно зберігати:
| Поле | Опис |
|---|---|
| document_version_id | ID версії документа. |
| file_hash_sha256 | Hash файлу. |
| file_size | Розмір файлу. |
| mime_type | MIME type. |
| created_at | Дата створення версії. |
| created_by | Хто створив версію. |
Приклад hash:
sha256(file_bytes)
19. Callback або polling
19.1. Callback-сценарій
Callback endpoint повинен:
- приймати тільки HTTPS-запити;
- перевіряти підпис або секрет callback, якщо передбачено API;
- перевіряти session_id;
- перевіряти request_id;
- перевіряти idempotency callback;
- зберігати raw payload;
- оновлювати статус сесії;
- зберігати файл підпису або посилання на результат;
- запускати перевірку підпису;
- повертати коректний HTTP status.
19.2. Polling-сценарій
Якщо API не надсилає callback, Python-сервіс повинен періодично перевіряти статус сесії.
1. Створити сесію підписання. 2. Перевести заявку у WAITING_SIGNATURE. 3. Запустити polling worker. 4. Перевіряти статус кожні N секунд. 5. Після отримання фінального статусу зберегти результат. 6. Запустити перевірку підпису. 7. Оновити K2 ERP.
Критично важливо: callback і polling повинні бути ідемпотентними. Повторне отримання одного й того самого результату не повинно дублювати підпис або некоректно змінювати фінальний статус.
20. Перевірка підпису
Після отримання результату підписання система повинна виконати перевірку.
Перевіряється:
- цілісність документа;
- відповідність підпису конкретній версії документа;
- валідність підпису;
- валідність сертифіката;
- дані підписанта;
- час підписання;
- статус відкликання сертифіката, якщо доступно;
- чи відповідає підписант очікуваному користувачу;
- чи не минув строк сесії;
- чи не змінювався документ після підпису.
Можливі результати:
| Результат | Код | Опис | Колір |
|---|---|---|---|
| Валідний | VALID | Підпис пройшов перевірку. | Зелений |
| Невалідний | INVALID | Підпис не пройшов перевірку. | Червоний |
| Не той документ | HASH_MISMATCH | Hash документа не збігається. | Червоний |
| Не той підписант | SIGNER_MISMATCH | Підписант не відповідає очікуваному. | Червоний |
| Прострочений сертифікат | CERT_EXPIRED | Сертифікат підписанта недійсний на момент перевірки. | Червоний |
| Потребує ручної перевірки | MANUAL_REVIEW | Неможливо автоматично визначити результат. | Фіолетовий |
21. Дедублікація
Система повинна не допускати дублювання заявок і підписів.
Ключі дедублікації:
| Ключ | Призначення |
|---|---|
| external_document_id | ID документа в K2 ERP. |
| document_version_id | Версія документа. |
| signer_id | Підписант. |
| idempotency_key | Унікальний ключ заявки. |
| smartid_session_id | ID сесії SmartID. |
| callback_event_id | ID callback-події, якщо надається. |
| file_hash_sha256 | Hash документа. |
22. Черга обробки
22.1. Логіка черги
1. K2 ERP створює документ. 2. Користувач натискає «Підписати через Приват24 / SmartID». 3. Python-сервіс перевіряє документ. 4. Створюється signature_request. 5. Створюється signature_session. 6. Користувач підтверджує підпис у Приват24 / SmartID. 7. Callback Controller або Polling Worker отримує результат. 8. Signature Storage зберігає підпис. 9. Verification Service перевіряє підпис. 10. K2 ERP отримує фінальний статус.
22.2. Пріоритети задач
| Задача | Пріоритет | Коментар |
|---|---|---|
| Прийом callback | Критичний | Не можна втрачати результат підписання. |
| Збереження підпису | Критичний | Юридично значущий результат. |
| Перевірка підпису | Високий | Потрібна для фінального статусу. |
| Створення сесії | Високий | Основний сценарій користувача. |
| Polling статусу | Середній | Потрібен, якщо callback недоступний. |
| Оновлення dashboard | Середній | Контроль. |
| Нагадування про прострочення | Низький | Фоновий процес. |
23. Модель даних
23.1. smartid_signature_integrations
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID інтеграції. |
| provider | varchar | privatbank_smartid. |
| name | varchar | Назва інтеграції. |
| base_url | varchar | URL API. |
| partner_id | varchar | ID партнера. |
| partner_secret_encrypted | text | Зашифрований секрет. |
| service_certificate_path | varchar | Шлях до сертифіката сервісу. |
| callback_url | varchar | Callback URL. |
| is_active | boolean | Активність. |
| created_at | timestamp | Дата створення. |
| updated_at | timestamp | Дата оновлення. |
23.2. sign_documents
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID документа. |
| external_document_id | varchar | ID документа в K2 ERP. |
| document_type | varchar | CONTRACT, ACT, APPLICATION тощо. |
| document_name | varchar | Назва документа. |
| document_number | varchar | Номер документа. |
| document_date | date | Дата документа. |
| current_version_id | uuid | Поточна версія. |
| status | varchar | Статус документа. |
| created_at | timestamp | Дата створення. |
23.3. sign_document_versions
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID версії. |
| document_id | uuid | ID документа. |
| file_id | uuid | Файл документа. |
| file_name | varchar | Назва файлу. |
| mime_type | varchar | MIME type. |
| file_size | integer | Розмір файлу. |
| file_hash_sha256 | varchar | Hash файлу. |
| version_number | integer | Номер версії. |
| created_at | timestamp | Дата створення. |
23.4. signature_requests
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID заявки. |
| document_id | uuid | Документ. |
| document_version_id | uuid | Версія документа. |
| signer_id | uuid | Підписант. |
| idempotency_key | varchar | Ключ дедублікації. |
| status | varchar | Статус заявки. |
| expires_at | timestamp | Строк дії. |
| created_by | uuid | Хто створив заявку. |
| created_at | timestamp | Дата створення. |
23.5. signature_sessions
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID сесії. |
| signature_request_id | uuid | Заявка. |
| smartid_session_id | varchar | ID сесії SmartID. |
| status | varchar | Статус сесії. |
| raw_request | jsonb | Запит до SmartID. |
| raw_response | jsonb | Відповідь SmartID. |
| created_at | timestamp | Дата створення. |
| expires_at | timestamp | Дата завершення. |
23.6. signature_files
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID файлу підпису. |
| signature_request_id | uuid | Заявка. |
| file_id | uuid | Файл у сховищі. |
| file_type | varchar | signature, signed_container, signed_pdf. |
| file_hash_sha256 | varchar | Hash файлу. |
| source | varchar | SMARTID_API або MANUAL_UPLOAD. |
| created_at | timestamp | Дата створення. |
23.7. signature_verifications
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID перевірки. |
| signature_request_id | uuid | Заявка. |
| result | varchar | VALID, INVALID, HASH_MISMATCH тощо. |
| signer_name | varchar | ПІБ підписанта з сертифіката. |
| signer_identifier | varchar | Ідентифікатор підписанта, якщо доступний. |
| signed_at | timestamp | Час підписання. |
| certificate_info | jsonb | Дані сертифіката. |
| raw_result | jsonb | Повний результат перевірки. |
| created_at | timestamp | Дата перевірки. |
23.8. signature_events
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID події. |
| entity_type | varchar | document, request, session, callback, verification. |
| entity_id | uuid | ID сутності. |
| event_type | varchar | Тип події. |
| old_status | varchar | Попередній статус. |
| new_status | varchar | Новий статус. |
| source | varchar | K2_ERP, PYTHON_SERVICE, SMARTID, USER. |
| payload | jsonb | Технічні дані. |
| created_at | timestamp | Дата події. |
24. Приклад Python-логіки
24.1. Створення заявки на підпис
def create_signature_request(command: "CreateSignatureRequestCommand", db: "Session") -> "SignatureRequest":
existing = signature_request_repository.get_by_idempotency_key(
db=db,
idempotency_key=command.idempotency_key,
)
if existing:
return existing
document = document_repository.get_by_external_id(
db=db,
external_document_id=command.external_document_id,
)
signature_validator.validate_document_for_signing(document, command)
request = signature_request_repository.create(
db=db,
data={
"document_id": document.id,
"document_version_id": document.current_version_id,
"signer_id": command.signer_id,
"idempotency_key": command.idempotency_key,
"status": "CREATING",
"expires_at": command.expires_at,
},
)
signature_queue.enqueue(
task_name="create_smartid_signature_session",
payload={"signature_request_id": str(request.id)},
)
audit_logger.log(
entity_type="signature_request",
entity_id=request.id,
event_type="SIGNATURE_REQUEST_CREATED",
new_status="CREATING",
payload={"external_document_id": command.external_document_id},
)
db.commit()
return request
24.2. Створення сесії SmartID
async def create_smartid_signature_session(signature_request_id: str, db: "Session") -> None:
request = signature_request_repository.get_by_id(db, signature_request_id)
document_version = document_version_repository.get_by_id(db, request.document_version_id)
try:
payload = smartid_mapper.to_signature_session_payload(
request=request,
document_version=document_version,
)
response = await smartid_client.create_session(payload)
session = signature_session_repository.create(
db=db,
data={
"signature_request_id": request.id,
"smartid_session_id": response.session_id,
"status": "ACTIVE",
"raw_request": payload,
"raw_response": response.raw_payload,
"expires_at": response.expires_at,
},
)
request.status = "WAITING_SIGNATURE"
audit_logger.log(
entity_type="signature_session",
entity_id=session.id,
event_type="SMARTID_SIGNATURE_SESSION_CREATED",
new_status="ACTIVE",
payload={"smartid_session_id": response.session_id},
)
except Exception as exc:
request.status = "SIGN_ERROR"
audit_logger.log(
entity_type="signature_request",
entity_id=request.id,
event_type="SMARTID_SIGNATURE_SESSION_ERROR",
new_status="SIGN_ERROR",
payload={"error": str(exc)},
)
finally:
db.commit()
24.3. Polling статусу сесії
async def poll_smartid_session(signature_session_id: str, db: "Session") -> None:
session = signature_session_repository.get_by_id(db, signature_session_id)
if session.status in ["COMPLETED", "DECLINED", "EXPIRED", "ERROR"]:
return
status_response = await smartid_client.get_session_status(session.smartid_session_id)
new_status = smartid_status_mapper.from_api(status_response.status)
if new_status == "COMPLETED":
result = await smartid_client.get_signature_result(session.smartid_session_id)
signature_file = signature_storage.save_signature_result(
signature_request_id=session.signature_request_id,
payload=result.raw_payload,
)
session.status = "COMPLETED"
session.signature_request.status = "SIGNED"
verification_queue.enqueue(
task_name="verify_signature",
payload={
"signature_request_id": str(session.signature_request_id),
"signature_file_id": str(signature_file.id),
},
)
elif new_status in ["DECLINED", "EXPIRED", "ERROR"]:
session.status = new_status
session.signature_request.status = smartid_status_mapper.to_request_status(new_status)
db.commit()
24.4. Callback controller
from fastapi import APIRouter, Request, HTTPException
router = APIRouter()
@router.post("/api/v1/smartid-signature/callback")
async def smartid_signature_callback(request: Request):
payload = await request.json()
# Перевірка callback signature / secret залежить від офіційної документації SmartID.
if not callback_security_service.is_valid(request, payload):
raise HTTPException(status_code=401, detail="Invalid callback signature")
callback_id = callback_service.get_callback_id(payload)
if callback_repository.exists(callback_id):
return {"status": "already_processed"}
callback_event = callback_repository.create_raw_event(payload)
signature_session = signature_session_repository.get_by_smartid_session_id(
smartid_session_id=payload["session_id"],
)
if not signature_session:
callback_event.status = "UNKNOWN_SESSION"
return {"status": "unknown_session"}
callback_processor.process_signature_result(
signature_session=signature_session,
payload=payload,
)
return {"status": "ok"}
24.5. Ручне завантаження підпису
def upload_manual_signature(
document_id: str,
signature_file: "UploadedFile",
current_user: "User",
db: "Session",
) -> "SignatureFile":
document = document_repository.get_by_id(db, document_id)
if document.status not in ["READY_TO_SIGN", "WAITING_SIGNATURE", "SIGN_ERROR"]:
raise BusinessError("Document cannot accept signature in current status")
stored_file = file_storage.save(signature_file)
signature_record = signature_file_repository.create(
db=db,
data={
"signature_request_id": None,
"file_id": stored_file.id,
"file_type": "signature",
"file_hash_sha256": stored_file.sha256,
"source": "MANUAL_UPLOAD",
},
)
verification_queue.enqueue(
task_name="verify_manual_signature",
payload={
"document_id": str(document.id),
"signature_file_id": str(signature_record.id),
},
)
audit_logger.log(
entity_type="document",
entity_id=document.id,
event_type="MANUAL_SIGNATURE_UPLOADED",
payload={"uploaded_by": str(current_user.id)},
)
db.commit()
return signature_record
24.6. Перевірка підпису
def verify_signature(signature_request_id: str, signature_file_id: str, db: "Session") -> None:
request = signature_request_repository.get_by_id(db, signature_request_id)
document_version = document_version_repository.get_by_id(db, request.document_version_id)
signature_file = signature_file_repository.get_by_id(db, signature_file_id)
try:
result = signature_verifier.verify(
document_file_id=document_version.file_id,
signature_file_id=signature_file.file_id,
expected_hash=document_version.file_hash_sha256,
)
signature_verification_repository.create(
db=db,
data={
"signature_request_id": request.id,
"result": result.code,
"signer_name": result.signer_name,
"signer_identifier": result.signer_identifier,
"signed_at": result.signed_at,
"certificate_info": result.certificate_info,
"raw_result": result.raw,
},
)
if result.code == "VALID":
request.status = "VERIFIED"
request.document.status = "VERIFIED"
else:
request.status = "VERIFY_ERROR"
except Exception as exc:
request.status = "MANUAL_REVIEW"
audit_logger.log(
entity_type="signature_request",
entity_id=request.id,
event_type="SIGNATURE_VERIFY_EXCEPTION",
new_status="MANUAL_REVIEW",
payload={"error": str(exc)},
)
finally:
db.commit()
25. Обробка помилок
| Тип помилки | Опис | Дія системи |
|---|---|---|
| ValidationError | Документ або підписант невалідний. | Не створювати сесію. |
| AuthError | Невірні credentials SmartID. | Зупинити інтеграцію і повідомити адміністратора. |
| EncryptionError | Помилка шифрування або підготовки запиту. | Перевести в SIGN_ERROR. |
| DocumentChangedError | Документ змінено після заявки. | Скасувати заявку або створити нову. |
| FileTooLargeError | Документ перевищує ліміт. | Показати користувачу помилку. |
| SessionExpiredError | Сесія підписання прострочена. | Статус EXPIRED, дозволити створити нову. |
| CallbackValidationError | Callback не пройшов перевірку. | Відхилити callback і записати подію. |
| SignatureResultError | Не вдалося отримати результат підпису. | Перевести в SIGN_ERROR або NEEDS_RETRY. |
| VerificationError | Підпис не пройшов перевірку. | Статус VERIFY_ERROR. |
| SignerMismatchError | Підписант не відповідає очікуваному. | Статус MANUAL_REVIEW або VERIFY_ERROR. |
| TimeoutError | API недоступне або timeout. | Retry, якщо безпечно. |
26. Retry-логіка
Retry дозволений для:
- timeout;
- HTTP 429;
- HTTP 500;
- HTTP 502;
- HTTP 503;
- HTTP 504;
- тимчасової помилки створення сесії;
- тимчасової помилки отримання статусу;
- тимчасової помилки отримання результату;
- тимчасової помилки перевірки підпису;
- повторного callback з тим самим callback_id.
Retry заборонений для:
- невалідного документа;
- документа, який змінився;
- простроченої сесії;
- відхилення користувачем;
- невірного callback signature;
- невідповідності підписанта;
- вже фінального статусу VERIFIED.
27. Dashboard керівника
27.1. Основні KPI
| KPI | Опис | Колір |
|---|---|---|
| Документів створено | Загальна кількість документів. | Інформація |
| Очікують підпису | Документи з активною сесією. | Увага |
| Підписано | Підпис отримано. | Норма |
| Перевірено | Підпис пройшов перевірку. | Норма |
| Відхилено | Користувач відмовився. | Потрібна дія |
| Прострочено | Сесія не завершена вчасно. | Потрібна дія |
| Помилки | Помилки підписання або callback. | Критично |
| Ручна перевірка | Потрібне втручання адміністратора. | Контроль |
| Ручне завантаження | Підписи, завантажені користувачем вручну. | Інформація |
27.2. Приклад dashboard
| Показник | Значення | Стан |
|---|---|---|
| Документів за день | 184 | Інформація |
| Очікують підпису | 32 | Увага |
| Підписано через SmartID | 118 | Норма |
| Завантажено вручну | 10 | Інформація |
| Перевірено | 126 | Норма |
| Відхилено | 8 | Потрібна дія |
| Прострочено | 10 | Потрібна дія |
| Помилки callback/API | 3 | Критично |
| Ручна перевірка | 2 | Контроль |
27.3. Проблемні документи
| Дата | Документ | Підписант | Статус | Причина | Дія |
|---|---|---|---|---|---|
| 07.05.2026 | Договір №123 | Іван Петренко | Прострочено | Користувач не завершив підписання | Створити нову заявку |
| 07.05.2026 | Акт №45 | Олена Сидоренко | Помилка перевірки | Hash документа не збігається | Ручна перевірка |
| 07.05.2026 | Заява №77 | ТОВ «Альфа» | Ручна перевірка | Неможливо автоматично визначити підписанта | Перевірити сертифікат |
28. Безпека
Система повинна забезпечити:
- HTTPS для всіх endpoint-ів;
- перевірку SSL;
- зберігання секретів тільки в secret storage;
- шифрування файлів підпису;
- шифрування документів або контроль доступу до них;
- обмеження доступу до callback endpoint;
- перевірку callback signature / secret;
- ідемпотентність callback;
- журнал усіх дій;
- маскування персональних даних у логах;
- контроль доступу до документів;
- окремі права на створення заявки;
- окремі права на повторне підписання;
- окремі права на ручне завантаження підпису;
- окремі права на ручну перевірку;
- заборону підписання зміненої версії документа;
- заборону зберігання пароля користувача до SmartID.
29. Логування та аудит
Система повинна логувати:
| Подія | Що зберігати |
|---|---|
| Створення документа | Тип, номер, версія, hash. |
| Створення заявки | Підписант, строк дії, ініціатор. |
| Створення сесії SmartID | smartid_session_id, статус, expires_at. |
| Підтвердження користувачем | Статус сесії, час. |
| Callback | callback_id, raw payload, статус перевірки. |
| Polling статусу | Старий статус, новий статус. |
| Отримання підпису | file_id, hash підпису, час. |
| Ручне завантаження | Хто завантажив, файл, hash. |
| Перевірка підпису | результат, підписант, сертифікат. |
| Помилка | код, повідомлення, stack trace без секретів. |
| Ручна перевірка | хто перевірив, рішення, коментар. |
30. Acceptance Criteria
30.1. Інтеграція
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-1 | Адміністратор створює інтеграцію SmartID. | Інтеграція зберігається в системі. |
| AC-2 | Адміністратор перевіряє підключення. | Система повертає успішний або помилковий статус. |
| AC-3 | Credentials неправильні. | Система показує AuthError і не створює сесії. |
30.2. Документ
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-4 | Документ валідний. | Система створює заявку на підпис. |
| AC-5 | Документ перевищує ліміт розміру. | Заявка не створюється. |
| AC-6 | Документ змінено після створення заявки. | Попередня заявка стає INVALIDATED або скасовується. |
30.3. Підписання
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-7 | Користувач натискає «Підписати через Приват24 / SmartID». | Система створює сесію підписання. |
| AC-8 | Користувач підтверджує підпис. | Система отримує результат і зберігає підпис. |
| AC-9 | Користувач відхиляє підписання. | Статус стає DECLINED_BY_USER. |
| AC-10 | Сесія прострочена. | Статус стає EXPIRED. |
30.4. Ручне завантаження
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-11 | Користувач завантажує p7s або підписаний контейнер. | Система зберігає файл і запускає перевірку. |
| AC-12 | Підпис відповідає документу. | Статус стає VERIFIED. |
| AC-13 | Підпис не відповідає документу. | Статус стає HASH_MISMATCH або VERIFY_ERROR. |
30.5. Перевірка
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-14 | Підпис валідний. | Статус стає VERIFIED. |
| AC-15 | Hash документа не збігається. | Статус стає VERIFY_ERROR. |
| AC-16 | Підписант не відповідає очікуваному. | Статус стає MANUAL_REVIEW або VERIFY_ERROR. |
30.6. Callback / polling
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-17 | Callback має правильний підпис/секрет. | Система приймає callback. |
| AC-18 | Callback повторився. | Система не дублює результат. |
| AC-19 | Callback невалідний. | Система повертає помилку і записує подію. |
| AC-20 | Callback недоступний, але polling увімкнений. | Система отримує статус через polling worker. |
30.7. Dashboard
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-21 | Керівник відкриває dashboard. | Він бачить документи, підписи, помилки, прострочення. |
| AC-22 | Є помилки підписання. | Вони підсвічуються червоним. |
| AC-23 | Є прострочені заявки. | Вони підсвічуються помаранчевим. |
| AC-24 | Є документи на ручній перевірці. | Вони підсвічуються фіолетовим. |
31. MVP
До MVP входить:
- створення інтеграції SmartID / Приват24;
- перевірка підключення;
- створення документа;
- збереження версії документа;
- розрахунок hash;
- створення заявки на підпис;
- створення сесії підписання, якщо доступний API;
- polling або callback для отримання результату;
- збереження результату підписання;
- ручне завантаження p7s / підписаного контейнера як fallback;
- базова перевірка підпису;
- статуси документа;
- журнал подій;
- dashboard API;
- retry для технічних помилок;
- ідемпотентність callback / polling;
- unit-тести;
- mock SmartID client.
До MVP не входить:
- масове підписання великого пакета документів;
- складний UI документообігу;
- власний кваліфікований надавач електронних довірчих послуг;
- повна юридична експертиза документів;
- інтеграція з усіма зовнішніми ЕДО-системами;
- автоматичне виправлення документів;
- архів довгострокового зберігання за окремими регламентами.
32. Етапи реалізації
Етап 1. Аналіз інтеграції SmartID
- отримати офіційну технічну документацію;
- отримати тестові credentials;
- погодити callback URL або polling-сценарій;
- перевірити тестовий сценарій;
- визначити формат результату підписання;
- визначити правила перевірки підпису.
Етап 2. Базовий Python-сервіс
- створити FastAPI-проєкт;
- налаштувати PostgreSQL;
- створити моделі документів, заявок, сесій, підписів;
- налаштувати Alembic;
- реалізувати healthcheck.
Етап 3. SmartID Client
- реалізувати get_service_certificate;
- реалізувати create_session;
- реалізувати create_signature_request;
- реалізувати get_session_status;
- реалізувати get_signature_result;
- реалізувати обробку помилок.
Етап 4. Документи
- реалізувати завантаження документа;
- реалізувати версіонування;
- реалізувати hash;
- реалізувати валідацію;
- реалізувати дедублікацію.
Етап 5. Callback / polling та підпис
- реалізувати callback endpoint;
- реалізувати polling worker;
- реалізувати перевірку callback;
- реалізувати збереження результату;
- реалізувати ідемпотентність;
- реалізувати raw event storage.
Етап 6. Ручне завантаження підпису
- реалізувати upload endpoint;
- реалізувати перевірку типу файлу;
- реалізувати збереження p7s / контейнера;
- реалізувати зв'язок із документом;
- реалізувати перевірку підпису.
Етап 7. Перевірка підпису
- реалізувати Verification Service;
- реалізувати статуси перевірки;
- реалізувати ручну перевірку;
- реалізувати журнал перевірок.
Етап 8. Dashboard та аудит
- реалізувати dashboard API;
- реалізувати список проблемних документів;
- реалізувати фільтри;
- реалізувати експорт, якщо потрібно.
Етап 9. Production hardening
- додати rate limiting;
- додати alerting;
- додати dead letter queue;
- додати backup файлів;
- додати моніторинг callback / polling;
- додати безпечне зберігання секретів.
33. Ризики
| Ризик | Опис | Як зменшити |
|---|---|---|
| Немає API-доступу SmartID | Без доступу неможливо реалізувати повну автоматичну інтеграцію. | Передбачити fallback: ручне завантаження підпису. |
| Документ змінено після заявки | Можна підписати неактуальну версію. | Версіонування і hash документа. |
| Callback втрачено | Система не дізнається про результат. | Callback retry, polling статусу, журнал raw events. |
| Дублювання callback | Може повторно змінити статус. | Ідемпотентність callback. |
| Невідомий формат підпису | Неможливо зберегти/перевірити результат. | Уточнити формат за документацією SmartID. |
| Невідповідність підписанта | Документ підписала не та особа. | Перевірка даних сертифіката. |
| Помилка перевірки | Підпис отримано, але не підтверджено. | MANUAL_REVIEW і аудит. |
| Прострочені сесії | Користувач не завершив підписання. | TTL, нагадування, повторна заявка. |
34. Відкриті питання
- Чи є офіційний API-доступ до SmartID для цього проєкту?
- Який точний формат результату підписання: p7s, ASIC, PDF з підписом або інший?
- Чи потрібно підписувати PDF, XML, DOCX або будь-який файл?
- Який максимальний розмір документа?
- Чи потрібне пакетне підписання?
- Чи потрібен fallback зі ручним завантаженням p7s?
- Чи потрібно підписувати документи клієнтами, співробітниками або обома?
- Чи потрібно перевіряти РНОКПП / ЄДРПОУ підписанта?
- Чи потрібна інтеграція з K2 ERP?
- Чи потрібно зберігати підписані документи в архіві довгострокового зберігання?
- Чи потрібен UI для підписанта?
- Чи потрібні email/SMS-нагадування?
- Який строк дії сесії підписання?
- Який callback security mechanism надає SmartID?
- Чи потрібна інтеграція з ЕДО-системами після підписання?
35. Джерела
- Офіційна сторінка SmartID ПриватБанку.
- Офіційна сторінка SmartID для бізнесу.
- Інструкція ПриватБанку щодо створення SmartID.
- Технічна документація SmartID API, яка надається після підключення.
- Документація K2 ERP щодо документів і бізнес-процесів.
- Законодавчі вимоги до КЕП і електронного документообігу.