From d61758c195cf0f5a4c6bd452d733e542f28098d1 Mon Sep 17 00:00:00 2001
From: m-muxfeld-diw <143803170+m-muxfeld-diw@users.noreply.github.com>
Date: Thu, 8 Aug 2024 10:06:03 +0200
Subject: [PATCH] PISHPS-329: added UrlParsingService to separate tracking code
from tracking url when not entered correctly (#805)
---
src/Resources/config/services.xml | 6 +-
src/Resources/config/services/services.xml | 4 +-
.../MollieApi/LineItemDataExtractor.php | 91 ++--------
src/Service/TrackingInfoStructFactory.php | 24 ++-
src/Service/UrlParsingService.php | 131 +++++++++++++++
src/Subscriber/OrderDeliverySubscriber.php | 2 +-
.../ShipmentManager/ShipmentManagerTest.php | 3 +-
.../Builder/AbstractMollieOrderBuilder.php | 3 +-
.../Builder/MollieLineItemBuilderTest.php | 3 +-
.../MollieApi/LineItemDataExtractorTest.php | 11 +-
.../Service/TrackingInfoStructFactoryTest.php | 31 +---
.../PHPUnit/Service/UrlParsingServiceTest.php | 159 ++++++++++++++++++
.../Utils/Traits/PaymentBuilderTrait.php | 3 +-
13 files changed, 343 insertions(+), 128 deletions(-)
create mode 100644 src/Service/UrlParsingService.php
create mode 100644 tests/PHPUnit/Service/UrlParsingServiceTest.php
diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml
index 7f61d88d6..5f0063151 100644
--- a/src/Resources/config/services.xml
+++ b/src/Resources/config/services.xml
@@ -176,7 +176,11 @@
%env(default::APP_URL)%
-
+
+
+
+
+
diff --git a/src/Resources/config/services/services.xml b/src/Resources/config/services/services.xml
index e0f4f20c4..29b206deb 100644
--- a/src/Resources/config/services/services.xml
+++ b/src/Resources/config/services/services.xml
@@ -113,7 +113,9 @@
-
+
diff --git a/src/Service/MollieApi/LineItemDataExtractor.php b/src/Service/MollieApi/LineItemDataExtractor.php
index 2ae07eb1a..24191d344 100644
--- a/src/Service/MollieApi/LineItemDataExtractor.php
+++ b/src/Service/MollieApi/LineItemDataExtractor.php
@@ -2,6 +2,7 @@
namespace Kiener\MolliePayments\Service\MollieApi;
+use Kiener\MolliePayments\Service\UrlParsingService;
use Kiener\MolliePayments\Struct\LineItemExtraData;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Content\Media\MediaEntity;
@@ -13,6 +14,15 @@
class LineItemDataExtractor
{
+ /**
+ * @var UrlParsingService
+ */
+ private $urlParsingService;
+ public function __construct(UrlParsingService $urlParsingService)
+ {
+ $this->urlParsingService = $urlParsingService;
+ }
+
public function extractExtraData(OrderLineItemEntity $lineItem): LineItemExtraData
{
$product = $lineItem->getProduct();
@@ -30,7 +40,7 @@ public function extractExtraData(OrderLineItemEntity $lineItem): LineItemExtraDa
&& $medias->first()->getMedia() instanceof MediaEntity
) {
$url = $medias->first()->getMedia()->getUrl();
- $url = $this->encodePathAndQuery($url);
+ $url = $this->urlParsingService->encodePathAndQuery($url);
$extraData->setImageUrl($url);
}
@@ -43,83 +53,4 @@ public function extractExtraData(OrderLineItemEntity $lineItem): LineItemExtraDa
return $extraData;
}
-
- private function encodePathAndQuery(string $fullUrl):string
- {
- $urlParts = parse_url($fullUrl);
-
- $scheme = isset($urlParts['scheme']) ? $urlParts['scheme'] . '://' : '';
-
- $host = isset($urlParts['host']) ? $urlParts['host'] : '';
-
- $port = isset($urlParts['port']) ? ':' . $urlParts['port'] : '';
-
- $user = isset($urlParts['user']) ? $urlParts['user'] : '';
-
- $pass = isset($urlParts['pass']) ? ':' . $urlParts['pass'] : '';
-
- $pass = ($user || $pass) ? "$pass@" : '';
-
- $path = isset($urlParts['path']) ? $urlParts['path'] : '';
-
- if (mb_strlen($path) > 0) {
- $pathParts = explode('/', $path);
- array_walk($pathParts, function (&$pathPart) {
- $pathPart = rawurlencode($pathPart);
- });
- $path = implode('/', $pathParts);
- }
-
- $query = '';
- if (isset($urlParts['query'])) {
- $urlParts['query'] = $this->sanitizeQuery(explode('&', $urlParts['query']));
- $query = '?' . implode('&', $urlParts['query']);
- }
-
-
- $fragment = isset($urlParts['fragment']) ? '#' . $urlParts['fragment'] : '';
-
- return trim($scheme.$user.$pass.$host.$port.$path.$query.$fragment);
- }
-
- /**
- * Sanitizes an array of query strings by URL encoding their components.
- *
- * This method takes an array of query strings, where each string is expected to be in the format
- * 'key=value'. It applies the sanitizeQueryPart method to each query string to ensure the keys
- * and values are URL encoded, making them safe for use in URLs.
- *
- * @param string[] $query An array of query strings to be sanitized.
- * @return string[] The sanitized array with URL encoded query strings.
- */
- private function sanitizeQuery(array $query): array
- {
- // Use array_map to apply the sanitizeQueryPart method to each element of the $query array
- return array_map([$this, 'sanitizeQueryPart'], $query);
- }
-
- /**
- * Sanitizes a single query string part by URL encoding its key and value.
- *
- * This method takes a query string part, expected to be in the format 'key=value', splits it into
- * its key and value components, URL encodes each component, and then recombines them into a single
- * query string part.
- *
- * @param string $queryPart A single query string part to be sanitized.
- * @return string The sanitized query string part with URL encoded components.
- */
- private function sanitizeQueryPart(string $queryPart): string
- {
- if (strpos($queryPart, '=') === false) {
- return $queryPart;
- }
-
- // Split the query part into key and value based on the '=' delimiter
- [$key, $value] = explode('=', $queryPart);
-
- $key = rawurlencode($key);
- $value = rawurlencode($value);
-
- return sprintf('%s=%s', $key, $value);
- }
}
diff --git a/src/Service/TrackingInfoStructFactory.php b/src/Service/TrackingInfoStructFactory.php
index 5e4d1f908..b0a5b5a6a 100644
--- a/src/Service/TrackingInfoStructFactory.php
+++ b/src/Service/TrackingInfoStructFactory.php
@@ -13,6 +13,16 @@ class TrackingInfoStructFactory
{
use StringTrait;
+ /**
+ * @var UrlParsingService
+ */
+ private $urlParsingService;
+
+ public function __construct(UrlParsingService $urlParsingService)
+ {
+ $this->urlParsingService = $urlParsingService;
+ }
+
/**
* Mollie throws an error with length >= 100
@@ -91,6 +101,11 @@ private function createInfoStruct(string $trackingCarrier, string $trackingCode,
throw new \InvalidArgumentException('Missing Argument for Tracking Code!');
}
+ // determine if the provided tracking code is actually a tracking URL
+ if (empty($trackingUrl) === true || $this->urlParsingService->isUrl($trackingCode)) {
+ [$trackingCode, $trackingUrl] = $this->urlParsingService->parseTrackingCodeFromUrl($trackingCode);
+ }
+
# we just have to completely remove those codes, so that no tracking happens, but a shipping works.
# still, if we find multiple codes (because separators exist), then we use the first one only
if (mb_strlen($trackingCode) > self::MAX_TRACKING_CODE_LENGTH) {
@@ -114,13 +129,8 @@ private function createInfoStruct(string $trackingCarrier, string $trackingCode,
$trackingUrl = trim(sprintf($trackingUrl, $trackingCode));
- if (filter_var($trackingUrl, FILTER_VALIDATE_URL) === false) {
- $trackingUrl = '';
- }
-
- # following characters are not allowed in the tracking URL {,},<,>,#
- if (preg_match_all('/[{}<>#]/m', $trackingUrl)) {
- $trackingUrl = '';
+ if ($this->urlParsingService->isUrl($trackingUrl) === false) {
+ return new ShipmentTrackingInfoStruct($trackingCarrier, $trackingCode, '');
}
return new ShipmentTrackingInfoStruct($trackingCarrier, $trackingCode, $trackingUrl);
diff --git a/src/Service/UrlParsingService.php b/src/Service/UrlParsingService.php
new file mode 100644
index 000000000..2bbeec37d
--- /dev/null
+++ b/src/Service/UrlParsingService.php
@@ -0,0 +1,131 @@
+ 0) {
+ $pathParts = explode('/', $path);
+ array_walk($pathParts, function (&$pathPart) {
+ $pathPart = rawurlencode($pathPart);
+ });
+ $path = implode('/', $pathParts);
+ }
+
+ $query = '';
+ if (isset($urlParts['query'])) {
+ $urlParts['query'] = $this->sanitizeQuery(explode('&', $urlParts['query']));
+ $query = '?' . implode('&', $urlParts['query']);
+ }
+
+
+ $fragment = isset($urlParts['fragment']) ? '#' . rawurlencode($urlParts['fragment']) : '';
+
+ return trim($scheme.$user.$pass.$host.$port.$path.$query.$fragment);
+ }
+
+ /**
+ * Sanitizes an array of query strings by URL encoding their components.
+ *
+ * This method takes an array of query strings, where each string is expected to be in the format
+ * 'key=value'. It applies the sanitizeQueryPart method to each query string to ensure the keys
+ * and values are URL encoded, making them safe for use in URLs.
+ *
+ * @param string[] $query An array of query strings to be sanitized.
+ * @return string[] The sanitized array with URL encoded query strings.
+ */
+ public function sanitizeQuery(array $query): array
+ {
+ // Use array_map to apply the sanitizeQueryPart method to each element of the $query array
+ return array_map([$this, 'sanitizeQueryPart'], $query);
+ }
+
+ /**
+ * Sanitizes a single query string part by URL encoding its key and value.
+ *
+ * This method takes a query string part, expected to be in the format 'key=value', splits it into
+ * its key and value components, URL encodes each component, and then recombines them into a single
+ * query string part.
+ *
+ * @param string $queryPart A single query string part to be sanitized.
+ * @return string The sanitized query string part with URL encoded components.
+ */
+ public function sanitizeQueryPart(string $queryPart): string
+ {
+ if (strpos($queryPart, '=') === false) {
+ return $queryPart;
+ }
+
+ // Split the query part into key and value based on the '=' delimiter
+ [$key, $value] = explode('=', $queryPart);
+
+ $key = rawurlencode($key);
+ $value = rawurlencode($value);
+
+ return sprintf('%s=%s', $key, $value);
+ }
+}
diff --git a/src/Subscriber/OrderDeliverySubscriber.php b/src/Subscriber/OrderDeliverySubscriber.php
index c7d3b7a8f..dd915e1d7 100644
--- a/src/Subscriber/OrderDeliverySubscriber.php
+++ b/src/Subscriber/OrderDeliverySubscriber.php
@@ -119,7 +119,7 @@ public function onOrderDeliveryChanged(StateMachineStateChangeEvent $event): voi
$this->mollieShipment->shipOrderRest($order, null, $event->getContext());
} catch (\Throwable $ex) {
- $this->logger->error('Failed to transfer delivery state to mollie: '.$ex->getMessage());
+ $this->logger->error('Failed to transfer delivery state to mollie: '.$ex->getMessage(), ['exception' => $ex]);
return;
}
}
diff --git a/tests/PHPUnit/Components/ShipmentManager/ShipmentManagerTest.php b/tests/PHPUnit/Components/ShipmentManager/ShipmentManagerTest.php
index d14ce1de2..952af4d0e 100644
--- a/tests/PHPUnit/Components/ShipmentManager/ShipmentManagerTest.php
+++ b/tests/PHPUnit/Components/ShipmentManager/ShipmentManagerTest.php
@@ -14,6 +14,7 @@
use Kiener\MolliePayments\Service\OrderService;
use Kiener\MolliePayments\Service\TrackingInfoStructFactory;
use Kiener\MolliePayments\Service\Transition\DeliveryTransitionService;
+use Kiener\MolliePayments\Service\UrlParsingService;
use MolliePayments\Tests\Fakes\FakeShipment;
use MolliePayments\Tests\Traits\OrderTrait;
use PHPUnit\Framework\TestCase;
@@ -65,7 +66,7 @@ public function setUp(): void
$orderService,
$deliveryExtractor,
new OrderItemsExtractor(),
- new TrackingInfoStructFactory()
+ new TrackingInfoStructFactory(new UrlParsingService())
);
$this->context = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock();
diff --git a/tests/PHPUnit/Service/MollieApi/Builder/AbstractMollieOrderBuilder.php b/tests/PHPUnit/Service/MollieApi/Builder/AbstractMollieOrderBuilder.php
index 423ebecc0..fec432dc0 100644
--- a/tests/PHPUnit/Service/MollieApi/Builder/AbstractMollieOrderBuilder.php
+++ b/tests/PHPUnit/Service/MollieApi/Builder/AbstractMollieOrderBuilder.php
@@ -23,6 +23,7 @@
use Kiener\MolliePayments\Service\Router\RoutingDetector;
use Kiener\MolliePayments\Service\SettingsService;
use Kiener\MolliePayments\Service\Transition\TransactionTransitionServiceInterface;
+use Kiener\MolliePayments\Service\UrlParsingService;
use Kiener\MolliePayments\Setting\MollieSettingStruct;
use Kiener\MolliePayments\Validator\IsOrderLineItemValid;
use MolliePayments\Tests\Fakes\FakeCompatibilityGateway;
@@ -180,7 +181,7 @@ public function setUp(): void
new MollieLineItemBuilder(
new IsOrderLineItemValid(),
new PriceCalculator(),
- new LineItemDataExtractor(),
+ new LineItemDataExtractor(new UrlParsingService()),
new FakeCompatibilityGateway(),
new RoundingDifferenceFixer(),
new MollieLineItemHydrator(new MollieOrderPriceBuilder()),
diff --git a/tests/PHPUnit/Service/MollieApi/Builder/MollieLineItemBuilderTest.php b/tests/PHPUnit/Service/MollieApi/Builder/MollieLineItemBuilderTest.php
index 97f586acf..08db54f81 100644
--- a/tests/PHPUnit/Service/MollieApi/Builder/MollieLineItemBuilderTest.php
+++ b/tests/PHPUnit/Service/MollieApi/Builder/MollieLineItemBuilderTest.php
@@ -9,6 +9,7 @@
use Kiener\MolliePayments\Service\MollieApi\Fixer\RoundingDifferenceFixer;
use Kiener\MolliePayments\Service\MollieApi\LineItemDataExtractor;
use Kiener\MolliePayments\Service\MollieApi\PriceCalculator;
+use Kiener\MolliePayments\Service\UrlParsingService;
use Kiener\MolliePayments\Setting\MollieSettingStruct;
use Kiener\MolliePayments\Validator\IsOrderLineItemValid;
use Mollie\Api\Types\OrderLineType;
@@ -38,7 +39,7 @@ public function setUp(): void
$this->builder = new MollieLineItemBuilder(
(new IsOrderLineItemValid()),
(new PriceCalculator()),
- (new LineItemDataExtractor()),
+ (new LineItemDataExtractor(new UrlParsingService())),
new FakeCompatibilityGateway(),
new RoundingDifferenceFixer(),
new MollieLineItemHydrator(new MollieOrderPriceBuilder()),
diff --git a/tests/PHPUnit/Service/MollieApi/LineItemDataExtractorTest.php b/tests/PHPUnit/Service/MollieApi/LineItemDataExtractorTest.php
index 2974f936a..272d8df14 100644
--- a/tests/PHPUnit/Service/MollieApi/LineItemDataExtractorTest.php
+++ b/tests/PHPUnit/Service/MollieApi/LineItemDataExtractorTest.php
@@ -3,6 +3,7 @@
namespace MolliePayments\Tests\Service\MollieApi;
use Kiener\MolliePayments\Service\MollieApi\LineItemDataExtractor;
+use Kiener\MolliePayments\Service\UrlParsingService;
use PHPUnit\Framework\TestCase;
use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity;
use Shopware\Core\Content\Media\MediaEntity;
@@ -17,7 +18,7 @@ class LineItemDataExtractorTest extends TestCase
{
public function testWithMissingProduct(): void
{
- $extractor = new LineItemDataExtractor();
+ $extractor = new LineItemDataExtractor(new UrlParsingService());
$lineItemId = Uuid::randomHex();
$lineItem = new OrderLineItemEntity();
$lineItem->setId($lineItemId);
@@ -31,7 +32,7 @@ public function testWithMissingProduct(): void
public function testNoMediaNoSeo(): void
{
$expected = 'foo';
- $extractor = new LineItemDataExtractor();
+ $extractor = new LineItemDataExtractor(new UrlParsingService());
$lineItem = new OrderLineItemEntity();
$product = new ProductEntity();
$product->setProductNumber($expected);
@@ -47,7 +48,7 @@ public function testMediaExtraction(): void
{
$expectedImageUrl = 'https://bar.baz';
$expectedProductNumber = 'foo';
- $extractor = new LineItemDataExtractor();
+ $extractor = new LineItemDataExtractor(new UrlParsingService());
$lineItem = new OrderLineItemEntity();
$product = new ProductEntity();
$product->setProductNumber($expectedProductNumber);
@@ -71,7 +72,7 @@ public function testSeoUrlExtraction(): void
{
$expectedSeoUrl = 'https://bar.foo';
$expectedProductNumber = 'foo';
- $extractor = new LineItemDataExtractor();
+ $extractor = new LineItemDataExtractor(new UrlParsingService());
$lineItem = new OrderLineItemEntity();
$product = new ProductEntity();
$product->setProductNumber($expectedProductNumber);
@@ -93,7 +94,7 @@ public function testCompleteExtraction(): void
$expectedImageUrl = 'https://bar.baz';
$expectedSeoUrl = 'https://bar.foo';
$expectedProductNumber = 'foo';
- $extractor = new LineItemDataExtractor();
+ $extractor = new LineItemDataExtractor(new UrlParsingService());
$lineItem = new OrderLineItemEntity();
$product = new ProductEntity();
$product->setProductNumber($expectedProductNumber);
diff --git a/tests/PHPUnit/Service/TrackingInfoStructFactoryTest.php b/tests/PHPUnit/Service/TrackingInfoStructFactoryTest.php
index 15598acdc..df5e64643 100644
--- a/tests/PHPUnit/Service/TrackingInfoStructFactoryTest.php
+++ b/tests/PHPUnit/Service/TrackingInfoStructFactoryTest.php
@@ -5,6 +5,7 @@
use Kiener\MolliePayments\Components\ShipmentManager\Exceptions\NoDeliveriesFoundExceptions;
use Kiener\MolliePayments\Service\TrackingInfoStructFactory;
+use Kiener\MolliePayments\Service\UrlParsingService;
use PHPUnit\Framework\TestCase;
use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryCollection;
use Shopware\Core\Checkout\Order\Aggregate\OrderDelivery\OrderDeliveryEntity;
@@ -23,7 +24,7 @@ class TrackingInfoStructFactoryTest extends TestCase
public function setUp(): void
{
- $this->factory = new TrackingInfoStructFactory();
+ $this->factory = new TrackingInfoStructFactory(new UrlParsingService());
}
@@ -160,34 +161,6 @@ public function testCommaSeparatorHasHigherPriority(): void
}
- /**
- * @return array
- */
- public function invalidCodes(): array
- {
- return [
- ['some{code'],
- ['some}code'],
- ['somecode'],
- ['some#code'],
- ['some#<>{},' . str_repeat('1', 200)],
- [str_repeat('1', 200)],
- ];
- }
-
- /**
- * @dataProvider invalidCodes
- * @param string $invalidCode
- * @return void
- */
- public function testUrlEmptyOnInvalidCodes(string $invalidCode): void
- {
- $trackingInfoStruct = $this->factory->create('test', $invalidCode, 'https://foo.bar/%s');
-
- $this->assertSame('', $trackingInfoStruct->getUrl());
- }
-
/**
* @return array
diff --git a/tests/PHPUnit/Service/UrlParsingServiceTest.php b/tests/PHPUnit/Service/UrlParsingServiceTest.php
new file mode 100644
index 000000000..52ed45b61
--- /dev/null
+++ b/tests/PHPUnit/Service/UrlParsingServiceTest.php
@@ -0,0 +1,159 @@
+service = new UrlParsingService();
+ }
+
+ /**
+ * @dataProvider urlProvider
+ */
+ public function testIsUrl(string $url, bool $expected)
+ {
+ $this->assertEquals($expected, $this->service->isUrl($url));
+ }
+
+ public function urlProvider(): array
+ {
+ return [
+ ['https://www.example.com', true],
+ ['http://example.com', true],
+ ['not a url', false],
+ ['example.com', false],
+ ];
+ }
+
+ /**
+ * @dataProvider queryParameterProvider
+ */
+ public function testParseTrackingCodeQueryParameter(string $input, array $expected)
+ {
+ $this->assertEquals($expected, $this->service->parseTrackingCodeFromUrl($input));
+ }
+
+ public function queryParameterProvider(): array
+ {
+ return [
+ ['https://www.example.com/product?code=12345', ['12345', 'https://www.example.com/product?code=12345']],
+ ['https://www.example.com/product?shipment=abc123', ['abc123', 'https://www.example.com/product?shipment=abc123']],
+ ['https://www.example.com/product?track=track123', ['track123', 'https://www.example.com/product?track=track123']],
+ ['https://www.example.com/product?tracking=track456', ['track456', 'https://www.example.com/product?tracking=track456']],
+ ];
+ }
+
+ /**
+ * @dataProvider pathProvider
+ */
+ public function testParseTrackingCodePath(string $input, array $expected)
+ {
+ $this->assertEquals($expected, $this->service->parseTrackingCodeFromUrl($input));
+ }
+
+ public function pathProvider(): array
+ {
+ return [
+ ['https://www.example.com/code/12345/product', ['12345', 'https://www.example.com/code/12345/product']],
+ ['https://www.example.com/shipment/abc123/product', ['abc123', 'https://www.example.com/shipment/abc123/product']],
+ ['https://www.example.com/track/track123/product', ['track123', 'https://www.example.com/track/track123/product']],
+ ['https://www.example.com/tracking/track456/product', ['track456', 'https://www.example.com/tracking/track456/product']],
+ ];
+ }
+
+ /**
+ * @dataProvider hashProvider
+ */
+ public function testParseTrackingCodeHash(string $input, array $expected)
+ {
+ $this->assertEquals($expected, $this->service->parseTrackingCodeFromUrl($input));
+ }
+
+ public function hashProvider(): array
+ {
+ return [
+ ['https://www.example.com/product#code=12345', ['12345', 'https://www.example.com/product#code=12345']],
+ ['https://www.example.com/product#shipment=abc123', ['abc123', 'https://www.example.com/product#shipment=abc123']],
+ ['https://www.example.com/product#track=track123', ['track123', 'https://www.example.com/product#track=track123']],
+ ['https://www.example.com/product#tracking=track456', ['track456', 'https://www.example.com/product#tracking=track456']],
+ ];
+ }
+
+ /**
+ * @dataProvider notFoundProvider
+ */
+ public function testParseTrackingCodeNotFound(string $input, array $expected)
+ {
+ $this->assertEquals($expected, $this->service->parseTrackingCodeFromUrl($input));
+ }
+
+ public function notFoundProvider(): array
+ {
+ return [
+ ['https://www.example.com/product', ['', 'https://www.example.com/product']],
+ ['https://www.example.com/code/product', ['', 'https://www.example.com/code/product']],
+ ];
+ }
+
+ /**
+ * @dataProvider encodePathAndQueryProvider
+ */
+ public function testEncodePathAndQuery(string $input, string $expected)
+ {
+ $this->assertEquals($expected, $this->service->encodePathAndQuery($input));
+ }
+
+ public function encodePathAndQueryProvider(): array
+ {
+ return [
+ ['https://www.example.com/path/to/resource', 'https://www.example.com/path/to/resource'],
+ ['https://www.example.com/path/to/{resource}', 'https://www.example.com/path/to/%7Bresource%7D'],
+ ['https://www.example.com/path with spaces/to/resource', 'https://www.example.com/path%20with%20spaces/to/resource'],
+ ['https://www.example.com/path/to/resource?query=123&test={test}', 'https://www.example.com/path/to/resource?query=123&test=%7Btest%7D'],
+ ];
+ }
+
+ /**
+ * @dataProvider sanitizeQueryProvider
+ */
+ public function testSanitizeQuery(array $input, array $expected)
+ {
+ $this->assertEquals($expected, $this->service->sanitizeQuery($input));
+ }
+
+ public function sanitizeQueryProvider(): array
+ {
+ return [
+ [['key=value'], ['key=value']],
+ [['key with spaces=value with spaces'], ['key%20with%20spaces=value%20with%20spaces']],
+ [['key={value}'], ['key=%7Bvalue%7D']],
+ [['key1=value1', 'key2=value2'], ['key1=value1', 'key2=value2']],
+ ];
+ }
+
+ /**
+ * @dataProvider sanitizeQueryPartProvider
+ */
+ public function testSanitizeQueryPart(string $input, string $expected)
+ {
+ $this->assertEquals($expected, $this->service->sanitizeQueryPart($input));
+ }
+
+ public function sanitizeQueryPartProvider(): array
+ {
+ return [
+ ['key=value', 'key=value'],
+ ['key with spaces=value with spaces', 'key%20with%20spaces=value%20with%20spaces'],
+ ['key={value}', 'key=%7Bvalue%7D'],
+ ['key', 'key'], // No '=' in the input, should return as is
+ ];
+ }
+}
diff --git a/tests/PHPUnit/Utils/Traits/PaymentBuilderTrait.php b/tests/PHPUnit/Utils/Traits/PaymentBuilderTrait.php
index 4c65cd4d4..5796fbbe9 100644
--- a/tests/PHPUnit/Utils/Traits/PaymentBuilderTrait.php
+++ b/tests/PHPUnit/Utils/Traits/PaymentBuilderTrait.php
@@ -9,6 +9,7 @@
use Kiener\MolliePayments\Service\MollieApi\Fixer\RoundingDifferenceFixer;
use Kiener\MolliePayments\Service\MollieApi\LineItemDataExtractor;
use Kiener\MolliePayments\Service\MollieApi\PriceCalculator;
+use Kiener\MolliePayments\Service\UrlParsingService;
use Kiener\MolliePayments\Validator\IsOrderLineItemValid;
use MolliePayments\Tests\Fakes\FakeCompatibilityGateway;
use Shopware\Core\Checkout\Cart\LineItem\LineItem;
@@ -81,7 +82,7 @@ public function getExpectedLineItems(string $taxStatus, ?OrderLineItemCollection
$mollieLineItemBuilder = new MollieLineItemBuilder(
new IsOrderLineItemValid(),
new PriceCalculator(),
- new LineItemDataExtractor(),
+ new LineItemDataExtractor(new UrlParsingService()),
new FakeCompatibilityGateway(),
new RoundingDifferenceFixer(),
new MollieLineItemHydrator(new MollieOrderPriceBuilder()),