From 68592a940964f23ec9ca750a15a2c4d42cecbec8 Mon Sep 17 00:00:00 2001 From: andrewrowanwallee Date: Mon, 3 Nov 2025 13:29:47 +0100 Subject: [PATCH] Release 6.1.17 --- CHANGELOG.md | 5 + CHANGELOG_de-DE.md | 5 + README.md | 8 +- composer.json | 2 +- .../PaymentMethodConfigurationService.php | 181 +++++--- .../Service/TransactionService.php | 87 ++-- .../WebHooks/Controller/WebHookController.php | 8 + .../Controller/CheckoutController.php | 119 +++++- .../Subscriber/CheckoutSubscriber.php | 386 ++++++++++++------ src/Core/Util/Analytics/Analytics.php | 67 ++- src/Core/Util/Payload/TransactionPayload.php | 44 +- .../sw-vrpayment-credentials/index.html.twig | 12 +- .../sw-vrpayment-credentials/index.js | 52 ++- .../page/vrpayment-settings/index.html.twig | 282 ++++++------- .../page/vrpayment-settings/index.js | 2 +- .../v-r-payment-payment.js | 1 + .../services/core/storefront/checkout.xml | 2 + .../css/v-r-payment-payment.css | 2 + .../administration/js/v-r-payment-payment.js | 1 + 19 files changed, 844 insertions(+), 422 deletions(-) create mode 100644 src/Resources/app/storefront/dist/storefront/js/v-r-payment-payment/v-r-payment-payment.js create mode 100644 src/Resources/public/administration/css/v-r-payment-payment.css create mode 100644 src/Resources/public/administration/js/v-r-payment-payment.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 167cdd9..754b4c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 6.1.17 +- Sales channels now support different spaces +- Upgraded SDK to include latest fallback CA Bundle +- Fixed error screen when returning from portal on failed payment + # 6.1.16 - Fixed issue with pending orders remaining open diff --git a/CHANGELOG_de-DE.md b/CHANGELOG_de-DE.md index c8a9006..bcb2cf5 100644 --- a/CHANGELOG_de-DE.md +++ b/CHANGELOG_de-DE.md @@ -1,3 +1,8 @@ +# 6.1.17 +- Vertriebskanäle unterstützen jetzt verschiedene Bereiche +- SDK aktualisiert und enthält nun das neueste CA-Fallback-Bundle +- Fehlerbildschirm beim Zurückkehren vom Portal nach fehlgeschlagener Zahlung behoben + # 6.1.16 - Problem behoben, bei dem die Versandkosten nicht korrekt verarbeitet wurden diff --git a/README.md b/README.md index 532362e..5d45669 100644 --- a/README.md +++ b/README.md @@ -14,10 +14,10 @@ The VR Payment Payment Plugin integrates modern payment processing into Shopware ## Documentation -- For English documentation click [here](@WalleeDocPath(/docs/en/documentation.html)) -- Für die deutsche Dokumentation klicken Sie [hier](@WalleeDocPath(/docs/de/documentation.html)) -- Pour la documentation Française, cliquez [ici](@WalleeDocPath(/docs/fr/documentation.html)) -- Per la documentazione in tedesco, clicca [qui](@WalleeDocPath(/docs/it/documentation.html)) +- For English documentation click [here](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/en/documentation.html) +- Für die deutsche Dokumentation klicken Sie [hier](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/de/documentation.html) +- Pour la documentation Française, cliquez [ici](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/fr/documentation.html) +- Per la documentazione in tedesco, clicca [qui](https://docs.plugin-documentation.vr-payment.de/vr-payment/shopware-6/6.1.17/docs/it/documentation.html) ## Installation diff --git a/composer.json b/composer.json index 812ae5a..b02a130 100644 --- a/composer.json +++ b/composer.json @@ -59,5 +59,5 @@ "vrpayment/sdk": "^4.0.0" }, "type": "shopware-platform-plugin", - "version": "6.1.16" + "version": "6.1.17" } diff --git a/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php b/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php index 508f807..11df089 100644 --- a/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php +++ b/src/Core/Api/PaymentMethodConfiguration/Service/PaymentMethodConfigurationService.php @@ -251,9 +251,9 @@ class PaymentMethodConfigurationService { { $data = []; $paymentMethodData = []; - $salesChannelPaymentMethodData = []; - $criteria = (new Criteria())->addFilter(new EqualsFilter('state', 'ACTIVE')); + $criteria = (new Criteria())->addFilter(new EqualsFilter('state', 'ACTIVE')) + ->addFilter(new EqualsFilter('spaceId', $this->getSpaceId())); /** * @var $vRPaymentPMConfigurationRepository @@ -276,7 +276,7 @@ class PaymentMethodConfigurationService { ]; $paymentMethodData[] = [ - 'id' => $paymentMethodConfigurationEntity->getId(), + 'id' => $paymentMethodConfigurationEntity->getPaymentMethodId(), 'active' => false, ]; } @@ -349,35 +349,68 @@ class PaymentMethodConfigurationService { */ foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) { - $paymentMethodConfigurationEntity = $this->getPaymentMethodConfigurationEntity( - $paymentMethodConfiguration->getSpaceId(), - $paymentMethodConfiguration->getId(), - $context + $entity = $this->getPaymentMethodConfigurationEntity( + $paymentMethodConfiguration->getSpaceId(), + $paymentMethodConfiguration->getId(), + $context ); - $id = is_null($paymentMethodConfigurationEntity) ? Uuid::randomHex() : $paymentMethodConfigurationEntity->getId(); + $configId = $entity ? $entity->getId() : Uuid::randomHex(); + $technicalName = $paymentMethodConfiguration->getName(); + + $paymentMethodId = $this->getOrCreatePaymentMethodId( + $technicalName, + VRPaymentPaymentHandler::class, + $context + ); $data = [ - 'id' => $id, - 'paymentMethodConfigurationId' => $paymentMethodConfiguration->getId(), - 'paymentMethodId' => $id, - 'data' => json_decode(strval($paymentMethodConfiguration), true), - 'sortOrder' => $paymentMethodConfiguration->getSortOrder(), - 'spaceId' => $paymentMethodConfiguration->getSpaceId(), - 'state' => CreationEntityState::ACTIVE, + 'id' => $configId, + 'paymentMethodConfigurationId' => $paymentMethodConfiguration->getId(), + 'paymentMethodId' => $paymentMethodId, + 'data' => json_decode(strval($paymentMethodConfiguration), true), + 'sortOrder' => $paymentMethodConfiguration->getSortOrder(), + 'spaceId' => $paymentMethodConfiguration->getSpaceId(), + 'state' => CreationEntityState::ACTIVE, ]; - $this->upsertPaymentMethod($id, $paymentMethodConfiguration, $context); - - try { - $this->container->get(PaymentMethodConfigurationEntityDefinition::ENTITY_NAME . '.repository')->upsert([$data], $context); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), [$e->getTraceAsString()]); - } + try { + $this->upsertPaymentMethod($paymentMethodId, $paymentMethodConfiguration, $context); + $this->container + ->get(PaymentMethodConfigurationEntityDefinition::ENTITY_NAME . '.repository') + ->upsert([$data], $context); + } catch (\Exception $e) { + $this->logger->error($e->getMessage(), [$e->getTraceAsString()]); + } } } + + private function getOrCreatePaymentMethodId(string $technicalName, string $handlerIdentifier, Context $context): string + { + $criteria = new Criteria(); + $criteria->addFilter(new EqualsFilter('technicalName', $technicalName)); + $criteria->setLimit(1); + + $existing = $this->paymentMethodRepository->search($criteria, $context)->first(); + if ($existing !== null) { + return $existing->getId(); + } + + $paymentMethodId = Uuid::randomHex(); + + $this->paymentMethodRepository->upsert([[ + 'id' => $paymentMethodId, + 'handlerIdentifier' => $handlerIdentifier, + 'technicalName' => $technicalName, + 'name' => $technicalName, + 'active' => false, + ]], $context); + + return $paymentMethodId; + } + /** * Fetch active merchant payment methods from VRPayment API * @@ -479,38 +512,38 @@ class PaymentMethodConfigurationService { * @throws \VRPayment\Sdk\VersioningException */ protected function upsertPaymentMethod( - string $id, - PaymentMethodConfiguration $paymentMethodConfiguration, - Context $context - ): void - { + string $id, + PaymentMethodConfiguration $paymentMethodConfiguration, + Context $context + ): void { /** @var PluginIdProvider $pluginIdProvider */ $pluginIdProvider = $this->container->get(PluginIdProvider::class); - $pluginId = $pluginIdProvider->getPluginIdByBaseClass( - VRPaymentPayment::class, - $context + $pluginId = $pluginIdProvider->getPluginIdByBaseClass( + VRPaymentPayment::class, + $context ); $data = [ - 'id' => $id, - 'handlerIdentifier' => VRPaymentPaymentHandler::class, - 'pluginId' => $pluginId, - 'position' => $paymentMethodConfiguration->getSortOrder() - 100, - 'afterOrderEnabled' => true, - 'active' => true, - 'translations' => $this->getPaymentMethodConfigurationTranslation($paymentMethodConfiguration, $context), + 'id' => $id, + 'handlerIdentifier' => VRPaymentPaymentHandler::class, + 'pluginId' => $pluginId, + 'position' => $paymentMethodConfiguration->getSortOrder() - 100, + 'afterOrderEnabled' => true, + 'active' => true, + 'translations' => $this->getPaymentMethodConfigurationTranslation($paymentMethodConfiguration, $context), + 'technicalName' => $paymentMethodConfiguration->getName(), ]; - $data['mediaId'] = $this->upsertMedia($id, $paymentMethodConfiguration, $context); - - $data = array_filter($data); - - try { - $this->paymentMethodRepository->upsert([$data], $context); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), [$e->getTraceAsString()]); - } + $mediaId = $this->upsertMedia($id, $paymentMethodConfiguration, $context); + if ($mediaId) { + $data['mediaId'] = $mediaId; + } + try { + $this->paymentMethodRepository->upsert([$data], $context); + } catch (\Exception $e) { + $this->logger->error($e->getMessage(), [$e->getTraceAsString()]); + } } /** @@ -612,46 +645,58 @@ class PaymentMethodConfigurationService { * * @return string|null */ + /** + * Upload or update Payment Method icons + */ protected function upsertMedia(string $id, PaymentMethodConfiguration $paymentMethodConfiguration, Context $context): ?string { try { - $existingRecord = $this->getMediaDefaultFolderForPaymentMethod($paymentMethodConfiguration, $context); + $folderKey = 'payment_method_' . $paymentMethodConfiguration->getId(); - if ($existingRecord->count() > 0) { - $id = $existingRecord->first()->getId(); + // Check existing default folder + $criteria = new Criteria(); + $criteria->addFilter(new EqualsFilter('entity', $folderKey)); + $existingFolder = $this->mediaDefaultFolderRepository->search($criteria, $context); + + $folderId = $id; + if ($existingFolder->count() > 0) { + $folderId = $existingFolder->first()->getId(); } + // Ensure default folder $this->mediaDefaultFolderRepository->upsert([ - [ - 'id' => $id, - 'associationFields' => [], - 'entity' => 'payment_method_' . $paymentMethodConfiguration->getId(), - ], + [ + 'id' => $folderId, + 'associationFields' => [], + 'entity' => $folderKey, + ], ], $context); + // Ensure media folder $this->mediaFolderRepository->upsert([ - [ - 'id' => $id, - 'defaultFolderId' => $id, - 'name' => $paymentMethodConfiguration->getName(), - 'useParentConfiguration' => false, - 'configuration' => [], - ], + [ + 'id' => $folderId, + 'defaultFolderId' => $folderId, + 'name' => $paymentMethodConfiguration->getName(), + 'useParentConfiguration' => false, + 'configuration' => [], + ], ], $context); - /** - * @var \Shopware\Core\Content\Media\MediaDefinition - */ + // Media insert/update $mediaDefinition = $this->container->get(MediaDefinition::class); $this->mediaSerializer->setRegistry($this->serializerRegistry); + $data = [ - 'id' => $id, - 'title' => $paymentMethodConfiguration->getName(), - 'url' => $paymentMethodConfiguration->getResolvedImageUrl(), - 'mediaFolderId' => $id, + 'id' => $id, + 'title' => $paymentMethodConfiguration->getName(), + 'url' => $paymentMethodConfiguration->getResolvedImageUrl(), + 'mediaFolderId' => $folderId, ]; + $data = $this->mediaSerializer->deserialize(new Config([], [], []), $mediaDefinition, $data); $this->mediaRepository->upsert([$data], $context); + return $id; } catch (\Exception $e) { $this->logger->critical($e->getMessage(), [$e->getTraceAsString()]); diff --git a/src/Core/Api/Transaction/Service/TransactionService.php b/src/Core/Api/Transaction/Service/TransactionService.php index 64f4754..6963e62 100644 --- a/src/Core/Api/Transaction/Service/TransactionService.php +++ b/src/Core/Api/Transaction/Service/TransactionService.php @@ -15,6 +15,7 @@ use Shopware\Core\{ System\SalesChannel\SalesChannelContext }; use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent; +use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use VRPayment\Sdk\{ Model\AddressCreate, @@ -46,6 +47,9 @@ use VRPaymentPayment\Core\{ Util\Payload\TransactionPayload }; +use Shopware\Core\Checkout\Order\Aggregate\OrderLineItem\OrderLineItemEntity; +use Shopware\Core\Framework\Struct\ArrayEntity; + /** * Class TransactionService * @@ -180,10 +184,19 @@ class TransactionService $transaction->getOrderTransaction()->getPaymentMethodId(), $transaction->getOrder()->getSalesChannelId() ); - $_SESSION['transactionId'] = null; - $_SESSION['arrayOfPossibleMethods'] = null; - $_SESSION['addressCheck'] = null; - $_SESSION['currencyCheck'] = null; + $salesChannelContext->getContext()->addExtension( + 'checkoutState', + new ArrayEntity([ + 'transactionId' => null, + 'addressHash' => null, + 'currency' => null, + ]) + ); + + $salesChannelContext->getContext()->addExtension( + 'possibleMethods', + new ArrayEntity(['ids' => []]) + ); $this->holdDelivery($transaction->getOrder()->getId(), $salesChannelContext->getContext()); @@ -478,14 +491,18 @@ class TransactionService /** * @param SalesChannelContext $salesChannelContext - * @param CheckoutConfirmPageLoadedEvent|null $event + * @param $event * @return int */ - public function createPendingTransaction(SalesChannelContext $salesChannelContext, ?CheckoutConfirmPageLoadedEvent $event = null): int - { + + public function createPendingTransaction(SalesChannelContext $salesChannelContext, $event = null): int + { $expiredTransaction = true; $transactionId = $_SESSION['transactionId'] ?? null; $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId()); + if (!$settings) { + throw new \Exception('Space settings not configured'); + } if ($transactionId) { $transactionService = $settings->getApiClient()->getTransactionService(); @@ -494,6 +511,7 @@ class TransactionService TransactionState::DECLINE, TransactionState::FAILED, TransactionState::VOIDED, + null ]; if (!in_array($pendingTransaction->getState(), $failedStates)) { $expiredTransaction = false; @@ -567,13 +585,20 @@ class TransactionService $lineItems = []; if ($event) { - $cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements(); - foreach ($cartLineItems as $cartLineItem) { - if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) { - continue; - } - $lineItems[] = $this->createTempLineItem($cartLineItem); - } + if ($event instanceof CheckoutConfirmPageLoadedEvent) { + $cartLineItems = $event->getPage()->getCart()->getLineItems()->getElements(); + foreach ($cartLineItems as $cartLineItem) { + if ($cartLineItem->getType() === CustomProductsLineItemTypes::LINE_ITEM_TYPE_CUSTOMIZED_PRODUCTS) { + continue; + } + $lineItems[] = $this->createTempLineItem($cartLineItem); + } + } elseif ($event instanceof AccountEditOrderPageLoadedEvent) { + $order = $event->getPage()->getOrder(); + foreach ($order->getLineItems() as $orderLineItem) { + $lineItems[] = $this->createTempLineItem($orderLineItem); + } + } } $customerId = ""; @@ -731,16 +756,28 @@ class TransactionService * @param LineItem $productData * @return LineItemCreate */ - private function createTempLineItem(LineItem $productData): LineItemCreate - { - $lineItem = new LineItemCreate(); - $lineItem->setName($productData->getLabel()); - $lineItem->setUniqueId($productData->getId()); - $lineItem->setSku($productData->getId()); - $lineItem->setQuantity($productData->getQuantity()); - $lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice()); - $lineItem->setType(LineItemType::PRODUCT); + private function createTempLineItem($productData): LineItemCreate + { + $lineItem = new LineItemCreate(); - return $lineItem; - } + if ($productData instanceof LineItem) { + $lineItem->setName($productData->getLabel()); + $lineItem->setUniqueId($productData->getId()); + $lineItem->setSku($productData->getReferencedId() ?? $productData->getId()); + $lineItem->setQuantity($productData->getQuantity()); + $lineItem->setAmountIncludingTax($productData->getPrice()->getUnitPrice()); + } elseif ($productData instanceof OrderLineItemEntity) { + $lineItem->setName($productData->getLabel()); + $lineItem->setUniqueId($productData->getId()); + $lineItem->setSku($productData->getProductId() ?? $productData->getIdentifier() ?? $productData->getId()); + $lineItem->setQuantity($productData->getQuantity()); + $lineItem->setAmountIncludingTax($productData->getUnitPrice()); + } else { + throw new \InvalidArgumentException('Unsupported line item type: ' . get_class($productData)); + } + + $lineItem->setType(LineItemType::PRODUCT); + + return $lineItem; + } } diff --git a/src/Core/Api/WebHooks/Controller/WebHookController.php b/src/Core/Api/WebHooks/Controller/WebHookController.php index 5658540..dc2c0a2 100644 --- a/src/Core/Api/WebHooks/Controller/WebHookController.php +++ b/src/Core/Api/WebHooks/Controller/WebHookController.php @@ -226,6 +226,14 @@ class WebHookController extends AbstractController { $this->settings = $this->settingsService->getSettings($salesChannelId); $signature = $request->server->get('HTTP_X_SIGNATURE'); $requestJson = json_decode($request->getContent(), true); + + if ($requestJson['eventId'] == null && $requestJson['entityId'] == null && $requestJson['listenerEntityId'] == null && $requestJson['listenerEntityId'] == null && $requestJson['listenerEntityTechnicalName'] == null && $requestJson['spaceId'] == null) { + throw new \InvalidArgumentException('Empty webhook'); + } + + if (!$this->settings->getSpaceId() || !$this->settings->getUserId() || !$this->settings->getApplicationKey()) { + throw new \InvalidArgumentException('Not correct webhook configuration for salesChannelId: ' . $salesChannelId . ' Debug: ' . var_dump($requestJson)); + } $apiClient = $this->settings->getApiClient(); $callBackData->assign($requestJson); diff --git a/src/Core/Storefront/Checkout/Controller/CheckoutController.php b/src/Core/Storefront/Checkout/Controller/CheckoutController.php index 37bcad0..d142f16 100644 --- a/src/Core/Storefront/Checkout/Controller/CheckoutController.php +++ b/src/Core/Storefront/Checkout/Controller/CheckoutController.php @@ -2,8 +2,12 @@ namespace VRPaymentPayment\Core\Storefront\Checkout\Controller; -use Psr\Log\LoggerInterface; +use Psr\{ + Log\LoggerInterface, + Cache\CacheItemPoolInterface +}; use Shopware\Core\{ + Checkout\Payment\PaymentException, Checkout\Cart\Cart, Checkout\Cart\CartException, Checkout\Cart\LineItemFactoryRegistry, @@ -35,9 +39,13 @@ use Shopware\Storefront\{ use Symfony\Component\{ HttpFoundation\Request, HttpFoundation\Response, + HttpFoundation\RedirectResponse, Routing\Attribute\Route, - Routing\Generator\UrlGeneratorInterface + Routing\Generator\UrlGeneratorInterface, + Cache\Adapter\FilesystemAdapter, + DependencyInjection\ParameterBag\ParameterBagInterface }; +use Symfony\Contracts\Cache\ItemInterface; use VRPayment\Sdk\{ Model\Transaction, Model\TransactionState @@ -51,7 +59,6 @@ use VRPaymentPayment\Core\{ Util\Payload\TransactionPayload }; - /** * Class CheckoutController * @@ -114,6 +121,11 @@ class CheckoutController extends StorefrontController { */ private $orderRoute; + /** + * @var \Psr\Cache\CacheItemPoolInterface + */ + private CacheItemPoolInterface $cache; + /** * PaymentController constructor. * @@ -124,7 +136,8 @@ class CheckoutController extends StorefrontController { * @param \Shopware\Storefront\Page\GenericPageLoaderInterface $genericLoader * @param \Shopware\Core\Checkout\Order\SalesChannel\AbstractOrderRoute $orderRoute * @param \Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStateHandler $orderTransactionStateHandler - * @param \Shopware\Core\System\StateMachine\StateMachineRegistry $stateMachineRegistry + * @param \Shopware\Core\System\StateMachine\StateMachineRegistry $stateMachineRegistry + * @param Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface $params */ public function __construct( LineItemFactoryRegistry $lineItemFactoryRegistry, @@ -134,7 +147,8 @@ class CheckoutController extends StorefrontController { GenericPageLoaderInterface $genericLoader, AbstractOrderRoute $orderRoute, OrderTransactionStateHandler $orderTransactionStateHandler, - StateMachineRegistry $stateMachineRegistry + StateMachineRegistry $stateMachineRegistry, + ParameterBagInterface $params ) { $this->cartService = $cartService; @@ -145,6 +159,7 @@ class CheckoutController extends StorefrontController { $this->orderRoute = $orderRoute; $this->orderTransactionStateHandler = $orderTransactionStateHandler; $this->stateMachineRegistry = $stateMachineRegistry; + $this->cache = new FilesystemAdapter('vrpayment', 0, rtrim($params->get('kernel.cache_dir'), '/') . '/vrpayment-cache'); } /** @@ -386,6 +401,32 @@ class CheckoutController extends StorefrontController { if($orderEntity->getSalesChannelId() !== $salesChannelContext->getSalesChannelId()) { $this->settings = $this->settingsService->getSettings($orderEntity->getSalesChannelId()); $trans = $this->getTransaction($orderId, $salesChannelContext->getContext()); + + // Adoption in case of duplicate requests + // Get order specific value from cache + $cacheKey = 'vrpayment_recreate_order_' . $orderId; + $isFound = $this->cache->get($cacheKey, function (ItemInterface $item) { + $item->expiresAfter(10); + return false; + }); + + // If value is found in cache - send user directly to successful checkout confirmation page for unpaid transactions + if ($isFound === true && in_array($trans->getState(), [TransactionState::FAILED])) { + $unpaidUrl = $this->getUnpaidUrlFromToken($trans->getSuccessUrl()) + ?? $this->buildUnpaidUrl($orderEntity->getSalesChannelId(), $salesChannelContext, $orderId); + if ($unpaidUrl) { + return new RedirectResponse( + $unpaidUrl . (parse_url($unpaidUrl, \PHP_URL_QUERY) ? '&' : '?') . 'error-code=' . PaymentException::PAYMENT_CUSTOMER_CANCELED_EXTERNAL + ); + } + } + + // Cache order specific value for some time on first request + $this->cache->delete($cacheKey); + $this->cache->get($cacheKey, function (ItemInterface $item) { + $item->expiresAfter(10); + return true; + }); return $this->redirect($trans->getSuccessUrl()); } // End Adoption for Headless Storefronts @@ -459,6 +500,74 @@ class CheckoutController extends StorefrontController { return $this->redirectToRoute('frontend.checkout.confirm.page'); } + /** + * Tries to return successful checkout confirmation url for unpaid transactions. + * + * It achieves that by getting payment token from successUrl, parsing and decoding + * it, and finally reading the claims. + * + * @param string $successUrl + * + * @return string|null + */ + private function getUnpaidUrlFromToken(string $successUrl): ?string { + $query = []; + parse_str((string) parse_url($successUrl, PHP_URL_QUERY), $query); + $jwt = $query['_sw_payment_token'] ?? null; + + if (!$jwt) { + return null; + } + + $data = explode('.', $jwt, 3); + if (count($data) !== 3) { + return null; + } + + [, $c, ] = $data; + + try { + $urlSafeData = strtr($c, '-_', '+/'); + $paddedData = str_pad($urlSafeData, \strlen($urlSafeData) % 4, '='); + $decoded = base64_decode($paddedData, true); + if (!$decoded) { + return null; + } + $claims = json_decode(json: $decoded, associative: true, flags: JSON_THROW_ON_ERROR); + $unpaidUrl = $claims['eul'] ?? null; + return $unpaidUrl; + } catch (\Throwable $e) { + $this->logger->warning("CheckoutController::getUnpaidUrlFromToken - JWT parse failed: {errorMessage}", [ + 'errorMessage' => $e->getMessage(), + ]); + return null; + } + } + + /** + * Tries to return successful checkout confirmation url for unpaid transactions. + * + * It achieves that by fetching headless storefront's base url, + * and building custom url. + * + * @param string $salesChannelId + * @param SalesChannelContext $salesChannelContext + * @param string $orderId + * + * @return string|null + */ + private function buildUnpaidUrl(string $salesChannelId, SalesChannelContext $salesChannelContext, string $orderId): ?string { + $salesChannelDomainRepo = $this->container->get('sales_channel_domain.repository'); + $criteria = new Criteria(); + $criteria->addFilter(new EqualsFilter('salesChannelId', $salesChannelId))->setLimit(10); + $domain = $salesChannelDomainRepo->search($criteria, $salesChannelContext->getContext())->first(); + if(!$domain) { + return null; + } + $baseUrl = rtrim($domain->getUrl(), '/'); + return sprintf('%s/checkout/success/%s/unpaid', $baseUrl, $orderId); + } + /** * @param OrderLineItemCollection $orderItems * diff --git a/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php b/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php index 9baf214..1c7ea24 100644 --- a/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php +++ b/src/Core/Storefront/Checkout/Subscriber/CheckoutSubscriber.php @@ -4,33 +4,41 @@ namespace VRPaymentPayment\Core\Storefront\Checkout\Subscriber; use Psr\Log\LoggerInterface; use Shopware\Core\{Checkout\Order\Aggregate\OrderTransaction\OrderTransactionCollection, - Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates, - Checkout\Order\OrderEntity, - Content\MailTemplate\Service\Event\MailBeforeValidateEvent}; + Checkout\Order\Aggregate\OrderTransaction\OrderTransactionStates, + Checkout\Order\OrderEntity, + Content\MailTemplate\Service\Event\MailBeforeValidateEvent}; +use Shopware\Core\Checkout\Payment\PaymentMethodCollection; +use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\MultiFilter; use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent; +use Shopware\Storefront\Page\Account\PaymentMethod\AccountPaymentMethodPageLoadedEvent; use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent; +use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use VRPaymentPayment\Core\{Api\Transaction\Service\OrderMailService, - Api\Transaction\Service\TransactionService, - Checkout\PaymentHandler\VRPaymentPaymentHandler, - Settings\Service\SettingsService, - Settings\Struct\Settings, - Util\PaymentMethodUtil}; +use VRPaymentPayment\Core\{Api\Transaction\Service\TransactionService, + Checkout\PaymentHandler\VRPaymentPaymentHandler, + Settings\Service\SettingsService, + Settings\Struct\Settings, + Util\PaymentMethodUtil}; use VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Service\PaymentMethodConfigurationService; use VRPaymentPayment\Sdk\{Model\AddressCreate, - Model\ChargeAttempt, - Model\CreationEntityState, - Model\CriteriaOperator, - Model\EntityQuery, - Model\EntityQueryFilter, - Model\EntityQueryFilterType, - Model\LineItemAttributeCreate, - Model\LineItemCreate, - Model\LineItemType, - Model\TaxCreate, - Model\Transaction, - Model\TransactionCreate, - Model\TransactionPending}; + Model\ChargeAttempt, + Model\CreationEntityState, + Model\CriteriaOperator, + Model\EntityQuery, + Model\EntityQueryFilter, + Model\EntityQueryFilterType, + Model\LineItemAttributeCreate, + Model\LineItemCreate, + Model\LineItemType, + Model\TaxCreate, + Model\Transaction, + Model\TransactionCreate, + Model\TransactionPending}; +use Shopware\Core\Framework\Struct\ArrayEntity; /** * Class CheckoutSubscriber @@ -65,6 +73,9 @@ class CheckoutSubscriber implements EventSubscriberInterface */ private $paymentMethodUtil; + /** @var EntityRepository */ + private EntityRepository $paymentMethodRepository; + /** * CheckoutSubscriber constructor. * @@ -73,12 +84,13 @@ class CheckoutSubscriber implements EventSubscriberInterface * @param \VRPaymentPayment\Core\Settings\Service\SettingsService $settingsService * @param \VRPaymentPayment\Core\Util\PaymentMethodUtil $paymentMethodUtil */ - public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService, TransactionService $transactionService, SettingsService $settingsService, PaymentMethodUtil $paymentMethodUtil) + public function __construct(PaymentMethodConfigurationService $paymentMethodConfigurationService, TransactionService $transactionService, SettingsService $settingsService, PaymentMethodUtil $paymentMethodUtil, EntityRepository $paymentMethodRepository) { - $this->paymentMethodConfigurationService = $paymentMethodConfigurationService; - $this->transactionService = $transactionService; - $this->settingsService = $settingsService; - $this->paymentMethodUtil = $paymentMethodUtil; + $this->paymentMethodConfigurationService = $paymentMethodConfigurationService; + $this->transactionService = $transactionService; + $this->settingsService = $settingsService; + $this->paymentMethodUtil = $paymentMethodUtil; + $this->paymentMethodRepository = $paymentMethodRepository; } /** @@ -99,8 +111,10 @@ class CheckoutSubscriber implements EventSubscriberInterface public static function getSubscribedEvents(): array { return [ - CheckoutConfirmPageLoadedEvent::class => ['onConfirmPageLoaded', 1], - MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1], + CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmLoaded', + AccountEditOrderPageLoadedEvent::class => 'onAccountOrderEditLoaded', + AccountPaymentMethodPageLoadedEvent::class => 'onAccountPaymentMethodLoaded', + MailBeforeValidateEvent::class => ['onMailBeforeValidate', 1], ]; } @@ -152,109 +166,231 @@ class CheckoutSubscriber implements EventSubscriberInterface } } - /** - * @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event - */ - public function onConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void - { - try { - $salesChannelContext = $event->getSalesChannelContext(); - $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId()); - if (is_null($settings)) { - $this->logger->notice('Removing payment methods because settings are invalid'); - $this->removeVRPaymentPaymentMethodFromConfirmPage($event); - } + /** + * @param CheckoutConfirmPageLoadedEvent $event + * @return void + */ + public function onCheckoutConfirmLoaded(CheckoutConfirmPageLoadedEvent $event): void + { + try { + $salesChannelContext = $event->getSalesChannelContext(); + $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId()); + if (is_null($settings)) { + $this->logger->notice('Removing payment methods because settings are invalid'); + $this->removeVRPaymentPaymentMethodFromConfirmPage($event); + } - $createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event); - $this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId); + $createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event); + $this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId); - $this->getAvailablePaymentMethods($settings, $createdTransactionId); - $this->setPossiblePaymentMethods($settings->getSpaceId(), $event); - } catch (\Exception $e) { - $this->logger->error($e->getMessage()); - $this->removeVRPaymentPaymentMethodFromConfirmPage($event); - } - } + $this->getAvailablePaymentMethods($settings, $createdTransactionId, $salesChannelContext); + $this->setPossiblePaymentMethods($settings->getSpaceId(), $event); + } catch (\Exception $e) { + $this->logger->error($e->getMessage()); + $this->removeVRPaymentPaymentMethodFromConfirmPage($event); + } + } - /** - * @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event - */ - private function removeVRPaymentPaymentMethodFromConfirmPage(CheckoutConfirmPageLoadedEvent $event): void - { - $paymentMethodCollection = $event->getPage()->getPaymentMethods(); - $paymentMethodIds = $this->paymentMethodUtil->getVRPaymentPaymentMethodIds($event->getContext()); - foreach ($paymentMethodIds as $paymentMethodId) { - $paymentMethodCollection->remove($paymentMethodId); - } - } + /** + * @param AccountEditOrderPageLoadedEvent $event + * @return void + */ + public function onAccountOrderEditLoaded(AccountEditOrderPageLoadedEvent $event): void + { + try { + $this->handlePaymentMethodFiltering($event); + } catch (\Throwable $e) { + $this->logger->error($e->getMessage()); + $this->removeVRPaymentPaymentMethodFromConfirmPage($event); + } + } - /** - * @param Settings $settings - * @param int $createdTransactionId - * @return void - */ - private function getAvailablePaymentMethods(Settings $settings, int $createdTransactionId): void - { - $transactionService = $settings->getApiClient()->getTransactionService(); - $possiblePaymentMethods = $transactionService->fetchPaymentMethods( - $settings->getSpaceId(), - $createdTransactionId, - $settings->getIntegration() - ); - $arrayOfPossibleMethods = []; - foreach ($possiblePaymentMethods as $possiblePaymentMethod) { - $arrayOfPossibleMethods[] = $possiblePaymentMethod->getid(); - } - $_SESSION['arrayOfPossibleMethods'] = $arrayOfPossibleMethods; - } + /** + * @param AccountPaymentMethodPageLoadedEvent $event + * @return void + */ + public function onAccountPaymentMethodLoaded(AccountPaymentMethodPageLoadedEvent $event): void + { + try { + $this->handlePaymentMethodFiltering($event); + } catch (\Throwable $e) { + $this->logger->error($e->getMessage()); + $this->removeVRPaymentPaymentMethodFromConfirmPage($event); + } + } - /** - * @param int $spaceId - * @param CheckoutConfirmPageLoadedEvent $event - * @return void - */ - private function setPossiblePaymentMethods(int $spaceId, CheckoutConfirmPageLoadedEvent $event): void - { - $localPaymentMethods = []; - $paymentMethodConfigurations = $this->paymentMethodConfigurationService->getAllPaymentMethodConfigurations($spaceId, $event->getSalesChannelContext()->getContext()); - foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) { - $localPaymentMethods[$paymentMethodConfiguration->getId()] = $paymentMethodConfiguration->getPaymentMethodConfigurationId(); - } + /** + * @param \Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent $event + */ + public function onConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $event): void + { + try { + $this->handlePaymentMethodFiltering($event); + } catch (\Throwable $e) { + $this->logger->error($e->getMessage()); + $this->removeVRPaymentPaymentMethodFromConfirmPage($event); + } + } - $paymentMethodCollection = $event->getPage()->getPaymentMethods(); - foreach ($paymentMethodCollection as $paymentMethodCollectionItem) { - $isVRPaymentPM = VRPaymentPaymentHandler::class == $paymentMethodCollectionItem->getHandlerIdentifier(); - if (!$isVRPaymentPM) { - continue; - } + /** + * @param $event + * @return void + */ + private function handlePaymentMethodFiltering($event): void + { + $salesChannelContext = $event->getSalesChannelContext(); + $settings = $this->settingsService->getValidSettings($salesChannelContext->getSalesChannel()->getId()); - $paymentMethodConfigurationId = $localPaymentMethods[$paymentMethodCollectionItem->getId()]; - if (!\in_array($paymentMethodConfigurationId, $_SESSION['arrayOfPossibleMethods'])) { - $paymentMethodCollection->remove($paymentMethodCollectionItem->getId()); - } - } - } + if (is_null($settings)) { + $this->logger->notice('Removing payment methods because settings are invalid'); + $this->removeVRPaymentPaymentMethodFromConfirmPage($event); + return; + } - /** - * @param SalesChannelContext $salesChannelContext - * @param int $createdTransactionId - * @return void - */ - private function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $createdTransactionId): void - { - $addressCheck = $_SESSION['addressCheck'] ?? null; - $currencyCheck = $_SESSION['currencyCheck'] ?? null; + $createdTransactionId = $this->transactionService->createPendingTransaction($salesChannelContext, $event); + $this->updateTempTransactionIfNeeded($salesChannelContext, $createdTransactionId); - $customer = $salesChannelContext->getCustomer(); - $addressHash = md5(json_encode((array)$customer)); - $currency = $salesChannelContext->getCurrency()->getIsoCode(); - if (($addressCheck && $currencyCheck) && $addressCheck !== $addressHash || $currencyCheck !== $currency) { - if ($createdTransactionId) { - $this->transactionService->updateTempTransaction($salesChannelContext, $createdTransactionId); - } - $_SESSION['arrayOfPossibleMethods'] = null; - $_SESSION['addressCheck'] = $addressHash; - $_SESSION['currencyCheck'] = $currency; - } - } + $this->getAvailablePaymentMethods($settings, $createdTransactionId, $salesChannelContext); + $this->setPossiblePaymentMethods($settings->getSpaceId(), $event); + } + + /** + * @param $event + * @return void + */ + private function removeVRPaymentPaymentMethodFromConfirmPage($event): void + { + $paymentMethodCollection = $event->getPage()->getPaymentMethods(); + $paymentMethodIds = $this->paymentMethodUtil->getVRPaymentPaymentMethodIds($event->getContext()); + foreach ($paymentMethodIds as $paymentMethodId) { + $paymentMethodCollection->remove($paymentMethodId); + } + } + + /** + * @param Settings $settings + * @param int $createdTransactionId + * @return void + */ + private function getAvailablePaymentMethods(Settings $settings, int $createdTransactionId, SalesChannelContext $salesChannelContext): void + { + $transactionService = $settings->getApiClient()->getTransactionService(); + $possiblePaymentMethods = $transactionService->fetchPaymentMethods( + $settings->getSpaceId(), + $createdTransactionId, + $settings->getIntegration() + ); + $arrayOfPossibleMethods = []; + foreach ($possiblePaymentMethods as $possiblePaymentMethod) { + $arrayOfPossibleMethods[] = $possiblePaymentMethod->getId(); + } + + $salesChannelContext->getContext()->addExtension( + 'possibleMethods', + new ArrayEntity(['ids' => $arrayOfPossibleMethods]) + ); + } + + /** + * @param int $spaceId + * @param CheckoutConfirmPageLoadedEvent $event + * @return void + */ + private function setPossiblePaymentMethods(int $spaceId, $event): void + { + $paymentIds = []; + $paymentMethodCollection = $event->getPage()->getPaymentMethods(); + + foreach ($paymentMethodCollection as $paymentMethodCollectionItem) { + $isVRPaymentPM = VRPaymentPaymentHandler::class === $paymentMethodCollectionItem->getHandlerIdentifier(); + if (!$isVRPaymentPM) { + $paymentIds[] = $paymentMethodCollectionItem->getId(); + } + } + + $allowedWLMethods = []; + $paymentMethodConfigurations = $this->paymentMethodConfigurationService + ->getAllPaymentMethodConfigurations($spaceId, $event->getSalesChannelContext()->getContext()); + + foreach ($paymentMethodConfigurations as $paymentMethodConfiguration) { + if ($paymentMethodConfiguration->getPaymentMethod() === null) { + continue; + } + + $pmId = $paymentMethodConfiguration->getPaymentMethod()->getId(); + $pmConfigId = $paymentMethodConfiguration->getPaymentMethodConfigurationId(); + $allowedIds = $this->getAllowedPaymentMethodIds($event->getSalesChannelContext()); + + if ($paymentMethodConfiguration->getSpaceId() === $spaceId + && \in_array($pmConfigId, $allowedIds, true)) { + $allowedWLMethods[] = $pmId; + } + } + + $allPaymentIds = array_unique(array_merge($paymentIds, $allowedWLMethods)); + $collection = new PaymentMethodCollection(); + if (!empty($allPaymentIds)) { + $criteria = new Criteria($allPaymentIds); + $criteria->addFilter(new EqualsFilter('active', true)); + $criteria->addFilter( + new EqualsFilter('salesChannels.id', $event->getSalesChannelContext()->getSalesChannelId()) + ); + + $result = $this->paymentMethodRepository->search($criteria, $event->getContext()); + foreach ($result->getEntities() as $method) { + if (!$collection->has($method->getId())) { + $collection->add($method); + } + } + } + + $event->getPage()->setPaymentMethods($collection); + } + + /** + * @param SalesChannelContext $salesChannelContext + * @param int $createdTransactionId + * @return void + */ + private function updateTempTransactionIfNeeded(SalesChannelContext $salesChannelContext, int $createdTransactionId): void + { + $ctx = $salesChannelContext->getContext(); + + /** @var ArrayEntity|null $ext */ + $ext = $ctx->getExtension('checkoutState'); + + $oldAddressHash = $ext instanceof ArrayEntity ? $ext->get('addressHash') : null; + $oldCurrency = $ext instanceof ArrayEntity ? $ext->get('currency') : null; + + $customer = $salesChannelContext->getCustomer(); + $addressHash = md5(json_encode((array) $customer)); + $currency = $salesChannelContext->getCurrency()->getIsoCode(); + + $needsUpdate = ($oldAddressHash !== $addressHash) || ($oldCurrency !== $currency); + + if ($needsUpdate) { + if ($createdTransactionId) { + $this->transactionService->updateTempTransaction($salesChannelContext, $createdTransactionId); + } + + $ctx->addExtension('possibleMethods', new ArrayEntity(['ids' => []])); + $ctx->addExtension( + 'checkoutState', + new ArrayEntity([ + 'addressHash' => $addressHash, + 'currency' => $currency, + ]) + ); + } + } + + /** + * @param SalesChannelContext $salesChannelContext + * @return array + */ + private function getAllowedPaymentMethodIds(SalesChannelContext $salesChannelContext): array + { + $ext = $salesChannelContext->getContext()->getExtension('possibleMethods'); + return $ext instanceof ArrayEntity ? ($ext->get('ids') ?? []) : []; + } } diff --git a/src/Core/Util/Analytics/Analytics.php b/src/Core/Util/Analytics/Analytics.php index e3ff536..dcde480 100644 --- a/src/Core/Util/Analytics/Analytics.php +++ b/src/Core/Util/Analytics/Analytics.php @@ -3,6 +3,7 @@ namespace VRPaymentPayment\Core\Util\Analytics; use VRPayment\Sdk\ApiClient; +use Shopware\Core\Kernel; /** * Class Analytics @@ -19,26 +20,78 @@ class Analytics { /** * @return array */ - public static function getDefaultData() + public static function getDefaultData(): array { + $shopwareVersion = self::getShopwareVersion(); + return [ - self::SHOP_SYSTEM => 'shopware', - self::SHOP_SYSTEM_VERSION => '6', - self::SHOP_SYSTEM_AND_VERSION => 'shopware-6', - self::PLUGIN_SYSTEM_VERSION => '6.1.16', + self::SHOP_SYSTEM => 'shopware', + self::SHOP_SYSTEM_VERSION => $shopwareVersion, + self::SHOP_SYSTEM_AND_VERSION => 'shopware-' . $shopwareVersion, + self::PLUGIN_SYSTEM_VERSION => '6.1.17', ]; } /** * @param \VRPayment\Sdk\ApiClient $apiClient */ - public static function addHeaders(ApiClient &$apiClient) + public static function addHeaders(ApiClient &$apiClient): void { $data = self::getDefaultData(); foreach ($data as $key => $value) { $apiClient->addDefaultHeader($key, $value); } } + + /** + * Reads Shopware version and caches it for performance. + * + * @return string + */ + public static function getShopwareVersion(): string + { + static $cachedVersion = null; + + if ($cachedVersion !== null) { + return $cachedVersion; + } + + $basePath = dirname(__DIR__, 7); + $installedFile = $basePath . '/vendor/composer/installed.php'; + + if (is_file($installedFile)) { + $installed = include $installedFile; + $packages = []; + + if (isset($installed['versions'])) { + $packages = $installed['versions']; + } elseif (is_array($installed)) { + foreach ($installed as $section) { + if (isset($section['versions'])) { + $packages = $section['versions']; + break; + } + } + } + + if (isset($packages['shopware/core']['pretty_version'])) { + return $cachedVersion = ltrim($packages['shopware/core']['pretty_version'], 'v'); + } + } + + $lockFile = $basePath . '/composer.lock'; + if (is_file($lockFile)) { + $data = json_decode((string) file_get_contents($lockFile), true); + if (!empty($data['packages'])) { + foreach ($data['packages'] as $package) { + if (($package['name'] ?? '') === 'shopware/core') { + return $cachedVersion = ltrim($package['version'], 'v'); + } + } + } + } + + return $cachedVersion = Kernel::SHOPWARE_FALLBACK_VERSION; + } } - diff --git a/src/Core/Util/Payload/TransactionPayload.php b/src/Core/Util/Payload/TransactionPayload.php index 8f9bc59..d2bcb89 100644 --- a/src/Core/Util/Payload/TransactionPayload.php +++ b/src/Core/Util/Payload/TransactionPayload.php @@ -12,6 +12,7 @@ use Shopware\Core\{Checkout\Cart\Tax\Struct\CalculatedTaxCollection, Framework\DataAbstractionLayer\Search\Criteria, System\SalesChannel\SalesChannelContext }; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Contracts\Translation\TranslatorInterface; use VRPayment\Sdk\{Model\AddressCreate, @@ -193,10 +194,16 @@ class TransactionPayload extends AbstractPayload ->setShippingAddress($shippingAddress) ->setShippingMethod($transactionData['shipping_method']); - $paymentConfiguration = $this->getPaymentConfiguration($this->salesChannelContext->getPaymentMethod()->getId()); - - $transactionPayload->setAllowedPaymentMethodConfigurations([$paymentConfiguration->getPaymentMethodConfigurationId()]); + $paymentConfiguration = $this->getPaymentConfiguration( + $this->salesChannelContext->getPaymentMethod()->getId(), + $this->settings->getSpaceId() + ); + if ($paymentConfiguration) { + $transactionPayload->setAllowedPaymentMethodConfigurations([ + $paymentConfiguration->getPaymentMethodConfigurationId() + ]); + } $successUrl = $this->transaction->getReturnUrl() . '&status=paid'; $failedUrl = $this->getFailUrl($this->transaction->getOrder()->getId()) . '&status=fail'; $transactionPayload->setSuccessUrl($successUrl) @@ -210,6 +217,23 @@ class TransactionPayload extends AbstractPayload return $transactionPayload; } + + /** + * @param string $paymentMethodId + * @param int $spaceId + * @return PaymentMethodConfigurationEntity|null + */ + protected function getPaymentConfiguration(string $paymentMethodId, int $spaceId): ?PaymentMethodConfigurationEntity + { + $criteria = new Criteria(); + $criteria->addFilter(new EqualsFilter('paymentMethodId', $paymentMethodId)); + $criteria->addFilter(new EqualsFilter('spaceId', $spaceId)); + + return $this->container->get('vrpayment_payment_method_configuration.repository') + ->search($criteria, $this->salesChannelContext->getContext()) + ->first(); + } + /** * Get transaction line items * @@ -792,20 +816,6 @@ class TransactionPayload extends AbstractPayload return $addressPayload; } - /** - * @param string $id - * - * @return \VRPaymentPayment\Core\Api\PaymentMethodConfiguration\Entity\PaymentMethodConfigurationEntity - */ - protected function getPaymentConfiguration(string $id): PaymentMethodConfigurationEntity - { - $criteria = (new Criteria([$id])); - - return $this->container->get('vrpayment_payment_method_configuration.repository') - ->search($criteria, $this->salesChannelContext->getContext()) - ->getEntities()->first(); - } - /** * Get failure URL * diff --git a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig index 90f9b5e..1e16db3 100644 --- a/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig +++ b/src/Resources/app/administration/src/module/vrpayment-settings/component/sw-vrpayment-credentials/index.html.twig @@ -14,7 +14,7 @@ {% block vrpayment_settings_content_card_channel_config_credentials_card_container_settings_space_id %}