CurlLexwareTransport.php
Pfad: src/Infrastructure/Rechnung/CurlLexwareTransport.php
Ext: php
Größe: 3376 Bytes
Geändert: 2026-07-16 17:37:59+02
<?php
declare(strict_types=1);
namespace Demo\Infrastructure\Rechnung;
use CURLFile;
use RuntimeException;
final class CurlLexwareTransport implements LexwareTransportPort
{
private float $lastRequestAt = 0.0;
public function __construct(
private readonly string $baseUrl,
private readonly string $apiKey,
private readonly int $rateLimitPerSecond = 2,
) {
if ($this->apiKey === '') {
throw new RuntimeException('Lexware API-Key fehlt');
}
}
public function request(
string $method,
string $path,
array $query = [],
?array $payload = null,
?string $filePath = null,
): array {
$this->throttle();
$url = $this->baseUrl . $path;
if ($query !== []) {
$url .= '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986);
}
$headers = ['Authorization: Bearer ' . $this->apiKey, 'Accept: application/json'];
$body = null;
if ($filePath !== null) {
$body = ['file' => new CURLFile($filePath, 'application/pdf', basename($filePath))];
} elseif ($payload !== null) {
$body = json_encode($payload, JSON_THROW_ON_ERROR);
$headers[] = 'Content-Type: application/json';
}
$curl = curl_init($url);
if ($curl === false) {
throw new RuntimeException('Lexware HTTP-Client kann nicht initialisiert werden');
}
$method = strtoupper($method);
if (!in_array($method, ['GET', 'POST'], true)) {
throw new RuntimeException('Nicht erlaubte Lexware-HTTP-Methode');
}
$configured = curl_setopt_array($curl, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => $headers,
CURLOPT_CONNECTTIMEOUT => 8,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_SSL_VERIFYHOST => 2,
]);
if (!$configured || ($method === 'POST' && !curl_setopt($curl, CURLOPT_POST, true))) {
throw new RuntimeException('Lexware HTTP-Client kann nicht konfiguriert werden');
}
if ($body !== null && !curl_setopt($curl, CURLOPT_POSTFIELDS, $body)) {
throw new RuntimeException('Lexware HTTP-Payload kann nicht gesetzt werden');
}
$response = curl_exec($curl);
$status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$errorNumber = curl_errno($curl);
if (!is_string($response)) {
throw new RuntimeException('Lexware Transportfehler ' . $errorNumber);
}
if ($status < 200 || $status >= 300) {
throw new RuntimeException('Lexware HTTP-Fehler ' . $status);
}
if ($response === '') {
return [];
}
$decoded = json_decode($response, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($decoded)) {
throw new RuntimeException('Lexware-Antwort ist kein JSON-Objekt');
}
return $decoded;
}
private function throttle(): void
{
$minimum = 1 / max(1, $this->rateLimitPerSecond);
$remaining = $minimum - (microtime(true) - $this->lastRequestAt);
if ($remaining > 0) {
usleep((int) ceil($remaining * 1_000_000));
}
$this->lastRequestAt = microtime(true);
}
}