Integracja · LMS × KSeF 2.0

Instrukcja
integracji z KSeF

Krok po kroku — od certyfikatu do pierwszej faktury w Krajowym Systemie e-Faktur

Wersja LMS28-git (2026)
KSeF2.0 produkcja
Obowiązkowy od01.04.2026
Autorcocoban78

Słowo wstępne

Cześć! Na samym wstępie muszę się przyznać — jestem wieloletnim i oddanym fanem systemu LMS :D Poniższy projekt powstawał momentami w bólach, prawdopodobnie bywa pełen chaosu i z pewnością nie wygra konkursu na najlepiej udokumentowany kod w historii. Początkowo tworzyłem to wyłącznie na swoje własne, absolutnie prywatne potrzeby, żeby jakoś okiełznać nadchodzącego potwora zwanego KSeF. Nie ukrywam! W dużej mierze pomogła tutaj sztuczna inteligencja...

Jednak, ulegając presji tłumu i na prośbę wielu z Was, postanowiłem podzielić się tym dziełem zupełnie nieodpłatnie. Musicie jednak wiedzieć o jednym: nie działam w porozumieniu z autorami ani programistami oficjalnego LMS-a. Oznacza to, że wszelkie ich poprawki i aktualizacje mogą znacząco wpłynąć na (nie)działanie niniejszej integracji. Mój projekt nie jest oficjalną częścią systemu. Oczywiście bardzo się starałem, by całość była jak najbardziej spójna i niczego nie zepsuła, ale korzystacie z tego na własną odpowiedzialność! Oczywiście udostępniony kod może być także inspiracją dla innych, śmiało korzystacjie! Zachęcam również do dzielenia się :P

Szukasz rozwiązania w pełni profesjonalnego? Jeśli nie lubisz dreszczyku emocji i zależy Ci na stabilnym, w 100% profesjonalnie wspieranym zintegrowaniu KSeF z systemem LMS, z czystym sumieniem polecam zapoznanie się z ofertą LMS+ (https://lms.plus/).

Wymagania

SkładnikMinimalna wersjaSprawdź
LMS28-git, migracje ≥ 2026033100SELECT keyvalue FROM dbinfo WHERE keytype='dbversion'
PHP8.1+php -v
Composerdowolnacomposer --version
Biblioteka KSeFn1ebieski/ksef-php-clientls vendor/n1ebieski/
Certyfikat KSeFPlik .p12 tworzony z plików .crt, .key oraz hasła wygenerowanych w portalu KSeF
ext-zipwymagane przez PHPphp -m | grep zip

Jeśli brak biblioteki ksef-php-client:

cd /sciezka/do/lms
composer require n1ebieski/ksef-php-client

Krok 1 Certyfikat PKCS12

Aby uwierzytelnić się w systemie KSeF, potrzebujesz certyfikatu. W portalu KSeF Ministerstwa Finansów (Aplikacja Podatnika → Certyfikaty) generujesz parę plików o rozszerzeniach .crt oraz .key, a także podajesz dla nich hasło, które musisz zapamiętać i wpisać do pliku lms.ini. Z tych danych należy wygenerować docelowy plik .p12 (PKCS#12) przy użyciu poniższego polecenia:

# Połącz .crt i .key w plik .p12
openssl pkcs12 -export \
  -in certyfikat.crt \
  -inkey certyfikat.key \
  -out /etc/lms/ksef_cert.p12 \
  -passout pass:TwojeHaslo123!

# Zabezpiecz plik
chmod 640 /etc/lms/ksef_cert.p12
chown root:apache /etc/lms/ksef_cert.p12
⚠ Bezpieczeństwo Plik .p12 i hasło do niego to dostęp do wystawiania faktur w Twoim imieniu. Upewnij się że plik nie jest dostępny publicznie ani dla innych użytkowników serwera.

Krok 2 Konfiguracja lms.ini

Edytuj plik /etc/lms/lms.ini i dodaj / uzupełnij poniższe sekcje:

[phpui]
plugins = KSeFSubmit           # dodaj do istniejącej listy pluginów

[ksef]
environment    = prod          # prod / test / demo
auth_method    = certificate   # certificate (zalecane) lub token
certificate    = /etc/lms/ksef_cert.p12
password       = "TwojeHaslo123!"
boundary_date  = 2026/04/01    # data od której wysyłamy faktury
encryption_key = Wpisz32ZnakowyLosowyKluczAES256!
encryption_iv  = 16ZnakowIV1234
show_rozliczenie = 0           # <- tożsame z brakiem wpisu
debug_log      = /var/log/lms-ksef-gui.log

[invoices]
issuer            = 'Dział Obsługi Klienta'
info_box_text     = 'Przelew 14 dni\nKonto: XX XXXX XXXX XXXX XXXX XXXX XXXX'
info_box_text_b2c = 'Dziękujemy za korzystanie z naszych usług'

[sendinvoices]            # faktury B2B (firmy)
sender_email   = biuro@twojafirma.pl
sender_name    = Twoja Firma Sp. z o.o.
mail_subject   = Faktura VAT %invoice
mail_body      = /etc/lms/mail_body_b2b.txt
mail_format    = html
invoice_filename = Faktura_%number

[sendinvoices-b2c]        # faktury B2C (osoby fizyczne)
sender_email   = biuro@twojafirma.pl
sender_name    = Twoja Firma Sp. z o.o.
mail_subject   = Faktura %invoice
mail_body      = /etc/lms/mail_body_b2c.txt
mail_format    = html
invoice_filename = Faktura_%number

Generowanie kluczy szyfrowania

# encryption_key — 32 znaki losowe
openssl rand -base64 24 | tr -d '=+/' | head -c 32

# encryption_iv — 16 znaków losowych
openssl rand -base64 12 | tr -d '=+/' | head -c 16
⚠ Ustawienie show_rozliczenie Jeśli nie chcesz, aby do faktury w KSeF szła informacja dodatkowa w sekcji Rozliczenie → Obciążenia → Powód obciążenia (dotychczasowa niedopłata), ustaw ten wpis na 0 lub nie dodawaj go wcale. Jeśli chcesz go mieć, ustaw wartość na 1.

Informacja dodatowa: Jeśli faktura wystawiła się wcześniej niż zostały zaksięgowane wcześniejsze płatności, to taka informacja zaburza sens na fakturze w KSeF. W tym przypadku zaznaczenie/odznaczenie opcji "Saldo na fakturze" w edycji klienta nie ma tu wpływu na generowanie dokumentu XML i wysyłkę do KSeF.

Krok 3 Wgranie plików

Rozpakuj paczkę i skopiuj pliki do katalogu LMS:

lms/
├── lib/KSeF/
│ └── KSeF.php ← zastępuje oryginał
├── lib/LMSDocuments/
│ └── LMSTcpdfInvoice.php ← szablon PDF faktury
├── plugins/KSeFSubmit/ ← NOWY katalog
│ ├── KSeFSubmit.php
│ ├── handlers/
│ │ └── KSeFSubmitHandler.php
│ └── lib/
│ └── KSeFApiService.php
└── bin/
├── lms-ksef.php ← batch wysyłka (cron)
├── lms-ksef-download.php ← pobieranie faktur zakupowych
├── lms-ksef-sync.php ← jednorazowy sync historyczny
└── lms-ksef-bulk-correct.php
# Uruchom z katalogu z rozpakowaną paczką
PACK=/tmp/lms-ksef-pack
LMS=/sciezka/do/lms

cp $PACK/lib/KSeF/KSeF.php                        $LMS/lib/KSeF/KSeF.php
cp $PACK/lib/LMSDocuments/LMSTcpdfInvoice.php     $LMS/lib/LMSDocuments/LMSTcpdfInvoice.php

mkdir -p $LMS/plugins/KSeFSubmit/handlers $LMS/plugins/KSeFSubmit/lib
cp $PACK/plugins/KSeFSubmit/KSeFSubmit.php                 $LMS/plugins/KSeFSubmit/
cp $PACK/plugins/KSeFSubmit/handlers/KSeFSubmitHandler.php $LMS/plugins/KSeFSubmit/handlers/
cp $PACK/plugins/KSeFSubmit/lib/KSeFApiService.php         $LMS/plugins/KSeFSubmit/lib/

cp $PACK/bin/lms-ksef.php              $LMS/bin/
cp $PACK/bin/lms-ksef-download.php     $LMS/bin/
cp $PACK/bin/lms-ksef-sync.php         $LMS/bin/
cp $PACK/bin/lms-ksef-bulk-correct.php $LMS/bin/
chmod +x $LMS/bin/lms-ksef*.php

KSeF.php — 2 poprawki względem oryginału

Poprawka 1 — setlocale(LC_NUMERIC, 'C')

Lokalizacja: getInvoiceXml(), pierwsza linia funkcji

Problem: lib/locale/Localisation.php (linia ~246) ustawia LC_NUMERIC na pl_PL.UTF-8 podczas inicjalizacji LMS. Skutek: sprintf('%.2f', 35.5) zwraca 35,50. KSeF odrzuca XML z przecinkami w kwotach (walidacja XSD).

public function getInvoiceXml(array $invoice)
{
    setlocale(LC_NUMERIC, 'C');    // poprawka 1
    if (!isset($this->divisions[$invoice['divisionid']])) {

Poprawka 2 — show_rozliczenie przez lms.ini

Lokalizacja: getInvoiceXml(), sekcja <Rozliczenie> (~linia 1001)

Problem: Oryginał pobiera getCustomerBalance() i wpisuje "dotychczasową niedopłatę" do XML.

Rozwiązanie: Sterowanie przez lms.ini:

[ksef]
; show_rozliczenie = 0   ← domyślnie gdy brak klucza = wyłączone
; show_rozliczenie = 1   ← oryginalne zachowanie z saldem

Gdy show_rozliczenie = 0 lub brak klucza — sekcja w XML zawiera tylko bieżącej kwotę faktury:

<Rozliczenie>
    <Obciazenia>
        <Kwota>2595.30</Kwota>
        <Powod>dokument LMS_002/04/2026</Powod>
    </Obciazenia>
    <SumaObciazen>2595.30</SumaObciazen>
    <SumaOdliczen>0.00</SumaOdliczen>    <!-- wymagane przez XSD -->
    <DoZaplaty>2595.30</DoZaplaty>
</Rozliczenie>

Krok 4 Aktualizacja bazy danych i Autoload

Przejdź do katalogu głównego LMS, odśwież mapę klas Composera, a następnie wykonaj migracje bazy danych:

# Przeładuj klasy (wymagane po dodaniu nowych plików wtyczki)
composer dump-autoload

# Zaktualizuj bazę i wyczyść cache szablonów
php bin/lms-upgradedb.php -C /etc/lms/lms.ini
rm -rf templates_c/*
✓ Migracje Skrypt upgradedb wykona automatycznie wszystkie brakujące migracje, w tym tabele ksefinvoices, ksefdocuments, ksefbatchsessions i powiązane.

Jeśli wszystkie kroki przebiegły prawidłowo, w systemie LMS powinna pojawić się nowa wtyczka w Konfigruacja->Wtyczki, którą należy włączyć, jak na poniższym obrazku:

Zrzut ekranu LMS z zakładką KSeF

Krok 5 Sync historycznych faktur zakupowych

Pierwsze uruchomienie — pobierz wszystkie faktury które wystawcy przesłali do KSeF na Twój NIP. Skrypt używa Export API (jeden zaszyfrowany ZIP zamiast setek osobnych zapytań):

cd /sciezka/do/lms/bin

# Najpierw dry-run — sprawdź ile faktur zostanie pobranych
./lms-ksef-sync.php -C /etc/lms/lms.ini --from=2026/01/01 --dry-run

# Właściwy sync (zakres maksymalnie 3 miesiące, podziel jeśli większy)
./lms-ksef-sync.php -C /etc/lms/lms.ini --from=2026/01/01 --to=2026/03/31
./lms-ksef-sync.php -C /etc/lms/lms.ini --from=2026/04/01
ℹ Skrypt czeka kilkanaście sekund MF przetwarza eksport asynchronicznie — skrypt odpytuje co 15 sekund aż eksport będzie gotowy, potem pobiera i rozpakowuje zaszyfrowany ZIP. Przy kilkudziesięciu fakturach trwa ok. 30 sekund.

Wysyłka historycznych faktur sprzedaży do KSeF

# Dry-run — sprawdź ile faktur do wysłania
./lms-ksef.php -C /etc/lms/lms.ini --dry-run

# Właściwa wysyłka
./lms-ksef.php -C /etc/lms/lms.ini

Krok 6 Konfiguracja Cron

# crontab -e

# Wysyłka faktur sprzedaży — 1. dnia każdego miesiąca
 0  2 1 * *  /sciezka/lms/bin/lms-payments.php -C /etc/lms/lms.ini
10  2 1 * *  php /sciezka/lms/bin/lms-ksef.php -C /etc/lms/lms.ini >> /var/log/lms-ksef.log 2>&1
30  2 1 * *  /sciezka/lms/bin/lms-sendinvoices.php -C /etc/lms/lms.ini --ksef
35  2 1 * *  /sciezka/lms/bin/lms-sendinvoices.php -C /etc/lms/lms.ini --without-ksef --section=sendinvoices-b2c

# Sprawdzanie statusu sesji batch (co godzinę)
 0  * * * *  php /sciezka/lms/bin/lms-ksef.php -C /etc/lms/lms.ini --status-only >> /var/log/lms-ksef.log 2>&1

# Pobieranie faktur zakupowych z KSeF (codziennie o 6:15)
15  6 * * *  php /sciezka/lms/bin/lms-ksef-download.php -C /etc/lms/lms.ini -q >> /var/log/lms-ksef-download.log 2>&1
lms-ksef.phpWysyła faktury sprzedaży do KSeF w paczce batch1. dnia miesiąca
lms-ksef.php --status-onlyPobiera numery KSeF dla wysłanych fakturco godzinę
lms-sendinvoices.php --ksefWysyła e-mail z PDF do klientów B2B (po otrzymaniu numeru KSeF)1. dnia miesiąca
lms-sendinvoices.php --without-ksefWysyła e-mail z PDF do klientów B2C (osoby fizyczne)1. dnia miesiąca
lms-ksef-download.phpPobiera nowe faktury zakupowe od dostawcówcodziennie

Szablony e-mail

/etc/lms/mail_body_b2b.txt — dla firm (B2B)

Szanowni Państwo,<br>
<br>
w załączniku faktura VAT <b>%invoice</b>.<br>
Numer KSeF: <b>%ksef-number</b><br>
Kwota do zapłaty: <b>%value PLN</b><br>
Numer konta: %bankaccount<br>
<br>
Z poważaniem

/etc/lms/mail_body_b2c.txt — dla osób fizycznych (B2C)

Szanowny Kliencie,<br>
<br>
w załączniku faktura <b>%invoice</b>.<br>
Kwota do zapłaty: <b>%value PLN</b><br>
Numer konta: %bankaccount<br>
<br>
Dziękujemy!
ZmiennaZnaczenie
%invoiceNumer faktury (np. LMS_001/04/2026)
%ksef-numberNumer KSeF (po potwierdzeniu przez MF)
%valueKwota brutto
%bankaccountNumer konta bankowego
%customeridID klienta w LMS
%pinPIN klienta

Test generowania XML

Przed wysyłką do KSeF warto sprawdzić czy XML faktury wygląda poprawnie. Skrypt gen-xml.php generuje XML bez wysyłki:

cd /sciezka/do/lms
php bin/gen-xml.php -C /etc/lms/lms.ini ID_FAKTURY

# XML zapisywany jest do /tmp/ID_FAKTURY.xml
cat /tmp/ID_FAKTURY.xml

Sprawdź w wygenerowanym XML:

Procedury awaryjne

SytuacjaRozwiązanie
Faktury wysłane, brak numerów KSeF (status 100) Poczekaj — skrypt --status-only w cronie odpytuje co godzinę. Można też uruchomić ręcznie: ./lms-ksef.php --status-only
Faktury z błędem (status 400) ./lms-ksef.php --retry-errors
Błąd 429 Too Many Requests przy pobieraniu MF ma limit ~64 zapytań/h. Odczekaj godzinę. Do historycznego sync używaj lms-ksef-sync.php zamiast lms-ksef-download.php
Błędna kwota w KSeF (niedopłata) Upewnij się że w lms.ini NIE MA wpisu show_rozliczenie = 1. Wystaw korekty: ./lms-ksef-bulk-correct.php --dry-run
Awaria MF (kilka dni) Faktury są w kolejce — skrypt automatycznie nadrobi po powrocie MF
Błąd certyfikatu openssl pkcs12 -in /etc/lms/ksef_cert.p12 -info — sprawdź czy certyfikat jest ważny i hasło się zgadza

Pobieranie plików

Pobierz wymagane paczki do prawidłowej instalacji i integracji:

Informacja: Załączony zip z LMS-MASTER z GIT na dzień 03.04.2026 23:50. Wszystkie pliki z paczki KSEF_PACK.ZIP działają stabilnie z tą wersją. W miarę możliwości postaram się uaktualniać pliki, jeśli będą się pojawiały znaczące zmiany w repozytorium GITHUB (https://github.com/chilek/lms). Plik który wymaga drobnych zmian do poprawnego działania skryptów integrujących oraz pluginu, to lib/KSeF/KSeF.php.


Weryfikacja po instalacji i po każdej aktualizacji LMS Uruchom skrypt weryfikujący czy wszystkie poprawki są na miejscu:
cd /sciezka/do/lms && bash check-netlink-patches.sh
⚠ Ważne: Kopia zapasowa Przed każdą instalacją integracji LMS z KSeF zaleca się zrobienie kopii zapasowej bieżącej instalacji oraz bazy danych.

Uwagi lub zapytania dotyczące działania integracji można zgłaszać na adres: cocoban78@gmail.com.