Official PHP SDK for the NaprawKSeF API (KSeF XML validation, FA(3) correction builder, webhooks). Zero non-stdlib runtime dependencies; works with Laravel, Symfony and plain PHP.

Maintainers

Package info

github.com/naprawksef/sdk-php

Homepage

Documentation

pkg:composer/naprawksef/sdk

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-25 12:55 UTC

This package is auto-updated.

Last update: 2026-05-25 13:09:46 UTC


README

Official PHP SDK for the NaprawKSeF API.

  • Full coverage of /api/v1: validate, correction analyze, scenarios, webhooks, me, health
  • Built-in retries (429, 5xx, network), respects Retry-After
  • Automatic Idempotency-Key generation for POST requests
  • Webhook signature verification helper (constant-time HMAC-SHA256)
  • Zero runtime dependencies — uses native ext-curl

Requirements

  • PHP 8.1+
  • ext-curl
  • ext-json

Installation

composer require naprawksef/sdk

Quick start

<?php

use NaprawKsef\Sdk\NaprawKsef;
use NaprawKsef\Sdk\Exceptions\RateLimitException;
use NaprawKsef\Sdk\Exceptions\ValidationException;

require __DIR__ . '/vendor/autoload.php';

$client = new NaprawKsef([
    'api_key' => $_ENV['NAPRAW_KSEF_API_KEY'], // nk_live_... or nk_test_...
    'app_name' => 'trawers-erp/7.4.2',         // optional, attached to User-Agent
]);

// 1. Who am I?
$me = $client->me->retrieve();
echo $me['organization']['name'], PHP_EOL;

// 2. Validate a KSeF XML
$xml = file_get_contents('invoice.xml');
try {
    $result = $client->validate->run($xml, filename: 'invoice.xml');
    if (!$result['valid']) {
        foreach ($result['issues'] as $issue) {
            printf("[%s] %s → %s\n", $issue['severity'], $issue['code'], $issue['message']);
        }
    }
} catch (RateLimitException $e) {
    printf("Throttled. Wait %d seconds.\n", $e->retryAfterSeconds() ?? 60);
} catch (ValidationException $e) {
    printf("Bad input: %s (%s)\n", $e->getMessage(), $e->code);
}

// 3. Analyse for FA(3) correction scenarios
$analysis = $client->correction->analyze($xml);
print_r($analysis['suggestions']); // top 5 scenarios with confidence

API keys

API keys come in two environments:

Prefix Environment Quota History
nk_live_* production counts against your plan persisted in dashboard
nk_test_* sandbox bypassed not persisted

The SDK rejects malformed keys at construction time (44 chars, nk_<env>_<4 hex prefix><32 hex secret>).

Idempotency

POSTs to mutating endpoints accept an idempotency key (8–200 chars). The SDK auto-generates a UUID v4 when you don't supply one — pass your own for end-to-end replay safety:

$client->validate->run(
    xml: $xml,
    idempotencyKey: "invoice-{$invoice->id}-v3",
);

Replays return the original response for 24 hours. Conflicting bodies under the same key yield an IdempotencyConflictException.

Errors

All API errors are subclasses of NaprawKsefException:

use NaprawKsef\Sdk\Exceptions\{
    NaprawKsefException,
    AuthException,
    ForbiddenException,
    NotFoundException,
    ValidationException,
    RateLimitException,
    IdempotencyConflictException,
    ServerException,
    NetworkException,
};

Each exception exposes $status, $code (stable string, e.g. QUOTA_EXCEEDED), $requestId, $headers and $details.

RateLimitException and IdempotencyConflictException expose retryAfterSeconds() to make retry loops easy.

Webhooks

Verify incoming events with the standalone helper (no SDK client needed):

<?php

use NaprawKsef\Sdk\Webhooks\SignatureVerifier;

require __DIR__ . '/vendor/autoload.php';

$rawBody = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_NK_SIGNATURE'] ?? null;

$result = SignatureVerifier::verify(
    secret: $_ENV['NAPRAW_KSEF_WEBHOOK_SECRET'],
    rawBody: $rawBody,
    header: $header,
);

if (!$result['ok']) {
    http_response_code(401);
    exit('Invalid signature: ' . $result['reason']);
}

$event = json_decode($rawBody, true);
// handle $event['type'] (e.g. "validation.completed")

http_response_code(200);
echo 'ok';

Laravel controller:

public function __invoke(Request $request)
{
    $result = SignatureVerifier::verify(
        secret: config('services.naprawksef.webhook_secret'),
        rawBody: $request->getContent(),
        header: $request->header('X-NK-Signature'),
    );

    abort_if(!$result['ok'], 401, 'Invalid signature: ' . $result['reason']);

    $event = $request->json()->all();
    // …

    return response('ok');
}

Retries

408, 425, 429, 500, 502, 503, 504 and network errors are retried up to max_retries (default 3) with exponential backoff + ±20 % jitter. Retry-After is honoured on 429/503.

$client = new NaprawKsef([
    'api_key' => $apiKey,
    'max_retries' => 0, // disable retries
    'timeout_ms' => 60_000, // longer per-request timeout
]);

Testing

composer install
composer test

The included PHPUnit suite covers signature verification end-to-end.

License

MIT. See LICENSE.