| -
|
Polling Worker
|
-
|
AC-16
|
Підпис відповідає документу. SEO-опис
"provider_id": provider_repository.get_by_code(db, "MANUAL_UPLOAD").id,
30. Acceptance Criteria"signature_type": command.signature_type,
11. Очікуваний результат
щоб контролювати доступні сценарії підписання. Очікуваний результат
"phone": "+380671112233",
35. Джерела
| -
|
credentials_encrypted
|
jsonb
|
Зашифровані credentials.=== Варіант 3. 7.3. Підписання через локальний агент ===
|
Опційно
|
| CAdES
|
Формат CMS-підпису. Тип
|
| Відокремлений підпис
|
DETACHED
|
Статус стає VERIFIED. |-
|
Секрети потрапили в лог
|
}
},
"email": "client@example.com",
v
document = document_repository.get_by_external_id(
"file_type": "signature",
|
| id
|
uuid
|
Так
|
| ASIC
|
Запускається локальний або файловий сценарій. SEO-опис
9. Основні сутності
|
| Зелений
|
#c8e6c9
|
style="background:#bbdefb;" | Блакитний
|
| Очікує вибору провайдера
|
WAITING_PROVIDER
|
Підписант ще не обрав спосіб підпису. Підписант
Етап 4. Адаптери
self.adapters = adapters
|
-
|
Ручне завантаження
|
Хто завантажив, файл, hash.
я хочу вибрати спосіб підписання: Дія, Приват24 SmartID, файловий КЕП або інший КНЕДП,
request.document.status = "VERIFIED"
|
Що зберігати
|
-
|
SmartIDAdapter
|
-
|
qr_payload
|
text
|
-
|
file_hash_sha256
|
Hash документа. Verification Service перевіряє підпис. Колір
|
-
|
конкурентні переваги
|
-
|
document_id
|
uuid
|
Документ. SEO-опис
|
style="background:#fff9c4;" | Жовтий
|
| Підписується
|
SIGNING
|
Виконується бізнес-процес підписання. Валідація, hash, версії, політики
|
|
3. Тип
|
-
|
document_id
|
uuid
|
-
|
AC-11
|
Підпис отримано. SEO-опис
Метою задачі виступає як створення уніфікованого Python-сервісу електронного підпису для роботи з різними сервісними центрами та провайдерами КЕП України. |}
Provider Adapters
def get_allowed_providers(self, document_type: str, signer_type: str) -> list [str]:
expected_hash=document_version.file_hash_sha256,
Етап 9. Production hardening
"signature_request_id": None,
|
| AC-18
|
Керівник відкриває dashboard.=== 17.11. Перевірка підпису ===
|
Показати інструкцію користувачу. # Чи потрібно підтримувати ІІТ DLL/SO напряму? |-
|
Ручне рішення для бізнесу
|
-
|
Signer
|
Підписант. Як зменшити
|
}
4. Типи підписання
try:
)
pass
| Підходить для
|
-
|
version_number
|
Номер версії. ! * Інструкції ПриватБанку щодо створення SmartID. # Чи потрібні нагадування про прострочення?! |-
| signer_id
| uuid
| Підписант. Збереження і перевірка
"file_mime_type": "application/pdf",
{| class="wikitable"
callback_repository.create_raw_event(
[[Категорія:КЕП]]
Уніфікований компонент застосовується для для:
=== 30.1. Провайдери ===
"document_date": "2026-05-07",
|-
| Unified Signature API
| Єдиний REST API для K2 ERP. Помилки
{| class="wikitable"
)
щоб підписати документ зручним для мене способом. {| class="wikitable"
* зміненого документа;
* невалідного callback;
* відхилення користувачем;
* невідповідності підписанта;
* невалідного пароля файлового ключа;
* фінального статусу VERIFIED;
* ручного рішення для бізнесу адміністратора. | Запропонувати іншого провайдера або manual upload. ! | Hash і document version lock. | style="background:#f3e5f5;" | Фіолетовий
|-
| Скасовано
| CANCELLED
| Заявку або документ скасовано. |-
| Створення заявки
| Підписант, строк, provider_code. | Показати користувачу помилку. | ASIC / p7s container. |-
| is_active
| boolean
| Активність. * Офіційна сторінка SmartID ПриватБанку. |-
| LocalAgentUnavailableError
| Локальний агент недоступний. SEO-опис
{| class="wikitable"
|
| 2. Поле
|-
| Чернетка
| DRAFT
| Документ створений, але ще не готовий до підпису. |-
| created_at
| timestamp
| Дата створення. платформа розраховує hash документа. Поле
{| class="wikitable"
v
== 19. Валідація перед підписом ==
{| class="wikitable"
7. Варіанти реалізації"signature_format": signature_format_detector.detect(file.filename, stored_file.bytes),
from fastapi import APIRouter, Request, HTTPException
Окремо варто відзначити який надає K2 ERP / CRM / документообігу один уніфікований API; так само реалізовано ПриватБанк SmartID, файловий КЕП, ІІТ, КЕП ДПС і інші КНЕДП. |-
| raw_result
|
jsonb
|
Вона підсвічується фіолетовим. |-
|
payload
|
jsonb
|
-
|
created_at
|
timestamp
|
Дата перевірки.![[Категорія:Інтеграції]]
db.commit()
db=db,
{{SEO
|title=Технічне завдання: Уніфіковане накладання електронного підпису різних сервісних центрів України для Python
|description=Технічне завдання на реалізацію уніфікованого Python-модуля електронного підпису для різних сервісних центрів України: Дія.Підпис, ПриватБанк SmartID, файловий КЕП, ІІТ, ДПС, хмарний КЕП, p7s, ASIC, CAdES, XAdES, перевірка підпису, callback, polling, dashboard та журналювання.
|keywords=Python, КЕП, електронний підпис, Дія.Підпис, SmartID, Приват24, ІІТ, ДПС, ЦСК, КНЕДП, p7s, ASIC, CAdES, XAdES, FastAPI, K2 ERP, електронний документообіг
}}
@abstractmethod
request = signature_request_repository.get_by_id(db, signature_request_id)
</div>
! SEO-опис
)
=== 17.10. Завантаження підписаного документа ===
</pre>
request = signature_request_repository.get_by_id(db, signature_request_id)
=== 10.1. користувач системи обирає провайдера ===
! |-
| Audit Logger
| Логує всі дії. №
* реалізувати callback endpoint;
* реалізувати polling worker;
* реалізувати raw event storage;
* реалізувати idempotency. |-
| file_name
| varchar
| Назва файлу. |}
db.commit()
{| class="wikitable"
)
! * Документація КНЕДП ДПС. # Чи потрібен довгостроковий архів підписаних документів? | style="background:#fff9c4;" | Увага
|-
| Підписано
| Підпис отримано. |-
| Статуси
| WAITING_SIGNATURE, SIGNING, SIGNED, VERIFIED. | style="background:#c8e6c9;" | Норма
|-
| Перевірено
| Підпис валідний. |-
| Provider Router
| Вибирає провайдера підпису. |}
== 23. Модель даних ==
raise HTTPException(status_code=401, detail="Invalid callback")
'''Технічний стек:''' Python 3.11+, FastAPI, PostgreSQL, SQLAlchemy, Alembic, httpx, Pydantic, Celery/RQ/APScheduler, Redis, Docker, S3-compatible file storage, локальні crypto adapters. |-
| signed_at
| timestamp
| Час підписання. Очікуваний результат
! Приклад
"provider_session_id": response.get("session_id"),
result = await verifier.verify(
"external_signer_id": "CLIENT-001",
=== 17.3. Створення документа ===
"idempotency_key": "K2-DOC-2026-000123-sign-v1",
@abstractmethod
<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">
"expires_at": response.get("expires_at"),
"provider_code": "DIIA_SIGN",
Як адміністратор,
=== 15.1. Загальний інтерфейс провайдера ===
def __init__(self, adapters: dict [str, SignatureProviderAdapter]):
== 36. Див. так само ==
=== 23.3. sign_documents ===
db=db,
document_version = document_version_repository.get_by_id(db, request.document_version_id)
async def create_signature_session(self, request: dict) -> dict:
=== Етап 1. Аналіз провайдерів ===
=== 30.4. Перевірка ===
! Критерій
=== 24.2. Запуск сесії через router ===
<pre>
! |-
| CallbackValidationError
| Callback не пройшов перевірку. |-
| IITAdapter
| інтеграційні функції ERP з бібліотеками ІІТ або локальним агентом. Тип
я хочу бачити dashboard підписання,
document_file_id=document_version.file_id,
POST /api/v1/signature/documents
=== 10.2. K2 ERP функціонує з єдиним API ===
! stored_file = await file_storage.save(file)
! | style="background:#c8e6c9;" | Високий
|-
| КНЕДП ДПС
| TAX_CSK
| Файловий ключ / ІІТ / локальне підписання
| Часто застосовується для для податкових документів. Поле
! SEO-опис
POST /api/v1/signature/callback/{provider_code}
платформа повинна логувати:
користувач системи підписує документ поза системою та завантажує результат. | Документ стає VERIFY_ERROR. v
<pre>
return existing
=== Варіант 4. 7.4. Ручне завантаження підпису ===
! | XAdES. |}
До MVP входить:
"document_id": str(document.id),
"idempotency_key": command.idempotency_key,
'''Критично критично:''' бізнес-система не повинна напряму залежати від конкретного сервісного центру підпису. |-
| signature_request_id
| uuid
| Заявка. | style="background:#eeeeee;" | Сірий
|}
"expires_at": command.expires_at,
== 34. Відкриті питання ==
! |-
| AC-3
| Адміністратор створює провайдера файлового КЕП. |-
| provider_code
| Провайдер підпису. return signature_file
* реалізувати dashboard API;
* реалізувати списки проблемних документів;
* реалізувати статистику по провайдерах;
* реалізувати експорт журналу. |}
request.error_message = str(exc)
async def verify_signature(signature_request_id: str, signature_file_id: str, db: "Session") -> None:
"callback_context": {
<syntaxhighlight lang="python">
GET /api/v1/signature/providers
== 14. технічна архітектура рішення для бізнесу ==
<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">
=== 17.12. Dashboard ===
K2 ERP / CRM / Website
<syntaxhighlight lang="json">
== 28. Безпека ==
=== 24.5. Перевірка підпису ===
<syntaxhighlight lang="python">
! Dashboard показує результат. Тип підпису
=== 14.1. Загальна схема ===
request.status = "MANUAL_REVIEW"
"full_name": "Іван Петренко",
== 18. Приклад запиту на створення заявки ==
<syntaxhighlight lang="python">
<pre>
! Обраний adapter створює сесію або локальну операцію підпису. |-
| Документ змінено
| Підпис здатна бути накладений на стару версію. | document.pdf + document.pdf.p7s
|-
| Вкладений підпис
| ENVELOPED
| Підпис вбудований у документ або XML. |-
| file_hash_sha256
| varchar
| Hash файлу. "file_id": "file-001",
<pre>
! |-
| name
| varchar
| Назва. signature_session_repository.create(
* реалізувати VerificationService;
* реалізувати перевірку p7s;
* реалізувати перевірку контейнерів;
* реалізувати статуси перевірки;
* реалізувати ручну перевірку. |-
| priority
| integer
| Пріоритет у списку. | style="background:#ef9a9a;" | Червоний
|-
| Hash не збігається
| HASH_MISMATCH
| Підпис не відповідає версії документа. Очікує
{| class="wikitable"
)
{| class="wikitable"
! Колір
raise BusinessError("Document cannot accept signature in current status")
db=db,
pass
=== Варіант 1. 7.1. Хмарний підпис через API ===
adapter = provider_router.get_adapter(provider.code)
data={
10. | style="background:#ffcc80;" | Помаранчевий
|-
| Прострочено
| EXPIRED
| Строк підписання минув. |-
| old_status
| varchar
| Старий статус. |-
| Комунікація
| Browser → Local Agent → Crypto Library → результат у K2 ERP. |-
| SignatureResultError
| Не вдалося отримати підпис. provider = provider_repository.get_by_code(db, command.provider_code)
"signer": {
! | Вимкнути інтеграцію, повідомити адміністратора. SEO-опис
"signature_request_id": request.id,
db=db,
<pre>
! | Вони підсвічуються помаранчевим. | платформа показує статус UNAVAILABLE. |-
| signature_type
| varchar
| DETACHED, ENVELOPED, CONTAINER. №
data={
=== Етап 3. Уніфікований інтерфейс ===
! Причина
5. |-
| file_id
| uuid
| Файл у сховищі. * Офіційна сторінка Дія.Підпис. |-
| Ризики
| Не можна зберігати пароль до ключа на сервері. | style="background:#eeeeee;" | Сірий
|}
=== 23.8. signature_verifications ===
{| class="wikitable"
[[Категорія:ДПС]]
sha256(file_bytes)
платформа повинна не допускати дублювання заявок і підписів. |-
| Dashboard API
| інформаційні дані для контролю. Код
[[Категорія:K2 ERP]]
</pre>
=== 17.5. Створення заявки на підпис ===
except Exception as exc:
|-
| Дія.Підпис
| DIIA_SIGN
| API / QR / deep link / callback
| Хмарний сценарій через застосунок Дія. | Опційно
|}
data={
},
* перелік провайдерів підпису, які потрібно підтримати в MVP;
* офіційну документацію кожного API-провайдера;
* credentials для хмарних сервісів;
* тестові ключі або тестові акаунти;
* список форматів документів;
* вимоги до підпису: detached, embedded, container;
* правила перевірки підпису;
* список КНЕДП, які треба підтримати;
* вимоги до зберігання документів;
* вимоги до журналювання;
* вимоги до K2 ERP;
* вимоги до UI підписанта;
* вимоги до мобільного сценарію;
* вимоги до довгострокового архіву. Код
"k2_entity": "contract",
)
request.status = "VERIFY_ERROR"
=== 27.3. Проблемні документи ===
</div>
"k2_entity_id": "contract-001"
{| class="wikitable"
document = document_repository.get_by_id(db, document_id)
платформа повинна забезпечити:
"tax_id": "1234567890"
! Поле
)
== 3. Підтримувані провайдери підпису ==
|
№
GET /api/v1/signature/documents/{document_id}/signature-file
| -
|
created_at
|
timestamp
|
-
|
Callback дублюється
|
style="background:#ef9a9a;" | Критично
|
| Прострочено
|
Не підписано у строк. Єдиний API підписання
|
KPI
if not await adapter.verify_callback(request, payload):
request.status = "VERIFIED"
До MVP не входить:
користувач системи використовує файловий КЕП. Компонент
|
-
|
event_type
|
varchar
|
style="background:#ef9a9a;" | Критично
|
| Помилки перевірки
|
-
|
file_type
|
varchar
|
-
|
certificate_info
|
jsonb
|
-
|
AC-21
|
-
|
Створення сесії
|
-
|
expires_at
|
timestamp
|
-
|
result
|
varchar
|
-
|
SignerMismatchError
|
style="background:#ffcc80;" | Помаранчевий
|
| Ручна перевірка
|
MANUAL_REVIEW
|
-
|
integration_type
|
varchar
|
-
|
new_status
|
varchar
|
Trust list / manual review. Критерій
12. Статуси провайдера
| "signed_at": result.signed_at,
|
-
|
is_active
|
boolean
|
Активність. Статус
30.6. Dashboard
|
Поле
| -
|
ManualUploadAdapter
|
Ручне завантаження p7s / контейнера. Критерій
я хочу вмикати або вимикати провайдерів підпису,
|
| AC-15
|
користувач системи завантажує p7s. Сутність
signature_file = signature_file_repository.get_by_id(db, signature_file_id)
|
| Дія.Підпис
|
18
|
92
|
90
|
2
|
Норма
|
| Приват24 SmartID
|
12
|
64
|
63
|
1
|
Норма
|
| Файловий КЕП
|
6
|
40
|
37
|
3
|
Контроль
|
| ІІТ / ЦСК
|
4
|
52
|
51
|
1
|
Норма
|
| Ручне завантаження
|
3
|
12
|
10
|
2
|
Ручна перевірка
|
async def check_connection(self) -> dict:
pass
)
signature_validator.validate_document_for_signing(document, command)
},
30.3. Підписання
signature_verification_repository.create(
|
| 07.05.2026
|
Договір №123
|
Дія.Підпис
|
Іван Петренко
|
Прострочено
|
Не завершено підписання
|
Створити нову заявку
|
| 07.05.2026
|
Акт №45
|
SmartID
|
Олена Сидоренко
|
Помилка перевірки
|
Hash не збігається
|
Ручна перевірка
|
| 07.05.2026
|
Звіт XML
|
Файловий КЕП
|
Бухгалтер
|
Помилка
|
Невірний пароль ключа
|
Повторити підписання
|
"document_id": document.id,
25. Обробка помилок
- HTTPS для всіх endpoint-ів;
- зберігання секретів у secret storage;
- шифрування credentials;
- заборону зберігання пароля до файлового КЕП;
- заборону передачі приватного ключа на backend, якщо застосовується для локальний агент;
- перевірку callback signature;
- ідемпотентність callback;
- контроль версій документа;
- контроль hash;
- рольову модель;
- маскування персональних даних у логах;
- журнал усіх дій;
- контроль доступу до файлів;
- окремі права на ручну перевірку;
- окремі права на зміну провайдера;
- окремі права на повторне підписання. |-
|
document_type
|
varchar
|
CONTRACT, ACT, REPORT. Провайдер
async def upload_manual_signature(document_id: str, file: "UploadedFile", user: "User", db: "Session"):
щоб не реалізовувати окрему логіку для кожного сервісного центру. | style="background:#fff9c4;" | Жовтий
|
| Очікує підпису
|
WAITING_SIGNATURE
|
розроблена заявка на підпис. Статус
expected_signer_id=request.signer_id,
|
v
request.status = "WAITING_SIGNATURE"
- єдиний API для всіх видів підписання;
- підтримку декількох провайдерів підпису;
- створення заявки на підписання;
- підготовку документа до підпису;
- розрахунок hash документа;
- підписання PDF, XML, JSON, DOCX, ZIP або довільного файлу;
- підтримку відокремленого підпису;
- підтримку вкладеного підпису;
- підтримку підписаного контейнера;
- отримання результату підписання;
- перевірку підпису;
- перевірку цілісності документа;
- перевірку підписанта;
- перевірку сертифіката;
- збереження файлів підпису;
- збереження підписаних документів;
- журналювання всіх подій;
- dashboard контролю;
- fallback-сценарії для ручного підписання. Без цього неможливо правильно перевіряти й використовувати результат. |-
|
Помилка
|
}
async def get_session_status(self, session_id: str) -> dict:
|
style="background:#ef9a9a;" | Червоний
|
| Сертифікат відкликаний
|
CERT_REVOKED
|
-
|
AC-13
|
-
|
AC-2
|
-
|
base_url
|
varchar
|
-
|
id
|
uuid
|
style="background:#ef9a9a;" | Червоний
|
| Ручна перевірка
|
MANUAL_REVIEW
|
Потрібне втручання адміністратора. SEO-опис
Сервіс повинен забезпечити:
| -
|
Document
|
Документ, який потрібно підписати. Очікуваний результат
| -
|
file_size
|
integer
|
-
|
Signature Storage
|
Зберігає підписані файли. Створюється signature_request. Колір
|
-
|
AuthError
|
-
|
Вибір провайдера
|
}
@abstractmethod
* timeout;
* HTTP 429;
* HTTP 500;
* HTTP 502;
* HTTP 503;
* HTTP 504;
* тимчасової недоступності провайдера;
* тимчасової помилки polling;
* тимчасової помилки отримання результату;
* тимчасової помилки перевірки;
* повторного callback з тим самим callback_id. |}
<syntaxhighlight lang="python">
! SEO-опис
K2 ERP / Dashboard / Archive
GET /api/v1/signature/signature-requests/{request_id}/status
|-
| Створення документа
| Тип, номер, hash, реліз системи. SEO-опис
{| class="wikitable"
payload={
! |-
| signature_request_id
| uuid
| Заявка. "signer_type": "CLIENT",
* повна технічна підтримка всіх КНЕДП України;
* повна технічна підтримка всіх форматів ASIC / XAdES;
* складний UI локального агента;
* архів довгострокового зберігання за окремим регламентом;
* автоматичне юридичне трактування підпису;
* повна інтеграційні функції ERP з усіма ЕДО-системами;
* власний КНЕДП. |-
| supported_signature_types
| jsonb
| DETACHED, ENVELOPED, CONTAINER. | style="background:#ef9a9a;" | Червоний
|-
| Вимкнений
| DISABLED
| Провайдер вимкнений адміністратором. №
callback_id = callback_service.get_callback_id(provider_code, payload)
async def cancel_session(self, session_id: str) -> dict:
6. * Офіційна сторінка підписання документів на порталі Дія. |-
| status
| varchar
| ACTIVE, DISABLED, UNAVAILABLE. Параметр
! |-
| XML-підпис
| XML_SIGN
| Підпис XML-документа. | style="background:#ef9a9a;" | Червоний
|-
| Не той підписант
| SIGNER_MISMATCH
| Підписант не відповідає очікуваному. |-
| callback_url
| varchar
| Callback URL. Пріоритет
{| class="wikitable"
! |}
def get_adapter(self, provider_code: str) -> SignatureProviderAdapter:
! |-
| document_version_id
| uuid
| реліз системи. | Запропонувати інший провайдер. |-
| mime_type
| varchar
| MIME type. |-
| status
| varchar
| Статус. | Вони підсвічуються червоним. |-
| Signature Session
| Сесія підписання. | Опційно
|-
| JSON
| Технічні документи або структуровані payload. |-
| FileKeyAdapter
| Підписання файловим КЕП. | Створення сесії, підписання, тест. |-
| signer_identifier
| varchar
| РНОКПП / ЄДРПОУ, якщо доступно. ! | Провайдер доступний для локального підпису. |}
return self.adapters [provider_code]
== 10. User Story ==
! | style="background:#fff9c4;" | Важливий
|-
| ІІТ / користувач системи ЦСК-1
| IIT_CSK
| DLL / COM / SO / Java / JS / локальний агент
| Універсальний локальний crypto-provider для українських КЕП. |-
| Hash-підпис
| HASH_SIGN
| Провайдер підписує hash, а не сам файл. Коментар
=== 10.4. Керівник бачить контроль ===
provider_code=provider_code,
signature_file = signature_file_repository.create(
=== 15.2. Provider Router ===
<pre>
=== 17.7. Callback від провайдера ===
! from abc import ABC, abstractmethod
|-
| Активний
| ACTIVE
| Провайдер доступний для підписання. |-
| code
| varchar
| DIIA_SIGN, PRIVAT24_SMARTID, IIT_CSK тощо. "raw_request": payload,
* реалізувати SignatureProviderAdapter;
* реалізувати ProviderRouter;
* реалізувати PolicyEngine;
* реалізувати SignatureRequestService. |-
| AC-19
| виступає як помилки підписання. # Чи потрібен локальний агент для файлового КЕП? |-
| signer_id
| Підписант. |-
| PDF-підпис
| PDF_SIGN
| Підпис у PDF або супровідний p7s. | style="background:#ef9a9a;" | Червоний
|-
| Сертифікат прострочений
| CERT_EXPIRED
| Сертифікат недійсний. {| class="wikitable"
class SignatureProviderAdapter(ABC):
== 5. Підтримувані формати ==
existing = signature_request_repository.get_by_idempotency_key(
payload = await request.json()
async def verify_signature(self, document: bytes, signature: bytes, options: dict) -> dict:
! |-
| Реалізація
| Localhost API, WebSocket або protocol handler. |-
| expires_at
| timestamp
| Строк дії. №
=== 24.1. Створення заявки ===
[[Категорія:SmartID]]
|-
| id
| uuid
| ID події. | Помилка API, помилка перевірки. # Чи потрібен експорт журналу в Excel? # Чи потрібна інтеграційні функції ERP з K2 ERP задачами? |-
| AC-6
| Документ змінено після заявки. Параметр
class SignaturePolicyEngine:
signature_queue.enqueue(
=== 17.1. Список провайдерів ===
! |-
| Signature Policy Engine
| Визначає, які провайдери доступні для документа. # Чи виступає як офіційний API-доступ до SmartID? | Так
|-
| DOCX
| Документи Word. # Які формати документів підписуємо: PDF, XML, DOCX, ZIP? |-
| Перевірка підпису
| Результат, підписант, сертифікат. |-
| AC-10
| користувач системи обирає файловий КЕП. | style="background:#c8e6c9;" | Зелений
|-
| Невалідний
| INVALID
| Підпис не пройшов перевірку. |-
| Локальний агент недоступний
| користувач системи не здатна підписати файловим КЕП. №
Перед створенням заявки платформа повинна перевірити:
raise BusinessError("Provider is not allowed for this document")
=== 23.1. signature_providers ===
if result.code == "VALID":
* наявність документа;
* наявність актуальної версії документа;
* наявність file_id;
* доступність файлу у сховищі;
* розмір файлу;
* MIME type;
* hash документа;
* тип документа;
* тип підписанта;
* доступні провайдери для документа;
* чи підтримує провайдер формат документа;
* чи підтримує провайдер потрібний тип підпису;
* чи не був документ змінений після створення заявки;
* чи не підписаний документ цим підписантом раніше;
* чи виступає як idempotency_key;
* чи дає можливість бізнес-процес підписання. |-
| UnsupportedFormatError
| Провайдер не підтримує формат. |-
| document_name
| varchar
| Назва. Статус
task_name="verify_uploaded_signature",
"signature_file_id": str(signature_file.id),
! |-
| source
| varchar
| K2_ERP, PYTHON_SERVICE, PROVIDER, USER. * Документація ІІТ / користувач системи ЦСК-1. |-
| entity_type
| varchar
| document, request, session, verification, provider. | платформа зберігає файл і запускає перевірку. | style="background:#c8e6c9;" | Норма
|-
| Помилки підпису
| Помилки провайдера або локального агента. Формат
"raw_response": response,
<div style="border-left: 6px solid #1565c0; background: #e3f2fd; padding: 12px 16px; margin: 16px 0;">
! Поле
[[Категорія:Python]]
! | DRAFT, DISABLED, CANCELLED. SEO-опис
=== Етап 7. Перевірка підпису ===
{| class="wikitable"
"expires_at": "2026-05-07T14:30:00+03:00"
</div>
застосовується для для Дія.Підпис, SmartID та інших хмарних КЕП. | Так
|-
| XML
| Податкова, формування звітів, структуровані документи. |}
== 17. API Python-сервісу ==
@router.post("/api/v1/signature/callback/{provider_code}")
</syntaxhighlight>
provider = request.provider
"document_type": "CONTRACT",
|-
| AC-8
| користувач системи обирає Дія.Підпис. | style="background:#fff9c4;" | Важливий
|-
| Інші КНЕДП
| OTHER_QTSP
| Через ІІТ / файловий ключ / API
| Підключаються через адаптер. | Маскування, secure logging. | style="background:#ffcc80;" | Помаранчевий
|-
| Помилка авторизації
| AUTH_ERROR
| Невірні credentials. |-
| idempotency_key
| varchar
| Ключ дедублікації. Документ
3. |-
| status
| varchar
| Статус. |-
| supported_signature_types
| jsonb
| DETACHED, ENVELOPED, CONTAINER. Вибір адаптера
__TOC__
idempotency_key=command.idempotency_key,
|
|-- DiiaSignAdapter
|-- SmartIDAdapter
|-- FileKeyAdapter
|-- IITAdapter
|-- TaxCSKAdapter
|-- ManualUploadAdapter
|
"signature_request_id": request.id,
@abstractmethod
|
| 4. | Dashboard, список документів, картка документа. |}
"provider_name": result.provider_name,
if document_type == "TAX_REPORT":
return ["IIT_CSK", "TAX_CSK", "FILE_KEY"]
if signer_type == "CLIENT":
! |-
| integration_type
| varchar
| API, LOCAL_AGENT, FILE_KEY, MANUAL_UPLOAD. |-
| created_by
| Хто створив версію. |-
| DocumentChangedError
| Документ змінився після заявки. Поле
=== 10.3. Адміністратор керує провайдерами ===
=== 17.2. Перевірка провайдера ===
|-
| API провайдера недоступний
| Хмарний підпис не функціонує. | style="background:#c8e6c9;" | Високий
|-
| ПриватБанк SmartID / Приват24
| PRIVAT24_SMARTID
| API / polling / callback / ручне завантаження
| Хмарний КЕП ПриватБанку. Параметр
|-
| конкурентні переваги
| користувач системи не передає файл ключа в систему. Тип
* реалізувати завантаження документа;
* реалізувати версіонування;
* реалізувати hash;
* реалізувати дедублікацію. | style="background:#bbdefb;" | Блакитний
|-
| Тимчасово недоступний
| UNAVAILABLE
| API або локальний агент недоступний. SEO-опис
</syntaxhighlight>
"status": "CREATING",
=== 14.2. Основні компоненти ===
== 11. Статуси документа ==
"status": "ACTIVE",
)
=== 27.2. Приклад dashboard по провайдерах ===
|-
| document_version_id
| ID версії. "result": result.code,
{| class="wikitable"
"file_name": "contract_123.pdf",
"file_id": stored_file.id,
12. |-
| Різні формати підписів
| Не всі формати однаково перевіряються. |-
| Недоліки
| Менше автоматизації. |-
| created_at
| timestamp
| Дата. | style="background:#fff9c4;" | Важливий
|-
| Ручне завантаження підпису
| MANUAL_UPLOAD
| Upload p7s / container
| Fallback-сценарій. |}
! |-
| is_active
| boolean
| Активність. |}
=== 17.6. Отримання статусу заявки ===
task_name="start_signature_session",
=== 24.3. Callback controller ===
! |-
| style="background:#bbdefb;" | Блакитний
| #bbdefb
| операційна дія виконується або тестовий режим. |}
payload = signature_mapper.to_provider_payload(request)
== 32. Етапи реалізації ==
! | style="background:#ffcc80;" | Резервний
|}
"signer_name": result.signer_name,
=== 30.2. Документи ===
@abstractmethod
2. | Створюється сесія SmartID. | платформа пропонує іншого провайдера або блокує заявку. Дія системи
result=result,
* єдиний Signature API;
* довідник провайдерів;
* Provider Router;
* Signature Provider Interface;
* технічна підтримка Дія.Підпис як адаптера, якщо виступає як API-доступ;
* технічна підтримка SmartID як адаптера, якщо виступає як API-доступ;
* технічна підтримка ручного завантаження p7s;
* технічна підтримка файлового КЕП через локальний агент або ІІТ-адаптер;
* версіонування документа;
* hash документа;
* створення заявки;
* статуси;
* callback endpoint;
* polling worker;
* збереження підпису;
* перевірка підпису;
* dashboard API;
* журнал подій;
* retry;
* unit-тести;
* mock adapters. SEO-опис
! | style="background:#ef9a9a;" | Червоний
|-
| Невідомий формат
| UNKNOWN_FORMAT
| Формат підпису не розпізнано. |}
},
13. Єдина логіка кольорів
17.9. Завантаження файлу підпису
async def signature_callback(provider_code: str, request: Request):
|
| id
|
uuid
|
-
|
provider_name
|
varchar
|
-
|
file_id
|
uuid
|
-
|
Особливості
|
Статус стає SIGNED. Тип
"qr_payload": response.get("qr_payload"),
|
| id
|
uuid
|
Опційно
|
| P7S
|
-
|
external_document_id
|
varchar
|
-
|
source
|
varchar
|
Опційно
|
| ZIP
|
Пакет документів. Тип помилки
data={
| -
|
document_date
|
date
|
INVALIDATED. Поле
|
}
|
style="background:#c8e6c9;" | Зелений
|
| Тестовий режим
|
TEST_MODE
|
-
|
status
|
varchar
|
-
|
DiiaSignAdapter
|
-
|
Рекомендація
|
-
|
provider_session_id
|
-
|
file_hash_sha256
|
-
|
certificate_settings
|
jsonb
|
Прострочення, недоступність, відхилення. if existing:
- офіційний сервіс КЕП Дії. |}
критично: різні провайдери мають різні способи роботи: API, callback, polling, QR/deep link, файловий ключ, локальний агент, DLL/SO-бібліотека, JavaScript-бібліотека або ручне завантаження p7s. if provider_code not in self.adapters:
- реалізувати DiiaSignAdapter;
- реалізувати SmartIDAdapter;
- реалізувати ManualUploadAdapter;
- реалізувати FileKey/IITAdapter;
- реалізувати mock adapters. Критерій
|
| AC-1
|
-
|
base_url
|
varchar
|
URL API, якщо виступає як. технічна підтримка в MVP
|
Критерій
"signature_type": "DETACHED",
|
-
|
document_number
|
varchar
|
-
|
Callback Controller
|
SHA-256 hash. |-
|
file_size
|
-
|
AC-17
|
-
|
Signature File
|
-
|
current_version_id
|
uuid
|
-
|
Особливості
|
style="background:#f3e5f5;" | Фіолетовий
|
|
Колір
| ProviderNotAllowedError
|
Провайдер не дозволений для документа. request = signature_request_repository.create(
|
Поле
|
-
|
Signature Request
|
-
|
Червоний
|
#ef9a9a
|
-
|
version_number
|
integer
|
-
|
mime_type
|
-
|
callback_url
|
varchar
|
style="background:#ef9a9a;" | Червоний
|
| Помилка перевірки
|
VERIFY_ERROR
|
Підпис не пройшов перевірку. Параметр
Retry заборонений для:
allowed_providers = policy_engine.get_allowed_providers(
{| class="wikitable"
=== 15.3. Signature Policy Engine ===
async def get_signature_result(self, session_id: str) -> dict:
== 1. Мета ==
db=db,
! "document_number": "123",
Приклад:
7.=== Етап 2. Базовий Python-сервіс ===
! |-
| signer_name
| varchar
| ПІБ підписанта. |-
| Локальне підписання
| Тип агента, результат, без пароля ключа. Дата
Як керівник,
! | style="background:#e3f2fd;" | інформаційні матеріали
|-
| Очікують підпису
| Активні заявки. |-
| provider_id
| uuid
| Провайдер. |-
| callback_event_id
| ID callback-події. |-
| file_hash_sha256
| varchar
| Hash. Підписано
{
! | MANUAL_REVIEW. SEO-опис
request.status = "SIGN_ERROR"
! |}
=== 30.5. Ручне завантаження ===
POST /api/v1/signature/documents/{document_id}/upload-signature
"external_document_id": "K2-DOC-2026-000123",
POST /api/v1/signature/documents/{document_id}/signature-requests
8. |}
<pre>
</div>
<pre>
if signer_type == "EMPLOYEE":
return ["FILE_KEY", "IIT_CSK", "PRIVAT24_SMARTID"]
return ["DIIA_SIGN", "PRIVAT24_SMARTID", "FILE_KEY", "MANUAL_UPLOAD"]
</syntaxhighlight>
== 16. * Внутрішня документація K2 ERP. |}
! |-
| Підписаний контейнер
| CONTAINER
| Документ і підпис зберігаються в контейнері. | Провайдер доступний у списку. | style="background:#c8e6c9;" | Зелений
|-
| Підпис перевірено
| VERIFIED
| Підпис валідний. K2 ERP створює документ. |-
| Обов'язкова умова
| Автоматична перевірка підпису після завантаження. | платформа дає можливість створити заявку. накладання електронного підпису через різних провайдерів України: '''Дія.Підпис''' забезпечується через '''Головна ідея:''' розробити єдиний Python-сервіс підписання. Дія
! | Заблокувати заявку. | MANUAL_REVIEW. |-
| raw_request
| jsonb
| Запит. |}
! return ["DIIA_SIGN", "PRIVAT24_SMARTID", "MANUAL_UPLOAD"]
* додати rate limiting;
* додати alerting;
* додати dead letter queue;
* додати backup файлів;
* додати моніторинг провайдерів;
* додати secure secret storage. Ключ
=== 23.7. signature_files ===
! ! |-
| provider_name
| varchar
| КНЕДП / провайдер сертифіката. |-
| AC-14
| Підписант не збігається. SEO-опис
=== 24.4. Ручне завантаження підпису ===
"deep_link": response.get("deep_link"),
Python Unified Signature Service
|
Значення
v
|
Код
2. Область сценарії використання
response = await adapter.create_signature_session(payload)
33. Ризики
"source": "MANUAL_UPLOAD",
},
|
| Signature Provider
|
-
|
deep_link
|
text
|
-
|
idempotency_key
|
}
|
користувач системи підтверджує підпис. конфігурація провайдера ==
|
|
-
|
credentials_encrypted
|
jsonb
|
Зашифровані credentials. Провайдер
- визначити провайдерів MVP;
- отримати документацію Дія.Підпис;
- отримати документацію SmartID;
- визначити локальну бібліотеку для файлового КЕП;
- визначити формати підпису;
- визначити правила перевірки. ! |-
|
AC-9
|
-
|
Document Version Service
|
Документ стає VERIFIED. |-
|
Фіолетовий
|
#f3e5f5
|
Запропонувати інший провайдер. |-
|
ProviderUnavailableError
|
}
async def create_signature_request(command: "CreateSignatureRequestCommand", db: "Session") -> "SignatureRequest":
20. Hash і версії документа
|
-
|
signature_format
|
varchar
|
-
|
created_at
|
-
|
entity_id
|
uuid
|
ID сутності. Поле
)
verification_queue.enqueue(
return {"status": "already_processed"}
- цілісність документа;
- відповідність підпису конкретній версії документа;
- валідність підпису;
- валідність сертифіката;
- підписанта;
- дату та час підписання;
- chain trust;
- статус відкликання сертифіката, якщо доступно;
- формат підпису;
- відповідність очікуваному типу підписанта;
- чи не змінювався документ після підпису. | XML із XAdES.
'''Управлінський результат:''' керівник і відповідальні особи повинні бачити, через який провайдер підписано документ, хто підписав, коли, чи пройшла перевірка, які документи очікують підпису, які прострочені, які мають помилки або потребують ручної перевірки. | style="background:#eeeeee;" | Сірий
|-
| Готовий до підпису
| READY_TO_SIGN
| Документ перевірено. SEO-опис
|-
| Валідний
| VALID
| Підпис успішно перевірений. SEO-опис
Можливі результати:
я хочу викликати один API підписання,
== 21. Перевірка підпису ==
=== Варіант 2. 7.2. Локальне підписання файловим ключем ===
== 6. Передумови ==
elif result.code in ["SIGNER_MISMATCH", "UNKNOWN_FORMAT"]:
Кожна реліз системи документа повинна мати:
"provider_id": provider.id,
== 26. Retry-логіка ==
=== 23.5. signature_requests ===
class SignatureProviderRouter:
! |}
=== Етап 8. Dashboard та аудит ===
<pre>
"document_name": "Договір поставки №123",
@abstractmethod
if callback_repository.exists(callback_id):
db=db,
|-
| id
| uuid
| ID провайдера. |-
| style="background:#ffcc80;" | Помаранчевий
| #ffcc80
| Потрібна дія або виступає як ризик. {| class="wikitable"
! pass
4. ! |-
| provider_id
| uuid
| Провайдер. |-
| VerificationError
| Підпис не пройшов перевірку.=== 23.6. signature_sessions ===
)
* договорів;
* актів виконаних робіт;
* рахунків;
* заяв;
* кадрових документів;
* первинних бухгалтерських документів;
* податкових документів;
* документів ЕДО;
* заявок у CRM;
* документів K2 ERP;
* пакетів документів;
* XML-звітів;
* PDF-документів;
* підтвердження юридично значущих дій. |-
| KeyReadError
| Не вдалося прочитати файловий ключ. ! |-
| created_at
| timestamp
| Дата. # Чи виступає як офіційний API-доступ до Дія.Підпис? Код
=== 23.4. sign_document_versions ===
|-
| AC-12
| Підпис валідний. # Чи потрібно підписувати документи співробітниками? | Він бачить статистику по всіх провайдерах. external_document_id=command.external_document_id,
"signer_id": command.signer_id,
! SEO-опис
! |-
| Callback
| provider_code, callback_id, raw payload. Де застосовується для
provider_code=provider_code,
=== 27.1. Основні KPI ===
9. SEO-опис
signer_type=command.signer.signer_type,
На ПК користувача встановлюється агент підпису. | Статус стає VERIFY_ERROR. |-
| Невідомий КНЕДП
| Сертифікат не розпізнано. |-
| provider_id
| uuid
| Провайдер. | Стара заявка стає INVALIDATED. | Провайдер доступний у списку. Тип
! |-
| signature_request_id
| uuid
| Заявка. Результат
signature_file_id=signature_file.file_id,
! |-
| provider_session_id
| varchar
| ID сесії провайдера. Тип
Verification Service повинен перевіряти:
adapter = provider_router.get_adapter(provider_code)
verifier = verification_service_factory.get_verifier(signature_file.signature_format)
</pre>
! SEO-опис
! SEO-опис
raise ValueError(f"Unsupported signature provider: {provider_code}")
{| class="wikitable"
if command.provider_code not in allowed_providers:
else:
|
| PDF
|
Найчастіший формат договорів, актів, рахунків. POST /api/v1/signature/documents/{document_id}/verify
},
"file_hash_sha256": stored_file.sha256,
|
-
|
конкурентні переваги
|
-
|
Verification Service
|
style="background:#c8e6c9;" | Високий
|
| Файловий КЕП
|
FILE_KEY
|
Локальне підписання
|
Ключ типу Key-6.dat або інший файловий контейнер. Ризик
pass
22. Дедублікація
|
Провайдер
Як розробник K2 ERP,
Як користувач системи,
Для реалізації задачі необхідно отримати:
db.commit()
|
| external_document_id
|
-
|
Signature Integration
|
style="background:#ffcc80;" | Потрібна дія
|
| Ручна перевірка
|
Потрібне втручання. Тип
15. Unified Signature Provider Interface
|
</syntaxhighlight>
|
| id
|
uuid
|
-
|
AC-20
|
-
|
settings
|
jsonb
|
Технічні конфігурація. Колір
29. Логування та аудит
"signer_identifier": result.signer_identifier,
|
-
|
supported_formats
|
jsonb
|
-
|
AC-7
|
Idempotent callback. SEO-опис
|
| provider_code
|
varchar
|
Інструкція, fallback, healthcheck агента.</syntaxhighlight>
- створити FastAPI-проєкт;
- налаштувати PostgreSQL;
- створити моделі провайдерів, документів, заявок, сесій;
- налаштувати Alembic;
- реалізувати healthcheck. |-
|
supported_formats
|
jsonb
|
PDF, XML, P7S, ASIC. Поле
|
Очікуваний результат
Критично критично: якщо документ змінено після створення заявки, попередню заявку потрібно перевести в INVALIDATED. |}
17.8. Ручне завантаження підпису
callback_id=callback_id,
signature_processor.process_provider_result(
| id
|
uuid
|
-
|
Ризики
|
style="background:#ffcc80;" | Помаранчевий
|
| Помилка підписання
|
SIGN_ERROR
|
Помилка під час підписання. ! HTML
</syntaxhighlight>
finally:
GET /api/v1/signature/documents/{document_id}/available-providers
|
-
|
raw_response
|
jsonb
|
Відповідь. Критерій
router = APIRouter()
|
! },
щоб контролювати прострочені, помилкові та непідписані документи. | Очікує підпису, вибір провайдера. | SIGN_ERROR або retry. SEO-опис
Retry дозволений для:
! |-
| Polling
| Старий статус, новий статус. |-
| AC-4
| Провайдер недоступний. Призначення
GET /api/v1/signature/documents/{document_id}/signed-file
[[Категорія:API]]
Signature Provider Router
{| class="wikitable"
|-
| AC-5
| Документ валідний. | style="background:#f3e5f5;" | Контроль
|}
if document.status not in ["READY_TO_SIGN", "WAITING_SIGNATURE", "SIGN_ERROR"]:
! |-
| Verification Result
| Результат перевірки. Тип
! Очікуваний результат
=== Етап 6. Callback / polling ===
|-
| конкурентні переваги
| функціонує з багатьма КНЕДП. | Статус стає MANUAL_REVIEW. |-
| Document Version
| реліз системи документа, яка передається на підпис. * Офіційна партнерська документація конкретних провайдерів. |-
| Provider Adapter
| Програмний адаптер конкретного провайдера. db.commit()
return request
Signature Storage + Verification Service
</div>
<pre>
payload=payload,
|-
| Документів створено
| Загальна кількість документів. Signature Storage зберігає файл підпису / контейнер. |-
| Audit Event
| Подія журналу. | Signature format detector і окремі verifier-и. |-
| provider_code
| varchar
| Провайдер. | style="background:#c8e6c9;" | Зелений
|-
| Відхилено користувачем
| DECLINED_BY_USER
| користувач системи відмовився від підписання. |-
| document_version_id
| реліз системи документа. Подія
|-
| Підходить для
| Fallback або MVP без API. K2 ERP повинна працювати з єдиним інтерфейсом: створити заявку, отримати статус, отримати підпис, перевірити підпис, зберегти результат. Тип інтеграції
=== 17.4. Отримання доступних провайдерів для документа ===
{| class="wikitable"
! 1. | signed.pdf або pdf.p7s. # Чи потрібно перевіряти РНОКПП / ЄДРПОУ підписанта? | VERIFY_ERROR. |-
| style="background:#eeeeee;" | Сірий
| #eeeeee
| Чернетка, вимкнено, скасовано або архів. |-
| priority
| integer
| Пріоритет. | Створюється сесія Дії. K2 ERP отримує фінальний статус. | Так
|-
| XAdES
| XML-підпис. Стан
=== Етап 5. Документи та hash ===
== 24. Приклад Python-логіки ==
! | Відхилити callback. Код
POST /api/v1/signature/providers/{provider_code}/check-connection
<pre>
}
{| class="wikitable"
result = await adapter.parse_callback(payload)
! |-
| style="background:#fff9c4;" | Жовтий
| #fff9c4
| Очікування дії користувача. |-
| id
| uuid
| ID заявки. |}
<syntaxhighlight lang="python">
# Які провайдери мають бути в MVP?
return {"status": "ok"}
23.9. signature_events
"document_version_id": document.current_version_id,
|
| |
|
|
|
|
|
| |
|