Приклади й файли
Готові приклади для інтеграції з ApexSFA: пакети конвертів, відповіді черги, файли заказів і файли-відповіді, клієнт обміну й перевірка вебхука.
Усі значення в прикладах вигадані. Файли можна завантажити й одразу використати як шаблони; повний опис полів — у відповідних розділах. Машиночитна специфікація — openapi.json, уся документація одним файлом — PDF.
Обмін з обліковою системою
exchange-inbound-batch-catalog.json Пакет довідників: філія, склад, тип цін, виробник, товар, контрагент, точка, ціна. Завантажити
{
"partial": true,
"items": [
{
"entity_type": "branch",
"operation": "upsert",
"external_id": "0a1b2c3d-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"name": "Філія Полтава",
"address": "м. Полтава, вул. Промислова, 5"
}
},
{
"entity_type": "warehouse",
"operation": "upsert",
"external_id": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"name": "Основний склад",
"address": "м. Полтава, вул. Промислова, 5",
"is_active": true
}
},
{
"entity_type": "price_type",
"operation": "upsert",
"external_id": "6d7e8f90-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"name": "Оптова",
"code": "000000002",
"is_active": true
}
},
{
"entity_type": "manufacturer",
"operation": "upsert",
"external_id": "000000017",
"payload": {
"name": "ТОВ «Сади Приклад»",
"code": "000000017",
"tax_number": "41234567",
"is_active": true
}
},
{
"entity_type": "product",
"operation": "upsert",
"external_id": "b3f61c42-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"name": "Сік яблучний 1 л",
"sku": "JA-1000",
"barcode": "4820017000123",
"unit": "шт",
"base_price": "18.50",
"vat_rate": 20,
"manufacturer_external_id": "000000017",
"manufacturer": "ТОВ «Сади Приклад»",
"pack_size": 6,
"weight_kg": "1.6",
"units_per_pallet": 720,
"is_active": true
}
},
{
"entity_type": "counterparty",
"operation": "upsert",
"external_id": "7a2d4f10-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"name": "ФОП Петренко Олена Василівна",
"kind": "buyer",
"edrpou": "3012345678",
"payment_terms_days": 7,
"is_active": true
}
},
{
"entity_type": "client",
"operation": "upsert",
"external_id": "5c1e8a20-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"name": "Маркет «Колосок»",
"address": "вул. Шевченка, 12",
"city": "Полтава",
"phone": "+380671234567",
"channel": "Традиційна торгівля",
"counterparty_external_id": "7a2d4f10-4b7f-11ee-9d3c-0050569a1b2c",
"price_list_code": "000000002",
"payment_terms_days": "14",
"latitude": "49.5883",
"longitude": "34.5514"
}
},
{
"entity_type": "price_list_item",
"operation": "upsert",
"external_id": "000000002:b3f61c42-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"price_list_code": "000000002",
"product_code_1c": "b3f61c42-4b7f-11ee-9d3c-0050569a1b2c",
"price": "17.90"
}
}
]
} exchange-inbound-batch-stock.json Залишки складу та денний зріз залишків. Завантажити
{
"partial": true,
"items": [
{
"entity_type": "product_stock",
"operation": "upsert",
"external_id": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c",
"payload": {
"warehouse_code_1c": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c",
"items": [
{"product_code_1c": "b3f61c42-4b7f-11ee-9d3c-0050569a1b2c", "qty": "1320", "amount": "19140.00"},
{"product_code_1c": "b3f61c43-4b7f-11ee-9d3c-0050569a1b2c", "qty": "48", "amount": "1056.00"},
{"product_code_1c": "b3f61c44-4b7f-11ee-9d3c-0050569a1b2c", "qty": "0"}
]
}
},
{
"entity_type": "product_stock_snapshot",
"operation": "upsert",
"external_id": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c:2026-10-26",
"payload": {
"warehouse_code_1c": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c",
"on_date": "2026-10-26",
"items": [
{"product_code_1c": "b3f61c42-4b7f-11ee-9d3c-0050569a1b2c", "qty": "1416", "amount": "20532.00"},
{"product_code_1c": "b3f61c43-4b7f-11ee-9d3c-0050569a1b2c", "qty": "60", "amount": "1320.00"}
]
}
}
]
} exchange-inbound-batch-sales.json Продаж, повернення, закриття вікна продажів, документ боргу. Завантажити
{
"partial": true,
"items": [
{
"entity_type": "goods_document",
"operation": "upsert",
"external_id": "e7a1c3d5-6f80-11ef-8a1b-0050569a1b2c",
"payload": {
"doc_type": "sale",
"doc_number": "РН-0004512",
"doc_date": "2026-10-26T14:35:00",
"is_posted": true,
"warehouse_code": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c",
"counterparty_code": "7a2d4f10-4b7f-11ee-9d3c-0050569a1b2c",
"counterparty_name": "ФОП Петренко Олена Василівна",
"client_code_1c": "5c1e8a20-4b7f-11ee-9d3c-0050569a1b2c",
"client_name": "Маркет «Колосок»",
"agent_external_id": "a8b9c0d1-4b7f-11ee-9d3c-0050569a1b2c",
"agent_name": "Іваненко Ігор",
"manufacturer_external_id": "000000017",
"manufacturer_name": "ТОВ «Сади Приклад»",
"trade_kind": "Безготівковий розрахунок",
"order_number": "ORD-20261026-0042",
"total_amount": "644.40",
"lines": [
{"line_no": 1, "product_code_1c": "b3f61c42-4b7f-11ee-9d3c-0050569a1b2c", "quantity": "24", "price": "17.90", "amount": "429.60", "vat_amount": "71.60"},
{"line_no": 2, "product_code_1c": "b3f61c43-4b7f-11ee-9d3c-0050569a1b2c", "quantity": "12", "price": "17.90", "amount": "214.80", "vat_amount": "35.80"}
]
}
},
{
"entity_type": "goods_document",
"operation": "upsert",
"external_id": "e7a1c3d6-6f80-11ef-8a1b-0050569a1b2c",
"payload": {
"doc_type": "sale_return",
"doc_number": "ПВ-0000318",
"doc_date": "2026-10-26T16:10:00",
"warehouse_code": "2f6c9e30-4b7f-11ee-9d3c-0050569a1b2c",
"client_code_1c": "5c1e8a20-4b7f-11ee-9d3c-0050569a1b2c",
"agent_external_id": "a8b9c0d1-4b7f-11ee-9d3c-0050569a1b2c",
"manufacturer_external_id": "000000017",
"lines": [
{"line_no": 1, "product_code_1c": "b3f61c42-4b7f-11ee-9d3c-0050569a1b2c", "quantity": "2", "price": "17.90", "amount": "35.80"}
]
}
},
{
"entity_type": "goods_period",
"operation": "upsert",
"external_id": "period-2026-09-27-2026-10-26",
"payload": {
"date_from": "2026-09-27",
"date_to": "2026-10-26",
"started_at": "2026-10-26T18:00:00",
"doc_types": ["sale", "sale_return"],
"totals": [
{"doc_type": "sale", "docs": 1, "quantity": "36", "amount": "644.40"},
{"doc_type": "sale_return", "docs": 1, "quantity": "2", "amount": "35.80"}
]
}
},
{
"entity_type": "debt_document",
"operation": "upsert",
"external_id": "d2c4e6f8-6f80-11ef-8a1b-0050569a1b2c",
"payload": {
"client_code_1c": "5c1e8a20-4b7f-11ee-9d3c-0050569a1b2c",
"client_name": "Маркет «Колосок»",
"doc_number": "РН-0004512",
"doc_type": "realization",
"doc_date": "2026-10-26",
"due_date": "2026-10-26",
"amount": "644.40",
"remaining_amount": "644.40",
"manufacturer_external_id": "000000017",
"agent_external_id": "a8b9c0d1-4b7f-11ee-9d3c-0050569a1b2c"
}
}
]
} exchange-outbox-pull-response.json Відповідь outbox/pull: заявка торгового й оплата. Завантажити
{
"data": {
"items": [
{
"queue_id": "0192f3a1-7c2e-7b10-9a4d-5e6f7a8b9c01",
"entity_type": "order",
"entity_id": "0192f3a0-1111-7aaa-8bbb-0c0d0e0f1a2b",
"operation": "create",
"attempt": 1,
"payload": {
"order_id": "0192f3a0-1111-7aaa-8bbb-0c0d0e0f1a2b",
"order_number": "ORD-20261027-0042",
"order_type": "sale",
"return_basis": null,
"payment_form": "cash",
"comment": "Доставити до 12:00",
"order_date": "2026-10-27",
"requested_delivery_date": "2026-10-28",
"client_id": "0192e001-2222-7ccc-8ddd-1e2f3a4b5c6d",
"client_code_1c": "5b1f2c3d-4e5f-11ee-9a0b-0050569a1b2c",
"agent_id": "0192e002-3333-7eee-8fff-2a3b4c5d6e7f",
"agent": {"name": "Іваненко Ірина", "code_1c": "7c2a3b4d-5e6f-11ee-9a0b-0050569a1b2c"},
"price_list": {"id": "0192e003-4444-7aaa-8bbb-3b4c5d6e7f80", "name": "Оптова", "code": "OPT"},
"status": "approved",
"subtotal": "1250.00",
"discount_amount": "0.00",
"vat_amount": "208.33",
"total_amount": "1250.00",
"currency": "UAH",
"warehouse": {"id": "0192e004-5555-7ccc-8ddd-4c5d6e7f8091", "name": "Склад Основний", "code_1c": "8d3b4c5e-6f70-11ee-9a0b-0050569a1b2c"},
"lines": [
{
"line_number": 1,
"product_id": "0192e005-6666-7eee-8fff-5d6e7f8091a2",
"logistics_product_id": null,
"product_code_1c": "9e4c5d6f-7081-11ee-9a0b-0050569a1b2c",
"product_sku": "JA-1000",
"product_name": "Сік яблучний 1 л",
"quantity": "10.000",
"pack_size": 1,
"packages": null,
"unit_price": "125.00",
"vat_rate": "20.00",
"line_total": "1250.00",
"is_promo": false,
"promo_name": null,
"manufacturer_name": "ТОВ «Сади Приклад»",
"manufacturer_code_1c": "af5d6e70-8192-11ee-9a0b-0050569a1b2c",
"price_list": {"id": "0192e003-4444-7aaa-8bbb-3b4c5d6e7f80", "name": "Оптова", "code": "OPT"}
}
],
"manufacturer": {"id": "0192e006-7777-7aaa-8bbb-6e7f8091a2b3", "name": "ТОВ «Сади Приклад»", "code_1c": "af5d6e70-8192-11ee-9a0b-0050569a1b2c"},
"manufacturers": [
{"id": "0192e006-7777-7aaa-8bbb-6e7f8091a2b3", "name": "ТОВ «Сади Приклад»", "code_1c": "af5d6e70-8192-11ee-9a0b-0050569a1b2c"}
],
"multi_manufacturer": false
}
},
{
"queue_id": "0192f3a1-7c2e-7b10-9a4d-5e6f7a8b9c02",
"entity_type": "order_payment",
"entity_id": "0192f3a0-2222-7aaa-8bbb-0c0d0e0f1a2c",
"operation": "create",
"attempt": 1,
"payload": {
"payment_id": "0192f3a0-2222-7aaa-8bbb-0c0d0e0f1a2c",
"payment_number": "PAY-20261027-0007",
"client_id": "0192e001-2222-7ccc-8ddd-1e2f3a4b5c6d",
"client_code_1c": "5b1f2c3d-4e5f-11ee-9a0b-0050569a1b2c",
"agent_id": "0192e002-3333-7eee-8fff-2a3b4c5d6e7f",
"amount": "800.00",
"currency": "UAH",
"method": "cash",
"collected_at": "2026-10-27T09:41:12.512000+00:00",
"reference": null,
"status": "completed",
"allocations": [],
"debt_documents": [
{"external_id_1c": "c07e8f90-a1b2-11ee-9a0b-0050569a1b2c", "doc_number": "РН-001234", "amount": "800.00"}
]
}
}
],
"has_more": false
}
} exchange-outbox-ack-request.json Тіло outbox/ack: успіх і помилка. Завантажити
{
"tenant_id": "3f2a9c1e-5b7d-4e2a-9c1f-0a1b2c3d4e5f",
"results": [
{
"queue_id": "0192f3a1-7c2e-7b10-9a4d-5e6f7a8b9c01",
"status": "ok",
"external_id": "d18f9a01-b2c3-11ee-9a0b-0050569a1b2c",
"permanent": false
},
{
"queue_id": "0192f3a1-7c2e-7b10-9a4d-5e6f7a8b9c02",
"status": "error",
"error_message": "Не знайдено точку за client_code_1c 5b1f2c3d-4e5f-11ee-9a0b-0050569a1b2c",
"permanent": false
}
]
} exchange_client.py Мінімальний клієнт обміну на Python: підпис, пакети, pull і ack. Завантажити
"""Мінімальний клієнт обміну ApexSFA ↔ облікова система (приклад).
Залежність: requests (pip install requests).
Змінні середовища:
APEX_BASE_URL — напр. https://api.apexsfa.com/api/v1
APEX_TENANT_ID — UUID тенанта (точки синхронізації)
APEX_SECRET — ключ обміну тенанта
Запуск:
python exchange_client.py send # відправити тестовий батч довідників
python exchange_client.py pull # забрати документи з черги й підтвердити
"""
from __future__ import annotations
import hashlib
import json
import os
import sys
import time
import uuid
from collections.abc import Callable
from typing import Any
import requests
BASE_URL = os.environ.get("APEX_BASE_URL", "https://api.apexsfa.com/api/v1").rstrip("/")
TENANT_ID = os.environ["APEX_TENANT_ID"]
SECRET = os.environ["APEX_SECRET"]
BATCH_CHUNK = 1000 # сервер приймає до 5000 конвертів, але менші пакети надійніші
def sign_v2(body: str, secret: str, ts: int | None = None) -> str:
"""X-Signature схеми v2: sha256(ts + "." + body + "." + secret) у hex."""
ts = int(time.time()) if ts is None else ts
digest = hashlib.sha256(f"{ts}.{body}.{secret}".encode()).hexdigest()
return f"t={ts},v2={digest}"
def _post(
path: str, payload: dict[str, Any], *, max_429_retries: int = 4
) -> requests.Response:
# Тіло серіалізуємо один раз: підпис рахується саме з тих байтів, що підуть у мережу.
body = json.dumps(payload, ensure_ascii=False, separators=(",", ":"))
for attempt in range(max_429_retries + 1):
headers = {
"Content-Type": "application/json",
"X-Tenant-Id": TENANT_ID,
"X-Signature": sign_v2(body, SECRET),
}
resp = requests.post(
f"{BASE_URL}{path}", data=body.encode("utf-8"), headers=headers, timeout=300
)
if resp.status_code != 429 or attempt == max_429_retries:
return resp
pause = int(resp.headers.get("Retry-After", "5") or 5)
time.sleep(max(1, min(pause, 60)))
return resp
def send_batch(
items: list[dict[str, Any]], *, partial: bool = True
) -> list[dict[str, Any]]:
"""Відправити конверти пакетами по BATCH_CHUNK. Повертає результати всіх рядків."""
all_results: list[dict[str, Any]] = []
for start in range(0, len(items), BATCH_CHUNK):
chunk = items[start : start + BATCH_CHUNK]
resp = _post("/webhooks/1c/inbound/batch", {"partial": partial, "items": chunk})
if resp.status_code != 200:
raise RuntimeError(
f"batch {start}: HTTP {resp.status_code} {resp.text[:500]}"
)
data = resp.json()["data"]
for row in data["results"]:
row["index"] += start
if not row["accepted"]:
print(
f" ✗ {row['entity_type']} {row['external_id']}: {row.get('error')}"
)
all_results.extend(data["results"])
print(
f"пакет {start}: прийнято {data['accepted_count']}, відхилено {data['failed_count']}"
)
return all_results
def pull_and_ack(
handler: Callable[[dict[str, Any]], str | None],
*,
entity_types: list[str] | None = None,
batch_size: int = 100,
max_rounds: int = 20,
) -> None:
"""Забрати елементи черги, обробити кожен handler-ом і підтвердити.
handler(item) повертає ідентифікатор створеного документа (або None)
чи кидає виняток. Виняток з текстом, що починається на "[PERM]", —
остаточна відмова без повторів.
"""
for _ in range(max_rounds):
req: dict[str, Any] = {"batch_size": batch_size}
if entity_types:
req["entity_types"] = entity_types
# Однакове тіло в ту саму секунду дасть однаковий підпис, а повтор
# підпису сервер відхиляє (антиповтор) — тому унікальна мітка.
req["request_nonce"] = str(uuid.uuid4())
resp = _post("/webhooks/1c/outbox/pull", req)
if resp.status_code != 200:
raise RuntimeError(f"pull: HTTP {resp.status_code} {resp.text[:500]}")
data = resp.json()["data"]
items = data["items"]
if not items:
print("черга порожня")
return
results: list[dict[str, Any]] = []
for item in items:
try:
external_id = handler(item)
res: dict[str, Any] = {"queue_id": item["queue_id"], "status": "ok"}
if external_id:
res["external_id"] = str(external_id)[:200]
except Exception as exc:
message = str(exc)[:2000] or exc.__class__.__name__
res = {
"queue_id": item["queue_id"],
"status": "error",
"error_message": message,
"permanent": message.startswith("[PERM]"),
}
results.append(res)
for start in range(0, len(results), 200):
ack = _post(
"/webhooks/1c/outbox/ack", {"results": results[start : start + 200]}
)
if ack.status_code != 200:
# Без ACK елементи повернуться у видачу самі після тайм-ауту блокування.
raise RuntimeError(f"ack: HTTP {ack.status_code} {ack.text[:500]}")
print("ack:", ack.json()["data"])
if not data.get("has_more"):
return
def _demo_handler(item: dict[str, Any]) -> str | None:
payload = item["payload"]
if item["entity_type"] == "order":
print(
f"заявка {payload.get('order_number')} ({item['operation']}), рядків {len(payload.get('lines') or [])}"
)
return f"DOC-{payload.get('order_number')}"
if item["entity_type"] == "order_payment":
print(
f"оплата {payload.get('payment_number')} на {payload.get('amount')} {payload.get('currency')}"
)
return f"PKO-{payload.get('payment_number')}"
raise RuntimeError(
f"[PERM] тип {item['entity_type']} цим клієнтом не підтримується"
)
if __name__ == "__main__":
command = sys.argv[1] if len(sys.argv) > 1 else "pull"
if command == "send":
send_batch(
[
{
"entity_type": "warehouse",
"operation": "upsert",
"external_id": "8d3b4c5e-6f70-11ee-9a0b-0050569a1b2c",
"payload": {"name": "Склад Основний"},
}
]
)
elif command == "pull":
pull_and_ack(_demo_handler, entity_types=["order", "order_payment"])
else:
sys.exit(f"невідома команда: {command}") Файли заказів виробника
order-file-example.xml Файл заказів у форматі XML «Заказы». Завантажити
<?xml version="1.0" encoding="UTF-8"?>
<Заказы>
<Документы>
<Документ>
<Идентификатор>7c1e2a4b-9d3f-11ef-8a2b-00155d012345</Идентификатор>
<Номер>ВН-000123</Номер>
<Дата>2026-10-27T09:15:00</Дата>
<ВидОперации>Продажа</ВидОперации>
<Организация>
<Наименование>ТОВ «Приклад Виробник»</Наименование>
<ЕГРПОУ>12345678</ЕГРПОУ>
</Организация>
<Контрагент>
<Идентификатор>000000439</Идентификатор>
<Наименование>ТОВ «Мережа Приклад»</Наименование>
<ЕГРПОУ>87654321</ЕГРПОУ>
</Контрагент>
<АдресДоставки>
<Идентификатор>4 820 000 900 017</Идентификатор>
<Адрес>Київська обл., с. Приклад, вул. Садова, 1</Адрес>
</АдресДоставки>
<Договор>
<Наименование>Договір поставки №15</Наименование>
<Номер>15</Номер>
<Дата>2026-01-10</Дата>
<Тип>СПокупателем</Тип>
</Договор>
<Заказ>
<Номер>PO-2026-000501</Номер>
<Дата>2026-10-26</Дата>
</Заказ>
<СуммаВключаетНДС>Истина</СуммаВключаетНДС>
<ДатаПоставки>2026-10-28</ДатаПоставки>
<Комментарий>Доставка до 12:00</Комментарий>
<Строки>
<Строка>
<Номенклатура>
<Идентификатор>000001283</Идентификатор>
<Наименование>Печиво здобне «Приклад» 0,3 кг</Наименование>
</Номенклатура>
<ШтрихкодКонтрагента>4820000000017</ШтрихкодКонтрагента>
<АртикулКонтрагента>100500017</АртикулКонтрагента>
<ОКЕИ>шт</ОКЕИ>
<Количество>24</Количество>
<КоличествоКоробок>2</КоличествоКоробок>
<ЦенаБезНДС>35,50</ЦенаБезНДС>
<ЦенаСНДС>42,60</ЦенаСНДС>
<СуммаБезНДС>852,00</СуммаБезНДС>
<СуммаНДС>170,40</СуммаНДС>
<СуммаСНДС>1 022,40</СуммаСНДС>
</Строка>
<Строка>
<Номенклатура>
<Идентификатор>000001290</Идентификатор>
<Наименование>Вафлі «Приклад» 0,2 кг</Наименование>
</Номенклатура>
<ШтрихкодКонтрагента>4820000000024</ШтрихкодКонтрагента>
<ОКЕИ>шт</ОКЕИ>
<Количество>12</Количество>
<ЦенаБезНДС>20,00</ЦенаБезНДС>
<ЦенаСНДС>24,00</ЦенаСНДС>
<СуммаБезНДС>240,00</СуммаБезНДС>
<СуммаНДС>48,00</СуммаНДС>
<СуммаСНДС>288,00</СуммаСНДС>
</Строка>
</Строки>
<Итоги>
<ВсегоСтрок>2</ВсегоСтрок>
</Итоги>
</Документ>
</Документы>
</Заказы> order-file-response-example.xml Файл-відповідь XML: документ «Редаговано». Завантажити
<?xml version='1.0' encoding='UTF-8'?>
<Заказы>
<Документы>
<Документ>
<Идентификатор>7c1e2a4b-9d3f-11ef-8a2b-00155d012345</Идентификатор>
<Номер>ВН-000123</Номер>
<Дата>2026-10-27T09:15:00</Дата>
<ВидОперации>Продажа</ВидОперации>
<Состояние>Редаговано</Состояние>
<Организация>
<Наименование>ТОВ «Приклад Виробник»</Наименование>
<ЕГРПОУ>12345678</ЕГРПОУ>
</Организация>
<Контрагент>
<Идентификатор>000000439</Идентификатор>
<Наименование>ТОВ «Мережа Приклад»</Наименование>
<ЕГРПОУ>87654321</ЕГРПОУ>
</Контрагент>
<АдресДоставки>
<Идентификатор>4 820 000 900 017</Идентификатор>
<Адрес>Київська обл., с. Приклад, вул. Садова, 1</Адрес>
</АдресДоставки>
<Договор>
<Наименование>Договір поставки №15</Наименование>
<Номер>15</Номер>
<Дата>2026-01-10</Дата>
<Тип>СПокупателем</Тип>
</Договор>
<Заказ>
<Номер>PO-2026-000501</Номер>
<Дата>2026-10-26</Дата>
</Заказ>
<СуммаВключаетНДС>Истина</СуммаВключаетНДС>
<ДатаПоставки>2026-10-28</ДатаПоставки>
<Комментарий>Доставка до 12:00</Комментарий>
<Строки>
<Строка>
<Номенклатура>
<Идентификатор>000001283</Идентификатор>
<Наименование>Печиво здобне «Приклад» 0,3 кг</Наименование>
</Номенклатура>
<ШтрихкодКонтрагента>4820000000017</ШтрихкодКонтрагента>
<АртикулКонтрагента>100500017</АртикулКонтрагента>
<ОКЕИ>шт</ОКЕИ>
<Количество>20</Количество>
<КоличествоКоробок>2</КоличествоКоробок>
<ЦенаБезНДС>35,50</ЦенаБезНДС>
<ЦенаСНДС>42,60</ЦенаСНДС>
<СуммаБезНДС>710</СуммаБезНДС>
<СуммаНДС>142</СуммаНДС>
<СуммаСНДС>852</СуммаСНДС>
</Строка>
</Строки>
<Итоги>
<ВсегоСтрок>1</ВсегоСтрок>
</Итоги>
</Документ>
</Документы>
</Заказы> order-file-example.json Файл заказів у форматі JSON «Замовлення». Завантажити
{
"Замовлення": [
{
"Ссылка": "5b0f3c2e-1a7d-11ef-9c41-00155d0abcde",
"Номер": "РН-004715",
"ДатаОтгрузки": "2026-10-28",
"НомерЗаказаEDIN": "PO-2026-000502",
"ДатаЗаказаEDIN": "2026-10-27",
"Организация": "ТОВ «Приклад Виробник»",
"ЕДРПОУ_Орг": "12345678",
"Контрагент": "ТОВ «Мережа Приклад»",
"ЕДРПОУ_Контр": "87654321",
"ИНН_Контр": "876543226591",
"КодТочкиGLN": "4820000900024",
"АдресДоставки": "Київська обл., с. Приклад, вул. Княгині Ольги, 67",
"Договор": "Договір поставки №15",
"НомерДоговора": "15",
"ДатаДоговора": "2026-01-10",
"ЦеныВклНДС": "Да",
"СуммаДокумента": "1310.40",
"Товары": [
{
"НомСтр": "1",
"Код": "000001283",
"Ссылка": "9a8b7c6d-0000-11ef-8a2b-00155d012345",
"Товар": "Печиво здобне «Приклад» 0,3 кг",
"Артикул1": "100200300",
"Артикул2": "100500017",
"Штрихкод": "4820000000017",
"ШтрихкодЯщ": "14820000000014",
"Единица": "шт",
"Количество": "24",
"КоличествоЯщ": "2",
"Цена": "42.60",
"Сумма": "1022.40",
"СуммаНДС": "170.40"
},
{
"НомСтр": "2",
"Код": "000001290",
"Ссылка": "9a8b7c6d-0001-11ef-8a2b-00155d012345",
"Товар": "Вафлі «Приклад» 0,2 кг",
"Артикул1": "100200301",
"Артикул2": "100500024",
"Штрихкод": "4820000000024",
"Единица": "шт",
"Количество": "12",
"КоличествоЯщ": "1",
"Цена": "24.00",
"Сумма": "288.00",
"СуммаНДС": "48.00"
}
]
}
]
} order-file-response-example.json Файл-відповідь JSON. Завантажити
{
"Замовлення": [
{
"Ссылка": "5b0f3c2e-1a7d-11ef-9c41-00155d0abcde",
"Номер": "РН-004715",
"ДатаОтгрузки": "2026-10-28",
"НомерЗаказаEDIN": "PO-2026-000502",
"ДатаЗаказаEDIN": "2026-10-27",
"Организация": "ТОВ «Приклад Виробник»",
"ЕДРПОУ_Орг": "12345678",
"Контрагент": "ТОВ «Мережа Приклад»",
"ЕДРПОУ_Контр": "87654321",
"ИНН_Контр": "876543226591",
"КодТочкиGLN": "4820000900024",
"АдресДоставки": "Київська обл., с. Приклад, вул. Княгині Ольги, 67",
"Договор": "Договір поставки №15",
"НомерДоговора": "15",
"ДатаДоговора": "2026-01-10",
"ЦеныВклНДС": "Да",
"СуммаДокумента": "1310.40",
"Состояние": "Опрацьовано",
"Товары": [
{
"НомСтр": "1",
"Код": "000001283",
"Ссылка": "9a8b7c6d-0000-11ef-8a2b-00155d012345",
"Товар": "Печиво здобне «Приклад» 0,3 кг",
"Артикул1": "100200300",
"Артикул2": "100500017",
"Штрихкод": "4820000000017",
"ШтрихкодЯщ": "14820000000014",
"Единица": "шт",
"Количество": "24",
"КоличествоЯщ": "2",
"Цена": "42.60",
"Сумма": "1022.40",
"СуммаНДС": "170.40"
},
{
"НомСтр": "2",
"Код": "000001290",
"Ссылка": "9a8b7c6d-0001-11ef-8a2b-00155d012345",
"Товар": "Вафлі «Приклад» 0,2 кг",
"Артикул1": "100200301",
"Артикул2": "100500024",
"Штрихкод": "4820000000024",
"Единица": "шт",
"Количество": "12",
"КоличествоЯщ": "1",
"Цена": "24.00",
"Сумма": "288.00",
"СуммаНДС": "48.00"
}
]
}
]
} order-file-example.csv Файл заказів CSV за номером документа (Windows-1251). Завантажити
НомерДокумента;ДатаДокумента;Заказ;КодКонтрагента;НаименованиеКонтрагента;ЕДРПОУ_Контрагента;ИНН Контрагента;Номер_Договора;GLN_ТочкиДоставки;КодТочкиДоставки;НаименованиеТочкиДоставки;КодСкладаОтгрузки;НаименованиеСкладаОтгрузки;КодТовара;НаименованиеТовара;ШтрихКодТовара;НазваниеЕдИзм;Количество;КоличествоМест;СуммаСНДС;СуммаНДС
ЗМ-000501;27.10.2026;PO-2026-000501;000000439;ТОВ «Мережа Приклад»;87654321;876543226591;15;4820000900017;0001;Магазин №1, вул. Садова, 1;01;Основний склад;100500017;Печиво здобне «Приклад» 0,3 кг;4820000000017;шт;24;2;1022,40;170,40
ЗМ-000501;27.10.2026;PO-2026-000501;000000439;ТОВ «Мережа Приклад»;87654321;876543226591;15;4820000900017;0001;Магазин №1, вул. Садова, 1;01;Основний склад;100500024;Вафлі «Приклад» 0,2 кг;4820000000024;шт;12;1;288,00;48,00
ЗМ-000502;27.10.2026;PO-2026-000502;000000439;ТОВ «Мережа Приклад»;87654321;876543226591;15;4820000900024;0002;Магазин №2, вул. Княгині Ольги, 67;01;Основний склад;100500017;Печиво здобне «Приклад» 0,3 кг;4820000000017;шт;48;4;2044,80;340,80 order-file-response-example.csv Файл-відповідь CSV з колонкою «Состояние». Завантажити
НомерДокумента;ДатаДокумента;Заказ;КодКонтрагента;НаименованиеКонтрагента;ЕДРПОУ_Контрагента;ИНН Контрагента;Номер_Договора;GLN_ТочкиДоставки;КодТочкиДоставки;НаименованиеТочкиДоставки;КодСкладаОтгрузки;НаименованиеСкладаОтгрузки;КодТовара;НаименованиеТовара;ШтрихКодТовара;НазваниеЕдИзм;Количество;КоличествоМест;СуммаСНДС;СуммаНДС;Состояние
ЗМ-000501;27.10.2026;PO-2026-000501;000000439;ТОВ «Мережа Приклад»;87654321;876543226591;15;4820000900017;0001;Магазин №1, вул. Садова, 1;01;Основний склад;100500017;Печиво здобне «Приклад» 0,3 кг;4820000000017;шт;24;2;1022,40;170,40;Опрацьовано
ЗМ-000501;27.10.2026;PO-2026-000501;000000439;ТОВ «Мережа Приклад»;87654321;876543226591;15;4820000900017;0001;Магазин №1, вул. Садова, 1;01;Основний склад;100500024;Вафлі «Приклад» 0,2 кг;4820000000024;шт;12;1;288,00;48,00;Опрацьовано
ЗМ-000502;27.10.2026;PO-2026-000502;000000439;ТОВ «Мережа Приклад»;87654321;876543226591;15;4820000900024;0002;Магазин №2, вул. Княгині Ольги, 67;01;Основний склад;100500017;Печиво здобне «Приклад» 0,3 кг;4820000000017;шт;48;4;2044,80;340,80;Відхилено Вебхуки
webhook-event-example.json Тіло події order.approved. Завантажити
{
"delivered_attempt": 1,
"entity_id": "0192f3c1-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"entity_type": "order",
"event_id": "0192f3c1-6e7f-7091-8a2b-3c4d5e6f7a8b",
"event_type": "order.approved",
"payload": {
"agent_id": "3f2a9b7c-1d2e-4f50-8a6b-7c8d9e0f1a2b",
"client_id": "3f2a4d5e-6f70-4812-9a3b-4c5d6e7f8091",
"currency": "UAH",
"order_date": "2026-10-27",
"order_id": "0192f3c1-5d6e-7f80-9a1b-2c3d4e5f6a7b",
"order_number": "ORD-20261027-0042",
"status": "approved",
"total_amount": "12450.00"
},
"tenant_id": "3f2a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c"
} webhook_verify.py Перевірка підпису вебхука на Python (з прикладом для Flask). Завантажити
"""Перевірка підпису вихідного вебхука ApexSFA на стороні отримувача.
Заголовок: X-Webhook-Signature: t=<unix-секунди>,v1=<hex HMAC-SHA256>
Підписується рядок f"{t}.{сире тіло запиту}" секретом підписки (secret_key).
Перевіряйте САМЕ сирі байти тіла — не перекодований JSON.
"""
from __future__ import annotations
import hashlib
import hmac
import time
TOLERANCE_SECONDS = 300
def verify_signature(
raw_body: bytes,
signature_header: str,
secret: str,
*,
tolerance: int = TOLERANCE_SECONDS,
) -> bool:
try:
parts = dict(item.split("=", 1) for item in signature_header.split(","))
timestamp = int(parts["t"])
received = parts["v1"]
except (KeyError, ValueError):
return False
if tolerance and abs(time.time() - timestamp) > tolerance:
return False
signed = f"{timestamp}.".encode() + raw_body
expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received)
# Приклад на Flask:
#
# from flask import Flask, request, abort
# app = Flask(__name__)
# SECRET = "whsec_XXXXXXXXXXXXXXXXXXXXXXXX"
# seen_event_ids: set[str] = set() # у проді — таблиця у вашій БД
#
# @app.post("/apexsfa/webhook")
# def apexsfa_webhook():
# raw = request.get_data()
# if not verify_signature(raw, request.headers.get("X-Webhook-Signature", ""), SECRET):
# abort(401)
# event = request.get_json()
# if event["event_id"] in seen_event_ids:
# return "", 200 # повтор — уже оброблено
# seen_event_ids.add(event["event_id"])
# # ... обробка event["event_type"], event["payload"] ...
# return "", 200
if __name__ == "__main__":
secret = "whsec_XXXXXXXXXXXXXXXXXXXXXXXX"
body = b'{"delivered_attempt":1,"entity_id":null,"entity_type":null,"event_id":"0192f3c1-6e7f-7091-8a2b-3c4d5e6f7a8b","event_type":"webhook.test","payload":{"ping_at":"2026-10-27T08:00:00+00:00","test_payload":{"test":true}},"tenant_id":"3f2a1b2c-3d4e-4f5a-8b6c-7d8e9f0a1b2c"}'
ts = int(time.time())
digest = hmac.new(
secret.encode(), f"{ts}.".encode() + body, hashlib.sha256
).hexdigest()
header = f"t={ts},v1={digest}"
print(header, verify_signature(body, header, secret))