Search by

beziworld / laravel-debt-registers

kamcw

Laravel client for Polish debt registers: KRD (Krajowy Rejestr Długów) debtor checks over Chase 3.3 and debt submissions over Siddin/Nicci 3.2, with drivers and multiple accounts.

Package info

github.com/Beziworld/laravel-debt-registers

Homepage

Issues

pkg:composer/beziworld/laravel-debt-registers

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-11-04 19:27 UTC

This package is auto-updated.

Last update: 2026-11-04 19:27:35 UTC


README

tests Latest Version License

Laravel client for Polish debt registers (biura informacji gospodarczej). Check a person by PESEL or a company by NIP and report unpaid debts, with one driver per register and any number of accounts:

  • KRD (Krajowy Rejestr Długów BIG S.A.), the register with publicly documented APIs:
    • debtor checks over the Chase 3.3 SOAP service: consumers by PESEL, companies and sole traders by NIP, foreign numbers, incognito checks for entitled entities, report re-download by number
    • typed results: unpaid obligations with creditor, debtor, amounts, dates, proceedings and objections; paid obligations (positive information); summaries; the API password expiry reported by KRD
    • adding, updating and removing debts over the Siddin 2.1 service with Nicci 3.2 batches: a typed batch builder (validated against the official XSD during development), chunked upload, job status, processing report with the KRD identifiers of every case and obligation
  • DebtRegisters::driver('krd'), named connections per account, runtime credentials with using(), custom drivers with extend()
  • PESEL and NIP checksum validation before any request, a DebtorChecked event for audit trails
  • no auto-retry of anything that reached the register (checks are billed, submissions change data)
  • demo environment by default, so a fresh install never runs billed production checks

Polska wersja dokumentacji

Which registers have a public API?

Register Public API documentation Driver
KRD (Krajowy Rejestr Długów) Yes: info.krd.pl, Programista (Chase, Nicci/Siddin, Rastin, Solvig, Amnell, Wexler), public WSDL and a free demo environment krd
BIG InfoMonitor Message schemas only (big.pl/dokumentacja/api); the endpoint, authentication and test environment are given to subscribers not included
ERIF BIG No public API documentation not included

The architecture is driver-based, so a BIG InfoMonitor or ERIF driver can be registered with extend() by anyone who has the subscriber documentation.

Why this package

Packagist has no Laravel package for KRD. The existing PHP packages are solvenfinance/krdapi (2017, Zend Framework 2, PHP 5.6/7, last release 0.0.2) and goosfraba/krd-rastin-sdk (the Rastin PESEL and ID card verification service only). This package covers the current Chase 3.3 check service and the Siddin/Nicci 3.2 submission service, works without ext-soap, returns typed results and keeps every call fakeable with Http::fake().

Installation

composer require beziworld/laravel-debt-registers
php artisan vendor:publish --tag=debt-registers-config   # optional
KRD_ENVIRONMENT=demo          # demo (demo.krd.pl) or production (services.krd.pl, billed)
KRD_LOGIN=your-api-login
KRD_PASSWORD=your-api-password
KRD_AUTH=password_hash        # password, password_hash, certificate, certificate_with_credentials
KRD_CERTIFICATE=              # PEM client certificate for certificate logins
KRD_CERTIFICATE_PASSWORD=
KRD_SSL_KEY=
KRD_CHUNK_SIZE=512000         # characters per Siddin upload chunk
DEBT_REGISTERS_TIMEOUT=60
DEBT_REGISTERS_RETRIES=0      # extra attempts only after a connection failure with no response

A free demo account is available on request from KRD (info.krd.pl, demo environment). In the demo, reports contain only the obligations added by the same account. API access in production requires an agreement with KRD.

Checking a company or a person

use Beziworld\DebtRegisters\Facades\DebtRegisters;

$result = DebtRegisters::driver('krd')->checkCompany('526-025-09-95');   // separators and "PL" are accepted

$result->reportNumber;                // "U1/0000123456/2026"
$result->createdAt;                   // CarbonImmutable, Europe/Warsaw
$result->hasNegativeInformation();    // true when unpaid obligations are listed
$result->totalArrears();              // Money: amount "4520.11" (decimal string), currency "PLN"
$result->summary->creditorsCount;     // 2

foreach ($result->obligations as $obligation) {
    $obligation->title;               // "FV/0001/2026"
    $obligation->type;                // "Invoice"
    $obligation->arrears;             // Money
    $obligation->paymentDate;         // CarbonImmutable
    $obligation->disputed;            // true when the debtor raised objections
    $obligation->creditor->name;      // "Hurtownia Alfa S.A."
    $obligation->creditor->nip;
}

foreach ($result->paidObligations as $paid) {
    $paid->paidDebt;                  // Money
    $paid->paidDate;
}

Consumers are checked by PESEL with the date of their authorisation (Polish law requires the consumer's consent before the disclosure):

$result = DebtRegisters::checkConsumer('44051401359', consentGivenAt: $application->consent_at);

Every check in production is billed and appears in the subject's query register. Use the demo environment for development, and never re-run a check just to read the same data: download the report again by its number.

$krd = DebtRegisters::krd();

$krd->report('U1/0000123456/2026');                 // GetDisclosureReport, not a new disclosure
$krd->checkCompanyByOtherNumber('DE123456789');     // foreign company number
$krd->checkConsumerByOtherNumber('X1234567', now());
$krd->incognito()->checkCompany('5260250995');      // art. 25 entities only: no trace in the query register

$krd->lastSession()?->passwordExpiresAt;            // warn before the API password expires
$krd->lastSession()?->shouldChangePassword;

DebtorChecked (register, connection, result, incognito) is dispatched after every successful check, which is a convenient place to keep the audit trail of disclosed information.

Several accounts

// config/debt-registers.php
'connections' => [
    'krd' => ['driver' => 'krd', 'login' => env('KRD_LOGIN'), 'password' => env('KRD_PASSWORD')],
    'krd_branch' => ['driver' => 'krd', 'environment' => 'production', 'login' => env('KRD_BRANCH_LOGIN'), 'password' => env('KRD_BRANCH_PASSWORD')],
],
DebtRegisters::driver('krd_branch')->checkCompany($nip);

// Credentials stored per tenant
DebtRegisters::using(['login' => $tenant->krd_login, 'password' => $tenant->krd_password, 'environment' => 'production'])
    ->checkCompany($nip);

Options outside connections (timeout, retries) are shared by all connections. DebtRegister (the contract) and KrdDriver can be injected; they resolve to the default connection.

Reporting debts to KRD (Nicci 3.2)

Debts are sent as batches processed asynchronously by KRD. KRD asks for few large batches instead of many small ones.

use Beziworld\DebtRegisters\Enums\ObligationType;
use Beziworld\DebtRegisters\Nicci\Address;
use Beziworld\DebtRegisters\Nicci\Debtor;
use Beziworld\DebtRegisters\Nicci\NicciDocument;
use Beziworld\DebtRegisters\Nicci\Obligation;

$batch = (new NicciDocument())
    ->addCase(
        orderId: 'order-1',
        debtor: Debtor::company('Dłużnik Sp. z o.o.', '5260250995', Address::polish('ul. Prosta 1', '00-001', 'Warszawa')),
        obligations: [
            new Obligation(
                title: 'FV/0001/2026',
                paymentDate: $invoice->due_date,
                callSent: $invoice->demand_sent_at,     // payment demand with the warning about KRD
                arrears: '1230.50',
                debt: '1500.00',
                type: ObligationType::Invoice,
                userId: 'invoice-'.$invoice->id,        // your id, used to update or remove it later
            ),
        ],
        caseId: 'customer-'.$customer->id,
    )
    ->addCase('order-2', Debtor::consumer('Jan', 'Kowalski', '44051401359', Address::polish('ul. Długa 2', '00-002', 'Warszawa')), [/* ... */])
    ->addCase('order-3', Debtor::soleTrader('Jan Nowak Usługi', '8226951300', 'Jan', 'Nowak'), [/* ... */]);

$submissions = DebtRegisters::krd()->submissions();
$jobId = $submissions->submit($batch, description: 'November 2026');   // never retried automatically

KRD accepts an obligation only when it has been due for at least 30 days, at least 30 days passed since the payment demand (callSent), and the arrears exceed 200 PLN for a consumer or 500 PLN for a business. These rules are checked by KRD and reported per order. Without objections an obligation is sent as undisputed.

Updating and removing:

use Beziworld\DebtRegisters\Enums\InformationType;
use Beziworld\DebtRegisters\Enums\RemoveReason;

$batch = (new NicciDocument())
    ->updateObligation('u-1', 'invoice-15', $obligationWithLowerArrears)         // partial payment
    ->updateCase('u-2', 'customer-7', $debtor, $obligations)                     // obligations left out are removed
    ->remove('r-1', 'customer-9')                                                // whole case paid
    ->remove('r-2', 'invoice-21', InformationType::ObligationInformation, RemoveReason::Cession);

Status and processing report:

$job = $submissions->job($jobId);   // Job: status (Queued, Started, Processed, Canceled, Failed), progress, queueNumber

if ($job?->isProcessed()) {
    $report = $submissions->result($jobId);

    $report->successful();                         // no fatal error and no failed order
    $report->failedOrders();                       // OrderResult with errorMessage and errorCode
    $report->order('order-1')?->systemId;          // KRD case id, e.g. "CAF/0000219303/2018"
    $report->order('order-1')?->obligationSystemIds;   // ['invoice-15' => 'OBF/0000408118/2018']
    $report->xml;                                  // the original report, for archiving
}

$submissions->jobs(changedSince: now()->subDay());   // for a scheduled poller
$submissions->cancel($jobId);                        // only while queued

A complete Nicci document built elsewhere can be passed to submit() as a string. NicciDocument::toXml() returns the document for review or archiving.

Errors

Situation Result
Invalid login or password (SecurityFault code 1), empty Siddin ticket AuthenticationFailed
Missing permission or access denied (codes 2, 3) PermissionDenied
Data rejected by KRD (ValidationFault, SchemaValidationFault, codes 13, 14) ValidationFailed with validationErrors() per field
Unknown report number (EntityNotFoundFault, code 50) ReportNotFound
Other SOAP faults, HTTP errors, connection failures, unreadable responses ApiException
Invalid PESEL or NIP checksum InvalidIdentifier, before any request
Invalid batch data (empty batch, amounts, lengths, REGON, duplicate order ids) InvalidSubmission, before any request
Report download in incognito mode, result of an unprocessed job, Siddin with a certificate-only connection UnsupportedOperation
Missing credentials, unknown connection or driver InvalidConfiguration

ApiException (and its subclasses) expose errorCode(), faultType(), faultId() (the error id to quote to KRD support at pomocit@krd.pl), httpStatus(), operation() and raw(). All exceptions extend Beziworld\DebtRegisters\Exceptions\DebtRegistersException.

Not covered

  • KRD registry of queries (SearchRegistryReport, GetGenericDisclosureReport), monitoring, positive information (PaidObligationCase), executive and alimony obligations in the batch builder (they can be sent in a raw Nicci document), Rastin (PESEL and ID card verification), Solvig (scoring), Amnell (PESEL reservation), Wexler (financial statements).
  • BIG InfoMonitor and ERIF (no public endpoint and authentication documentation).

Development

composer install
composer ci   # pint --test, phpstan (level 8), pest

Fixtures follow the examples and the WSDL/XSD published by KRD; no request leaves the test suite. The SOAP envelope, the endpoints and the fault mapping were checked against the KRD demo environment with invalid credentials only.

Author

Created and maintained by Kamil Ćwiertnia, beziworld.eu.

Need an integration? Get in touch through beziworld.eu.

Other packages by the author

laravel-company-data · laravel-krs · laravel-polish-validation · laravel-ksef

License

MIT. See LICENSE.md. This package is not affiliated with or endorsed by Krajowy Rejestr Długów Biuro Informacji Gospodarczej S.A., BIG InfoMonitor S.A. or ERIF Biuro Informacji Gospodarczej S.A. Use of the KRD services requires an agreement with KRD and compliance with the act on disclosure of economic information.