| -
|
idempotency_key
|
varchar
|
-
|
Ручне завантаження
|
Хто завантажив, файл, hash.; характеристика
- реалізувати VerificationService;
- реалізувати перевірку p7s;
- реалізувати перевірку контейнерів;
- реалізувати статуси перевірки;
- реалізувати ручну перевірку.; Сутність
"full_name": "Іван Петренко",
23.6. signature_sessions
payload = await request.json()
30. Acceptance Criteria=== 23.4. sign_document_versions ===
pass
{| class="wikitable"
"signature_format": signature_format_detector.detect(file.filename, stored_file.bytes),
"signer_type": "CLIENT",
Verification Service повинен перевіряти:
=== 17.3.; Створення документа ===
!; | Показати інструкцію користувачу.; |-
| callback_url
| varchar
| Callback URL.; |-
| Signature Session
| Сесія підписання.; Тип
data={
13.; Єдина логіка кольорів23.3. sign_documentsif result.code == "VALID":
| id
|
uuid
|
-
|
Створення заявки
|
-
|
SignatureResultError
|
-
|
PDF-підпис
|
PDF_SIGN
|
Підпис у PDF або супровідний p7s.; характеристика
|
-
|
status
|
varchar
|
-
|
LocalAgentUnavailableError
|
-
|
AC-10
|
-
|
status
|
varchar
|
Статус.;== 19.; Валідація перед підписом ==
|
-
|
supported_signature_types
|
jsonb
|
-
|
Polling
|
Старий статус, новий статус.; # Чи потрібні нагадування про прострочення?; "file_type": "signature",
- створити FastAPI-проєкт;
- налаштувати PostgreSQL;
- створити моделі провайдерів, документів, заявок, сесій;
- налаштувати Alembic;
- реалізувати healthcheck.; signature_file = signature_file_repository.get_by_id(db, signature_file_id)
@abstractmethod
@abstractmethod
платформа повинна не допускати дублювання заявок і підписів.; # Чи потрібно підтримувати ІІТ DLL/SO напряму?; Пріоритет
| -
|
is_active
|
boolean
|
-
|
signer_name
|
varchar
|
-
|
AC-14
|
-
|
Помаранчевий
|
#ffcc80
|
-
|
переважні аспекти
|
-
|
provider_name
|
varchar
|
-
|
AC-21
|
}
24.1.; Створення заявки"provider_code": "DIIA_SIGN",
| -
|
Provider Router
|
Вибирає провайдера підпису.; )
|
| Чернетка
|
DRAFT
|
Документ створений, але ще не готовий до підпису.; Поле
"k2_entity_id": "contract-001"
|
| Валідний
|
VALID
|
-
|
callback_event_id
|
-
|
Hash-підпис
|
HASH_SIGN
|
style="background:#ffcc80;" | Потрібна дія
|
| Ручна перевірка
|
style="background:#ef9a9a;" | Червоний
|
| Сертифікат прострочений
|
CERT_EXPIRED
|
-
|
Ручне рішення для бізнесу
|
Хто ухвалив, коментар, дата.; Статус
)
@abstractmethod
</syntaxhighlight>
23.7. signature_files
)
GET /api/v1/signature/signature-requests/{request_id}/status
self.adapters = adapters
db=db,
Можливі результати:
|
-
|
Verification Service
|
-
|
Signature Integration
|
конфігурація конкретного провайдера.; №
data={
|
; Параметр
POST /api/v1/signature/documents/{document_id}/signature-requests
|
style="background:#c8e6c9;" | Норма
|
| Перевірено
|
-
|
AuthError
|
Показати користувачу помилку.; |-
|
document_date
|
date
|
style="background:#ef9a9a;" | Червоний
|
| Не той підписант
|
SIGNER_MISMATCH
|
Підписант не відповідає очікуваному.; Помилки
"document_date": "2026-05-07",
|
| AC-18
|
Керівник відкриває dashboard.;=== 30.6. Dashboard ===
|
-
|
Signature Request
|
-
|
Статуси
|
Запускається локальний або файловий сценарій.; |-
|
Блакитний
|
#bbdefb
|
операційна дія виконується або тестовий режим.; характеристика
request.document.status = "VERIFIED"
|-
| provider_code
| varchar
| DIIA_SIGN, PRIVAT24_SMARTID, IIT_CSK тощо.; |-
| deep_link
| text
| Deep link, якщо є собою.; # Чи потрібен довгостроковий архів підписаних документів?; |-
| Секрети потрапили в лог
| Ризик компрометації.; # Чи потрібно пакетне підписання?; | style="background:#ef9a9a;" | Червоний
|-
| Вимкнений
| DISABLED
| Провайдер вимкнений адміністратором.; Тип
3.; |-
| Реалізація
| Localhost API, WebSocket або protocol handler.; |-
| file_hash_sha256
| varchar
| Hash.; платформа отримує результат.; !; |-
| document_number
| varchar
| Номер.; Статус
{| class="wikitable"
== 14.; технічна архітектура рішення для бізнесу ==
!; Критерій
<pre>
=== Етап 8.; Dashboard та аудит ===
signature_verification_repository.create(
!; | Так
|-
| XML
| Податкова, формування звітів, структуровані документи.; |-
| document_version_id
| реліз документа.; |}
{| class="wikitable"
!; |
| 3.; |-
| supported_signature_types
| jsonb
| DETACHED, ENVELOPED, CONTAINER.; * Документація КНЕДП ДПС.; | Запропонувати інший провайдер.; | Прострочення, недоступність, відхилення.; |-
| Підходить для
| Робочих місць бухгалтерів, кадровиків, юристів.; |-
| Signature Policy Engine
| Визначає, які провайдери доступні для документа.; | style="background:#eeeeee;" | Сірий
|}
</syntaxhighlight>
користувач системи підписує документ поза системою та завантажує результат.; Тип підпису
|-
| id
| uuid
| ID інтеграції.; |-
| provider_id
| uuid
| Провайдер.; Тип
{| class="wikitable"
я хочу викликати один API підписання,
|-
| id
| uuid
| ID документа.; |-
| Ризики
| Залежність від зовнішнього API.; Перевірено
Сервіс повинен забезпечити:
8.; характеристика
[[Категорія:КЕП]]
== 27.; Dashboard керівника ==
|
| 1.; * зміненого документа;
* невалідного callback;
* відхилення користувачем;
* невідповідності підписанта;
* невалідного пароля файлового ключа;
* фінального статусу VERIFIED;
* ручного рішення для бізнесу адміністратора.; Подія
!; | Опційно
|}
"signer_id": command.signer_id,
"status": "CREATING",
* перелік провайдерів підпису, які потрібно підтримати в MVP;
* офіційну документацію кожного API-провайдера;
* credentials для хмарних сервісів;
* тестові ключі або тестові акаунти;
* список форматів документів;
* вимоги до підпису: detached, embedded, container;
* правила перевірки підпису;
* список КНЕДП, які треба підтримати;
* вимоги до зберігання документів;
* вимоги до журналювання;
* вимоги до K2 ERP;
* вимоги до UI підписанта;
* вимоги до мобільного сценарію;
* вимоги до довгострокового архіву.; | Опційно
|-
| ZIP
| Пакет документів.; | Створюється сесія Дії.; | style="background:#ffcc80;" | Резервний
|}
{| class="wikitable"
"expires_at": "2026-05-07T14:30:00+03:00"
"external_document_id": "K2-DOC-2026-000123",
<pre>
!; | Створюється сесія SmartID.; K2 ERP отримує фінальний статус.; | style="background:#f3e5f5;" | Фіолетовий
|-
| Скасовано
| CANCELLED
| Заявку або документ скасовано.;
17.5.; Створення заявки на підпис
- визначити провайдерів MVP;
- отримати документацію Дія.Підпис;
- отримати документацію SmartID;
- визначити локальну бібліотеку для файлового КЕП;
- визначити формати підпису;
- визначити правила перевірки.; | style="background:#c8e6c9;" | Зелений
|
| Відхилено користувачем
|
DECLINED_BY_USER
|
користувач системи відмовився від підписання.; Дата
request.status = "SIGN_ERROR"
)
callback_id=callback_id,
|
Провайдер доступний у списку.; Тип
Етап 3.; Уніфікований інтерфейс
|
; Як зменшити
|
; Очікуваний результат
payload = signature_mapper.to_provider_payload(request)
- цілісність документа;
- відповідність підпису конкретній версії документа;
- валідність підпису;
- валідність сертифіката;
- підписанта;
- дату та час підписання;
- chain trust;
- статус відкликання сертифіката, якщо доступно;
- формат підпису;
- відповідність очікуваному типу підписанта;
- чи не змінювався документ після підпису.; | style="background:#ef9a9a;" | Червоний
|
| Помилка перевірки
|
VERIFY_ERROR
|
Підпис не пройшов перевірку.; K2 ERP повинна працювати з єдиним інтерфейсом: створити заявку, отримати статус, отримати підпис, перевірити підпис, зберегти результат.; характеристика
Як адміністратор,
"document_version_id": document.current_version_id,
)
17.7.; Callback від провайдера
},
Варіант 3.; 7.3.; Підписання через локальний агент
db=db,
"expires_at": command.expires_at,
|
-
|
integration_type
|
varchar
|
API, LOCAL_AGENT, FILE_KEY, MANUAL_UPLOAD.; Очікуваний результат
|
Помилка API, помилка перевірки.; характеристика
|
| id
|
uuid
|
Документ стає VERIFY_ERROR.; Тип
|
Заблокувати заявку.; Підпис має накладатися тільки на конкретну версію документа.; | Trust list / manual review.; |-
|
created_at
|
timestamp
|
платформа пропонує іншого провайдера або блокує заявку.; №
}
|
; Тип
18.; Приклад запиту на створення заявки
|
; характеристика
7.; Варіанти реалізації
pass
|
MANUAL_REVIEW.; |}
1.; Колір
я хочу вмикати або вимикати провайдерів підпису,
"idempotency_key": "K2-DOC-2026-000123-sign-v1",
async def check_connection(self) -> dict:
|
; Колір
|
Провайдер доступний для локального підпису.; |-
|
status
|
varchar
|
}
db=db,
"signature_request_id": None,
|
; характеристика
|
| Документів створено
|
Загальна кількість документів.; характеристика
Приклад:
@abstractmethod
|
; Критерій
|
| Дія.Підпис
|
DIIA_SIGN
|
API / QR / deep link / callback
|
Хмарний сценарій через застосунок Дія.; Компонент
|
-
|
Локальний агент недоступний
|
style="background:#fff9c4;" | Жовтий
|
| Підписується
|
SIGNING
|
}
|
-
|
Невідповідність підписанта
|
}
"document_type": "CONTRACT",
Див.; 36.; додатково
request = signature_request_repository.get_by_id(db, signature_request_id)
<pre>
|-
| AC-5
| Документ валідний.; | Вимкнути інтеграцію, повідомити адміністратора.; |-
| Підписаний контейнер
| CONTAINER
| Документ і підпис зберігаються в контейнері.; Очікуваний результат
if command.provider_code not in allowed_providers:
|-
| переважні аспекти
| функціонує з багатьма КНЕДП.; |-
| qr_payload
| text
| QR payload, якщо є собою.; |-
| Помилка
| Код, повідомлення, без секретів.; | Так
|-
| ASIC
| Контейнер підпису.; | style="background:#ef9a9a;" | Критично
|-
| Прострочено
| Не підписано у строк.; Очікує
document_type=document.document_type,
"phone": "+380671112233",
== 8.; Уніфікований бізнес-процес ==
v
task_name="start_signature_session",
request = signature_request_repository.get_by_id(db, signature_request_id)
|
style="background:#c8e6c9;" | Зелений
|
| Підпис перевірено
|
VERIFIED
|
-
|
VerificationError
|
-
|
Особливості
|
Запропонувати іншого провайдера або manual upload.; | style="background:#ffcc80;" | Помаранчевий
|
| Прострочено
|
EXPIRED
|
}
result=result,
17.10.; Завантаження підписаного документа
Метою задачі є собою створення уніфікованого Python-сервісу електронного підпису для роботи з різними сервісними центрами та провайдерами КЕП України.; |}
!; Signature Provider Router
* [[Python]]
* [[FastAPI]]
* [[K2 ERP]]
* [[КЕП]]
* [[Електронний підпис]]
* [[Дія.Підпис]]
* [[Приват24]]
* [[SmartID]]
* [[ІІТ]]
* [[Користувач ЦСК-1]]
* [[ДПС]]
* [[КНЕДП]]
* [[p7s]]
* [[CAdES]]
* [[XAdES]]
* [[ASIC]]
* [[Електронний документообіг]]
* [[API інтеграція]]
if document.status not in ["READY_TO_SIGN", "WAITING_SIGNATURE", "SIGN_ERROR"]:
!; # Чи потрібно підписувати документи клієнтами?; |-
| style="background:#c8e6c9;" | Зелений
| #c8e6c9
| Успішно: підписано, перевірено, провайдер активний.; !; Поле
|
| 2.; |-
| Callback Controller
| Приймає callback від хмарних сервісів.; |-
| Комунікація
| Browser → Local Agent → Crypto Library → результат у K2 ERP.; |-
| certificate_settings
| jsonb
| конфігурація сертифікатів.; | Вона підсвічується фіолетовим.; | style="background:#c8e6c9;" | Норма
|-
| Помилки підпису
| Помилки провайдера або локального агента.; |-
| signer_id
| Підписант.; | Маскування, secure logging.; Критерій
<pre>
request.status = "MANUAL_REVIEW"
GET /api/v1/signature/documents/{document_id}/available-providers
</pre>
"file_mime_type": "application/pdf",
Retry заборонений для:
!; |-
| signature_request_id
| uuid
| Заявка.; |-
| old_status
| varchar
| Старий статус.; Збереження і перевірка
</div>
return request
=== 27.1.; Основні KPI ===
{| class="wikitable"
=== Етап 9.; Production hardening ===
платформа повинна забезпечити:
[[Категорія:ІІТ]]
|-
| Unified Signature API
| Єдиний REST API для K2 ERP.; Документ
verification_queue.enqueue(
* договорів;
* актів виконаних робіт;
* рахунків;
* заяв;
* кадрових документів;
* первинних бухгалтерських документів;
* податкових документів;
* документів ЕДО;
* заявок у CRM;
* документів K2 ERP;
* пакетів документів;
* XML-звітів;
* PDF-документів;
* підтвердження юридично значущих дій.; async def verify_signature(self, document: bytes, signature: bytes, options: dict) -> dict:
=== 23.1. signature_providers ===
!; Що зберігати
=== 14.1.; Загальна схема ===
</div>
"provider_id": provider.id,
!; Тип
<syntaxhighlight lang="python">
},
def get_allowed_providers(self, document_type: str, signer_type: str) -> list [str]:
"signer": {
result = await verifier.verify(
!; |-
| style="background:#ef9a9a;" | Червоний
| #ef9a9a
| Помилка або негативний результат.; |-
| created_at
| timestamp
| Дата.; | Стара заявка стає INVALIDATED.; # Які формати документів підписуємо: PDF, XML, DOCX, ZIP?; |-
| FileKeyAdapter
| Підписання файловим КЕП.; |-
| Невідомий КНЕДП
| Сертифікат не розпізнано.; | Так
|-
| XAdES
| XML-підпис.; | VERIFY_ERROR.; |-
| AC-20
| є собою прострочені заявки.; |-
| is_active
| boolean
| Активність.; платформа розраховує hash документа.; |-
| Signature File
| Файл підпису або контейнер.; | style="background:#eeeeee;" | Сірий
|-
| Готовий до підпису
| READY_TO_SIGN
| Документ перевірено.; |-
| external_document_id
| varchar
| ID документа в K2 ERP.; Verification Service перевіряє підпис.; | style="background:#ef9a9a;" | Червоний
|-
| Ручна перевірка
| MANUAL_REVIEW
| Потрібне втручання адміністратора.;== 11.; Статуси документа ==
* реалізувати завантаження документа;
* реалізувати версіонування;
* реалізувати hash;
* реалізувати дедублікацію.; Поле
!; * Офіційна сторінка SmartID ПриватБанку.; з цієї причини потрібен адаптерний шар.; | style="background:#ffcc80;" | Помаранчевий
|-
| Помилка підписання
| SIGN_ERROR
| Помилка під час підписання.; else:
До MVP входить:
"file_id": stored_file.id,
[[Категорія:Дія]]
</div>
|-
| переважні аспекти
| користувач системи не передає файл ключа в систему.; * Офіційна сторінка Дія.Підпис.; Причина
=== 15.3. Signature Policy Engine ===
class SignaturePolicyEngine:
"signer_identifier": result.signer_identifier,
== 34.; Відкриті питання ==
!; | Статус стає SIGNED.; * Документація ІІТ / користувач системи ЦСК-1.; K2 ERP створює документ.; |-
| signature_format
| varchar
| P7S, ASIC, CAdES, XAdES, PDF.; |-
| created_at
| Дата створення.; | document.pdf + document.pdf.p7s
|-
| Вкладений підпис
| ENVELOPED
| Підпис вбудований у документ або XML.; |-
| is_active
| boolean
| Активність.; | Статус стає MANUAL_REVIEW.; Поле
|-
| PDF
| Найчастіший формат договорів, актів, рахунків.; Критерій
|-
| Відокремлений підпис
| DETACHED
| Підпис зберігається окремо від документа.; |-
| mime_type
| Тип файлу.; |-
| signature_type
| varchar
| DETACHED, ENVELOPED, CONTAINER.; |-
| Створення сесії
| provider_session_id, статус.; Формат
},
=== 23.8. signature_verifications ===
щоб підписати документ зручним для мене способом.; |-
| file_hash_sha256
| Hash документа.; | Hash і document version lock.; |-
| document_id
| uuid
| Документ.; | style="background:#c8e6c9;" | Високий
|-
| КНЕДП ДПС
| TAX_CSK
| Файловий ключ / ІІТ / локальне підписання
| Часто застосовується для податкових документів.; Єдиний API підписання
v
!; |-
| raw_request
| jsonb
| Запит.; Створюється signature_request.; |-
| style="background:#f3e5f5;" | Фіолетовий
| #f3e5f5
| Ручна перевірка або нестандартний сценарій.; Очікуваний результат
result = await adapter.parse_callback(payload)
9.; |-
| Dashboard API
| інформаційні дані для контролю.; |-
| signer_identifier
| varchar
| РНОКПП / ЄДРПОУ, якщо доступно.; Параметр
* реалізувати callback endpoint;
* реалізувати polling worker;
* реалізувати raw event storage;
* реалізувати idempotency.; |-
| expires_at
| timestamp
| Строк дії.; | signed.pdf або pdf.p7s.; Поле
<pre>
db.commit()
!; |}
== 6.; Передумови ==
payload=payload,
я хочу вибрати спосіб підписання: Дія, Приват24 SmartID, файловий КЕП або інший КНЕДП,
signer_type=command.signer.signer_type,
return ["DIIA_SIGN", "PRIVAT24_SMARTID", "MANUAL_UPLOAD"]
{| class="wikitable"
if document_type == "TAX_REPORT":
return ["IIT_CSK", "TAX_CSK", "FILE_KEY"]
if signer_type == "CLIENT":
=== 17.9.; Завантаження файлу підпису ===
if signer_type == "EMPLOYEE":
return ["FILE_KEY", "IIT_CSK", "PRIVAT24_SMARTID"]
return ["DIIA_SIGN", "PRIVAT24_SMARTID", "FILE_KEY", "MANUAL_UPLOAD"]
== 16.; |-
| source
|
varchar
|
-
|
created_by
|
-
|
signature_request_id
|
uuid
|
Заявка.; характеристика
Критично істотно: бізнес-система не повинна напряму залежати від конкретного сервісного центру підпису.; | style="background:#ffcc80;" | Помаранчевий
|
| Ручна перевірка
|
MANUAL_REVIEW
|
Потрібне втручання адміністратора.; характеристика
=== 15.1.; Загальний інтерфейс провайдера ===
<div style="border-left: 6px solid #c62828; background: #ffebee; padding: 12px 16px; margin: 16px 0;">
{
!; # Чи є собою офіційно затверджений API-доступ до SmartID?; |}
!; Код
12.; |-
| Provider Adapter
| Програмний адаптер конкретного провайдера.; |-
| Вибір провайдера
| Хто обрав, який провайдер.; |-
| DocumentChangedError
| Документ змінився після заявки.; №
adapter = provider_router.get_adapter(provider.code)
</pre>
router = APIRouter()
async def signature_callback(provider_code: str, request: Request):
payload={
!;
|
-
|
settings
|
jsonb
|
}
signature_processor.process_provider_result(
idempotency_key=command.idempotency_key,
істотно: різні провайдери мають різні способи роботи: API, callback, polling, QR/deep link, файловий ключ, локальний агент, DLL/SO-бібліотека, JavaScript-бібліотека або ручне завантаження p7s.; # Чи потрібно підписувати документи співробітниками?; | style="background:#f3e5f5;" | Фіолетовий
raise BusinessError("Provider is not allowed for this document")
|
}
21.; Перевірка підпису
"provider_session_id": response.get("session_id"),
"email": "client@example.com",
| id
|
uuid
|
ID перевірки.; Дія
; конфігурація провайдера ==
try:
"signer_name": result.signer_name,
POST /api/v1/signature/documents/{document_id}/upload-signature
},
Signature Storage + Verification Service
pass
verifier = verification_service_factory.get_verifier(signature_file.signature_format)
| -
|
Callback
|
-
|
Callback дублюється
|
-
|
AC-19
|
є собою помилки підписання.; Поле
"signature_type": "DETACHED",
data={
я хочу бачити dashboard підписання,
30.1.; Провайдери
| Signature Provider
|
Інструкція, fallback, healthcheck агента.; | платформа показує статус UNAVAILABLE.; |-
|
expires_at
|
timestamp
|
Строк дії.; Поле
<syntaxhighlight lang="python">
},
pass
|
| Створення документа
|
Тип, номер, hash, реліз.; Тип
async def verify_signature(signature_request_id: str, signature_file_id: str, db: "Session") -> None:
raise ValueError(f"Unsupported signature provider: {provider_code}")
Критично істотно: якщо документ змінено після створення заявки, попередню заявку потрібно перевести в INVALIDATED.; |-
|
provider_id
|
uuid
|
Провайдер.; характеристика
платформа повинна логувати:
15.2. Provider Router
"document_name": "Договір поставки №123",
existing = signature_request_repository.get_by_idempotency_key(
Python Unified Signature Service
v
return existing
| ; * єдиний API для всіх видів підписання;
* підтримку декількох провайдерів підпису;
* створення заявки на підписання;
* підготовку документа до підпису;
* розрахунок hash документа;
* підписання PDF, XML, JSON, DOCX, ZIP або довільного файлу;
* підтримку відокремленого підпису;
* підтримку вкладеного підпису;
* підтримку підписаного контейнера;
* отримання результату підписання;
* перевірку підпису;
* перевірку цілісності документа;
* перевірку підписанта;
* перевірку сертифіката;
* збереження файлів підпису;
* збереження підписаних документів;
* журналювання всіх подій;
* dashboard контролю;
* fallback-сценарії для ручного підписання.; Ключ
|
| Підходить для
|
ASIC / p7s container.; | style="background:#e3f2fd;" | відомості
|
| Очікують підпису
|
Активні заявки.; характеристика
"document_id": document.id,
== 24.; Приклад Python-логіки ==
|
|
|
|
|
|
|
|
|
|