🚀 Easy Invoice API

Kompletny przewodnik po tworzeniu faktur i integracji z KSeF

Easy Invoice API pozwala programistycznie tworzyć faktury VAT, dodawać pozycje, wysyłać dokumenty do Krajowego Systemu e-Faktur (KSeF) i pobierać potwierdzenia odbioru (UPO).

Ten przewodnik przeprowadzi Cię przez kompletny proces wystawienia faktury — od pustego dokumentu do numeru KSeF i kodu QR.

Konfiguracja klienta PHP

Pobierz klasę classUddsOx.php i skonfiguruj połączenie:

PHP
<?php
require_once('classUddsOx.php');

$udds = new classUddsOx();
$udds->customerCode = 'TWOJ_KOD_KLIENTA';  // Otrzymasz od KSI Media
$udds->login        = 'twoj_login';
$udds->password     = 'twoje_haslo';
$udds->serverUrl    = 'https://api.dlaoperatora.pl/udds/';
$udds->timeout      = 30; // sekundy
💡 Dane dostępowe

Dane do połączenia z API (customerCode, login, password) otrzymasz od administratora systemu dlaoperatora.pl lub KSI Media.

🔄 Workflow tworzenia faktury

6 kroków od utworzenia do numeru KSeF

1
Utwórz nagłówek
createInvoiceHead
2
Dodaj pozycje
createInvoiceItem ×N
3
Aktywuj
setInvoiceStatus
4
Wyślij do KSeF
sendInvoiceToKsef
5
Sprawdź status
getInvoice (polling)
6
Pobierz UPO/QR
ksef section
⚠️ Ważna kolejność

Musisz zachować kolejność kroków. Nie możesz wysłać faktury do KSeF bez wcześniejszego dodania pozycji i aktywacji dokumentu.

📄 Tworzenie nagłówka faktury

Endpoint: createInvoiceHead

Nagłówek faktury zawiera wszystkie dane oprócz pozycji: nabywcę, daty, metodę płatności, walutę. Numer faktury generuje się automatycznie z sekwencji systemowej.

💡 Status początkowy

Faktura tworzy się jako TYMCZASOWA (robocza). Możesz swobodnie dodawać/usuwać pozycje dopóki nie aktywujesz dokumentu.

Przykład — faktura krajowa PLN

PHP
<?php
/**
 * KROK 1: Tworzenie nagłówka faktury
 * 
 * Tworzymy fakturę VAT dla polskiej firmy.
 * Numer faktury generuje się automatycznie z sekwencji.
 */

$params = [
    // ═══════════════════════════════════════════════════════════════
    // DATY I MIEJSCE
    // ═══════════════════════════════════════════════════════════════
    'issuePlace'    => 'Warszawa',           // Miejsce wystawienia
    'issueDate'     => date('Y-m-d'),            // Data wystawienia (dziś)
    'saleDate'      => date('Y-m-d'),            // Data sprzedaży (dziś)
    
    // ═══════════════════════════════════════════════════════════════
    // TYP DOKUMENTU
    // ═══════════════════════════════════════════════════════════════
    'documentType'  => 'FV',                 // FV = Faktura VAT
                                             // FK = Faktura korygująca
                                             // PRO = Pro-forma
    
    'template'      => 'VAT',                // VAT lub MARZA
    'currency'      => 'PLN',                // Waluta (PLN, EUR, USD...)
    
    // ═══════════════════════════════════════════════════════════════
    // PŁATNOŚĆ
    // ═══════════════════════════════════════════════════════════════
    'paymentMethod' => 'przelew',            // przelew, gotówka, karta
    'paymentDueDate'=> date('Y-m-d', strtotime('+14 days')), // Termin płatności (+14 dni)
    
    // ═══════════════════════════════════════════════════════════════
    // NABYWCA (KUPUJĄCY)
    // ═══════════════════════════════════════════════════════════════
    'buyerName'     => 'Innowacje IT Sp. z o.o.',
    'buyerNip'      => '5213456789',          // NIP (10 cyfr, bez kresek)
    'buyerAddress'  => 'ul. Marszałkowska 100/5',
    'buyerPostCode' => '00-001',
    'buyerCity'     => 'Warszawa',
    'buyerCountry'  => 'Polska',
    
    // ═══════════════════════════════════════════════════════════════
    // KSEF — oznaczyć do wysyłki już teraz
    // ═══════════════════════════════════════════════════════════════
    'toKsef'        => 1,                    // 1 = wyślij do KSeF
                                             // 0 = nie wysyłaj
    
    // ═══════════════════════════════════════════════════════════════
    // OPCJONALNE
    // ═══════════════════════════════════════════════════════════════
    'notes'         => 'Płatność w terminie 14 dni',
    'printNotes'    => '1',                  // Drukuj uwagi na fakturze
];

$result = $udds->createInvoiceHead($params);

// ─────────────────────────────────────────────────────────────────────
// OBSŁUGA ODPOWIEDZI
// ─────────────────────────────────────────────────────────────────────

if ($result['status'] === 'OK') {
    $idInvoice     = $result['id'];      // np. "ABCD1234EFGH5678..."
    $invoiceNumber = $result['number'];  // np. "FV/0001/02/2026"
    
    echo "✅ Utworzono fakturę: {$invoiceNumber}\n";
    echo "   ID do dalszych operacji: {$idInvoice}\n";
    
} else {
    echo "❌ Błąd tworzenia faktury:\n";
    foreach ($result['errors'] as $error) {
        echo "   [{$error['code']}] {$error['message']}\n";
    }
    exit(1);
}

Struktura odpowiedzi

JSON
// Sukces
{
    "status": "OK",
    "id": "00000004M1G221031126JF1B44ASNQ6D",  // ID faktury (32 znaki)
    "number": "FV/0001/02/2026",               // Numer faktury
    "errors": []
}

// Błąd
{
    "status": "ERROR",
    "id": null,
    "number": null,
    "errors": [
        { "code": 1001, "message": "Pole buyerName jest wymagane" }
    ]
}

📦 Dodawanie pozycji do faktury

Endpoint: createInvoiceItem

Każda pozycja to osobne wywołanie createInvoiceItem. API automatycznie przelicza VAT i aktualizuje sumy na fakturze.

🔴 Kluczowe — przeliczniki

Wszystkie kwoty podajesz w groszach (×100)
100.00 PLN = 10000

Ilości też mnożysz przez 100
1 sztuka = 100, 0.5 kg = 50

Tabela przeliczników

Wartość Jak przeliczyć Przykład
Cena złotówki × 100 149.99 PLN → 14999
Ilość sztuki × 100 2.5 szt → 250
Stawka VAT string '23', '8', 'zw', 'np'

Przykład — różne pozycje

PHP
<?php
/**
 * KROK 2: Dodawanie pozycji do faktury
 * 
 * PAMIĘTAJ O PRZELICZNIKACH:
 *   - Kwoty w GROSZACH: 100.00 PLN = 10000 
 *   - Ilości ×100: 1 sztuka = 100, 0.5 kg = 50
 */

$idInvoice = 'ABCD1234EFGH5678...'; // ID z poprzedniego kroku

// ═══════════════════════════════════════════════════════════════════════
// POZYCJA 1: Usługa programistyczna (VAT 23%)
// ═══════════════════════════════════════════════════════════════════════

$item1 = $udds->createInvoiceItem([
    'idInvoice'  => $idInvoice,
    'name'       => 'Usługa programistyczna - rozwój aplikacji webowej',
    'quantity'   => 100,              // 1.00 sztuki (1 × 100)
    'unit'       => 'szt.',           // Jednostka miary
    'vatRate'    => '23',             // Stawka VAT
    'priceNetto' => 500000,           // 5000.00 PLN netto
]);

if ($item1['status'] === 'OK') {
    echo "✅ Dodano: Usługa programistyczna\n";
    echo "   Netto:  " . number_format($item1['calculatedValues']['valueNetto'] / 100, 2) . " PLN\n";
    echo "   VAT:    " . number_format($item1['calculatedValues']['valueVat'] / 100, 2) . " PLN\n";
    echo "   Brutto: " . number_format($item1['calculatedValues']['valueBrutto'] / 100, 2) . " PLN\n";
}

// ═══════════════════════════════════════════════════════════════════════
// POZYCJA 2: Szkolenie (stawka ZW — zwolniony)
// ═══════════════════════════════════════════════════════════════════════

$item2 = $udds->createInvoiceItem([
    'idInvoice'  => $idInvoice,
    'name'       => 'Szkolenie z obsługi systemu (8h)',
    'quantity'   => 800,              // 8.00 godzin (8 × 100)
    'unit'       => 'godz.',
    'vatRate'    => 'zw',             // Zwolniony z VAT
    'priceNetto' => 15000,            // 150.00 PLN/godz
]);

// ═══════════════════════════════════════════════════════════════════════
// POZYCJA 3: Produkt z ilością ułamkową
// ═══════════════════════════════════════════════════════════════════════

$item3 = $udds->createInvoiceItem([
    'idInvoice'  => $idInvoice,
    'name'       => 'Kabel sieciowy Cat6',
    'quantity'   => 2550,             // 25.50 metrów (25.5 × 100)
    'unit'       => 'mb',             // metr bieżący
    'vatRate'    => '23',
    'priceNetto' => 350,              // 3.50 PLN/mb
]);

// Wynik: 25.5 mb × 3.50 PLN = 89.25 PLN netto

Algorytm kalkulacji VAT

🧮 Wzór obliczania (tryb netto)
valueNetto  = priceNetto × quantity / 100
valueVat    = round(valueNetto × vatRate / 100)
valueBrutto = valueNetto + valueVat

// Przykład: 1 szt × 500 PLN netto, VAT 23%
// valueNetto  = 50000 × 100 / 100 = 50000 (500.00 PLN)
// valueVat    = round(50000 × 23 / 100) = 11500 (115.00 PLN)
// valueBrutto = 50000 + 11500 = 61500 (615.00 PLN)

Aktywacja faktury

Endpoint: setInvoiceStatus

Po dodaniu wszystkich pozycji zmień status na AKTYWNA. To "zamyka" fakturę — nadal możesz ją wysłać do KSeF, ale edycja jest ograniczona.

PHP
<?php
/**
 * KROK 3: Aktywacja faktury
 * 
 * Zmieniamy status z TYMCZASOWA na AKTYWNA.
 * Po aktywacji faktura jest gotowa do wysyłki.
 */

$result = $udds->setInvoiceStatus([
    'idInvoice' => $idInvoice,
    'status'    => 'AKTYWNA',
]);

if ($result['status'] === 'OK') {
    echo "✅ Faktura aktywowana\n";
    echo "   Poprzedni status: {$result['data']['previousStatus']}\n";
    echo "   Nowy status:      {$result['data']['newStatus']}\n";
} else {
    echo "❌ Błąd: {$result['errors'][0]['message']}\n";
}
🚫 Blokada po wysyłce do KSeF

Jeśli faktura ma już numer KSeF (ksefNumber), nie możesz zmienić jej statusu ani edytować treści.

📤 Wysyłka do KSeF

Endpoint: sendInvoiceToKsef

Wysyłka do Krajowego Systemu e-Faktur wymaga działającego KSeF Service (mikroserwis) i aktywnej sesji z urzędem.

PHP
<?php
/**
 * KROK 4: Wysyłka faktury do KSeF
 * 
 * Faktura musi mieć ustawione toKsef = 1
 * KSeF Service musi być uruchomiony i zainicjowany.
 */

// Opcjonalnie: jeśli nie oznaczyłeś faktury do KSeF przy tworzeniu
$udds->updateInvoiceHead([
    'idInvoice' => $idInvoice,
    'toKsef'    => 1,
]);

// Wysyłka
$result = $udds->sendInvoiceToKsef([
    'idInvoice' => $idInvoice,
]);

if ($result['status'] === 'OK') {
    echo "✅ Faktura wysłana do KSeF!\n";
    echo "   Odpowiedź: {$result['ksefResponse']['message']}\n";
    
} else {
    echo "❌ Błąd wysyłki: {$result['message']}\n";
    
    // Szczegóły błędu z KSeF Service
    if (isset($result['ksefResponse'])) {
        echo "   KSeF: [{$result['ksefResponse']['httpCode']}] ";
        echo "{$result['ksefResponse']['message']}\n";
    }
}

Typowe błędy KSeF Service

Kod Znaczenie Rozwiązanie
REST-003 KSeF nie jest zainicjowany Uruchom sesję w KSeF Service
REST-011 Brak rekordu w KSEF_FAKTURY Ustaw toKsef = 1
1004 Błąd połączenia Sprawdź czy KSeF Service działa

🔍 Weryfikacja statusu, UPO i QR code

Endpoint: getInvoice

Po wysłaniu faktury KSeF przetwarza ją asynchronicznie. Używaj getInvoice do sprawdzenia statusu i pobrania:

  • Numer KSeF — oficjalny numer w systemie
  • UPO — Urzędowe Poświadczenie Odbioru (link do pobrania)
  • QR Code — URL weryfikacyjny do zeskanowania

Przykład — polling statusu

PHP
<?php
/**
 * KROK 5: Weryfikacja statusu w KSeF
 * 
 * KSeF przetwarza faktury asynchronicznie.
 * Sprawdzamy co kilka sekund aż pojawi się numer KSeF.
 */

$maxAttempts = 10;
$delaySeconds = 3;

for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
    
    echo "🔄 Sprawdzanie statusu (próba {$attempt}/{$maxAttempts})...\n";
    
    $invoice = $udds->getInvoice(['idInvoice' => $idInvoice]);
    
    if ($invoice['status'] !== 'OK') {
        echo "❌ Błąd pobierania faktury\n";
        break;
    }
    
    $ksef = $invoice['data']['invoice']['ksef'];
    
    // ═══════════════════════════════════════════════════════════════════
    // Sprawdź czy mamy numer KSeF
    // ═══════════════════════════════════════════════════════════════════
    
    if (!empty($ksef['ksefNumber'])) {
        
        echo "\n════════════════════════════════════════════════════════════\n";
        echo "✅ FAKTURA ZAREJESTROWANA W KSEF!\n";
        echo "════════════════════════════════════════════════════════════\n\n";
        
        // ─────────────────────────────────────────────────────────────────
        // NUMER KSEF — drukujesz na fakturze
        // ─────────────────────────────────────────────────────────────────
        echo "📋 Numer KSeF:     {$ksef['ksefNumber']}\n";
        echo "📅 Data KSeF:      {$ksef['invoicingDatetime']}\n";
        
        // ─────────────────────────────────────────────────────────────────
        // UPO — Urzędowe Poświadczenie Odbioru
        // ─────────────────────────────────────────────────────────────────
        if (!empty($ksef['upoLink'])) {
            echo "\n📜 UPO (Urzędowe Poświadczenie Odbioru):\n";
            echo "   Link: {$ksef['upoLink']}\n";
            echo "   Hash: {$ksef['upoHash']}\n";
        }
        
        // ─────────────────────────────────────────────────────────────────
        // QR CODE — URL do weryfikacji
        // ─────────────────────────────────────────────────────────────────
        if (!empty($ksef['verificationUrl'])) {
            echo "\n📱 QR Code / URL weryfikacyjny:\n";
            echo "   {$ksef['verificationUrl']}\n";
        }
        
        break; // Sukces — wychodzimy z pętli
        
    } else {
        $status = $ksef['statusDescription'] ?? 'oczekiwanie...';
        echo "   Status: {$status}\n";
        
        if ($attempt < $maxAttempts) {
            sleep($delaySeconds);
        }
    }
}

if (empty($ksef['ksefNumber'])) {
    echo "\n⏳ Faktura wciąż w przetwarzaniu. Sprawdź później.\n";
}

Struktura sekcji KSEF w odpowiedzi

PHP
$invoice['data']['invoice']['ksef'] = [
    'toKsef'            => 1,                           // Flaga wysyłki
    'ksefNumber'        => '1234567890-20260204-...',   // ⭐ Numer w KSeF
    'processingCode'    => 200,                         // Kod przetwarzania
    'statusDescription' => 'Faktura przyjęta',          // Opis statusu
    'invoicingDatetime' => '2026-02-04 14:32:15',       // Data rejestracji
    'upoLink'           => 'https://ksef.../upo/...',   // 📜 Link do UPO
    'upoHash'           => 'a1b2c3d4...',               // Hash UPO
    'verificationUrl'   => 'https://ksef.../verify/...', // 📱 URL do QR
    'invoiceHash'       => 'x9y8z7w6...',               // Hash faktury
];
✅ Jak wygenerować QR code?

Użyj wartości verificationUrl jako danych wejściowych dla dowolnej biblioteki QR (np. phpqrcode, endroid/qr-code). Klient może zeskanować kod i zweryfikować fakturę w KSeF.

🧮 Faktury od netto vs od brutto

Parametr: calculationType

Faktury mogą być liczone od netto (podajesz cenę netto, system wylicza brutto) lub od brutto (podajesz cenę brutto, system wylicza netto).

📊 Tryb NETTO (domyślny)

Podajesz cenę netto → system wylicza VAT i brutto

Typowe dla: B2B, hurtownie, gdzie negocjujesz ceny netto

calculationType = 'netto'
priceNetto = 10000
💳 Tryb BRUTTO

Podajesz cenę brutto → system wylicza netto i VAT

Typowe dla: B2C, sklepy, gdzie cena na metce to brutto

calculationType = 'brutto'
priceBrutto = 12300

Przykład — faktura od brutto

PHP
<?php
// FAKTURA OD BRUTTO — ceny na metce
$invoiceHead = $udds->createInvoiceHead([
    'issuePlace'      => 'Warszawa',
    'issueDate'       => date('Y-m-d'),
    'saleDate'        => date('Y-m-d'),
    'documentType'    => 'FV',
    'template'        => 'VAT',
    'currency'        => 'PLN',
    'calculationType' => 'brutto',    // ← Wyliczanie od brutto
    'paymentMethod'   => 'karta',
    'buyerName'       => 'Jan Kowalski',
    // ... pozostałe dane
]);

// Przy dodawaniu pozycji używasz priceBrutto (nie priceNetto!)
$udds->createInvoiceItem([
    'idInvoice'   => $invoiceHead['id'],
    'name'        => 'Smartfon XYZ Pro',
    'quantity'    => 100,             // 1 szt
    'unit'        => 'szt.',
    'vatRate'     => '23',
    'priceBrutto' => 249900,          // 2499.00 PLN brutto
]);

// System wyliczy:
// - netto:  2031.71 PLN
// - VAT:     467.29 PLN
// - brutto: 2499.00 PLN

💱 Faktury w walutach obcych

Parametr: currency, exchangeRate

Możesz wystawiać faktury w EUR, USD i innych walutach. Kurs NBP pobiera się automatycznie (z dnia poprzedzającego datę sprzedaży), ale możesz podać własny.

Przykład — faktura w EUR

PHP
<?php
/**
 * FAKTURA W WALUCIE OBCEJ (EUR)
 * Kurs wymiany pobierany automatycznie z NBP.
 */

$invoiceHead = $udds->createInvoiceHead([
    'issuePlace'    => 'Warszawa',
    'issueDate'     => date('Y-m-d'),
    'saleDate'      => date('Y-m-d'),
    'documentType'  => 'FV',
    'template'      => 'VAT',
    
    // ═══════════════════════════════════════════════════════════════════
    // WALUTA
    // ═══════════════════════════════════════════════════════════════════
    'currency'      => 'EUR',           // Waluta dokumentu
    
    // Opcjonalnie: własny kurs (jeśli nie podasz — pobierze z NBP)
    // 'exchangeRate'     => 43250,     // 4.3250 PLN/EUR (×10000!)
    // 'exchangeRateDate' => date('Y-m-d', strtotime('-1 day')),
    
    'paymentMethod' => 'przelew',
    
    // Nabywca zagraniczny
    'buyerName'     => 'Acme GmbH',
    'buyerNip'      => 'DE123456789',   // NIP z prefiksem kraju
    'buyerAddress'  => 'Hauptstraße 42',
    'buyerPostCode' => '10115',
    'buyerCity'     => 'Berlin',
    'buyerCountry'  => 'Niemcy',
    
    'toKsef'        => 1,
]);

// Pozycje — ceny w eurocentach!
$udds->createInvoiceItem([
    'idInvoice'  => $invoiceHead['id'],
    'name'       => 'Consulting services',
    'quantity'   => 100,
    'unit'       => 'szt.',
    'vatRate'    => '0',                // Eksport usług — 0%
    'priceNetto' => 100000,             // 1000.00 EUR
]);
💱 Kurs wymiany ×10000

Jeśli podajesz własny exchangeRate, pamiętaj o mnożniku ×10000.

Przykład: 4.5632 PLN/EUR → exchangeRate = 45632

Dostępne waluty

KodNazwa
PLNPolski złoty (domyślna)
EUREuro
USDDolar amerykański
GBPFunt brytyjski
CHFFrank szwajcarski
CZKKorona czeska

🏷️ Faktury VAT-marża

Parametr: template = 'MARZA'

Szablon MARZA stosuje się przy sprzedaży towarów używanych, dzieł sztuki, antyków — gdzie VAT nalicza się tylko od marży, nie od całej wartości.

⚠️ Różnice VAT vs MARŻA

Na fakturze VAT-marża nie pokazuje się stawki VAT, kwoty VAT ani ceny netto. Klient widzi tylko kwotę brutto.

Przykład — faktura marża

PHP
<?php
/**
 * FAKTURA VAT-MARŻA
 * 
 * Stosowana przy:
 * - Sprzedaży towarów używanych (komisy samochodowe)
 * - Dzieł sztuki, przedmiotów kolekcjonerskich
 * - Usług turystycznych (marża biura podróży)
 */

$invoiceHead = $udds->createInvoiceHead([
    'issuePlace'    => 'Warszawa',
    'issueDate'     => date('Y-m-d'),
    'saleDate'      => date('Y-m-d'),
    'documentType'  => 'FV',
    
    // ═══════════════════════════════════════════════════════════════════
    // SZABLON MARŻA
    // ═══════════════════════════════════════════════════════════════════
    'template'      => 'MARZA',
    
    'currency'      => 'PLN',
    'paymentMethod' => 'gotówka',
    
    'buyerName'     => 'Jan Kowalski',
    'buyerAddress'  => 'ul. Prywatna 5',
    'buyerPostCode' => '00-001',
    'buyerCity'     => 'Warszawa',
    'buyerNip'      => '',               // Osoba prywatna — bez NIP
    
    'notes'         => 'Procedura VAT-marża - towary używane',
]);

// Pozycja — cena to kwota brutto
$udds->createInvoiceItem([
    'idInvoice'   => $invoiceHead['id'],
    'name'        => 'Samochód używany - VW Golf 2020',
    'quantity'    => 100,
    'unit'        => 'szt.',
    'vatRate'     => '23',               // VAT od marży (wewnętrznie)
    'priceBrutto' => 4500000,            // 45,000.00 PLN brutto
]);

💰 Faktury zaliczkowe

Parametr: advanceType

System obsługuje pełny cykl zaliczek:

  1. Faktura zaliczkowa — przy wpłacie zaliczki
  2. Faktura końcowa — rozliczenie zaliczki z pozostałą kwotą

Workflow zaliczkowy

1
Zamówienie
10,000 PLN
2
Wpłata zaliczki
3,000 PLN
3
FV Zaliczkowa
advanceType='Zaliczka'
4
FV Końcowa
Do zapłaty: 7,000

Przykład — cykl zaliczkowy

PHP
<?php
/**
 * CYKL ZALICZKOWY
 * 
 * Scenariusz:
 * - Klient zamawia usługę za 10,000 PLN netto
 * - Wpłaca zaliczkę 3,000 PLN netto
 * - Po wykonaniu usługi wystawiamy fakturę końcową na 7,000 PLN
 */

// ═══════════════════════════════════════════════════════════════════════════════
// KROK 1: FAKTURA ZALICZKOWA (przy wpłacie)
// ═══════════════════════════════════════════════════════════════════════════════

$zaliczka = $udds->createInvoiceHead([
    'issuePlace'    => 'Warszawa',
    'issueDate'     => date('Y-m-d'),
    'saleDate'      => date('Y-m-d'),
    'documentType'  => 'FV',
    'template'      => 'VAT',
    'currency'      => 'PLN',
    'paymentMethod' => 'przelew',
    
    'advanceType'   => 'Zaliczka',       // ← TYP ZALICZKI
    
    'buyerName'     => 'Klient Sp. z o.o.',
    'buyerNip'      => '5213456789',
    'buyerAddress'  => 'ul. Biznesowa 10',
    'buyerPostCode' => '00-001',
    'buyerCity'     => 'Warszawa',
]);

$udds->createInvoiceItem([
    'idInvoice'  => $zaliczka['id'],
    'name'       => 'Zaliczka na usługę programistyczną',
    'quantity'   => 100,
    'unit'       => 'szt.',
    'vatRate'    => '23',
    'priceNetto' => 300000,              // 3,000.00 PLN netto
]);

$udds->setInvoiceStatus(['idInvoice' => $zaliczka['id'], 'status' => 'AKTYWNA']);

echo "✅ Faktura zaliczkowa: {$zaliczka['number']}\n";

// ═══════════════════════════════════════════════════════════════════════════════
// KROK 2: FAKTURA KOŃCOWA (po wykonaniu usługi)
// ═══════════════════════════════════════════════════════════════════════════════

$koncowa = $udds->createInvoiceHead([
    'issuePlace'    => 'Warszawa',
    'issueDate'     => date('Y-m-d'),
    'saleDate'      => date('Y-m-d'),
    'documentType'  => 'FV',
    'template'      => 'VAT',
    'currency'      => 'PLN',
    'paymentMethod' => 'przelew',
    
    'advanceType'   => 'Rozliczenie zaliczki',   // ← ROZLICZENIE
    
    'buyerName'     => 'Klient Sp. z o.o.',
    'buyerNip'      => '5213456789',
    'buyerAddress'  => 'ul. Biznesowa 10',
    'buyerPostCode' => '00-001',
    'buyerCity'     => 'Warszawa',
]);

// Pozycja za pełną usługę
$udds->createInvoiceItem([
    'idInvoice'  => $koncowa['id'],
    'name'       => 'Usługa programistyczna - projekt XYZ',
    'quantity'   => 100,
    'unit'       => 'szt.',
    'vatRate'    => '23',
    'priceNetto' => 1000000,             // 10,000.00 PLN (pełna wartość)
]);

// Pozycja odliczająca zaliczkę (WARTOŚĆ UJEMNA!)
$udds->createInvoiceItem([
    'idInvoice'  => $koncowa['id'],
    'name'       => 'Rozliczenie zaliczki z FV ' . $zaliczka['number'],
    'quantity'   => 100,
    'unit'       => 'szt.',
    'vatRate'    => '23',
    'priceNetto' => -300000,             // -3,000.00 PLN (minus!)
]);

// Do zapłaty: 10,000 - 3,000 = 7,000 PLN netto
$udds->setInvoiceStatus(['idInvoice' => $koncowa['id'], 'status' => 'AKTYWNA']);

echo "✅ Faktura końcowa: {$koncowa['number']}\n";
echo "   Do zapłaty: 7,000.00 PLN netto + VAT\n";

✏️ Faktury korygujące

Parametr: documentType = 'FK'

Faktura korygująca (FK) poprawia błędy na fakturze pierwotnej lub zmienia jej wartość (np. rabat, zwrot towaru).

🔴 Wymagane pole

Dla documentType = 'FK' musisz podać relatedDocumentId — ID korygowanej faktury.

Przykład — korekta (zwrot towaru)

PHP
<?php
/**
 * FAKTURA KORYGUJĄCA
 * 
 * Scenariusz: Klient zwrócił 2 z 5 zakupionych produktów.
 * Wystawiamy korektę zmniejszającą ilość.
 */

// ID faktury, którą korygujemy
$originalInvoiceId = '00000004M1G221031126JF1B44ASNQ6D';

// Pobierz dane oryginalnej faktury
$original = $udds->getInvoice(['idInvoice' => $originalInvoiceId]);

// ═══════════════════════════════════════════════════════════════════════════════
// TWORZENIE KOREKTY
// ═══════════════════════════════════════════════════════════════════════════════

$korekta = $udds->createInvoiceHead([
    'issuePlace'    => 'Warszawa',
    'issueDate'     => date('Y-m-d'),
    'saleDate'      => date('Y-m-d'),
    
    // ═══════════════════════════════════════════════════════════════════
    // TYP: FAKTURA KORYGUJĄCA
    // ═══════════════════════════════════════════════════════════════════
    'documentType'      => 'FK',
    'relatedDocumentId' => $originalInvoiceId,  // ← WYMAGANE!
    
    'template'      => 'VAT',
    'currency'      => 'PLN',
    'paymentMethod' => 'przelew',
    
    // Dane nabywcy — takie same jak na oryginale
    'buyerName'     => $original['data']['invoice']['buyer']['name'],
    'buyerNip'      => $original['data']['invoice']['buyer']['nip'],
    'buyerAddress'  => $original['data']['invoice']['buyer']['address'],
    'buyerPostCode' => $original['data']['invoice']['buyer']['postCode'],
    'buyerCity'     => $original['data']['invoice']['buyer']['city'],
    
    'notes'         => 'Korekta - zwrot towaru',
]);

// ═══════════════════════════════════════════════════════════════════════════════
// POZYCJE KOREKTY (wartości ujemne = zmniejszenie)
// ═══════════════════════════════════════════════════════════════════════════════

// Zwrot 2 sztuk produktu po 500 PLN netto
$udds->createInvoiceItem([
    'idInvoice'  => $korekta['id'],
    'name'       => 'Zwrot: Produkt XYZ (2 szt.)',
    'quantity'   => -200,               // -2.00 szt. (ujemna ilość!)
    'unit'       => 'szt.',
    'vatRate'    => '23',
    'priceNetto' => 50000,              // 500.00 PLN/szt. (cena dodatnia)
]);

// Wartość pozycji: -2 × 500 = -1000 PLN netto
$udds->setInvoiceStatus(['idInvoice' => $korekta['id'], 'status' => 'AKTYWNA']);

echo "✅ Korekta: {$korekta['number']}\n";
echo "   Kwota korekty: -1,000.00 PLN netto\n";

📖 Słowniki wartości

Dozwolone wartości parametrów

API korzysta ze słowników wartości. Podanie nieprawidłowej wartości zwróci błąd 1002.

Typy dokumentów (documentType)

KodNazwaUwagi
FVFaktura VATStandardowa faktura sprzedaży
FKFaktura KorygującaWymaga relatedDocumentId
PROPro-formaNie jest dokumentem księgowym

Szablony (template)

KodOpis
VATStandardowa faktura VAT — pokazuje netto/brutto/VAT
MARZAVAT-marża — tylko kwota brutto, bez rozbicia VAT

Stawki VAT (vatRate)

KodOpis
23Stawka podstawowa 23%
8Stawka obniżona 8%
5Stawka obniżona 5%
0Stawka 0% (np. eksport)
zwZwolniony z VAT
npNie podlega VAT
ooOdwrotne obciążenie

Jednostki miary (unit)

KodOpis
szt.Sztuka
kgKilogram
mbMetr bieżący
m2Metr kwadratowy
m3Metr sześcienny
godz.Godzina
mies.Miesiąc
kpl.Komplet
usł.Usługa
lLitr

Metody płatności (paymentMethod)

KodOpis
przelewPrzelew bankowy
gotówkaGotówka
kartaKarta płatnicza
za pobraniemPłatność przy odbiorze
kompensataKompensata należności

Typ zaliczki (advanceType)

KodOpis
(pusty)Zwykła faktura (nie zaliczkowa)
ZaliczkaFaktura zaliczkowa
Rozliczenie zaliczkiFaktura końcowa rozliczająca

⚠️ Kody błędów

Referencja błędów API

createInvoiceHead

KodOpis
1001Brakuje wymaganego pola
1002Nieprawidłowa wartość słownikowa
1003Nieprawidłowy format daty (wymagany: YYYY-MM-DD)
1004Nie znaleziono konta bankowego firmy
1005Błąd sekwencji numeru faktury
1006Nie znaleziono dokumentu źródłowego (dla FK)
1007Błąd bazy danych

createInvoiceItem

KodOpis
1001Brakuje wymaganego pola
1002Nieprawidłowa wartość słownikowa (unit, vatRate)
1003Faktura nie istnieje
1004Faktura już wysłana do KSeF — nie można modyfikować
1005Tryb manualny wymaga wszystkich pól wartości
1006Błąd bazy danych

sendInvoiceToKsef

KodOpis
1001Brak parametru idInvoice
1002Faktura nie istnieje
1003Brak konfiguracji KSeF Service
1004Błąd połączenia z KSeF Service
1005Błąd odpowiedzi KSeF Service

KSeF Service (w ksefResponse.message)

KodOpis
REST-001Unauthorized — nieprawidłowy API key
REST-003KSeF nie jest zainicjowany
REST-011Brak rekordu w KSEF_FAKTURY
REST-014Wyjątek systemowy

💡 Kompletny przykład

Cały proces w jednym skrypcie

Poniższy skrypt pokazuje pełny workflow: od utworzenia faktury do pobrania numeru KSeF. 📥 Pobierz plik PHP

complete-flow.php
<?php
/**
 * ══════════════════════════════════════════════════════════════════════════════
 * EASY INVOICE - KOMPLETNY PRZYKŁAD
 * ══════════════════════════════════════════════════════════════════════════════
 * 
 * Pełny workflow wystawiania faktury:
 * 1. Utworzenie nagłówka
 * 2. Dodanie pozycji
 * 3. Aktywacja
 * 4. Wysyłka do KSeF
 * 5. Pobranie UPO i QR code
 */

require_once('classUddsOx.php');

// ══════════════════════════════════════════════════════════════════════════════
// KONFIGURACJA
// ══════════════════════════════════════════════════════════════════════════════

$udds = new classUddsOx();
$udds->customerCode = 'TWOJ_KOD';
$udds->login        = 'login';
$udds->password     = 'haslo';
$udds->serverUrl    = 'https://api.dlaoperatora.pl/udds/';
$udds->timeout      = 30;

echo "══════════════════════════════════════════════════════════════════\n";
echo " EASY INVOICE - Tworzenie faktury z wysyłką do KSeF\n";
echo "══════════════════════════════════════════════════════════════════\n\n";

// ══════════════════════════════════════════════════════════════════════════════
// KROK 1: UTWÓRZ NAGŁÓWEK FAKTURY
// ══════════════════════════════════════════════════════════════════════════════

echo "📄 KROK 1: Tworzenie nagłówka faktury...\n";

$invoiceHead = $udds->createInvoiceHead([
    'issuePlace'    => 'Warszawa',
    'issueDate'     => date('Y-m-d'),
    'saleDate'      => date('Y-m-d'),
    'paymentMethod' => 'przelew',
    'paymentDueDate'=> date('Y-m-d', strtotime('+14 days')),
    'documentType'  => 'FV',
    'template'      => 'VAT',
    'currency'      => 'PLN',
    'buyerName'     => 'Przykładowa Firma Sp. z o.o.',
    'buyerNip'      => '5213456789',
    'buyerAddress'  => 'ul. Testowa 123',
    'buyerPostCode' => '00-001',
    'buyerCity'     => 'Warszawa',
    'buyerCountry'  => 'Polska',
    'toKsef'        => 1,
]);

if ($invoiceHead['status'] !== 'OK') {
    die("❌ Błąd: " . $invoiceHead['errors'][0]['message'] . "\n");
}

$idInvoice = $invoiceHead['id'];
$invoiceNumber = $invoiceHead['number'];

echo "   ✅ Utworzono: {$invoiceNumber}\n";
echo "   ID: {$idInvoice}\n\n";

// ══════════════════════════════════════════════════════════════════════════════
// KROK 2: DODAJ POZYCJE
// ══════════════════════════════════════════════════════════════════════════════

echo "📦 KROK 2: Dodawanie pozycji...\n";

$items = [
    ['name' => 'Usługa programistyczna',  'qty' => 100, 'unit' => 'szt.', 'vat' => '23', 'price' => 500000],
    ['name' => 'Licencja oprogramowania', 'qty' => 100, 'unit' => 'szt.', 'vat' => '23', 'price' => 120000],
    ['name' => 'Wsparcie techniczne',     'qty' => 100, 'unit' => 'mies.','vat' => '23', 'price' => 50000],
];

$totalNetto = 0;
foreach ($items as $item) {
    $result = $udds->createInvoiceItem([
        'idInvoice'  => $idInvoice,
        'name'       => $item['name'],
        'quantity'   => $item['qty'],
        'unit'       => $item['unit'],
        'vatRate'    => $item['vat'],
        'priceNetto' => $item['price'],
    ]);
    
    if ($result['status'] === 'OK') {
        $netto = $result['calculatedValues']['valueNetto'] / 100;
        $totalNetto += $netto;
        echo "   ✅ {$item['name']}: " . number_format($netto, 2) . " PLN\n";
    }
}

echo "   ────────────────────────────────\n";
echo "   RAZEM NETTO: " . number_format($totalNetto, 2) . " PLN\n\n";

// ══════════════════════════════════════════════════════════════════════════════
// KROK 3: AKTYWUJ FAKTURĘ
// ══════════════════════════════════════════════════════════════════════════════

echo "✅ KROK 3: Aktywacja faktury...\n";

$activate = $udds->setInvoiceStatus([
    'idInvoice' => $idInvoice,
    'status'    => 'AKTYWNA',
]);

if ($activate['status'] === 'OK') {
    echo "   ✅ Status: AKTYWNA\n\n";
} else {
    die("❌ Błąd aktywacji\n");
}

// ══════════════════════════════════════════════════════════════════════════════
// KROK 4: WYŚLIJ DO KSEF
// ══════════════════════════════════════════════════════════════════════════════

echo "📤 KROK 4: Wysyłka do KSeF...\n";

$send = $udds->sendInvoiceToKsef(['idInvoice' => $idInvoice]);

if ($send['status'] === 'OK') {
    echo "   ✅ Wysłano do KSeF\n\n";
} else {
    echo "   ⚠️ Błąd: {$send['message']}\n\n";
}

// ══════════════════════════════════════════════════════════════════════════════
// KROK 5: POBIERZ STATUS, UPO, QR CODE
// ══════════════════════════════════════════════════════════════════════════════

echo "🔍 KROK 5: Weryfikacja statusu KSeF...\n";

sleep(2);

for ($i = 1; $i <= 5; $i++) {
    $invoice = $udds->getInvoice(['idInvoice' => $idInvoice]);
    $ksef = $invoice['data']['invoice']['ksef'] ?? [];
    
    if (!empty($ksef['ksefNumber'])) {
        echo "\n══════════════════════════════════════════════════════════════════\n";
        echo " ✅ FAKTURA ZAREJESTROWANA W KSEF!\n";
        echo "══════════════════════════════════════════════════════════════════\n\n";
        
        echo "📋 Numer faktury:  {$invoiceNumber}\n";
        echo "📋 Numer KSeF:     {$ksef['ksefNumber']}\n";
        echo "📅 Data KSeF:      {$ksef['invoicingDatetime']}\n";
        
        if (!empty($ksef['upoLink'])) {
            echo "\n📜 UPO: {$ksef['upoLink']}\n";
        }
        
        if (!empty($ksef['verificationUrl'])) {
            echo "📱 QR:  {$ksef['verificationUrl']}\n";
        }
        
        echo "\n══════════════════════════════════════════════════════════════════\n";
        exit(0);
    }
    
    echo "   Próba {$i}/5 - oczekiwanie...\n";
    sleep(3);
}

echo "\n⏳ Faktura w przetwarzaniu. Sprawdź status później.\n";
echo "   ID: {$idInvoice}\n";