Swagger

Webhooky

KROS vie o udalostiach vo Fakturácii a Financiách informovať váš server v reálnom čase — namiesto toho, aby ste dáta museli pravidelne dopytovať. Táto stránka popisuje tri eventy, ktoré KROS posiela, ako overiť, že notifikácia naozaj prišla z KROSu, a ako čítať telo notifikácie vrátane čiastočných zlyhaní.

Tri eventy

KROS posiela tri typy notifikácií. Výber správneho — a rozlíšenie doklad vs. Financie — je dôležitý: zámena je dôvod, prečo partneri dostávajú eventy, ktoré nečakali, alebo im naopak chýbajú tie, ktoré potrebujú.

Event Kedy sa pošle
Document webhook doklad vo Fakturácii bol vytvorený, zmenený alebo vymazaný.
Payment webhook (doklad) platba dokladu bola vytvorená, zmenená alebo vymazaná; netýka sa zmien v module Financie.
Payment webhook (Financie) platba v module Financie bola vytvorená, zmenená alebo vymazaná; nezahŕňa platby dokladov, ktoré nie sú viazané na bankový účet, platobnú bránu ani pokladnicu.

Kde sa webhook nastavuje

Adresu svojho endpointu zadáte jedným z dvoch spôsobov: buď priamo v KROS UI v sekcii API prepojenia, do poľa URL pre príjem notifikácií (pozri Manuálny token), alebo cez parameter webhook consent URL, ak používate self-service autorizáciu (pozri Integration Consent).

Verifikácia podpisu

Kľúč aj telo sa kóduje ako UTF-16LE, nie UTF-8

Toto je najčastejšia príčina zlyhania integrácie. Podpis v hlavičke X-Kros-Signature-256 je HMAC-SHA256 nad telom požiadavky, kde sa aj telo, aj tajný kľúč kódujú ako UTF-16LE — nie ako UTF-8, ktoré je prirodzeným (a nesprávnym) predpokladom. Výsledný hash sa zapíše ako Base64. Odporúčaná dĺžka kľúča je 256 bitov. Ak kľúč alebo telo zakódujete ako UTF-8, výpočet prebehne bez chyby, ale vypočítaný podpis sa nikdy nebude zhodovať s tým, čo poslal KROS — chyba sa navonok neprejaví inak než tichým zlyhaním verifikácie.

Štyri funkčne rovnaké implementácie nižšie počítajú ten istý hash bajt po bajte. C# a PHP sú prevzaté priamo zo špecifikácie KROS Webhooks; JavaScript (Node) a Python sú napísané tak, aby dávali identický výsledok — kódovanie UTF-16LE zabezpečuje Buffer.from(s, 'utf16le') v Node a s.encode('utf-16-le') v Pythone.

Program.cs
private string? GenerateHash(string payload, string webHookSecret)
{
    var hash = new HMACSHA256(Encoding.Unicode.GetBytes(webHookSecret));
    return Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(payload)));
}

// Overenie prijatého webhooku — konštantný čas, nie ==:
var expected = GenerateHash(requestBody, webHookSecret);
var received = request.Headers["X-Kros-Signature-256"];
var isValid = CryptographicOperations.FixedTimeEquals(
    Convert.FromBase64String(expected),
    Convert.FromBase64String(received));
verify.php
function generateHash($data, $key) {
    $keyEncoded  = mb_convert_encoding($key, "UTF-16LE");
    $dataEncoded = mb_convert_encoding($data, "UTF-16LE");
    return base64_encode(hash_hmac("sha256", $dataEncoded, $keyEncoded, true));
}

// Overenie prijatého webhooku — konštantný čas, nie ==:
$expected = generateHash($requestBody, $webHookSecret);
$received = $_SERVER['HTTP_X_KROS_SIGNATURE_256'] ?? '';
$isValid = hash_equals($expected, $received);
verify.js
const crypto = require('crypto');

function generateHash(payload, webHookSecret) {
  const key = Buffer.from(webHookSecret, 'utf16le');
  const data = Buffer.from(payload, 'utf16le');
  return crypto.createHmac('sha256', key).update(data).digest('base64');
}

// Overenie prijatého webhooku — konštantný čas, nie ==:
const expected = generateHash(requestBody, webHookSecret);
const received = req.headers['x-kros-signature-256'] || '';
const isValid =
  expected.length === received.length &&
  crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
verify.py
import base64
import hashlib
import hmac

def generate_hash(payload: str, web_hook_secret: str) -> str:
    key = web_hook_secret.encode("utf-16-le")
    data = payload.encode("utf-16-le")
    digest = hmac.new(key, data, hashlib.sha256).digest()
    return base64.b64encode(digest).decode("ascii")

# Overenie prijatého webhooku — konštantný čas, nie ==:
expected = generate_hash(request_body, web_hook_secret)
received = request.headers.get("X-Kros-Signature-256", "")
is_valid = hmac.compare_digest(expected, received)

Vo všetkých štyroch jazykoch sa porovnanie robí konštantno-časovou funkciou (CryptographicOperations.FixedTimeEquals, hash_equals, crypto.timingSafeEqual, hmac.compare_digest), nikdy operátorom == ani ===. Naivné porovnanie reťazcov sa zastaví pri prvom nezhodnom znaku, a rozdiel v čase odpovede tak útočníkovi prezradí podpis bajt po bajte, kým ho neuhádne celý.

Telo notifikácie

Príklad úspešne spracovanej dávky:

JSON
{
  "companyId": 119919,
  "entityType": 2,
  "results": {
    "entities": [{
      "index": 0,
      "source": 1,
      "operation": 2,
      "status": 201,
      "data": {
        "documentType": 2,
        "documentId": 926523,
        "variableSymbol": "4444555",
        "sumOfPayment": 1
      },
      "problems": null
    }],
    "relatedEntities": []
  },
  "status": 200,
  "requestId": "50304274-197c-40e9-8804-84e23e28efa2"
}

207 znamená čiastočné zlyhanie

Vrchný status 200 znamená, že spracovanie prebehlo úplne v poriadku. 207 znamená čiastočné zlyhanie — niektoré položky v dávke sa nespracovali a podrobnosti nájdete v poli problems príslušnej entity. Webhook handler, ktorý kontroluje iba to, že status je 200, tieto čiastočné zlyhania potichu zahodí.

Pole requestId vám umožní priradiť túto notifikáciu k pôvodnému nahratiu — je to tá istá hodnota, akú ste dostali v odpovedi 202 Accepted pri nahrávaní dokladu.

Typy problémov

Typ Význam
resource-locked „Resource is locked by non-editable lock, therefore the resource cannot be edited.“
id-conflict zdroj sa nepodarilo jednoznačne identifikovať na úpravu.
duplicate-document-number rovnaké číslo dokladu a rad sa v dávke vyskytuje viac ako raz.

Tok

Vaša aplikácia KROS 1. Nahráte doklad napr. POST /api/invoices/batch 2. 202 Accepted telo obsahuje requestId 3. Asynchrónne spracovanie v KROSe 4. POST na váš webhook endpoint notifikácia o výsledku spracovania 5. Overíte podpis X-Kros-Signature-256 6. requestId priradí notifikáciu ku kroku 2
Tok webhooku: nahratie dokladu, 202 Accepted s requestId, asynchrónne spracovanie v KROSe, POST notifikácia na váš endpoint, overenie podpisu a priradenie výsledku k pôvodnému nahratiu podľa requestId.

Špecifikácia

Webhooky majú vlastnú OpenAPI špecifikáciu, oddelenú od špecifikácie endpointov: