xingen/xingen-sdk
Composer 安装命令:
composer require xingen/xingen-sdk
包简介
PHP client SDK for the Xingen e-invoice validation API
README 文档
README
PHP client SDK for the Xingen 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
composer require xingen/xingen-sdk
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
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)
use Xingen\Sdk\Invoices\InvoiceSubmission; use Xingen\Sdk\Invoices\LineInput; use Xingen\Sdk\Invoices\PartyInput; $submission = new InvoiceSubmission( invoiceNumber: 'INV-2024-0042', issueDate: '2024-03-15', currency: 'EUR', validationProfile: ValidationProfile::XRECHNUNG, supplier: new PartyInput(name: 'Acme GmbH', vatId: 'DE123456789'), buyer: new PartyInput(name: 'Buyer Co', leitwegId: '991-12345-06'), buyerReference: '991-12345-06', lines: [ new LineInput( description: 'Software License Q1', quantity: '5', unit: 'C62', price: '199.00', taxRate: '19', ), ], ); $result = $client->invoices->submitAndWait($submission);
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);
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
$pdf = $client->invoices->downloadPdf($id); // ZUGFeRD PDF with embedded XML $idocXml = $client->invoices->downloadIdocXml($id); // SAP IDoc XML
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
All SDK exceptions extend Xingen\Sdk\Error\XingenException. HTTP errors map to typed subclasses
of ApiException:
| Exception | Status | Notes |
|---|---|---|
AuthenticationException |
401 | Missing or invalid API key |
PermissionException |
403 | Resource exists but isn't owned by the caller |
NotFoundException |
404 | |
ValidationRequestException |
400 | ->fieldErrors has details for request-body validation failures |
QuotaExceededException |
429 | Monthly request quota exhausted |
ApiException |
other 4xx/5xx | Fallback; ->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
- 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, neverfloat. PHP has no native arbitrary-precision decimal type, sofloatwould 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'sjson_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 itsBigDecimalfields 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
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
MIT — see LICENSE.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-10