cmelda/eet

PHP 8.4+ client for Czech EET 2.0 (interface 4.1) with sale validation, SOAP/WS-Security signing, submission, and signed response validation

Maintainers

Package info

gitlab.com/cmelda/eet

Issues

pkg:composer/cmelda/eet

Transparency log

Fund package maintenance!

Ko-Fi

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

v1.0.0 2026-07-16 21:20 UTC

This package is auto-updated.

Last update: 2026-07-16 19:24:00 UTC


README

Latest Stable Version Total Downloads License PHP Version Require

PHP 8.4+ client for EET 2.0, interface version 4.1.

The library validates sale data, builds XML from the supplied EET schema, wraps it in SOAP 1.1, signs the soap:Body using WS-Security, sends the request through Guzzle, and validates the signed response.

Requirements

  • PHP 8.4 or later
  • DOM and libxml extensions
  • OpenSSL extension
  • JSON extension
  • a valid EET certificate for the selected environment

Installation

composer require cmelda/eet

Examples

The examples directory contains simple standalone scripts:

  • submit-order.php represents an order loaded from an application database, maps it to Sale, submits it in playground verification mode, and handles confirmation, rejection, warnings, and exceptions;
  • exact-amounts.php demonstrates exact CZK inputs and float rounding.

Install dependencies before running an example. The order example also requires playground certificate environment variables:

composer install
EET_PLAYGROUND_CERTIFICATE=/secure/path/playground.p12 \
EET_PLAYGROUND_CERTIFICATE_PASSWORD=secret \
php examples/submit-order.php

Replace the example order array with an order loaded from your own repository or ORM. Store the POK only after a ConfirmationResponse; store rejections separately and route transport or signature failures through the application's retry and incident procedure.

Client setup

Load a PKCS#12 certificate and create the client:

use Cmelda\Eet\Certificate\CertificateCredentials;
use Cmelda\Eet\Client\EetClient;

$credentials = CertificateCredentials::fromPkcs12File(
    '/secure/path/eet-certificate.p12',
    $certificatePassword,
);

$client = new EetClient($credentials);

EetClient uses Environment::Production by default. Select the playground explicitly for development and verification:

use Cmelda\Eet\Enum\Environment;

$playgroundClient = new EetClient(
    $credentials,
    Environment::Playground,
);

The service endpoints are resolved internally from Environment. The public API does not accept custom EET endpoints.

Sale submission

Use strings or Brick\Money\Money for exact monetary input whenever possible:

use Cmelda\Eet\Data\Sale;
use DateTimeImmutable;

$sale = Sale::create(
    taxpayerEic: 'CZ00000019',
    establishmentId: 1,
    cashRegisterId: 'REGISTER-1',
    receiptSerialNumber: '2026-0001',
    saleAt: new DateTimeImmutable('now'),
    total: '250.00',
);

$result = $client->send($sale);

send() uses SubmissionMode::Live by default. Live mode omits the XML attribute overeni. Verification mode must be selected explicitly:

use Cmelda\Eet\Enum\SubmissionMode;

$result = $playgroundClient->send(
    $sale,
    SubmissionMode::Verification,
);

Use Environment::Playground together with SubmissionMode::Verification for playground checks. Never send test sales to the production environment.

Response and error handling

SubmissionResult::response is either ConfirmationResponse or ErrorResponse. A successful HTTP request does not by itself mean that the sale was confirmed.

use Cmelda\Eet\Exception\EetException;
use Cmelda\Eet\Response\ConfirmationResponse;
use Cmelda\Eet\Response\ErrorResponse;

try {
    $result = $client->send($sale);
} catch (EetException $exception) {
    // Queue the sale for your documented failure or retry procedure.
    // Do not log certificate passwords, private keys, or complete credentials.
    throw $exception;
}

$response = $result->response;

if ($response instanceof ConfirmationResponse) {
    $pok = $response->pok;
    $acceptedAt = $response->acceptedAt;

    // Persist the POK and the data required by your audit and retention policy.
} elseif ($response instanceof ErrorResponse) {
    $errorCode = $response->code;
    $errorMessage = $response->message;

    // Record the rejection and follow your documented correction procedure.
}

foreach ($response->warnings as $warning) {
    // Record and monitor every warning according to your operational policy.
}

When only the confirmation state or POK is needed, use:

$confirmed = $result->isConfirmed();
$pok = $result->pok(); // null for an ErrorResponse

Transport, certificate, XML, schema, response, signature, and input validation failures derive from Cmelda\Eet\Exception\EetException.

Certificates

The following certificate inputs are supported:

CertificateCredentials::fromPkcs12Contents($contents, $password);
CertificateCredentials::fromPkcs12File($path, $password);
CertificateCredentials::fromPemContents($certificate, $privateKey, $password);
CertificateCredentials::fromPemFiles($certificatePath, $privateKeyPath, $password);

The library verifies that the certificate and private key can be loaded, match each other, and can produce a SHA-256 signature. Never commit, log, or expose production certificates, private keys, or passwords. Credentials included in this repository are for the EET playground test suite only.

Signed confirmation responses are accepted only when the SOAP signature and body digest are valid, the embedded certificate is currently valid, its complete chain leads to the official bundled trust anchor for the selected environment, and its subject matches the EET signing-certificate profile. Production requires the organization Generální finanční ředitelství; playground accepts the officially documented signing certificate common names.

EET error responses are intentionally unsigned according to the interface specification, including a successful verification-mode response with error code 0. The client accepts these responses only as ErrorResponse; an unsigned response can never produce a confirmation or POK. TLS certificate verification remains enabled for all transport communication.

Amounts

The public API accepts string, int, float, Brick\Money\Money, and MoneyAmount. Values are stored without an internal floating-point representation.

use Brick\Money\Money;
use Cmelda\Eet\Data\MoneyAmount;

MoneyAmount::fromString('10.20')->toXmlValue();       // 10.20
MoneyAmount::fromInt(10)->toXmlValue();               // 10.00
MoneyAmount::fromMoney(Money::of('10.20', 'CZK'))
    ->toXmlValue();                                   // 10.20
MoneyAmount::fromFloat(10.205)->toXmlValue();         // 10.21

Float input is a convenience API only. MoneyAmount::fromFloat() converts it immediately and rounds HALF_UP to two decimal places. Non-CZK Money values, non-finite floats, malformed decimal strings, and values outside the EET schema range are rejected.

Production considerations

A production integration should, at minimum:

  • persist a POK only from a confirmed response;
  • handle ErrorResponse, warnings, transport failures, and invalid signatures separately;
  • retain enough data to trace, audit, and safely retry each submission;
  • monitor rejected and unconfirmed sales, certificate expiry, and service availability;
  • prevent accidental switching between production and playground environments;
  • run an application-level end-to-end verification before deployment and after upgrades;
  • maintain a documented outage, retry, reconciliation, and business continuity procedure;
  • verify current legal and technical requirements using authoritative sources.

Testing and quality checks

Install development dependencies and run the complete quality gate:

composer install
composer check

composer check runs PHPCS, PHPStan at max level with strict rules, Rector in dry-run mode, and all deterministic unit and integration tests. It does not contact the EET playground.

Run individual suites when diagnosing a failure:

composer unit
composer integration
composer playground

composer playground is an explicit opt-in network test. It uses the test-only credentials in tests/Support/Data/Certificates. CI may override them with these environment variables:

  • EET_PLAYGROUND_EIC
  • EET_PLAYGROUND_ESTABLISHMENT_ID
  • EET_PLAYGROUND_CERTIFICATE
  • EET_PLAYGROUND_CERTIFICATE_CONTENTS (raw or base64-encoded PKCS#12 data)
  • EET_PLAYGROUND_CERTIFICATE_PASSWORD

The playground test requires network access and may be skipped when usable playground credentials are not available. It never targets the production endpoint.

GitLab CI runs the complete quality gate on PHP 8.4 and PHP 8.5. Before a release, also run:

composer validate --strict
composer audit --abandoned=fail

License

This project is distributed under the standard MIT License (SPDX identifier: MIT).