gruene-seele/dev/dhl-for-woocommerce/includes/pr-dhl-api/class-pr-dhl-api-deutsche-post.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

755 lines
23 KiB
PHP

<?php
use PR\DHL\REST_API\Deutsche_Post\Auth;
use PR\DHL\REST_API\Deutsche_Post\Client;
use PR\DHL\REST_API\Deutsche_Post\Item_Info;
use PR\DHL\REST_API\Drivers\JSON_API_Driver;
use PR\DHL\REST_API\Drivers\Logging_Driver;
use PR\DHL\REST_API\Drivers\WP_API_Driver;
use PR\DHL\REST_API\Interfaces\API_Auth_Interface;
use PR\DHL\REST_API\Interfaces\API_Driver_Interface;
// Exit if accessed directly or class already exists
if (! defined('ABSPATH') || class_exists('PR_DHL_API_Deutsche_Post', false)) {
return;
}
class PR_DHL_API_Deutsche_Post extends PR_DHL_API
{
/**
* The URL to the API.
*
* @since [*next-version*]
*/
const API_URL_PRODUCTION = 'https://api.dhl.com/';
/**
* The URL to the sandbox API.
*
* @since [*next-version*]
*/
const API_URL_SANDBOX = 'https://api-sandbox.dhl.com/';
/**
* The transient name where the API access token is stored.
*
* @since [*next-version*]
*/
const ACCESS_TOKEN_TRANSIENT = 'pr_dhl_deutsche_post_access_token';
/**
* The API driver instance.
*
* @since [*next-version*]
*
* @var API_Driver_Interface
*/
public $api_driver;
/**
* The API authorization instance.
*
* @since [*next-version*]
*
* @var Auth
*/
public $api_auth;
/**
* The API client instance.
*
* @since [*next-version*]
*
* @var Client
*/
public $api_client;
/**
* Constructor.
*
* @since [*next-version*]
*
* @param string $country_code The country code.
*
* @throws Exception If an error occurred while creating the API driver, auth or client.
*/
public function __construct($country_code)
{
$this->country_code = $country_code;
try {
$this->api_driver = $this->create_api_driver();
$this->api_auth = $this->create_api_auth();
$this->api_client = $this->create_api_client();
} catch (Exception $e) {
throw $e;
}
}
/**
* Initializes the API client instance.
*
* @since [*next-version*]
*
* @return Client
*
* @throws Exception If failed to create the API client.
*/
protected function create_api_client()
{
// Create the API client, using this instance's driver and auth objects
return new Client(
$this->get_ekp(),
$this->get_contact_name(),
$this->get_contact_phone(),
$this->get_api_url(),
$this->api_driver,
$this->api_auth
);
}
/**
* Initializes the API driver instance.
*
* @since [*next-version*]
*
* @return API_Driver_Interface
*
* @throws Exception If failed to create the API driver.
*/
protected function create_api_driver()
{
// Use a standard WordPress-driven API driver to send requests using WordPress' functions
$driver = new WP_API_Driver;
// This will log requests given to the original driver and log responses returned from it
$driver = new Logging_Driver(PR_DHL(), $driver);
// This will prepare requests given to the previous driver for JSON content
// and parse responses returned from it as JSON.
$driver = new JSON_API_Driver($driver);
// , decorated using the JSON driver decorator class
return $driver;
}
/**
* Initializes the API auth instance.
*
* @since [*next-version*]
*
* @return API_Auth_Interface
*
* @throws Exception If failed to create the API auth.
*/
protected function create_api_auth()
{
// Get the saved DHL customer API credentials
[$client_id, $client_secret] = $this->get_api_creds();
// Create the auth object using this instance's API driver and URL
return new Auth(
$this->api_driver,
$this->get_api_url(),
$client_id,
$client_secret,
static::ACCESS_TOKEN_TRANSIENT
);
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function is_dhl_deutsche_post()
{
return true;
}
/**
* Retrieves the API URL.
*
* @since [*next-version*]
*
* @return string
*
* @throws Exception If failed to determine if using the sandbox API or not.
*/
public function get_api_url()
{
$is_sandbox = $this->get_setting('dhl_sandbox');
$is_sandbox = filter_var($is_sandbox, FILTER_VALIDATE_BOOLEAN);
$api_url = ($is_sandbox) ? static::API_URL_SANDBOX : static::API_URL_PRODUCTION;
return $api_url;
}
/**
* Retrieves the API credentials.
*
* @since [*next-version*]
*
* @return array The client ID and client secret.
*
* @throws Exception If failed to retrieve the API credentials.
*/
public function get_api_creds()
{
return [
$this->get_setting('dhl_api_key'),
$this->get_setting('dhl_api_secret'),
];
}
/**
* Retrieves the DHL account number, or "EKP".
*
* @since [*next-version*]
*
* @return string
*
* @throws Exception If failed to retrieve the EKP from the settings.
*/
public function get_ekp()
{
return $this->get_setting('dhl_account_num');
}
/**
* Retrieves the DHL contact name for orders.
*
* @since [*next-version*]
*
* @return string
*
* @throws Exception If failed to retrieve the contact name from the settings.
*/
public function get_contact_name()
{
return $this->get_setting('dhl_contact_name');
}
/**
* Retrieves the DHL contact name for orders.
*
* @since [*next-version*]
*
* @return string
*
* @throws Exception If failed to retrieve the contact name from the settings.
*/
public function get_contact_phone()
{
return $this->get_setting('dhl_contact_phone_number');
}
/**
* Retrieves a single setting.
*
* @since [*next-version*]
*
* @param string $key The key of the setting to retrieve.
* @param string $default The value to return if the setting is not saved.
* @return mixed The setting value.
*/
public function get_setting($key, $default = '')
{
$settings = $this->get_settings();
return isset($settings[$key]) ? $settings[$key] : $default;
}
/**
* Retrieves all of the Deutsche Post settings.
*
* @since [*next-version*]
*
* @return array An associative array of the settings keys mapping to their values.
*/
public function get_settings()
{
return get_option('woocommerce_pr_dhl_dp_settings', []);
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function dhl_test_connection($client_id, $client_secret)
{
try {
// Test the given ID and secret
$token = $this->api_auth->test_connection($client_id, $client_secret);
// Save the token if successful
$this->api_auth->save_token($token);
return $token;
} catch (Exception $e) {
$this->api_auth->save_token(null);
throw $e;
}
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function dhl_reset_connection()
{
return $this->api_auth->revoke();
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function get_dhl_products_international()
{
return [
'GMP-STANDARD' => esc_html__('Packet Standard', 'dhl-for-woocommerce'),
'GMP' => esc_html__('Packet Priority', 'dhl-for-woocommerce'),
'GPP' => esc_html__('Packet Plus', 'dhl-for-woocommerce'),
'GPT' => esc_html__('Packet Tracked', 'dhl-for-woocommerce'),
];
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function get_dhl_products_domestic()
{
return $this->get_dhl_products_international();
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function get_dhl_label($args)
{
$order_id = isset($args['order_details']['order_id'])
? $args['order_details']['order_id']
: null;
$order = wc_get_order($order_id);
$item_barcode = $order->get_meta('pr_dhl_dp_item_barcode');
// If order has no saved barcode, create the DHL item and get the barcode
if (empty($item_barcode)) {
$uom = get_option('woocommerce_weight_unit');
try {
$item_info = new Item_Info($args, $uom);
} catch (Exception $e) {
throw $e;
}
// Create the item and get the barcode
$item_response = $this->api_client->create_item($item_info);
$item_barcode = $item_response->barcode;
$item_id = $item_response->id;
// Save it in the order
$order->update_meta_data('pr_dhl_dp_item_barcode', $item_barcode);
$order->update_meta_data('pr_dhl_dp_item_id', $item_id);
$order->save();
}
// Get the label for the created item
$label_pdf_data = $this->api_client->get_item_label($item_barcode);
// Save the label to a file
$this->save_dhl_label_file('item', $item_barcode, $label_pdf_data);
return [
'label_path' => $this->get_dhl_item_label_file_info($item_barcode)->path,
'item_barcode' => $item_barcode,
'tracking_number' => $item_barcode,
'tracking_status' => '',
];
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function delete_dhl_label($label_info)
{
if (! isset($label_info['label_path'])) {
throw new Exception(esc_html__('DHL Label has no path!', 'dhl-for-woocommerce'));
}
$label_path = $label_info['label_path'];
if (file_exists($label_path)) {
$res = wp_delete_file($label_path);
if (! $res) {
throw new Exception(esc_html__('DHL Label could not be deleted!', 'dhl-for-woocommerce'));
}
}
}
/**
* Retrieves the filename for DHL item label files.
*
* @since [*next-version*]
*
* @param string $barcode The DHL item barcode.
* @param string $format The file format.
* @return string
*/
public function get_dhl_item_label_file_name($barcode, $format = 'pdf')
{
// Translators: %1$s is the barcode number, and %2$s is the file format (e.g., pdf, jpg).
return sprintf('dhl-label-%1$s.%2$s', $barcode, $format);
}
/**
* Retrieves the filename for DHL AWB label files.
*
* @since [*next-version*]
*
* @param string $awb The AWB.
* @param string $format The file format.
* @return string
*/
public function get_dhl_awb_label_file_name($awb, $format = 'pdf')
{
// Translators: %1$s is the Air Waybill (AWB) number and %2$s is the file format (e.g., pdf, jpg).
return sprintf('dhl-label-awb-%1$s.%2$s', $awb, $format);
}
/**
* Retrieves the filename for DHL order label files (a.k.a. merged AWB label files).
*
* @since [*next-version*]
*
* @param string $order_id The DHL order ID.
* @param string $format The file format.
* @return string
*/
public function get_dhl_order_label_file_name($order_id, $format = 'pdf')
{
// Translators: %1$s is the order ID and %2$s is the file format (e.g., pdf, jpg).
return sprintf('dhl-waybill-order-%1$s.%2$s', $order_id, $format);
}
/**
* Retrieves the file info for a DHL item label file.
*
* @since [*next-version*]
*
* @param string $barcode The DHL item barcode.
* @param string $format The file format.
* @return object An object containing the file "path" and "url" strings.
*/
public function get_dhl_item_label_file_info($barcode, $format = 'pdf')
{
$file_name = $this->get_dhl_item_label_file_name($barcode, $format);
return (object) [
'path' => PR_DHL()->get_dhl_label_folder_dir().$file_name,
'url' => PR_DHL()->get_dhl_label_folder_url().$file_name,
];
}
/**
* Retrieves the file info for DHL AWB label files.
*
* @since [*next-version*]
*
* @param string $awb The AWB.
* @param string $format The file format.
* @return object An object containing the file "path" and "url" strings.
*/
public function get_dhl_awb_label_file_info($awb, $format = 'pdf')
{
$file_name = $this->get_dhl_awb_label_file_name($awb, $format);
return (object) [
'path' => PR_DHL()->get_dhl_label_folder_dir().$file_name,
'url' => PR_DHL()->get_dhl_label_folder_url().$file_name,
];
}
/**
* Retrieves the file info for DHL order label files (a.k.a. merged AWB label files).
*
* @since [*next-version*]
*
* @param string $order_id The DHL order ID.
* @param string $format The file format.
* @return object An object containing the file "path" and "url" strings.
*/
public function get_dhl_order_label_file_info($order_id, $format = 'pdf')
{
$file_name = $this->get_dhl_order_label_file_name($order_id, $format);
return (object) [
'path' => PR_DHL()->get_dhl_label_folder_dir().$file_name,
'url' => PR_DHL()->get_dhl_label_folder_url().$file_name,
];
}
/**
* Retrieves the file info for any DHL label file, based on type.
*
* @since [*next-version*]
*
* @param string $type The label type: "item", "awb" or "order".
* @param string $key The key: barcode for type "item", AWB for type "awb" and order ID for type "order".
* @return object An object containing the file "path" and "url" strings.
*/
public function get_dhl_label_file_info($type, $key)
{
// Return file info for "awb" type
if ($type === 'awb') {
return $this->get_dhl_awb_label_file_info($key);
}
// Return file info for "order" type
if ($type === 'order') {
return $this->get_dhl_order_label_file_info($key);
}
// Return info for "item" type
return $this->get_dhl_item_label_file_info($key);
}
/**
* Saves an item label file.
*
* @since [*next-version*]
*
* @param string $type The label type: "item", "awb" or "order".
* @param string $key The key: barcode for type "item", AWB for type "awb" and order ID for type "order".
* @param string $data The label file data.
* @return object The info for the saved label file, containing the "path" and "url".
*
* @throws Exception If failed to save the label file.
*/
public function save_dhl_label_file($type, $key, $data)
{
// Get the file info based on type
$file_info = $this->get_dhl_label_file_info($type, $key);
// Validate the file path
if (validate_file($file_info->path) > 0 && validate_file($file_info->path) !== 2) {
throw new Exception(esc_html__('Invalid file path!', 'dhl-for-woocommerce'));
}
$file_ret = file_put_contents($file_info->path, $data);
if (empty($file_ret)) {
throw new Exception(esc_html__('DHL label file cannot be saved!', 'dhl-for-woocommerce'));
}
return $file_info;
}
/**
* Deletes an AWB label file.
*
* @since [*next-version*]
*
* @param string $type The label type: "item", "awb" or "order".
* @param string $key The key: barcode for type "item", AWB for type "awb" and order ID for type "order".
*
* @throws Exception If the file could not be deleted.
*/
public function delete_dhl_label_file($type, $key)
{
// Get the file info based on type
$file_info = $this->get_dhl_label_file_info($type, $key);
// Do nothing if file does not exist
if (! file_exists($file_info->path)) {
return;
}
// Attempt to delete the file
$res = wp_delete_file($file_info->path);
// Throw error if the file could not be deleted
if (! $res) {
throw new Exception(esc_html__('DHL AWB Label could not be deleted!', 'dhl-for-woocommerce'));
}
}
/**
* Checks if an AWB label file already exist, and if not fetches it from the API and saves it.
*
* @since [*next-version*]
*
* @param string $awb The AWB.
* @return object An object containing the "path" and "url" to the label file.
*
* @throws Exception
*/
public function create_dhl_awb_label_file($awb)
{
$file_info = $this->get_dhl_awb_label_file_info($awb);
// Skip creating the file if it already exists
if (file_exists($file_info->path)) {
return $file_info;
}
// Get the label data from the API client
$label_data = $this->api_client->get_awb_label($awb);
// Save the label file
$this->save_dhl_label_file('awb', $awb, $label_data);
return $file_info;
}
/**
* Checks if an order label file already exist, and if not fetches it from the API and saves it.
*
* @since [*next-version*]
*
* @param string $order_id The DHL order ID.
* @return object An object containing the "path" and "url" to the label file.
*
* @throws Exception
*/
public function create_dhl_order_label_file($order_id)
{
$file_info = $this->get_dhl_order_label_file_info($order_id);
// Skip creating the file if it already exists
if (file_exists($file_info->path)) {
return $file_info;
}
// Get the order with the given ID
$order = $this->api_client->get_order($order_id);
if ($order === null) {
// Translators: %s is replaced with the order ID.
throw new Exception(esc_html(sprintf('DHL order %s does not exist.', $order_id)));
}
// For multiple shipments, maybe create each label file and then merge them
$loader = PR_DHL_Libraryloader::instance();
$pdfMerger = $loader->get_pdf_merger();
if ($pdfMerger === null) {
throw new Exception(esc_html__('Library conflict, could not merge PDF files. Please download PDF files individually.', 'dhl-for-woocommerce'));
}
foreach ($order['shipments'] as $shipment) {
// Create the single AWB label file
$awb_label_info = $this->create_dhl_awb_label_file($shipment->awb);
// Ensure file exists
if (! file_exists($awb_label_info->path)) {
continue;
}
// Ensure it is a PDF file
$ext = pathinfo($awb_label_info->path, PATHINFO_EXTENSION);
if (stripos($ext, 'pdf') === false) {
throw new Exception(esc_html__('Not all the file formats are the same.', 'dhl-for-woocommerce'));
}
// Add to merge queue
$pdfMerger->addPDF($awb_label_info->path, 'all');
}
// Merge all files in the queue
$pdfMerger->merge('file', $file_info->path);
return $file_info;
}
/**
* {@inheritdoc}
*
* @since [*next-version*]
*/
public function dhl_validate_field($key, $value) {}
/**
* Finalizes and creates the current Deutsche Post order.
*
* @since [*next-version*]
*
* @return string The ID of the created DHL order.
*
* @throws Exception If an error occurred while and the API failed to create the order.
*/
public function create_order($copy_count = 1)
{
// Create the DHL order
$response = $this->api_client->create_order($copy_count);
$this->get_settings();
// Get the current DHL order - the one that was just submitted
$order = $this->api_client->get_order($response->orderId);
$order_items = $order['items'];
// Get the tracking note type setting
// $tracking_note_type = $this->get_setting('dhl_tracking_note', 'customer');
// $tracking_note_type = ($tracking_note_type == 'yes') ? '' : 'customer';
// Go through the shipments retrieved from the API and save the AWB of the shipment to
// each DHL item's associated WooCommerce order in post meta. This will make sure that each
// WooCommerce order has a reference to the its DHL shipment AWB.
// At the same time, we will be collecting the AWBs to merge the label PDFs later on, as well
// as adding order notes for the AWB to each WC order.
$awbs = [];
foreach ($response->shipments as $shipment) {
foreach ($shipment->items as $item) {
if (! isset($order_items[$item->barcode])) {
continue;
}
// Get the WC order for this DHL item
$item_wc_order_id = $order_items[$item->barcode];
$item_wc_order = wc_get_order($item_wc_order_id);
// Save the AWB to the WC order
$item_wc_order->update_meta_data('pr_dhl_dp_awb', $shipment->awb);
// An an order note for the AWB
$item_awb_note = esc_html__('Shipment AWB: ', 'dhl-for-woocommerce').$shipment->awb;
// 'type' should alwasys be private for AWB
$item_wc_order->add_order_note($item_awb_note, '', true);
// Save the AWB in the list.
$awbs[] = $shipment->awb;
// Save the DHL order ID in the WC order meta
$item_wc_order->update_meta_data('pr_dhl_dp_order', $response->orderId);
$item_wc_order->save();
}
}
// Generate the merged AWB label file
$this->create_dhl_order_label_file($response->orderId);
return $response->orderId;
}
public function get_dhl_nature_type()
{
return [
'SALE_GOODS' => esc_html__('Sale Goods', 'dhl-for-woocommerce'),
'RETURN_GOODS' => esc_html__('Return Goods', 'dhl-for-woocommerce'),
'GIFT' => esc_html__('Gift', 'dhl-for-woocommerce'),
'COMMERCIAL_SAMPLE' => esc_html__('Commercial Sample', 'dhl-for-woocommerce'),
'DOCUMENTS' => esc_html__('Documents', 'dhl-for-woocommerce'),
'MIXED_CONTENTS' => esc_html__('Mixed Contents', 'dhl-for-woocommerce'),
'OTHERS' => esc_html__('Others', 'dhl-for-woocommerce'),
];
}
}