diff --git a/CHANGELOG_de-DE.md b/CHANGELOG_de-DE.md index 4c5d7067..fdad6386 100644 --- a/CHANGELOG_de-DE.md +++ b/CHANGELOG_de-DE.md @@ -1,3 +1,11 @@ +# 6.5.8 +* Der Express-Checkout funktioniert unter Shopware 6.6 wieder zuverlässig, einschließlich PayPal und Apple Pay +* Beim Express-Checkout wird nun eine verständliche Fehlermeldung angezeigt, wenn die Erstellung der Zahlung fehlschlägt, anstatt den Kunden kommentarlos weiterzuleiten +* Bestellbestätigungen werden nun in der korrekten Sprache versendet und gehen nicht mehr verloren +* Benutzer ohne Berechtigung für die Erweiterung können die Plugin-Einstellungen nicht mehr anzeigen oder bearbeiten +* Produktbilder im Warenkorb werden nun automatisch in einer geeigneten Größe verarbeitet und verursachen keine Gateway-Fehler mehr +* Bestell- und Zahlungsstatus für Direktüberweisung werden nun zuverlässig anhand des Zahlungs- und Transaktionsstatus aktualisiert + # 6.5.7 * Zuverlässigerer Checkout: Der „Bezahlen“-Button wartet jetzt, bis alle erforderlichen Komponenten vollständig geladen sind, wodurch fehlgeschlagene Bestellungen verhindert werden. * Leistungsverbesserung: Ein redundanter Abruf der Händlerkonfiguration wurde entfernt, wenn die Konfiguration bereits vorhanden ist. diff --git a/CHANGELOG_en-GB.md b/CHANGELOG_en-GB.md index f212177b..80c33524 100644 --- a/CHANGELOG_en-GB.md +++ b/CHANGELOG_en-GB.md @@ -1,3 +1,11 @@ +# 6.5.8 +* Express checkout no longer fails in Shopware 6.6, including PayPal and Apple Pay +* Express checkout now shows a clear error message when payment creation fails, instead of silently redirecting +* Order confirmation emails are sent in the correct language and no longer go missing +* Users without extension permissions can no longer view or edit the plugin settings +* Basket item images are resized so they no longer cause gateway errors +* Order and payment status for Direct Bank Transfer now update correctly based on payment and transaction state + # 6.5.7 * More reliable checkout: The "Pay" button now waits until all required components are fully loaded, preventing failed orders. * Performance improvement: Eliminated a redundant merchant config fetch when the config is already provided. diff --git a/composer.json b/composer.json index b528d104..d974f22e 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "unzerdev/shopware6", "description": "Unzer payment integration for Shopware 6", - "version": "6.5.7", + "version": "6.5.8", "type": "shopware-platform-plugin", "license": "Apache-2.0", "minimum-stability": "dev", diff --git a/src/Components/AddressHashGenerator/AddressHashGenerator.php b/src/Components/AddressHashGenerator/AddressHashGenerator.php deleted file mode 100644 index 927f6bb5..00000000 --- a/src/Components/AddressHashGenerator/AddressHashGenerator.php +++ /dev/null @@ -1,36 +0,0 @@ - [ - 'countryId' => $billingAddress->getCountryId(), - 'firstName' => $billingAddress->getFirstName(), - 'lastName' => $billingAddress->getLastName(), - 'zipCode' => $billingAddress->getZipcode(), - 'city' => $billingAddress->getCity(), - 'street' => $billingAddress->getStreet(), - 'company' => $billingAddress->getCompany(), - ], - 'shipping' => [ - 'countryId' => $shippingAddress->getCountryId(), - 'firstName' => $shippingAddress->getFirstName(), - 'lastName' => $shippingAddress->getLastName(), - 'zipCode' => $shippingAddress->getZipcode(), - 'city' => $shippingAddress->getCity(), - 'street' => $shippingAddress->getStreet(), - 'company' => $shippingAddress->getCompany(), - ], - ]; - - return md5(serialize($data)); - } -} diff --git a/src/Components/AddressHashGenerator/AddressHashGeneratorInterface.php b/src/Components/AddressHashGenerator/AddressHashGeneratorInterface.php deleted file mode 100644 index c2fde7c4..00000000 --- a/src/Components/AddressHashGenerator/AddressHashGeneratorInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -getCustomer(); - if (empty($orderTransaction->getOrder())) { - $orderTransaction = $this->fetchTransactionById($orderTransaction->getId(), $salesChannelContext->getContext()); - } + // re-fetch to make sure all necessary fields are filled + $orderTransaction = $this->fetchTransactionById($orderTransaction->getId(), $salesChannelContext->getContext()); + $fetchedCustomer = null; if (!empty($unzerCustomerId)) { diff --git a/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php b/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php index 592e3870..222ce6cf 100644 --- a/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php @@ -237,11 +237,15 @@ public function finalize( && $this->paymentType instanceof Paypal && $this->paymentType->getEmail() !== null ) { - $this->saveToDeviceVault( - $salesChannelContext->getCustomer(), - UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL, - $salesChannelContext->getContext() - ); + try { + $this->saveToDeviceVault( + $salesChannelContext->getCustomer(), + UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL, + $salesChannelContext->getContext() + ); + } catch (\Throwable $e) { + $this->logger->warning('Could not save to device vault: ' . $e->getMessage()); + } } $this->transactionStateHandler->transformTransactionState( diff --git a/src/Components/PaymentTransitionMapper/OpenBankingTransitionMapper.php b/src/Components/PaymentTransitionMapper/OpenBankingTransitionMapper.php index 1f59a7b8..e025889a 100644 --- a/src/Components/PaymentTransitionMapper/OpenBankingTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/OpenBankingTransitionMapper.php @@ -4,13 +4,32 @@ namespace UnzerPayment6\Components\PaymentTransitionMapper; -use UnzerPayment6\Components\PaymentTransitionMapper\Traits\IsBasicPaymentMethodTransitionMapper; +use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions; +use UnzerPayment6\Components\PaymentTransitionMapper\Exception\TransitionMapperException; +use UnzerSDK\Resources\Payment; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; use UnzerSDK\Resources\PaymentTypes\OpenbankingPis; class OpenBankingTransitionMapper extends AbstractTransitionMapper { - use IsBasicPaymentMethodTransitionMapper; + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string + { + try { + $charges = $paymentObject->getCharges(); + $charge = reset($charges); + if ($paymentObject->isCompleted() && $charge->isPending()) { + return StateMachineTransitionActions::ACTION_REOPEN; + } + + return parent::getTargetPaymentStatus($paymentObject, $orderTransactionId); + } catch (TransitionMapperException $exception) { + if ($paymentObject->isPending()) { + return StateMachineTransitionActions::ACTION_REOPEN; + } + + throw $exception; + } + } public function supports(BasePaymentType $paymentType): bool { diff --git a/src/Components/ResourceHydrator/BasketResourceHydrator.php b/src/Components/ResourceHydrator/BasketResourceHydrator.php index 491d9e40..241a997b 100755 --- a/src/Components/ResourceHydrator/BasketResourceHydrator.php +++ b/src/Components/ResourceHydrator/BasketResourceHydrator.php @@ -108,15 +108,6 @@ protected function hydrateLineItems( $basketItem->setTitle($label); $basketItem->setQuantity($lineItem->getQuantity()); $basketItem->setType($lineItem->getUnitPrice() < 0 ? BasketItemTypes::VOUCHER : BasketItemTypes::GOODS); - if (!empty($lineItem->getCover()?->getUrl()) && !str_contains($lineItem->getCover()?->getUrl(), '.ddev.site')) { - try { - $media = $lineItem->getCover(); - $url = $media?->getThumbnails()?->first()?->getUrl() ?? $media?->getUrl(); - $basketItem->setImageUrl($url); - } catch (\Exception $e) { - $basketItem->setImageUrl($lineItem->getCover()?->getUrl()); - } - } $taxCounter = 0; $amountTax = 0.0; @@ -169,14 +160,12 @@ protected function hydrateShippingCosts( $amountPerUnit = round($shippingCosts->getUnitPrice(), $currencyPrecision); } else { $priceGross = 0.00; - $amountVat = 0.00; $taxRate = 0; $taxCounter = 0; /** @var CalculatedTax $tax */ foreach ($shippingCosts->getCalculatedTaxes() as $tax) { $priceGross += $tax->getPrice(); - $amountVat += $tax->getTax(); $taxRate += $tax->getTaxRate(); ++$taxCounter; diff --git a/src/Components/Struct/Webhook.php b/src/Components/Struct/Webhook.php index 0dc1273f..8a651621 100644 --- a/src/Components/Struct/Webhook.php +++ b/src/Components/Struct/Webhook.php @@ -12,6 +12,8 @@ class Webhook private string $retrieveUrl; + private string $paymentId; + public function __construct(string $jsonData) { $this->fromJson($jsonData); @@ -24,6 +26,7 @@ public function fromJson(string $jsonData): void $this->event = $webhookData['event'] ?? ''; $this->publicKey = $webhookData['publicKey'] ?? ''; $this->retrieveUrl = $webhookData['retrieveUrl'] ?? ''; + $this->paymentId = $webhookData['paymentId'] ?? ''; } public function getEvent(): string @@ -61,4 +64,16 @@ public function setRetrieveUrl(string $retrieveUrl): self return $this; } + + public function getPaymentId(): string + { + return $this->paymentId; + } + + public function setPaymentId(string $paymentId): self + { + $this->paymentId = $paymentId; + + return $this; + } } diff --git a/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php b/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php index 76f16dbb..94a8b4ec 100644 --- a/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php +++ b/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php @@ -4,12 +4,15 @@ namespace UnzerPayment6\Components\WebhookHandler; +use Doctrine\DBAL\Connection; use Psr\Log\LoggerInterface; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; +use Shopware\Core\Defaults; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\Uuid\Exception\InvalidUuidException; +use Shopware\Core\Framework\Uuid\Uuid; use Shopware\Core\System\SalesChannel\SalesChannelContext; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\CustomFieldsHelper\CustomFieldsHelperInterface; @@ -27,19 +30,25 @@ public function __construct( private readonly ClientFactoryInterface $clientFactory, private readonly EntityRepository $orderTransactionRepository, private readonly LoggerInterface $logger, - private readonly CustomFieldsHelperInterface $customFieldsHelper + private readonly CustomFieldsHelperInterface $customFieldsHelper, + private readonly Connection $connection ) { } public function supports(Webhook $webhook, SalesChannelContext $context): bool { - return stripos($webhook->getEvent(), 'payment.') !== false; + return stripos($webhook->getEvent(), 'payment.') !== false || stripos($webhook->getEvent(), 'charge.succeeded') !== false; } public function execute(Webhook $webhook, SalesChannelContext $context): void { $client = $this->clientFactory->createClientFromPublicKey($webhook->getPublicKey(), $context->getSalesChannelId()); - $payment = $client->getResourceService()->fetchResourceByUrl($webhook->getRetrieveUrl()); + + if (stripos($webhook->getEvent(), 'charge.') !== false) { + $payment = $client->fetchPayment($webhook->getPaymentId()); + } else { + $payment = $client->getResourceService()->fetchResourceByUrl($webhook->getRetrieveUrl()); + } if (!$payment instanceof Payment) { $this->logger->error( @@ -65,7 +74,7 @@ public function execute(Webhook $webhook, SalesChannelContext $context): void return; } - $context->getContext()->assign(['languageIdChain' => [$transaction->getOrder()->getLanguageId()]]); + $context->getContext()->assign(['languageIdChain' => $this->getLanguageChain($transaction->getOrder()->getLanguageId())]); $this->customFieldsHelper->setOrderTransactionCustomFields($transaction, $context->getContext()); $this->transactionStateHandler->transformTransactionState( @@ -94,4 +103,15 @@ private function getOrderTransaction(?string $orderId, Context $context): ?Order return null; } } + + private function getLanguageChain(string $languageId): array + { + $parent = $this->connection->fetchOne( + 'SELECT LOWER(HEX(language.parent_id)) FROM language WHERE language.id = :languageId', + ['languageId' => Uuid::fromHexToBytes($languageId)] + ); + $chain = array_filter(array_unique([$languageId, $parent, Defaults::LANGUAGE_SYSTEM])); + + return $chain; + } } diff --git a/src/Components/WebhookRegistrator/WebhookRegistrator.php b/src/Components/WebhookRegistrator/WebhookRegistrator.php index a41b5cbe..642f9a23 100644 --- a/src/Components/WebhookRegistrator/WebhookRegistrator.php +++ b/src/Components/WebhookRegistrator/WebhookRegistrator.php @@ -35,6 +35,48 @@ public function __construct( ) { } + public function registerWebhookDirectly(RequestDataBag $requestDataBag): array + { + $returnData = []; + $url = $requestDataBag->get('url', ''); + $privateKey = $requestDataBag->get('privateKey'); + try { + $relativePath = $this->router->generate('frontend.unzer.webhook.execute', [], UrlGeneratorInterface::ABSOLUTE_PATH); + if (!str_contains($url, $relativePath)) { + $url .= $relativePath; + } + $result = $this->clientFactory + ->createClientFromPrivateKey($privateKey) + ->createWebhook($url, 'all'); + + $returnData = [ + 'success' => true, + 'url' => $url, + 'data' => $result, + 'message' => 'unzer-payment-settings.webhook.register.done', + ]; + + $this->logger->info(\sprintf('Webhooks registered for domain %s', $url)); + } catch (UnzerApiException|\Throwable $exception) { + $returnData[$url] = [ + 'success' => false, + 'message' => 'unzer-payment-settings.webhook.register.error', + ]; + + $this->logger->error( + \sprintf('Webhook registration failed for domain %s', $url), + [ + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'trace' => $exception->getTraceAsString(), + ] + ); + } + + return $returnData; + } + public function registerWebhook(RequestDataBag $salesChannelDomains): array { $returnData = []; @@ -54,7 +96,9 @@ public function registerWebhook(RequestDataBag $salesChannelDomains): array try { $relativePath = $this->router->generate('frontend.unzer.webhook.execute', [], UrlGeneratorInterface::ABSOLUTE_PATH); - $url = $domainUrl . $relativePath; + if (!str_contains($domainUrl, $relativePath)) { + $url = $domainUrl . $relativePath; + } $result = $this->clientFactory ->createClientFromPrivateKey($privateKey, $salesChannelId) diff --git a/src/Controllers/Administration/UnzerPaymentConfigurationController.php b/src/Controllers/Administration/UnzerPaymentConfigurationController.php index 7388f8c8..9a68725a 100644 --- a/src/Controllers/Administration/UnzerPaymentConfigurationController.php +++ b/src/Controllers/Administration/UnzerPaymentConfigurationController.php @@ -104,6 +104,15 @@ public function registerWebhooks(RequestDataBag $dataBag): JsonResponse ); } + #[Route(path: '/api/_action/unzer-payment/register-webhook-directly', name: 'api.action.unzer.webhook.register-directly', methods: ['POST'])] + public function registerWebhookDirectly(RequestDataBag $dataBag): JsonResponse + { + return new JsonResponse( + $this->webhookRegistrator->registerWebhookDirectly($dataBag), + 200 + ); + } + #[Route(path: '/api/_action/unzer-payment/clear-webhooks', name: 'api.action.unzer.webhooks.clear', methods: ['POST'])] public function clearWebhooks(RequestDataBag $dataBag): JsonResponse { diff --git a/src/Controllers/Administration/UnzerPaymentTransactionController.php b/src/Controllers/Administration/UnzerPaymentTransactionController.php index 64d16336..a9e5a3bf 100644 --- a/src/Controllers/Administration/UnzerPaymentTransactionController.php +++ b/src/Controllers/Administration/UnzerPaymentTransactionController.php @@ -33,7 +33,7 @@ class UnzerPaymentTransactionController extends AbstractController public function __construct( private readonly ClientFactoryInterface $clientFactory, private readonly UnzerTransactionUtil $unzerTransactionUtil, - private readonly PaymentActionService $paymentTransactionService, + private readonly PaymentActionService $paymentActionService, private readonly PaymentResourceHydratorInterface $hydrator, private readonly CancelServiceInterface $cancelService, private readonly ShipServiceInterface $shipService, @@ -197,7 +197,7 @@ public function unifiedRefund(Request $request, Context $context): JsonResponse $comment = (string) $request->get('comment', ''); try { - $this->paymentTransactionService->doUnifiedRefund( + $this->paymentActionService->doUnifiedRefund( orderTransaction: $orderTransaction, amount: $amount, context: $context, diff --git a/src/Controllers/Storefront/UnzerExpressCheckoutController.php b/src/Controllers/Storefront/UnzerExpressCheckoutController.php index 5b5133c8..8352f2e2 100644 --- a/src/Controllers/Storefront/UnzerExpressCheckoutController.php +++ b/src/Controllers/Storefront/UnzerExpressCheckoutController.php @@ -18,25 +18,21 @@ use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Components\ResourceHydrator\MetadataResourceHydrator; -use UnzerPayment6\Components\ResourceHydrator\ResourceHydratorInterface; use UnzerPayment6\Installer\PaymentInstaller; use UnzerSDK\Resources\Basket; use UnzerSDK\Resources\EmbeddedResources\BasketItem; -use UnzerSDK\Resources\Metadata; use UnzerSDK\Resources\TransactionTypes\Authorization; use UnzerSDK\Resources\TransactionTypes\Charge; #[Route(defaults: ['_routeScope' => ['storefront']])] class UnzerExpressCheckoutController extends StorefrontController { - /** - * @param MetadataResourceHydrator $metadataResourceHydrator - */ public function __construct( private readonly ExpressCheckoutService $expressCheckoutService, - private readonly ResourceHydratorInterface $metadataResourceHydrator, + private readonly MetadataResourceHydrator $metadataResourceHydrator, protected readonly ConfigReaderInterface $configReader, protected readonly LoggerInterface $logger, + protected readonly ClientFactory $clientFactory, ) { } @@ -44,9 +40,7 @@ public function __construct( public function paypalExpress(Request $request, SalesChannelContext $salesChannelContext): Response { $paymentTypeId = $request->get('paymentTypeId'); - /** @var ClientFactory $clientFactory */ - $clientFactory = $this->container->get(ClientFactory::class); - $client = $clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $request); + $client = $this->clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $request); $shopwareCart = $this->expressCheckoutService->getCart($salesChannelContext); $basket = (new Basket()) @@ -59,7 +53,6 @@ public function paypalExpress(Request $request, SalesChannelContext $salesChanne $basket->addBasketItem($basketItem); $basketResult = $client->createBasket($basket); - /** @var Metadata $metaData */ $metaData = $this->metadataResourceHydrator->hydrateObject($salesChannelContext); $this->metadataResourceHydrator->setIsExpress($metaData, true); @@ -102,9 +95,8 @@ public function paypalExpressReturn(Request $request, SalesChannelContext $sales return $this->redirectToRoute('frontend.checkout.cart.page'); // TODO } - /** @var ClientFactory $clientFactory */ - $clientFactory = $this->container->get(ClientFactory::class); - $client = $clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $request); + + $client = $this->clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $request); $payment = $client->fetchPayment($paymentId); try { diff --git a/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php b/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php index 4443d200..b759a4ac 100644 --- a/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php +++ b/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php @@ -24,6 +24,9 @@ class UnzerPaymentDeviceEntity extends Entity protected array $data; + /** + * @deprecated To be removed in future release + */ protected string $addressHash; public function getCustomerId(): string diff --git a/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php b/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php index f420f7b4..82d9f85d 100644 --- a/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php +++ b/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php @@ -12,13 +12,12 @@ class OrderTransactionExtension extends EntityExtension { - /** - * {@inheritdoc} - */ + public const TRANSFER_INFO_EXTENSION = 'transferInfo'; + public function extendFields(FieldCollection $collection): void { $collection->add( - (new ObjectField('transfer_info', 'transferInfo'))->addFlags(new Runtime()) + (new ObjectField('transfer_info', self::TRANSFER_INFO_EXTENSION))->addFlags(new Runtime()) ); } diff --git a/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php b/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php index e6194180..8da40a55 100644 --- a/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php +++ b/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php @@ -12,32 +12,22 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult; use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter; use Shopware\Core\Framework\Uuid\Uuid; -use UnzerPayment6\Components\AddressHashGenerator\AddressHashGeneratorInterface; use UnzerPayment6\DataAbstractionLayer\Entity\PaymentDevice\UnzerPaymentDeviceEntity; readonly class UnzerPaymentDeviceRepository implements UnzerPaymentDeviceRepositoryInterface { + public const DEFAULT_ADDRESS_HASH = 'no_hash'; + public function __construct( - private EntityRepository $entityRepository, - private AddressHashGeneratorInterface $addressHashService + private EntityRepository $entityRepository ) { } - /** - * {@inheritdoc} - */ public function getCollectionByCustomer(CustomerEntity $customer, Context $context, ?string $deviceType = null): EntitySearchResult { - if ($customer->getActiveBillingAddress() === null || $customer->getActiveShippingAddress() === null) { - throw new \RuntimeException('Customer has no active billing or shipping address'); - } - - $addressHash = $this->addressHashService->generateHash($customer->getActiveBillingAddress(), $customer->getActiveShippingAddress()); - $criteria = new Criteria(); $criteria->addFilter( - new EqualsFilter('customerId', $customer->getId()), - new EqualsFilter('addressHash', $addressHash) + new EqualsFilter('customerId', $customer->getId()) ); if ($deviceType) { @@ -47,9 +37,6 @@ public function getCollectionByCustomer(CustomerEntity $customer, Context $conte return $this->entityRepository->search($criteria, $context); } - /** - * {@inheritdoc} - */ public function create( CustomerEntity $customer, string $deviceType, @@ -57,19 +44,13 @@ public function create( array $data, Context $context ): EntityWrittenContainerEvent { - if ($customer->getActiveBillingAddress() === null || $customer->getActiveShippingAddress() === null) { - throw new \RuntimeException('Customer has no active billing or shipping address'); - } - - $addressHash = $this->addressHashService->generateHash($customer->getActiveBillingAddress(), $customer->getActiveShippingAddress()); - $createData = [ 'id' => Uuid::randomHex(), 'deviceType' => $deviceType, 'typeId' => $typeId, 'data' => $data, 'customerId' => $customer->getId(), - 'addressHash' => $addressHash, + 'addressHash' => self::DEFAULT_ADDRESS_HASH, ]; return $this->entityRepository->create([ @@ -77,9 +58,6 @@ public function create( ], $context); } - /** - * {@inheritdoc} - */ public function remove(string $id, Context $context): EntityWrittenContainerEvent { return $this->entityRepository->delete([ @@ -87,9 +65,6 @@ public function remove(string $id, Context $context): EntityWrittenContainerEven ], $context); } - /** - * {@inheritdoc} - */ public function exists(string $typeId, Context $context): bool { $criteria = new Criteria(); @@ -100,9 +75,6 @@ public function exists(string $typeId, Context $context): bool return $this->entityRepository->search($criteria, $context)->getTotal() > 0; } - /** - * {@inheritdoc} - */ public function read(string $id, Context $context): ?UnzerPaymentDeviceEntity { $criteria = new Criteria([$id]); diff --git a/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php b/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php index aa152c3b..e922d924 100644 --- a/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php +++ b/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php @@ -18,9 +18,6 @@ public function __construct(private EntityRepository $entityRepository) { } - /** - * {@inheritdoc} - */ public function create( TransferInformation $transferInformation, Context $context @@ -28,9 +25,6 @@ public function create( return $this->entityRepository->create([$transferInformation->getEntityData()], $context); } - /** - * {@inheritdoc} - */ public function remove(string $id, Context $context): EntityWrittenContainerEvent { return $this->entityRepository->delete([ @@ -38,9 +32,6 @@ public function remove(string $id, Context $context): EntityWrittenContainerEven ], $context); } - /** - * {@inheritdoc} - */ public function exists(string $transactionId, Context $context): bool { $criteria = new Criteria(); @@ -51,9 +42,6 @@ public function exists(string $transactionId, Context $context): bool return $this->entityRepository->search($criteria, $context)->getTotal() > 0; } - /** - * {@inheritdoc} - */ public function read(string $transactionId, Context $context): ?UnzerPaymentTransferInfoEntity { $criteria = new Criteria(); diff --git a/src/EventListeners/Checkout/ConfirmPageEventListener.php b/src/EventListeners/Checkout/ConfirmPageEventListener.php index ff9c796a..7ba6b8b6 100644 --- a/src/EventListeners/Checkout/ConfirmPageEventListener.php +++ b/src/EventListeners/Checkout/ConfirmPageEventListener.php @@ -5,10 +5,7 @@ namespace UnzerPayment6\EventListeners\Checkout; use Shopware\Core\Checkout\Payment\PaymentMethodEntity; -use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; -use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; -use Shopware\Core\System\Language\LanguageEntity; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Shopware\Core\System\SystemConfig\SystemConfigService; use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent; @@ -39,11 +36,14 @@ use UnzerPayment6\Components\UnzerUtil\UnzerApiUtil; use UnzerPayment6\DataAbstractionLayer\Entity\PaymentDevice\UnzerPaymentDeviceEntity; use UnzerPayment6\DataAbstractionLayer\Repository\PaymentDevice\UnzerPaymentDeviceRepositoryInterface; +use UnzerPayment6\EventListeners\Traits\HasLocaleTrait; use UnzerPayment6\Installer\PaymentInstaller; use UnzerSDK\Resources\Customer; class ConfirmPageEventListener implements EventSubscriberInterface { + use HasLocaleTrait; + private ?Configuration $configData = null; public function __construct( @@ -343,21 +343,6 @@ private function addPaylaterDirectDebitSecuredExtension(PageLoadedEvent $event): $event->getPage()->addExtension(PaylaterDirectDebitSecuredPageExtension::EXTENSION_NAME, $extension); } - private function getLocaleByLanguageId(string $languageId, Context $context): string - { - $criteria = new Criteria([$languageId]); - $criteria->addAssociation('locale'); - - /** @var LanguageEntity|null $searchResult */ - $searchResult = $this->languageRepository->search($criteria, $context)->first(); - - if ($searchResult === null || $searchResult->getLocale() === null) { - return ClientFactoryInterface::DEFAULT_LOCALE; - } - - return $searchResult->getLocale()->getCode(); - } - private function getPublicKey(SalesChannelContext $salesChannelContext): string { $keyPairContext = KeyPairContext::createFromSalesChannelContext($salesChannelContext); diff --git a/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php b/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php index 9c96b78c..a5cab583 100644 --- a/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php +++ b/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php @@ -20,15 +20,19 @@ use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\ApplePayV2PageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\GooglePayPageExtension; use UnzerPayment6\Components\UnzerUtil\UnzerApiUtil; +use UnzerPayment6\EventListeners\Traits\HasLocaleTrait; use UnzerPayment6\Installer\PaymentInstaller; -class ExpressButtonsEventListener implements EventSubscriberInterface +readonly class ExpressButtonsEventListener implements EventSubscriberInterface { + use HasLocaleTrait; + public function __construct( private ConfigReaderInterface $configReader, private ExtensionFactory $extensionFactory, private EntityRepository $salesChannelRepository, - private UnzerApiUtil $unzerApiUtil + private UnzerApiUtil $unzerApiUtil, + private EntityRepository $languageRepository ) { } @@ -42,6 +46,7 @@ public static function getSubscribedEvents() public function addExpressButtons(PageLoadedEvent $event): void { + $context = $event->getSalesChannelContext()->getContext(); $config = $this->configReader->read($event->getSalesChannelContext()->getSalesChannel()->getId()); if (!$config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_PAYPAL) @@ -53,6 +58,7 @@ public function addExpressButtons(PageLoadedEvent $event): void $event->getPage()->addExtension('UnzerExpressButtons', new ArrayStruct([ 'publicKey' => $config->get(ConfigReader::CONFIG_KEY_PUBLIC_KEY), 'keyPairConfig' => $this->unzerApiUtil->getCachedKeypairConfig($config->get(ConfigReader::CONFIG_KEY_PUBLIC_KEY)), + 'locale' => $this->getLocaleByLanguageId($context->getLanguageId(), $context), 'usePaypal' => $config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_PAYPAL) && $this->isPaymentMethodActive(PaymentInstaller::PAYMENT_ID_PAYPAL, $event->getSalesChannelContext()), 'useGooglePay' => $config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_GOOGLE) && $this->isPaymentMethodActive(PaymentInstaller::PAYMENT_ID_GOOGLE_PAY, $event->getSalesChannelContext()), 'useApplePay' => $config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_APPLEPAY) && $this->isPaymentMethodActive(PaymentInstaller::PAYMENT_ID_APPLE_PAY_V2, $event->getSalesChannelContext()), diff --git a/src/EventListeners/StateMachine/TransitionEventListener.php b/src/EventListeners/StateMachine/TransitionEventListener.php index 37d17663..e8fd4f2f 100644 --- a/src/EventListeners/StateMachine/TransitionEventListener.php +++ b/src/EventListeners/StateMachine/TransitionEventListener.php @@ -117,6 +117,7 @@ protected function doAutomaticTransactions(StateMachineTransitionEvent $event, ? } if (\is_array($autoCaptureStatus) && \in_array($event->getToPlace()->getId(), $autoCaptureStatus, true)) { + $this->logger->info(\sprintf('Automatic capture for order [%s] was triggered', $order->getOrderNumber())); try { $this->paymentActionService->captureOrder($order, $event->getContext()); } catch (\Throwable $exception) { diff --git a/src/EventListeners/Traits/HasLocaleTrait.php b/src/EventListeners/Traits/HasLocaleTrait.php new file mode 100644 index 00000000..ede8ada1 --- /dev/null +++ b/src/EventListeners/Traits/HasLocaleTrait.php @@ -0,0 +1,28 @@ +addAssociation('locale'); + + /** @var LanguageEntity|null $searchResult */ + $searchResult = $this->languageRepository->search($criteria, $context)->first(); + + if ($searchResult === null || $searchResult->getLocale() === null) { + return ClientFactoryInterface::DEFAULT_LOCALE; + } + + return $searchResult->getLocale()->getCode(); + } +} diff --git a/src/Installer/CustomFieldInstaller.php b/src/Installer/CustomFieldInstaller.php index 8b459b67..9388b0f1 100755 --- a/src/Installer/CustomFieldInstaller.php +++ b/src/Installer/CustomFieldInstaller.php @@ -60,41 +60,26 @@ public function __construct(private EntityRepository $customFieldSetRepository) { } - /** - * {@inheritdoc} - */ public function install(InstallContext $context, ?object $publicFileSystem = null): void { $this->customFieldSetRepository->upsert(self::CUSTOM_FIELDS, $context->getContext()); } - /** - * {@inheritdoc} - */ public function update(UpdateContext $context, ?object $publicFileSystem = null): void { $this->customFieldSetRepository->upsert(self::CUSTOM_FIELDS, $context->getContext()); } - /** - * {@inheritdoc} - */ public function uninstall(UninstallContext $context): void { $this->customFieldSetRepository->delete(self::CUSTOM_FIELDS, $context->getContext()); } - /** - * {@inheritdoc} - */ public function activate(ActivateContext $context): void { // Nothing to do here } - /** - * {@inheritdoc} - */ public function deactivate(DeactivateContext $context): void { // Nothing to do here diff --git a/src/Resources/app/administration/src/api/unzer-payment-configuration.service.js b/src/Resources/app/administration/src/api/unzer-payment-configuration.service.js index 47144374..acd8006e 100644 --- a/src/Resources/app/administration/src/api/unzer-payment-configuration.service.js +++ b/src/Resources/app/administration/src/api/unzer-payment-configuration.service.js @@ -30,6 +30,20 @@ class UnzerPaymentConfigurationService extends ApiService { }); } + registerWebhookDirectly(data) { + return this.httpClient + .post( + `_action/${this.getApiBasePath()}/register-webhook-directly`, + data, + { + headers: this.getBasicHeaders(), + } + ) + .then((response) => { + return ApiService.handleResponse(response); + }); + } + clearWebhooks(data) { return this.httpClient .post(`_action/${this.getApiBasePath()}/clear-webhooks`, data, { diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/register-webhook.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/register-webhook.html.twig index 6d8a27ef..24d5e924 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/register-webhook.html.twig +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/register-webhook.html.twig @@ -28,34 +28,35 @@ > {{ $tc('unzer-payment-settings.modal.webhook.httpsInfo') }} - - - - + - {{ $tc('unzer-payment-settings.modal.webhook.registered') }} - - + + + + {{ $tc('unzer-payment-settings.modal.webhook.registered') }} + + + { + if ( + !salesChannels || + !salesChannels.length || + !webhooks || + !webhooks.length + ) { + return false; + } + + for (const salesChannel of salesChannels) { + const domains = salesChannel.domains || []; + + for (const domain of domains) { + const webhook = webhooks.find( + (item) => + item.url.indexOf(domain.url) > -1 && + item.event !== 'all' + ); + + if (webhook) { + this.webhookStatus = { + needsUpdate: true, + webhook: webhook, + privateKey: privateKey, + url: webhook.url, + }; + + return true; + } + } + } + + return false; + }); + }, + + updateWebhook(privateKey, webhook) { + this.updateWebhookIsLoading = true; + const data = { + privateKey: privateKey, + selection: {}, + }; + data.selection[webhook.id] = { + url: webhook.url || '', + }; + + this.UnzerPaymentConfigurationService.clearWebhooks(data) + .then((response) => { + const newWebhook = { + url: webhook.url, + privateKey: privateKey, + }; + + this.UnzerPaymentConfigurationService.registerWebhookDirectly( + newWebhook + ) + .then((response) => { + this.fetchWebhookStatus(); + }) + .finally(() => { + this.updateWebhookIsLoading = false; + }); + }) + .catch((error) => { + this.updateWebhookIsLoading = false; + }); + }, + loadWebhooks(privateKey) { this.isLoadingWebhooks = true; diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/page/unzer-payment-settings/unzer-payment-settings.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/page/unzer-payment-settings/unzer-payment-settings.html.twig index 627b665b..26e07f40 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/page/unzer-payment-settings/unzer-payment-settings.html.twig +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/page/unzer-payment-settings/unzer-payment-settings.html.twig @@ -44,6 +44,26 @@ domain="UnzerPayment6.settings" > \n{% endblock %}",inject:["UnzerPaymentConfigurationService"],data(){return{readOnlyUnzerGooglePayGatewayMerchantId:{}}},watch:{currentSalesChannelId(){this.getUnzerGooglePayGatewayMerchantId(),this.$emit("sales-channel-changed",this.actualConfigData[this.currentSalesChannelId],this.currentSalesChannelId)},isLoading(e){this.$emit("loading-changed",e),!1===e&&this.getUnzerGooglePayGatewayMerchantId()}},computed:{unzerGooglePayGatewayMerchantId(){return this.readOnlyUnzerGooglePayGatewayMerchantId||""}},methods:{async createdComponent(){await this.$super("createdComponent"),this.getUnzerGooglePayGatewayMerchantId()},getUnzerGooglePayGatewayMerchantId(){"UnzerPayment6.settings"===this.domain&&this.UnzerPaymentConfigurationService.getGooglePayGatewayMerchantId(this.currentSalesChannelId).then(e=>{this.readOnlyUnzerGooglePayGatewayMerchantId=e.gatewayMerchantId}).catch(()=>{})}}}),n(345);let{Component:M,Mixin:I,Context:T}=Shopware;M.register("unzer-payment-settings",{template:'{% block unzer_payment_settings %}\n \n {% block unzer_payment_settings_header %}\n \n {% endblock %}\n\n {% block unzer_payment_settings_actions %}\n \n {% endblock %}\n\n {% block unzer_payment_settings_content %}\n \n \n \n \n \n {% endblock %}\n \n{% endblock %}',mixins:[I.getByName("notification"),I.getByName("sw-inline-snippet")],inject:["repositoryFactory","UnzerPaymentConfigurationService"],data(){return{isLoading:!0,isLoadingWebhooks:!0,isAdditionalKeysExpanded:!1,selectedKeyPairForTesting:!1,isTestSuccessful:!1,isSaveSuccessful:!1,config:{},webhooks:[],loadedWebhooksPrivateKey:!1,selectedSalesChannelId:null,keyPairSettings:[{key:"b2b-eur",group:"paylaterInvoice"},{key:"b2b-chf",group:"paylaterInvoice"},{key:"b2c-eur",group:"paylaterInvoice"},{key:"b2c-chf",group:"paylaterInvoice"},{key:"b2c-eur",group:"paylaterInstallment"},{key:"b2c-chf",group:"paylaterInstallment"},{key:"b2c-eur",group:"paylaterDirectDebitSecured"}],openModalKeyPair:null}},metaInfo(){return{title:"UnzerPayment"}},computed:{paymentMethodRepository(){return this.repositoryFactory.create("payment_method")},arrowIconName(){return T.app.config.version.match(/((\d+)\.?(\d+?)\.?(\d+)?\.?(\d*))-?([A-z]+?\d+)?/i)[3]>=5?"regular-chevron-right-xs":"small-arrow-medium-right"},defaultKeyPair(){return{privateKey:this.getConfigValue("privateKey"),publicKey:this.getConfigValue("publicKey")}}},watch:{openModalKeyPair(e){e&&e.privateKey!==this.loadedWebhooksPrivateKey&&this.loadWebhooks(e.privateKey)}},methods:{getConfigValue(e){if(!this.config||!this.$refs.systemConfig||!this.$refs.systemConfig.actualConfigData||!this.$refs.systemConfig.actualConfigData.null)return"";let t=this.$refs.systemConfig.actualConfigData.null;return this.config[`UnzerPayment6.settings.${e}`]||t[`UnzerPayment6.settings.${e}`]},onValidateCredentials(e){this.isTestSuccessful=!1,this.selectedKeyPairForTesting=e;let t=this.getArrayKeyOfKeyPairSetting(e),n=e;-1!==t&&(n=this.keyPairSettings[t]);let a={publicKey:n.publicKey,privateKey:n.privateKey,salesChannel:this.$refs.systemConfig.currentSalesChannelId};this.UnzerPaymentConfigurationService.validateCredentials(a).then(()=>{this.createNotificationSuccess({title:this.$tc("unzer-payment-settings.form.message.success.title"),message:this.$tc("unzer-payment-settings.form.message.success.message")}),this.isTestSuccessful=!0,this.selectedKeyPairForTesting=!1}).catch(()=>{this.createNotificationError({title:this.$tc("unzer-payment-settings.form.message.error.title"),message:this.$tc("unzer-payment-settings.form.message.error.message")}),this.onTestFinished()})},onTestFinished(){this.selectedKeyPairForTesting=!1,this.isTestSuccessful=!1},setPublicKey(e,t){this.keyPairSettings[this.getArrayKeyOfKeyPairSetting(e)].publicKey=t},setPrivateKey(e,t){this.keyPairSettings[this.getArrayKeyOfKeyPairSetting(e)].privateKey=t},getArrayKeyOfKeyPairSetting(e){return this.keyPairSettings.findIndex(t=>t.key===e.key&&t.group===e.group)},onSave(){this.isLoading=!0,["paylaterInvoice","paylaterInstallment","paylaterDirectDebitSecured"].forEach(e=>{this.config[`UnzerPayment6.settings.${e}`]=[]}),this.keyPairSettings.reduce((e,t)=>(t&&t.privateKey&&t.publicKey&&e[`UnzerPayment6.settings.${t.group}`].push(t),e),this.config),this.$refs.systemConfig.saveAll().then(()=>{this.isSaveSuccessful=!0;let e=this.$tc("sw-plugin-config.messageSaveSuccess");"sw-plugin-config.messageSaveSuccess"===e&&(e=this.$tc("sw-extension-store.component.sw-extension-config.messageSaveSuccess")),this.createNotificationSuccess({title:this.$tc("global.default.success"),message:e}),document.dispatchEvent(new CustomEvent("unzer-settings-saved",{}))}).catch(e=>{this.isSaveSuccessful=!1,this.createNotificationError({title:this.$tc("global.default.error"),message:e}),this.isLoading=!1})},onConfigChange(e){this.config=e,this.isLoading=!1,this.syncKeyPairConfig()},onLoadingChanged(e){this.isLoading=e},onSalesChannelChanged(e,t){e&&this.onConfigChange(e),this.selectedSalesChannelId=t},onWebhookRegistered(e){this.loadWebhooks(e)},loadWebhooks(e){this.isLoadingWebhooks=!0,this.UnzerPaymentConfigurationService.getWebhooks(e).then(t=>{this.webhooks=t,this.webhookSelection=null,this.webhookSelectionLength=0,this.loadedWebhooksPrivateKey=e}).catch(()=>{this.webhooks=[],this.loadedWebhooksPrivateKey=!1}).finally(()=>{this.isLoadingWebhooks=!1,this.isClearingSuccessful=!1})},getBind(e,t){let n;return t!==this.config&&(this.config=t),this.$refs.systemConfig.config.forEach(t=>{t.elements.forEach(t=>{if(t.name===e.name){n=t;return}})}),n||e},keyPairSettingTitle(e){return this.$tc(`unzer-payment.methods.${e.group}.${e.key}`)},keyPairSettingGroupTitle(e){return this.$tc(`unzer-payment.methods.${e.group}.main`)},isShowWebhooksButtonEnabled(e){return e&&e.privateKey&&e.publicKey},isRegisterWebhooksButtonEnabled(e){return!this.isLoading&&e&&e.privateKey},syncKeyPairConfig(){let e=this;["paylaterInvoice","paylaterInstallment","paylaterDirectDebitSecured"].forEach(t=>{this.config[`UnzerPayment6.settings.${t}`]&&this.config[`UnzerPayment6.settings.${t}`].forEach(t=>{e.keyPairSettings.forEach((e,n,a)=>{e.group===t.group&&e.key===t.key&&(a[n]=t)})})})},toggleAdditionalKeys(){this.isAdditionalKeysExpanded=!this.isAdditionalKeysExpanded}}});let{Module:A}=Shopware;A.register("unzer-payment-configuration",{type:"plugin",name:"UnzerPayment",title:"unzer-payment-settings.module.title",description:"unzer-payment-settings.module.description",version:"1.1.0",targetVersion:"1.1.0",snippets:{"de-DE":w,"en-GB":z},routes:{settings:{component:"unzer-payment-settings",path:"settings",meta:{parentPath:"sw.settings.index"}}},settingsItem:{name:"unzer-payment-configuration",to:"unzer.payment.configuration.settings",label:"unzer-payment-settings.module.title",group:"plugins",iconComponent:"unzer-payment-plugin-icon",backgroundEnabled:!1}}),n(748),n(963),Shopware.Component.override("sw-payment-card",{template:'{% block sw_payment_card_description %}\n
\n \n \n {{ $tc(\'sw-payment-card.deprecated\') }}\n \n
\n
\n \n{% endblock %}',snippets:{"de-DE":w,"en-GB":z}}),n(34)}()}(); \ No newline at end of file +!function(){var e={296:function(){},906:function(){},836:function(){},325:function(){},299:function(){},963:function(){let{Application:e}=Shopware,t=Shopware.Classes.ApiService;class n extends t{constructor(e,t,n="unzer-payment"){super(e,t,n)}validateCredentials(e){return this.httpClient.post(`_action/${this.getApiBasePath()}/validate-credentials`,e,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}registerWebhooks(e){return this.httpClient.post(`_action/${this.getApiBasePath()}/register-webhooks`,e,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}registerWebhookDirectly(e){return this.httpClient.post(`_action/${this.getApiBasePath()}/register-webhook-directly`,e,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}clearWebhooks(e){return this.httpClient.post(`_action/${this.getApiBasePath()}/clear-webhooks`,e,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}getWebhooks(e){return this.httpClient.post(`_action/${this.getApiBasePath()}/get-webhooks`,{privateKey:e},{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}getGooglePayGatewayMerchantId(e){return this.httpClient.get(`_action/${this.getApiBasePath()}/get-google-pay-gateway-merchant-id?salesChannelId=${e||""}`,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}}e.addServiceProvider("UnzerPaymentConfigurationService",t=>new n(e.getContainer("init").httpClient,t.loginService))},748:function(){let{Application:e}=Shopware,t=Shopware.Classes.ApiService;class n extends t{constructor(e,t,n="unzer-payment"){super(e,t,n)}fetchPaymentDetails(e){let n=`_action/${this.getApiBasePath()}/transaction/${e}/details`;return this.httpClient.get(n,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}chargeTransaction(e,n,a){let s=`_action/${this.getApiBasePath()}/transaction/${e}/charge/${a}`;return this.httpClient.get(s,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}refundTransaction(e,n,a,s=null){let i=`_action/${this.getApiBasePath()}/transaction/${e}/refund/${n}/${a}`;return null!==s&&(i=`${i}/${s}`),this.httpClient.get(i,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}cancelTransaction(e,n,a){let s=`_action/${this.getApiBasePath()}/transaction/${e}/cancel/${n}/${a}`;return this.httpClient.get(s,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}ship(e){let n=`_action/${this.getApiBasePath()}/transaction/${e}/ship`;return this.httpClient.get(n,{headers:this.getBasicHeaders()}).then(e=>t.handleResponse(e))}}e.addServiceProvider("UnzerPaymentService",t=>new n(e.getContainer("init").httpClient,t.loginService))},337:function(){let{Component:e}=Shopware,{Criteria:t,EntityCollection:n}=Shopware.Data;e.extend("unzer-entity-multi-select-delivery-status","sw-entity-multi-id-select",{inject:["repositoryFactory"],props:{repository:{type:Object,required:!0,default(){return this.repositoryFactory.create("state_machine_state")}},criteria:{type:Object,required:!1,default(){let e=new t(1,100);return e.addFilter(t.equals("stateMachine.technicalName","order_delivery.state")),e}},entityCollection(){}}})},708:function(){let{Component:e}=Shopware,{Criteria:t}=Shopware.Data;e.extend("unzer-entity-single-select-delivery-status","sw-entity-single-select",{props:{criteria:{type:Object,required:!1,default(){let e=new t(1,100);return e.addFilter(t.equals("stateMachine.technicalName","order_delivery.state")),e}}}})},129:function(e,t,n){var a=n(296);a.__esModule&&(a=a.default),"string"==typeof a&&(a=[[e.id,a,""]]),a.locals&&(e.exports=a.locals),(0,n(534).A)("0620ec53",a,!0,{})},425:function(e,t,n){var a=n(906);a.__esModule&&(a=a.default),"string"==typeof a&&(a=[[e.id,a,""]]),a.locals&&(e.exports=a.locals),(0,n(534).A)("f0afdb6a",a,!0,{})},345:function(e,t,n){var a=n(836);a.__esModule&&(a=a.default),"string"==typeof a&&(a=[[e.id,a,""]]),a.locals&&(e.exports=a.locals),(0,n(534).A)("61f99e4d",a,!0,{})},90:function(e,t,n){var a=n(325);a.__esModule&&(a=a.default),"string"==typeof a&&(a=[[e.id,a,""]]),a.locals&&(e.exports=a.locals),(0,n(534).A)("05921a3e",a,!0,{})},34:function(e,t,n){var a=n(299);a.__esModule&&(a=a.default),"string"==typeof a&&(a=[[e.id,a,""]]),a.locals&&(e.exports=a.locals),(0,n(534).A)("3bbcbade",a,!0,{})},534:function(e,t,n){"use strict";function a(e,t){for(var n=[],a={},s=0;sn.parts.length&&(a.parts.length=n.parts.length)}else{for(var i=[],s=0;s\n {% block unzer_payment_actions_amount_field %}\n
\n \n \n
\n {% endblock %}\n
\n \n {% block unzer_payment_actions_charge_button %}\n \n {{ $tc(\'unzer-payment.paymentDetails.actions.chargeButton\') }}\n \n {% endblock %}\n\n {% block unzer_payment_actions_cancel_button %}\n \n {{ $tc(\'unzer-payment.paymentDetails.actions.cancelButton\') }}\n \n {% endblock %}\n \n \n {% block unzer_payment_actions_reason_field %}\n \n \n {% endblock %}\n\n {% block unzer_payment_actions_refund_button %}\n \n {{ $tc(\'unzer-payment.paymentDetails.actions.refundButton\') }}\n \n {% endblock %}\n \n {% block unzer_payment_actions_button_container_inner %}{% endblock %}\n
\n \n\n
\n {{ $tc(\'unzer-payment.paymentDetails.actions.noActions\') }}\n
\n{% endblock %}',inject:["UnzerPaymentService"],mixins:[t.getByName("notification")],data(){return{isLoading:!1,isSuccessful:!1,transactionAmount:0,reasonCode:null}},props:{transactionResource:{type:Object,required:!0},paymentResource:{type:Object,required:!0},decimalPrecision:{type:Number,required:!0,default:4}},computed:{isChargePossible:function(){return"authorization"===this.transactionResource.type&&"error"!==this.transactionResource.state},isRefundPossible:function(){return"charge"===this.transactionResource.type&&"error"!==this.transactionResource.state&&"pending"!==this.transactionResource.state&&!(this.transactionResource.isFirst&&"085b64d0028a8bd447294e03c4eb411a"===this.paymentResource.paymentMethodId&&"pending"!==this.paymentResource.state.name&&"partly"!==this.paymentResource.state.name)},maxTransactionAmount(){let e=0,t=this.isRefundPossible&&"085b64d0028a8bd447294e03c4eb411a"===this.paymentResource.paymentMethodId;return this.isRefundPossible&&(e=this.transactionResource.amount),this.isChargePossible&&(e=this.paymentResource.amount.remaining),"remainingAmount"in this.transactionResource&&(e=this.transactionResource.remainingAmount),this.transactionResource.isFirst&&t&&(e=this.paymentResource.amount.remaining),e/10**this.paymentResource.amount.decimalPrecision},reasonCodeSelection(){return[{label:this.$tc("unzer-payment.paymentDetails.actions.reason.cancel"),value:"CANCEL"},{label:this.$tc("unzer-payment.paymentDetails.actions.reason.credit"),value:"CREDIT"},{label:this.$tc("unzer-payment.paymentDetails.actions.reason.return"),value:"RETURN"}]}},created(){this.transactionAmount=this.maxTransactionAmount},methods:{charge(){this.isLoading=!0,this.UnzerPaymentService.chargeTransaction(this.paymentResource.orderTransactionId,this.transactionResource.id,this.transactionAmount).then(()=>{this.createNotificationSuccess({title:this.$tc("unzer-payment.paymentDetails.notifications.chargeSuccessTitle"),message:this.$tc("unzer-payment.paymentDetails.notifications.chargeSuccessMessage")}),this.isSuccessful=!0,this.$emit("reload")}).catch(e=>{let t=e.response.data.errors[0];"generic-error"===t&&(t=this.$tc("unzer-payment.paymentDetails.notifications.genericErrorMessage")),"paylater-invoice-document-required"===t&&(t=this.$tc("unzer-payment.paymentDetails.notifications.paylaterInvoiceDocumentRequiredErrorMessage")),this.createNotificationError({title:this.$tc("unzer-payment.paymentDetails.notifications.chargeErrorTitle"),message:t}),this.isLoading=!1})},refund(){this.isLoading=!0,this.UnzerPaymentService.refundTransaction(this.paymentResource.orderTransactionId,this.transactionResource.id,this.transactionAmount,this.reasonCode).then(()=>{this.createNotificationSuccess({title:this.$tc("unzer-payment.paymentDetails.notifications.refundSuccessTitle"),message:this.$tc("unzer-payment.paymentDetails.notifications.refundSuccessMessage")}),this.isSuccessful=!0,this.$emit("reload")}).catch(e=>{let t=e.response.data.errors[0];"generic-error"===t&&(t=this.$tc("unzer-payment.paymentDetails.notifications.genericErrorMessage")),this.createNotificationError({title:this.$tc("unzer-payment.paymentDetails.notifications.refundErrorTitle"),message:t}),this.isLoading=!1})},startCancel(){this.$emit("cancel",this.transactionAmount)}}});let{Component:a,Mixin:s,Module:i}=Shopware;a.register("unzer-payment-detail",{template:'{% block unzer_payment_detail %}\n \n \n {% block unzer_payment_detail_footer %}\n \n {% block unzer_payment_detail_ship_button %}\n \n {{ $tc(\'unzer-payment.paymentDetails.actions.shipButton\') }}\n \n {% endblock %}\n \n {% endblock %}\n \n{% endblock %}',inject:["UnzerPaymentService"],mixins:[s.getByName("notification")],data(){return{isLoading:!1,isSuccessful:!1,paylaterPaymentMethods:["09588ffee8064f168e909ff31889dd7f","12fbfbce271a43a89b3783453b88e9a6","6d6adcd4b7bf40499873c294a85f32ed"]}},props:{paymentResource:{type:Object,required:!0}},computed:{unzerMaxDigits(){let e=i.getModuleRegistry().get("unzer-payment");return e&&e.manifest?e.manifest.maxDigits:4},remainingAmount(){return this.paymentResource&&this.paymentResource.amount?this.formatAmount(this.paymentResource.amount.remaining,this.paymentResource.amount.decimalPrecision):0},cancelledAmount(){return this.paymentResource&&this.paymentResource.amount?this.formatAmount(this.paymentResource.amount.cancelled,this.paymentResource.amount.decimalPrecision):0},chargedAmount(){return this.paymentResource&&this.paymentResource.amount?this.formatAmount(this.paymentResource.amount.charged,this.paymentResource.amount.decimalPrecision):0}},methods:{reloadOrderDetail(){this.$emit("reloadOrderDetails")},ship(){this.isLoading=!0,this.UnzerPaymentService.ship(this.paymentResource.orderTransactionId).then(()=>{this.createNotificationSuccess({title:this.$tc("unzer-payment.paymentDetails.notifications.shipSuccessTitle"),message:this.$tc("unzer-payment.paymentDetails.notifications.shipSuccessMessage")}),this.isSuccessful=!0,this.$emit("reload")}).catch(e=>{let t=e.response.data.errors[0];"generic-error"===t?t=this.$tc("unzer-payment.paymentDetails.notifications.genericErrorMessage"):"invoice-missing-error"===t?t=this.$tc("unzer-payment.paymentDetails.notifications.invoiceNotFoundMessage"):"documentdate-missing-error"===t?t=this.$tc("unzer-payment.paymentDetails.notifications.documentDateMissingError"):"payment-missing-error"===t&&(t=this.$tc("unzer-payment.paymentDetails.notifications.paymentMissingError")),this.createNotificationError({title:this.$tc("unzer-payment.paymentDetails.notifications.shipErrorTitle"),message:t}),this.isLoading=!1})},formatAmount(e,t){return e/10**Math.min(this.unzerMaxDigits,t)},formatCurrency(e){return Shopware.Utils.format.currency(e||0,this.paymentResource.currency)},isPaylaterPaymentMethod(e){return this.paylaterPaymentMethods.indexOf(e)>=0}}});let{Component:r,Module:o,Mixin:c}=Shopware;r.register("unzer-payment-history",{template:'{% block unzer_payment_history %}\n \n {% block unzer_payment_history_container %}\n \n {% endblock %}\n \n{% endblock %}',inject:["repositoryFactory","UnzerPaymentService"],mixins:[c.getByName("notification")],data(){return{showCancelModal:!1,isCancelLoading:!1,cancelAmount:0}},props:{paymentResource:{type:Object,required:!0}},computed:{unzerMaxDigits(){let e=o.getModuleRegistry().get("unzer-payment");return e&&e.manifest?e.manifest.maxDigits:4},orderTransactionRepository:function(){return this.repositoryFactory.create("order_transaction")},decimalPrecision(){return this.paymentResource&&this.paymentResource.amount&&this.paymentResource.amount.decimalPrecision?Math.min(this.unzerMaxDigits,this.paymentResource.amount.decimalPrecision):this.unzerMaxDigits},data:function(){let e=[];return Object.values(this.paymentResource.transactions).forEach(t=>{let n=this.formatCurrency(this.formatAmount(parseFloat(t.amount),this.decimalPrecision)),a=Shopware.Filter.getByName("date")(t.date,{hour:"numeric",minute:"numeric",second:"numeric"});e.push({type:this.transactionTypeRenderer(t.type),amount:n,date:a,state:t.state||"",resource:t})}),e},columns:function(){return[{property:"type",label:this.$tc("unzer-payment.paymentDetails.history.column.type"),rawData:!0},{property:"amount",label:this.$tc("unzer-payment.paymentDetails.history.column.amount"),rawData:!0},{property:"date",label:this.$tc("unzer-payment.paymentDetails.history.column.date"),rawData:!0},{property:"state",label:this.$tc("unzer-payment.paymentDetails.history.column.state"),rawData:!0}]}},methods:{transactionTypeRenderer:function(e){switch(e){case"authorization":return this.$tc("unzer-payment.paymentDetails.history.type.authorization");case"charge":return this.$tc("unzer-payment.paymentDetails.history.type.charge");case"shipment":return this.$tc("unzer-payment.paymentDetails.history.type.shipment");case"refund":return this.$tc("unzer-payment.paymentDetails.history.type.refund");case"cancellation":return this.$tc("unzer-payment.paymentDetails.history.type.cancellation");default:return this.$tc("unzer-payment.paymentDetails.history.type.default")}},reload:function(){this.$emit("reload"),this.$emit("reloadOrderDetails")},formatAmount(e,t){return e/10**t},openCancelModal(e,t){this.showCancelModal=e.resource.id,this.cancelAmount=t},closeCancelModal(){this.showCancelModal=!1,this.cancelAmount=0},cancel(){this.isCancelLoading=!0,this.UnzerPaymentService.cancelTransaction(this.paymentResource.orderTransactionId,this.paymentResource.id,this.cancelAmount).then(()=>{this.createNotificationSuccess({title:this.$tc("unzer-payment.paymentDetails.notifications.cancelSuccessTitle"),message:this.$tc("unzer-payment.paymentDetails.notifications.cancelSuccessMessage")}),this.reload()}).catch(e=>{let t=e.response.data.errors[0];"generic-error"===t&&(t=this.$tc("unzer-payment.paymentDetails.notifications.cancelErrorMessage")),this.createNotificationError({title:this.$tc("unzer-payment.paymentDetails.notifications.cancelErrorTitle"),message:t}),this.isCancelLoading=!1,this.reload()})},formatCurrency(e){return Shopware.Utils.format.currency(e||0,this.paymentResource.currency)}}});let{Component:l}=Shopware;l.register("unzer-payment-metadata",{template:'{% block unzer_payment_metadata %}\n