gruene-seele/dev/dhl-for-woocommerce/includes/REST_API/Deutsche_Post/Client.php
Kevin Adametz 0f641ef33a Housekeeping: offene Dev-Dateien eingecheckt + Sidebar „Bestellungen"→„Versand"
- Sidebar-Menü „Bestellungen" in „Versand" umbenannt (layout-sidenav), sonst nur
  Blade-Formatierung.
- Entwicklungspläne in ISO-Datumsformat umbenannt (entwicklungsplan-aktualisiert-*).
- dev/dhl-for-woocommerce/ (DHL-Plugin) + dev/partnercenter-api.zip + PastedGraphic
  hinzugefügt.
- .claude/ bewusst NICHT eingecheckt (lokale Claude-Code-Konfiguration).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-28 12:04:41 +00:00

465 lines
14 KiB
PHP

<?php
namespace PR\DHL\REST_API\Deutsche_Post;
use Exception;
use PR\DHL\REST_API\API_Client;
use PR\DHL\REST_API\Interfaces\API_Auth_Interface;
use PR\DHL\REST_API\Interfaces\API_Driver_Interface;
use stdClass;
/**
* The API client for Deutsche Post.
*
* @since [*next-version*]
*/
class Client extends API_Client
{
/**
* The customer EKP.
*
* @since [*next-version*]
*
* @var string
*/
protected $ekp;
/**
* The contact name to use for creating orders.
*
* @since [*next-version*]
*
* @var string
*/
protected $contact_name;
/**
* The contact phone number to use for creating orders for "Express" customers
*
* @since [*next-version*]
*
* @var string
*/
protected $contact_phone;
/**
* {@inheritdoc}
*
* @since [*next-version*]
*
* @param string $ekp The customer EKP.
* @param string $contact_name The contact name to use for creating orders.
* @param string $contact_phone The contact phone number to use for creating orders.
*/
public function __construct($ekp, $contact_name, $contact_phone, $base_url, API_Driver_Interface $driver, ?API_Auth_Interface $auth = null)
{
parent::__construct($base_url, $driver, $auth);
$this->ekp = $ekp;
$this->contact_name = $contact_name;
$this->contact_phone = $contact_phone;
}
/**
* Creates an item on the remote API.
*
* @since [*next-version*]
*
* @param Item_Info $item_info The information of the item to be created.
* @return stdClass The item information as returned by the remote API.
*
* @throws Exception
*/
public function create_item(Item_Info $item_info)
{
// Prepare the request route and data
$route = $this->customer_route('items');
$data = $this->item_info_to_request_data($item_info);
// Send the request and get the response
$response = $this->post($route, $data);
// Return the response body on success
if ($response->status === 200) {
return $response->body;
}
// Otherwise throw an exception using the response's error messages
$message = ! empty($response->body->messages)
? implode(', ', $response->body->messages)
: strval($response->body);
throw new Exception(
// Translators: %s is the error message from the API.
sprintf(esc_html__('API error: %s', 'dhl-for-woocommerce'), esc_html($message))
);
}
/**
* Deletes an item from the remote API.
*
* @since [*next-version*]
*
* @param int $item_id The ID of the item to delete.
* @return stdClass The response.
*
* @throws Exception
*/
public function delete_item($item_id)
{
// Compute the route to the API endpoint
$route = $this->customer_route('items/'.$item_id);
// Send the DELETE request
$response = $this->delete($route);
// Return the response body on success
if ($response->status === 200) {
return $response->body;
}
// Otherwise throw an exception using the response's error messages
$message = ! empty($response->body->messages)
? implode(', ', $response->body->messages)
: strval($response->body);
throw new Exception(
// Translators: %s is the error message from the API.
sprintf(esc_html__('API error: %s', 'dhl-for-woocommerce'), esc_html($message))
);
}
/**
* Retrieves the label for a DHL item, by its barcode.
*
* @param string $item_barcode The barcode of the item whose label to retrieve.
* @return string The raw PDF data for the item's label.
*
* @throws Exception
*/
public function get_item_label($item_barcode)
{
$route = sprintf('items/%s/label', $item_barcode);
$response = $this->get(
$this->customer_route($route),
[],
[
'Accept' => 'application/pdf',
]
);
if ($response->status === 200) {
return $response->body;
}
$message = ! empty($response->body->messages)
? implode(', ', $response->body->messages)
: strval($response->body);
throw new Exception(
// Translators: %s is the error message from the API.
sprintf(esc_html__('API error: %s', 'dhl-for-woocommerce'), esc_html($message))
);
}
/**
* Retrieves items from the remote API.
*
* @since [*next-version*]
*
* @return array The list of items.
*
* @throws Exception
*/
public function get_items()
{
$response = $this->get($this->customer_route('items'));
if ($response->status === 200) {
return (array) $response->body;
}
throw new Exception(
sprintf(
// Translators: %s is the error message from the API.
esc_html__('Failed to get items from the API: %s', 'dhl-for-woocommerce'),
esc_html(implode(', ', $response->body->messages))
)
);
}
/**
* Retrieves the current DHL order, or an existing one if an ID is given.
*
* @since [*next-version*]
*
* @param int|null $orderId Optional DHL order ID.
* @return array
*/
public function get_order($orderId = null)
{
$current = get_option('pr_dhl_dp_order', $this->get_default_order_info());
if (empty($orderId)) {
return $current;
}
return get_option('pr_dhl_dp_order_'.$orderId, $current);
}
/**
* Adds an item to the current DHL order.
*
* @since [*next-version*]
*
* @param string $item_barcode The barcode of the item to add.
* @param string $wc_order The ID of the WooCommerce order.
*/
public function add_item_to_order($item_barcode, $wc_order)
{
$order = $this->get_order();
$order['items'][$item_barcode] = $wc_order;
update_option('pr_dhl_dp_order', $order);
}
/**
* Adds an item to the current DHL order.
*
* @since [*next-version*]
*
* @param string $item_barcode The barcode of the item to add.
*/
public function remove_item_from_order($item_barcode)
{
$order = $this->get_order();
unset($order['items'][$item_barcode]);
update_option('pr_dhl_dp_order', $order);
}
/**
* Resets the current order.
*
* @since [*next-version*]
*/
public function reset_current_order()
{
update_option('pr_dhl_dp_order', $this->get_default_order_info());
}
/**
* Creates the Deutsche Post order for the current local order of items.
*
* @since [*next-version*]
*
* @return object The response data.
*
* @throws Exception
*/
public function create_order($copy_count = 1)
{
$order = $this->get_order();
$items = $order['items'];
$barcodes = array_keys($items);
$copy_count = intval($copy_count);
$route = $this->customer_route('orders');
$data = [
'itemBarcodes' => $barcodes,
'paperwork' => [
'awbCopyCount' => $copy_count,
'contactName' => $this->contact_name,
'telephoneNumber' => $this->contact_phone,
],
];
$response = $this->post($route, $data);
if ($response->status === 200) {
$this->close_order($response->body);
return $response->body;
}
throw new Exception(
sprintf(
// Translators: %s is the error message from the API.
esc_html__('Failed to create order: %s', 'dhl-for-woocommerce'),
esc_html(implode(', ', $response->body->messages))
)
);
}
/**
* Closes the current order with information received from the REST API.
*
* @since [*next-version*]
*
* @param object $info The information received from the REST API.
*/
protected function close_order($info)
{
// Get the current order from the options
$order = $this->get_order();
// Save the response info in the order
$order['id'] = $info->orderId;
$order['shipments'] = $info->shipments;
// Save the order status if it's given
if (! empty($info->orderStatus)) {
$order['status'] = $info->orderStatus;
}
// Save the order in a new option
update_option('pr_dhl_dp_order_'.$info->orderId, $order);
// Reset the current order
$this->reset_current_order();
}
/**
* Retrieves the label for a given AWB
*
* @since [*next-version*]
*
* @param string $awb The AWB of the shipment.
* @return string The raw PDF data for the AWB label.
*
* @throws Exception
*/
public function get_awb_label($awb)
{
$response = $this->get(
sprintf('dpi/shipping/v1/shipments/%s/awblabels', $awb),
[],
[
'Accept' => 'application/pdf',
]
);
if ($response->status === 200) {
return $response->body;
}
$message = ! empty($response->body->messages)
? implode(', ', $response->body->messages)
: strval($response->body);
throw new Exception(
// Translators: %s is the error message from the API.
sprintf(esc_html__('API error: %s', 'dhl-for-woocommerce'), esc_html($message))
);
}
/**
* Retrieves tracking info for a shipment by its AWB
*
* @since [*next-version*]
*
* @param string $awb The AWB of the shipment.
* @return object The tracking info as returned by the remote API.
*
* @throws Exception
*/
public function get_shipment_tracking_info($awb)
{
$response = $this->get(sprintf('dpi/tracking/v1/trackings/awb/%s', $awb));
if ($response->status === 200) {
return $response->body;
}
$message = ! empty($response->body->messages)
? implode(', ', $response->body->messages)
: strval($response->body);
throw new Exception(
// Translators: %s is the error message from the API.
sprintf(esc_html__('API error: %s', 'dhl-for-woocommerce'), esc_html($message))
);
}
/**
* Transforms an item info object into a request data array.
*
* @param Item_Info $item_info The item info object to transform.
* @return array The request data for the given item info object.
*/
protected function item_info_to_request_data(Item_Info $item_info)
{
$contents = [];
foreach ($item_info->contents as $content_info) {
$item_desc = $content_info['description'];
if (! empty($content_info['description_export'])) {
$item_desc = $content_info['description_export'];
}
$data = [
'contentPieceAmount' => $content_info['qty'],
'contentPieceDescription' => $item_desc,
'contentPieceIndexNumber' => $content_info['product_id'],
'contentPieceNetweight' => $content_info['weight'],
'contentPieceOrigin' => $content_info['origin'],
'contentPieceValue' => $content_info['value'],
'contentPieceHsCode' => trim($content_info['hs_code']),
];
// Only include HS code if it's not empty
if (empty($data['contentPieceHsCode'])) {
unset($data['contentPieceHsCode']);
}
$contents[] = $data;
}
return [
'serviceLevel' => $item_info->shipment['service_level'],
'product' => $item_info->shipment['product'],
'custRef' => $item_info->shipment['label_ref'],
'custRef2' => $item_info->shipment['label_ref_2'],
'shipmentAmount' => $item_info->shipment['value'],
'shipmentCurrency' => $item_info->shipment['currency'],
'shipmentGrossWeight' => $item_info->shipment['weight'],
'shipmentNaturetype' => $item_info->shipment['nature_type'],
'returnItemWanted' => $item_info->shipment['packet_return'],
'importerTaxId' => $item_info->shipment['importer_taxid'],
'senderTaxId' => $item_info->shipment['sender_taxid'],
'recipient' => $item_info->recipient['name'],
'recipientPhone' => $item_info->recipient['phone'],
'recipientEmail' => $item_info->recipient['email'],
'addressLine1' => $item_info->recipient['address_1'],
'addressLine2' => $item_info->recipient['address_2'],
'city' => $item_info->recipient['city'],
'postalCode' => $item_info->recipient['postcode'],
'state' => $item_info->recipient['state'],
'destinationCountry' => $item_info->recipient['country'],
'contents' => $contents,
];
}
/**
* @return array
*/
protected function get_default_order_info()
{
return [
'id' => null,
'status' => null,
'items' => [],
'shipments' => [],
];
}
/**
* Prepares an API route with the customer namespace and EKP.
*
* @since [*next-version*]
*
* @param string $route The route to prepare.
* @return string
*/
protected function customer_route($route)
{
return sprintf('dpi/shipping/v1/customers/%s/%s', $this->ekp, $route);
}
}