PHPackages                             xingen/xingen-sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. xingen/xingen-sdk

ActiveLibrary[Payment Processing](/categories/payments)

xingen/xingen-sdk
=================

PHP client SDK for the Xingen e-invoice validation API

v0.2.0(1mo ago)02MITPHPPHP &gt;=8.1CI passing

Since Jul 10Pushed 1mo agoCompare

[ Source](https://github.com/nko-technologies/xingen-php-sdk)[ Packagist](https://packagist.org/packages/xingen/xingen-sdk)[ Docs](https://github.com/nko-technologies/xingen-php-sdk)[ RSS](/packages/xingen-xingen-sdk/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (3)Used By (0)

xingen-sdk
==========

[](#xingen-sdk)

PHP client SDK for the [Xingen](https://xingen.de) e-invoice validation API — submit UBL, CII, ZUGFeRD, and SAP IDoc/OData invoices for validation against EN16931, XRechnung, and Peppol.

Requires PHP 8.1+ with the `curl` and `json` extensions (both bundled with PHP by default). No third-party runtime dependencies.

> Status: v1, covering invoice submission/validation and API key management. Contacts and dashboard/user endpoints are not exposed (they're Firebase-auth-only on the backend).

Install
-------

[](#install)

```
composer require xingen/xingen-sdk
```

Authentication
--------------

[](#authentication)

Every request needs an API key (`xgn_live_...` for production, `xgn_test_...` for sandbox — sandbox requests never count toward quota). Create one from the Xingen dashboard or via `$client->apiKeys`.

```
use Xingen\Sdk\XingenClient;

$client = new XingenClient(apiKey: getenv('XINGEN_API_KEY'));
```

`XingenClient` holds one connection-pooled curl handle — construct it once and reuse it, don't rebuild it per request. `baseUrl:` overrides the default `https://app.xingen.de/api`, useful for self-hosted or local (`./gradlew bootRun`, port 10001) testing.

Validate a file
---------------

[](#validate-a-file)

Every validate/submit endpoint is asynchronous — the backend queues the invoice and returns immediately. Use a `*AndWait` helper to submit and poll for the result in one call:

```
use Xingen\Sdk\Models\InvoiceStatus;
use Xingen\Sdk\Models\ValidationProfile;

$result = $client->invoices->validateFileAndWait('invoice.xml', ValidationProfile::XRECHNUNG);

if ($result->status === InvoiceStatus::VALIDATED && $result->validationResult->valid) {
    echo "Valid!\n";
} else {
    foreach ($result->validationResult->errors as $error) {
        echo "{$error->severity->value}: {$error->message} ({$error->field})\n";
    }
}
```

`PollOptions` controls the backoff (`initialInterval`, `maxInterval`, `backoffMultiplier`), the overall `timeout`, and an optional `cancellationCheck`. A **failed validation is not an exception** — it's a completed API call that found the invoice invalid, so `*AndWait` returns normally with `validationResult->valid === false`. Only a transport failure, cancellation, or timeout throws.

```
use Xingen\Sdk\Invoices\PollOptions;

$options = new PollOptions(initialInterval: 0.3, maxInterval: 3.0, timeout: 30.0);
$result = $client->invoices->validateFileAndWait('invoice.xml', ValidationProfile::XRECHNUNG, $options);
```

If you'd rather manage polling yourself, use the low-level pair:

```
$submitted = $client->invoices->validateFile('invoice.xml', ValidationProfile::EN16931);
// ... later ...
$record = $client->invoices->get($submitted->id);
```

`validateIdoc` / `validateIdocAndWait` work the same way for SAP IDoc XML files. Both, and `validateFile`/`validateFileAndWait`, also accept a `[$filename, $content]` array if you already hold the file bytes in memory instead of a path.

Submit a structured invoice (JSON)
----------------------------------

[](#submit-a-structured-invoice-json)

```
use Xingen\Sdk\Invoices\AddressInput;
use Xingen\Sdk\Invoices\InvoiceSubmission;
use Xingen\Sdk\Invoices\LineInput;
use Xingen\Sdk\Invoices\PartyInput;
use Xingen\Sdk\Invoices\PaymentMeansInput;

$submission = new InvoiceSubmission(
    invoiceNumber: 'INV-2024-0042',
    issueDate: '2024-03-15',
    currency: 'EUR',
    validationProfile: ValidationProfile::XRECHNUNG,
    supplier: new PartyInput(
        name: 'Acme GmbH',
        vatId: 'DE123456789',
        address: new AddressInput(city: 'Berlin', countryCode: 'DE'),
    ),
    buyer: new PartyInput(
        name: 'Buyer Co',
        leitwegId: '991-12345-06',
        address: new AddressInput(countryCode: 'DE'),
    ),
    buyerReference: '991-12345-06',
    lines: [
        new LineInput(
            description: 'Software License Q1',
            quantity: '5',
            unit: 'C62',
            price: '199.00',
            taxRate: '19',
        ),
    ],
    paymentMeans: [
        new PaymentMeansInput(typeCode: '58', creditTransferAccountId: 'DE89370400440532013000'),
    ],
);

$result = $client->invoices->submitAndWait($submission);
```

`InvoiceSubmission` has full parity with the backend's domain model — every invoice type it can validate, it can also submit, including non-standard VAT categories (exempt/reverse-charge/export via `LineInput::$taxCategoryCode` + `$exemptionReason`/`$exemptionReasonCode`), `payee`/ `taxRepresentative`, `delivery`, `invoicePeriod`, `precedingInvoiceReferences` (for credit notes), and the full BT-11..BT-19 document reference fields. See the constructor parameters on `InvoiceSubmission` and its nested value objects for the complete set.

SAP S/4HANA OData supplier-invoice payloads are supported as a thin passthrough — pass a raw JSON string or a plain array rather than a fully typed model:

```
$client->invoices->submitOdata($rawOdataJson, ValidationProfile::EN16931);
```

Extract an invoice from a PDF (AI)
----------------------------------

[](#extract-an-invoice-from-a-pdf-ai)

Upload a plain invoice PDF — including scanned/image-based PDFs — and let the backend extract structured fields with Claude. Works exactly like the other submit endpoints: async, so use `extractInvoiceAndWait` or the low-level `extractInvoice`/`get` pair.

```
use Xingen\Sdk\Models\ExtractionModelTier;

$result = $client->invoices->extractInvoiceAndWait(
    'scanned-invoice.pdf',
    ValidationProfile::EN16931,
    ExtractionModelTier::FAST,   // or ACCURATE -- higher accuracy, Pro subscription required
);
```

If the extraction missed a field or validation flagged something, correct it with a JSON merge-patch (RFC 7386) and re-validate synchronously — only invoices that finished processing (`VALIDATED` or `FAILED_VALIDATION`) can be corrected. Array fields (`lines`, `paymentMeans`, `allowanceCharges`, `taxBreakdowns`) are replaced wholesale when present in the patch:

```
$corrected = $client->invoices->patchInvoice($result->id, [
    'currency' => 'EUR',
    'buyerReference' => '991-12345-06',
]);
```

To find out which fields the backend fills in automatically per profile (so you know what *not*to prompt the user for):

```
$autoFilled = $client->invoices->getAutoFilledFields();
// ['EN16931' => [AutoFilledField, ...], 'PEPPOL' => [...], ...]
```

List and retrieve invoices
--------------------------

[](#list-and-retrieve-invoices)

```
$page = $client->invoices->list(0, 20, 'createdAt,desc');

// or, to walk every invoice without managing page indices yourself:
foreach ($client->invoices->listAll(50) as $record) {
    echo "{$record->id} -> {$record->status->value}\n";
}

$one = $client->invoices->get('inv_01HXYZ');
```

Download results
----------------

[](#download-results)

```
$pdf = $client->invoices->downloadPdf($id);          // ZUGFeRD PDF with embedded XML
$idocXml = $client->invoices->downloadIdocXml($id);   // SAP IDoc XML
```

API keys
--------

[](#api-keys)

```
use Xingen\Sdk\ApiKeys\CreateApiKeyRequest;

$created = $client->apiKeys->create(new CreateApiKeyRequest(name: 'Production CI', sandbox: false));
echo "Store this now, it's shown only once: {$created->rawKey}\n";

$keys = $client->apiKeys->list();
$client->apiKeys->revoke($created->id);
```

Error handling
--------------

[](#error-handling)

All SDK exceptions extend `Xingen\Sdk\Error\XingenException`. HTTP errors map to typed subclasses of `ApiException`:

ExceptionStatusNotes`AuthenticationException`401Missing or invalid API key`PermissionException`403Resource exists but isn't owned by the caller`NotFoundException`404`ValidationRequestException`400`->fieldErrors` has details for request-body validation failures`QuotaExceededException`429Monthly request quota exhausted`ApiException`other 4xx/5xxFallback; `->statusCode` / `->rawBody` always available```
use Xingen\Sdk\Error\QuotaExceededException;
use Xingen\Sdk\Error\ValidationRequestException;
use Xingen\Sdk\Error\XingenException;

try {
    $client->invoices->submit($submission);
} catch (ValidationRequestException $e) {
    foreach ($e->fieldErrors as $field => $message) {
        echo "{$field}: {$message}\n";
    }
} catch (QuotaExceededException $e) {
    echo "Quota exceeded — upgrade or wait for the next billing period\n";
} catch (XingenException $e) {
    echo "Request failed: {$e->getMessage()}\n";
}
```

Design notes
------------

[](#design-notes)

- **No automatic retries.** Retrying a `submit()` after a client-side timeout is unsafe without idempotency keys, which the API doesn't support yet — a retried submit could create a duplicate invoice. Handle retries at the call site if you need them.
- **Monetary and quantity fields are `string`, never `float`.** PHP has no native arbitrary-precision decimal type, so `float` would silently lose precision on large or exact-decimal amounts. Response bodies are parsed through a small dependency-free JSON decoder (`Xingen\Sdk\Internal\Json`) that preserves every numeric literal as its exact source-text string instead of routing it through PHP's `json_decode()` (which, like most JSON parsers, converts numbers through a float64 intermediate). Request bodies send these same fields as plain PHP strings, which the backend's Jackson-based deserializer accepts for its `BigDecimal` fields without issue.
- **No third-party dependencies.** HTTP is handled via `ext-curl` (bundled with PHP), reusing a single connection-pooled handle across requests rather than a Composer HTTP client package.

Contributing
------------

[](#contributing)

```
composer install
vendor/bin/phpunit
```

Tests run against a real (loopback) `php -S` server subprocess, not a mocking framework — no network calls leave the machine, and no external test-server dependency is required.

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

34

—

LowBetter than 74% of packages

Maintenance91

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity33

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~4 days

Total

2

Last Release

44d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/039cd0f5cc2de645fe9b03c31cf48e5480230032c2b66a8f4de99dfa605d2d1a?d=identicon)[nk0t3ch](/maintainers/nk0t3ch)

---

Top Contributors

[![manuelgerstner](https://avatars.githubusercontent.com/u/4587093?v=4)](https://github.com/manuelgerstner "manuelgerstner (4 commits)")

---

Tags

invoicexrechnungE-InvoiceublpeppolciiEN16931xingen

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/xingen-xingen-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/xingen-xingen-sdk/health.svg)](https://phpackages.com/packages/xingen-xingen-sdk)
```

###  Alternatives

[num-num/ubl-invoice

A modern object-oriented PHP library to create and read valid UBL and EN 16931/Peppol BIS 3.0 files

138964.8k](/packages/num-num-ubl-invoice)[josemmo/einvoicing

Library for reading and creating European-compliant electronic invoices (EN 16931)

177384.0k5](/packages/josemmo-einvoicing)[horstoeko/zugferd

A library for creating and reading european electronic invoices

4306.4M37](/packages/horstoeko-zugferd)[horstoeko/zugferdublbridge

Convert Factur-X/ZUGFeRD (CII-Syntax) to PEPPOL (UBL-Syntax) and visa versa

16190.6k12](/packages/horstoeko-zugferdublbridge)[easybill/e-invoicing

A package to read and create EN16931 e-invoices or CIUS like: XRechnung, ZUGFeRD etc.

16169.2k](/packages/easybill-e-invoicing)[cleverit/ubl_invoice

A PHP wrapper for UBL invoices

36288.1k](/packages/cleverit-ubl-invoice)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
