27-05-2026 DHL Modul v2.1 / Optimierung tracking

This commit is contained in:
Kevin Adametz 2026-05-27 18:51:23 +02:00
parent 036595be94
commit 2bdc9ada3c
33 changed files with 2367 additions and 2086 deletions

View file

@ -8,6 +8,7 @@ use Acme\Dhl\Jobs\CreateShipmentJob;
use Acme\Dhl\Models\DhlShipment;
use Acme\Dhl\Support\DhlClient;
use App\Services\DhlProductResolver;
use App\Services\DhlShipmentService;
use App\Services\DhlShipmentWeightCalculator;
use Exception;
use Illuminate\Support\Facades\DB;
@ -34,7 +35,7 @@ class ShippingService
*/
public function createLabel(array $orderData): array
{
Log::info('createLabel', $orderData);
Log::info('[DHL Shipping] createLabel called', DhlShipmentService::sanitizeOrderDataForLog($orderData));
$validatedData = $this->validateOrderData($orderData);
if (config('dhl.use_queue')) {
CreateShipmentJob::dispatch($validatedData);
@ -45,12 +46,7 @@ class ShippingService
return DB::transaction(function () use ($validatedData) {
$payload = $this->buildShipmentPayload($validatedData);
// Debug logging: Log the exact payload being sent to DHL API
Log::info('[DHL API] Sending payload to DHL', [
'endpoint' => '/parcel/de/shipping/v2/orders',
'payload' => $payload,
'payload_json' => json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
]);
Log::info('[DHL API] Sending payload to DHL', $this->buildPayloadLogContext($payload, $validatedData));
try {
// Build query parameters for print format
@ -62,9 +58,7 @@ class ShippingService
$response = $this->client->request('post', '/parcel/de/shipping/v2/orders', $payload, $query);
Log::info('[DHL API] Response received', [
'response' => $response,
]);
Log::info('[DHL API] Response received', $this->buildResponseLogContext($response));
$this->assertSuccessfulShipmentResponse($response, $this->shouldUseMustEncode($validatedData));
} catch (DhlValidationException $e) {
if ($this->shouldUseMustEncode($validatedData) && $this->looksLikeAddressValidationError($e->getMessage())) {
@ -73,10 +67,10 @@ class ShippingService
throw $e;
} catch (Exception $e) {
Log::error('[DHL API] Request failed', [
'error' => $e->getMessage(),
'payload' => $payload,
]);
Log::error('[DHL API] Request failed', array_merge(
$this->buildPayloadLogContext($payload, $validatedData),
['error' => $e->getMessage()]
));
throw $e;
}
@ -204,6 +198,53 @@ class ShippingService
return null;
}
/**
* Build a redacted DHL request log context.
*
* Logs only routing-relevant metadata and never the full payload, since
* the payload contains personal address data and billing numbers.
*
* @param array<string, mixed> $payload
* @param array<string, mixed> $validatedData
* @return array<string, mixed>
*/
private function buildPayloadLogContext(array $payload, array $validatedData): array
{
$billingNumber = data_get($payload, 'shipments.0.billingNumber');
return [
'endpoint' => '/parcel/de/shipping/v2/orders',
'order_id' => $validatedData['order_id'] ?? null,
'product_code' => data_get($payload, 'shipments.0.product'),
'billing_number_suffix' => is_string($billingNumber) ? mb_substr($billingNumber, -4) : null,
'weight_grams' => data_get($payload, 'shipments.0.details.weight.value'),
'consignee_country' => data_get($payload, 'shipments.0.consignee.country'),
'has_reference' => ! empty(data_get($payload, 'shipments.0.refNo')),
'must_encode' => $this->shouldUseMustEncode($validatedData),
];
}
/**
* Build a redacted DHL response log context.
*
* Drops the base64 label payload, which is large and not useful in logs.
*
* @param array<string, mixed> $response
* @return array<string, mixed>
*/
private function buildResponseLogContext(array $response): array
{
return [
'shipment_number' => $this->extractShipmentNumber($response),
'has_label' => $this->extractLabelData($response) !== null,
'routing_code' => $this->extractRoutingCode($response),
'item_status_code' => data_get($response, 'items.0.sstatus.statusCode')
?? data_get($response, 'status.statusCode'),
'item_status_title' => data_get($response, 'items.0.sstatus.title')
?? data_get($response, 'status.title'),
];
}
/**
* Validate required order data according to DHL API v2 specification
*/
@ -380,20 +421,30 @@ class ShippingService
$addressData['houseNumber'] = $parsed['houseNumber'];
Log::info('Parsed German address', [
'original' => $street,
'parsed_street' => $parsed['street'],
'parsed_houseNumber' => $parsed['houseNumber'],
'original_street_length' => strlen($street),
'parsed_street_length' => strlen($parsed['street']),
'parsed_house_number_length' => strlen($parsed['houseNumber']),
]);
} elseif (! $parsed['houseNumber']) {
// If we can't parse house number, use a default
$addressData['houseNumber'] = '1';
Log::warning('Could not parse house number from address, using default', [
'street' => $street,
]);
return $addressData;
}
return $addressData;
// No house number could be parsed from the street. We must not invent
// one (the previous `'1'` default caused parcels to be delivered to
// the wrong address) and the DHL API rejects shipments without a
// house number anyway. Surface a validation error so the operator can
// fix the address before we ever hit DHL.
Log::warning('Could not parse house number from address', [
'street_length' => strlen($street),
'country' => $addressData['country'] ?? null,
'postal_prefix' => isset($addressData['postalCode']) && is_string($addressData['postalCode'])
? mb_substr($addressData['postalCode'], 0, 2)
: null,
]);
throw new InvalidArgumentException(
'Hausnummer fehlt in der Adresse und konnte nicht automatisch aus dem Strassenfeld ermittelt werden. Bitte Strasse und Hausnummer separat erfassen.'
);
}
/**