From 4172cf01a1040f526b49d21ec90f67d57a9399aa Mon Sep 17 00:00:00 2001 From: Tadas Labutis Date: Thu, 9 Jul 2026 09:41:51 +0300 Subject: [PATCH] DGS-431 DGS-432 DGS-433 Improve DPD shipment creation error messages - DGS-431: reword the shipment creation failure message into a clear sentence - DGS-432: detect DPD authentication errors and show an actionable message pointing to the web service API credentials in Basic Settings (new ApiErrorUtility + unit test) - DGS-433: move variables out of the translated strings so shipment creation errors translate to the shop language --- CHANGELOG.md | 5 +++ src/Service/Exception/ExceptionService.php | 13 ++++++ src/Service/Label/LabelPrintingService.php | 15 ++++--- src/Util/ApiErrorUtility.php | 50 ++++++++++++++++++++++ tests/Unit/Util/ApiErrorUtilityTest.php | 47 ++++++++++++++++++++ tests/Unit/Util/index.php | 30 +++++++++++++ 6 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 src/Util/ApiErrorUtility.php create mode 100644 tests/Unit/Util/ApiErrorUtilityTest.php create mode 100644 tests/Unit/Util/index.php diff --git a/CHANGELOG.md b/CHANGELOG.md index d64c9d10..97f5ba97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -204,3 +204,8 @@ - Added timeframes to shipment request - Fixed phone area code, city and street select width rendering as 0px when switching carriers in checkout - Fixed DPDBaltics menu item disappearing from sidebar when navigating to module pages + +## [3.3.2] +- Improved shipment creation error message to be a clear, readable sentence +- Improved DPD API authentication error to explain that the API credentials in Basic Settings were rejected +- Fixed shipment creation error messages not being translated to the shop language diff --git a/src/Service/Exception/ExceptionService.php b/src/Service/Exception/ExceptionService.php index 6806517f..f4e5a502 100644 --- a/src/Service/Exception/ExceptionService.php +++ b/src/Service/Exception/ExceptionService.php @@ -23,6 +23,7 @@ use DPDBaltics; use Exception; +use Invertus\dpdBaltics\Util\ApiErrorUtility; use Invertus\dpdBaltics\Validate\ShipmentData\Exception\InvalidShipmentDataField; use Invertus\dpdBalticsApi\Exception\DPDBalticsAPIException; @@ -64,6 +65,18 @@ public function getAPIErrorMessages() ]; } + public function getApiErrorMessage($rawErrorLog) + { + if (ApiErrorUtility::isAuthenticationError($rawErrorLog)) { + return $this->module->l( + 'DPD rejected the API credentials. Please check that the API username and password in Basic Settings match the web service credentials provided by DPD, not your DPD web portal login.', + self::SHORT_CLASS_NAME + ); + } + + return $rawErrorLog; + } + public function getShipmentFieldErrorMessages() { return [ diff --git a/src/Service/Label/LabelPrintingService.php b/src/Service/Label/LabelPrintingService.php index a369d185..99e95fba 100644 --- a/src/Service/Label/LabelPrintingService.php +++ b/src/Service/Label/LabelPrintingService.php @@ -115,17 +115,22 @@ public function printAndSaveLabel(ShipmentData $shipmentData, $shipmentId, $orde return $response; } catch (Exception $e) { - $exception = $e->getMessage(). ' ID order: '. $orderId; - $response['message'] = $this->module->l("Failed to create DPD shipment: {$exception}"); + $response['message'] = sprintf( + $this->module->l('Failed to create DPD shipment: %s (order ID: %s)'), + $e->getMessage(), + $orderId + ); $this->logger->error($response['message']); return $response; } if ($shipmentCreationResponse->getStatus() !== Config::API_SUCCESS_STATUS) { - $exception = $shipmentCreationResponse->getErrLog(). ' ID order: '. $orderId; - $response['message'] = $this->module->l( - "Failed to create DPD shipment API status failed : {$exception}" + $apiError = $this->exceptionService->getApiErrorMessage($shipmentCreationResponse->getErrLog()); + $response['message'] = sprintf( + $this->module->l('Failed to create DPD shipment: %s (order ID: %s)'), + $apiError, + $orderId ); $this->logger->error($response['message']); diff --git a/src/Util/ApiErrorUtility.php b/src/Util/ApiErrorUtility.php new file mode 100644 index 00000000..08ee225d --- /dev/null +++ b/src/Util/ApiErrorUtility.php @@ -0,0 +1,50 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +declare(strict_types=1); + + +namespace Invertus\dpdBaltics\Util; + +if (!defined('_PS_VERSION_')) { + exit; +} + +class ApiErrorUtility +{ + const AUTHENTICATION_ERROR_PATTERNS = [ + 'unable to find user to authenticate', + 'authentication failed', + 'invalid username or password', + ]; + + public static function isAuthenticationError($rawErrorLog) + { + $haystack = mb_strtolower((string) $rawErrorLog); + + foreach (self::AUTHENTICATION_ERROR_PATTERNS as $pattern) { + if (false !== strpos($haystack, $pattern)) { + return true; + } + } + + return false; + } +} diff --git a/tests/Unit/Util/ApiErrorUtilityTest.php b/tests/Unit/Util/ApiErrorUtilityTest.php new file mode 100644 index 00000000..9571b142 --- /dev/null +++ b/tests/Unit/Util/ApiErrorUtilityTest.php @@ -0,0 +1,47 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + + +use Invertus\dpdBaltics\Util\ApiErrorUtility; +use PHPUnit\Framework\TestCase; + +class ApiErrorUtilityTest extends TestCase +{ + /** + * @dataProvider authenticationErrorProvider + */ + public function testIsAuthenticationError($rawErrorLog, $expected) + { + $this->assertSame($expected, ApiErrorUtility::isAuthenticationError($rawErrorLog)); + } + + public function authenticationErrorProvider() + { + return [ + 'exact DPD message' => ['Unable to find user to authenticate!', true], + 'different casing' => ['unable to FIND user to authenticate', true], + 'authentication failed' => ['Authentication failed', true], + 'invalid credentials' => ['Invalid username or password', true], + 'unrelated error' => ['Parcel weight is required', false], + 'empty string' => ['', false], + 'null' => [null, false], + ]; + } +} diff --git a/tests/Unit/Util/index.php b/tests/Unit/Util/index.php new file mode 100644 index 00000000..58b8b473 --- /dev/null +++ b/tests/Unit/Util/index.php @@ -0,0 +1,30 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + + +header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); +header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); + +header('Cache-Control: no-store, no-cache, must-revalidate'); +header('Cache-Control: post-check=0, pre-check=0', false); +header('Pragma: no-cache'); + +header('Location: ../'); +exit;