Інтеграція з Укрпоштою в Python
Головна ідея: розробити Python-сервіс, який інтегрує K2 ERP / CRM / інтернет-магазин / WMS з API Укрпошти для створення відправлень, адрес, клієнтів, друку супровідних ярликів, розрахунку вартості, трекінгу статусів і контролю доставки.
Критично важливо: інтеграція не повинна створювати дублікати відправлень. Кожне замовлення, кожне відправлення, повторний запит, друк ярлика, помилка API та зміна статусу повинні мати внутрішній ID, idempotency_key, журнал подій і контроль повторної обробки.
Важливо: для роботи з API Укрпошти використовуються user token та authorization bearer, які потрібно отримати після підписання договору. Authorization bearer передається в заголовку Authorization, а тіло запиту використовує JSON.
Технічний стек: Python 3.11+, FastAPI, PostgreSQL, SQLAlchemy, Alembic, httpx, Pydantic, Celery/RQ/APScheduler, Redis, Docker.
Управлінський результат: менеджер, склад і керівник повинні бачити, скільки відправлень створено, скільки ярликів надруковано, які посилки доставлені, які повертаються, які мають помилки адреси, індексу, оплати або статусу.
1. Мета
Метою задачі є створення Python-сервісу для інтеграції з Укрпоштою з метою автоматизації процесів доставки.
Сервіс повинен забезпечити:
- зберігання налаштувань інтеграції;
- перевірку підключення до API;
- створення адреси відправника;
- створення адреси одержувача;
- створення клієнта-відправника;
- створення клієнта-одержувача;
- створення відправлення або групи відправлень;
- друк супровідних документів / ярликів;
- розрахунок вартості доставки, якщо підтримується API;
- перевірку доступності маршруту доставки;
- пошук поштових індексів;
- пошук відділень;
- отримання статусів відправлень;
- синхронізацію статусів назад у K2 ERP;
- підтримку внутрішніх відправлень по Україні;
- підтримку міжнародних відправлень як окремого сценарію;
- журналювання всіх API-запитів;
- dashboard для контролю логістики.
2. Область застосування
Інтеграція призначена для:
- інтернет-магазинів;
- CRM;
- ERP;
- WMS;
- складів;
- служб доставки;
- торгових компаній;
- дистриб'юторів;
- компаній, які створюють багато поштових відправлень;
- компаній, які хочуть контролювати доставку прямо з K2 ERP.
3. Основні можливості API Укрпошти
Офіційний портал API Укрпошти містить такі ключові напрями:
| Розділ API | Призначення | Коментар |
|---|---|---|
| Відправлення по Україні | Створення внутрішніх поштових відправлень. | Основний сценарій для MVP. |
| Міжнародні відправлення | Створення міжнародних відправлень. | Окремий сценарій із власними правилами. |
| Адресний класифікатор | Робота з адресами, індексами та населеними пунктами. | Потрібен для валідації адрес. |
| Відстеження відправлень | Отримання статусів доставки. | Потрібен для синхронізації з K2 ERP. |
| Внутрішні листи | Окремий API-сценарій для листів. | Може бути окремим модулем. |
| Swagger | Технічна документація API. | Використовується розробниками. |
| Пошук відділень та індексів | Пошук індексів, відділень, адресних даних. | Потрібен для UI та валідації. |
Офіційна інструкція «Як почати роботу з API» описує базовий workflow: створити адресу відправника, створити адресу одержувача, створити клієнта-відправника, створити клієнта-одержувача, створити відправлення або групу відправлень, після чого надрукувати супровідні документи. :contentReference[oaicite:2]{index=2}
4. Передумови
Для реалізації задачі необхідно отримати:
- підписаний договір з Укрпоштою;
- user token;
- authorization bearer;
- доступ до API-документації;
- тестове середовище або тестові дані, якщо надаються;
- дані відправника;
- адресу відправника;
- контактну особу відправника;
- правила оплати доставки;
- правила післяплати;
- правила міжнародних відправлень, якщо потрібні;
- правила друку ярликів;
- правила оновлення статусів;
- перелік сервісів доставки, які будуть використовуватись;
- вимоги K2 ERP до збереження номерів відправлень.
Критично важливо: user token і authorization bearer потрібно зберігати тільки в зашифрованому вигляді або secret storage. Заборонено зберігати їх у коді, Git, frontend-змінних або відкритих логах.
5. Базовий workflow створення відправлення
API-документація Укрпошти описує послідовність створення простого відправлення так:
1. Створити адресу відправника. 2. Створити адресу одержувача. 3. Створити клієнта-відправника. 4. Створити клієнта-одержувача. 5. Створити відправлення або групу відправлень. 6. Надрукувати супровідні документи / ярлик.
Для K2 ERP: цей workflow потрібно приховати від користувача. Менеджер натискає одну кнопку «Створити відправлення Укрпошта», а Python-сервіс сам виконує всі технічні кроки.
6. Основні бізнес-сценарії
6.1. Створення відправлення з K2 ERP
K2 ERP передає в Python-сервіс замовлення, дані одержувача, адресу, індекс, параметри вантажу та оплату.
Python-сервіс:
- перевіряє замовлення;
- перевіряє одержувача;
- перевіряє адресу та індекс;
- створює або знаходить адресу відправника;
- створює або знаходить адресу одержувача;
- створює або знаходить клієнта-відправника;
- створює або знаходить клієнта-одержувача;
- створює відправлення;
- зберігає barcode / shipment UUID / номер відправлення;
- формує супровідний ярлик;
- передає номер відправлення назад у K2 ERP.
6.2. Друк ярлика
Після створення відправлення система повинна отримати супровідний документ / sticker / label.
Користувачі:
- менеджер;
- комірник;
- оператор складу.
Результат:
- PDF або інший формат, який повертає API;
- файл зберігається у картці відправлення;
- статус змінюється на LABEL_PRINTED;
- дія логуються в аудиті.
6.3. Синхронізація статусів
Python-сервіс регулярно отримує статуси відправлень і оновлює K2 ERP.
Синхронізуються:
- створено;
- зареєстровано;
- прийнято;
- у дорозі;
- прибуло;
- доставлено;
- вручено;
- повертається;
- повернуто;
- скасовано;
- помилка;
- невідомий статус.
6.4. Міжнародні відправлення
Міжнародні відправлення потрібно реалізовувати окремим модулем, тому що вони можуть мати:
- інший формат створення;
- митний опис;
- категорію вкладення;
- країну призначення;
- англомовні адресні поля;
- обмеження по вазі;
- додаткові статуси;
- друк митних або супровідних документів.
6.5. Внутрішні листи
API Укрпошти має окрему документацію для внутрішніх листів. Для них потрібно передбачити окремий тип документа, оскільки листи мають інший життєвий цикл та власні параметри.
7. Основні сутності
| Сутність | Опис |
|---|---|
| Integration Account | Налаштування API Укрпошти. |
| Sender Address | Адреса відправника. |
| Recipient Address | Адреса одержувача. |
| Sender Client | Клієнт-відправник в API Укрпошти. |
| Recipient Client | Клієнт-одержувач в API Укрпошти. |
| Delivery Order | Замовлення на доставку в K2 ERP. |
| Shipment | Відправлення в API Укрпошти. |
| Shipment Group | Група відправлень. |
| Label / Sticker | Супровідний ярлик. |
| Tracking Status | Статус доставки. |
| Address Classifier | Адресний класифікатор. |
| Post Office | Відділення Укрпошти. |
| API Event | Подія інтеграції. |
8. User Story
8.1. Створення відправлення
Як менеджер інтернет-магазину, я хочу натиснути кнопку «Створити відправлення Укрпошта», щоб система автоматично створила відправлення без ручного введення в кабінеті або іншій системі.
8.2. Друк ярлика
Як комірник, я хочу надрукувати супровідний ярлик по створеному відправленню, щоб наклеїти його на посилку.
8.3. Контроль статусів
Як менеджер, я хочу бачити статус доставки прямо в K2 ERP, щоб швидко реагувати на повернення, затримки та проблемні доставки.
8.4. Dashboard керівника
Як керівник, я хочу бачити статистику по доставках Укрпоштою, щоб контролювати якість логістики, повернення, затримки та помилки.
9. Типи відправлень
| Тип | Код | Опис | Колір |
|---|---|---|---|
| Внутрішня посилка | DOMESTIC_PARCEL | Відправлення по Україні. | Базова |
| Міжнародна посилка | INTERNATIONAL_PARCEL | Відправлення за кордон. | Важлива |
| Внутрішній лист | DOMESTIC_LETTER | Лист по Україні. | Додаткова |
| Рекомендований лист | REGISTERED_LETTER | Лист із реєстрацією та трекінгом. | Додаткова |
| Група відправлень | SHIPMENT_GROUP | Пакетна передача декількох відправлень. | Групова |
10. Статуси відправлень
| Статус K2 ERP | Код | Опис | Колір |
|---|---|---|---|
| Чернетка | DRAFT | Замовлення є в K2 ERP, але відправлення ще не створено. | Сірий |
| Очікує створення | PENDING_CREATE | Замовлення в черзі на створення відправлення. | Жовтий |
| Створюється | CREATING | Виконується API-запит до Укрпошти. | Блакитний |
| Створено | CREATED | Відправлення створено в API. | Зелений |
| Зареєстровано | REGISTERED | Відправлення зареєстровано. | Зелений |
| Ярлик надруковано | LABEL_PRINTED | Супровідний ярлик отримано або надруковано. | Зелений |
| Передано Укрпошті | ACCEPTED_BY_UKRPOSHTA | Відправлення прийнято оператором. | Блакитний |
| У дорозі | IN_TRANSIT | Відправлення рухається. | Блакитний |
| Прибуло | ARRIVED | Відправлення прибуло у відділення або точку видачі. | Жовтий |
| Доставлено | DELIVERED | Відправлення вручено / доставлено. | Зелений |
| Повертається | RETURNING | Відправлення повертається відправнику. | Помаранчевий |
| Повернуто | RETURNED | Відправлення повернуто відправнику. | Помаранчевий |
| Помилка | ERROR | Помилка створення або синхронізації. | Червоний |
| Потребує повтору | NEEDS_RETRY | Технічна помилка, можна повторити. | Помаранчевий |
| Потребує виправлення | NEEDS_CORRECTION | Некоректна адреса, індекс або дані одержувача. | Помаранчевий |
| Невідомий статус | UNKNOWN_STATUS | API повернув статус, якого немає в мапінгу. | Фіолетовий |
| Скасовано | CANCELLED | Відправлення або замовлення скасовано. | Сірий |
У документації для міжнародних відправлень описується lifecycle із полями `status` та `statusDate`; після створення статус змінюється на `CREATED`, а після реєстрації — на `REGISTERED`. :contentReference[oaicite:3]{index=3}
11. Єдина логіка кольорів
| Колір | HTML | Значення | Де використовується |
|---|---|---|---|
| Зелений | #c8e6c9 | Успішно: створено, зареєстровано, ярлик надруковано, доставлено. | Dashboard, список відправлень, картка замовлення. |
| Блакитний | #bbdefb | Операція виконується або доставка в процесі. | Черга, API-статуси, транспортування. |
| Жовтий | #fff9c4 | Очікування або прибуття. | Очікує створення, прибуло. |
| Помаранчевий | #ffcc80 | Потрібна дія або є ризик. | Повернення, retry, неправильна адреса. |
| Червоний | #ef9a9a | Помилка або негативний результат. | Помилка API, неправильний token, недоступний маршрут. |
| Фіолетовий | #f3e5f5 | Спеціальний або невідомий статус. | UNKNOWN_STATUS, ручна перевірка. |
| Сірий | #eeeeee | Чернетка, скасовано або неактивно. | Архів, чернетки. |
12. Архітектура рішення
12.1. Загальна схема
K2 ERP / CRM / Website / WMS
|
| 1. Замовлення, клієнт, адреса, вантаж
v
Python Ukrposhta Integration Service
|
| 2. Валідація, мапінг, дедублікація, черга
v
Ukrposhta API Client
|
| 3. API Укрпошти
v
Укрпошта
|
| 4. Відправлення, статуси, ярлики
v
Python Status Sync Worker
|
| 5. Оновлення статусів
v
K2 ERP / Dashboard / Склад / Менеджер
12.2. Основні компоненти Python-сервісу
| Компонент | Опис |
|---|---|
| API Layer | REST API для прийому замовлень і команд із K2 ERP. |
| Validation Layer | Перевіряє одержувача, адресу, індекс, вагу, оплату. |
| Mapping Layer | Перетворює структури K2 ERP у формат API Укрпошти. |
| Ukrposhta Client | Python-клієнт для API Укрпошти. |
| Address Service | Створює та кешує адреси відправника й одержувача. |
| Client Service | Створює та кешує клієнтів відправника й одержувача. |
| Shipment Service | Створює відправлення або групи відправлень. |
| Label Service | Отримує супровідні документи / ярлики. |
| Tracking Worker | Оновлює статуси відправлень. |
| Directory Sync Worker | Оновлює адресний класифікатор, індекси, відділення. |
| Audit Logger | Зберігає запити, відповіді, помилки та зміни статусів. |
| Dashboard API | Дані для менеджера, складу та керівника. |
13. Ukrposhta Client
13.1. Призначення
Ukrposhta Client — це Python-клас або пакет, який інкапсулює роботу з API Укрпошти.
13.2. Основні методи Python-клієнта
class UkrposhtaClient:
def check_connection(self) -> "ConnectionStatus":
pass
def create_address(self, payload: "AddressPayload") -> "AddressResponse":
pass
def create_client(self, payload: "ClientPayload") -> "ClientResponse":
pass
def create_shipment(self, payload: "ShipmentPayload") -> "ShipmentResponse":
pass
def create_shipment_group(self, payload: "ShipmentGroupPayload") -> "ShipmentGroupResponse":
pass
def get_shipment(self, shipment_id: str) -> "ShipmentResponse":
pass
def update_shipment(self, shipment_id: str, payload: "ShipmentPayload") -> "ShipmentResponse":
pass
def delete_shipment(self, shipment_id: str) -> "DeleteShipmentResponse":
pass
def get_label(self, shipment_id: str, format: str = "pdf") -> bytes:
pass
def track_shipment(self, barcode: str) -> "TrackingResponse":
pass
def track_shipments(self, barcodes: list[str]) -> "TrackingListResponse":
pass
def check_route_availability(self, sender_postcode: str, recipient_postcode: str) -> "RouteAvailabilityResponse":
pass
def search_post_offices(self, filters: dict) -> "PostOfficeListResponse":
pass
def search_postcodes(self, filters: dict) -> "PostcodeListResponse":
pass
Важливо: методи Python-клієнта є внутрішньою абстракцією. Реальні URI API потрібно брати з актуальної Swagger-документації Укрпошти.
14. Конфігурація клієнта
from pydantic_settings import BaseSettings
class UkrposhtaSettings(BaseSettings):
base_url: str = "https://www.ukrposhta.ua/ecom/0.0.1/"
label_base_url: str | None = None
user_token: str
bearer_token: str
timeout_seconds: int = 30
retry_count: int = 3
retry_backoff_seconds: int = 5
verify_ssl: bool = True
language: str = "ua"
Приклад `.env`:
UKRPOSHTA_BASE_URL=https://www.ukrposhta.ua/ecom/0.0.1/ UKRPOSHTA_LABEL_BASE_URL=https://www.ukrposhta.ua/ecom/0.0.1/ UKRPOSHTA_USER_TOKEN=******** UKRPOSHTA_BEARER_TOKEN=******** UKRPOSHTA_TIMEOUT_SECONDS=30 UKRPOSHTA_RETRY_COUNT=3 UKRPOSHTA_RETRY_BACKOFF_SECONDS=5 UKRPOSHTA_LANGUAGE=ua
15. API Python-сервісу
15.1. Створення інтеграції
POST /api/v1/ukrposhta/integrations
15.2. Перевірка підключення
POST /api/v1/ukrposhta/integrations/{integration_id}/check-connection
15.3. Синхронізація адресних довідників
POST /api/v1/ukrposhta/directories/sync
15.4. Пошук індексів
GET /api/v1/ukrposhta/postcodes?query=01001
15.5. Пошук відділень
GET /api/v1/ukrposhta/post-offices?postcode=01001
15.6. Перевірка маршруту між індексами
POST /api/v1/ukrposhta/routes/check-availability
15.7. Створення відправлення
POST /api/v1/ukrposhta/shipments
15.8. Оновлення відправлення
PATCH /api/v1/ukrposhta/shipments/{shipment_id}
15.9. Скасування / видалення відправлення
POST /api/v1/ukrposhta/shipments/{shipment_id}/cancel
15.10. Друк ярлика
GET /api/v1/ukrposhta/shipments/{shipment_id}/label
15.11. Синхронізація статусів
POST /api/v1/ukrposhta/tracking/sync
15.12. Dashboard
GET /api/v1/ukrposhta/dashboard?date_from=2026-05-01&date_to=2026-05-31
16. Приклад запиту на створення відправлення
{
"external_order_id": "K2-ORDER-2026-000123",
"idempotency_key": "K2-ORDER-2026-000123-ukrposhta-v1",
"shipment_type": "DOMESTIC_PARCEL",
"sender": {
"client_id": "sender-client-id",
"address_id": "sender-address-id",
"name": "ТОВ «Відправник»",
"phone": "+380501112233",
"postcode": "01001",
"address": {
"region": "Київська",
"city": "Київ",
"street": "Хрещатик",
"building": "1",
"apartment": null
}
},
"recipient": {
"first_name": "Іван",
"middle_name": "Іванович",
"last_name": "Петренко",
"phone": "+380671112233",
"postcode": "79000",
"address": {
"region": "Львівська",
"city": "Львів",
"street": "Січових Стрільців",
"building": "10",
"apartment": "5"
}
},
"delivery": {
"description": "Одяг",
"declared_price": 1500.00,
"weight": 2.5,
"length": 30,
"width": 20,
"height": 15,
"postpay_enabled": true,
"postpay_amount": 1500.00,
"sms": true
}
}
17. Валідація перед створенням відправлення
Перед створенням відправлення система повинна перевірити:
- наявність external_order_id;
- наявність idempotency_key;
- чи не створено вже відправлення для цього замовлення;
- user token активний;
- bearer token активний;
- ПІБ або назву одержувача;
- телефон одержувача;
- індекс одержувача;
- адресу одержувача;
- індекс відправника;
- адресу відправника;
- доступність маршруту між індексами, якщо використовується перевірка;
- вагу більше 0;
- габарити більше 0, якщо обов'язкові;
- оголошену вартість;
- післяплату, якщо використовується;
- SMS-опцію, якщо використовується;
- коректність типу відправлення;
- коректність міжнародних полів, якщо це міжнародне відправлення.
Критично важливо: якщо відправлення вже створене, повторний запит не повинен створювати нове відправлення. Система повинна повернути існуючий barcode / shipment_id та його поточний статус.
18. Адресний класифікатор і довідники
18.1. Поштові індекси
Потрібно зберігати:
- індекс;
- область;
- район;
- населений пункт;
- вулицю, якщо доступна;
- відділення, якщо прив'язане;
- ознаку активності;
- дату оновлення.
18.2. Відділення
Потрібно зберігати:
- ID або код відділення;
- поштовий індекс;
- назву;
- адресу;
- населений пункт;
- координати, якщо доступні;
- графік роботи;
- ознаку активності;
- доступні сервіси.
18.3. Графік оновлення довідників
| Довідник | Частота оновлення | Коментар |
|---|---|---|
| Поштові індекси | 1 раз на добу або за потреби | Потрібні для валідації адрес. |
| Відділення | 1 раз на добу або за потреби | Потрібні для вибору точки відправлення/видачі. |
| Адресний класифікатор | 1 раз на добу або за регламентом | Може бути великим довідником. |
| Типи відправлень | 1 раз на добу або після зміни API | Потрібні для валідації. |
19. Дедублікація
Система повинна не допускати дублювання відправлень.
Ключі дедублікації:
| Ключ | Призначення |
|---|---|
| external_order_id | ID замовлення в K2 ERP. |
| idempotency_key | Унікальний ключ конкретної спроби створення відправлення. |
| shipment_id | ID відправлення в API Укрпошти. |
| barcode | Штрихкод / номер відправлення. |
| shipment_hash | Hash основних параметрів відправлення. |
Приклад hash:
sha256(external_order_id + recipient_phone + recipient_postcode + declared_price + weight)
20. Черга створення відправлень
20.1. Логіка черги
1. K2 ERP створює замовлення. 2. Користувач натискає «Створити відправлення Укрпошта» або спрацьовує автоматичне правило. 3. Python-сервіс виконує валідацію. 4. Створюється запис delivery_order зі статусом PENDING_CREATE. 5. Worker створює адресу відправника, якщо її ще немає. 6. Worker створює адресу одержувача. 7. Worker створює клієнта-відправника, якщо потрібно. 8. Worker створює клієнта-одержувача. 9. Worker створює відправлення. 10. API повертає shipment_id / barcode. 11. Python-сервіс зберігає номер відправлення. 12. K2 ERP отримує номер відправлення. 13. Label Service отримує супровідний ярлик. 14. Tracking Worker оновлює статуси доставки.
20.2. Пріоритети задач
| Тип задачі | Пріоритет | Коментар |
|---|---|---|
| Створення відправлення | Високий | Основний процес відвантаження. |
| Друк ярлика | Високий | Потрібно для складу. |
| Скасування відправлення | Високий | Важливо до передачі посилки. |
| Синхронізація статусів | Середній | Фоновий процес. |
| Перевірка маршруту | Середній | Валідаційний процес. |
| Оновлення довідників | Низький | Регламентна задача. |
21. Модель даних
21.1. ukrposhta_integrations
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID інтеграції. |
| provider | varchar | ukrposhta. |
| name | varchar | Назва інтеграції. |
| base_url | varchar | URL API. |
| label_base_url | varchar | URL для супровідних документів. |
| user_token_encrypted | text | Зашифрований user token. |
| bearer_token_encrypted | text | Зашифрований bearer token. |
| default_sender_client_id | varchar | Клієнт-відправник за замовчуванням. |
| default_sender_address_id | varchar | Адреса відправника за замовчуванням. |
| is_active | boolean | Активність. |
| created_at | timestamp | Дата створення. |
| updated_at | timestamp | Дата оновлення. |
21.2. ukrposhta_addresses
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | Внутрішній ID. |
| external_address_id | varchar | ID адреси в API Укрпошти. |
| postcode | varchar | Поштовий індекс. |
| region | varchar | Область. |
| district | varchar | Район. |
| city | varchar | Місто / населений пункт. |
| street | varchar | Вулиця. |
| building | varchar | Будинок. |
| apartment | varchar | Квартира / офіс. |
| raw_response | jsonb | Відповідь API. |
21.3. ukrposhta_clients
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | Внутрішній ID. |
| external_client_id | varchar | ID клієнта в API Укрпошти. |
| client_type | varchar | sender або recipient. |
| name | varchar | ПІБ або назва компанії. |
| phone | varchar | Телефон. |
| varchar | Email. | |
| address_id | uuid | Адреса. |
| raw_response | jsonb | Відповідь API. |
21.4. ukrposhta_shipments
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID відправлення. |
| external_order_id | varchar | ID замовлення K2 ERP. |
| idempotency_key | varchar | Ключ дедублікації. |
| shipment_id | varchar | ID відправлення в API Укрпошти. |
| barcode | varchar | Штрихкод / номер відправлення. |
| shipment_type | varchar | DOMESTIC_PARCEL, INTERNATIONAL_PARCEL тощо. |
| sender_client_id | uuid | Клієнт-відправник. |
| recipient_client_id | uuid | Клієнт-одержувач. |
| declared_price | numeric | Оголошена вартість. |
| weight | numeric | Вага. |
| length | numeric | Довжина. |
| width | numeric | Ширина. |
| height | numeric | Висота. |
| postpay_enabled | boolean | Чи є післяплата. |
| postpay_amount | numeric | Сума післяплати. |
| sms_enabled | boolean | Чи замовлено SMS. |
| status | varchar | Статус K2 ERP. |
| ukrposhta_status | varchar | Оригінальний статус API. |
| raw_request | jsonb | Запит. |
| raw_response | jsonb | Відповідь. |
| error_message | text | Помилка. |
| created_at | timestamp | Дата створення. |
| sent_at | timestamp | Дата створення в API. |
| delivered_at | timestamp | Дата доставки. |
21.5. ukrposhta_labels
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID ярлика. |
| shipment_id | uuid | Відправлення. |
| format | varchar | PDF, PNG або інший формат. |
| file_id | uuid | Файл у сховищі. |
| print_count | integer | Кількість друків. |
| printed_at | timestamp | Дата друку. |
| printed_by | uuid | Користувач. |
21.6. ukrposhta_events
| Поле | Тип | Опис |
|---|---|---|
| id | uuid | ID події. |
| entity_type | varchar | integration, shipment, address, client, label, directory. |
| entity_id | uuid | ID сутності. |
| event_type | varchar | Тип події. |
| old_status | varchar | Попередній статус. |
| new_status | varchar | Новий статус. |
| source | varchar | K2_ERP, PYTHON_SERVICE, UKRPOSHTA, USER. |
| payload | jsonb | Технічні дані. |
| created_at | timestamp | Дата події. |
22. Приклад Python-логіки
22.1. Базовий API-клієнт
import httpx
class UkrposhtaApiError(Exception):
pass
class UkrposhtaClient:
def __init__(
self,
base_url: str,
user_token: str,
bearer_token: str,
timeout_seconds: int = 30,
):
self.base_url = base_url.rstrip("/")
self.user_token = user_token
self.bearer_token = bearer_token
self.timeout_seconds = timeout_seconds
async def request(
self,
method: str,
path: str,
json: dict | None = None,
params: dict | None = None,
) -> dict:
headers = {
"Authorization": f"Bearer {self.bearer_token}",
"Content-Type": "application/json",
}
params = params or {}
params.setdefault("token", self.user_token)
async with httpx.AsyncClient(timeout=self.timeout_seconds) as client:
response = await client.request(
method=method,
url=f"{self.base_url}/{path.lstrip('/')}",
json=json,
params=params,
headers=headers,
)
if response.status_code >= 400:
raise UkrposhtaApiError(response.text)
if response.content:
return response.json()
return {}
22.2. Створення відправлення
async def create_ukrposhta_shipment(
command: "CreateUkrposhtaShipmentCommand",
db: "Session",
) -> "UkrposhtaShipment":
existing = shipment_repository.get_by_idempotency_key(
db=db,
idempotency_key=command.idempotency_key,
)
if existing:
return existing
ukrposhta_validator.validate(command)
shipment = shipment_repository.create(
db=db,
data={
"external_order_id": command.external_order_id,
"idempotency_key": command.idempotency_key,
"shipment_type": command.shipment_type,
"declared_price": command.delivery.declared_price,
"weight": command.delivery.weight,
"postpay_enabled": command.delivery.postpay_enabled,
"postpay_amount": command.delivery.postpay_amount,
"status": "PENDING_CREATE",
"raw_request": command.model_dump(),
},
)
delivery_queue.enqueue(
task_name="send_ukrposhta_shipment",
payload={"shipment_id": str(shipment.id)},
)
audit_logger.log(
entity_type="ukrposhta_shipment",
entity_id=shipment.id,
event_type="UKRPOSHTA_SHIPMENT_QUEUED",
new_status="PENDING_CREATE",
payload={"external_order_id": command.external_order_id},
)
db.commit()
return shipment
22.3. Worker створення відправлення
async def send_ukrposhta_shipment(shipment_id: str, db: "Session") -> None:
shipment = shipment_repository.get_by_id(db, shipment_id)
if shipment.status in ["CREATED", "REGISTERED", "DELIVERED"] and shipment.barcode:
return
try:
shipment.status = "CREATING"
db.commit()
sender_address = await address_service.ensure_sender_address(shipment)
recipient_address = await address_service.ensure_recipient_address(shipment)
sender_client = await client_service.ensure_sender_client(shipment, sender_address)
recipient_client = await client_service.ensure_recipient_client(shipment, recipient_address)
payload = ukrposhta_mapper.to_shipment_payload(
shipment=shipment,
sender_client=sender_client,
recipient_client=recipient_client,
)
response = await ukrposhta_client.create_shipment(payload)
shipment.shipment_id = response.id
shipment.barcode = response.barcode
shipment.status = "CREATED"
shipment.raw_response = response.raw_payload
shipment.sent_at = utc_now()
audit_logger.log(
entity_type="ukrposhta_shipment",
entity_id=shipment.id,
event_type="UKRPOSHTA_SHIPMENT_CREATED",
old_status="CREATING",
new_status="CREATED",
payload={
"shipment_id": shipment.shipment_id,
"barcode": shipment.barcode,
},
)
except TemporaryUkrposhtaError as exc:
shipment.status = "NEEDS_RETRY"
shipment.error_message = str(exc)
except Exception as exc:
shipment.status = "ERROR"
shipment.error_message = str(exc)
finally:
db.commit()
22.4. Синхронізація статусів
async def sync_ukrposhta_statuses(barcodes: list[str], db: "Session") -> None:
for barcode in barcodes:
shipment = shipment_repository.get_by_barcode(db, barcode)
if not shipment:
continue
status_response = await ukrposhta_client.track_shipment(barcode)
old_status = shipment.status
new_status = ukrposhta_status_mapper.from_api(status_response)
if old_status != new_status:
shipment.status = new_status
shipment.ukrposhta_status = status_response.status
if new_status == "DELIVERED":
shipment.delivered_at = utc_now()
audit_logger.log(
entity_type="ukrposhta_shipment",
entity_id=shipment.id,
event_type="UKRPOSHTA_STATUS_SYNCED",
old_status=old_status,
new_status=new_status,
payload=status_response.raw_payload,
)
db.commit()
23. Обробка помилок
23.1. Типи помилок
| Тип помилки | Опис | Дія системи |
|---|---|---|
| ValidationError | Некоректні дані замовлення. | Не створювати відправлення, показати список помилок. |
| AuthError | Невірний user token або bearer token. | Зупинити інтеграцію, повідомити адміністратора. |
| AddressError | Некоректна адреса. | Перевести в NEEDS_CORRECTION. |
| PostcodeError | Некоректний або недоступний індекс. | Перевести в NEEDS_CORRECTION. |
| RouteUnavailableError | Маршрут доставки недоступний. | Показати менеджеру. |
| PhoneValidationError | Некоректний телефон одержувача. | Перевести в NEEDS_CORRECTION. |
| ApiError | API повернув помилку. | Зберегти raw-відповідь. |
| TimeoutError | Перевищено час очікування. | Перевести в NEEDS_RETRY. |
| DuplicateShipmentError | Відправлення вже створено. | Повернути існуюче відправлення. |
| TrackingError | Не вдалося отримати статус. | Повторити фоново. |
23.2. Retry-логіка
Retry дозволений для:
- timeout;
- HTTP 429;
- HTTP 500;
- HTTP 502;
- HTTP 503;
- HTTP 504;
- тимчасової недоступності API;
- тимчасової помилки друку ярлика;
- тимчасової помилки синхронізації статусів.
Retry заборонений для:
- неправильного user token;
- неправильного bearer token;
- помилок валідації;
- неправильного індексу;
- недоступного маршруту;
- некоректного телефону;
- відправлення, яке вже створене;
- відправлення, яке вже доставлене;
- відправлення, яке вже скасоване.
24. Dashboard менеджера і керівника
24.1. Основні KPI
| KPI | Опис | Колір |
|---|---|---|
| Замовлень до відправки | Замовлення без створеного відправлення. | Увага |
| Відправлень створено | Кількість створених відправлень. | Норма |
| Ярлики надруковано | Готові до пакування відправлення. | Норма |
| У дорозі | Відправлення в транспортуванні. | В роботі |
| Прибуло | Очікує отримувача. | Контроль |
| Доставлено | Успішні доставки. | Норма |
| Повернення | Відправлення повертаються. | Потрібна дія |
| Помилки API | Помилки створення або статусів. | Критично |
| Потребують виправлення | Некоректні адреси, індекси, телефони. | Потрібна дія |
24.2. Приклад dashboard
| Показник | Значення | Стан |
|---|---|---|
| Замовлень до відправки | 42 | Увага |
| Відправлень створено сьогодні | 185 | Норма |
| Ярлики надруковано | 172 | Норма |
| У дорозі | 620 | В роботі |
| Прибуло | 88 | Контроль |
| Доставлено | 410 | Норма |
| Повернення | 21 | Потрібна дія |
| Помилки створення | 5 | Критично |
| Потребують виправлення | 9 | Потрібна дія |
24.3. Проблемні відправлення
| Дата | Замовлення | Barcode | Одержувач | Статус | Причина | Дія |
|---|---|---|---|---|---|---|
| 07.05.2026 | K2-ORDER-123 | - | Іван Петренко | Помилка | Некоректний індекс | Виправити адресу |
| 07.05.2026 | K2-ORDER-124 | 0500000000000 | Олена Сидоренко | Повернення | Не отримано | Контроль повернення |
| 07.05.2026 | K2-ORDER-125 | 0500000000001 | ТОВ «Альфа» | Невідомий статус | Новий статус API без мапінгу | Оновити мапінг |
25. Безпека
Система повинна забезпечити:
- зберігання user token і bearer token тільки у secret storage або в зашифрованому вигляді;
- заборону логування token;
- HTTPS для всіх API-запитів;
- перевірку SSL;
- рольову модель доступу;
- окремі права на створення відправлення;
- окремі права на скасування відправлення;
- окремі права на друк ярлика;
- журнал усіх дій;
- захист від дублювання відправлень;
- маскування телефонів одержувачів у логах;
- контроль доступу до персональних даних.
26. Логування та аудит
Система повинна логувати:
| Подія | Що зберігати |
|---|---|
| Створення запиту на відправлення | Замовлення, одержувач, індекс, адреса, сума. |
| Валідація | Результат, список помилок. |
| Створення адреси | Тип адреси, API ID, відповідь. |
| Створення клієнта | Тип клієнта, API ID, відповідь. |
| Створення відправлення | Shipment ID, barcode, дата, відповідь API. |
| Друк ярлика | Хто надрукував, коли, формат. |
| Отримання статусу | Старий статус, новий статус, джерело. |
| Помилка API | Код, повідомлення, raw-відповідь. |
| Повторна операція | Хто запустив, причина, результат. |
| Скасування | Хто скасував, причина. |
27. Acceptance Criteria
27.1. Інтеграція
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-1 | Адміністратор створює інтеграцію Укрпошти. | Інтеграція зберігається в системі. |
| AC-2 | Адміністратор перевіряє підключення. | Система повертає успішний або помилковий статус. |
| AC-3 | Token неправильний. | Система показує AuthError і не створює відправлення. |
27.2. Довідники
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-4 | Система запускає синхронізацію індексів. | Довідник індексів оновлюється. |
| AC-5 | Система запускає синхронізацію відділень. | Довідник відділень оновлюється. |
| AC-6 | Індекс недоступний або некоректний. | Відправлення не створюється без виправлення. |
27.3. Створення відправлення
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-7 | K2 ERP передає валідне замовлення. | Python-сервіс створює відправлення. |
| AC-8 | API повертає barcode. | Barcode зберігається в K2 ERP. |
| AC-9 | Повторний запит має той самий idempotency_key. | Друге відправлення не створюється. |
| AC-10 | Адреса некоректна. | Відправлення не створюється, статус NEEDS_CORRECTION. |
27.4. Друк ярлика
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-11 | Користувач натискає «Друк ярлика». | Система повертає PDF або інший доступний формат. |
| AC-12 | Ярлик надруковано. | Статус змінюється на LABEL_PRINTED. |
| AC-13 | Ярлик друкується повторно. | Print count збільшується, дія логуються. |
27.5. Статуси
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-14 | Tracking API повертає новий статус. | K2 ERP оновлює статус відправлення. |
| AC-15 | Відправлення доставлено. | Статус стає DELIVERED і підсвічується зеленим. |
| AC-16 | Відправлення повертається. | Статус стає RETURNING і підсвічується помаранчевим. |
| AC-17 | API повертає невідомий статус. | Статус стає UNKNOWN_STATUS і потрапляє в список ручної перевірки. |
27.6. Dashboard
| № | Критерій | Очікуваний результат |
|---|---|---|
| AC-18 | Менеджер відкриває dashboard. | Він бачить замовлення, відправлення, доставки, повернення та помилки. |
| AC-19 | Є помилки створення відправлень. | Вони підсвічуються червоним. |
| AC-20 | Є повернення. | Вони підсвічуються помаранчевим. |
28. MVP
До MVP входить:
- створення інтеграції Укрпошти;
- перевірка user token і bearer token;
- створення адреси відправника;
- створення адреси одержувача;
- створення клієнта-відправника;
- створення клієнта-одержувача;
- створення внутрішнього відправлення по Україні;
- збереження shipment_id / barcode;
- друк супровідного ярлика;
- синхронізація статусів;
- дедублікація;
- retry-механізм;
- журнал подій;
- dashboard API;
- базові unit-тести;
- mock API для інтеграційних тестів.
До MVP не входить:
- повна підтримка міжнародних відправлень;
- повна підтримка внутрішніх листів;
- складна робота з митними даними;
- повна автоматизація післяплати;
- складний UI складу;
- власний модуль адресного класифікатора без API;
- інтеграція з фінансовими сервісами;
- масова оптимізація логістики.
29. Етапи реалізації
Етап 1. Аналіз API Укрпошти
- отримати user token;
- отримати authorization bearer;
- перевірити доступ до API;
- отримати актуальну Swagger-документацію;
- перевірити створення адрес;
- перевірити створення клієнтів;
- перевірити створення тестового відправлення;
- перевірити друк ярлика;
- перевірити трекінг.
Етап 2. Базовий Python-сервіс
- створити FastAPI-проєкт;
- налаштувати PostgreSQL;
- створити моделі інтеграції, адрес, клієнтів, відправлень, ярликів, подій;
- налаштувати Alembic;
- реалізувати healthcheck.
Етап 3. Ukrposhta Client
- реалізувати базовий request method;
- реалізувати create_address;
- реалізувати create_client;
- реалізувати create_shipment;
- реалізувати get_label;
- реалізувати track_shipment;
- реалізувати check_route_availability;
- реалізувати обробку помилок.
Етап 4. Адреси та клієнти
- реалізувати створення адреси відправника;
- реалізувати створення адреси одержувача;
- реалізувати кешування адрес;
- реалізувати створення клієнтів;
- реалізувати повторне використання клієнтів.
Етап 5. Відправлення і валідація
- реалізувати створення відправлення;
- реалізувати мапінг K2 ERP → API Укрпошти;
- реалізувати валідацію;
- реалізувати hash відправлення;
- реалізувати дедублікацію.
Етап 6. Статуси та друк
- реалізувати синхронізацію статусів;
- реалізувати друк ярлика;
- реалізувати retry;
- реалізувати збереження PDF.
Етап 7. Dashboard та аудит
- реалізувати dashboard API;
- реалізувати список проблемних відправлень;
- реалізувати фільтри;
- реалізувати експорт, якщо потрібно.
Етап 8. Production hardening
- додати rate limiting;
- додати моніторинг;
- додати alerting;
- додати dead letter queue;
- додати резервне копіювання;
- додати безпечне зберігання секретів.
30. Ризики
| Ризик | Опис | Як зменшити |
|---|---|---|
| Неправильний token | Інтеграція не працюватиме. | Check-connection і повідомлення адміністратору. |
| Некоректний індекс | Відправлення може не створитись. | Валідація індексу до API-запиту. |
| Некоректна адреса | API може повернути помилку. | Address classifier і ручна перевірка. |
| Дублювання відправлень | Повторний запит може створити друге відправлення. | Idempotency key і перевірка external_order_id. |
| Зміна API | Можуть змінитись URI або поля. | Версіонування клієнта і contract-тести. |
| Недоступність API | Відправлення не створюються. | Черга, retry, dashboard помилок. |
| Невідомі статуси | API може повернути новий статус. | UNKNOWN_STATUS і таблиця status_mapping. |
| Міжнародні відправлення складніші | Потрібні митні й країнові поля. | Винести в окремий етап. |
31. Відкриті питання
- Який тип договору та доступу до API використовується?
- Чи потрібні тільки внутрішні відправлення по Україні?
- Чи потрібні міжнародні відправлення?
- Чи потрібна підтримка внутрішніх листів?
- Чи потрібно підтримувати декілька відправників?
- Чи потрібно підтримувати декілька складів?
- Чи потрібно зберігати адреси одержувачів у K2 ERP?
- Чи потрібна післяплата?
- Чи потрібно друкувати ярлики автоматично після створення?
- Який формат друку потрібен: A4, термопринтер, PDF?
- Чи потрібно формувати групи відправлень?
- Як часто синхронізувати статуси?
- Чи потрібно автоматично сповіщати клієнта?
- Чи потрібна інтеграція з K2 ERP документами реалізації та оплат?
- Чи потрібно експортувати журнал відправлень в Excel?
32. Джерела
- https://dev.ukrposhta.ua/
- https://dev.ukrposhta.ua/documentation
- https://dev.ukrposhta.ua/faq
- https://dev.ukrposhta.ua/for-business
- API documentation 2025.
- Як почати роботу з API, версія 11.02.2026.
- API documentation: International shipments.
- API documentation: Internal letters.
- Swagger-документація Укрпошти.