Перейти до вмісту

Інтеграція з Укрпоштою в Python

Матеріал з K2 ERP Wiki Ukraine — База знань з автоматизації та санкцій в Україні


SEO title: Технічне завдання: Інтеграція з Укрпоштою для Python SEO description: Технічне завдання на реалізацію Python-сервісу для інтеграції K2 ERP, CRM або інтернет-магазину з API Укрпошти: створення відправлень, адреси, клієнти, ярлики, тарифи, статуси, трекінг, міжнародні відправлення, dashboard та журналювання. SEO keywords: Python, Укрпошта, Ukrposhta API, API Укрпошта, ТТН, ярлик, відправлення, доставка, K2 ERP, CRM, інтернет-магазин, FastAPI, логістика, поштовий індекс, трекінг Alternative to:



Головна ідея: розробити 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 Телефон.
email 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. Відкриті питання

  1. Який тип договору та доступу до API використовується?
  2. Чи потрібні тільки внутрішні відправлення по Україні?
  3. Чи потрібні міжнародні відправлення?
  4. Чи потрібна підтримка внутрішніх листів?
  5. Чи потрібно підтримувати декілька відправників?
  6. Чи потрібно підтримувати декілька складів?
  7. Чи потрібно зберігати адреси одержувачів у K2 ERP?
  8. Чи потрібна післяплата?
  9. Чи потрібно друкувати ярлики автоматично після створення?
  10. Який формат друку потрібен: A4, термопринтер, PDF?
  11. Чи потрібно формувати групи відправлень?
  12. Як часто синхронізувати статуси?
  13. Чи потрібно автоматично сповіщати клієнта?
  14. Чи потрібна інтеграція з K2 ERP документами реалізації та оплат?
  15. Чи потрібно експортувати журнал відправлень в Excel?

32. Джерела

33. Див. також