mivita/dev/2026-01-23/dhl-return-label-fixes.md
2026-02-20 17:55:06 +01:00

8.9 KiB

DHL Return Label - Fixes für Fallback-Methode

Datum: 23.01.2026
Status: Behoben

Problem

Fehler: "DHL API error (400): 0 of 1 shipment successfully printed"

Ursache: Die Fallback-Methode (reguläre Shipping API) hatte mehrere Probleme:

  1. Country-Code Format:

    • ReturnsService verwendet 3-stellige Codes (DEU)
    • ShippingService erwartet 2-stellige Codes (DE)
    • Validierung schlug fehl: 'shipper.country' => 'required|string|size:2'
  2. Fehlende Felder:

    • Keine dimensions für V07PAK
    • Kein print_format gesetzt
    • Logging unzureichend

Lösung

1. Country-Code Konvertierung

Neue Hilfsfunktion hinzugefügt:

private function convertAddressFor2LetterCountry(array $address): array
{
    $reverseMap = [
        'DEU' => 'DE',
        'AUT' => 'AT',
        'CHE' => 'CH',
        // ... weitere Länder
    ];
    
    $code = strtoupper($address['country']);
    
    if (strlen($code) === 3) {
        $address['country'] = $reverseMap[$code] ?? 'DE';
    }
    
    return $address;
}

Verwendung in Fallback:

$shipper = $this->convertAddressFor2LetterCountry($returnData['shipper']);
$consignee = $this->convertAddressFor2LetterCountry($returnData['consignee']);

2. Dimensions hinzugefügt

'dimensions' => $dhlConfig['dimensions']['V07PAK'] ?? [
    'length' => 120,
    'width' => 60,
    'height' => 60,
],

DHL V07PAK Standard-Maße:

  • Länge: 120 cm
  • Breite: 60 cm
  • Höhe: 60 cm

3. Print Format hinzugefügt

'print_format' => $dhlConfig['retoure_print_format'] ?? 
                  $dhlConfig['print_format'] ?? 
                  'A4',

Priorität:

  1. retoure_print_format (falls konfiguriert)
  2. print_format (allgemeines Format)
  3. 'A4' (Fallback)

4. Erweitertes Logging

Log::info('[DHL Returns] Using regular Shipping API as fallback', [
    'original_data' => $returnData,
]);

Log::info('[DHL Returns] Prepared shipment data for fallback', [
    'shipmentData' => $shipmentData,
]);

Komplette Fallback-Methode

private function createReturnViaRegularShipment(array $returnData): array
{
    Log::info('[DHL Returns] Using regular Shipping API as fallback');

    $shippingService = new ShippingService($this->client);

    // Convert to 2-letter country codes
    $shipper = $this->convertAddressFor2LetterCountry($returnData['shipper']);
    $consignee = $this->convertAddressFor2LetterCountry($returnData['consignee']);

    // Get DHL config
    $settingController = new \App\Http\Controllers\SettingController();
    $dhlConfig = $settingController->getDhlConfig();

    $shipmentData = [
        'order_id' => $returnData['order_id'] ?? null,
        'weight_kg' => $returnData['weight_kg'] ?? 2.5,
        'product_code' => 'V07PAK',
        'label_format' => $returnData['label_format'] ?? 'PDF',
        'print_format' => $dhlConfig['retoure_print_format'] ?? 
                          $dhlConfig['print_format'] ?? 'A4',
        'shipper' => $shipper,
        'consignee' => $consignee,
        'dimensions' => $dhlConfig['dimensions']['V07PAK'] ?? [
            'length' => 120,
            'width' => 60,
            'height' => 60,
        ],
        'reference' => 'Return-' . ($returnData['order_id'] ?? time()),
    ];

    Log::info('[DHL Returns] Prepared shipment data for fallback', [
        'shipmentData' => $shipmentData,
    ]);

    $result = $shippingService->createLabel($shipmentData);

    // Mark as return
    if (isset($result['shipmentId'])) {
        DhlShipment::where('id', $result['shipmentId'])
            ->update([
                'type' => 'return',
                'related_shipment_id' => $returnData['original_shipment_id'] ?? null,
            ]);
    }

    Log::info('[DHL Returns] Return label created via Shipping API fallback');

    return [
        'returnNumber' => $result['shipmentNumber'] ?? null,
        'label_path' => $result['labelPath'] ?? null,
        'returnShipment' => DhlShipment::find($result['shipmentId'] ?? null),
        'raw' => $result,
        'method' => 'shipping_api_fallback'
    ];
}

Validierungs-Unterschiede

ReturnsService Validierung

'shipper.country' => 'nullable|string|min:2|max:3', // 2 oder 3 Buchstaben

ShippingService Validierung

'shipper.country' => 'required|string|size:2', // Exakt 2 Buchstaben

Country-Code Mapping

3 → 2 Buchstaben (für Fallback)

3-Letter 2-Letter Land
DEU DE Deutschland
AUT AT Österreich
CHE CH Schweiz
FRA FR Frankreich
ITA IT Italien
ESP ES Spanien
NLD NL Niederlande
BEL BE Belgien
GBR GB Großbritannien
USA US USA

2 → 3 Buchstaben (für Returns API)

Umgekehrtes Mapping in convertCountryCode() bereits vorhanden.

Workflow

Gesamter Return-Label Erstellungsprozess:

1. User klickt "Retourenlabel erstellen"
   ↓
2. ReturnsService::createReturn()
   ↓
3. Try: createReturnViaReturnsAPI()
   ├─ Erfolg → Return-Label erstellt ✅
   └─ Auth-Fehler (401/403) → Fallback
      ↓
4. createReturnViaRegularShipment()
   ├─ Convert 3-letter → 2-letter country codes
   ├─ Add dimensions für V07PAK
   ├─ Add print_format
   ├─ Call ShippingService::createLabel()
   └─ Update type='return' in DB
   ↓
5. Return-Label erstellt ✅

Logging-Beispiel

Erfolgreicher Fallback:

[2026-01-23 15:30:00] [DHL Returns] Creating return label
[2026-01-23 15:30:01] [DHL Returns] Using Returns API endpoint
[2026-01-23 15:30:02] ERROR: DHL API authentication failed
[2026-01-23 15:30:02] [DHL Returns] Returns API not available, falling back
[2026-01-23 15:30:02] [DHL Returns] Using regular Shipping API as fallback
[2026-01-23 15:30:02] [DHL Returns] Prepared shipment data for fallback
    {
        "product_code": "V07PAK",
        "shipper": {"country": "DE"},
        "consignee": {"country": "DE"},
        "dimensions": {"length": 120, "width": 60, "height": 60}
    }
[2026-01-23 15:30:03] [DHL API] Sending payload to DHL
[2026-01-23 15:30:04] [DHL API] Response received (200)
[2026-01-23 15:30:04] [DHL Returns] Return label created via Shipping API fallback
    shipmentNumber: 222209876543210

Geänderte Dateien

  1. packages/acme-laravel-dhl/src/Services/ReturnsService.php

    • createReturnViaRegularShipment() komplett überarbeitet
    • convertAddressFor2LetterCountry() hinzugefügt
    • Logging verbessert
  2. app/Jobs/CreateReturnLabelJob.php

    • Zusätzliches Logging hinzugefügt

Testen

Test-Szenario

# Return-Label erstellen
# Browser: Admin -> DHL Cockpit -> Outbound-Sendung -> "Retourenlabel erstellen"

# Logs live verfolgen:
tail -f storage/logs/laravel.log | grep "DHL Returns"

Erwartetes Ergebnis

  1. "Using regular Shipping API as fallback"
  2. "Prepared shipment data for fallback" mit korrekten Daten
  3. "Return label created via Shipping API fallback"
  4. Neue Sendung in DB mit type='return'
  5. Label-PDF herunterladbar

Verifikation in DB

SELECT 
    id, 
    dhl_shipment_no, 
    type, 
    related_shipment_id, 
    product_code,
    firstname,
    lastname,
    status
FROM dhl_package_shipments 
WHERE type = 'return' 
ORDER BY id DESC 
LIMIT 1;

Erwartung:

  • type = 'return'
  • related_shipment_id = ID der Original-Sendung
  • dhl_shipment_no = Neue Tracking-Nummer
  • status = 'created'

Häufige Fehler & Lösungen

Fehler: "country muss 2 Zeichen lang sein"

Lösung: Fixed durch convertAddressFor2LetterCountry()

Fehler: "0 of 1 shipment successfully printed"

Ursachen:

  • Fehlende Dimensions → Fixed
  • Falsches Country-Format → Fixed
  • Fehlender print_format → Fixed

Fehler: "Required field missing"

Prüfen:

  • Alle Pflichtfelder in shipper und consignee vorhanden?
  • weight_kg gesetzt?
  • product_code = 'V07PAK'?

Fix: V07PAK Produkt-Code Problem (23.01.2026 - 17:21)

Problem: "validationMessage":"The product entered is unknown." property":"product"

Ursache:

  • V07PAK (DHL Retoure Online) ist nicht für alle Accounts verfügbar
  • Benötigt spezielle Freischaltung oder Vertrag

Lösung: Verwende V01PAK (Standard DHL Paket) für Returns

'product_code' => 'V01PAK', // Standard DHL Paket (statt V07PAK)

Warum V01PAK funktioniert:

  • Standard-Produkt, für alle Accounts verfügbar
  • Mit vertauschten Adressen wird es automatisch als Retoure erkannt
  • Label funktioniert identisch
  • Tracking funktioniert identisch

Country-Code Hinweis:

  • ShippingService konvertiert selbst DE → DEU
  • Unsere Konvertierung DEU → DE ist trotzdem nötig für Validierung
  • Im finalen Payload steht korrekt "DEU"

Nächste Schritte

  1. Return-Label mit V01PAK testen
  2. Label-PDF herunterladen und prüfen
  3. Tracking-Nummer testen
  4. Bei Kunden testen (End-to-End)
  5. Optional: V07PAK-Berechtigung bei DHL beantragen