diff --git a/.bc-exclude.php b/.bc-exclude.php index 5e15ac79a..c65a17ef1 100644 --- a/.bc-exclude.php +++ b/.bc-exclude.php @@ -9,5 +9,7 @@ \preg_quote('An enum expression Monolog\Level::Debug is not supported in class Monolog\Handler\AbstractHandler'), // Storefront package is not installed \preg_quote('"Shopware\Storefront\Framework\Cookie\CookieProviderInterface" could not be found in the located source'), + // internal const + \preg_quote('Value of constant Swag\PayPal\Setting\Settings::DEFAULT_VALUES changed from array') . '.*', ], ]; diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b95f0ecf..545dd7e52 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # REPLACE_GLOBALLY_WITH_NEXT_VERSION - Fixes an issue, where cookies are added even though associated payment methods are not active (shopware/SwagPayPal#457) - Fixes an issue, where the state of a country was not correctly transmitted to PayPal (shopware/SwagPayPal#469) +- Added option to disable PayPal callbacks (shopware/SwagPayPal#463) # 10.3.0 - Added compatibility with subscription mixed carts (shopware/shopware#10486) diff --git a/CHANGELOG_de-DE.md b/CHANGELOG_de-DE.md index f8414e621..98f7ea3ca 100644 --- a/CHANGELOG_de-DE.md +++ b/CHANGELOG_de-DE.md @@ -1,6 +1,7 @@ # REPLACE_GLOBALLY_WITH_NEXT_VERSION - Behebt ein Problem, bei dem Cookies geladen wurden, obwohl die zugehörigen Zahlungsarten nicht aktiv sind (shopware/SwagPayPal#457) - Behebt ein Problem, bei dem der Bundesstaat nicht korrekt an PayPal übermittelt wurde (shopware/SwagPayPal#469) +- Fügt eine Option zum deaktiveren von PayPal Callbacks hinzu (shopware/SwagPayPal#463) # 10.3.0 - Fügt Kompatibilität mit Abos in gemischten Warenkörben hinzu (shopware/shopware#10486) diff --git a/src/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRoute.php b/src/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRoute.php index 23d42ecd4..75c8ee98e 100644 --- a/src/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRoute.php +++ b/src/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRoute.php @@ -14,6 +14,7 @@ use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException; use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Shopware\Core\System\SystemConfig\SystemConfigService; use Shopware\PayPalSDK\Struct\V2\Order\ApplicationContext; use Shopware\PayPalSDK\Struct\V2\Order\PaymentSource\Common\Attributes\OrderUpdateCallbackConfig; use Swag\PayPal\Checkout\Cart\Service\CartPriceService; @@ -22,6 +23,7 @@ use Swag\PayPal\OrdersApi\Builder\PayPalOrderBuilder; use Swag\PayPal\RestApi\PartnerAttributionId; use Swag\PayPal\RestApi\V2\Resource\OrderResource; +use Swag\PayPal\Setting\Settings; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; @@ -40,6 +42,7 @@ public function __construct( private readonly PayPalOrderBuilder $paypalOrderBuilder, private readonly OrderResource $orderResource, private readonly CartPriceService $cartPriceService, + private readonly SystemConfigService $systemConfigService, private readonly RouterInterface $router, private readonly LoggerInterface $logger, ) { @@ -80,17 +83,21 @@ public function createPayPalOrder(Request $request, SalesChannelContext $salesCh $experienceContext->setUserAction(ApplicationContext::USER_ACTION_CONTINUE); // Configure shipping callback for dynamic price recalculation - $callbackConfig = new OrderUpdateCallbackConfig(); - $callbackUrl = $this->router->generate( - 'store-api.paypal.express.shipping_callback', - ['salesChannelId' => $salesChannelContext->getSalesChannelId(), 'token' => $salesChannelContext->getToken()], - UrlGeneratorInterface::ABSOLUTE_URL, - ); - $callbackConfig->setCallbackUrl($callbackUrl); - $callbackConfig->setCallbackEvents([OrderUpdateCallbackConfig::CALLBACK_EVENT_SHIPPING_OPTIONS]); - $experienceContext->setOrderUpdateCallbackConfig($callbackConfig); + if (!$this->systemConfigService->getBool(Settings::IS_LOCAL_ENVIRONMENT)) { + $callbackConfig = new OrderUpdateCallbackConfig(); + $callbackUrl = $this->router->generate( + 'store-api.paypal.express.shipping_callback', + ['salesChannelId' => $salesChannelContext->getSalesChannelId(), 'token' => $salesChannelContext->getToken()], + UrlGeneratorInterface::ABSOLUTE_URL, + ); + $callbackConfig->setCallbackUrl($callbackUrl); + $callbackConfig->setCallbackEvents([OrderUpdateCallbackConfig::CALLBACK_EVENT_SHIPPING_OPTIONS]); + $experienceContext->setOrderUpdateCallbackConfig($callbackConfig); - $this->logger->debug('Configured shipping callback', ['callbackUrl' => $callbackUrl]); + $this->logger->debug('Configured shipping callback', ['callbackUrl' => $callbackUrl]); + } else { + $this->logger->debug('Skipped shipping callback due to being disabled in system config'); + } } $orderResponse = $this->orderResource->create( diff --git a/src/Resources/app/administration/src/app/snippet/de.json b/src/Resources/app/administration/src/app/snippet/de.json index ccd895ede..48647fabb 100644 --- a/src/Resources/app/administration/src/app/snippet/de.json +++ b/src/Resources/app/administration/src/app/snippet/de.json @@ -83,7 +83,8 @@ "vaultingEnabledACDC": "Vaulting für Kredit- und Debitkarten-Zahlungen verwenden", "vaultingEnabledVenmo": "Vaulting für Venmo-Zahlungen verwenden", "crossBorderMessagingEnabled" : "Länderübergreifende Lokalisierung der \"Später bezahlen\"-Nachricht aktivieren", - "crossBorderBuyerCountry" : "Lokalisierung" + "crossBorderBuyerCountry" : "Lokalisierung", + "isLocalEnvironment": "Lokale Entwicklungsumgebung" }, "helpText": { "clientId": "Die Client-ID der REST-API, die das Plugin dazu verwendet, sich mit der PayPal-API zu authentifizieren.", diff --git a/src/Resources/app/administration/src/app/snippet/en.json b/src/Resources/app/administration/src/app/snippet/en.json index 86322d343..7af392094 100644 --- a/src/Resources/app/administration/src/app/snippet/en.json +++ b/src/Resources/app/administration/src/app/snippet/en.json @@ -83,7 +83,8 @@ "vaultingEnabledACDC": "Enable Vaulting for credit and debit cards", "vaultingEnabledVenmo": "Enable Vaulting for Venmo payments", "crossBorderMessagingEnabled" : "Enable cross-border localization of Pay Later message", - "crossBorderBuyerCountry" : "Localization" + "crossBorderBuyerCountry" : "Localization", + "isLocalEnvironment": "Local development environment" }, "helpText": { "clientId": "The REST API client ID is used to authenticate this plugin with the PayPal API.", diff --git a/src/Resources/app/administration/src/constant/swag-paypal-settings.constant.ts b/src/Resources/app/administration/src/constant/swag-paypal-settings.constant.ts index 48474e671..2910cf19a 100644 --- a/src/Resources/app/administration/src/constant/swag-paypal-settings.constant.ts +++ b/src/Resources/app/administration/src/constant/swag-paypal-settings.constant.ts @@ -139,6 +139,8 @@ export const SYSTEM_CONFIGS = [ 'SwagPayPal.settings.webhookId', 'SwagPayPal.settings.webhookExecuteToken', + + 'SwagPayPal.settings.isLocalEnvironment', ] as const; export type SYSTEM_CONFIG = typeof SYSTEM_CONFIGS[number]; diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/de.json b/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/de.json index 5aa718519..f67c77f99 100644 --- a/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/de.json +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/de.json @@ -103,6 +103,10 @@ "info": "Mit länderübergreifende Nachrichten für \"Später bezahlen\" wird die \"Später bezahlen\"-Nachricht in der Sprache des Kunden anzeigt. Diese Funktion ist nur für bestimmte Länder verfügbar. {0}", "buyerCountryAuto": "Automatische Bestimmung", "buyerCountryOverride": "Lokalisierung" + }, + "environment": { + "title": "Umgebung", + "info": "PayPal sendet Bestellstatus-Updates über Webhooks an den Shop und fordert außerdem eine Neuberechnung des Warenkorbs an, wenn Kunden Änderungen in der PayPal Express Checkout-Benutzeroberfläche vornehmen. Wenn der Shop nicht öffentlich zugänglich ist (z. B. wenn es sich um eine Entwicklungs-/Staging-Umgebung handelt oder eine Authentifizierung erfordert), kannst du diese Instanz als \"lokale Umgebung\" kennzeichnen, um Verbindungsfehler zu vermeiden.\nBitte beachte, dass alle Webhook-Listener beim Speichern deregistriert werden." } }, "swag-paypal-settings-sales-channel-switch": { diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/en.json b/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/en.json index 6f5205f88..e988ed455 100644 --- a/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/en.json +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/snippet/en.json @@ -103,6 +103,10 @@ "info": "Pay Later messaging is a feature that allows you to display a Pay Later message in the language of the customer. This feature is only available for certain countries. {0}", "buyerCountryAuto": "Automatic determination", "buyerCountryOverride": "Localization" + }, + "environment": { + "title": "Environment", + "info": "PayPal will send order status updates to the shop via webhooks, and will also request cart recalculations when customers make changes in the PayPal Express Checkout UI. If the shop is not publicly accessible (e.g. if it is in a development/staging environment, or if it requires authentication), you can mark this instance as a \"local environment\" to prevent connection errors.\nPlease note that any webhook listener will be deregistered when saving." } }, "swag-paypal-settings-sales-channel-switch": { diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.html.twig b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.html.twig index 14bc59055..d09b164f5 100644 --- a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.html.twig +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.html.twig @@ -1,3 +1,18 @@ + +
+ + {{ $t('swag-paypal-settings.environment.info') }} + + + +
+
+ {% block swag_paypal_settings_webhook %} {% endblock %} diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.scss b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.scss index cd4801086..61ee916b6 100644 --- a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.scss +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.scss @@ -1,13 +1,13 @@ @import "~scss/variables"; -.swag-paypal-settings-cross-border { +.swag-paypal-settings-cross-border, .swag-paypal-settings-environment { &__warning-text { width: 100%; } &__content { display: grid; - gap: 8px; + gap: 16px; font-size: $font-size-xs; line-height: $line-height-sm; } diff --git a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts index 10c1056d0..ac5549336 100644 --- a/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts +++ b/src/Resources/app/administration/src/module/swag-paypal-settings/view/swag-paypal-settings-advanced/swag-paypal-settings-advanced.spec.ts @@ -41,6 +41,7 @@ describe('swag-paypal-settings-advanced', () => { .filter((cl) => cl.startsWith('swag-paypal')); expect(cardClasses).toEqual([ + 'swag-paypal-settings-environment', 'swag-paypal-settings-cross-border', ]); }); @@ -52,6 +53,7 @@ describe('swag-paypal-settings-advanced', () => { const settings = Object.fromEntries(components.map((el) => [el.props().path, el])); expect(Object.keys(settings)).toEqual([ + 'SwagPayPal.settings.isLocalEnvironment', 'SwagPayPal.settings.crossBorderMessagingEnabled', 'SwagPayPal.settings.crossBorderBuyerCountry', ]); @@ -63,7 +65,7 @@ describe('swag-paypal-settings-advanced', () => { it('should have cross-border information', async () => { const wrapper = await createWrapper(); - const alert = wrapper.find('.mt-banner'); + const alert = wrapper.find('.swag-paypal-settings-cross-border .mt-banner'); expect(alert.exists()).toBe(true); expect(alert.classes()).toContain('swag-paypal-settings-cross-border__warning-text'); @@ -73,4 +75,13 @@ describe('swag-paypal-settings-advanced', () => { expect(info.exists()).toBe(true); expect(info.text()).toBe('swag-paypal-settings.crossBorder.info'); }); + + it('should have paypal-callback information', async () => { + const wrapper = await createWrapper(); + + const info = wrapper.find('.swag-paypal-settings-environment__info-text'); + + expect(info.exists()).toBe(true); + expect(info.text()).toBe('swag-paypal-settings.environment.info'); + }); }); diff --git a/src/Resources/app/administration/src/types/system-config.ts b/src/Resources/app/administration/src/types/system-config.ts index 1451ce10c..7dc92c679 100644 --- a/src/Resources/app/administration/src/types/system-config.ts +++ b/src/Resources/app/administration/src/types/system-config.ts @@ -51,6 +51,8 @@ export declare type SystemConfig = { 'SwagPayPal.settings.crossBorderMessagingEnabled'?: boolean; 'SwagPayPal.settings.crossBorderBuyerCountry'?: typeof COUNTRY_OVERRIDES[number] | null; + + 'SwagPayPal.settings.isLocalEnvironment'?: boolean; }; /** @@ -106,4 +108,6 @@ export const SystemConfigDefinition: Record + diff --git a/src/Setting/Settings.php b/src/Setting/Settings.php index e2893cf66..c45e58b55 100644 --- a/src/Setting/Settings.php +++ b/src/Setting/Settings.php @@ -61,6 +61,7 @@ final class Settings public const VAULTING_ENABLED_WALLET = self::SYSTEM_CONFIG_DOMAIN . 'vaultingEnabledWallet'; public const VAULTING_ENABLED_ACDC = self::SYSTEM_CONFIG_DOMAIN . 'vaultingEnabledACDC'; public const VAULTING_ENABLED_VENMO = self::SYSTEM_CONFIG_DOMAIN . 'vaultingEnabledVenmo'; + public const IS_LOCAL_ENVIRONMENT = self::SYSTEM_CONFIG_DOMAIN . 'isLocalEnvironment'; /** * @internal these may change at any time @@ -98,6 +99,7 @@ final class Settings self::VAULTING_ENABLED_VENMO => false, self::CROSS_BORDER_MESSAGING_ENABLED => false, self::CROSS_BORDER_BUYER_COUNTRY => null, + self::IS_LOCAL_ENVIRONMENT => false, ]; public const LIVE_CREDENTIAL_KEYS = [ diff --git a/src/Webhook/Registration/WebhookSystemConfigHelper.php b/src/Webhook/Registration/WebhookSystemConfigHelper.php index e80cea794..86ecfae22 100644 --- a/src/Webhook/Registration/WebhookSystemConfigHelper.php +++ b/src/Webhook/Registration/WebhookSystemConfigHelper.php @@ -74,7 +74,7 @@ public function checkWebhookBefore(array $newData): array continue; } - if (!$this->configHasChangedSettings($newSettings, $oldActualSettings)) { + if (!$newData[Settings::IS_LOCAL_ENVIRONMENT] && !$this->configHasChangedSettings($newSettings, $oldActualSettings)) { // No writing of new credentials in this Sales Channel continue; } @@ -105,6 +105,10 @@ public function checkWebhookAfter(array $salesChannelIds): array $salesChannelId = null; } + if ($this->systemConfigService->get(Settings::IS_LOCAL_ENVIRONMENT, $salesChannelId)) { + continue; + } + $newSettings = $this->fetchSettings($salesChannelId); if (empty(\array_filter($newSettings))) { // has no own valid configuration diff --git a/tests/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRouteTest.php b/tests/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRouteTest.php index 24d96ff5c..52a2c11dc 100644 --- a/tests/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRouteTest.php +++ b/tests/Checkout/ExpressCheckout/SalesChannel/ExpressCreateOrderRouteTest.php @@ -14,6 +14,8 @@ use Shopware\Core\Checkout\Cart\SalesChannel\CartService; use Shopware\Core\Framework\Log\Package; use Shopware\Core\Framework\Test\TestCaseBase\IntegrationTestBehaviour; +use Shopware\Core\System\SystemConfig\SystemConfigService; +use Shopware\PayPalSDK\Struct\V2\Order; use Swag\PayPal\Checkout\Cart\Service\CartPriceService; use Swag\PayPal\Checkout\Exception\OrderZeroValueException; use Swag\PayPal\Checkout\ExpressCheckout\SalesChannel\ExpressCreateOrderRoute; @@ -47,7 +49,7 @@ class ExpressCreateOrderRouteTest extends TestCase use GatewayTestBehaviour; use IntegrationTestBehaviour; - public function testCreatePayment(): void + public function testCreatePaymentWithZeroValueCart(): void { $salesChannelContext = $this->getSalesChannelContext(); @@ -62,6 +64,7 @@ public function testCreatePayment(): void $this->getContainer()->get(PayPalOrderBuilder::class), new OrderResource(self::orderGateway(), new ApiContextFactoryMock()), $this->getContainer()->get(CartPriceService::class), + $this->getContainer()->get(SystemConfigService::class), $this->createMock(RouterInterface::class), new NullLogger(), ); @@ -71,7 +74,7 @@ public function testCreatePayment(): void $route->createPayPalOrder(new Request(), $salesChannelContext); } - public function testCreatePaymentWithZeroValueCart(): void + public function testCreatePayment(): void { $salesChannelContext = $this->getSalesChannelContext(); @@ -79,13 +82,42 @@ public function testCreatePaymentWithZeroValueCart(): void static::assertSame(Response::HTTP_OK, $response->getStatusCode()); static::assertSame(CreateOrderCapture::ID, $response->getToken()); + + $request = $this->getClient()->getLast(); + static::assertNotNull($request); + + $order = (new Order())->assign($request->getRequestBody() ?? []); + + $experienceContext = $order->getPaymentSource()?->getPaypal()?->getExperienceContext(); + static::assertNotNull($experienceContext); + static::assertNotNull($experienceContext->getOrderUpdateCallbackConfig()); } - private function createRoute(): ExpressCreateOrderRoute + public function testCreateWithoutShippingCallback(): void + { + $salesChannelContext = $this->getSalesChannelContext(); + + $response = $this->createRoute(true)->createPayPalOrder(new Request(), $salesChannelContext); + + static::assertSame(Response::HTTP_OK, $response->getStatusCode()); + static::assertSame(CreateOrderCapture::ID, $response->getToken()); + + $request = $this->getClient()->getLast(); + static::assertNotNull($request); + + $order = (new Order())->assign($request->getRequestBody() ?? []); + + $experienceContext = $order->getPaymentSource()?->getPaypal()?->getExperienceContext(); + static::assertNotNull($experienceContext); + static::assertNull($experienceContext->getOrderUpdateCallbackConfig()); + } + + private function createRoute(bool $callbacksDisabled = false): ExpressCreateOrderRoute { $systemConfig = $this->createSystemConfigServiceMock([ Settings::CLIENT_ID => 'testClientId', Settings::CLIENT_SECRET => 'testClientSecret', + Settings::IS_LOCAL_ENVIRONMENT => $callbacksDisabled, ]); $priceFormatter = new PriceFormatter(); @@ -108,6 +140,7 @@ private function createRoute(): ExpressCreateOrderRoute $paypalOrderBuilder, new OrderResource(self::orderGateway(), new ApiContextFactoryMock()), $this->getContainer()->get(CartPriceService::class), + $systemConfig, $this->createMock(RouterInterface::class), new NullLogger(), ); diff --git a/tests/Webhook/Registration/WebhookSystemConfigHelperTest.php b/tests/Webhook/Registration/WebhookSystemConfigHelperTest.php index da3a91a6e..85f48b88a 100644 --- a/tests/Webhook/Registration/WebhookSystemConfigHelperTest.php +++ b/tests/Webhook/Registration/WebhookSystemConfigHelperTest.php @@ -9,11 +9,12 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Log\NullLogger; use Shopware\Core\Framework\Log\Package; -use Shopware\Core\System\SystemConfig\SystemConfigService; -use Swag\PayPal\Setting\Service\SettingsValidationServiceInterface; +use Shopware\Core\Test\Stub\SystemConfigService\StaticSystemConfigService; +use Swag\PayPal\Setting\Service\SettingsValidationService; use Swag\PayPal\Setting\Settings; use Swag\PayPal\Webhook\Registration\WebhookSystemConfigHelper; use Swag\PayPal\Webhook\WebhookServiceInterface; @@ -33,18 +34,120 @@ class WebhookSystemConfigHelperTest extends TestCase Settings::SANDBOX, ]; + private const VALID_SETTINGS = [ + Settings::CLIENT_ID => 'valid-client-id', + Settings::CLIENT_SECRET => 'valid-client-secret', + Settings::SANDBOX => false, + Settings::IS_LOCAL_ENVIRONMENT => false, + ]; + + private MockObject&WebhookServiceInterface $webhookService; + + private StaticSystemConfigService $systemConfigService; + private WebhookSystemConfigHelper $helper; protected function setUp(): void { $this->helper = new WebhookSystemConfigHelper( new NullLogger(), - $this->createMock(WebhookServiceInterface::class), - $this->createMock(SystemConfigService::class), - $this->createMock(SettingsValidationServiceInterface::class), + $this->webhookService = $this->createMock(WebhookServiceInterface::class), + $this->systemConfigService = new StaticSystemConfigService(), + new SettingsValidationService( + $this->systemConfigService, + new NullLogger(), + ), ); } + /** + * @param array> $newConfig + * @param array> $existingConfig + */ + #[DataProvider('providerCheckWebhookBefore')] + public function testCheckWebhookBefore(bool $expected, array $newConfig, array $existingConfig): void + { + foreach ($existingConfig as $salesChannelId => $config) { + $salesChannelId = $salesChannelId === 'null' ? null : $salesChannelId; + $this->systemConfigService->setMultiple($config, $salesChannelId); + } + + $this->webhookService + ->expects($expected ? $this->once() : $this->never()) + ->method('deregisterWebhook'); + + $this->helper->checkWebhookBefore($newConfig); + } + + public static function providerCheckWebhookBefore(): \Generator + { + yield 'no old distinct settings' => [ + false, + ['null' => self::VALID_SETTINGS], + [], + ]; + + yield 'missing actual settings' => [ + false, + ['null' => self::VALID_SETTINGS], + ['null' => [Settings::CLIENT_ID => 'old-client-id']], + ]; + + yield 'no actual settings changes' => [ + false, + ['null' => [Settings::CLIENT_ID => 'same-client-id']], + ['null' => [Settings::CLIENT_ID => 'same-client-id']], + ]; + + yield 'actual settings changed' => [ + true, + ['null' => [...self::VALID_SETTINGS, Settings::CLIENT_ID => 'new-client-id']], + ['null' => self::VALID_SETTINGS], + ]; + + yield 'paypal callbacks disabled' => [ + true, + ['null' => [...self::VALID_SETTINGS, Settings::CLIENT_ID => 'new-client-id', Settings::IS_LOCAL_ENVIRONMENT => true]], + ['null' => self::VALID_SETTINGS], + ]; + } + + /** + * @param array> $config + */ + #[DataProvider('providerCheckWebhookAfter')] + public function testCheckWebhookAfter(bool $expected, array $config): void + { + foreach ($config as $salesChannelId => $values) { + $salesChannelId = $salesChannelId === 'null' ? null : $salesChannelId; + $this->systemConfigService->setMultiple($values, $salesChannelId); + } + + $this->webhookService + ->expects($expected ? $this->once() : $this->never()) + ->method('registerWebhook'); + + $this->helper->checkWebhookAfter([null]); + } + + public static function providerCheckWebhookAfter(): \Generator + { + yield 'no valid settings' => [ + false, + ['null' => [Settings::CLIENT_ID => 'incomplete-client-id']], + ]; + + yield 'valid settings' => [ + true, + ['null' => self::VALID_SETTINGS], + ]; + + yield 'paypal callbacks disabled' => [ + false, + ['null' => [...self::VALID_SETTINGS, Settings::IS_LOCAL_ENVIRONMENT => true]], + ]; + } + /** * @param array $config */