diff --git a/CHANGELOG_de-DE.md b/CHANGELOG_de-DE.md index cbbc488f..3a9be857 100644 --- a/CHANGELOG_de-DE.md +++ b/CHANGELOG_de-DE.md @@ -1,3 +1,9 @@ +# 6.5.0 +* Migration von Unzer UI Component V1 zu Unzer UI Component V2 +* Express-Checkout für Apple Pay, Google Pay und PayPal wurde hinzugefügt +* EAA-Unterstützung, siehe https://docs.unzer.com/online-payments/compliance/eaa/ +* Veraltete Zahlungsmethoden wurden durch neue ersetzt, entfernt oder umbenannt. Die vollständige Liste befindet sich unter https://docs.unzer.com/plugins/shopware-6/shop6-migrate-v1-v2/ + # 6.4.5 * Customer object Update von alter Version zu neuester diff --git a/CHANGELOG_en-GB.md b/CHANGELOG_en-GB.md index 90faaf1d..2ff43a05 100644 --- a/CHANGELOG_en-GB.md +++ b/CHANGELOG_en-GB.md @@ -1,3 +1,9 @@ +# 6.5.0 +* Migration from Unzer UI Component V1 to Unzer UI Component V2 +* Express checkout for Apple Pay, Google Pay and Paypal has been added +* EAA Support, https://docs.unzer.com/online-payments/compliance/eaa/ +* Deprecated payment methods have been replaced with new ones, removed or renamed. For full list see https://docs.unzer.com/plugins/shopware-6/shop6-migrate-v1-v2/ + # 6.4.5 * Customer object change from old version to latest diff --git a/README.md b/README.md index 582f8dcf..f1eb985f 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Unzer payment integration for Shopware 6 includes the following payment methods: * Alipay * Apple Pay * Bancontact -* Credit Card +* Credit Card and Click to Pay * Unzer Direct Bank Transfer * EPS * Google Pay @@ -32,16 +32,20 @@ Unzer payment integration for Shopware 6 includes the following payment methods: * PayPal * Prepayment * SEPA Direct Debit -* SOFORT * TWINT * Unzer Direct Debit -* Unzer direct Debit (secured) +* Unzer Direct Debit (secured) * Unzer Invoice B2C / B2B (secured) * Unzer Installment (secured) * WeChat Pay +* Wero Regarding plugin compatibility, please take a look at the release notes for more information. +## Updating + +Version 6.5.0 is a breaking change - please see https://docs.unzer.com/plugins/shopware-6/shop6-migrate-v1-v2/ + ## Installation ### For production diff --git a/composer.json b/composer.json index 9c4566ee..fa69c06f 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "unzerdev/shopware6", "description": "Unzer payment integration for Shopware 6", - "version": "6.4.5", + "version": "6.5.0", "type": "shopware-platform-plugin", "license": "Apache-2.0", "minimum-stability": "dev", @@ -21,7 +21,7 @@ }, "require": { "php": ">=8.2 || <=8.3", - "unzerdev/php-sdk": "~3.11.0", + "unzerdev/php-sdk": "~3.13.0", "shopware/core": "~6.6.0", "shopware/administration": "~6.6.0", "shopware/storefront": "~6.6.0" diff --git a/src/Commands/RegisterWebhookCommand.php b/src/Commands/RegisterWebhookCommand.php index 66cc8cb2..2b988046 100644 --- a/src/Commands/RegisterWebhookCommand.php +++ b/src/Commands/RegisterWebhookCommand.php @@ -16,7 +16,6 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; -use Throwable; use UnzerPayment6\Components\WebhookRegistrator\WebhookRegistrator; use UnzerPayment6\Components\WebhookRegistrator\WebhookRegistratorInterface; use UnzerSDK\Exceptions\UnzerApiException; @@ -34,7 +33,7 @@ class RegisterWebhookCommand extends Command public function __construct(WebhookRegistratorInterface $webhookRegistrator, EntityRepository $domainRepository) { $this->webhookRegistrator = $webhookRegistrator; - $this->domainRepository = $domainRepository; + $this->domainRepository = $domainRepository; parent::__construct(); } @@ -56,7 +55,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $host = $input->getArgument('host') ?? ''; - if (!is_string($host)) { + if (!\is_string($host)) { return WebhookRegistrator::EXIT_CODE_INVALID_HOST; } @@ -69,7 +68,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int try { $domainDataBag = new RequestDataBag([ new RequestDataBag([ - 'id' => $domain->getId(), + 'id' => $domain->getId(), 'url' => $domain->getUrl(), ]), ]); @@ -79,7 +78,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int $style->error($exception->getMerchantMessage()); return WebhookRegistrator::EXIT_CODE_API_ERROR; - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $style->error($exception->getMessage()); return WebhookRegistrator::EXIT_CODE_UNKNOWN_ERROR; @@ -90,7 +89,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $style->success( - sprintf('The webhooks have been registered to the following URL: %s', $host) + \sprintf('The webhooks have been registered to the following URL: %s', $host) ); return WebhookRegistrator::EXIT_CODE_SUCCESS; @@ -100,7 +99,7 @@ protected function handleDomain(string $providedHost, SymfonyStyle $style): ?Sal { $parsedHost = parse_url($providedHost); - if (!is_array($parsedHost) || empty($parsedHost['host']) || empty($parsedHost['scheme'])) { + if (!\is_array($parsedHost) || empty($parsedHost['host']) || empty($parsedHost['scheme'])) { $style->warning('The provided host is invalid.'); return null; @@ -109,7 +108,7 @@ protected function handleDomain(string $providedHost, SymfonyStyle $style): ?Sal $salesChannelDomain = $this->getSalesChannelByHost($providedHost); if ($salesChannelDomain === null) { - $style->warning('The provided host does not exist in any saleschannel.'); + $style->warning('The provided host does not exist in any Sales Channel.'); $possibleDomains = []; /** @var SalesChannelDomainEntity $domainResult */ diff --git a/src/Commands/SendShippingNotificationCommand.php b/src/Commands/SendShippingNotificationCommand.php index 6afe3b1c..12aea721 100644 --- a/src/Commands/SendShippingNotificationCommand.php +++ b/src/Commands/SendShippingNotificationCommand.php @@ -19,7 +19,6 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Throwable; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\Event\AutomaticShippingNotificationEvent; @@ -35,10 +34,10 @@ )] class SendShippingNotificationCommand extends Command { - private const EXIT_CODE_SUCCESS = 0; - private const EXIT_CODE_API_ERROR = 1; + private const EXIT_CODE_SUCCESS = 0; + private const EXIT_CODE_API_ERROR = 1; private const EXIT_CODE_UNKNOWN_ERROR = 2; - private const EXIT_CODE_NO_ORDERS = 3; + private const EXIT_CODE_NO_ORDERS = 3; private const EXIT_CODE_CONFIGURATION = 4; private ConfigReaderInterface $configReader; @@ -57,18 +56,18 @@ public function __construct( EventDispatcherInterface $eventDispatcher, ShipServiceInterface $shipService ) { - $this->configReader = $configReader; + $this->configReader = $configReader; $this->transactionRepository = $transactionRepository; - $this->context = Context::createDefaultContext(); - $this->eventDispatcher = $eventDispatcher; - $this->shipService = $shipService; + $this->context = Context::createDefaultContext(); + $this->eventDispatcher = $eventDispatcher; + $this->shipService = $shipService; parent::__construct(); } protected function execute(InputInterface $input, OutputInterface $output): int { - $config = $this->configReader->read(); + $config = $this->configReader->read(); $configuredState = $config->get(ConfigReader::CONFIG_KEY_SHIPPING_STATUS); if (empty($configuredState)) { @@ -77,7 +76,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return self::EXIT_CODE_CONFIGURATION; } - $transactions = $this->getMatchingTransactions($configuredState); + $transactions = $this->getMatchingTransactions($configuredState); $transactionCount = $transactions->count(); if ($transactionCount === 0) { @@ -86,7 +85,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int return self::EXIT_CODE_NO_ORDERS; } - $output->writeln(sprintf('Found %s possible order(s) for automatic shipping notification', $transactionCount)); + $output->writeln(\sprintf('Found %s possible order(s) for automatic shipping notification', $transactionCount)); $currentTransactionCounter = 0; /** @var OrderTransactionEntity $transaction */ @@ -96,18 +95,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int $order = $transaction->getOrder(); if ($order === null) { - $output->writeln(sprintf('Transaction %s has no order', $transaction->getId())); + $output->writeln(\sprintf('Transaction %s has no order', $transaction->getId())); continue; } if ($order->getDocuments() === null) { - $output->writeln(sprintf('Order %s has no documents', $order->getOrderNumber())); + $output->writeln(\sprintf('Order %s has no documents', $order->getOrderNumber())); continue; } - $output->write(sprintf('(%s/%s) Order %s', $currentTransactionCounter, $transactionCount, $order->getOrderNumber())); + $output->write(\sprintf('(%s/%s) Order %s', $currentTransactionCounter, $transactionCount, $order->getOrderNumber())); $entityFilter = new DocumentTypeEntity(); $entityFilter->setTechnicalName('invoice'); @@ -120,9 +119,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln("\tOK"); } catch (UnzerApiException $apiException) { - $output->writeln(sprintf("\t%s", $apiException->getMerchantMessage())); + $output->writeln(\sprintf("\t%s", $apiException->getMerchantMessage())); - //Already insured but flag in DB missing! + // Already insured but flag in DB missing! /** @var string $exceptionCode */ $exceptionCode = $apiException->getCode(); @@ -134,8 +133,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int } return self::EXIT_CODE_API_ERROR; - } catch (Throwable $exception) { - $output->writeln(sprintf("\t%s", $exception->getMessage())); + } catch (\Throwable $exception) { + $output->writeln(\sprintf("\t%s", $exception->getMessage())); return self::EXIT_CODE_UNKNOWN_ERROR; } @@ -153,7 +152,7 @@ protected function setCustomFields( ]); $update = [ - 'id' => $transaction->getId(), + 'id' => $transaction->getId(), 'customFields' => $customFields, ]; @@ -164,7 +163,7 @@ private function getMatchingTransactions(string $stateId): EntityCollection { $criteria = new Criteria(); $criteria->addFilter( - new EqualsFilter(sprintf('customFields.%s', CustomFieldInstaller::UNZER_PAYMENT_IS_SHIPPED), false), + new EqualsFilter(\sprintf('customFields.%s', CustomFieldInstaller::UNZER_PAYMENT_IS_SHIPPED), false), new EqualsAnyFilter('paymentMethodId', AutomaticShippingValidatorInterface::HANDLED_PAYMENT_METHODS), new EqualsFilter('order.deliveries.stateId', $stateId), new EqualsFilter('order.documents.documentType.technicalName', 'invoice') diff --git a/src/Components/AbstractUnzerPaymentException.php b/src/Components/AbstractUnzerPaymentException.php index 7fb68c44..32684eee 100644 --- a/src/Components/AbstractUnzerPaymentException.php +++ b/src/Components/AbstractUnzerPaymentException.php @@ -4,12 +4,9 @@ namespace UnzerPayment6\Components; -use Exception; - -abstract class AbstractUnzerPaymentException extends Exception +abstract class AbstractUnzerPaymentException extends \Exception { - /** @var string */ - protected $customerMessage = 'exception/statusMapper'; + protected string $customerMessage = 'exception/statusMapper'; public function getCustomerMessage(): string { diff --git a/src/Components/ApplePay/CertificateManager.php b/src/Components/ApplePay/CertificateManager.php deleted file mode 100644 index 89886704..00000000 --- a/src/Components/ApplePay/CertificateManager.php +++ /dev/null @@ -1,46 +0,0 @@ -configReader = $configReader; - } - - public function getMerchantIdentificationCertificatePath(?string $salesChannelId): string - { - $config = $this->configReader->read($salesChannelId); - - return sprintf('%s/%s/%s', self::APPLE_PAY_CERTIFICATE_PATH, $config->get(ConfigReader::CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFICATION_CERTIFICATE_ID), self::MERCHANT_IDENTIFICATION_CERTIFICATE_FILENAME); - } - - public function getMerchantIdentificationCertificatePathForUpdate(?string $salesChannelId): string - { - return sprintf('%s/%s/%s', self::APPLE_PAY_CERTIFICATE_PATH, $salesChannelId, self::MERCHANT_IDENTIFICATION_CERTIFICATE_FILENAME); - } - - public function getMerchantIdentificationKeyPath(?string $salesChannelId): string - { - $config = $this->configReader->read($salesChannelId); - - return sprintf('%s/%s/%s', self::APPLE_PAY_CERTIFICATE_PATH, $config->get(ConfigReader::CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFICATION_CERTIFICATE_ID), self::MERCHANT_IDENTIFICATION_KEY_FILENAME); - } - - public function getMerchantIdentificationKeyPathForUpdate(?string $salesChannelId): string - { - return sprintf('%s/%s/%s', self::APPLE_PAY_CERTIFICATE_PATH, $salesChannelId, self::MERCHANT_IDENTIFICATION_KEY_FILENAME); - } -} diff --git a/src/Components/ApplePay/Exception/InvalidCertificate.php b/src/Components/ApplePay/Exception/InvalidCertificate.php deleted file mode 100644 index b384d793..00000000 --- a/src/Components/ApplePay/Exception/InvalidCertificate.php +++ /dev/null @@ -1,41 +0,0 @@ - $certificateType]); - $this->certificateType = $certificateType; - } - - public function getStatusCode(): int - { - return Response::HTTP_BAD_REQUEST; - } - - public function getErrorCode(): string - { - return 'UNZER_PAYMENT__INVALID_CERTIFICATE'; - } - - public function getTranslationKey(): string - { - return 'unzer-payment-settings.apple-pay.certificates.update.error.messageInvalidCertificate'; - } - - public function getTranslationData(): array - { - return [ - 'type' => $this->certificateType, - ]; - } -} diff --git a/src/Components/ApplePay/Exception/MissingCertificateFiles.php b/src/Components/ApplePay/Exception/MissingCertificateFiles.php deleted file mode 100644 index dcd2bbbf..00000000 --- a/src/Components/ApplePay/Exception/MissingCertificateFiles.php +++ /dev/null @@ -1,41 +0,0 @@ - $certificateType]); - $this->certificateType = $certificateType; - } - - public function getStatusCode(): int - { - return Response::HTTP_BAD_REQUEST; - } - - public function getErrorCode(): string - { - return 'UNZER_PAYMENT__MISSING_CERTIFICATE_FILES'; - } - - public function getTranslationKey(): string - { - return 'unzer-payment-settings.apple-pay.certificates.update.error.messageMissingCertificateFiles'; - } - - public function getTranslationData(): array - { - return [ - 'type' => $this->certificateType, - ]; - } -} diff --git a/src/Components/ApplePay/Struct/CertificateInformation.php b/src/Components/ApplePay/Struct/CertificateInformation.php deleted file mode 100644 index 56681460..00000000 --- a/src/Components/ApplePay/Struct/CertificateInformation.php +++ /dev/null @@ -1,47 +0,0 @@ -paymentProcessingValid; - } - - public function isPaymentProcessingInherited(): bool - { - return $this->paymentProcessingInherited; - } - - public function isMerchantIdentificationValid(): bool - { - return $this->merchantIdentificationValid; - } - - public function isMerchantIdentificationInherited(): bool - { - return $this->merchantIdentificationInherited; - } - - public function getMerchantIdentificationValidUntil(): ?DateTimeInterface - { - return $this->merchantIdentificationValidUntil; - } -} diff --git a/src/Components/BasketConverter/BasketConverter.php b/src/Components/BasketConverter/BasketConverter.php index 36c1595b..16a4a7ea 100644 --- a/src/Components/BasketConverter/BasketConverter.php +++ b/src/Components/BasketConverter/BasketConverter.php @@ -21,14 +21,14 @@ public function populateDeprecatedVariables(array $basket): array private function updateBasketItem(array $item, float $vat): array { - $vat = $vat / 100; + $vat /= 100; if ($item['type'] === 'voucher') { $item['amountDiscount'] = round((float) $item['amountDiscountPerUnitGross'] * (int) $item['quantity'], UnzerPayment6::MAX_DECIMAL_PRECISION); } $item['amountPerUnit'] = $item['amountPerUnitGross']; - $item['amountGross'] = $item['amountPerUnitGross'] * $item['quantity']; + $item['amountGross'] = $item['amountPerUnitGross'] * $item['quantity']; $amountNet = (float) $item['amountGross'] / (1 + $vat); diff --git a/src/Components/BookingMode.php b/src/Components/BookingMode.php index 8bba0218..f6f375b5 100755 --- a/src/Components/BookingMode.php +++ b/src/Components/BookingMode.php @@ -6,6 +6,6 @@ class BookingMode { - public const CHARGE = 'charge'; + public const CHARGE = 'charge'; public const AUTHORIZE = 'authorize'; } diff --git a/src/Components/CancelService/CancelService.php b/src/Components/CancelService/CancelService.php index 7a07182e..f37e1723 100644 --- a/src/Components/CancelService/CancelService.php +++ b/src/Components/CancelService/CancelService.php @@ -13,10 +13,13 @@ use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\Struct\KeyPairContext; +use UnzerPayment6\Components\TransactionStateHandler\TransactionStateHandlerInterface; +use UnzerPayment6\Components\UnzerUtil\UnzerTransactionUtil; use UnzerPayment6\Installer\PaymentInstaller; use UnzerPayment6\UnzerPayment6; use UnzerSDK\Constants\CancelReasonCodes; use UnzerSDK\Resources\TransactionTypes\Cancellation; +use UnzerSDK\Unzer; class CancelService implements CancelServiceInterface { @@ -24,21 +27,15 @@ class CancelService implements CancelServiceInterface PaymentInstaller::PAYMENT_ID_PAYLATER_INVOICE, PaymentInstaller::PAYMENT_ID_PAYLATER_INSTALLMENT, PaymentInstaller::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED, + PaymentInstaller::PAYMENT_ID_KLARNA, // same procedure for Klarna as for UPL! ]; - private EntityRepository $orderTransactionRepository; - - private ClientFactoryInterface $clientFactory; - private LoggerInterface $logger; - public function __construct( - EntityRepository $orderTransactionRepository, - ClientFactoryInterface $clientFactory, - LoggerInterface $logger + private EntityRepository $orderTransactionRepository, + private ClientFactoryInterface $clientFactory, + private TransactionStateHandlerInterface $transactionStateHandler, + private LoggerInterface $logger ) { - $this->orderTransactionRepository = $orderTransactionRepository; - $this->clientFactory = $clientFactory; - $this->logger = $logger; } /** @@ -65,38 +62,38 @@ public function cancelChargeById(string $orderTransactionId, string $chargeId, f $taxRates[] = $calculatedTax->getTaxRate(); } - $clearedTaxRate = count($taxRates) > 0 - ? array_sum($taxRates) / count($taxRates) + $clearedTaxRate = \count($taxRates) > 0 + ? array_sum($taxRates) / \count($taxRates) : 0; $roundedAmountGross = (int) round($amountGross * (10 ** $decimalPrecision)); - $roundedAmountNet = (int) round($roundedAmountGross / (100 + $clearedTaxRate) * 100); - $roundedAmountVat = $roundedAmountGross - $roundedAmountNet; - $amountNet = $roundedAmountNet / (10 ** $decimalPrecision); - $amountVat = $roundedAmountVat / (10 ** $decimalPrecision); + $roundedAmountNet = (int) round($roundedAmountGross / (100 + $clearedTaxRate) * 100); + $roundedAmountVat = $roundedAmountGross - $roundedAmountNet; + $amountNet = $roundedAmountNet / (10 ** $decimalPrecision); + $amountVat = $roundedAmountVat / (10 ** $decimalPrecision); $client = $this->clientFactory->createClient(KeyPairContext::createFromOrderTransaction($transaction)); - + $payment = UnzerTransactionUtil::fetchPaymentFromOrderTransaction($transaction, $client); if ($this->isPaylaterPaymentMethod($transaction->getPaymentMethodId())) { $cancellation = new Cancellation($amountGross); $client->cancelChargedPayment( - $orderTransactionId, + $payment, $cancellation ); - - return; + } else { + $client->cancelChargeById( + $payment, + $chargeId, + $amountGross, + $this->getCancelReasonCode($reasonCode), + '', + $amountNet, + $amountVat + ); } - $client->cancelChargeById( - $orderTransactionId, - $chargeId, - $amountGross, - $this->getCancelReasonCode($reasonCode), - '', - $amountNet, - $amountVat - ); + $this->updateOrderStatus($client, $transaction, $context); } /** @@ -116,11 +113,12 @@ public function cancelAuthorizationById(string $orderTransactionId, string $paym if ($this->isPaylaterPaymentMethod($transaction->getPaymentMethodId())) { $this->logger->info('Canceling authorization by payment', ['authorization' => $authorization->getPayment()]); $client->cancelAuthorizedPayment($authorization->getPayment(), new Cancellation($amountGross)); - - return; + } else { + $this->logger->info('Canceling authorization', ['authorization' => $authorization]); + $authorization->cancel($amountGross); } - $this->logger->info('Canceling authorization', ['authorization' => $authorization]); - $authorization->cancel($amountGross); + + $this->updateOrderStatus($client, $transaction, $context); } protected function getOrderTransaction(string $orderTransactionId, Context $context): ?OrderTransactionEntity @@ -143,6 +141,20 @@ protected function getCancelReasonCode(?string $reasonCode): string protected function isPaylaterPaymentMethod(string $paymentMethodId): bool { - return in_array($paymentMethodId, self::PAYLATER_PAYMENT_METHODS); + return \in_array($paymentMethodId, self::PAYLATER_PAYMENT_METHODS, true); + } + + private function updateOrderStatus(Unzer $client, OrderTransactionEntity $orderTransaction, Context $context): void + { + try { + $payment = UnzerTransactionUtil::fetchPaymentFromOrderTransaction($orderTransaction, $client); + $this->transactionStateHandler->transformTransactionState( + $orderTransaction->getId(), + $payment, + $context + ); + } catch (\Throwable $e) { + $this->logger->error('error updating transaction state after cancel action: ' . $e->getMessage()); + } } } diff --git a/src/Components/CancelService/CancelServiceInterface.php b/src/Components/CancelService/CancelServiceInterface.php index 68e39bfb..98fe84af 100644 --- a/src/Components/CancelService/CancelServiceInterface.php +++ b/src/Components/CancelService/CancelServiceInterface.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\CancelService; -use RuntimeException; use Shopware\Core\Framework\Context; use UnzerSDK\Exceptions\UnzerApiException; @@ -12,7 +11,7 @@ interface CancelServiceInterface { /** * @throws UnzerApiException - * @throws RuntimeException + * @throws \RuntimeException */ public function cancelChargeById( string $orderTransactionId, @@ -24,12 +23,12 @@ public function cancelChargeById( /** * @throws UnzerApiException - * @throws RuntimeException + * @throws \RuntimeException */ public function cancelAuthorizationById( - string $orderTransactionId, - string $paymentId, - float $amountGross, + string $orderTransactionId, + string $paymentId, + float $amountGross, Context $context ): void; } diff --git a/src/Components/Cart/Validator/PaymentMethodValidator.php b/src/Components/Cart/Validator/PaymentMethodValidator.php index cef336b4..b17f6c33 100644 --- a/src/Components/Cart/Validator/PaymentMethodValidator.php +++ b/src/Components/Cart/Validator/PaymentMethodValidator.php @@ -28,7 +28,7 @@ public function __construct(EntityRepository $pluginRepository) public function validate(Cart $cart, ErrorCollection $errors, SalesChannelContext $context): void { - if (!in_array($context->getPaymentMethod()->getId(), PaymentInstaller::PAYMENT_METHOD_IDS)) { + if (!\in_array($context->getPaymentMethod()->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true)) { return; } @@ -42,7 +42,7 @@ public function validate(Cart $cart, ErrorCollection $errors, SalesChannelContex return; } - if (!in_array($context->getCurrency()->getIsoCode(), ['EUR', 'CHF'])) { + if (!\in_array($context->getCurrency()->getIsoCode(), ['EUR', 'CHF'], true)) { $errors->add(new PaymentMethodBlockedError((string) $context->getPaymentMethod()->getTranslation('name'))); return; @@ -52,7 +52,7 @@ public function validate(Cart $cart, ErrorCollection $errors, SalesChannelContex return; } - if (!in_array($context->getCustomer()->getActiveBillingAddress()->getCountry()->getIso(), ['DE', 'AT', 'CH'])) { + if (!\in_array($context->getCustomer()->getActiveBillingAddress()->getCountry()->getIso(), ['DE', 'AT', 'CH'], true)) { $errors->add(new PaymentMethodBlockedError((string) $context->getPaymentMethod()->getTranslation('name'))); } } diff --git a/src/Components/ClientFactory/ClientFactory.php b/src/Components/ClientFactory/ClientFactory.php index f60c7864..5d082603 100644 --- a/src/Components/ClientFactory/ClientFactory.php +++ b/src/Components/ClientFactory/ClientFactory.php @@ -4,6 +4,8 @@ namespace UnzerPayment6\Components\ClientFactory; +use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\HttpFoundation\Request; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\ConfigReader\KeyPairConfigReader; @@ -34,6 +36,38 @@ public function createClient(KeyPairContext $keyPairContext, string $locale = se return $client; } + /** + * This will always use the main keypair + */ + public function createClientFromSalesChannelId(?string $salesChannelId, ?Request $request = null): Unzer + { + $locale = self::DEFAULT_LOCALE; + if ($request !== null) { + $locale = empty($request->getLocale()) ? $request->getDefaultLocale() : $request->getLocale(); + } + + $config = $this->configReader->read($salesChannelId); + $privateKey = $config->get(ConfigReader::CONFIG_KEY_PRIVATE_KEY); + + return $this->createClientFromPrivateKey($privateKey, $salesChannelId, $locale); + } + + /** + * This applies currency, payment method etc to use the correct keypair + */ + public function createClientFromSalesChannelContext(SalesChannelContext $salesChannelContext, ?Request $request = null): Unzer + { + $locale = self::DEFAULT_LOCALE; + if ($request !== null) { + $locale = empty($request->getLocale()) ? $request->getDefaultLocale() : $request->getLocale(); + } + + return $this->createClient( + KeyPairContext::createFromSalesChannelContext($salesChannelContext), + $locale + ); + } + public function createClientFromPrivateKey(string $privateKey, string $salesChannelId = '', string $locale = self::DEFAULT_LOCALE): Unzer { $client = new Unzer($privateKey, $locale); @@ -52,10 +86,10 @@ public function createClientFromPublicKey(string $publicKey, string $salesChanne return $client; } - protected function applyGlobalClientSettings(Unzer $client, string $salesChannelId = '') + protected function applyGlobalClientSettings(Unzer $client, string $salesChannelId = ''): void { $config = $this->configReader->read($salesChannelId); - $client->setDebugMode((bool)$config->get(ConfigReader::CONFIG_KEY_EXTENDED_LOGGING)); + $client->setDebugMode((bool) $config->get(ConfigReader::CONFIG_KEY_EXTENDED_LOGGING)); $client->setDebugHandler($this->debugHandler); $client->setClientIp($_SERVER['REMOTE_ADDR'] ?? null); } diff --git a/src/Components/ClientFactory/ClientFactoryInterface.php b/src/Components/ClientFactory/ClientFactoryInterface.php index 1e90496b..9960e32c 100644 --- a/src/Components/ClientFactory/ClientFactoryInterface.php +++ b/src/Components/ClientFactory/ClientFactoryInterface.php @@ -4,16 +4,24 @@ namespace UnzerPayment6\Components\ClientFactory; +use Shopware\Core\System\SalesChannel\SalesChannelContext; +use Symfony\Component\HttpFoundation\Request; use UnzerPayment6\Components\Struct\KeyPairContext; use UnzerSDK\Unzer; interface ClientFactoryInterface { - /** @var string */ + /** + * @var string + */ public const DEFAULT_LOCALE = 'en-GB'; public function createClient(KeyPairContext $keyPairContext, string $locale = self::DEFAULT_LOCALE): Unzer; + public function createClientFromSalesChannelContext(SalesChannelContext $salesChannelContext, ?Request $request = null): Unzer; + + public function createClientFromSalesChannelId(?string $salesChannelId, ?Request $request = null): Unzer; + public function createClientFromPrivateKey(string $privateKey, string $salesChannelId = '', string $locale = self::DEFAULT_LOCALE): Unzer; public function createClientFromPublicKey(string $publicKey, string $salesChannelId = '', string $locale = self::DEFAULT_LOCALE): Unzer; diff --git a/src/Components/ConfigReader/ConfigReader.php b/src/Components/ConfigReader/ConfigReader.php index 21319b62..6a831557 100644 --- a/src/Components/ConfigReader/ConfigReader.php +++ b/src/Components/ConfigReader/ConfigReader.php @@ -9,23 +9,23 @@ class ConfigReader implements ConfigReaderInterface { - /** @var string */ + /** + * @var string + */ public const SYSTEM_CONFIG_DOMAIN = 'UnzerPayment6.settings.'; - public const CONFIG_KEY_PUBLIC_KEY = 'publicKey'; - public const CONFIG_KEY_PRIVATE_KEY = 'privateKey'; - public const CONFIG_KEY_TEST_DATA = 'testData'; + public const CONFIG_KEY_PUBLIC_KEY = 'publicKey'; + public const CONFIG_KEY_PRIVATE_KEY = 'privateKey'; + public const CONFIG_KEY_TEST_DATA = 'testData'; public const CONFIG_KEY_EXTENDED_LOGGING = 'extendedLogging'; - public const CONFIG_KEY_BOOKING_MODE_CARD = 'bookingModeCreditCard'; - public const CONFIG_KEY_BOOKING_MODE_PAYPAL = 'bookingModePayPal'; - public const CONFIG_KEY_BOOKING_MODE_APPLE_PAY = 'bookingModeApplePay'; - public const CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID = 'applePayPaymentProcessingCertificateId'; - public const CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFICATION_CERTIFICATE_ID = 'applePayMerchantIdentificationCertificateId'; - public const CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFIER = 'applePayMerchantIdentifier'; - public const CONFIG_KEY_PAYLATER_INSTALLMENT = 'paylaterInstallment'; - public const CONFIG_KEY_PAYLATER_INVOICE = 'paylaterInvoice'; - public const CONFIG_KEY_PAYLATER_DIRECT_DEBIT_SECURED = 'paylaterDirectDebitSecured'; + public const CONFIG_KEY_BOOKING_MODE_CARD = 'bookingModeCreditCard'; + public const CONFIG_KEY_BOOKING_MODE_PAYPAL = 'bookingModePayPal'; + public const CONFIG_KEY_BOOKING_MODE_APPLE_PAY = 'bookingModeApplePay'; + public const CONFIG_KEY_BOOKING_MODE_WERO = 'bookingModeWero'; + public const CONFIG_KEY_PAYLATER_INSTALLMENT = 'paylaterInstallment'; + public const CONFIG_KEY_PAYLATER_INVOICE = 'paylaterInvoice'; + public const CONFIG_KEY_PAYLATER_DIRECT_DEBIT_SECURED = 'paylaterDirectDebitSecured'; public const CONFIG_KEY_SHIPPING_STATUS = 'statusForAutomaticShippingNotification'; @@ -41,8 +41,12 @@ class ConfigReader implements ConfigReaderInterface public const CONFIG_KEY_GOOGLE_PAY_BUTTON_SIZE_MODE = 'googlePayButtonSizeMode'; public const CONFIG_KEY_PAYPAL_SHOW_SAVE_ACCOUNT = 'paypalShowSaveAccount'; - public const CONFIG_KEY_DELIVERY_STATUS_FOR_CAPTURE = 'deliveryStatusForAutomaticCapture'; - public const CONFIG_KEY_DELIVERY_STATUS_FOR_REFUND = 'deliveryStatusForAutomaticRefund'; + public const CONFIG_KEY_DELIVERY_STATUS_FOR_CAPTURE = 'deliveryStatusForAutomaticCapture'; + public const CONFIG_KEY_DELIVERY_STATUS_FOR_REFUND = 'deliveryStatusForAutomaticRefund'; + + public const CONFIG_KEY_USE_EXPRESS_PAYPAL = 'usePaypalExpress'; + public const CONFIG_KEY_USE_EXPRESS_GOOGLE = 'useGooglePayExpress'; + public const CONFIG_KEY_USE_EXPRESS_APPLEPAY = 'useApplePayExpress'; private SystemConfigService $systemConfigService; @@ -62,7 +66,7 @@ public function read(string $salesChannelId = '', bool $fallback = true): Config $config = []; foreach ($values as $key => $value) { - $property = substr($key, strlen(self::SYSTEM_CONFIG_DOMAIN)); + $property = substr($key, \strlen(self::SYSTEM_CONFIG_DOMAIN)); if (!empty($property)) { $config[$property] = $value; diff --git a/src/Components/ConfigReader/KeyPairConfigReader.php b/src/Components/ConfigReader/KeyPairConfigReader.php index 7d0ea02c..0baa4147 100644 --- a/src/Components/ConfigReader/KeyPairConfigReader.php +++ b/src/Components/ConfigReader/KeyPairConfigReader.php @@ -40,7 +40,7 @@ public function getMatchingKey(string $key, string $salesChannelId): ?string $paylaterInvoiceKeys = $config->get(ConfigReader::CONFIG_KEY_PAYLATER_INVOICE); - if (is_array($paylaterInvoiceKeys)) { + if (\is_array($paylaterInvoiceKeys)) { foreach ($paylaterInvoiceKeys as $keyPairConfig) { if ($keyPairConfig['publicKey'] === $key) { return $keyPairConfig['privateKey']; @@ -54,7 +54,7 @@ public function getMatchingKey(string $key, string $salesChannelId): ?string $paylaterInstallmentKeys = $config->get(ConfigReader::CONFIG_KEY_PAYLATER_INSTALLMENT); - if (is_array($paylaterInstallmentKeys)) { + if (\is_array($paylaterInstallmentKeys)) { foreach ($paylaterInstallmentKeys as $keyPairConfig) { if ($keyPairConfig['publicKey'] === $key) { return $keyPairConfig['privateKey']; @@ -68,7 +68,7 @@ public function getMatchingKey(string $key, string $salesChannelId): ?string $paylaterDirectDebitSecuredKeys = $config->get(ConfigReader::CONFIG_KEY_PAYLATER_DIRECT_DEBIT_SECURED); - if (is_array($paylaterDirectDebitSecuredKeys)) { + if (\is_array($paylaterDirectDebitSecuredKeys)) { foreach ($paylaterDirectDebitSecuredKeys as $keyPairConfig) { if ($keyPairConfig['publicKey'] === $key) { return $keyPairConfig['privateKey']; @@ -101,11 +101,11 @@ private function getKey(KeyPairContext $keyPairContext, string $keyPairConfigKey return $privateKey; } - $keyPairConfigs = $configData->get($configKey); + $keyPairConfigs = $configData->get($configKey, []); foreach ($keyPairConfigs as $keyPairConfig) { $customerType = $keyPairContext->isB2B() ? 'b2b' : 'b2c'; - $currentKey = sprintf('%s-%s', $customerType, strtolower($keyPairContext->getCurrencyIsoCode())); + $currentKey = \sprintf('%s-%s', $customerType, strtolower($keyPairContext->getCurrencyIsoCode())); if ($keyPairConfig['key'] === $currentKey) { return $keyPairConfig[$keyPairConfigKey]; diff --git a/src/Components/CustomFieldsHelper/CustomFieldsHelper.php b/src/Components/CustomFieldsHelper/CustomFieldsHelper.php index 657c675c..15861823 100644 --- a/src/Components/CustomFieldsHelper/CustomFieldsHelper.php +++ b/src/Components/CustomFieldsHelper/CustomFieldsHelper.php @@ -23,7 +23,7 @@ public function setOrderTransactionCustomFields( OrderTransactionEntity $transaction, Context $context ): void { - $shipmentExecuted = !in_array( + $shipmentExecuted = !\in_array( $transaction->getPaymentMethodId(), AutomaticShippingValidatorInterface::HANDLED_PAYMENT_METHODS, true @@ -32,11 +32,11 @@ public function setOrderTransactionCustomFields( $customFields = $transaction->getCustomFields() ?? []; $customFields = array_merge($customFields, [ CustomFieldInstaller::UNZER_PAYMENT_IS_TRANSACTION => true, - CustomFieldInstaller::UNZER_PAYMENT_IS_SHIPPED => $shipmentExecuted, + CustomFieldInstaller::UNZER_PAYMENT_IS_SHIPPED => $shipmentExecuted, ]); $update = [ - 'id' => $transaction->getId(), + 'id' => $transaction->getId(), 'customFields' => $customFields, ]; @@ -51,7 +51,7 @@ public function setOrderTransactionUnzerFlag(OrderTransactionEntity $transaction ]); $update = [ - 'id' => $transaction->getId(), + 'id' => $transaction->getId(), 'customFields' => $customFields, ]; diff --git a/src/Components/DependencyInjection/Factory/PaymentTransitionMapperFactory.php b/src/Components/DependencyInjection/Factory/PaymentTransitionMapperFactory.php index e8d04c1b..1f3522c9 100644 --- a/src/Components/DependencyInjection/Factory/PaymentTransitionMapperFactory.php +++ b/src/Components/DependencyInjection/Factory/PaymentTransitionMapperFactory.php @@ -10,7 +10,9 @@ class PaymentTransitionMapperFactory { - /** @var AbstractTransitionMapper[] */ + /** + * @var AbstractTransitionMapper[] + */ protected iterable $transitionMapperCollection = []; public function __construct(iterable $transitionMapperCollection) diff --git a/src/Components/Event/AutomaticShippingNotificationEvent.php b/src/Components/Event/AutomaticShippingNotificationEvent.php index 393c1ddf..d6f94a9f 100644 --- a/src/Components/Event/AutomaticShippingNotificationEvent.php +++ b/src/Components/Event/AutomaticShippingNotificationEvent.php @@ -19,8 +19,8 @@ class AutomaticShippingNotificationEvent extends Event public function __construct(OrderEntity $orderEntity, string $invoiceId, Context $context) { $this->orderEntity = $orderEntity; - $this->invoiceId = $invoiceId; - $this->context = $context; + $this->invoiceId = $invoiceId; + $this->context = $context; } public function getOrderEntity(): OrderEntity diff --git a/src/Components/ExpressCheckout/ExpressCheckoutService.php b/src/Components/ExpressCheckout/ExpressCheckoutService.php new file mode 100644 index 00000000..1351df07 --- /dev/null +++ b/src/Components/ExpressCheckout/ExpressCheckoutService.php @@ -0,0 +1,374 @@ +cartService->getCart($salesChannelContext->getToken(), $salesChannelContext); + } + + public function startCheckoutSessionFromUnzerPayment(Payment $payment, ?string $paymentMethodId, SalesChannelContext $salesChannelContext): void + { + $data = $this->getRegisterDataFromUnzerPayment($payment, $salesChannelContext); + $this->startCheckoutDataWithNormalizedCustomerData($data, $paymentMethodId, $salesChannelContext); + } + + public function startCheckoutSessionFromGooglePayData(array $paymentData, ?string $paymentMethodId, SalesChannelContext $salesChannelContext): void + { + $data = $this->getRegisterDataFromGooglePayData($paymentData, $salesChannelContext); + $this->startCheckoutDataWithNormalizedCustomerData($data, $paymentMethodId, $salesChannelContext); + } + + public function startCheckoutSessionFromApplePayData(array $paymentData, array $shippingContact, array $billingContact, ?string $paymentMethodId, SalesChannelContext $salesChannelContext): void + { + $data = $this->getRegisterDataFromApplePayData($shippingContact, $billingContact, $salesChannelContext); + $this->startCheckoutDataWithNormalizedCustomerData($data, $paymentMethodId, $salesChannelContext); + } + + public function setPaymentMethod(string $paymentMethodId, CustomerEntity $customer, SalesChannelContext $salesChannelContext): void + { + $this->customerRepository->upsert([ + [ + 'id' => $customer->getId(), + 'defaultPaymentMethodId' => $paymentMethodId, + ], + ], $salesChannelContext->getContext()); + $this->salesChannelContextSwitcher->update( + new RequestDataBag([ + SalesChannelContextService::PAYMENT_METHOD_ID => $paymentMethodId, + ]), + $salesChannelContext + ); + } + + private function startCheckoutDataWithNormalizedCustomerData(?DataBag $data, ?string $paymentMethodId, SalesChannelContext $salesChannelContext): void + { + if ($data === null) { + throw new \Exception('no customer data'); + } + if ($salesChannelContext->getCustomer()) { + $this->updateShippingAddress($data->get('shippingAddress'), $salesChannelContext->getCustomer()->getId(), $salesChannelContext->getContext()); + if ($paymentMethodId) { + $this->setPaymentMethod($paymentMethodId, $salesChannelContext->getCustomer(), $salesChannelContext); + } + } else { + $result = $this->registerRoute->register( + $data->toRequestDataBag(), + $salesChannelContext, + false + ); + + if ($result->getCustomer()) { + if ($paymentMethodId) { + $this->setPaymentMethod($paymentMethodId, $result->getCustomer(), $salesChannelContext); + } + $this->login($result->getCustomer(), $salesChannelContext); + } + } + } + + private function getRegisterDataFromGooglePayData(array $paymentData, SalesChannelContext $salesChannelContext): ?DataBag + { + $data = new DataBag(); + $password = Uuid::randomHex(); + $name = $paymentData['paymentMethodData']['info']['billingAddress']['name'] ?? '---- ----'; + $names = $this->separateName($name); + + $shippingAddress = $this->getAddressFromGoogleData($paymentData['shippingAddress'] ?? [], $salesChannelContext->getContext()); + $billingAddress = $this->getAddressFromGoogleData($paymentData['paymentMethodData']['info']['billingAddress'] ?? [], $salesChannelContext->getContext()); + if (!$this->isAddressDataBagComplete($billingAddress)) { + $billingAddress = $shippingAddress; + } + + $data->add([ + 'firstName' => $names['firstName'], + 'lastName' => $names['lastName'], + 'guest' => true, + 'email' => $paymentData['email'] ?? '', + 'salutationId' => $this->getDefaultSalutationId($salesChannelContext->getContext()), + 'acceptedDataProtection' => true, + 'password' => $password, + 'passwordConfirmation' => $password, + 'billingAddress' => $billingAddress, + 'shippingAddress' => $shippingAddress, + ]); + + return $data; + } + + private function getRegisterDataFromApplePayData(array $shippingContact, array $billingContact, SalesChannelContext $salesChannelContext): ?DataBag + { + $data = new DataBag(); + $password = Uuid::randomHex(); + $firstName = $shippingContact['givenName'] === '' ? '----' : $shippingContact['givenName']; + $lastName = $shippingContact['familyName'] === '' ? '----' : $shippingContact['familyName']; + + $shippingAddress = $this->getAddressFromApplePayData($shippingContact ?? [], $salesChannelContext->getContext()); + $billingAddress = $this->getAddressFromApplePayData($billingContact ?? [], $salesChannelContext->getContext()); + if (!$this->isAddressDataBagComplete($billingAddress)) { + $billingAddress = $shippingAddress; + } + + $data->add([ + 'firstName' => $firstName, + 'lastName' => $lastName, + 'guest' => true, + 'email' => $shippingContact['emailAddress'] ?? '', + 'salutationId' => $this->getDefaultSalutationId($salesChannelContext->getContext()), + 'acceptedDataProtection' => true, + 'password' => $password, + 'passwordConfirmation' => $password, + 'billingAddress' => $billingAddress, + 'shippingAddress' => $shippingAddress, + ]); + + return $data; + } + + private function getRegisterDataFromUnzerPayment(Payment $payment, SalesChannelContext $salesChannelContext): ?DataBag + { + $data = new DataBag(); + + $customer = $payment->getCustomer(); + + if ($customer === null) { + return null; + } + + $shippingAddress = $this->getAddressFromUnzerCustomer($customer, $salesChannelContext->getContext(), 'shipping'); + $billingAddress = $this->getAddressFromUnzerCustomer($customer, $salesChannelContext->getContext()); + if (!$this->isAddressDataBagComplete($billingAddress)) { + $billingAddress = $shippingAddress; + } + + $password = Uuid::randomHex(); + $data->add([ + 'firstName' => $customer->getFirstname(), + 'lastName' => $customer->getLastname(), + 'guest' => true, + 'email' => $customer->getEmail(), + 'salutationId' => $this->getDefaultSalutationId($salesChannelContext->getContext()), + 'acceptedDataProtection' => true, + 'password' => $password, + 'passwordConfirmation' => $password, + 'billingAddress' => $billingAddress, + 'shippingAddress' => $shippingAddress, + ]); + + return $data; + } + + private function isAddressDataBagComplete(DataBag $dataBag): bool + { + foreach ($dataBag->all() as $key => $value) { + if ($value === self::ADDRESS_PART_PLACEHOLDER) { + return false; + } + } + + return true; + } + + private function getAddressFromGoogleData(array $address, Context $context): ?DataBag + { + $nameParts = $this->separateName($address['name'] ?? self::ADDRESS_PART_PLACEHOLDER . ' ' . self::ADDRESS_PART_PLACEHOLDER); + + return new DataBag([ + 'salutationId' => $this->getDefaultSalutationId($context), + 'firstName' => $nameParts['firstName'], + 'lastName' => $nameParts['lastName'], + 'city' => $address['locality'] ?? self::ADDRESS_PART_PLACEHOLDER, + 'countryId' => $this->getCountryId($address['countryCode'], $context), + 'street' => $address['address1'] ?? self::ADDRESS_PART_PLACEHOLDER, + 'additionalAddressLine1' => $address['address2'] ?? null, + 'additionalAddressLine2' => $address['address3'] ?? null, + 'zipcode' => $address['postalCode'] ?? self::ADDRESS_PART_PLACEHOLDER, + 'phoneNumber' => '', + 'company' => '', + ]); + } + + private function getAddressFromApplePayData(array $address, Context $context): ?DataBag + { + return new DataBag([ + 'salutationId' => $this->getDefaultSalutationId($context), + 'firstName' => $address['givenName'] === '' ? '----' : $address['givenName'], + 'lastName' => $address['familyName'] === '' ? '----' : $address['familyName'], + 'city' => $address['locality'] ?? self::ADDRESS_PART_PLACEHOLDER, + 'countryId' => $this->getCountryId($address['countryCode'], $context), + 'street' => $address['addressLines'][0] ?? self::ADDRESS_PART_PLACEHOLDER, + 'additionalAddressLine1' => $address['addressLines'][1] ?? null, + 'additionalAddressLine2' => $address['addressLines'][2] ?? null, + 'zipcode' => $address['postalCode'] ?? self::ADDRESS_PART_PLACEHOLDER, + 'phoneNumber' => $address['phoneNumber'] ?? '', + 'company' => '', + ]); + } + + private function getAddressFromUnzerCustomer(Customer $customer, Context $context, string $addressType = 'billing'): ?DataBag + { + $address = ($addressType === 'billing' ? $customer->getBillingAddress() : $customer->getShippingAddress()); + + if (empty($address->getZip())) { + $address->setZip(self::ADDRESS_PART_PLACEHOLDER); + } + if (empty($address->getCity())) { + $address->setCity(self::ADDRESS_PART_PLACEHOLDER); + } + if (empty($address->getStreet())) { + $address->setStreet(self::ADDRESS_PART_PLACEHOLDER); + } + + $nameParts = $this->separateName($address->getName()); + + return new DataBag([ + 'salutationId' => $this->getDefaultSalutationId($context), + 'firstName' => $nameParts['firstName'], + 'lastName' => $nameParts['lastName'], + 'city' => $address->getCity(), + 'countryId' => $this->getCountryId($address->getCountry(), $context), + 'street' => $address->getStreet(), + 'zipcode' => $address->getZip(), + 'phoneNumber' => '', + 'company' => '', + ]); + } + + private function getCountryId(string $countryCode, Context $context): ?string + { + $criteria = new Criteria(); + $criteria->setLimit(1); + $criteria->addFilter( + new EqualsFilter('iso', $countryCode) + ); + + return $this->countryRepository->searchIds($criteria, $context)->firstId(); + } + + private function login(CustomerEntity $customer, SalesChannelContext $salesChannelContext): string + { + $newToken = $this->salesChannelContextPersister->replace($salesChannelContext->getToken(), $salesChannelContext); + $this->salesChannelContextPersister->save( + $newToken, + ['billingAddressId' => null, 'shippingAddressId' => null], + $salesChannelContext->getSalesChannel()->getId(), + $customer->getId() + ); + + $event = new CustomerLoginEvent($salesChannelContext, $customer, $newToken); + $this->eventDispatcher->dispatch($event); + + return $newToken; + } + + private function getDefaultSalutationId(Context $context): ?string + { + $criteria = new Criteria(); + $criteria->setLimit(1); + $criteria->addFilter( + new EqualsFilter('salutationKey', SalutationDefinition::NOT_SPECIFIED) + ); + + return $this->salutationRepository->searchIds($criteria, $context)->firstId(); + } + + private function separateName(string $name): array + { + $exploded = explode(' ', $name); + if (\count($exploded) > 1) { + $lastName = array_pop($exploded); + + return [ + 'firstName' => implode(' ', $exploded), + 'lastName' => $lastName, + ]; + } + + return [ + 'firstName' => $exploded[0], + 'lastName' => '.', + ]; + } + + private function updateShippingAddress(DataBag $address, string $customerId, Context $context): void + { + $addressId = $this->getExistingCustomerAddressId($customerId, $address, $context); + + if ($addressId) { + $this->customerRepository->upsert([ + [ + 'id' => $customerId, + 'defaultShippingAddressId' => $addressId, + ], + ], $context); + + return; + } + + $this->customerRepository->upsert([ + [ + 'id' => $customerId, + 'defaultShippingAddress' => $address->all(), + ], + ], $context); + } + + private function getExistingCustomerAddressId(string $customerId, DataBag $address, Context $context): ?string + { + $criteria = new Criteria(); + $criteria->addFilter( + new EqualsFilter('customerId', $customerId), + new EqualsFilter('firstName', $address->get('firstName')), + new EqualsFilter('lastName', $address->get('lastName')), + new EqualsFilter('city', $address->get('city')), + new EqualsFilter('zipcode', $address->get('zipcode')), + new EqualsFilter('street', $address->get('street')), + new EqualsFilter('countryId', $address->get('countryId')), + ); + + return $this->customerAddressRepository->searchIds($criteria, $context)->firstId(); + } +} diff --git a/src/Components/PaymentFrame/PaymentFrameFactory.php b/src/Components/PaymentFrame/PaymentFrameFactory.php index 1979bc61..54e8997e 100644 --- a/src/Components/PaymentFrame/PaymentFrameFactory.php +++ b/src/Components/PaymentFrame/PaymentFrameFactory.php @@ -8,7 +8,7 @@ class PaymentFrameFactory implements PaymentFrameFactoryInterface { public function getPaymentFrame(string $paymentMethodId): ?string { - if (!array_key_exists($paymentMethodId, self::DEFAULT_FRAME_MAPPING)) { + if (!\array_key_exists($paymentMethodId, PaymentFrameFactoryInterface::DEFAULT_FRAME_MAPPING)) { return null; } diff --git a/src/Components/PaymentFrame/PaymentFrameFactoryInterface.php b/src/Components/PaymentFrame/PaymentFrameFactoryInterface.php index f6e46fc9..d9ec1e4e 100644 --- a/src/Components/PaymentFrame/PaymentFrameFactoryInterface.php +++ b/src/Components/PaymentFrame/PaymentFrameFactoryInterface.php @@ -9,19 +9,13 @@ interface PaymentFrameFactoryInterface { public const DEFAULT_FRAME_MAPPING = [ - PaymentInstaller::PAYMENT_ID_CREDIT_CARD => '@Storefront/storefront/component/unzer/frames/credit-card.html.twig', - PaymentInstaller::PAYMENT_ID_INVOICE => '@Storefront/storefront/component/unzer/frames/invoice.html.twig', - PaymentInstaller::PAYMENT_ID_INVOICE_SECURED => '@Storefront/storefront/component/unzer/frames/invoice-secured.html.twig', - PaymentInstaller::PAYMENT_ID_PAYLATER_INVOICE => '@Storefront/storefront/component/unzer/frames/paylater-invoice.html.twig', - PaymentInstaller::PAYMENT_ID_IDEAL => '@Storefront/storefront/component/unzer/frames/ideal.html.twig', - PaymentInstaller::PAYMENT_ID_DIRECT_DEBIT => '@Storefront/storefront/component/unzer/frames/sepa-direct-debit.html.twig', - PaymentInstaller::PAYMENT_ID_DIRECT_DEBIT_SECURED => '@Storefront/storefront/component/unzer/frames/sepa-direct-debit-secured.html.twig', - PaymentInstaller::PAYMENT_ID_INSTALLMENT_SECURED => '@Storefront/storefront/component/unzer/frames/installment-secured.html.twig', - PaymentInstaller::PAYMENT_ID_PAYPAL => '@Storefront/storefront/component/unzer/frames/paypal.html.twig', - PaymentInstaller::PAYMENT_ID_APPLE_PAY => '@Storefront/storefront/component/unzer/frames/empty.html.twig', - PaymentInstaller::PAYMENT_ID_APPLE_PAY_V2 => '@Storefront/storefront/component/unzer/frames/empty.html.twig', - PaymentInstaller::PAYMENT_ID_GOOGLE_PAY => '@Storefront/storefront/component/unzer/frames/empty.html.twig', - PaymentInstaller::PAYMENT_ID_PAYLATER_INSTALLMENT => '@Storefront/storefront/component/unzer/frames/paylater-installment.html.twig', + PaymentInstaller::PAYMENT_ID_CREDIT_CARD => '@Storefront/storefront/component/unzer/frames/credit-card.html.twig', + PaymentInstaller::PAYMENT_ID_PAYLATER_INVOICE => '@Storefront/storefront/component/unzer/frames/paylater-invoice.html.twig', + PaymentInstaller::PAYMENT_ID_DIRECT_DEBIT => '@Storefront/storefront/component/unzer/frames/sepa-direct-debit.html.twig', + PaymentInstaller::PAYMENT_ID_PAYPAL => '@Storefront/storefront/component/unzer/frames/paypal.html.twig', + PaymentInstaller::PAYMENT_ID_APPLE_PAY_V2 => '@Storefront/storefront/component/unzer/frames/empty.html.twig', + PaymentInstaller::PAYMENT_ID_GOOGLE_PAY => '@Storefront/storefront/component/unzer/frames/empty.html.twig', + PaymentInstaller::PAYMENT_ID_PAYLATER_INSTALLMENT => '@Storefront/storefront/component/unzer/frames/paylater-installment.html.twig', PaymentInstaller::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED => '@Storefront/storefront/component/unzer/frames/paylater-direct-debit-secured.html.twig', ]; diff --git a/src/Components/PaymentHandler/AbstractUnzerPaymentHandler.php b/src/Components/PaymentHandler/AbstractUnzerPaymentHandler.php index 7d253bfb..5dbffd44 100755 --- a/src/Components/PaymentHandler/AbstractUnzerPaymentHandler.php +++ b/src/Components/PaymentHandler/AbstractUnzerPaymentHandler.php @@ -5,26 +5,31 @@ namespace UnzerPayment6\Components\PaymentHandler; use Psr\Log\LoggerInterface; +use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct; use Shopware\Core\Checkout\Payment\Cart\PaymentHandler\AsynchronousPaymentHandlerInterface; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; +use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Throwable; +use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\CustomFieldsHelper\CustomFieldsHelperInterface; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; +use UnzerPayment6\Components\ResourceHydrator\BasketResourceHydrator; use UnzerPayment6\Components\ResourceHydrator\CustomerResourceHydrator\CustomerResourceHydratorInterface; +use UnzerPayment6\Components\ResourceHydrator\MetadataResourceHydrator; use UnzerPayment6\Components\ResourceHydrator\ResourceHydratorInterface; use UnzerPayment6\Components\Struct\Configuration; use UnzerPayment6\Components\Struct\KeyPairContext; use UnzerPayment6\Components\TransactionStateHandler\TransactionStateHandlerInterface; +use UnzerPayment6\Installer\CustomFieldInstaller; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\AbstractUnzerResource; use UnzerSDK\Resources\Basket; @@ -37,30 +42,56 @@ abstract class AbstractUnzerPaymentHandler implements AsynchronousPaymentHandlerInterface { - /** @var BasePaymentType */ + public const SAVE_PAYMENT_DEVICE_KEY = 'save_payment_device'; + + /** + * @var BasePaymentType + */ protected $paymentType; - /** @var null|Payment */ + /** + * @var Payment|null + */ protected $payment; - /** @var Recurring */ + /** + * @var Recurring + */ protected $recurring; - /** @var Unzer */ + /** + * @var Unzer + */ protected $unzerClient; - /** @var Customer */ + /** + * @var Customer + */ protected $unzerCustomer; - /** @var Basket */ + /** + * @var Basket + */ protected $unzerBasket; - /** @var Metadata */ + /** + * @var Metadata + */ protected $unzerMetadata; - /** @var Configuration */ + /** + * @var Configuration + */ protected $pluginConfig; + protected bool $isExpress = false; + + protected string $bookingMode = BookingMode::CHARGE; + + /** + * @param BasketResourceHydrator $basketHydrator + * @param MetadataResourceHydrator $metadataHydrator + */ public function __construct( protected readonly ResourceHydratorInterface $basketHydrator, protected readonly CustomerResourceHydratorInterface $customerHydrator, @@ -74,7 +105,6 @@ public function __construct( protected readonly CustomFieldsHelperInterface $customFieldsHelper ) { - } public function pay( @@ -83,20 +113,20 @@ public function pay( SalesChannelContext $salesChannelContext ): RedirectResponse { + $this->logger->debug('Starting pay() base method in ' . static::class); $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); try { $salesChannelId = $salesChannelContext->getSalesChannel()->getId(); $this->pluginConfig = $this->configReader->read($salesChannelId); - $this->unzerClient = $this->clientFactory->createClient( - KeyPairContext::createFromSalesChannelContext($salesChannelContext), - empty($currentRequest->getLocale()) ? $currentRequest->getDefaultLocale() : $currentRequest->getLocale() - ); + $this->unzerClient = $this->clientFactory->createClientFromSalesChannelContext($salesChannelContext, $currentRequest); $this->unzerBasket = $this->basketHydrator->hydrateObject($salesChannelContext, $transaction); $this->unzerMetadata = $this->metadataHydrator->hydrateObject($salesChannelContext, $transaction); - $this->unzerCustomer = $this->getUnzerCustomer($currentRequest->get('unzerCustomerId', ''), $transaction->getOrderTransaction()->getPaymentMethodId(), $salesChannelContext); + $this->metadataHydrator->setIsExpress($this->unzerMetadata, $this->isExpress); + + $this->unzerCustomer = $this->getUnzerCustomer($currentRequest->get('unzerCustomerId', ''), $transaction->getOrderTransaction()->getPaymentMethodId(), $transaction->getOrderTransaction(), $salesChannelContext); $resourceId = $currentRequest->get('unzerResourceId', ''); @@ -109,7 +139,7 @@ public function pay( return new RedirectResponse($transaction->getReturnUrl()); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -123,9 +153,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -143,14 +173,24 @@ public function finalize( SalesChannelContext $salesChannelContext ): void { + $this->logger->debug('Starting finalize() base method in ' . static::class); try { $this->pluginConfig = $this->configReader->read($salesChannelContext->getSalesChannel()->getId()); $this->unzerClient = $this->clientFactory->createClient( KeyPairContext::createFromSalesChannelContext($salesChannelContext) ); - $this->payment = $this->unzerClient->fetchPaymentByOrderId( - $transaction->getOrderTransaction()->getId() - ); + try { + $this->payment = $this->unzerClient->fetchPaymentByOrderId( + $transaction->getOrderTransaction()->getId() + ); + } catch (UnzerApiException) { + $paymentId = $transaction->getOrderTransaction()->getCustomFields()[CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY] ?? null; + if ($paymentId) { + $this->payment = $this->unzerClient->fetchPayment($paymentId); + } else { + throw PaymentException::asyncFinalizeInterrupted($transaction->getOrderTransaction()->getId(), 'no payment found'); + } + } $this->transactionStateHandler->transformTransactionState( $transaction->getOrderTransaction()->getId(), @@ -161,7 +201,7 @@ public function finalize( $this->customFieldsHelper->setOrderTransactionCustomFields($transaction->getOrderTransaction(), $salesChannelContext->getContext()); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'transaction' => $transaction, 'request' => $this->getLoggableRequest($request), @@ -170,9 +210,9 @@ public function finalize( ); throw PaymentException::asyncFinalizeInterrupted($transaction->getOrderTransaction()->getId(), $apiException->getMessage()); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'transaction' => $transaction, 'request' => $this->getLoggableRequest($request), @@ -216,63 +256,61 @@ protected function executeFailTransition(string $transactionId, Context $context ); } - protected function getUnzerCustomer(string $unzerCustomerId, string $paymentMethodId, SalesChannelContext $salesChannelContext): AbstractUnzerResource + protected function getUnzerCustomer(string $unzerCustomerId, string $paymentMethodId, OrderTransactionEntity $orderTransaction, SalesChannelContext $salesChannelContext): AbstractUnzerResource { $customer = $salesChannelContext->getCustomer(); + if(empty($orderTransaction->getOrder())){ + $orderTransaction = $this->fetchTransactionById($orderTransaction->getId(), $salesChannelContext->getContext()); + } $fetchedCustomer = null; if (!empty($unzerCustomerId)) { try { $fetchedCustomer = $this->unzerClient->fetchCustomer($unzerCustomerId); - } catch (Throwable $t) { + } catch (\Throwable $t) { // silentfail } } if ($customer && !$fetchedCustomer) { - $customerNumber = $customer->getCustomerNumber(); - $billingAddress = $customer->getActiveBillingAddress(); - - if ($billingAddress !== null && !empty($billingAddress->getCompany())) { - $customerNumber .= '_b'; - } - + $orderBillingAddress = $orderTransaction->getOrder()->getBillingAddress(); + $customerNumber = $this->customerHydrator->getShopCustomerId($customer, $orderBillingAddress); try { $fetchedCustomer = $this->unzerClient->fetchCustomerByExtCustomerId($customerNumber); - } catch (Throwable $t) { + } catch (\Throwable $t) { // silentfail } } if ($fetchedCustomer) { /** @var Customer $updatedCustomer */ - $updatedCustomer = $this->customerHydrator->hydrateExistingCustomer($fetchedCustomer, $salesChannelContext); + $updatedCustomer = $this->customerHydrator->hydrateExistingCustomer($fetchedCustomer, $salesChannelContext, $orderTransaction); try { $updatedCustomer = $this->unzerClient->updateCustomer($updatedCustomer); - } catch (Throwable $t) { + } catch (\Throwable $t) { // silentfail } return $updatedCustomer; } - return $this->customerHydrator->hydrateObject($paymentMethodId, $salesChannelContext); + return $this->customerHydrator->hydrateObject($paymentMethodId, $salesChannelContext, $orderTransaction); } protected function getLoggableRequest(Request $request): array { $result = [ - 'request-info' => sprintf('%s %s %s', $request->getMethod(), $request->getRequestUri(), $request->getScheme()) . "\r\n", + 'request-info' => \sprintf('%s %s %s', $request->getMethod(), $request->getRequestUri(), $request->getScheme()) . "\r\n", 'header' => $request->headers->all(), 'content' => $request->getContent(false), ]; $cookies = []; foreach ($request->cookies->all() as $cookieKey => $cookieValue) { - if (is_array($cookieValue)) { + if (\is_array($cookieValue)) { $cookies[] = $cookieKey . '=' . json_encode($cookieValue); - } elseif (is_scalar($cookieValue)) { + } elseif (\is_scalar($cookieValue)) { $cookies[] = $cookieKey . '=' . $cookieValue; } } @@ -283,4 +321,26 @@ protected function getLoggableRequest(Request $request): array return $result; } + + protected function fetchTransactionById(string $transactionId, Context $context): ?OrderTransactionEntity + { + $transactionCriteria = new Criteria([$transactionId]); + $transactionCriteria->addAssociations([ + 'order', + 'order.billingAddress.country', + 'order.currency', + 'order.documents.documentType', + 'paymentMethod', + 'order.orderCustomer.customer', + 'order.deliveries.shippingMethod.translated', + 'order.deliveries.shippingOrderAddress.country', + 'order.lineItems.product.manufacturer', + 'order.lineItems.cover.url', + 'order.lineItems.calculatedPrices.taxes', + ]); + + $transactionSearchResult = $this->transactionRepository->search($transactionCriteria, $context); + + return $transactionSearchResult->first(); + } } diff --git a/src/Components/PaymentHandler/Exception/UnzerPaymentProcessException.php b/src/Components/PaymentHandler/Exception/UnzerPaymentProcessException.php index 3dcaf538..eec91660 100644 --- a/src/Components/PaymentHandler/Exception/UnzerPaymentProcessException.php +++ b/src/Components/PaymentHandler/Exception/UnzerPaymentProcessException.php @@ -10,15 +10,16 @@ class UnzerPaymentProcessException extends PaymentException { - /** @var string */ protected string $orderId; - /** @var UnzerApiException */ + /** + * @var UnzerApiException + */ protected $originalException; public function __construct(string $orderId, string $orderTransactionId, UnzerApiException $apiException) { - $this->orderId = $orderId; + $this->orderId = $orderId; $this->originalException = $apiException; parent::__construct( diff --git a/src/Components/PaymentHandler/Traits/CanAuthorize.php b/src/Components/PaymentHandler/Traits/CanAuthorize.php index 85c424bd..c7ff0c25 100644 --- a/src/Components/PaymentHandler/Traits/CanAuthorize.php +++ b/src/Components/PaymentHandler/Traits/CanAuthorize.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\PaymentHandler\Traits; -use RuntimeException; use UnzerSDK\Resources\EmbeddedResources\RiskData; use UnzerSDK\Resources\TransactionTypes\Authorization; @@ -14,18 +13,19 @@ public function authorize( string $returnUrl, ?float $amount = null, ?string $recurrenceType = null, - ?RiskData $riskData = null + ?RiskData $riskData = null, + ?callable $authorizationModifier = null ): string { if ($this->unzerClient === null) { - throw new RuntimeException('UnzerClient can not be null'); + throw new \RuntimeException('UnzerClient can not be null'); } if (!method_exists($this->unzerClient, 'performAuthorization')) { - throw new RuntimeException('The SDK Version is older then expected'); + throw new \RuntimeException('The SDK Version is older then expected'); } if ($this->paymentType === null) { - throw new RuntimeException('PaymentType can not be null'); + throw new \RuntimeException('PaymentType can not be null'); } $authorization = new Authorization( @@ -45,6 +45,10 @@ public function authorize( $authorization->setRiskData($riskData); } + if ($authorizationModifier !== null) { + $authorizationModifier($authorization); + } + $paymentResult = $this->unzerClient->performAuthorization( $authorization, $this->paymentType, diff --git a/src/Components/PaymentHandler/Traits/CanCharge.php b/src/Components/PaymentHandler/Traits/CanCharge.php index 59b03125..3cf158b0 100644 --- a/src/Components/PaymentHandler/Traits/CanCharge.php +++ b/src/Components/PaymentHandler/Traits/CanCharge.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\PaymentHandler\Traits; -use RuntimeException; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\EmbeddedResources\RiskData; use UnzerSDK\Resources\TransactionTypes\Charge; @@ -17,18 +16,19 @@ trait CanCharge public function charge( string $returnUrl, ?string $recurrenceType = null, - ?RiskData $riskData = null + ?RiskData $riskData = null, + ?callable $chargeModifier = null ): string { if ($this->unzerClient === null) { - throw new RuntimeException('UnzerClient can not be null'); + throw new \RuntimeException('UnzerClient can not be null'); } if (!method_exists($this->unzerClient, 'performAuthorization')) { - throw new RuntimeException('The SDK Version is older then expected'); + throw new \RuntimeException('The SDK Version is older then expected'); } if ($this->paymentType === null) { - throw new RuntimeException('PaymentType can not be null'); + throw new \RuntimeException('PaymentType can not be null'); } $charge = new Charge( @@ -48,6 +48,10 @@ public function charge( $charge->setRiskData($riskData); } + if ($chargeModifier !== null) { + $chargeModifier($charge); + } + $paymentResult = $this->unzerClient->performCharge( $charge, $this->paymentType, diff --git a/src/Components/PaymentHandler/Traits/CanRecur.php b/src/Components/PaymentHandler/Traits/CanRecur.php index 2eb87273..4bcf1bfa 100644 --- a/src/Components/PaymentHandler/Traits/CanRecur.php +++ b/src/Components/PaymentHandler/Traits/CanRecur.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\PaymentHandler\Traits; -use RuntimeException; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct; use Shopware\Core\Framework\Context; @@ -20,13 +19,19 @@ */ trait CanRecur { - /** @var string */ + /** + * @var string + */ protected $sessionIsRecurring = 'UnzerPaymentIsRecurring'; - /** @var string */ + /** + * @var string + */ protected $sessionPaymentTypeKey = 'UnzerPaymentTypeId'; - /** @var string */ + /** + * @var string + */ protected $sessionCustomerIdKey = 'UnzerPaymentCustomerId'; /** @@ -35,11 +40,11 @@ trait CanRecur public function activateRecurring(string $returnUrl, ?string $recurrenceType = null): string { if ($this->paymentType === null) { - throw new RuntimeException('PaymentType can not be null'); + throw new \RuntimeException('PaymentType can not be null'); } if (!method_exists($this->paymentType, 'activateRecurring')) { - throw new RuntimeException('This payment type does not support recurring'); + throw new \RuntimeException('This payment type does not support recurring'); } $this->recurring = $this->paymentType->activateRecurring($returnUrl, $recurrenceType); @@ -69,21 +74,8 @@ protected function recur( ): void { $orderTransaction = $this->fetchTransactionById($transaction->getOrderTransaction()->getId(), $salesChannelContext->getContext()); - $this->unzerBasket = $this->basketHydrator->hydrateObject($salesChannelContext, $orderTransaction ?? $transaction); + $this->unzerBasket = $this->basketHydrator->hydrateObject($salesChannelContext, $orderTransaction ?? $transaction); $this->unzerMetadata = $this->metadataHydrator->hydrateObject($salesChannelContext, $orderTransaction ?? $transaction); - $this->unzerCustomer = $this->getUnzerCustomer($transaction->getOrderTransaction()->getCustomFields()[$this->sessionCustomerIdKey] ?? '', $transaction->getOrderTransaction()->getPaymentMethodId(), $salesChannelContext); - } - - protected function fetchTransactionById(string $transactionId, Context $context): ?OrderTransactionEntity - { - $transactionCriteria = new Criteria([$transactionId]); - $transactionCriteria->addAssociation('order'); - $transactionCriteria->addAssociation('order.currency'); - $transactionCriteria->addAssociation('order.lineItems'); - $transactionCriteria->addAssociation('order.deliveries'); - - $transactionSearchResult = $this->transactionRepository->search($transactionCriteria, $context); - - return $transactionSearchResult->first(); + $this->unzerCustomer = $this->getUnzerCustomer($transaction->getOrderTransaction()->getCustomFields()[$this->sessionCustomerIdKey] ?? '', $transaction->getOrderTransaction()->getPaymentMethodId(), $transaction->getOrderTransaction(), $salesChannelContext); } } diff --git a/src/Components/PaymentHandler/Traits/HasDeviceVault.php b/src/Components/PaymentHandler/Traits/HasDeviceVault.php index 3e9a785e..39bf30e7 100644 --- a/src/Components/PaymentHandler/Traits/HasDeviceVault.php +++ b/src/Components/PaymentHandler/Traits/HasDeviceVault.php @@ -6,7 +6,6 @@ use Shopware\Core\Checkout\Customer\CustomerEntity; use Shopware\Core\Framework\Context; -use stdClass; use UnzerPayment6\DataAbstractionLayer\Repository\PaymentDevice\UnzerPaymentDeviceRepositoryInterface; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; @@ -15,7 +14,9 @@ */ trait HasDeviceVault { - /** @var UnzerPaymentDeviceRepositoryInterface */ + /** + * @var UnzerPaymentDeviceRepositoryInterface + */ protected $deviceRepository; protected function saveToDeviceVault(CustomerEntity $customer, string $deviceType, Context $context, array $additionalParams = []): void @@ -26,7 +27,7 @@ protected function saveToDeviceVault(CustomerEntity $customer, string $deviceTyp $exposedPaymentType = $this->paymentType->expose(); - if ($exposedPaymentType instanceof stdClass) { + if ($exposedPaymentType instanceof \stdClass) { $encoded = json_encode($exposedPaymentType); if (!$encoded) { @@ -34,7 +35,7 @@ protected function saveToDeviceVault(CustomerEntity $customer, string $deviceTyp } else { $exposedPaymentType = json_decode($encoded, true); - if (!is_array($exposedPaymentType) || empty($exposedPaymentType)) { + if (!\is_array($exposedPaymentType) || empty($exposedPaymentType)) { $exposedPaymentType = []; } } diff --git a/src/Components/PaymentHandler/Traits/HasRiskDataTrait.php b/src/Components/PaymentHandler/Traits/HasRiskDataTrait.php index 54d85d25..8fc9c30b 100644 --- a/src/Components/PaymentHandler/Traits/HasRiskDataTrait.php +++ b/src/Components/PaymentHandler/Traits/HasRiskDataTrait.php @@ -15,7 +15,7 @@ private function generateRiskDataResource(AsyncPaymentTransactionStruct $transac { $fraudPreventionSessionId = $this->fetchFraudPreventionSessionId($transaction, $context); - if (null === $fraudPreventionSessionId) { + if ($fraudPreventionSessionId === null) { return null; } @@ -24,8 +24,8 @@ private function generateRiskDataResource(AsyncPaymentTransactionStruct $transac $customer = $context->getCustomer(); - if (null !== $customer) { - $date = $customer->getCreatedAt() ? $customer->getCreatedAt()->format('Ymd') : null; + if ($customer !== null) { + $date = $customer->getCreatedAt()?->format('Ymd'); $riskData->setRegistrationLevel($customer->getGuest() ? '0' : '1'); $riskData->setRegistrationDate($date); @@ -36,9 +36,9 @@ private function generateRiskDataResource(AsyncPaymentTransactionStruct $transac private function fetchFraudPreventionSessionId(AsyncPaymentTransactionStruct $transaction, SalesChannelContext $context): ?string { - $orderTransaction = $transaction->getOrderTransaction(); - $currentRequest = $this->getCurrentRequestFromStack($orderTransaction->getId()); - $fraudPreventionSessionId = $currentRequest->get('unzerPaymentFraudPreventionSessionId', ''); + $orderTransaction = $transaction->getOrderTransaction(); + $currentRequest = $this->getCurrentRequestFromStack($orderTransaction->getId()); + $fraudPreventionSessionId = $currentRequest->get('unzerThreatMetrixId', ''); if (empty($fraudPreventionSessionId)) { $customFields = $orderTransaction->getCustomFields() ?? []; @@ -54,7 +54,7 @@ private function fetchFraudPreventionSessionId(AsyncPaymentTransactionStruct $tr $this->transactionRepository->upsert([ [ - 'id' => $orderTransaction->getId(), + 'id' => $orderTransaction->getId(), 'customFields' => [ CustomFieldInstaller::UNZER_PAYMENT_FRAUD_PREVENTION_SESSION_ID => $fraudPreventionSessionId, ], diff --git a/src/Components/PaymentHandler/Traits/HasTransferInfoTrait.php b/src/Components/PaymentHandler/Traits/HasTransferInfoTrait.php index 14c16062..062c8d8b 100644 --- a/src/Components/PaymentHandler/Traits/HasTransferInfoTrait.php +++ b/src/Components/PaymentHandler/Traits/HasTransferInfoTrait.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\PaymentHandler\Traits; -use RuntimeException; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; @@ -22,23 +21,23 @@ trait HasTransferInfoTrait private function saveTransferInfo(OrderTransactionEntity $orderTransactionEntity, Context $context): EntityWrittenContainerEvent { if (!isset($this->transactionRepository)) { - throw new RuntimeException('TransactionRepository can not be null'); + throw new \RuntimeException('TransactionRepository can not be null'); } if (!isset($this->payment)) { - throw new RuntimeException('Payment can not be null'); + throw new \RuntimeException('Payment can not be null'); } - /** @var null|Charge $charge */ + /** @var Charge|null $charge */ $charge = $this->payment->getChargeByIndex(0); if (!isset($charge)) { - throw new RuntimeException('Payment has not been charged'); + throw new \RuntimeException('Payment has not been charged'); } return $this->transactionRepository->upsert([ [ - 'id' => $orderTransactionEntity->getId(), + 'id' => $orderTransactionEntity->getId(), 'customFields' => array_merge( $orderTransactionEntity->getCustomFields() ?? [], [ @@ -52,23 +51,23 @@ private function saveTransferInfo(OrderTransactionEntity $orderTransactionEntity private function saveTransferInfoFromAuthorize(OrderTransactionEntity $orderTransactionEntity, Context $context): EntityWrittenContainerEvent { if (!isset($this->transactionRepository)) { - throw new RuntimeException('TransactionRepository can not be null'); + throw new \RuntimeException('TransactionRepository can not be null'); } if (!isset($this->payment)) { - throw new RuntimeException('Payment can not be null'); + throw new \RuntimeException('Payment can not be null'); } - /** @var null|Authorization $authorization */ + /** @var Authorization|null $authorization */ $authorization = $this->payment->getAuthorization(); if (!isset($authorization)) { - throw new RuntimeException('Payment has not been authorized'); + throw new \RuntimeException('Payment has not been authorized'); } return $this->transactionRepository->upsert([ [ - 'id' => $orderTransactionEntity->getId(), + 'id' => $orderTransactionEntity->getId(), 'customFields' => array_merge( $orderTransactionEntity->getCustomFields() ?? [], [ diff --git a/src/Components/PaymentHandler/Traits/IsBasicPaymentMethod.php b/src/Components/PaymentHandler/Traits/IsBasicPaymentMethod.php index e26ee27b..7e8f1f3a 100644 --- a/src/Components/PaymentHandler/Traits/IsBasicPaymentMethod.php +++ b/src/Components/PaymentHandler/Traits/IsBasicPaymentMethod.php @@ -9,7 +9,6 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; @@ -17,14 +16,12 @@ trait IsBasicPaymentMethod { use CanCharge; - abstract protected function getUnzerPaymentTypeObject(): ?BasePaymentType; public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); try { @@ -36,7 +33,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -50,9 +47,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -62,4 +59,6 @@ public function pay( throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } + + abstract protected function getUnzerPaymentTypeObject(): ?BasePaymentType; } diff --git a/src/Components/PaymentHandler/UnzerInvoicePaymentHandler.php b/src/Components/PaymentHandler/Traits/IsBasicPaymentMethodWithBookingMode.php similarity index 58% rename from src/Components/PaymentHandler/UnzerInvoicePaymentHandler.php rename to src/Components/PaymentHandler/Traits/IsBasicPaymentMethodWithBookingMode.php index 03ccc708..d4620634 100644 --- a/src/Components/PaymentHandler/UnzerInvoicePaymentHandler.php +++ b/src/Components/PaymentHandler/Traits/IsBasicPaymentMethodWithBookingMode.php @@ -2,45 +2,45 @@ declare(strict_types=1); -namespace UnzerPayment6\Components\PaymentHandler; +namespace UnzerPayment6\Components\PaymentHandler\Traits; use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; +use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; -use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; -use UnzerPayment6\Components\PaymentHandler\Traits\HasTransferInfoTrait; use UnzerSDK\Exceptions\UnzerApiException; +use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -class UnzerInvoicePaymentHandler extends AbstractUnzerPaymentHandler +trait IsBasicPaymentMethodWithBookingMode { - use HasTransferInfoTrait; + use CanAuthorize; use CanCharge; - /** - * {@inheritdoc} - */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); try { - $returnUrl = $this->charge($transaction->getReturnUrl()); - - $orderTransaction = $transaction->getOrderTransaction(); - $this->saveTransferInfo($orderTransaction, $salesChannelContext->getContext()); + if (empty($this->paymentType) && ($unzerPaymentType = $this->getUnzerPaymentTypeObject()) !== null) { + $this->paymentType = $this->unzerClient->createPaymentType($unzerPaymentType); + } + $this->setBookingMode(); + if ($this->bookingMode === BookingMode::CHARGE) { + $returnUrl = $this->charge($transaction->getReturnUrl()); + } else { + $returnUrl = $this->authorize($transaction->getReturnUrl()); + } return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -54,17 +54,20 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, 'exception' => $exception, ] ); - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } + + abstract protected function getUnzerPaymentTypeObject(): ?BasePaymentType; + + abstract protected function setBookingMode(): void; } diff --git a/src/Components/PaymentHandler/UnzerApplePayV2PaymentHandler.php b/src/Components/PaymentHandler/UnzerApplePayV2PaymentHandler.php index 02d3e48a..69d03c25 100755 --- a/src/Components/PaymentHandler/UnzerApplePayV2PaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerApplePayV2PaymentHandler.php @@ -9,23 +9,23 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ConfigReader\ConfigReader; +use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; -use UnzerSDK\Constants\RecurrenceTypes; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -use UnzerSDK\Resources\PaymentTypes\Card; class UnzerApplePayV2PaymentHandler extends AbstractUnzerPaymentHandler { - use CanCharge; use CanAuthorize; + use CanCharge; - /** @var BasePaymentType */ + /** + * @var BasePaymentType + */ protected $paymentType; /** @@ -33,10 +33,15 @@ class UnzerApplePayV2PaymentHandler extends AbstractUnzerPaymentHandler */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { + $currentRequest = $this->requestStack->getCurrentRequest(); + if (!empty($currentRequest->getSession()->get(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID))) { + $client = $this->clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $currentRequest); + $this->paymentType = $client->fetchPaymentType($currentRequest->getSession()->get(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID)); + $this->isExpress = true; + } parent::pay($transaction, $dataBag, $salesChannelContext); if ($this->paymentType === null) { @@ -53,7 +58,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -67,9 +72,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, diff --git a/src/Components/PaymentHandler/UnzerCreditCardPaymentHandler.php b/src/Components/PaymentHandler/UnzerCreditCardPaymentHandler.php index efaa9b50..af640b31 100755 --- a/src/Components/PaymentHandler/UnzerCreditCardPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerCreditCardPaymentHandler.php @@ -13,7 +13,6 @@ use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RequestStack; -use Throwable; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\ConfigReader\ConfigReader; @@ -35,29 +34,28 @@ class UnzerCreditCardPaymentHandler extends AbstractUnzerPaymentHandler { - use CanCharge; use CanAuthorize; + use CanCharge; use HasDeviceVault; - public const REMEMBER_CREDIT_CARD_KEY = 'creditCardRemember'; - - /** @var BasePaymentType|Card */ + /** + * @var BasePaymentType|Card + */ protected $paymentType; public function __construct( - ResourceHydratorInterface $basketHydrator, - CustomerResourceHydratorInterface $customerHydrator, - ResourceHydratorInterface $metadataHydrator, - EntityRepository $transactionRepository, - ConfigReaderInterface $configReader, - TransactionStateHandlerInterface $transactionStateHandler, - ClientFactoryInterface $clientFactory, - RequestStack $requestStack, - LoggerInterface $logger, - CustomFieldsHelperInterface $customFieldsHelper, + ResourceHydratorInterface $basketHydrator, + CustomerResourceHydratorInterface $customerHydrator, + ResourceHydratorInterface $metadataHydrator, + EntityRepository $transactionRepository, + ConfigReaderInterface $configReader, + TransactionStateHandlerInterface $transactionStateHandler, + ClientFactoryInterface $clientFactory, + RequestStack $requestStack, + LoggerInterface $logger, + CustomFieldsHelperInterface $customFieldsHelper, UnzerPaymentDeviceRepositoryInterface $deviceRepository - ) - { + ) { parent::__construct( $basketHydrator, $customerHydrator, @@ -79,10 +77,9 @@ public function __construct( */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); if ($this->paymentType === null) { @@ -91,8 +88,8 @@ public function pay( $customer = $salesChannelContext->getCustomer(); $bookingMode = $this->pluginConfig->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_CARD, BookingMode::CHARGE); - $registerCreditCards = $dataBag->has(self::REMEMBER_CREDIT_CARD_KEY); - $saveToDeviceVault = $this->canSaveToDeviceVault($registerCreditCards, $customer); + $savePaymentDevice = $dataBag->has(self::SAVE_PAYMENT_DEVICE_KEY); + $saveToDeviceVault = $this->canSaveToDeviceVault($savePaymentDevice, $customer); try { $recurrenceType = ($this->deviceRepository->exists($this->paymentType->getId(), $salesChannelContext->getContext()) || $saveToDeviceVault) @@ -114,7 +111,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -128,9 +125,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, diff --git a/src/Components/PaymentHandler/UnzerDirectDebitPaymentHandler.php b/src/Components/PaymentHandler/UnzerDirectDebitPaymentHandler.php index fec772dd..578236f7 100644 --- a/src/Components/PaymentHandler/UnzerDirectDebitPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerDirectDebitPaymentHandler.php @@ -12,7 +12,6 @@ use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\RequestStack; -use Throwable; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\CustomFieldsHelper\CustomFieldsHelperInterface; @@ -33,25 +32,24 @@ class UnzerDirectDebitPaymentHandler extends AbstractUnzerPaymentHandler use CanCharge; use HasDeviceVault; - public const REMEMBER_SEPA_MANDATE_KEY = 'rememberSepaMandate'; - - /** @var BasePaymentType|SepaDirectDebit */ + /** + * @var BasePaymentType|SepaDirectDebit + */ protected $paymentType; public function __construct( - ResourceHydratorInterface $basketHydrator, - CustomerResourceHydratorInterface $customerHydrator, - ResourceHydratorInterface $metadataHydrator, - EntityRepository $transactionRepository, - ConfigReaderInterface $configReader, - TransactionStateHandlerInterface $transactionStateHandler, - ClientFactoryInterface $clientFactory, - RequestStack $requestStack, - LoggerInterface $logger, - CustomFieldsHelperInterface $customFieldsHelper, + ResourceHydratorInterface $basketHydrator, + CustomerResourceHydratorInterface $customerHydrator, + ResourceHydratorInterface $metadataHydrator, + EntityRepository $transactionRepository, + ConfigReaderInterface $configReader, + TransactionStateHandlerInterface $transactionStateHandler, + ClientFactoryInterface $clientFactory, + RequestStack $requestStack, + LoggerInterface $logger, + CustomFieldsHelperInterface $customFieldsHelper, UnzerPaymentDeviceRepositoryInterface $deviceRepository - ) - { + ) { parent::__construct( $basketHydrator, $customerHydrator, @@ -73,22 +71,17 @@ public function __construct( */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); - if (!$this->isPaymentAllowed($transaction->getOrderTransaction()->getId())) { - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), 'SEPA direct debit mandate has not been accepted by the customer.'); - } - - $registerDirectDebit = $dataBag->has(self::REMEMBER_SEPA_MANDATE_KEY); + $savePaymentDevice = $dataBag->has(self::SAVE_PAYMENT_DEVICE_KEY); try { $returnUrl = $this->charge($transaction->getReturnUrl()); - if ($registerDirectDebit && $salesChannelContext->getCustomer() !== null && $salesChannelContext->getCustomer()->getGuest() === false) { + if ($savePaymentDevice && $salesChannelContext->getCustomer() !== null && $salesChannelContext->getCustomer()->getGuest() === false) { $this->saveToDeviceVault( $salesChannelContext->getCustomer(), UnzerPaymentDeviceEntity::DEVICE_TYPE_DIRECT_DEBIT, @@ -99,7 +92,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -113,9 +106,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -126,14 +119,4 @@ public function pay( throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } - - private function isPaymentAllowed(string $transactionId): bool - { - $currentRequest = $this->getCurrentRequestFromStack($transactionId); - - $isSepaAccepted = ((string)$currentRequest->get('acceptSepaMandate', 'off')) === 'on'; - $isNewAccount = ((string)$currentRequest->get('savedDirectDebitDevice', 'new')) === 'new'; - - return ($isSepaAccepted && $isNewAccount) || !$isNewAccount; - } } diff --git a/src/Components/PaymentHandler/UnzerDirectDebitSecuredPaymentHandler.php b/src/Components/PaymentHandler/UnzerDirectDebitSecuredPaymentHandler.php deleted file mode 100644 index 0162d380..00000000 --- a/src/Components/PaymentHandler/UnzerDirectDebitSecuredPaymentHandler.php +++ /dev/null @@ -1,154 +0,0 @@ -deviceRepository = $deviceRepository; - } - - /** - * {@inheritdoc} - */ - public function pay( - AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { - parent::pay($transaction, $dataBag, $salesChannelContext); - $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); - - if (!$this->isPaymentAllowed($transaction->getOrderTransaction()->getId())) { - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), 'SEPA direct debit mandate has not been accepted by the customer.'); - } - - $registerDirectDebit = $dataBag->has(UnzerDirectDebitPaymentHandler::REMEMBER_SEPA_MANDATE_KEY); - $birthday = $currentRequest->get('unzerPaymentBirthday', ''); - - try { - if (!empty($birthday) - && (empty($this->unzerCustomer->getBirthDate()) || $birthday !== $this->unzerCustomer->getBirthDate())) { - $this->unzerCustomer->setBirthDate($birthday); - } else { - $paymentDevice = $this->deviceRepository->getByPaymentTypeId($this->paymentType->getId(), $salesChannelContext->getContext()); - - if ($paymentDevice && array_key_exists('birthDate', $paymentDevice->getData())) { - $birthDate = $paymentDevice->getData()['birthDate']; - - if (!empty($birthDate)) { - $this->unzerCustomer->setBirthDate($birthDate); - } - } - } - - $this->unzerCustomer = $this->unzerClient->createOrUpdateCustomer($this->unzerCustomer); - - $returnUrl = $this->charge($transaction->getReturnUrl()); - - if ($registerDirectDebit && $salesChannelContext->getCustomer() !== null && $salesChannelContext->getCustomer()->getGuest() === false) { - $this->saveToDeviceVault( - $salesChannelContext->getCustomer(), - UnzerPaymentDeviceEntity::DEVICE_TYPE_DIRECT_DEBIT_SECURED, - $salesChannelContext->getContext(), - [ - 'birthDate' => $this->unzerCustomer->getBirthDate(), - ] - ); - } - - return new RedirectResponse($returnUrl); - } catch (UnzerApiException $apiException) { - $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), - [ - 'request' => $this->getLoggableRequest($currentRequest), - 'transaction' => $transaction, - 'exception' => $apiException, - ] - ); - - $this->executeFailTransition( - $transaction->getOrderTransaction()->getId(), - $salesChannelContext->getContext() - ); - - throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { - $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), - [ - 'request' => $this->getLoggableRequest($currentRequest), - 'transaction' => $transaction, - 'exception' => $exception, - ] - ); - - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); - } - } - - private function isPaymentAllowed(string $transactionId): bool - { - $currentRequest = $this->getCurrentRequestFromStack($transactionId); - - $isSepaAccepted = ((string)$currentRequest->get('acceptSepaMandate', 'off')) === 'on'; - $isNewAccount = ((string)$currentRequest->get('savedDirectDebitDevice', 'new')) === 'new'; - - return ($isSepaAccepted && $isNewAccount) || !$isNewAccount; - } -} diff --git a/src/Components/PaymentHandler/UnzerEpsPaymentHandler.php b/src/Components/PaymentHandler/UnzerEpsPaymentHandler.php index 00879ee4..c843b3ee 100644 --- a/src/Components/PaymentHandler/UnzerEpsPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerEpsPaymentHandler.php @@ -11,8 +11,8 @@ class UnzerEpsPaymentHandler extends AbstractUnzerPaymentHandler { use IsBasicPaymentMethod; - protected function getUnzerPaymentTypeObject(): Eps + protected function getUnzerPaymentTypeObject(): EPS { - return new Eps(); + return new EPS(); } } diff --git a/src/Components/PaymentHandler/UnzerGiropayPaymentHandler.php b/src/Components/PaymentHandler/UnzerGiropayPaymentHandler.php deleted file mode 100644 index 026c7450..00000000 --- a/src/Components/PaymentHandler/UnzerGiropayPaymentHandler.php +++ /dev/null @@ -1,68 +0,0 @@ -paymentType = $this->unzerClient->createPaymentType(new Giropay()); - - $returnUrl = $this->charge($transaction->getReturnUrl()); - - return new RedirectResponse($returnUrl); - } catch (UnzerApiException $apiException) { - $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), - [ - 'dataBag' => $dataBag, - 'transaction' => $transaction, - 'exception' => $apiException, - ] - ); - - $this->executeFailTransition( - $transaction->getOrderTransaction()->getId(), - $salesChannelContext->getContext() - ); - - throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { - $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), - [ - 'dataBag' => $dataBag, - 'transaction' => $transaction, - 'exception' => $exception, - ] - ); - - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); - } - } -} diff --git a/src/Components/PaymentHandler/UnzerGooglePayPaymentHandler.php b/src/Components/PaymentHandler/UnzerGooglePayPaymentHandler.php index 09ee7a00..6db299d1 100755 --- a/src/Components/PaymentHandler/UnzerGooglePayPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerGooglePayPaymentHandler.php @@ -4,29 +4,29 @@ namespace UnzerPayment6\Components\PaymentHandler; -use Exception; use Shopware\Core\Checkout\Payment\Cart\AsyncPaymentTransactionStruct; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ConfigReader\ConfigReader; +use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -use UnzerSDK\Resources\PaymentTypes\Card; use UnzerSDK\Unzer; class UnzerGooglePayPaymentHandler extends AbstractUnzerPaymentHandler { - use CanCharge; use CanAuthorize; + use CanCharge; - /** @var BasePaymentType|Card */ + /** + * @var BasePaymentType + */ protected $paymentType; /** @@ -34,10 +34,15 @@ class UnzerGooglePayPaymentHandler extends AbstractUnzerPaymentHandler */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { + $currentRequest = $this->requestStack->getCurrentRequest(); + if (!empty($currentRequest->getSession()->get(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID))) { + $client = $this->clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $currentRequest); + $this->paymentType = $client->fetchPaymentType($currentRequest->getSession()->get(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID)); + $this->isExpress = true; + } parent::pay($transaction, $dataBag, $salesChannelContext); if ($this->paymentType === null) { @@ -54,7 +59,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -68,9 +73,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -94,9 +99,10 @@ public static function fetchChannelId(Unzer $client): string } } } - } catch (Exception $e) { - //silent to return '' at the end + } catch (\Exception $e) { + // silent to return '' at the end } + // will only be reached, if no channel id was found return ''; } diff --git a/src/Components/PaymentHandler/UnzerInstallmentSecuredPaymentHandler.php b/src/Components/PaymentHandler/UnzerInstallmentSecuredPaymentHandler.php deleted file mode 100644 index 132ef4d0..00000000 --- a/src/Components/PaymentHandler/UnzerInstallmentSecuredPaymentHandler.php +++ /dev/null @@ -1,90 +0,0 @@ -unzerBasket->setTotalValueGross($this->unzerBasket->getTotalValueGross()); - - $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); - - $birthday = $currentRequest->get('unzerPaymentBirthday', ''); - - try { - if (!empty($birthday) - && (empty($this->unzerCustomer->getBirthDate()) || $birthday !== $this->unzerCustomer->getBirthDate())) { - $this->unzerCustomer->setBirthDate($birthday); - $this->unzerClient->createOrUpdateCustomer($this->unzerCustomer); - } - - /** @var int $currencyPrecision */ - $currencyPrecision = $transaction->getOrder()->getCurrency() !== null ? min( - $transaction->getOrder()->getCurrency()->getItemRounding()->getDecimals(), - UnzerPayment6::MAX_DECIMAL_PRECISION - ) : UnzerPayment6::MAX_DECIMAL_PRECISION; - - $returnUrl = $this->authorize( - $transaction->getReturnUrl(), - round($transaction->getOrder()->getAmountTotal(), $currencyPrecision) - ); - - /** @phpstan-ignore-next-line */ - $this->payment->charge(round($transaction->getOrder()->getAmountTotal(), $currencyPrecision)); - - return new RedirectResponse($returnUrl); - } catch (UnzerApiException $apiException) { - $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), - [ - 'request' => $this->getLoggableRequest($currentRequest), - 'transaction' => $transaction, - 'exception' => $apiException, - ] - ); - - $this->executeFailTransition( - $transaction->getOrderTransaction()->getId(), - $salesChannelContext->getContext() - ); - - throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { - $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), - [ - 'request' => $this->getLoggableRequest($currentRequest), - 'transaction' => $transaction, - 'exception' => $exception, - ] - ); - - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); - } - } -} diff --git a/src/Components/PaymentHandler/UnzerInvoiceSecuredPaymentHandler.php b/src/Components/PaymentHandler/UnzerInvoiceSecuredPaymentHandler.php deleted file mode 100644 index 18afb8d5..00000000 --- a/src/Components/PaymentHandler/UnzerInvoiceSecuredPaymentHandler.php +++ /dev/null @@ -1,77 +0,0 @@ -getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); - $birthday = $currentRequest->get('unzerPaymentBirthday', ''); - - try { - if (!empty($birthday) - && (empty($this->unzerCustomer->getBirthDate()) || $birthday !== $this->unzerCustomer->getBirthDate())) { - $this->unzerCustomer->setBirthDate($birthday); - $this->unzerCustomer = $this->unzerClient->createOrUpdateCustomer($this->unzerCustomer); - } - - $returnUrl = $this->charge($transaction->getReturnUrl()); - $orderTransaction = $transaction->getOrderTransaction(); - $this->saveTransferInfo($orderTransaction, $salesChannelContext->getContext()); - - return new RedirectResponse($returnUrl); - } catch (UnzerApiException $apiException) { - $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), - [ - 'request' => $this->getLoggableRequest($currentRequest), - 'transaction' => $transaction, - 'exception' => $apiException, - ] - ); - - $this->executeFailTransition( - $transaction->getOrderTransaction()->getId(), - $salesChannelContext->getContext() - ); - - throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { - $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), - [ - 'request' => $this->getLoggableRequest($currentRequest), - 'transaction' => $transaction, - 'exception' => $exception, - ] - ); - - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); - } - } -} diff --git a/src/Components/PaymentHandler/UnzerApplePayPaymentHandler.php b/src/Components/PaymentHandler/UnzerKlarnaPaymentHandler.php old mode 100755 new mode 100644 similarity index 61% rename from src/Components/PaymentHandler/UnzerApplePayPaymentHandler.php rename to src/Components/PaymentHandler/UnzerKlarnaPaymentHandler.php index b45ac0f2..63e08713 --- a/src/Components/PaymentHandler/UnzerApplePayPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerKlarnaPaymentHandler.php @@ -9,23 +9,23 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; -use UnzerPayment6\Components\BookingMode; -use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; -use UnzerSDK\Constants\RecurrenceTypes; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -use UnzerSDK\Resources\PaymentTypes\Card; +use UnzerSDK\Resources\PaymentTypes\Klarna; +use UnzerSDK\Resources\TransactionTypes\Authorization; +use UnzerSDK\Resources\TransactionTypes\Charge; -class UnzerApplePayPaymentHandler extends AbstractUnzerPaymentHandler +class UnzerKlarnaPaymentHandler extends AbstractUnzerPaymentHandler { - use CanCharge; use CanAuthorize; + use CanCharge; - /** @var BasePaymentType|Card */ + /** + * @var BasePaymentType + */ protected $paymentType; /** @@ -33,50 +33,50 @@ class UnzerApplePayPaymentHandler extends AbstractUnzerPaymentHandler */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { + $this->paymentType = new Klarna(); parent::pay($transaction, $dataBag, $salesChannelContext); - if ($this->paymentType === null) { - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), 'Can not process payment without a valid payment resource.'); - } - - $bookingMode = $this->pluginConfig->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_APPLE_PAY, BookingMode::CHARGE); - try { - $returnUrl = $bookingMode === BookingMode::CHARGE - ? $this->charge($transaction->getReturnUrl()) - : $this->authorize($transaction->getReturnUrl(), $this->unzerBasket->getTotalValueGross()); + $transactionModifier = function (Authorization|Charge $authorization): void { + $authorization->setTermsAndConditionUrl('https://unzer.com'); + $authorization->setPrivacyPolicyUrl('https://unzer.com'); + }; + + $returnUrl = $this->authorize( + $transaction->getReturnUrl(), + $this->unzerBasket->getTotalValueGross(), + null, + null, + $transactionModifier + ); return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, 'exception' => $apiException, ] ); - $this->executeFailTransition( $transaction->getOrderTransaction()->getId(), $salesChannelContext->getContext() ); - throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, 'exception' => $exception, ] ); - throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } diff --git a/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php b/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php index 47223011..95000903 100644 --- a/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerPayPalPaymentHandler.php @@ -13,12 +13,12 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; -use Throwable; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\CustomFieldsHelper\CustomFieldsHelperInterface; +use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; @@ -32,41 +32,37 @@ use UnzerPayment6\DataAbstractionLayer\Repository\PaymentDevice\UnzerPaymentDeviceRepositoryInterface; use UnzerPayment6\Installer\CustomFieldInstaller; use UnzerSDK\Exceptions\UnzerApiException; -use UnzerSDK\Resources\Payment; +use UnzerSDK\Resources\Basket; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; use UnzerSDK\Resources\PaymentTypes\Paypal; -use function array_key_exists; +use UnzerSDK\Resources\TransactionTypes\Authorization; +use UnzerSDK\Resources\TransactionTypes\Charge; - -/** - * @property Payment $payment - */ class UnzerPayPalPaymentHandler extends AbstractUnzerPaymentHandler { - use CanCharge; use CanAuthorize; + use CanCharge; use CanRecur; use HasDeviceVault; - public const REMEMBER_PAYPAL_ACCOUNT_KEY = 'payPalRemember'; - - /** @var BasePaymentType|Paypal */ + /** + * @var BasePaymentType|Paypal + */ protected $paymentType; public function __construct( - ResourceHydratorInterface $basketHydrator, - CustomerResourceHydratorInterface $customerHydrator, - ResourceHydratorInterface $metadataHydrator, - EntityRepository $transactionRepository, - ConfigReaderInterface $configReader, - TransactionStateHandlerInterface $transactionStateHandler, - ClientFactoryInterface $clientFactory, - RequestStack $requestStack, - LoggerInterface $logger, - CustomFieldsHelperInterface $customFieldsHelper, + ResourceHydratorInterface $basketHydrator, + CustomerResourceHydratorInterface $customerHydrator, + ResourceHydratorInterface $metadataHydrator, + EntityRepository $transactionRepository, + ConfigReaderInterface $configReader, + TransactionStateHandlerInterface $transactionStateHandler, + ClientFactoryInterface $clientFactory, + RequestStack $requestStack, + LoggerInterface $logger, + CustomFieldsHelperInterface $customFieldsHelper, UnzerPaymentDeviceRepositoryInterface $deviceRepository - ) - { + ) { parent::__construct( $basketHydrator, $customerHydrator, @@ -88,57 +84,73 @@ public function __construct( */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { - parent::pay($transaction, $dataBag, $salesChannelContext); + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); + if ($currentRequest->getSession()->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID)) { + try { + return $this->payExpress( + $currentRequest->getSession()->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID), + $transaction, + $dataBag, + $currentRequest, + $salesChannelContext + ); + } catch (\Throwable $e) { + $currentRequest->getSession()->remove(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID); + // continue with default paypal flow + } + } + parent::pay($transaction, $dataBag, $salesChannelContext); if (!empty($this->paymentType)) { - return $this->handleRecurringPayment($transaction, $salesChannelContext); + // this is from a saved payment device + try { + return $this->handleRecurringPayment($transaction, $salesChannelContext); + } catch (UnzerPaymentProcessException $e) { + // something went wrong with the API > fall back to the default flow + $this->paymentType = null; + } } $bookingMode = $this->pluginConfig->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_PAYPAL, BookingMode::CHARGE); try { - if ($this->paymentType === null) { - $registerAccounts = $dataBag->has(self::REMEMBER_PAYPAL_ACCOUNT_KEY); - $payPalPaymentType = new Paypal(); - - if (!empty($this->unzerCustomer->getEmail())) { - $payPalPaymentType->setEmail($this->unzerCustomer->getEmail()); - } - - $this->paymentType = $this->unzerClient->createPaymentType($payPalPaymentType); - - if ($registerAccounts && $salesChannelContext->getCustomer() !== null && $salesChannelContext->getCustomer()->getGuest() === false) { - $returnUrl = $this->activateRecurring($transaction->getReturnUrl()); - - if ($this->recurring !== null && !empty($this->recurring->getRedirectUrl())) { - $this->persistPaymentInformation( - [ - CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY => $this->paymentType->getId(), - $this->sessionPaymentTypeKey => $this->paymentType->getId(), - $this->sessionCustomerIdKey => $this->unzerCustomer->getId(), - self::REMEMBER_PAYPAL_ACCOUNT_KEY => true, - ], - $transaction->getOrderTransaction()->getId(), - $salesChannelContext->getContext() - ); - } + $savePaymentDevice = $dataBag->has(self::SAVE_PAYMENT_DEVICE_KEY) && $salesChannelContext->getCustomer() !== null && $salesChannelContext->getCustomer()->getGuest() === false; + $payPalPaymentType = new Paypal(); + if (!empty($this->unzerCustomer->getEmail())) { + $payPalPaymentType->setEmail($this->unzerCustomer->getEmail()); + } + $this->paymentType = $this->unzerClient->createPaymentType($payPalPaymentType); + + if ($savePaymentDevice) { + $returnUrl = $this->activateRecurring($transaction->getReturnUrl()); + if ($this->recurring !== null && !empty($this->recurring->getRedirectUrl())) { + $this->persistPaymentInformation( + [ + CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY => '', + $this->sessionPaymentTypeKey => $this->paymentType->getId(), + $this->sessionCustomerIdKey => $this->unzerCustomer->getId(), + self::SAVE_PAYMENT_DEVICE_KEY => true, + ], + $transaction->getOrderTransaction()->getId(), + $salesChannelContext->getContext() + ); return new RedirectResponse($returnUrl); } } + // at this point we are in the default process (no payment from express, saved device or with the command to save the device) + $returnUrl = $bookingMode === BookingMode::CHARGE ? $this->charge($transaction->getReturnUrl()) : $this->authorize($transaction->getReturnUrl()); $this->persistPaymentInformation( [ - $this->sessionIsRecurring => true, + $this->sessionIsRecurring => false, $this->sessionPaymentTypeKey => $this->payment->getId(), CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY => $this->payment->getId(), ], @@ -149,7 +161,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -163,9 +175,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'dataBag' => $dataBag, @@ -179,59 +191,57 @@ public function pay( public function finalize( AsyncPaymentTransactionStruct $transaction, - Request $request, - SalesChannelContext $salesChannelContext - ): void - { + Request $request, + SalesChannelContext $salesChannelContext + ): void { $this->pluginConfig = $this->configReader->read($salesChannelContext->getSalesChannel()->getId()); $bookingMode = $this->pluginConfig->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_PAYPAL, BookingMode::CHARGE); $transactionCustomFields = $transaction->getOrderTransaction()->getCustomFields(); - $registerAccounts = !empty($transactionCustomFields[self::REMEMBER_PAYPAL_ACCOUNT_KEY]); + $savePaymentDevice = !empty($transactionCustomFields[self::SAVE_PAYMENT_DEVICE_KEY]); $this->unzerClient = $this->clientFactory->createClient( KeyPairContext::createFromSalesChannelContext($salesChannelContext) ); - if (!$registerAccounts) { + if (!$savePaymentDevice) { parent::finalize($transaction, $request, $salesChannelContext); + + return; } - if ($transactionCustomFields === null || !array_key_exists(CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY, $transactionCustomFields)) { + // else we have to do the charge from a freshly saved payment device + + if ($transactionCustomFields === null || !\array_key_exists($this->sessionPaymentTypeKey, $transactionCustomFields)) { throw PaymentException::asyncFinalizeInterrupted($transaction->getOrderTransaction()->getId(), 'missing payment id'); } $this->recur($transaction, $salesChannelContext); try { - if (!($transactionCustomFields[$this->sessionIsRecurring] ?? false)) { - /** @phpstan-ignore-next-line */ - $this->paymentType = $this->fetchPaymentByTypeId($transactionCustomFields[$this->sessionPaymentTypeKey]); + /** @phpstan-ignore-next-line */ + $this->paymentType = $this->fetchPaymentByTypeId($transactionCustomFields[$this->sessionPaymentTypeKey]); - if ($this->paymentType === null) { - throw PaymentException::asyncFinalizeInterrupted($transaction->getOrderTransaction()->getId(), 'missing payment type'); - } + if ($this->paymentType === null) { + throw PaymentException::asyncFinalizeInterrupted($transaction->getOrderTransaction()->getId(), 'missing payment type'); + } - /** Return urls are needed but are not called */ - $bookingMode === BookingMode::CHARGE - ? $this->charge('https://not.needed') - : $this->authorize('https://not.needed'); - - if ($registerAccounts - && $salesChannelContext->getCustomer() !== null - && $salesChannelContext->getCustomer()->getGuest() === false - && $this->paymentType instanceof PayPal - && $this->paymentType->getEmail() !== null - ) { - $this->saveToDeviceVault( - $salesChannelContext->getCustomer(), - UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL, - $salesChannelContext->getContext() - ); - } - } else { - $this->payment = $this->unzerClient->fetchPayment($transactionCustomFields[CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY]); + /** Return urls are needed but are not called */ + $bookingMode === BookingMode::CHARGE + ? $this->charge('https://not.needed') + : $this->authorize('https://not.needed'); + + if ($salesChannelContext->getCustomer() !== null + && $salesChannelContext->getCustomer()->getGuest() === false + && $this->paymentType instanceof Paypal + && $this->paymentType->getEmail() !== null + ) { + $this->saveToDeviceVault( + $salesChannelContext->getCustomer(), + UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL, + $salesChannelContext->getContext() + ); } $this->transactionStateHandler->transformTransactionState( @@ -243,7 +253,7 @@ public function finalize( $this->customFieldsHelper->setOrderTransactionCustomFields($transaction->getOrderTransaction(), $salesChannelContext->getContext()); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'transaction' => $transaction, 'exception' => $apiException, @@ -251,9 +261,9 @@ public function finalize( ); throw PaymentException::asyncFinalizeInterrupted($transaction->getOrderTransaction()->getId(), $apiException->getMessage()); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'transaction' => $transaction, 'exception' => $exception, @@ -266,9 +276,8 @@ public function finalize( protected function handleRecurringPayment( AsyncPaymentTransactionStruct $transaction, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + SalesChannelContext $salesChannelContext + ): RedirectResponse { try { $bookingMode = $this->pluginConfig->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_PAYPAL, BookingMode::CHARGE); @@ -289,7 +298,7 @@ protected function handleRecurringPayment( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'transaction' => $transaction, 'exception' => $apiException, @@ -302,9 +311,9 @@ protected function handleRecurringPayment( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'transaction' => $transaction, 'exception' => $exception, @@ -314,4 +323,51 @@ protected function handleRecurringPayment( throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } + + private function payExpress( + string $unzerPaymentId, + AsyncPaymentTransactionStruct $transaction, + RequestDataBag $dataBag, + Request $currentRequest, + SalesChannelContext $salesChannelContext + ) { + $unzerClient = $this->unzerClient = $this->clientFactory->createClientFromSalesChannelContext($salesChannelContext, $currentRequest); + $payment = $unzerClient->fetchPayment($unzerPaymentId); + /** @var Basket $unzerBasket */ + $unzerBasket = $this->basketHydrator->hydrateObject($salesChannelContext, $transaction); + $unzerBasket->setId($payment->getBasket()->getId()); + $unzerBasket->setOrderId($transaction->getOrderTransaction()->getId()); + $unzerClient->updateBasket($unzerBasket); + + $orderTransaction = $this->fetchTransactionById($transaction->getOrderTransaction()->getId(), $salesChannelContext->getContext()); + // this does implicitly update the customer object + $this->getUnzerCustomer($payment->getCustomer()?->getId() ?? '', $transaction->getOrderTransaction()->getPaymentMethodId(), $orderTransaction, $salesChannelContext); + + if (empty($payment->getCharges())) { + $authorization = new Authorization( + $unzerBasket->getTotalValueGross(), + $unzerBasket->getCurrencyCode(), + $transaction->getReturnUrl() + ); + $unzerClient->updateAuthorization($payment->getId(), $authorization); + } else { + $charge = new Charge( + $unzerBasket->getTotalValueGross(), + $unzerBasket->getCurrencyCode(), + $transaction->getReturnUrl() + ); + $unzerClient->updateCharge($payment->getId(), $charge); + } + $this->persistPaymentInformation( + [ + $this->sessionIsRecurring => false, + $this->sessionPaymentTypeKey => $payment->getPaymentType()->getId(), + CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY => $payment->getId(), + ], + $transaction->getOrderTransaction()->getId(), + $salesChannelContext->getContext() + ); + + return new RedirectResponse($transaction->getReturnUrl()); + } } diff --git a/src/Components/PaymentHandler/UnzerPaylaterDirectDebitSecuredPaymentHandler.php b/src/Components/PaymentHandler/UnzerPaylaterDirectDebitSecuredPaymentHandler.php index 399ac9e3..9c7091fd 100644 --- a/src/Components/PaymentHandler/UnzerPaylaterDirectDebitSecuredPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerPaylaterDirectDebitSecuredPaymentHandler.php @@ -9,7 +9,6 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\HasRiskDataTrait; @@ -26,25 +25,15 @@ class UnzerPaylaterDirectDebitSecuredPaymentHandler extends AbstractUnzerPayment */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); $this->unzerBasket->setTotalValueGross($this->unzerBasket->getTotalValueGross()); - $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); - $birthday = $currentRequest->get('unzerPaymentBirthday', ''); - try { - if (!empty($birthday) - && (empty($this->unzerCustomer->getBirthDate()) || $birthday !== $this->unzerCustomer->getBirthDate())) { - $this->unzerCustomer->setBirthDate($birthday); - $this->unzerClient->createOrUpdateCustomer($this->unzerCustomer); - } - /** @var int $currencyPrecision */ $currencyPrecision = $transaction->getOrder()->getCurrency() !== null ? min( $transaction->getOrder()->getCurrency()->getItemRounding()->getDecimals(), @@ -63,7 +52,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -77,9 +66,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, diff --git a/src/Components/PaymentHandler/UnzerPaylaterInstallmentPaymentHandler.php b/src/Components/PaymentHandler/UnzerPaylaterInstallmentPaymentHandler.php index eb709e9f..2fa3fff9 100644 --- a/src/Components/PaymentHandler/UnzerPaylaterInstallmentPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerPaylaterInstallmentPaymentHandler.php @@ -9,8 +9,6 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Throwable; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\HasRiskDataTrait; @@ -27,10 +25,9 @@ class UnzerPaylaterInstallmentPaymentHandler extends AbstractUnzerPaymentHandler */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); $this->unzerBasket->setTotalValueGross($this->unzerBasket->getTotalValueGross()); @@ -38,11 +35,9 @@ public function pay( $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); try { - $this->updateUnzerCustomer($currentRequest); - $riskData = $this->generateRiskDataResource($transaction, $salesChannelContext); - if (null === $riskData) { + if ($riskData === null) { throw new \RuntimeException('fraud prevention session id is missing from the current request'); } @@ -56,7 +51,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -70,9 +65,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -83,16 +78,4 @@ public function pay( throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } - - private function updateUnzerCustomer(Request $request): void - { - $birthday = $request->get('unzerPaymentBirthday', ''); - - if (empty($birthday) || (!empty($this->unzerCustomer->getBirthDate()) && $birthday === $this->unzerCustomer->getBirthDate())) { - return; - } - - $this->unzerCustomer->setBirthDate($birthday); - $this->unzerCustomer = $this->unzerClient->createOrUpdateCustomer($this->unzerCustomer); - } } diff --git a/src/Components/PaymentHandler/UnzerPaylaterInvoicePaymentHandler.php b/src/Components/PaymentHandler/UnzerPaylaterInvoicePaymentHandler.php index 3c9e7849..58463a43 100644 --- a/src/Components/PaymentHandler/UnzerPaylaterInvoicePaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerPaylaterInvoicePaymentHandler.php @@ -9,42 +9,36 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Symfony\Component\HttpFoundation\Request; -use Throwable; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanAuthorize; use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; use UnzerPayment6\Components\PaymentHandler\Traits\HasRiskDataTrait; use UnzerPayment6\Components\PaymentHandler\Traits\HasTransferInfoTrait; use UnzerSDK\Exceptions\UnzerApiException; -use UnzerSDK\Resources\EmbeddedResources\CompanyInfo; class UnzerPaylaterInvoicePaymentHandler extends AbstractUnzerPaymentHandler { - use HasTransferInfoTrait; use CanAuthorize; use CanCharge; use HasRiskDataTrait; + use HasTransferInfoTrait; /** * {@inheritdoc} */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); $currentRequest = $this->getCurrentRequestFromStack($transaction->getOrderTransaction()->getId()); try { - $this->updateUnzerCustomer($currentRequest); - $riskData = $this->generateRiskDataResource($transaction, $salesChannelContext); - if (null === $riskData) { + if ($riskData === null) { throw new \RuntimeException('fraud prevention session id is missing from the current request'); } @@ -60,7 +54,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -74,9 +68,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'request' => $this->getLoggableRequest($currentRequest), 'transaction' => $transaction, @@ -87,31 +81,4 @@ public function pay( throw PaymentException::asyncProcessInterrupted($transaction->getOrderTransaction()->getId(), $exception->getMessage()); } } - - private function updateUnzerCustomer(Request $request): void - { - $birthday = $request->get('unzerPaymentBirthday', ''); - $companyType = $request->get('unzerPaymentCompanyType', ''); - $createOrUpdate = false; - - if (!empty($birthday) - && (empty($this->unzerCustomer->getBirthDate()) || $birthday !== $this->unzerCustomer->getBirthDate())) { - $createOrUpdate = true; - $this->unzerCustomer->setBirthDate($birthday); - } - - $companyInfo = $this->unzerCustomer->getCompanyInfo() ?? new CompanyInfo(); - - if (!empty($companyType) && $companyInfo->getCompanyType() !== $companyType) { - $createOrUpdate = true; - $companyInfo->setCompanyType($companyType); - $this->unzerCustomer->setCompanyInfo($companyInfo); - } - - if (!$createOrUpdate) { - return; - } - - $this->unzerCustomer = $this->unzerClient->createOrUpdateCustomer($this->unzerCustomer); - } } diff --git a/src/Components/PaymentHandler/UnzerPrePaymentPaymentHandler.php b/src/Components/PaymentHandler/UnzerPrePaymentPaymentHandler.php index 2db6a0ce..27efb710 100644 --- a/src/Components/PaymentHandler/UnzerPrePaymentPaymentHandler.php +++ b/src/Components/PaymentHandler/UnzerPrePaymentPaymentHandler.php @@ -9,7 +9,6 @@ use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Shopware\Core\System\SalesChannel\SalesChannelContext; use Symfony\Component\HttpFoundation\RedirectResponse; -use Throwable; use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; use UnzerPayment6\Components\PaymentHandler\Traits\CanCharge; use UnzerPayment6\Components\PaymentHandler\Traits\HasTransferInfoTrait; @@ -26,10 +25,9 @@ class UnzerPrePaymentPaymentHandler extends AbstractUnzerPaymentHandler */ public function pay( AsyncPaymentTransactionStruct $transaction, - RequestDataBag $dataBag, - SalesChannelContext $salesChannelContext - ): RedirectResponse - { + RequestDataBag $dataBag, + SalesChannelContext $salesChannelContext + ): RedirectResponse { parent::pay($transaction, $dataBag, $salesChannelContext); try { @@ -43,7 +41,7 @@ public function pay( return new RedirectResponse($returnUrl); } catch (UnzerApiException $apiException) { $this->logger->error( - sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught an API exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, @@ -57,9 +55,9 @@ public function pay( ); throw new UnzerPaymentProcessException($transaction->getOrder()->getId(), $transaction->getOrderTransaction()->getId(), $apiException); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( - sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), + \sprintf('Caught a generic exception in %s of %s', __METHOD__, __CLASS__), [ 'dataBag' => $dataBag, 'transaction' => $transaction, diff --git a/src/Components/PaymentHandler/UnzerSofortPaymentHandler.php b/src/Components/PaymentHandler/UnzerSofortPaymentHandler.php deleted file mode 100644 index f6fb69aa..00000000 --- a/src/Components/PaymentHandler/UnzerSofortPaymentHandler.php +++ /dev/null @@ -1,18 +0,0 @@ -bookingMode = $this->pluginConfig->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_WERO, BookingMode::CHARGE); + } +} diff --git a/src/Components/PaymentTransitionMapper/AbstractTransitionMapper.php b/src/Components/PaymentTransitionMapper/AbstractTransitionMapper.php index 8f51a9d0..08b0be8f 100644 --- a/src/Components/PaymentTransitionMapper/AbstractTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/AbstractTransitionMapper.php @@ -14,12 +14,16 @@ abstract class AbstractTransitionMapper { public const CONST_KEY_CHARGEBACK = 'ACTION_CHARGEBACK'; - public const CONST_KEY_AUTHORIZE = 'ACTION_AUTHORIZE'; + public const CONST_KEY_AUTHORIZE = 'ACTION_AUTHORIZE'; - /** @var string */ + /** + * @var string + */ public const INVALID_TRANSITION = 'invalid'; - /** @var bool */ + /** + * @var bool + */ protected $isShipmentAllowed = false; abstract public function supports(BasePaymentType $paymentType): bool; @@ -27,7 +31,7 @@ abstract public function supports(BasePaymentType $paymentType): bool; /** * @throws TransitionMapperException */ - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string { if ($paymentObject->isPending()) { return StateMachineTransitionActions::ACTION_REOPEN; @@ -46,7 +50,7 @@ public function getTargetPaymentStatus(Payment $paymentObject): string return $status; } - throw new TransitionMapperException($this->getResourceName()); + return StateMachineTransitionActions::ACTION_FAIL; } return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); @@ -64,7 +68,7 @@ protected function mapPaymentStatus(Payment $paymentObject): string $status = StateMachineTransitionActions::ACTION_CANCEL; if ($this->stateMachineTransitionExists(self::CONST_KEY_CHARGEBACK)) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, self::CONST_KEY_CHARGEBACK)); + return \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, self::CONST_KEY_CHARGEBACK)); } } elseif ($paymentObject->isPending()) { $status = StateMachineTransitionActions::ACTION_REOPEN; @@ -83,7 +87,7 @@ protected function mapPaymentStatus(Payment $paymentObject): string protected function checkForRefund(Payment $paymentObject, string $currentStatus = self::INVALID_TRANSITION): string { - $totalAmount = (int) round($paymentObject->getAmount()->getTotal() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); + $totalAmount = (int) round($paymentObject->getAmount()->getTotal() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); $cancelledAmount = (int) round($paymentObject->getAmount()->getCanceled() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); $remainingAmount = (int) round($paymentObject->getAmount()->getRemaining() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); @@ -91,7 +95,7 @@ protected function checkForRefund(Payment $paymentObject, string $currentStatus && $currentStatus !== StateMachineTransitionActions::ACTION_CANCEL && !( $this->stateMachineTransitionExists(self::CONST_KEY_CHARGEBACK) - && $currentStatus === constant(sprintf('%s::%s', StateMachineTransitionActions::class, self::CONST_KEY_CHARGEBACK)) + && $currentStatus === \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, self::CONST_KEY_CHARGEBACK)) ) ) { return StateMachineTransitionActions::ACTION_REFUND; @@ -102,12 +106,12 @@ protected function checkForRefund(Payment $paymentObject, string $currentStatus protected function checkForCancellation(Payment $paymentObject, string $currentStatus = self::INVALID_TRANSITION): string { - $amount = $paymentObject->getAmount(); - $total = (int) round($amount->getTotal() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); - $charged = (int) round($amount->getCharged() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); + $amount = $paymentObject->getAmount(); + $total = (int) round($amount->getTotal() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); + $charged = (int) round($amount->getCharged() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); $cancelled = (int) round($amount->getCanceled() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); - if ($total === 0 && $charged === 0 && $cancelled === 0 && count($paymentObject->getCancellations()) > 0) { + if ($total === 0 && $charged === 0 && $cancelled === 0 && \count($paymentObject->getCancellations()) > 0) { return StateMachineTransitionActions::ACTION_CANCEL; } @@ -116,8 +120,8 @@ protected function checkForCancellation(Payment $paymentObject, string $currentS protected function checkForShipment(Payment $paymentObject, string $currentStatus = self::INVALID_TRANSITION): string { - $shippedAmount = 0; - $totalAmount = (int) round($paymentObject->getAmount()->getTotal() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); + $shippedAmount = 0; + $totalAmount = (int) round($paymentObject->getAmount()->getTotal() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); $cancelledAmount = (int) round($paymentObject->getAmount()->getCanceled() * (10 ** UnzerPayment6::MAX_DECIMAL_PRECISION)); if (empty($paymentObject->getShipments())) { @@ -151,6 +155,6 @@ protected function checkForShipment(Payment $paymentObject, string $currentStatu */ protected function stateMachineTransitionExists(string $stateMachineActionConstantName): bool { - return defined(sprintf('%s::%s', StateMachineTransitionActions::class, $stateMachineActionConstantName)); + return \defined(\sprintf('%s::%s', StateMachineTransitionActions::class, $stateMachineActionConstantName)); } } diff --git a/src/Components/PaymentTransitionMapper/AliPayTransitionMapper.php b/src/Components/PaymentTransitionMapper/AliPayTransitionMapper.php index 674b21a3..152ac792 100644 --- a/src/Components/PaymentTransitionMapper/AliPayTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/AliPayTransitionMapper.php @@ -11,10 +11,12 @@ class AliPayTransitionMapper extends AbstractTransitionMapper { use IsBasicPaymentMethodTransitionMapper; + public function supports(BasePaymentType $paymentType): bool { return $paymentType instanceof Alipay; } + protected function getResourceName(): string { return Alipay::getResourceName(); diff --git a/src/Components/PaymentTransitionMapper/ApplePayTransitionMapper.php b/src/Components/PaymentTransitionMapper/ApplePayTransitionMapper.php index 18f79644..821cd820 100644 --- a/src/Components/PaymentTransitionMapper/ApplePayTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/ApplePayTransitionMapper.php @@ -4,84 +4,24 @@ namespace UnzerPayment6\Components\PaymentTransitionMapper; -use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; -use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ConfigReader\ConfigReader; -use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; -use UnzerPayment6\Components\PaymentTransitionMapper\Exception\TransitionMapperException; -use UnzerPayment6\Components\PaymentTransitionMapper\Traits\HasBookingMode; -use UnzerSDK\Resources\Payment; +use UnzerPayment6\Components\PaymentTransitionMapper\Traits\IsBasicPaymentMethodTransitionMapperWithBookingMode; use UnzerSDK\Resources\PaymentTypes\Applepay; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -use UnzerSDK\Resources\TransactionTypes\Authorization; class ApplePayTransitionMapper extends AbstractTransitionMapper { - use HasBookingMode; + use IsBasicPaymentMethodTransitionMapperWithBookingMode; private const BOOKING_MODE_KEY = ConfigReader::CONFIG_KEY_BOOKING_MODE_APPLE_PAY; - private const DEFAULT_MODE = BookingMode::CHARGE; - - public function __construct(ConfigReaderInterface $configReader, EntityRepository $orderTransactionRepository) - { - $this->configReader = $configReader; - $this->orderTransactionRepository = $orderTransactionRepository; - } + private const DEFAULT_MODE = BookingMode::CHARGE; public function supports(BasePaymentType $paymentType): bool { return $paymentType instanceof Applepay; } - public function getTargetPaymentStatus(Payment $paymentObject): string - { - try { - $bookingMode = $this->getBookingMode($paymentObject); - - if ($bookingMode !== self::DEFAULT_MODE) { - return $this->mapForAuthorizeMode($paymentObject); - } - - return parent::getTargetPaymentStatus($paymentObject); - } catch (TransitionMapperException $exception) { - if ($paymentObject->isPending()) { - return StateMachineTransitionActions::ACTION_REOPEN; - } - - throw $exception; - } - } - - protected function mapForAuthorizeMode(Payment $paymentObject): string - { - if ($paymentObject->isCanceled()) { - $status = $this->checkForRefund($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - $status = $this->checkForCancellation($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - throw new TransitionMapperException($this->getResourceName()); - } - - if ($this->stateMachineTransitionExists(AbstractTransitionMapper::CONST_KEY_AUTHORIZE) && $paymentObject->isPending()) { - $authorization = $paymentObject->getAuthorization(); - - if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); - } - } - - return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); - } - protected function getResourceName(): string { return Applepay::getResourceName(); diff --git a/src/Components/PaymentTransitionMapper/CreditCardTransitionMapper.php b/src/Components/PaymentTransitionMapper/CreditCardTransitionMapper.php index a649d748..46c299a7 100644 --- a/src/Components/PaymentTransitionMapper/CreditCardTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/CreditCardTransitionMapper.php @@ -4,78 +4,26 @@ namespace UnzerPayment6\Components\PaymentTransitionMapper; -use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; -use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ConfigReader\ConfigReader; -use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; -use UnzerPayment6\Components\PaymentTransitionMapper\Exception\TransitionMapperException; -use UnzerPayment6\Components\PaymentTransitionMapper\Traits\HasBookingMode; -use UnzerSDK\Resources\Payment; +use UnzerPayment6\Components\PaymentTransitionMapper\Traits\IsBasicPaymentMethodTransitionMapperWithBookingMode; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; use UnzerSDK\Resources\PaymentTypes\Card; -use UnzerSDK\Resources\TransactionTypes\Authorization; class CreditCardTransitionMapper extends AbstractTransitionMapper { - use HasBookingMode; + use IsBasicPaymentMethodTransitionMapperWithBookingMode; private const BOOKING_MODE_KEY = ConfigReader::CONFIG_KEY_BOOKING_MODE_CARD; - private const DEFAULT_MODE = BookingMode::CHARGE; - - public function __construct(ConfigReaderInterface $configReader, EntityRepository $orderTransactionRepository) - { - $this->configReader = $configReader; - $this->orderTransactionRepository = $orderTransactionRepository; - } + private const DEFAULT_MODE = BookingMode::CHARGE; public function supports(BasePaymentType $paymentType): bool { return $paymentType instanceof Card; } - public function getTargetPaymentStatus(Payment $paymentObject): string - { - $bookingMode = $this->getBookingMode($paymentObject); - - if ($bookingMode !== self::DEFAULT_MODE) { - return $this->mapForAuthorizeMode($paymentObject); - } - - return parent::getTargetPaymentStatus($paymentObject); - } - protected function getResourceName(): string { return Card::getResourceName(); } - - protected function mapForAuthorizeMode(Payment $paymentObject): string - { - if ($paymentObject->isCanceled()) { - $status = $this->checkForRefund($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - $status = $this->checkForCancellation($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - throw new TransitionMapperException($this->getResourceName()); - } - - if ($this->stateMachineTransitionExists(AbstractTransitionMapper::CONST_KEY_AUTHORIZE) && $paymentObject->isPending()) { - $authorization = $paymentObject->getAuthorization(); - - if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); - } - } - - return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); - } } diff --git a/src/Components/PaymentTransitionMapper/Exception/NoTransitionMapperFoundException.php b/src/Components/PaymentTransitionMapper/Exception/NoTransitionMapperFoundException.php index 34097cfc..c024bcd5 100644 --- a/src/Components/PaymentTransitionMapper/Exception/NoTransitionMapperFoundException.php +++ b/src/Components/PaymentTransitionMapper/Exception/NoTransitionMapperFoundException.php @@ -10,6 +10,6 @@ class NoTransitionMapperFoundException extends AbstractUnzerPaymentException { public function __construct(string $paymentName) { - parent::__construct(sprintf('No transition mapper was found for payment method: %s', $paymentName)); + parent::__construct(\sprintf('No transition mapper was found for payment method: %s', $paymentName)); } } diff --git a/src/Components/PaymentTransitionMapper/Exception/TransitionMapperException.php b/src/Components/PaymentTransitionMapper/Exception/TransitionMapperException.php index 20fc4643..b70840a9 100644 --- a/src/Components/PaymentTransitionMapper/Exception/TransitionMapperException.php +++ b/src/Components/PaymentTransitionMapper/Exception/TransitionMapperException.php @@ -10,6 +10,6 @@ class TransitionMapperException extends AbstractUnzerPaymentException { public function __construct(string $paymentName) { - parent::__construct(sprintf('Payment status transition is not allowed for payment method: %s', $paymentName)); + parent::__construct(\sprintf('Payment status transition is not allowed for payment method: %s', $paymentName)); } } diff --git a/src/Components/PaymentTransitionMapper/GiropayTransitionMapper.php b/src/Components/PaymentTransitionMapper/GiropayTransitionMapper.php deleted file mode 100644 index 529d4c20..00000000 --- a/src/Components/PaymentTransitionMapper/GiropayTransitionMapper.php +++ /dev/null @@ -1,24 +0,0 @@ -configReader = $configReader; - $this->orderTransactionRepository = $orderTransactionRepository; - } + private const DEFAULT_MODE = BookingMode::CHARGE; public function supports(BasePaymentType $paymentType): bool { return $paymentType instanceof Googlepay; } - public function getTargetPaymentStatus(Payment $paymentObject): string - { - try { - $bookingMode = $this->getBookingMode($paymentObject); - - if ($bookingMode !== self::DEFAULT_MODE) { - return $this->mapForAuthorizeMode($paymentObject); - } - - return parent::getTargetPaymentStatus($paymentObject); - } catch (TransitionMapperException $exception) { - if ($paymentObject->isPending()) { - return StateMachineTransitionActions::ACTION_REOPEN; - } - - throw $exception; - } - } - - protected function mapForAuthorizeMode(Payment $paymentObject): string - { - if ($paymentObject->isCanceled()) { - $status = $this->checkForRefund($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - $status = $this->checkForCancellation($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - throw new TransitionMapperException($this->getResourceName()); - } - - if ($this->stateMachineTransitionExists(AbstractTransitionMapper::CONST_KEY_AUTHORIZE) && $paymentObject->isPending()) { - $authorization = $paymentObject->getAuthorization(); - - if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); - } - } - - return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); - } - protected function getResourceName(): string { return Googlepay::getResourceName(); diff --git a/src/Components/PaymentTransitionMapper/InvoiceSecuredTransitionMapper.php b/src/Components/PaymentTransitionMapper/InvoiceSecuredTransitionMapper.php deleted file mode 100644 index 93f3ffb6..00000000 --- a/src/Components/PaymentTransitionMapper/InvoiceSecuredTransitionMapper.php +++ /dev/null @@ -1,47 +0,0 @@ -isCanceled()) { - $status = $this->checkForRefund($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - $status = $this->checkForCancellation($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - throw new TransitionMapperException($this->getResourceName()); - } - - return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); - } - - protected function getResourceName(): string - { - return InvoiceSecured::getResourceName(); - } -} diff --git a/src/Components/PaymentTransitionMapper/InvoiceTransitionMapper.php b/src/Components/PaymentTransitionMapper/InvoiceTransitionMapper.php deleted file mode 100644 index 2efca0de..00000000 --- a/src/Components/PaymentTransitionMapper/InvoiceTransitionMapper.php +++ /dev/null @@ -1,44 +0,0 @@ -isCanceled()) { - $status = $this->checkForRefund($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - $status = $this->checkForCancellation($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - throw new TransitionMapperException($this->getResourceName()); - } - - return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); - } - - protected function getResourceName(): string - { - return Invoice::getResourceName(); - } -} diff --git a/src/Components/PaymentTransitionMapper/InstallmentSecuredTransitionMapper.php b/src/Components/PaymentTransitionMapper/KlarnaTransitionMapper.php similarity index 54% rename from src/Components/PaymentTransitionMapper/InstallmentSecuredTransitionMapper.php rename to src/Components/PaymentTransitionMapper/KlarnaTransitionMapper.php index 3c2eb018..d2fc6fea 100644 --- a/src/Components/PaymentTransitionMapper/InstallmentSecuredTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/KlarnaTransitionMapper.php @@ -4,22 +4,28 @@ namespace UnzerPayment6\Components\PaymentTransitionMapper; +use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions; use UnzerPayment6\Components\PaymentTransitionMapper\Exception\TransitionMapperException; +use UnzerPayment6\Components\PaymentTransitionMapper\Traits\IsBasicPaymentMethodTransitionMapper; use UnzerSDK\Resources\Payment; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -use UnzerSDK\Resources\PaymentTypes\InstallmentSecured; +use UnzerSDK\Resources\PaymentTypes\Klarna; -class InstallmentSecuredTransitionMapper extends AbstractTransitionMapper +class KlarnaTransitionMapper extends AbstractTransitionMapper { - /** @var bool */ - protected $isShipmentAllowed = true; + use IsBasicPaymentMethodTransitionMapper; public function supports(BasePaymentType $paymentType): bool { - return $paymentType instanceof InstallmentSecured; + return $paymentType instanceof Klarna; } - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string + { + return $this->mapForAuthorizeMode($paymentObject); + } + + protected function mapForAuthorizeMode(Payment $paymentObject): string { if ($paymentObject->isCanceled()) { $status = $this->checkForRefund($paymentObject); @@ -37,11 +43,15 @@ public function getTargetPaymentStatus(Payment $paymentObject): string throw new TransitionMapperException($this->getResourceName()); } + if ($this->stateMachineTransitionExists(AbstractTransitionMapper::CONST_KEY_AUTHORIZE) && $paymentObject->isPending()) { + return \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); + } + return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); } protected function getResourceName(): string { - return InstallmentSecured::getResourceName(); + return Klarna::getResourceName(); } } diff --git a/src/Components/PaymentTransitionMapper/PayPalTransitionMapper.php b/src/Components/PaymentTransitionMapper/PayPalTransitionMapper.php index fae242e1..ea9ab137 100644 --- a/src/Components/PaymentTransitionMapper/PayPalTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/PayPalTransitionMapper.php @@ -4,78 +4,26 @@ namespace UnzerPayment6\Components\PaymentTransitionMapper; -use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; -use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions; use UnzerPayment6\Components\BookingMode; use UnzerPayment6\Components\ConfigReader\ConfigReader; -use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; -use UnzerPayment6\Components\PaymentTransitionMapper\Exception\TransitionMapperException; -use UnzerPayment6\Components\PaymentTransitionMapper\Traits\HasBookingMode; -use UnzerSDK\Resources\Payment; +use UnzerPayment6\Components\PaymentTransitionMapper\Traits\IsBasicPaymentMethodTransitionMapperWithBookingMode; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; use UnzerSDK\Resources\PaymentTypes\Paypal; -use UnzerSDK\Resources\TransactionTypes\Authorization; class PayPalTransitionMapper extends AbstractTransitionMapper { - use HasBookingMode; + use IsBasicPaymentMethodTransitionMapperWithBookingMode; private const BOOKING_MODE_KEY = ConfigReader::CONFIG_KEY_BOOKING_MODE_PAYPAL; - private const DEFAULT_MODE = BookingMode::CHARGE; - - public function __construct(ConfigReaderInterface $configReader, EntityRepository $orderTransactionRepository) - { - $this->configReader = $configReader; - $this->orderTransactionRepository = $orderTransactionRepository; - } + private const DEFAULT_MODE = BookingMode::CHARGE; public function supports(BasePaymentType $paymentType): bool { return $paymentType instanceof Paypal; } - public function getTargetPaymentStatus(Payment $paymentObject): string - { - $bookingMode = $this->getBookingMode($paymentObject); - - if ($bookingMode !== self::DEFAULT_MODE) { - return $this->mapForAuthorizeMode($paymentObject); - } - - return parent::getTargetPaymentStatus($paymentObject); - } - protected function getResourceName(): string { return Paypal::getResourceName(); } - - protected function mapForAuthorizeMode(Payment $paymentObject): string - { - if ($paymentObject->isCanceled()) { - $status = $this->checkForRefund($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - $status = $this->checkForCancellation($paymentObject); - - if ($status !== self::INVALID_TRANSITION) { - return $status; - } - - throw new TransitionMapperException($this->getResourceName()); - } - - if ($this->stateMachineTransitionExists(AbstractTransitionMapper::CONST_KEY_AUTHORIZE) && $paymentObject->isPending()) { - $authorization = $paymentObject->getAuthorization(); - - if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); - } - } - - return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); - } } diff --git a/src/Components/PaymentTransitionMapper/PaylaterDirectDebitSecuredTransitionMapper.php b/src/Components/PaymentTransitionMapper/PaylaterDirectDebitSecuredTransitionMapper.php index 9d620a56..01bf074a 100644 --- a/src/Components/PaymentTransitionMapper/PaylaterDirectDebitSecuredTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/PaylaterDirectDebitSecuredTransitionMapper.php @@ -13,7 +13,9 @@ class PaylaterDirectDebitSecuredTransitionMapper extends AbstractTransitionMapper { - /** @var bool */ + /** + * @var bool + */ protected $isShipmentAllowed = true; public function supports(BasePaymentType $paymentType): bool @@ -21,7 +23,7 @@ public function supports(BasePaymentType $paymentType): bool return $paymentType instanceof PaylaterDirectDebit; } - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string { if ($paymentObject->isCanceled()) { $status = $this->checkForRefund($paymentObject); @@ -43,7 +45,7 @@ public function getTargetPaymentStatus(Payment $paymentObject): string $authorization = $paymentObject->getAuthorization(); if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); + return \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); } } diff --git a/src/Components/PaymentTransitionMapper/PaylaterInstallmentTransitionMapper.php b/src/Components/PaymentTransitionMapper/PaylaterInstallmentTransitionMapper.php index 4983ca21..054804be 100644 --- a/src/Components/PaymentTransitionMapper/PaylaterInstallmentTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/PaylaterInstallmentTransitionMapper.php @@ -13,7 +13,9 @@ class PaylaterInstallmentTransitionMapper extends AbstractTransitionMapper { - /** @var bool */ + /** + * @var bool + */ protected $isShipmentAllowed = true; public function supports(BasePaymentType $paymentType): bool @@ -21,7 +23,7 @@ public function supports(BasePaymentType $paymentType): bool return $paymentType instanceof PaylaterInstallment; } - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string { if ($paymentObject->isCanceled()) { $status = $this->checkForRefund($paymentObject); @@ -43,7 +45,7 @@ public function getTargetPaymentStatus(Payment $paymentObject): string $authorization = $paymentObject->getAuthorization(); if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); + return \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); } } diff --git a/src/Components/PaymentTransitionMapper/PaylaterInvoiceTransitionMapper.php b/src/Components/PaymentTransitionMapper/PaylaterInvoiceTransitionMapper.php index c095afff..75ae8c5e 100644 --- a/src/Components/PaymentTransitionMapper/PaylaterInvoiceTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/PaylaterInvoiceTransitionMapper.php @@ -18,7 +18,7 @@ public function supports(BasePaymentType $paymentType): bool return $paymentType instanceof PaylaterInvoice; } - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string { if ($paymentObject->isCanceled()) { $status = $this->checkForRefund($paymentObject); @@ -40,7 +40,7 @@ public function getTargetPaymentStatus(Payment $paymentObject): string $authorization = $paymentObject->getAuthorization(); if ($authorization instanceof Authorization && $authorization->isSuccess()) { - return constant(sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); + return \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); } } diff --git a/src/Components/PaymentTransitionMapper/PrepaymentTransitionMapper.php b/src/Components/PaymentTransitionMapper/PrepaymentTransitionMapper.php index 5d48c0a4..bd446b58 100644 --- a/src/Components/PaymentTransitionMapper/PrepaymentTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/PrepaymentTransitionMapper.php @@ -16,7 +16,7 @@ public function supports(BasePaymentType $paymentType): bool return $paymentType instanceof Prepayment; } - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string { if ($paymentObject->isCanceled()) { $status = $this->checkForRefund($paymentObject); diff --git a/src/Components/PaymentTransitionMapper/SepaDirectDebitSecuredTransitionMapper.php b/src/Components/PaymentTransitionMapper/SepaDirectDebitSecuredTransitionMapper.php deleted file mode 100644 index 12bad372..00000000 --- a/src/Components/PaymentTransitionMapper/SepaDirectDebitSecuredTransitionMapper.php +++ /dev/null @@ -1,21 +0,0 @@ -getOrderByPayment($paymentObject->getOrderId()); + $order = $this->getOrderByPayment($orderTransactionId); if ($order === null) { return self::DEFAULT_MODE; @@ -61,4 +64,33 @@ protected function getTransactionById(string $transactionId): ?OrderTransactionE return $orderSearchResult->first(); } + + protected function mapForAuthorizeMode(Payment $paymentObject): string + { + if ($paymentObject->isCanceled()) { + $status = $this->checkForRefund($paymentObject); + + if ($status !== self::INVALID_TRANSITION) { + return $status; + } + + $status = $this->checkForCancellation($paymentObject); + + if ($status !== self::INVALID_TRANSITION) { + return $status; + } + + return StateMachineTransitionActions::ACTION_FAIL; + } + + if ($this->stateMachineTransitionExists(AbstractTransitionMapper::CONST_KEY_AUTHORIZE) && $paymentObject->isPending()) { + $authorization = $paymentObject->getAuthorization(); + + if ($authorization instanceof Authorization && $authorization->isSuccess()) { + return \constant(\sprintf('%s::%s', StateMachineTransitionActions::class, AbstractTransitionMapper::CONST_KEY_AUTHORIZE)); + } + } + + return $this->checkForRefund($paymentObject, $this->mapPaymentStatus($paymentObject)); + } } diff --git a/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapper.php b/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapper.php index 44438c93..f5f40086 100644 --- a/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapper.php +++ b/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapper.php @@ -10,10 +10,10 @@ trait IsBasicPaymentMethodTransitionMapper { - public function getTargetPaymentStatus(Payment $paymentObject): string + public function getTargetPaymentStatus(Payment $paymentObject, string $orderTransactionId): string { try { - return parent::getTargetPaymentStatus($paymentObject); + return parent::getTargetPaymentStatus($paymentObject, $orderTransactionId); } catch (TransitionMapperException $exception) { if ($paymentObject->isPending()) { return StateMachineTransitionActions::ACTION_REOPEN; diff --git a/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapperWithBookingMode.php b/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapperWithBookingMode.php new file mode 100644 index 00000000..af3a6446 --- /dev/null +++ b/src/Components/PaymentTransitionMapper/Traits/IsBasicPaymentMethodTransitionMapperWithBookingMode.php @@ -0,0 +1,33 @@ +getBookingMode($orderTransactionId); + + if ($bookingMode !== self::DEFAULT_MODE) { + return $this->mapForAuthorizeMode($paymentObject); + } + + return parent::getTargetPaymentStatus($paymentObject, $orderTransactionId); + } catch (TransitionMapperException $exception) { + if ($paymentObject->isPending()) { + return StateMachineTransitionActions::ACTION_REOPEN; + } + + throw $exception; + } + } +} diff --git a/src/Components/PaymentTransitionMapper/WeroTransitionMapper.php b/src/Components/PaymentTransitionMapper/WeroTransitionMapper.php new file mode 100644 index 00000000..1714be2b --- /dev/null +++ b/src/Components/PaymentTransitionMapper/WeroTransitionMapper.php @@ -0,0 +1,29 @@ +format; - } - - public function setFormat(string $format): self - { - $this->format = $format; - - return $this; - } - - public function getType(): string - { - return $this->type; - } - - public function setType(string $type): self - { - $this->type = $type; - - return $this; - } - - public function getPrivateKey(): string - { - return $this->privateKey; - } - - public function setPrivateKey(string $privateKey): self - { - $this->privateKey = $privateKey; - - return $this; - } - - public function getCertificate(): string - { - return $this->certificate; - } - - public function setCertificate(string $certificate): self - { - $this->certificate = $certificate; - - return $this; - } - - public function getActive(): bool { - return (bool)$this->active; - } - - public function setActive( $active ): self { - $this->active = (bool)$active; - - return $this; - } - - - public function expose() - { - $data = parent::expose(); - - if (!($data instanceof stdClass) && array_key_exists('privateKey', $data)) { - $data['private-key'] = $data['privateKey']; - unset($data['privateKey']); - } - - return $data; - } - - protected function getResourcePath($httpMethod = HttpAdapterInterface::REQUEST_GET): string - { - return 'keypair/applepay/certificates'; - } -} diff --git a/src/Components/Resource/ApplePayPrivateKey.php b/src/Components/Resource/ApplePayPrivateKey.php deleted file mode 100644 index 7067d8e0..00000000 --- a/src/Components/Resource/ApplePayPrivateKey.php +++ /dev/null @@ -1,58 +0,0 @@ -format; - } - - public function setFormat(string $format): self - { - $this->format = $format; - - return $this; - } - - public function getType(): string - { - return $this->type; - } - - public function setType(string $type): self - { - $this->type = $type; - - return $this; - } - - public function getCertificate(): string - { - return $this->certificate; - } - - public function setCertificate(string $certificate): self - { - $this->certificate = $certificate; - - return $this; - } - - protected function getResourcePath($httpMethod = HttpAdapterInterface::REQUEST_GET): string - { - return 'keypair/applepay/privatekeys'; - } -} diff --git a/src/Components/ResourceHydrator/BasketResourceHydrator.php b/src/Components/ResourceHydrator/BasketResourceHydrator.php index fc4252e7..491d9e40 100755 --- a/src/Components/ResourceHydrator/BasketResourceHydrator.php +++ b/src/Components/ResourceHydrator/BasketResourceHydrator.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\ResourceHydrator; -use InvalidArgumentException; use Shopware\Core\Checkout\Cart\LineItem\LineItem; use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice; use Shopware\Core\Checkout\Cart\Tax\Struct\CalculatedTax; @@ -31,17 +30,16 @@ class BasketResourceHydrator implements ResourceHydratorInterface */ public function hydrateObject( SalesChannelContext $channelContext, - $transaction = null - ): AbstractUnzerResource - { + $transaction = null + ): AbstractUnzerResource { if (!($transaction instanceof AsyncPaymentTransactionStruct) && !($transaction instanceof OrderTransactionEntity)) { - throw new InvalidArgumentException('Transaction struct can not be null'); + throw new \InvalidArgumentException('Transaction struct can not be null'); } $order = $transaction->getOrder(); if ($order === null) { - throw new InvalidArgumentException('Order can not be null'); + throw new \InvalidArgumentException('Order can not be null'); } if ($transaction instanceof AsyncPaymentTransactionStruct) { @@ -49,6 +47,7 @@ public function hydrateObject( } else { $transactionId = $transaction->getId(); } + return $this->generateUnzerBasket($order, $transactionId, $channelContext); } @@ -88,11 +87,10 @@ public function generateUnzerBasket(OrderEntity $order, string $transactionId, S protected function hydrateLineItems( OrderLineItemCollection $lineItemCollection, - Basket $unzerBasket, - int $currencyPrecision, - ?string $taxStatus - ): void - { + Basket $unzerBasket, + int $currencyPrecision, + ?string $taxStatus + ): void { $customProductLabels = $this->mapCustomProductsLabel($lineItemCollection); /** @var OrderLineItemEntity $lineItem */ @@ -102,15 +100,23 @@ protected function hydrateLineItems( } $basketItem = new BasketItem(); $label = $lineItem->getLabel(); - if (!empty($customProductLabels) && array_key_exists($lineItem->getId(), $customProductLabels)) { + if (!empty($customProductLabels) && \array_key_exists($lineItem->getId(), $customProductLabels)) { $label = $customProductLabels[$lineItem->getId()] - ? sprintf('%s: %s', $lineItem->getLabel(), $customProductLabels[$lineItem->getId()]) + ? \sprintf('%s: %s', $lineItem->getLabel(), $customProductLabels[$lineItem->getId()]) : $lineItem->getLabel(); } $basketItem->setTitle($label); $basketItem->setQuantity($lineItem->getQuantity()); $basketItem->setType($lineItem->getUnitPrice() < 0 ? BasketItemTypes::VOUCHER : BasketItemTypes::GOODS); - $basketItem->setImageUrl($lineItem->getCover() ? $lineItem->getCover()->getUrl() : null); + 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; @@ -122,7 +128,7 @@ protected function hydrateLineItems( foreach ($lineItem->getPrice()->getCalculatedTaxes() as $tax) { $amountTax += round($tax->getTax(), $currencyPrecision); $taxRate += $tax->getTaxRate(); - $taxCounter++; + ++$taxCounter; } $amountGross = round($lineItem->getTotalPrice(), $currencyPrecision); if ($taxStatus === CartPrice::TAX_STATE_NET) { @@ -148,11 +154,10 @@ protected function hydrateLineItems( protected function hydrateShippingCosts( OrderEntity $order, - Basket $basket, - int $currencyPrecision, - string $shippingMethodName - ): void - { + Basket $basket, + int $currencyPrecision, + string $shippingMethodName + ): void { $shippingCosts = $order->getShippingCosts(); $dispatchBasketItem = new BasketItem(); @@ -217,14 +222,13 @@ protected function mapCustomProductsLabel(OrderLineItemCollection $lineItemColle protected function isCustomProduct( OrderLineItemCollection $lineItemCollection, - OrderLineItemEntity $lineItemEntity - ): bool - { + OrderLineItemEntity $lineItemEntity + ): bool { if (!class_exists(CustomizedProductsCartDataCollector::class)) { return false; } - $isCustomProductOption = in_array( + $isCustomProductOption = \in_array( $lineItemEntity->getType(), [ CustomizedProductsCartDataCollector::CUSTOMIZED_PRODUCTS_OPTION_LINE_ITEM_TYPE, @@ -238,9 +242,8 @@ protected function isCustomProduct( protected function isParentCustomProduct( OrderLineItemCollection $lineItemCollection, - OrderLineItemEntity $lineItemEntity - ): bool - { + OrderLineItemEntity $lineItemEntity + ): bool { if (!class_exists(CustomizedProductsCartDataCollector::class)) { return false; } @@ -270,7 +273,7 @@ protected function getShippingMethodName(ShippingMethodEntity $shippingMethod): } if (!empty($shippingMethod->getTranslated()) - && array_key_exists('name', $shippingMethod->getTranslated()) + && \array_key_exists('name', $shippingMethod->getTranslated()) && !empty($shippingMethod->getTranslated()['name'])) { return $shippingMethod->getTranslated()['name']; } @@ -280,25 +283,25 @@ protected function getShippingMethodName(ShippingMethodEntity $shippingMethod): protected function isFreeBasketItem(BasketItem $basketItem, int $currencyPrecision): bool { - if ((int)round($basketItem->getAmountPerUnitGross() * (10 ** $currencyPrecision)) === 0 && (int)round($basketItem->getAmountDiscountPerUnitGross() * (10 ** $currencyPrecision)) === 0) { + if ((int) round($basketItem->getAmountPerUnitGross() * (10 ** $currencyPrecision)) === 0 && (int) round($basketItem->getAmountDiscountPerUnitGross() * (10 ** $currencyPrecision)) === 0) { return true; } return false; } - private function makeBasketValid(Basket $unzerBasket, int $currencyPrecision) + private function makeBasketValid(Basket $unzerBasket, int $currencyPrecision): void { $total = $unzerBasket->getTotalValueGross(); - foreach($unzerBasket->getBasketItems() as $item) { + foreach ($unzerBasket->getBasketItems() as $item) { $total -= $item->getAmountPerUnitGross() * $item->getQuantity(); $total += $item->getAmountDiscountPerUnitGross() * $item->getQuantity(); } - if(number_format($total, $currencyPrecision) !== number_format(0, $currencyPrecision)) { + if (number_format($total, $currencyPrecision) !== number_format(0, $currencyPrecision)) { $basketItem = new BasketItem(); $basketItem->setTitle('Unzer Shortfall'); $basketItem->setQuantity(1); - if($total > 0) { + if ($total > 0) { $basketItem->setAmountPerUnitGross($total); $basketItem->setType(BasketItemTypes::GOODS); } else { diff --git a/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydrator.php b/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydrator.php index ffe3e3a3..9e0cbca9 100755 --- a/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydrator.php +++ b/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydrator.php @@ -4,14 +4,18 @@ namespace UnzerPayment6\Components\ResourceHydrator\CustomerResourceHydrator; -use RuntimeException; use Shopware\Core\Checkout\Customer\Aggregate\CustomerAddress\CustomerAddressEntity; use Shopware\Core\Checkout\Customer\CustomerEntity; +use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; +use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; +use Shopware\Core\Framework\App\ShopId\ShopIdProvider; +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 Symfony\Component\HttpFoundation\RequestStack; use UnzerPayment6\Installer\PaymentInstaller; -use UnzerSDK\Constants\CompanyCommercialSectorItems; -use UnzerSDK\Constants\CompanyRegistrationTypes; use UnzerSDK\Constants\ShippingTypes; use UnzerSDK\Resources\AbstractUnzerResource; use UnzerSDK\Resources\Customer; @@ -27,68 +31,81 @@ class CustomerResourceHydrator implements CustomerResourceHydratorInterface PaymentInstaller::PAYMENT_ID_DIRECT_DEBIT_SECURED, ]; - private RequestStack $requestStack; + public function __construct( + protected readonly RequestStack $requestStack, + protected readonly EntityRepository $languageRepository, + protected readonly ShopIdProvider $shopIdProvider, + ) { + } - public function __construct(RequestStack $requestStack) + public function getShopCustomerId(CustomerEntity $customer, ?OrderAddressEntity $orderBillingAddress = null): string { - $this->requestStack = $requestStack; + $customerNumber = $customer->getCustomerNumber(); + + $company = $orderBillingAddress !== null ? $orderBillingAddress->getCompany() : $customer->getActiveBillingAddress()?->getCompany(); + + if (!empty($company)) { + $customerNumber .= '_b'; + } + + try { + $shopId = $this->shopIdProvider->getShopId(); + } catch (\Throwable) { + $shopId = ''; + } + $customerNumber .= '_' . $shopId; + + return $customerNumber; } + /** + * Creates a new prefilled Unzer Customer Object from the current SalesChannelContext + */ public function hydrateObject( string $paymentMethodId, - SalesChannelContext $channelContext + SalesChannelContext $channelContext, + ?OrderTransactionEntity $orderTransaction = null ): AbstractUnzerResource { $customer = $channelContext->getCustomer(); if (!$customer) { - throw new RuntimeException('Could not determine the customer'); + throw new \RuntimeException('Could not determine the customer'); } - $billingAddress = $customer->getActiveBillingAddress(); + $billingAddress = $customer->getActiveBillingAddress(); $shippingAddress = $customer->getActiveShippingAddress(); if (empty($billingAddress) || empty($shippingAddress)) { - throw new RuntimeException(sprintf('Could not determine the address for customer with number %s', $customer->getCustomerNumber())); + throw new \RuntimeException(\sprintf('Could not determine the address for customer with number %s', $customer->getCustomerNumber())); } - if (empty($billingAddress->getCompany()) || !in_array($paymentMethodId, self::B2B_CUSTOMERS_ALLOWED, true)) { + if (empty($billingAddress->getCompany()) || !\in_array($paymentMethodId, self::B2B_CUSTOMERS_ALLOWED, true)) { $unzerCustomer = CustomerFactory::createCustomer( $billingAddress->getFirstName(), $billingAddress->getLastName() ); } else { - $companyInfo = (new CompanyInfo()) - ->setRegistrationType(CompanyRegistrationTypes::REGISTRATION_TYPE_NOT_REGISTERED) - ->setFunction('OWNER') - ->setCommercialSector(CompanyCommercialSectorItems::OTHER); - - $unzerCustomer = (new Customer()) - ->setFirstname($billingAddress->getFirstName()) - ->setLastname($billingAddress->getLastName()) - ->setBirthDate($this->getBirthDate($customer)) - ->setBillingAddress($this->getUnzerAddress($billingAddress)) - ->setEmail($customer->getEmail()) - ->setCompany($billingAddress->getCompany()) - ->setCompanyInfo($companyInfo); + $unzerCustomer = CustomerFactory::createNotRegisteredB2bCustomer( + $billingAddress->getFirstName(), + $billingAddress->getLastName(), + (string) $this->getBirthDate($customer), + $this->getUnzerAddress($billingAddress), + $customer->getEmail(), + $billingAddress->getCompany() + ); } $unzerCustomer->setShippingAddress($this->getUnzerAddress($shippingAddress)); $unzerCustomer->setBillingAddress($this->getUnzerAddress($billingAddress)); + $unzerCustomer->setCustomerId($this->getShopCustomerId($customer)); - $customerNumber = $customer->getCustomerNumber(); - - if (!empty($billingAddress->getCompany())) { - $customerNumber .= '_b'; - } - - $unzerCustomer->setCustomerId($customerNumber); - - return $this->updateAdditionalDataToCustomer($unzerCustomer, $customer, $billingAddress); + return $this->updateAdditionalDataToCustomer($unzerCustomer, $customer, $billingAddress, $orderTransaction); } public function hydrateExistingCustomer( AbstractUnzerResource $unzerCustomer, - SalesChannelContext $salesChannelContext + SalesChannelContext $salesChannelContext, + ?OrderTransactionEntity $orderTransaction = null ): AbstractUnzerResource { if (!$unzerCustomer instanceof Customer) { return $unzerCustomer; @@ -97,16 +114,20 @@ public function hydrateExistingCustomer( $customer = $salesChannelContext->getCustomer(); if (!$customer) { - throw new RuntimeException('Could not determine the customer'); + throw new \RuntimeException('Could not determine the customer'); } - $billingAddress = $customer->getActiveBillingAddress(); + if ($orderTransaction !== null) { + $billingAddress = $orderTransaction->getOrder()->getBillingAddress(); + } else { + $billingAddress = $customer->getActiveBillingAddress(); + } if (!$billingAddress) { - throw new RuntimeException(sprintf('Could not determine the address for customer with number %s', $customer->getCustomerNumber())); + throw new \RuntimeException(\sprintf('Could not determine the address for customer with number %s', $customer->getCustomerNumber())); } - return $this->updateAdditionalDataToCustomer($unzerCustomer, $customer, $billingAddress); + return $this->updateAdditionalDataToCustomer($unzerCustomer, $customer, $billingAddress, $orderTransaction); } protected function getUnzerAddress(CustomerAddressEntity $shopwareAddress): Address @@ -114,12 +135,12 @@ protected function getUnzerAddress(CustomerAddressEntity $shopwareAddress): Addr $address = new Address(); $address->setCountry($shopwareAddress->getCountry() !== null ? $shopwareAddress->getCountry()->getIso() : null); $address->setState( - $shopwareAddress->getCountryState() !== null ? $shopwareAddress->getCountryState()->getShortCode() : null + $shopwareAddress->getCountryState() !== null ? $shopwareAddress->getCountryState()->getShortCode() : '' ); $address->setZip($shopwareAddress->getZipcode()); $address->setStreet($shopwareAddress->getStreet()); $address->setCity($shopwareAddress->getCity()); - $address->setName(sprintf('%s %s', $shopwareAddress->getFirstName(), $shopwareAddress->getLastName())); + $address->setName(\sprintf('%s %s', $shopwareAddress->getFirstName(), $shopwareAddress->getLastName())); return $address; } @@ -138,7 +159,8 @@ protected function addAdditionalDataToCustomer( protected function updateAdditionalDataToCustomer( Customer $unzerCustomer, CustomerEntity $customer, - CustomerAddressEntity $billingAddress + CustomerAddressEntity|OrderAddressEntity $billingAddress, + ?OrderTransactionEntity $orderTransaction = null ): Customer { $unzerBillingAddress = $unzerCustomer->getBillingAddress(); @@ -168,8 +190,32 @@ protected function updateAdditionalDataToCustomer( $unzerCustomer->setBirthDate($birthdate); } + $updatedCompany = false; if ($unzerCustomer->getCompany() !== $billingAddress->getCompany()) { $unzerCustomer->setCompany($billingAddress->getCompany()); + $updatedCompany = true; + } + + if (empty($unzerCustomer->getCompany())) { + $unzerCustomer->setCompanyInfo(null); + } else { + $companyInfo = $unzerCustomer->getCompanyInfo(); + if (empty($companyInfo)) { + $companyInfo = new CompanyInfo(); + } + if (empty($companyInfo->getCompanyType()) || $updatedCompany) { + $companyInfo->setCompanyType('Company Type'); + } +// if (empty($companyInfo->getRegistrationType()) || $updatedCompany) { +// $companyInfo->setRegistrationType('not_registered'); +// } + if (empty($companyInfo->getFunction()) || $updatedCompany) { + $companyInfo->setFunction('OWNER'); + } + if (empty($companyInfo->getCommercialSector()) || $updatedCompany) { + $companyInfo->setCommercialSector('OTHER'); + } + $unzerCustomer->setCompanyInfo($companyInfo); } if ($unzerBillingAddress->getStreet() !== $billingAddress->getStreet()) { @@ -189,7 +235,20 @@ protected function updateAdditionalDataToCustomer( } $unzerCustomer->setBillingAddress($unzerBillingAddress); - $this->updateShippingAddress($unzerCustomer, $customer->getActiveShippingAddress(), $billingAddress->getId()); + + $shippingAddress = $orderTransaction !== null ? $orderTransaction->getOrder()?->getDeliveries()?->first()?->getShippingOrderAddress() : $customer->getActiveShippingAddress(); + $this->updateShippingAddress($unzerCustomer, $shippingAddress, $billingAddress->getId()); + + $languageCriteria = new Criteria([$customer->getLanguageId()]); + $languageCriteria->addAssociation('locale'); + /** @var LanguageEntity $language */ + $language = $this->languageRepository->search($languageCriteria, Context::createDefaultContext())->first(); + if ($language !== null) { + if ($locale = $language->getLocale()) { + $localCode = $locale->getCode(); + $unzerCustomer->setLanguage(strtolower(substr($localCode, 0, 2))); + } + } return $unzerCustomer; } @@ -209,7 +268,7 @@ protected function getBirthDate(CustomerEntity $customer): ?string return $customer->getBirthday() !== null ? $customer->getBirthday()->format('Y-m-d') : null; } - private function updateShippingAddress(Customer $unzerCustomer, ?CustomerAddressEntity $shippingAddress, string $billingAddressId): void + private function updateShippingAddress(Customer $unzerCustomer, OrderAddressEntity|CustomerAddressEntity|null $shippingAddress, string $billingAddressId): void { $unzerShippingAddress = $unzerCustomer->getShippingAddress(); diff --git a/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydratorInterface.php b/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydratorInterface.php index 71133f5f..ee145f8a 100644 --- a/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydratorInterface.php +++ b/src/Components/ResourceHydrator/CustomerResourceHydrator/CustomerResourceHydratorInterface.php @@ -4,12 +4,17 @@ namespace UnzerPayment6\Components\ResourceHydrator\CustomerResourceHydrator; +use Shopware\Core\Checkout\Customer\CustomerEntity; +use Shopware\Core\Checkout\Order\Aggregate\OrderAddress\OrderAddressEntity; +use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; use Shopware\Core\System\SalesChannel\SalesChannelContext; use UnzerSDK\Resources\AbstractUnzerResource; interface CustomerResourceHydratorInterface { - public function hydrateObject(string $paymentMethodId, SalesChannelContext $channelContext): AbstractUnzerResource; + public function getShopCustomerId(CustomerEntity $customer, ?OrderAddressEntity $orderBillingAddress = null): string; - public function hydrateExistingCustomer(AbstractUnzerResource $unzerCustomer, SalesChannelContext $salesChannelContext): AbstractUnzerResource; + public function hydrateObject(string $paymentMethodId, SalesChannelContext $channelContext, ?OrderTransactionEntity $orderTransaction = null): AbstractUnzerResource; + + public function hydrateExistingCustomer(AbstractUnzerResource $unzerCustomer, SalesChannelContext $salesChannelContext, ?OrderTransactionEntity $orderTransaction = null): AbstractUnzerResource; } diff --git a/src/Components/ResourceHydrator/MetadataResourceHydrator.php b/src/Components/ResourceHydrator/MetadataResourceHydrator.php index 55cba363..f5580cd8 100755 --- a/src/Components/ResourceHydrator/MetadataResourceHydrator.php +++ b/src/Components/ResourceHydrator/MetadataResourceHydrator.php @@ -16,15 +16,19 @@ class MetadataResourceHydrator implements ResourceHydratorInterface { - /** @var string */ + /** + * @var string + */ private $shopwareVersion; - /** @var EntityRepository */ + /** + * @var EntityRepository + */ private $pluginRepository; public function __construct(string $shopwareVersion, EntityRepository $pluginRepository) { - $this->shopwareVersion = $shopwareVersion; + $this->shopwareVersion = $shopwareVersion; $this->pluginRepository = $pluginRepository; } @@ -46,6 +50,11 @@ public function hydrateObject( return $unzerMetadata; } + public function setIsExpress(Metadata $unzerMetadata, bool $isExpress): void + { + $unzerMetadata->addMetadata('isExpress', $isExpress ? '1' : '0'); + } + protected function getPluginData(Context $context): ?PluginEntity { $pluginSearchCriteria = new Criteria(); diff --git a/src/Components/ResourceHydrator/PaymentResourceHydrator/PaymentResourceHydrator.php b/src/Components/ResourceHydrator/PaymentResourceHydrator/PaymentResourceHydrator.php index 9c31a6cc..ab40ab3e 100644 --- a/src/Components/ResourceHydrator/PaymentResourceHydrator/PaymentResourceHydrator.php +++ b/src/Components/ResourceHydrator/PaymentResourceHydrator/PaymentResourceHydrator.php @@ -4,13 +4,10 @@ namespace UnzerPayment6\Components\ResourceHydrator\PaymentResourceHydrator; -use DateTimeImmutable; use Psr\Log\LoggerInterface; use Shopware\Core\Checkout\Document\DocumentEntity; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; use Shopware\Core\Checkout\Order\OrderEntity; -use stdClass; -use Throwable; use UnzerPayment6\UnzerPayment6; use UnzerSDK\Resources\EmbeddedResources\Amount; use UnzerSDK\Resources\Payment; @@ -23,35 +20,31 @@ use UnzerSDK\Resources\TransactionTypes\Shipment; use UnzerSDK\Unzer; -class PaymentResourceHydrator implements PaymentResourceHydratorInterface +readonly class PaymentResourceHydrator implements PaymentResourceHydratorInterface { private const TRANSACTION_TYPE_AUTHORIZATION = 'authorization'; - private const TRANSACTION_TYPE_CANCELLATION = 'cancellation'; - private const TRANSACTION_TYPE_CHARGE = 'charge'; - private const TRANSACTION_TYPE_SHIPMENT = 'shipment'; - private const TRANSACTION_TYPE_REFUND = 'refund'; + private const TRANSACTION_TYPE_CANCELLATION = 'cancellation'; + private const TRANSACTION_TYPE_CHARGE = 'charge'; + private const TRANSACTION_TYPE_SHIPMENT = 'shipment'; + private const TRANSACTION_TYPE_REFUND = 'refund'; - /** @var LoggerInterface */ - protected $logger; - - public function __construct(LoggerInterface $logger) + public function __construct(private LoggerInterface $logger) { - $this->logger = $logger; } public function hydrateArray(Payment $payment, OrderTransactionEntity $orderTransaction, Unzer $client): array { $decimalPrecision = $this->getDecimalPrecision($orderTransaction); - $data = $this->getBaseData($payment, $orderTransaction->getPaymentMethodId(), $decimalPrecision); + $data = $this->getBaseData($payment, $orderTransaction->getPaymentMethodId(), $decimalPrecision); try { $authorization = $payment->getAuthorization(); if ($authorization instanceof Authorization) { $data['transactions'][$this->getTransactionKey($authorization)] = $this->hydrateAuthorize($authorization, $decimalPrecision); - $data['descriptor'] = $authorization->getDescriptor(); + $data['descriptor'] = $authorization->getDescriptor(); } - } catch (Throwable $throwable) { + } catch (\Throwable $throwable) { $this->logResourceError($throwable); } @@ -61,7 +54,7 @@ public function hydrateArray(Payment $payment, OrderTransactionEntity $orderTran if ($payment->getMetadata() !== null) { $exposedMeta = $payment->getMetadata()->expose(); - if ($exposedMeta instanceof stdClass) { + if ($exposedMeta instanceof \stdClass) { $encoded = json_encode($exposedMeta); if (!$encoded) { @@ -70,7 +63,7 @@ public function hydrateArray(Payment $payment, OrderTransactionEntity $orderTran $exposedMeta = json_decode($encoded, true); - if (!is_array($exposedMeta) || empty($exposedMeta)) { + if (!\is_array($exposedMeta) || empty($exposedMeta)) { return $data; } } @@ -89,7 +82,7 @@ protected function getBaseData(Payment $payment, string $paymentMethodId, int $d $exposedPayment = $payment->expose(); - if ($exposedPayment instanceof stdClass) { + if ($exposedPayment instanceof \stdClass) { $encoded = json_encode($exposedPayment); if (!$encoded) { @@ -97,7 +90,7 @@ protected function getBaseData(Payment $payment, string $paymentMethodId, int $d } else { $exposedPayment = json_decode($encoded, true); - if (!is_array($exposedPayment) || empty($exposedPayment)) { + if (!\is_array($exposedPayment) || empty($exposedPayment)) { $exposedPayment = []; } } @@ -108,17 +101,17 @@ protected function getBaseData(Payment $payment, string $paymentMethodId, int $d [ 'state' => [ 'name' => $payment->getStateName(), - 'id' => $payment->getState(), + 'id' => $payment->getState(), ], - 'currency' => $payment->getCurrency(), - 'basket' => $payment->getBasket() ? $payment->getBasket()->expose() : null, - 'customer' => $payment->getCustomer() ? $payment->getCustomer()->expose() : null, - 'metadata' => [], + 'currency' => $payment->getCurrency(), + 'basket' => $payment->getBasket()?->expose(), + 'customer' => $payment->getCustomer()?->expose(), + 'metadata' => [], 'isShipmentAllowed' => $paymentType instanceof InvoiceSecured || $paymentType instanceof InstallmentSecured, - 'type' => $paymentType ? $paymentType->expose() : null, - 'amount' => $this->hydrateAmount($payment->getAmount(), $decimalPrecision), - 'transactions' => [], - 'paymentMethodId' => $paymentMethodId, + 'type' => $paymentType ? $paymentType->expose() : null, + 'amount' => $this->hydrateAmount($payment->getAmount(), $decimalPrecision), + 'transactions' => [], + 'paymentMethodId' => $paymentMethodId, ] ); } @@ -135,7 +128,7 @@ protected function hydrateTransactions(array &$data, Payment $payment, int $deci } foreach (array_reverse($data['transactions'], true) as $transaction) { - if (array_key_exists('shortId', $transaction) && !empty($transaction['shortId'])) { + if (\array_key_exists('shortId', $transaction) && !empty($transaction['shortId'])) { $data['shortId'] = $transaction['shortId']; break; @@ -147,25 +140,24 @@ protected function hydrateTransactions(array &$data, Payment $payment, int $deci protected function hydrateCharges(array &$data, Payment $payment, int $decimalPrecision): void { - /** @var Charge $lazyCharge */ + $isFirst = true; foreach ($payment->getCharges() as $lazyCharge) { try { - /** @var Charge $charge */ $charge = $payment->getCharge($lazyCharge->getId()); - } catch (Throwable $throwable) { + } catch (\Throwable $throwable) { $this->logResourceError($throwable); - continue; } - $data['transactions'][$this->getTransactionKey($charge)] = $this->hydrateCharge($charge, $decimalPrecision); + $data['transactions'][$this->getTransactionKey($charge)] = $this->hydrateCharge($charge, $decimalPrecision) + ['isFirst' => $isFirst]; + $isFirst = false; /** @var Cancellation $lazyCancellation */ foreach ($charge->getCancellations() as $lazyCancellation) { try { /** @var Cancellation $cancellation */ $cancellation = $charge->getCancellation($lazyCancellation->getId()); - } catch (Throwable $throwable) { + } catch (\Throwable $throwable) { $this->logResourceError($throwable); continue; @@ -186,7 +178,7 @@ protected function hydrateRefunds(array &$data, Payment $payment, int $decimalPr foreach ($payment->getRefunds() as $lazyRefund) { try { $cancellation = $client->fetchPaymentRefund($payment, $lazyRefund->getId()); - } catch (Throwable $throwable) { + } catch (\Throwable $throwable) { $this->logResourceError($throwable); continue; @@ -239,7 +231,7 @@ protected function hydrateShipments(array &$data, Payment $payment, int $decimal try { /** @var Shipment $shipment */ $shipment = $payment->getShipment($lazyShipment->getId()); - } catch (Throwable $throwable) { + } catch (\Throwable $throwable) { $this->logResourceError($throwable); continue; @@ -294,7 +286,7 @@ protected function validateIsShipmentAllowed(array &$data, ?OrderEntity $orderEn foreach ($filteredDocuments as $filteredDocument) { $documentConfig = $filteredDocument->getConfig(); - if (array_key_exists('documentNumber', $documentConfig) && !empty($documentConfig['documentNumber'])) { + if (\array_key_exists('documentNumber', $documentConfig) && !empty($documentConfig['documentNumber'])) { $data['isShipmentAllowed'] = true; } } @@ -304,10 +296,10 @@ protected function hydrateAmount(Amount $amount, int $decimalPrecision): array { return [ 'decimalPrecision' => $decimalPrecision, - 'total' => (int) round($amount->getTotal() * (10 ** $decimalPrecision)), - 'cancelled' => (int) round($amount->getCanceled() * (10 ** $decimalPrecision)), - 'charged' => (int) round($amount->getCharged() * (10 ** $decimalPrecision)), - 'remaining' => (int) round($amount->getRemaining() * (10 ** $decimalPrecision)), + 'total' => (int) round($amount->getTotal() * (10 ** $decimalPrecision)), + 'cancelled' => (int) round($amount->getCanceled() * (10 ** $decimalPrecision)), + 'charged' => (int) round($amount->getCharged() * (10 ** $decimalPrecision)), + 'remaining' => (int) round($amount->getRemaining() * (10 ** $decimalPrecision)), ]; } @@ -316,10 +308,10 @@ protected function getTransactionKey(AbstractTransactionType $item): string $date = ''; if (!empty($item->getDate())) { - $date = (new DateTimeImmutable($item->getDate()))->getTimestamp(); + $date = (new \DateTimeImmutable($item->getDate()))->getTimestamp(); } - return sprintf('%s_%s', $date, $item->getId()); + return \sprintf('%s_%s', $date, $item->getId()); } protected function hydrateCharge(Charge $charge, int $decimalPrecision): array @@ -327,9 +319,9 @@ protected function hydrateCharge(Charge $charge, int $decimalPrecision): array $data = $this->hydrateTransactionItem($charge, self::TRANSACTION_TYPE_CHARGE, $decimalPrecision); if ($charge->getCancelledAmount() !== null) { - $chargedAmount = (int) round($charge->getAmount() * (10 ** $decimalPrecision)); + $chargedAmount = (int) round($charge->getAmount() * (10 ** $decimalPrecision)); $cancelledAmount = (int) round($charge->getCancelledAmount() * (10 ** $decimalPrecision)); - $reducedAmount = $chargedAmount - $cancelledAmount; + $reducedAmount = $chargedAmount - $cancelledAmount; $data['processedAmount'] = $cancelledAmount; $data['remainingAmount'] = $reducedAmount; @@ -352,8 +344,8 @@ protected function hydrateAuthorize(Authorization $authorization, int $decimalPr $amount = $payment->getAmount(); $authorizedAmount = (int) round($authorization->getAmount() * (10 ** $decimalPrecision)); - $remainingAmount = (int) round($amount->getRemaining() * (10 ** $decimalPrecision)); - $reducedAmount = $authorizedAmount - $remainingAmount; + $remainingAmount = (int) round($amount->getRemaining() * (10 ** $decimalPrecision)); + $reducedAmount = $authorizedAmount - $remainingAmount; $data['processedAmount'] = $reducedAmount; $data['remainingAmount'] = $remainingAmount; @@ -370,12 +362,18 @@ protected function hydrateTransactionItem(AbstractTransactionType $item, string $amount = $item->getAmount(); } + $state = 'success'; + if ($item->isError()) { + $state = 'error'; + } + return [ - 'id' => $item->getId(), + 'id' => $item->getId(), 'shortId' => $item->getShortId(), - 'date' => $item->getDate(), - 'type' => $type, - 'amount' => (int) round(($amount * (10 ** $decimalPrecision))), + 'state' => $state, + 'date' => $item->getDate(), + 'type' => $type, + 'amount' => (int) round($amount * (10 ** $decimalPrecision)), ]; } @@ -393,13 +391,13 @@ protected function getDecimalPrecision(?OrderTransactionEntity $orderTransaction ); } - protected function logResourceError(Throwable $t): void + protected function logResourceError(\Throwable $t): void { $this->logger->error( - sprintf('Error while preparing payment data: %s', $t->getMessage()), + \sprintf('Error while preparing payment data: %s', $t->getMessage()), [ - 'file' => $t->getFile(), - 'line' => $t->getLine(), + 'file' => $t->getFile(), + 'line' => $t->getLine(), 'trace' => $t->getTraceAsString(), ] ); diff --git a/src/Components/ResourceHydrator/ResourceHydratorInterface.php b/src/Components/ResourceHydrator/ResourceHydratorInterface.php index 1ad23833..12849f10 100755 --- a/src/Components/ResourceHydrator/ResourceHydratorInterface.php +++ b/src/Components/ResourceHydrator/ResourceHydratorInterface.php @@ -12,7 +12,7 @@ interface ResourceHydratorInterface { /** - * @param null|AsyncPaymentTransactionStruct|OrderTransactionEntity $transaction + * @param AsyncPaymentTransactionStruct|OrderTransactionEntity|null $transaction */ public function hydrateObject(SalesChannelContext $channelContext, $transaction = null): AbstractUnzerResource; } diff --git a/src/Components/ShipService/ShipService.php b/src/Components/ShipService/ShipService.php index 10540f30..056a76d9 100644 --- a/src/Components/ShipService/ShipService.php +++ b/src/Components/ShipService/ShipService.php @@ -4,35 +4,27 @@ namespace UnzerPayment6\Components\ShipService; -use DateInterval; -use DateTime; -use DateTimeInterface; use Psr\Log\LoggerInterface; use Shopware\Core\Checkout\Document\Renderer\InvoiceRenderer; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; -use Shopware\Core\Checkout\Payment\Exception\InvalidTransactionException; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; -use UnzerPayment6\Components\BackwardsCompatibility\InvoiceGenerator; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\Struct\KeyPairContext; use UnzerPayment6\Components\TransactionStateHandler\TransactionStateHandlerInterface; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\Payment; -use UnzerSDK\Resources\PaymentTypes\InstallmentSecured; use UnzerSDK\Unzer; -class ShipService implements ShipServiceInterface +readonly class ShipService implements ShipServiceInterface { - - public function __construct( - private readonly ClientFactoryInterface $clientFactory, - private readonly TransactionStateHandlerInterface $transactionStateHandler, - private readonly EntityRepository $orderTransactionRepository, - private readonly LoggerInterface $logger + private ClientFactoryInterface $clientFactory, + private TransactionStateHandlerInterface $transactionStateHandler, + private EntityRepository $orderTransactionRepository, + private LoggerInterface $logger ) { } @@ -47,48 +39,48 @@ public function shipTransaction(string $orderTransactionId, Context $context): a throw PaymentException::invalidTransaction($orderTransactionId); } - $order = $transaction->getOrder(); - $documents = $transaction->getOrder()->getDocuments()->getElements(); + $order = $transaction->getOrder(); + $documents = $transaction->getOrder()->getDocuments()->getElements(); $invoiceNumber = null; - $documentDate = null; + $documentDate = null; foreach ($documents as $document) { if ($document->getDocumentType() && $document->getDocumentType()->getTechnicalName() === InvoiceRenderer::TYPE) { - $newDocumentDate = new DateTime($document->getConfig()['documentDate']); + $newDocumentDate = new \DateTime($document->getConfig()['documentDate']); if ($documentDate === null || $newDocumentDate->getTimestamp() > $documentDate->getTimestamp()) { - $documentDate = $newDocumentDate; + $documentDate = $newDocumentDate; $invoiceNumber = $document->getConfig()['documentNumber']; } } } if (!$documentDate) { - $this->logger->error(sprintf('Error while sending shipping notification for order [%s]: No DocumentDate for invoice found', $order->getOrderNumber())); + $this->logger->error(\sprintf('Error while sending shipping notification for order [%s]: No DocumentDate for invoice found', $order->getOrderNumber())); return [ - 'status' => false, + 'status' => false, 'message' => 'documentdate-missing-error', ]; } if (!$invoiceNumber) { - $this->logger->error(sprintf('Error while sending shipping notification for order [%s]: No invoiceNumber found', $order->getOrderNumber())); + $this->logger->error(\sprintf('Error while sending shipping notification for order [%s]: No invoiceNumber found', $order->getOrderNumber())); return [ - 'status' => false, + 'status' => false, 'message' => 'invoice-missing-error', ]; } - $client = $this->clientFactory->createClient(KeyPairContext::createFromOrderTransaction($transaction)); + $client = $this->clientFactory->createClient(KeyPairContext::createFromOrderTransaction($transaction)); $payment = $this->getPayment($orderTransactionId, $documentDate, $client); if ($payment === null) { - $this->logger->error(sprintf('Error while sending shipping notification for order [%s]: Payment could not be fetched', $order->getOrderNumber())); + $this->logger->error(\sprintf('Error while sending shipping notification for order [%s]: Payment could not be fetched', $order->getOrderNumber())); return [ - 'status' => false, + 'status' => false, 'message' => 'payment-missing-error', ]; } @@ -115,33 +107,14 @@ protected function getOrderTransaction(string $orderTransactionId, Context $cont return $this->orderTransactionRepository->search($criteria, $context)->first(); } - protected function getPayment(string $orderTransactionId, DateTimeInterface $documentDate, Unzer $client): ?Payment + protected function getPayment(string $orderTransactionId, \DateTimeInterface $documentDate, Unzer $client): ?Payment { try { $payment = $client->fetchPaymentByOrderId($orderTransactionId); - } catch (UnzerApiException $exception) { + } catch (UnzerApiException) { return null; } - $paymentType = $payment->getPaymentType(); - - if ($paymentType !== null && $paymentType instanceof InstallmentSecured) { - /** @var DateTime $invoiceDueDate */ - $invoiceDueDate = clone $documentDate; - /** @var DateInterval $dateInterval */ - $dateInterval = DateInterval::createFromDateString(sprintf('%s months', $paymentType->getNumberOfRates())); - $invoiceDueDate->add($dateInterval); - - $paymentType->setInvoiceDate($documentDate->format('Y-m-d')); - $paymentType->setInvoiceDueDate($invoiceDueDate->format('Y-m-d')); - - try { - $payment->setPaymentType($client->updatePaymentType($paymentType)); - } catch (UnzerApiException $exception) { - return null; - } - } - return $payment; } } diff --git a/src/Components/ShipService/ShipServiceInterface.php b/src/Components/ShipService/ShipServiceInterface.php index 8f193c77..fc23550d 100644 --- a/src/Components/ShipService/ShipServiceInterface.php +++ b/src/Components/ShipService/ShipServiceInterface.php @@ -4,8 +4,7 @@ namespace UnzerPayment6\Components\ShipService; -use RuntimeException; -use Shopware\Core\Checkout\Payment\Exception\InvalidTransactionException; +use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Context; use UnzerSDK\Exceptions\UnzerApiException; @@ -13,8 +12,8 @@ interface ShipServiceInterface { /** * @throws UnzerApiException - * @throws RuntimeException - * @throws InvalidTransactionException + * @throws \RuntimeException + * @throws PaymentException */ public function shipTransaction(string $orderTransactionId, Context $context): array; } diff --git a/src/Components/Storefront/ExtensionFactory.php b/src/Components/Storefront/ExtensionFactory.php new file mode 100644 index 00000000..a3b6eae8 --- /dev/null +++ b/src/Components/Storefront/ExtensionFactory.php @@ -0,0 +1,72 @@ +readConfig($salesChannelId); + $extension = new GooglePayPageExtension(); + $extension->setPublicConfig([ + 'merchantName' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_MERCHANT_NAME), + 'merchantId' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_MERCHANT_ID), + 'gatewayMerchantId' => $this->fetchGooglePayChannelId($salesChannelId), + 'countryCode' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_COUNTRY_CODE), + 'allowedCardNetworks' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_CARD_NETWORKS), + 'allowCreditCards' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_CREDIT_CARDS_ALLOWED), + 'allowPrepaidCards' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_PREPAID_CARDS_ALLOWED), + 'buttonColor' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_BUTTON_COLOR), + 'buttonSizeMode' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_BUTTON_SIZE_MODE), + ]); + + return $extension; + } + + public function getApplePayExtension(?string $salesChannelId = null): ApplePayV2PageExtension + { + $this->readConfig($salesChannelId); + $extension = new ApplePayV2PageExtension(); + $extension->setPublicConfig([ + 'countryCode' => 'DE', // TODO: Change based on what? + 'allowedCardNetworks' => $extension->getSupportedNetworks(), + 'merchantCapabilities' => $extension->getMerchantCapabilities(), + 'shopName' => $this->systemConfigService->get('core.basicInformation.shopName', $salesChannelId), + ]); + + return $extension; + } + + private function readConfig(?string $salesChannelId = null): void + { + $this->configData = $this->configReader->read($salesChannelId); + } + + private function fetchGooglePayChannelId($salesChannelId = null) + { + $publicKey = $this->configData->get(ConfigReader::CONFIG_KEY_PUBLIC_KEY); + $client = $this->clientFactory->createClientFromPublicKey($publicKey, (string) $salesChannelId); + + return UnzerGooglePayPaymentHandler::fetchChannelId($client); + } +} diff --git a/src/Components/Struct/Configuration.php b/src/Components/Struct/Configuration.php index 790b9a52..ada03017 100644 --- a/src/Components/Struct/Configuration.php +++ b/src/Components/Struct/Configuration.php @@ -6,18 +6,19 @@ class Configuration { - /** @var array */ - private $configuration; + private array $configuration; public function __construct(array $configuration) { $this->configuration = $configuration; } - /** @phpstan-ignore-next-line */ + /** + * @phpstan-ignore-next-line + */ public function get(string $key, $default = '') { - if (!array_key_exists($key, $this->configuration)) { + if (!\array_key_exists($key, $this->configuration)) { return $default; } diff --git a/src/Components/Struct/InstallmentSecured/InstallmentInfo.php b/src/Components/Struct/InstallmentSecured/InstallmentInfo.php index 11f90fbb..f7e72e6d 100644 --- a/src/Components/Struct/InstallmentSecured/InstallmentInfo.php +++ b/src/Components/Struct/InstallmentSecured/InstallmentInfo.php @@ -5,51 +5,78 @@ namespace UnzerPayment6\Components\Struct\InstallmentSecured; use Shopware\Core\Framework\Struct\Struct; -use stdClass; use UnzerSDK\Resources\InstalmentPlan; class InstallmentInfo extends Struct { - /** @var float */ + /** + * @var float + */ protected $totalAmount; - /** @var float */ + /** + * @var float + */ protected $totalInterest; - /** @var int */ + /** + * @var int + */ protected $numberOfRates; - /** @var string */ + /** + * @var string + */ protected $dayOfPurchase; - /** @var float */ + /** + * @var float + */ protected $totalPurchaseAmount; - /** @var float */ + /** + * @var float + */ protected $totalInterestAmount; - /** @var float */ + /** + * @var float + */ protected $effectiveInterestRate; - /** @var float */ + /** + * @var float + */ protected $nominalInterestRate; - /** @var float */ + /** + * @var float + */ protected $feeFirstRate; - /** @var float */ + /** + * @var float + */ protected $feePerRate; - /** @var float */ + /** + * @var float + */ protected $monthlyRate; - /** @var float */ + /** + * @var float + */ protected $lastRate; - /** @var string */ + /** + * @var string + */ protected $invoiceDate; - /** @var string */ + /** + * @var string + */ protected $invoiceDueDate; public function getTotalAmount(): float @@ -224,7 +251,7 @@ public function fromInstalmentPlan(InstalmentPlan $instalmentPlan): self { $values = $instalmentPlan->expose(); - if ($values instanceof stdClass) { + if ($values instanceof \stdClass) { $encoded = json_encode($values); if (!$encoded) { @@ -233,7 +260,7 @@ public function fromInstalmentPlan(InstalmentPlan $instalmentPlan): self $values = json_decode($encoded, true); - if (!is_array($values) || empty($values)) { + if (!\is_array($values) || empty($values)) { return $this; } } diff --git a/src/Components/Struct/KeyPairContext.php b/src/Components/Struct/KeyPairContext.php index 818248da..a14231ba 100644 --- a/src/Components/Struct/KeyPairContext.php +++ b/src/Components/Struct/KeyPairContext.php @@ -21,26 +21,26 @@ class KeyPairContext extends Struct private ?string $company; - public function __construct(string $salesChannelId, PaymentMethodEntity $paymentMethod, CurrencyEntity $currency, ?string $company) + public function __construct(string $salesChannelId, PaymentMethodEntity $paymentMethod, CurrencyEntity $currency, ?string $company = null) { $this->salesChannelId = $salesChannelId; - $this->paymentMethod = $paymentMethod; - $this->currency = $currency; - $this->company = $company; + $this->paymentMethod = $paymentMethod; + $this->currency = $currency; + $this->company = $company; } public static function createFromSalesChannelContext(SalesChannelContext $salesChannelContext): ?KeyPairContext { - if (!$salesChannelContext->getCustomer() || !$salesChannelContext->getCustomer()->getActiveBillingAddress()) { - return null; - } - - return new self( + $arguments = [ $salesChannelContext->getSalesChannelId(), $salesChannelContext->getPaymentMethod(), $salesChannelContext->getCurrency(), - $salesChannelContext->getCustomer()->getActiveBillingAddress()->getCompany() - ); + ]; + if ($salesChannelContext->getCustomer() && $salesChannelContext->getCustomer()->getActiveBillingAddress()) { + $arguments[] = $salesChannelContext->getCustomer()->getActiveBillingAddress()->getCompany(); + } + + return new self(...$arguments); } public static function createFromSalesChannel(?SalesChannelEntity $salesChannel): ?KeyPairContext @@ -52,8 +52,7 @@ public static function createFromSalesChannel(?SalesChannelEntity $salesChannel) return new self( $salesChannel->getId(), $salesChannel->getPaymentMethod(), - $salesChannel->getCurrency(), - null + $salesChannel->getCurrency() ); } diff --git a/src/Components/Struct/PageExtension/Account/PaymentMethodPageExtension.php b/src/Components/Struct/PageExtension/Account/PaymentMethodPageExtension.php index 0f5e99be..b7b84430 100644 --- a/src/Components/Struct/PageExtension/Account/PaymentMethodPageExtension.php +++ b/src/Components/Struct/PageExtension/Account/PaymentMethodPageExtension.php @@ -11,11 +11,12 @@ class PaymentMethodPageExtension extends Struct { public const EXTENSION_NAME = 'unzerPaymentMethod'; - /** @var UnzerPaymentDeviceEntity[] */ - protected $savedDevices = []; + /** + * @var UnzerPaymentDeviceEntity[] + */ + protected array $savedDevices = []; - /** @var bool */ - protected $deviceRemoved = false; + protected bool $deviceRemoved = false; public function addPaymentDevices(array $paymentDevices): self { @@ -34,8 +35,6 @@ public function getSavedDevices(): array /** * @param UnzerPaymentDeviceEntity[] $savedDevices - * - * @return PaymentMethodPageExtension */ public function setSavedDevices(array $savedDevices): self { diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayPageExtension.php deleted file mode 100644 index 8c94786f..00000000 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayPageExtension.php +++ /dev/null @@ -1,20 +0,0 @@ -supportedNetworks; - } -} diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayV2PageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayV2PageExtension.php index 679ee700..56fc41f9 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayV2PageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/ApplePayV2PageExtension.php @@ -10,16 +10,34 @@ class ApplePayV2PageExtension extends Struct { public const EXTENSION_NAME = 'unzerApplePayV2'; - /** @var string[] */ - protected $supportedNetworks = ['masterCard', 'visa']; - protected $merchantCapabilities = ['supports3DS']; + /** + * @var string[] + */ + protected array $supportedNetworks = ['masterCard', 'visa']; + + protected array $merchantCapabilities = ['supports3DS']; + + protected array $publicConfig = []; public function getSupportedNetworks(): array { return $this->supportedNetworks; } + public function getMerchantCapabilities(): array { return $this->merchantCapabilities; } + + public function getPublicConfig(): array + { + return $this->publicConfig; + } + + public function setPublicConfig(array $publicConfig): self + { + $this->publicConfig = $publicConfig; + + return $this; + } } diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/CreditCardPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/CreditCardPageExtension.php index 8337dad8..f786ae14 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/CreditCardPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/CreditCardPageExtension.php @@ -11,8 +11,10 @@ class CreditCardPageExtension extends Struct { public const EXTENSION_NAME = 'unzerCreditCard'; - /** @var UnzerPaymentDeviceEntity[] */ - protected $creditCards = []; + /** + * @var UnzerPaymentDeviceEntity[] + */ + protected array $creditCards = []; public function addCreditCard(UnzerPaymentDeviceEntity $creditCard): self { @@ -31,8 +33,6 @@ public function getCreditCards(): array /** * @param UnzerPaymentDeviceEntity[] $creditCards - * - * @return CreditCardPageExtension */ public function setCreditCards(array $creditCards): self { diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitPageExtension.php index ff592b06..4b0a4b0e 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitPageExtension.php @@ -11,8 +11,10 @@ class DirectDebitPageExtension extends Struct { public const EXTENSION_NAME = 'unzerDirectDebit'; - /** @var UnzerPaymentDeviceEntity[] */ - protected $directDebitDevices = []; + /** + * @var UnzerPaymentDeviceEntity[] + */ + protected array $directDebitDevices = []; public function addDirectDebitDevice(UnzerPaymentDeviceEntity $directDebitDevice): self { diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitSecuredPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitSecuredPageExtension.php index cde7adc5..dffe7dc2 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitSecuredPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/DirectDebitSecuredPageExtension.php @@ -11,10 +11,14 @@ class DirectDebitSecuredPageExtension extends Struct { public const EXTENSION_NAME = 'unzerDirectDebitSecured'; - /** @var UnzerPaymentDeviceEntity[] */ - protected $directDebitDevices = []; + /** + * @var UnzerPaymentDeviceEntity[] + */ + protected array $directDebitDevices = []; - /** @var bool */ + /** + * @var bool + */ protected $displayDirectDebitDeviceSelection; public function addDirectDebitDevice(UnzerPaymentDeviceEntity $directDebitDevice): self diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/FraudPreventionPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/FraudPreventionPageExtension.php deleted file mode 100644 index 94cc8b70..00000000 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/FraudPreventionPageExtension.php +++ /dev/null @@ -1,25 +0,0 @@ -fraudPreventionSessionId; - } - - public function setFraudPreventionSessionId(string $fraudPreventionSessionId): void - { - $this->fraudPreventionSessionId = $fraudPreventionSessionId; - } -} diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/InstallmentSecuredPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/InstallmentSecuredPageExtension.php index e93a9c8b..9e42da83 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/InstallmentSecuredPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/InstallmentSecuredPageExtension.php @@ -10,13 +10,10 @@ class InstallmentSecuredPageExtension extends Struct { public const EXTENSION_NAME = 'unzerInstallmentSecured'; - /** @var float */ - private $amount = 0.0; + private float $amount = 0.0; - /** @var string */ - private $currency = 'EUR'; + private string $currency = 'EUR'; - /** @var string */ private $orderDate = ''; public function getAmount(): float diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/PayPalPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/PayPalPageExtension.php index 2efd9523..d50fe650 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/PayPalPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/PayPalPageExtension.php @@ -11,8 +11,10 @@ class PayPalPageExtension extends Struct { public const EXTENSION_NAME = 'unzerPayPal'; - /** @var UnzerPaymentDeviceEntity[] */ - protected $payPalAccounts = []; + /** + * @var UnzerPaymentDeviceEntity[] + */ + protected array $payPalAccounts = []; protected array $publicConfig = []; @@ -45,8 +47,6 @@ public function getPayPalAccounts(): array /** * @param UnzerPaymentDeviceEntity[] $payPalAccounts - * - * @return PayPalPageExtension */ public function setPayPalAccounts(array $payPalAccounts): self { diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/PaymentFramePageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/PaymentFramePageExtension.php index ef451af9..93d9974f 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/PaymentFramePageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/PaymentFramePageExtension.php @@ -10,11 +10,9 @@ class PaymentFramePageExtension extends Struct { public const EXTENSION_NAME = 'unzerPaymentFrame'; - /** @var string */ - private $paymentFrame; + private string $paymentFrame; - /** @var string */ - private $shopName; + private string $shopName; public function getPaymentFrame(): string { diff --git a/src/Components/Struct/PageExtension/Checkout/Confirm/UnzerDataPageExtension.php b/src/Components/Struct/PageExtension/Checkout/Confirm/UnzerDataPageExtension.php index 0bbac2b1..8cdf9118 100644 --- a/src/Components/Struct/PageExtension/Checkout/Confirm/UnzerDataPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/Confirm/UnzerDataPageExtension.php @@ -12,17 +12,13 @@ class UnzerDataPageExtension extends Struct { public const EXTENSION_NAME = 'unzerPaymentData'; - /** @var string */ - private $publicKey; + private string $publicKey; - /** @var string */ - private $locale; + private string $locale; - /** @var bool */ - private $showTestData; + private bool $showTestData; - /** @var null|Customer */ - private $unzerCustomer; + private ?Customer $unzerCustomer; public function getPublicKey(): string { diff --git a/src/Components/Struct/PageExtension/Checkout/FinishPageExtension.php b/src/Components/Struct/PageExtension/Checkout/FinishPageExtension.php index fa1bff90..2358b2af 100644 --- a/src/Components/Struct/PageExtension/Checkout/FinishPageExtension.php +++ b/src/Components/Struct/PageExtension/Checkout/FinishPageExtension.php @@ -11,8 +11,10 @@ class FinishPageExtension extends Struct { public const EXTENSION_NAME = 'unzerFinishPage'; - /** @var InstallmentInfo[] */ - protected $installmentInformation = []; + /** + * @var InstallmentInfo[] + */ + protected array $installmentInformation = []; public function getInstallmentInformation(): array { diff --git a/src/Components/Struct/TransferInformation/TransferInformation.php b/src/Components/Struct/TransferInformation/TransferInformation.php index 5298a7b3..64c76fc5 100644 --- a/src/Components/Struct/TransferInformation/TransferInformation.php +++ b/src/Components/Struct/TransferInformation/TransferInformation.php @@ -11,44 +11,38 @@ class TransferInformation extends Struct { - /** @var null|string */ - protected $iban; + protected ?string $iban; - /** @var null|string */ - protected $bic; + protected ?string $bic; - /** @var null|string */ - protected $holder; + protected ?string $holder; - /** @var null|string */ - protected $descriptor; + protected ?string $descriptor; - /** @var null|float */ - protected $amount; + protected ?float $amount; /** * @param Authorization|Charge $payment */ public function __construct(AbstractTransactionType $payment) { - $this->iban = $payment->getIban(); - $this->bic = $payment->getBic(); - $this->holder = $payment->getHolder(); + $this->iban = $payment->getIban(); + $this->bic = $payment->getBic(); + $this->holder = $payment->getHolder(); $this->descriptor = $payment->getDescriptor(); - /** @var float $amount */ - $amount = $payment->getAmount(); + $amount = $payment->getAmount(); $this->amount = round($amount, 2); } public function getEntityData(): array { return [ - 'iban' => $this->getIban(), - 'bic' => $this->getBic(), - 'holder' => $this->getHolder(), + 'iban' => $this->getIban(), + 'bic' => $this->getBic(), + 'holder' => $this->getHolder(), 'descriptor' => $this->getDescriptor(), - 'amount' => $this->getAmount(), + 'amount' => $this->getAmount(), ]; } diff --git a/src/Components/Struct/Webhook.php b/src/Components/Struct/Webhook.php index e3cbe8b3..0dc1273f 100644 --- a/src/Components/Struct/Webhook.php +++ b/src/Components/Struct/Webhook.php @@ -6,14 +6,11 @@ class Webhook { - /** @var string */ - private $event; + private string $event; - /** @var string */ - private $publicKey; + private string $publicKey; - /** @var string */ - private $retrieveUrl; + private string $retrieveUrl; public function __construct(string $jsonData) { @@ -24,8 +21,8 @@ public function fromJson(string $jsonData): void { $webhookData = json_decode($jsonData, true); - $this->event = $webhookData['event'] ?? ''; - $this->publicKey = $webhookData['publicKey'] ?? ''; + $this->event = $webhookData['event'] ?? ''; + $this->publicKey = $webhookData['publicKey'] ?? ''; $this->retrieveUrl = $webhookData['retrieveUrl'] ?? ''; } diff --git a/src/Components/TransactionSelectionHelper/TransactionSelectionHelper.php b/src/Components/TransactionSelectionHelper/TransactionSelectionHelper.php index 708ad24f..5a51ae20 100644 --- a/src/Components/TransactionSelectionHelper/TransactionSelectionHelper.php +++ b/src/Components/TransactionSelectionHelper/TransactionSelectionHelper.php @@ -41,15 +41,15 @@ public function getLatestTransaction(OrderTransactionCollection $transactions): continue; } - if (empty($latest) || (array_key_exists('timestamp', $latest) && $latest['timestamp'] < $transaction->getCreatedAt()->getTimestamp())) { + if (empty($latest) || (\array_key_exists('timestamp', $latest) && $latest['timestamp'] < $transaction->getCreatedAt()->getTimestamp())) { $latest = [ 'timestamp' => $transaction->getCreatedAt()->getTimestamp(), - 'id' => $transaction->getId(), + 'id' => $transaction->getId(), ]; } } - if (!empty($latest) && array_key_exists('id', $latest)) { + if (!empty($latest) && \array_key_exists('id', $latest)) { $latestTransaction = $transactions->get($latest['id']); if ($latestTransaction !== null) { @@ -64,14 +64,14 @@ public function getLatestTransaction(OrderTransactionCollection $transactions): protected function filterByPaymentMethod(OrderTransactionCollection $transactions): OrderTransactionCollection { return $transactions->filter(static function (OrderTransactionEntity $transaction) { - return in_array($transaction->getPaymentMethodId(), PaymentInstaller::PAYMENT_METHOD_IDS); + return \in_array($transaction->getPaymentMethodId(), PaymentInstaller::PAYMENT_METHOD_IDS, true); }); } protected function filterByState(OrderTransactionCollection $transactions): OrderTransactionCollection { return $transactions->filter(static function (OrderTransactionEntity $transaction) { - if (null === $transaction->getStateMachineState() || null === $transaction->getPaymentMethod()) { + if ($transaction->getStateMachineState() === null || $transaction->getPaymentMethod() === null) { return false; } diff --git a/src/Components/TransactionStateHandler/TransactionStateHandler.php b/src/Components/TransactionStateHandler/TransactionStateHandler.php index 64aab9c1..77a7a89d 100755 --- a/src/Components/TransactionStateHandler/TransactionStateHandler.php +++ b/src/Components/TransactionStateHandler/TransactionStateHandler.php @@ -5,7 +5,6 @@ namespace UnzerPayment6\Components\TransactionStateHandler; use Psr\Log\LoggerInterface; -use RuntimeException; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionDefinition; use Shopware\Core\Framework\Context; use Shopware\Core\System\StateMachine\Aggregation\StateMachineTransition\StateMachineTransitionActions; @@ -18,12 +17,12 @@ use UnzerSDK\Resources\Payment; use UnzerSDK\Resources\PaymentTypes\BasePaymentType; -class TransactionStateHandler implements TransactionStateHandlerInterface +readonly class TransactionStateHandler implements TransactionStateHandlerInterface { public function __construct( - private readonly StateMachineRegistry $stateMachineRegistry, - private readonly PaymentTransitionMapperFactory $transitionMapperFactory, - private readonly LoggerInterface $logger + private StateMachineRegistry $stateMachineRegistry, + private PaymentTransitionMapperFactory $transitionMapperFactory, + private LoggerInterface $logger ) { } @@ -36,24 +35,28 @@ public function transformTransactionState( Context $context ): void { if ($payment->getPaymentType() === null) { - $this->logger->error(sprintf('The payment has no payment type for transition mapping. TransactionId: %s', $transactionId), [ + $this->logger->error(\sprintf('The payment has no payment type for transition mapping. TransactionId: %s', $transactionId), [ 'payment' => $payment, ]); return; } - $transition = $this->getTargetTransition($payment); + $transition = $this->getTargetTransition($payment, $transactionId); if (empty($transition)) { $this->logger->error('Due to an empty transition, the FAIL transition is executed'); $this->executeTransition($transactionId, StateMachineTransitionActions::ACTION_FAIL, $context); - throw new RuntimeException('Invalid transition status'); + throw new \RuntimeException('Invalid transition status'); } + $this->logger->debug('Transaction state transition for ' . $payment->getId() . ': ' . $transition, ['orderTransactionId' => $transactionId, 'payment' => $payment]); $this->executeTransition($transactionId, $transition, $context); + if ($transition === StateMachineTransitionActions::ACTION_FAIL) { + throw new \RuntimeException('Payment ' . $payment->getId() . ' has a failed.'); + } } public function fail(string $transactionId, Context $context): void @@ -74,18 +77,18 @@ public function pay(string $transactionId, Context $context): void ); } - protected function getTargetTransition(Payment $payment): string + protected function getTargetTransition(Payment $payment, string $orderTransactionId): string { try { /** @var BasePaymentType $paymentType */ - $paymentType = $payment->getPaymentType(); + $paymentType = $payment->getPaymentType(); $transitionMapper = $this->transitionMapperFactory->getTransitionMapper($paymentType); - $transition = $transitionMapper->getTargetPaymentStatus($payment); - } catch (NoTransitionMapperFoundException | TransitionMapperException $exception) { + $transition = $transitionMapper->getTargetPaymentStatus($payment, $orderTransactionId); + } catch (NoTransitionMapperFoundException|TransitionMapperException $exception) { $this->logger->error($exception->getMessage(), [ - 'code' => $exception->getCode(), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), 'trace' => $exception->getTraceAsString(), ]); } @@ -105,14 +108,14 @@ protected function executeTransition(string $transactionId, string $transition, ), $context ); - } catch (IllegalTransitionException $exception) { + } catch (IllegalTransitionException) { // false positive handling (state to state) like open -> open, paid -> paid, etc. } // If payment should be in state "paid", `do_pay` is given -> finalize state if ($transition === StateMachineTransitionActions::ACTION_DO_PAY) { $this->logger->debug( - sprintf( + \sprintf( '%s transition is executed as fallback for %s', StateMachineTransitionActions::ACTION_PAID, StateMachineTransitionActions::ACTION_DO_PAY diff --git a/src/Components/TransactionStateHandler/TransactionStateHandlerInterface.php b/src/Components/TransactionStateHandler/TransactionStateHandlerInterface.php index a1d4a225..dccc748d 100755 --- a/src/Components/TransactionStateHandler/TransactionStateHandlerInterface.php +++ b/src/Components/TransactionStateHandler/TransactionStateHandlerInterface.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\Components\TransactionStateHandler; -use RuntimeException; use Shopware\Core\Framework\Context; use UnzerSDK\Resources\Payment; @@ -13,7 +12,7 @@ interface TransactionStateHandlerInterface /** * Determines transition by payment and executes the transition if valid * - * @throws RuntimeException + * @throws \RuntimeException */ public function transformTransactionState( string $transactionId, diff --git a/src/Components/UnzerPaymentDebugHandler.php b/src/Components/UnzerPaymentDebugHandler.php index d8f19ac6..b840c2f5 100644 --- a/src/Components/UnzerPaymentDebugHandler.php +++ b/src/Components/UnzerPaymentDebugHandler.php @@ -7,14 +7,10 @@ use Psr\Log\LoggerInterface; use UnzerSDK\Interfaces\DebugHandlerInterface; -class UnzerPaymentDebugHandler implements DebugHandlerInterface +readonly class UnzerPaymentDebugHandler implements DebugHandlerInterface { - /** @var LoggerInterface */ - private $logger; - - public function __construct(LoggerInterface $logger) + public function __construct(private LoggerInterface $logger) { - $this->logger = $logger; } /** diff --git a/src/Components/UnzerUtil/UnzerTransactionUtil.php b/src/Components/UnzerUtil/UnzerTransactionUtil.php index 78f59c15..1a83bba1 100644 --- a/src/Components/UnzerUtil/UnzerTransactionUtil.php +++ b/src/Components/UnzerUtil/UnzerTransactionUtil.php @@ -1,8 +1,7 @@ -addAssociations([ + 'order', + 'order.billingAddress.country', + 'order.currency', + 'order.documents.documentType', + 'paymentMethod', + 'order.orderCustomer.customer', + 'order.deliveries.shippingMethod.translated', + 'order.deliveries.shippingOrderAddress.country', + 'order.lineItems.product.manufacturer', + 'order.lineItems.cover.url', + 'order.lineItems.calculatedPrices.taxes', + ]); + + return $this->orderTransactionRepository->search($criteria, $context)->first(); } /** @@ -48,11 +68,12 @@ public function getOrderTransactionFromOrder(OrderEntity $orderEntity, Context $ 'order.documents.documentType', 'paymentMethod', ]); + return $this->orderTransactionRepository->search($criteria, $context)->first(); } /** - * @throws Exception + * @throws \Exception */ public function captureOrder(OrderEntity $order, Context $context): bool { @@ -72,19 +93,22 @@ public function captureOrder(OrderEntity $order, Context $context): bool $context ); } catch (UnzerApiException $e) { - throw new Exception($e->getMerchantMessage() ?: $e->getClientMessage()); + throw new \Exception($e->getMerchantMessage() ?: $e->getClientMessage()); } return true; } - public function refundOrder(OrderEntity $order, Context $context) + /** + * @throws \Exception + */ + public function refundOrder(OrderEntity $order, Context $context): void { $this->logger->info('Refunding order', ['order' => $order->getId()]); $orderTransaction = $this->getOrderTransactionFromOrder($order, $context); if ($orderTransaction === null) { - return false; + return; } $client = $this->clientFactory->createClient(KeyPairContext::createFromOrderTransaction($orderTransaction)); @@ -99,7 +123,7 @@ public function refundOrder(OrderEntity $order, Context $context) $this->cancelService->cancelChargeById( $orderTransaction->getId(), $charge->getId(), - $charge->getAmount()-$charge->getCancelledAmount(), + $charge->getAmount() - $charge->getCancelledAmount(), null, $context ); @@ -128,10 +152,40 @@ public function refundOrder(OrderEntity $order, Context $context) $context ); } catch (UnzerApiException $e) { - throw new Exception($e->getMerchantMessage() ?: $e->getClientMessage()); + throw new \Exception($e->getMerchantMessage() ?: $e->getClientMessage()); } + } + public static function fetchPaymentFromOrderTransaction(OrderTransactionEntity $orderTransaction, Unzer $client): Payment + { + try { + $payment = $client->fetchPaymentByOrderId($orderTransaction->getId()); + // not sure what this is for - we'll leave it here for now: + $payment = $client->fetchPayment($payment); + } catch (UnzerApiException $e) { + $paymentId = $orderTransaction->getCustomFields()[CustomFieldInstaller::UNZER_PAYMENT_PAYMENT_ID_KEY] ?? null; + if ($paymentId) { + $payment = $client->fetchPayment($paymentId); + } else { + throw new \RuntimeException('no payment found'); + } + } + + return $payment; } + public function updateOrderTransactionStatus(Unzer $client, OrderTransactionEntity $orderTransaction, Context $context): void + { + try { + $payment = self::fetchPaymentFromOrderTransaction($orderTransaction, $client); + $this->transactionStateHandler->transformTransactionState( + $orderTransaction->getId(), + $payment, + $context + ); + } catch (\Throwable $e) { + $this->logger->error('error updating transaction state from util: ' . $e->getMessage(), ['trace'=>$e->getTraceAsString()]); + } + } -} \ No newline at end of file +} diff --git a/src/Components/Validator/AutomaticShippingValidator.php b/src/Components/Validator/AutomaticShippingValidator.php index 26473c58..266f40a9 100644 --- a/src/Components/Validator/AutomaticShippingValidator.php +++ b/src/Components/Validator/AutomaticShippingValidator.php @@ -11,11 +11,11 @@ use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\TransactionSelectionHelper\TransactionSelectionHelperInterface; -class AutomaticShippingValidator implements AutomaticShippingValidatorInterface +readonly class AutomaticShippingValidator implements AutomaticShippingValidatorInterface { public function __construct( - private readonly ConfigReaderInterface $configReader, - private readonly TransactionSelectionHelperInterface $transactionSelectionHelper + private ConfigReaderInterface $configReader, + private TransactionSelectionHelperInterface $transactionSelectionHelper ) { } @@ -24,12 +24,12 @@ public function __construct( */ public function shouldSendAutomaticShipping(OrderEntity $orderEntity, StateMachineStateEntity $deliveryState): bool { - $config = $this->configReader->read($orderEntity->getSalesChannelId()); + $config = $this->configReader->read($orderEntity->getSalesChannelId()); $configuredStatusId = $config->get(ConfigReader::CONFIG_KEY_SHIPPING_STATUS); $transaction = $this->transactionSelectionHelper->getBestUnzerTransaction($orderEntity); - if (!$transaction || !in_array($transaction->getPaymentMethodId(), self::HANDLED_PAYMENT_METHODS, false)) { + if (!$transaction || !\in_array($transaction->getPaymentMethodId(), self::HANDLED_PAYMENT_METHODS, true)) { return false; } diff --git a/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php b/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php index 3be95d05..e4b4c420 100644 --- a/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php +++ b/src/Components/WebhookHandler/PaymentStatusWebhookHandler.php @@ -38,12 +38,12 @@ public function supports(Webhook $webhook, SalesChannelContext $context): bool public function execute(Webhook $webhook, SalesChannelContext $context): void { - $client = $this->clientFactory->createClientFromPublicKey($webhook->getPublicKey(), $context->getSalesChannelId()); + $client = $this->clientFactory->createClientFromPublicKey($webhook->getPublicKey(), $context->getSalesChannelId()); $payment = $client->getResourceService()->fetchResourceByUrl($webhook->getRetrieveUrl()); if (!$payment instanceof Payment) { $this->logger->error( - sprintf( + \sprintf( 'Webhook could not be executed due to missing payment for retrieveUrl: %s', $webhook->getRetrieveUrl() ) @@ -56,7 +56,7 @@ public function execute(Webhook $webhook, SalesChannelContext $context): void if ($transaction === null) { $this->logger->error( - sprintf( + \sprintf( 'Webhook could not be executed due to missing transaction for payment: %s', $payment->getOrderId() ) diff --git a/src/Components/WebhookRegistrator/WebhookRegistrator.php b/src/Components/WebhookRegistrator/WebhookRegistrator.php index 10c75418..a41b5cbe 100644 --- a/src/Components/WebhookRegistrator/WebhookRegistrator.php +++ b/src/Components/WebhookRegistrator/WebhookRegistrator.php @@ -15,20 +15,17 @@ use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\Router; -use Throwable; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerSDK\Exceptions\UnzerApiException; class WebhookRegistrator implements WebhookRegistratorInterface { - public const EXIT_CODE_SUCCESS = 0; - public const EXIT_CODE_API_ERROR = 1; + public const EXIT_CODE_SUCCESS = 0; + public const EXIT_CODE_API_ERROR = 1; public const EXIT_CODE_UNKNOWN_ERROR = 2; - public const EXIT_CODE_INVALID_HOST = 3; - - /** @var null|RequestContext */ - protected $context; + public const EXIT_CODE_INVALID_HOST = 3; + protected ?RequestContext $context; public function __construct( private readonly ClientFactoryInterface $clientFactory, @@ -44,10 +41,10 @@ public function registerWebhook(RequestDataBag $salesChannelDomains): array /** @var RequestDataBag $salesChannelDomain */ foreach ($salesChannelDomains as $salesChannelDomain) { - $salesChannelId = $salesChannelDomain->get('salesChannelId'); + $salesChannelId = $salesChannelDomain->get('salesChannelId'); $preparationResult = $this->prepare($salesChannelDomain); - $domainUrl = $salesChannelDomain->get('url', ''); - $privateKey = $salesChannelDomain->get('privateKey'); + $domainUrl = $salesChannelDomain->get('url', ''); + $privateKey = $salesChannelDomain->get('privateKey'); if (!empty($preparationResult)) { $returnData[$preparationResult['key']] = $preparationResult['value']; @@ -57,7 +54,7 @@ public function registerWebhook(RequestDataBag $salesChannelDomains): array try { $relativePath = $this->router->generate('frontend.unzer.webhook.execute', [], UrlGeneratorInterface::ABSOLUTE_PATH); - $url = $domainUrl . $relativePath; + $url = $domainUrl . $relativePath; $result = $this->clientFactory ->createClientFromPrivateKey($privateKey, $salesChannelId) @@ -65,24 +62,24 @@ public function registerWebhook(RequestDataBag $salesChannelDomains): array $returnData[$domainUrl] = [ 'success' => true, - 'data' => $result, + 'data' => $result, 'message' => 'unzer-payment-settings.webhook.register.done', ]; - $this->logger->info(sprintf('Webhooks registered for domain %s', $domainUrl)); - } catch (UnzerApiException | Throwable $exception) { + $this->logger->info(\sprintf('Webhooks registered for domain %s', $domainUrl)); + } catch (UnzerApiException|\Throwable $exception) { $returnData[$domainUrl] = [ 'success' => false, 'message' => 'unzer-payment-settings.webhook.register.error', ]; $this->logger->error( - sprintf('Webhook registration failed for domain %s', $domainUrl), + \sprintf('Webhook registration failed for domain %s', $domainUrl), [ 'message' => $exception->getMessage(), - 'code' => $exception->getCode(), - 'file' => $exception->getFile(), - 'trace' => $exception->getTraceAsString(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'trace' => $exception->getTraceAsString(), ] ); } @@ -104,20 +101,20 @@ public function clearWebhooks(string $privateKey, array $webhookIds): array 'message' => 'unzer-payment-settings.webhook.clear.done', ]; - $this->logger->info(sprintf('Webhook %s (%s) deleted!', $webhookId, $data['url'])); - } catch (UnzerApiException | Throwable $exception) { + $this->logger->info(\sprintf('Webhook %s (%s) deleted!', $webhookId, $data['url'])); + } catch (UnzerApiException|\Throwable $exception) { $returnData[$data['url']] = [ 'success' => false, 'message' => 'unzer-payment-settings.webhook.clear.error', ]; $this->logger->error( - sprintf('Webhook deletion failed for %s (%s)!', $webhookId, $data['url']), + \sprintf('Webhook deletion failed for %s (%s)!', $webhookId, $data['url']), [ 'message' => $exception->getMessage(), - 'code' => $exception->getCode(), - 'file' => $exception->getFile(), - 'trace' => $exception->getTraceAsString(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'trace' => $exception->getTraceAsString(), ] ); } @@ -129,13 +126,13 @@ public function clearWebhooks(string $privateKey, array $webhookIds): array public function getWebhooks(string $privateKey): array { $webhooks = $this->clientFactory->createClientFromPrivateKey($privateKey)->fetchAllWebhooks(); - $data = []; + $data = []; foreach ($webhooks as $webhook) { $data[] = [ - 'id' => $webhook->getId(), + 'id' => $webhook->getId(), 'event' => $webhook->getEvent(), - 'url' => $webhook->getUrl(), + 'url' => $webhook->getUrl(), ]; } @@ -146,7 +143,7 @@ protected function prepare(DataBag $salesChannelDomain): array { if (!$salesChannelDomain->has('id') || !$salesChannelDomain->has('url')) { return [ - 'key' => 'missing', + 'key' => 'missing', 'value' => [ 'success' => false, 'message' => 'unzer-payment-settings.webhook.missing.fields', @@ -161,7 +158,7 @@ protected function prepare(DataBag $salesChannelDomain): array if ($salesChannelEntity === null) { return [ - 'key' => $salesChannelDomain->get('url', ''), + 'key' => $salesChannelDomain->get('url', ''), 'value' => [ 'success' => false, 'message' => 'unzer-payment-settings.webhook.notFound.salesChannel', @@ -173,7 +170,7 @@ protected function prepare(DataBag $salesChannelDomain): array if (!$this->context) { return [ - 'key' => $salesChannelDomain->get('url', ''), + 'key' => $salesChannelDomain->get('url', ''), 'value' => [ 'success' => false, 'message' => 'unzer-payment-settings.webhook.missing.context', @@ -187,14 +184,14 @@ protected function prepare(DataBag $salesChannelDomain): array protected function setContext(SalesChannelDomainEntity $host): void { $parsedUrl = parse_url($host->getUrl()); - $context = $this->router->getContext(); + $context = $this->router->getContext(); - if ($context !== null && is_array($parsedUrl)) { - if (array_key_exists('host', $parsedUrl) && !empty($parsedUrl['host'])) { + if (\is_array($parsedUrl)) { + if (\array_key_exists('host', $parsedUrl) && !empty($parsedUrl['host'])) { $context = $context->setHost($parsedUrl['host']); } - if (array_key_exists('scheme', $parsedUrl) && !empty($parsedUrl['scheme'])) { + if (\array_key_exists('scheme', $parsedUrl) && !empty($parsedUrl['scheme'])) { $context = $context->setScheme($parsedUrl['scheme']); } } diff --git a/src/Controllers/Administration/UnzerPaymentApplePayController.php b/src/Controllers/Administration/UnzerPaymentApplePayController.php deleted file mode 100644 index 4df46704..00000000 --- a/src/Controllers/Administration/UnzerPaymentApplePayController.php +++ /dev/null @@ -1,273 +0,0 @@ - ['api']])] -class UnzerPaymentApplePayController extends AbstractController -{ - private const INHERIT_PAYMENT_PROCESSING_PARAMETER = 'inheritPaymentProcessing'; - private const PAYMENT_PROCESSING_CERTIFICATE_PARAMETER = 'paymentProcessingCertificate'; - private const PAYMENT_PROCESSING_KEY_PARAMETER = 'paymentProcessingKey'; - private const INHERIT_MERCHANT_IDENTIFICATION_PARAMETER = 'inheritMerchantIdentification'; - private const MERCHANT_IDENTIFICATION_CERTIFICATE_PARAMETER = 'merchantIdentificationCertificate'; - private const MERCHANT_IDENTIFICATION_KEY_PARAMETER = 'merchantIdentificationKey'; - - - - public function __construct( - private readonly ClientFactoryInterface $clientFactory, - private readonly LoggerInterface $logger, - private readonly SystemConfigService $systemConfigService, - private readonly Filesystem $filesystem, - private readonly ConfigReaderInterface $configReader, - private readonly CertificateManager $certificateManager, - private readonly EntityRepository $salesChannelRepository - ) { - } - #[Route(path: '/api/_action/unzer-payment/apple-pay/certificates/{salesChannelId}', name: 'api.action.unzer.apple-pay.update-certificates', defaults: ['salesChannelId' => null], methods: ['POST'])] - public function updateApplePayCertificates(?string $salesChannelId, RequestDataBag $dataBag): JsonResponse - { - - - if ($dataBag->has(self::INHERIT_PAYMENT_PROCESSING_PARAMETER)) { - $this->logger->debug(sprintf('Payment Processing reference for sales channel %s cleared', $salesChannelId)); - $this->systemConfigService->delete(sprintf('%s%s', ConfigReader::SYSTEM_CONFIG_DOMAIN, ConfigReader::CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID), $salesChannelId); - } - - try { - $this->updatePaymentProcessingCertificate($dataBag, $salesChannelId); - - if ($dataBag->has(self::INHERIT_MERCHANT_IDENTIFICATION_PARAMETER)) { - if ($this->filesystem->has($this->certificateManager->getMerchantIdentificationCertificatePathForUpdate($salesChannelId))) { - $this->logger->debug(sprintf('Merchant Identification certificate for sales channel %s deleted', $salesChannelId)); - $this->filesystem->delete($this->certificateManager->getMerchantIdentificationCertificatePathForUpdate($salesChannelId)); - } - - if ($this->filesystem->has($this->certificateManager->getMerchantIdentificationKeyPathForUpdate($salesChannelId))) { - $this->logger->debug(sprintf('Merchant Identification key for sales channel %s deleted', $salesChannelId)); - $this->filesystem->delete($this->certificateManager->getMerchantIdentificationKeyPathForUpdate($salesChannelId)); - } - - $this->systemConfigService->delete(sprintf('%s%s', ConfigReader::SYSTEM_CONFIG_DOMAIN, ConfigReader::CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFICATION_CERTIFICATE_ID), $salesChannelId); - } - - if ($dataBag->get(self::MERCHANT_IDENTIFICATION_CERTIFICATE_PARAMETER) && $dataBag->get(self::MERCHANT_IDENTIFICATION_KEY_PARAMETER)) { - $certificate = $dataBag->get(self::MERCHANT_IDENTIFICATION_CERTIFICATE_PARAMETER); - $key = $dataBag->get(self::MERCHANT_IDENTIFICATION_KEY_PARAMETER); - - if (extension_loaded('openssl') && !openssl_x509_parse($certificate)) { - $this->logger->error('Invalid Merchant Identification certificate given'); - throw new InvalidCertificate('Merchant Identification'); - } - - $this->filesystem->write($this->certificateManager->getMerchantIdentificationCertificatePathForUpdate($salesChannelId), $certificate); - $this->filesystem->write($this->certificateManager->getMerchantIdentificationKeyPathForUpdate($salesChannelId), $key); - - $this->systemConfigService->set(sprintf('%s%s', ConfigReader::SYSTEM_CONFIG_DOMAIN, ConfigReader::CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFICATION_CERTIFICATE_ID), (string) $salesChannelId, $salesChannelId); - $this->logger->debug(sprintf('Merchant Identification certificate for sales channel %s updated', $salesChannelId)); - } elseif (($dataBag->get(self::MERCHANT_IDENTIFICATION_CERTIFICATE_PARAMETER) && !$dataBag->get(self::MERCHANT_IDENTIFICATION_KEY_PARAMETER)) - || (!$dataBag->get(self::MERCHANT_IDENTIFICATION_CERTIFICATE_PARAMETER) && $dataBag->get(self::MERCHANT_IDENTIFICATION_KEY_PARAMETER))) { - $this->logger->error('Merchant Identification certificate or key missing'); - throw new MissingCertificateFiles('Merchant Identification'); - } - } catch (UnzerApiException $e) { - return new JsonResponse( - [ - 'message' => $e->getMerchantMessage(), - 'translationData' => [], - ], - Response::HTTP_BAD_REQUEST - ); - } catch (Exception $e) { - if (method_exists($e, 'getTranslationKey')) { - $message = $e->getTranslationKey(); - } else { - $message = $e->getMessage(); - } - - if (method_exists($e, 'getTranslationData')) { - $translationData = $e->getTranslationData(); - } else { - $translationData = []; - } - - return new JsonResponse( - [ - 'message' => $message, - 'translationData' => $translationData, - ], - Response::HTTP_BAD_REQUEST - ); - } - - return new JsonResponse( - null, - 201 - ); - } - - protected function updatePaymentProcessingCertificate(RequestDataBag $dataBag, ?string $salesChannelId):void{ - if ($dataBag->get(self::PAYMENT_PROCESSING_CERTIFICATE_PARAMETER) && $dataBag->get(self::PAYMENT_PROCESSING_KEY_PARAMETER)) { - $client = $this->clientFactory->createClient(KeyPairContext::createFromSalesChannel($this->getSalesChannel($salesChannelId))); - $certificate = $dataBag->get(self::PAYMENT_PROCESSING_CERTIFICATE_PARAMETER); - - if (extension_loaded('openssl') && !openssl_x509_parse($certificate)) { - $this->logger->error('Invalid Payment Processing certificate given'); - throw new InvalidCertificate('Payment Processing'); - } - - $privateKeyResource = new ApplePayPrivateKey(); - $privateKeyResource->setCertificate($dataBag->get(self::PAYMENT_PROCESSING_KEY_PARAMETER)); - - $client->getResourceService()->createResource($privateKeyResource->setParentResource($client)); - /** @var string $privateKeyId */ - $privateKeyId = $privateKeyResource->getId(); - - $certificateResource = new ApplePayCertificate(); - $certificateResource->setCertificate($certificate); - $certificateResource->setPrivateKey($privateKeyId); - $client->getResourceService()->createResource($certificateResource->setParentResource($client)); - - if($this->activateCertificate($certificateResource->getId(), $client)){ - $this->systemConfigService->set(sprintf('%s%s', ConfigReader::SYSTEM_CONFIG_DOMAIN, ConfigReader::CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID), $certificateResource->getId(), $salesChannelId); - }else{ - $this->logger->error('Failed to activate Payment Processing certificate '.$certificateResource->getId()); - throw new MissingCertificateFiles('Payment Processing'); - } - $this->logger->debug(sprintf('Payment Processing certificate for sales channel %s updated and activated', $salesChannelId)); - } elseif (($dataBag->get(self::PAYMENT_PROCESSING_CERTIFICATE_PARAMETER) && !$dataBag->get(self::PAYMENT_PROCESSING_KEY_PARAMETER)) - || (!$dataBag->get(self::PAYMENT_PROCESSING_CERTIFICATE_PARAMETER) && $dataBag->get(self::PAYMENT_PROCESSING_KEY_PARAMETER))) { - $this->logger->error('Payment Processing certificate or key missing'); - throw new MissingCertificateFiles('Payment Processing'); - } - } - - public function activateCertificate( string $certificateId, Unzer $unzerClient ): bool { - $certificate = ( new ApplePayCertificate() ) - ->setId( $certificateId ) - ->setParentResource( $unzerClient ); - $responseJson = $unzerClient->getHttpService()->send( - '/keypair/applepay/certificates/' . $certificateId . '/activate', - $certificate, - HttpAdapterInterface::REQUEST_POST - ); - $response = json_decode( $responseJson, true ); - return $response['active'] ?? false; - } - - #[Route(path: '/api/_action/unzer-payment/apple-pay/certificates/{salesChannelId}', name: 'api.action.unzer.apple-pay.check-certificates', defaults: ['salesChannelId' => null], methods: ['GET'])] - public function checkApplePayCertificates(RequestDataBag $dataBag): JsonResponse - { - $salesChannelId = $dataBag->get('salesChannelId', ''); - $paymentProcessingValid = false; - $paymentProcessingActive = false; - $paymentProcessingInherited = false; - $merchantIdentificationValid = false; - $merchantIdentificationInherited = false; - $merchantIdentificationValidUntil = null; - - if (!empty($salesChannelId) && $this->filesystem->has($this->certificateManager->getMerchantIdentificationCertificatePath($salesChannelId)) && - $this->filesystem->has($this->certificateManager->getMerchantIdentificationKeyPath($salesChannelId))) { - $merchantIdentificationValid = true; - - if (extension_loaded('openssl')) { - $certificateData = openssl_x509_parse($this->filesystem->read($this->certificateManager->getMerchantIdentificationCertificatePath($salesChannelId))); - - if (is_array($certificateData) && array_key_exists('validTo_time_t', $certificateData)) { - $merchantIdentificationValidUntil = DateTimeImmutable::createFromFormat('U', (string) $certificateData['validTo_time_t']) ?: null; - } - } - } elseif ($this->filesystem->has($this->certificateManager->getMerchantIdentificationCertificatePath('')) && - $this->filesystem->has($this->certificateManager->getMerchantIdentificationKeyPath(''))) { - $merchantIdentificationValid = true; - $merchantIdentificationInherited = true; - - if (extension_loaded('openssl')) { - $certificateData = openssl_x509_parse($this->filesystem->read($this->certificateManager->getMerchantIdentificationCertificatePath(''))); - - if (is_array($certificateData) && array_key_exists('validTo_time_t', $certificateData)) { - $merchantIdentificationValidUntil = DateTimeImmutable::createFromFormat('U', (string) $certificateData['validTo_time_t']) ?: null; - } - } - } - - $configuration = $this->configReader->read($salesChannelId, false); - $baseConfiguration = $this->configReader->read(''); - - if ($configuration->get(ConfigReader::CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID)) { - $paymentProcessingValid = true; - $certificateId = $configuration->get(ConfigReader::CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID); - $unzerClient = $this->clientFactory->createClient(KeyPairContext::createFromSalesChannel($this->getSalesChannel($salesChannelId))); - - } elseif ($baseConfiguration->get(ConfigReader::CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID)) { - $paymentProcessingValid = true; - $paymentProcessingInherited = true; - $certificateId = $baseConfiguration->get(ConfigReader::CONFIG_KEY_APPLE_PAY_PAYMENT_PROCESSING_CERTIFICATE_ID); - $unzerClient = $this->clientFactory->createClient(KeyPairContext::createFromSalesChannel($this->getSalesChannel(''))); - } - - if(!empty($certificateId) && !empty($unzerClient) && $unzerClient instanceof Unzer) { - $certificateResource = new ApplePayCertificate(); - $certificateResource->setId($certificateId); - $certificateResource->setParentResource($unzerClient); - $submittedCertificate = $unzerClient->getResourceService()->fetchResource($certificateResource); - $paymentProcessingActive = $submittedCertificate->getActive(); - } - - return new JsonResponse( - new CertificateInformation( - $paymentProcessingValid, - $paymentProcessingActive, - $paymentProcessingInherited, - $merchantIdentificationValid, - $merchantIdentificationInherited, - $merchantIdentificationValidUntil - ), - Response::HTTP_OK - ); - } - - protected function getSalesChannel(?string $salesChannelId): ?SalesChannelEntity - { - $criteria = new Criteria(); - - if ($salesChannelId) { - $criteria->setIds([$salesChannelId]); - } - - $criteria->addAssociation('currency'); - $criteria->addAssociation('paymentMethod'); - - return $this->salesChannelRepository->search($criteria, Context::createDefaultContext())->first(); - } -} diff --git a/src/Controllers/Administration/UnzerPaymentConfigurationController.php b/src/Controllers/Administration/UnzerPaymentConfigurationController.php index fdaaa9aa..cae8fda8 100644 --- a/src/Controllers/Administration/UnzerPaymentConfigurationController.php +++ b/src/Controllers/Administration/UnzerPaymentConfigurationController.php @@ -5,7 +5,6 @@ namespace UnzerPayment6\Controllers\Administration; use Psr\Log\LoggerInterface; -use RuntimeException; use Shopware\Core\Framework\Validation\DataBag\DataBag; use Shopware\Core\Framework\Validation\DataBag\RequestDataBag; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -22,11 +21,10 @@ class UnzerPaymentConfigurationController extends AbstractController { public function __construct( - private readonly ClientFactoryInterface $clientFactory, - private readonly LoggerInterface $logger, + private readonly ClientFactoryInterface $clientFactory, + private readonly LoggerInterface $logger, private readonly WebhookRegistratorInterface $webhookRegistrator - ) - { + ) { } #[Route(path: '/api/_action/unzer-payment/validate-credentials', name: 'api.action.unzer.validate.credentials', methods: ['POST'])] @@ -47,7 +45,7 @@ public function validateCredentials(RequestDataBag $dataBag): JsonResponse if ($remoteKeypair->getPublicKey() !== $publicKey) { $responseCode = Response::HTTP_BAD_REQUEST; } - } catch (UnzerApiException|RuntimeException $apiException) { + } catch (UnzerApiException|\RuntimeException) { $responseCode = Response::HTTP_BAD_REQUEST; } @@ -71,6 +69,7 @@ public function getGooglePayGatewayMerchantId(RequestDataBag $dataBag): JsonResp $configuration = $configReader->read($salesChannelId); $client = $this->clientFactory->createClientFromPrivateKey($configuration->get(ConfigReader::CONFIG_KEY_PRIVATE_KEY)); $channelId = UnzerGooglePayPaymentHandler::fetchChannelId($client); + return new JsonResponse([ 'success' => true, 'gatewayMerchantId' => $channelId, diff --git a/src/Controllers/Administration/UnzerPaymentTransactionController.php b/src/Controllers/Administration/UnzerPaymentTransactionController.php index d0010d60..1e11235a 100644 --- a/src/Controllers/Administration/UnzerPaymentTransactionController.php +++ b/src/Controllers/Administration/UnzerPaymentTransactionController.php @@ -4,26 +4,22 @@ namespace UnzerPayment6\Controllers\Administration; -use DateTime; use Psr\Log\LoggerInterface; use Shopware\Core\Checkout\Document\Renderer\InvoiceRenderer; use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; use Shopware\Core\Checkout\Payment\PaymentException; use Shopware\Core\Framework\Context; -use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; -use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -use Throwable; -use UnzerPayment6\Components\BackwardsCompatibility\InvoiceGenerator; use UnzerPayment6\Components\BasketConverter\BasketConverterInterface; use UnzerPayment6\Components\CancelService\CancelServiceInterface; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\ResourceHydrator\PaymentResourceHydrator\PaymentResourceHydratorInterface; use UnzerPayment6\Components\ShipService\ShipServiceInterface; use UnzerPayment6\Components\Struct\KeyPairContext; +use UnzerPayment6\Components\UnzerUtil\UnzerTransactionUtil; use UnzerPayment6\Installer\PaymentInstaller; use UnzerSDK\Exceptions\UnzerApiException; use UnzerSDK\Resources\TransactionTypes\Charge; @@ -32,15 +28,14 @@ class UnzerPaymentTransactionController extends AbstractController { public function __construct( - private readonly ClientFactoryInterface $clientFactory, - private readonly EntityRepository $orderTransactionRepository, + private readonly ClientFactoryInterface $clientFactory, + private readonly UnzerTransactionUtil $unzerTransactionUtil, private readonly PaymentResourceHydratorInterface $hydrator, - private readonly CancelServiceInterface $cancelService, - private readonly ShipServiceInterface $shipService, - private readonly BasketConverterInterface $basketConverter, - private readonly LoggerInterface $logger - ) - { + private readonly CancelServiceInterface $cancelService, + private readonly ShipServiceInterface $shipService, + private readonly BasketConverterInterface $basketConverter, + private readonly LoggerInterface $logger + ) { } #[Route(path: '/api/_action/unzer-payment/transaction/{orderTransactionId}/details', name: 'api.action.unzer.transaction.details', methods: ['GET'])] @@ -55,16 +50,15 @@ public function fetchTransactionDetails(string $orderTransactionId, Context $con $client = $this->clientFactory->createClient(KeyPairContext::createFromOrderTransaction($transaction)); try { - $payment = $client->fetchPaymentByOrderId($orderTransactionId); - $payment = $client->fetchPayment($payment); - + $payment = UnzerTransactionUtil::fetchPaymentFromOrderTransaction($transaction, $client); $data = $this->hydrator->hydrateArray($payment, $transaction, $client); if (!empty($data['basket']['totalValueGross'])) { $data['basket'] = $this->basketConverter->populateDeprecatedVariables($data['basket']); } - } catch (UnzerApiException|Throwable $exception) { - $exceptionReturnValues = $this->handleException($exception, sprintf('Error while executing fetching transaction details for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + } catch (UnzerApiException|\Throwable $exception) { + $exceptionReturnValues = $this->handleException($exception, \sprintf('Error while executing fetching transaction details for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + return new JsonResponse($exceptionReturnValues[0], $exceptionReturnValues[1]); } @@ -82,7 +76,7 @@ public function chargeTransaction(string $orderTransactionId, float $amount, Con } $client = $this->clientFactory->createClient(KeyPairContext::createFromOrderTransaction($transaction)); - + $payment = UnzerTransactionUtil::fetchPaymentFromOrderTransaction($transaction, $client); try { $charge = new Charge($amount); @@ -94,9 +88,11 @@ public function chargeTransaction(string $orderTransactionId, float $amount, Con } } - $client->performChargeOnPayment($orderTransactionId, $charge); - } catch (UnzerApiException|Throwable $exception) { - $exceptionReturnValues = $this->handleException($exception, sprintf('Error while executing charge transaction for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + $client->performChargeOnPayment($payment, $charge); + $this->unzerTransactionUtil->updateOrderTransactionStatus($client, $transaction, $context); + } catch (UnzerApiException|\Throwable $exception) { + $exceptionReturnValues = $this->handleException($exception, \sprintf('Error while executing charge transaction for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + return new JsonResponse($exceptionReturnValues[0], $exceptionReturnValues[1]); } @@ -110,8 +106,9 @@ public function refundTransaction(string $orderTransactionId, string $chargeId, { try { $this->cancelService->cancelChargeById($orderTransactionId, $chargeId, $amount, $reasonCode, $context); - } catch (UnzerApiException|Throwable $exception) { - $exceptionReturnValues = $this->handleException($exception, sprintf('Error while executing refund transaction for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + } catch (UnzerApiException|\Throwable $exception) { + $exceptionReturnValues = $this->handleException($exception, \sprintf('Error while executing refund transaction for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + return new JsonResponse($exceptionReturnValues[0], $exceptionReturnValues[1]); } @@ -124,8 +121,9 @@ public function cancelTransaction(string $orderTransactionId, string $authorizat { try { $this->cancelService->cancelAuthorizationById($orderTransactionId, $authorizationId, $amount, $context); - } catch (UnzerApiException|Throwable $exception) { - $exceptionReturnValues = $this->handleException($exception, sprintf('Error while executing cancel transaction for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + } catch (UnzerApiException|\Throwable $exception) { + $exceptionReturnValues = $this->handleException($exception, \sprintf('Error while executing cancel transaction for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + return new JsonResponse($exceptionReturnValues[0], $exceptionReturnValues[1]); } @@ -138,15 +136,16 @@ public function shipTransaction(string $orderTransactionId, Context $context): J { try { $result = $this->shipService->shipTransaction($orderTransactionId, $context); - } catch (UnzerApiException|Throwable $exception) { - $exceptionReturnValues = $this->handleException($exception, sprintf('Error while executing shipping notification for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + } catch (UnzerApiException|\Throwable $exception) { + $exceptionReturnValues = $this->handleException($exception, \sprintf('Error while executing shipping notification for order transaction [%s]: %s', $orderTransactionId, $exception->getMessage())); + return new JsonResponse($exceptionReturnValues[0], $exceptionReturnValues[1]); } return new JsonResponse($result); } - protected function handleException(Throwable|UnzerApiException $exception, string $logMessage): array + protected function handleException(\Throwable|UnzerApiException $exception, string $logMessage): array { $this->logger->error($logMessage, [ 'trace' => $exception->getTraceAsString(), @@ -163,17 +162,7 @@ protected function handleException(Throwable|UnzerApiException $exception, strin protected function getOrderTransaction(string $orderTransactionId, Context $context): ?OrderTransactionEntity { - $criteria = new Criteria([$orderTransactionId]); - $criteria->addAssociations([ - 'order', - 'order.billingAddress', - 'order.currency', - 'order.documents', - 'order.documents.documentType', - 'paymentMethod', - ]); - - return $this->orderTransactionRepository->search($criteria, $context)->first(); + return $this->unzerTransactionUtil->getOrderTransaction($orderTransactionId, $context); } private function getInvoiceNumber(OrderTransactionEntity $transaction): ?string @@ -189,7 +178,7 @@ private function getInvoiceNumber(OrderTransactionEntity $transaction): ?string // get latest invoice document foreach ($documents as $document) { if ($document->getDocumentType() && $document->getDocumentType()->getTechnicalName() === InvoiceRenderer::TYPE) { - $newDocumentDate = new DateTime($document->getConfig()['documentDate']); + $newDocumentDate = new \DateTime($document->getConfig()['documentDate']); if ($documentDate === null || $newDocumentDate->getTimestamp() > $documentDate->getTimestamp()) { $documentDate = $newDocumentDate; @@ -200,4 +189,4 @@ private function getInvoiceNumber(OrderTransactionEntity $transaction): ?string return $invoiceNumber; } -} \ No newline at end of file +} diff --git a/src/Controllers/Storefront/UnzerCheckoutController.php b/src/Controllers/Storefront/UnzerCheckoutController.php index 081c8b5b..fdd5965c 100644 --- a/src/Controllers/Storefront/UnzerCheckoutController.php +++ b/src/Controllers/Storefront/UnzerCheckoutController.php @@ -15,24 +15,12 @@ use UnzerPayment6\Components\PaymentHandler\Exception\UnzerPaymentProcessException; #[Route(defaults: ['_routeScope' => ['storefront']])] - class UnzerCheckoutController extends CheckoutController { - /** - * For compatibility to other plugins, we set StorefrontController as the type hint for the argument. - * - * @var CheckoutController|StorefrontController - */ - protected $innerService; - - private CheckoutFinishPageLoader $finishPageLoader; - public function __construct( - StorefrontController $innerService, - CheckoutFinishPageLoader $finishPageLoader + readonly StorefrontController $innerService, + readonly CheckoutFinishPageLoader $finishPageLoader ) { - $this->innerService = $innerService; - $this->finishPageLoader = $finishPageLoader; } public function cartPage(Request $request, SalesChannelContext $context): Response @@ -68,9 +56,9 @@ public function order(RequestDataBag $data, SalesChannelContext $context, Reques return $this->forwardToRoute( 'frontend.checkout.finish.page', [ - 'orderId' => $apiException->getOrderId(), - 'changedPayment' => false, - 'paymentFailed' => true, + 'orderId' => $apiException->getOrderId(), + 'changedPayment' => false, + 'paymentFailed' => true, 'unzerPaymentExceptionMessage' => $apiException->getClientMessage(), ] ); @@ -90,7 +78,7 @@ public function finishPage(Request $request, SalesChannelContext $context, Reque $this->addFlash( 'danger', - sprintf( + \sprintf( '%s %s', $unzerPaymentExceptionMessage, $this->trans( diff --git a/src/Controllers/Storefront/UnzerExpressCheckoutController.php b/src/Controllers/Storefront/UnzerExpressCheckoutController.php new file mode 100644 index 00000000..5b5133c8 --- /dev/null +++ b/src/Controllers/Storefront/UnzerExpressCheckoutController.php @@ -0,0 +1,163 @@ + ['storefront']])] +class UnzerExpressCheckoutController extends StorefrontController +{ + /** + * @param MetadataResourceHydrator $metadataResourceHydrator + */ + public function __construct( + private readonly ExpressCheckoutService $expressCheckoutService, + private readonly ResourceHydratorInterface $metadataResourceHydrator, + protected readonly ConfigReaderInterface $configReader, + protected readonly LoggerInterface $logger, + ) { + } + + #[Route(path: '/unzer/paypal-express', name: 'frontend.unzer.paypal-express', defaults: ['csrf_protected' => false], methods: ['POST', 'GET'])] + 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); + + $shopwareCart = $this->expressCheckoutService->getCart($salesChannelContext); + $basket = (new Basket()) + ->setTotalValueGross($shopwareCart->getPrice()->getTotalPrice()) + ->setCurrencyCode($salesChannelContext->getCurrency()->getIsoCode()); + $basketItem = (new BasketItem()) + ->setAmountPerUnitGross($shopwareCart->getPrice()->getTotalPrice()) + ->setTitle('-'); // TODO? + + $basket->addBasketItem($basketItem); + + $basketResult = $client->createBasket($basket); + /** @var Metadata $metaData */ + $metaData = $this->metadataResourceHydrator->hydrateObject($salesChannelContext); + $this->metadataResourceHydrator->setIsExpress($metaData, true); + + $config = $this->configReader->read($salesChannelContext->getSalesChannelId()); + $bookingMode = $config->get(ConfigReader::CONFIG_KEY_BOOKING_MODE_PAYPAL); + $returnUrl = $this->generateUrl('frontend.unzer.paypal-express-return', [], UrlGeneratorInterface::ABSOLUTE_URL); + if ($bookingMode === BookingMode::AUTHORIZE) { + $authorization = (new Authorization( + $shopwareCart->getPrice()->getTotalPrice(), + $salesChannelContext->getCurrency()->getIsoCode(), + $returnUrl + )) + ->setCheckoutType('express', $paymentTypeId); + $resultTransaction = $client->performAuthorization($authorization, $paymentTypeId, null, $metaData, $basketResult); + } else { + $charge = (new Charge( + $shopwareCart->getPrice()->getTotalPrice(), + $salesChannelContext->getCurrency()->getIsoCode(), + $returnUrl + )) + ->setCheckoutType('express', $paymentTypeId); + $resultTransaction = $client->performCharge($charge, $paymentTypeId, null, $metaData, $basketResult); + } + + $request->getSession()->set(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID, $resultTransaction->getPaymentId()); + $request->getSession()->set(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_TYPE_ID, $paymentTypeId); + + return new JsonResponse([ + 'paymentId' => $resultTransaction->getPaymentId(), + 'redirectUrl' => $resultTransaction->getRedirectUrl(), + ]); + } + + #[Route(path: '/unzer/paypal-express-return', name: 'frontend.unzer.paypal-express-return', defaults: ['csrf_protected' => false], methods: ['POST', 'GET'])] + public function paypalExpressReturn(Request $request, SalesChannelContext $salesChannelContext): Response + { + $paymentId = $request->getSession()->get('paypal-express-checkout-payment-id'); + if (empty($paymentId)) { + $this->logger->error('paypalExpressReturn empty paymentId'); + + return $this->redirectToRoute('frontend.checkout.cart.page'); // TODO + } + /** @var ClientFactory $clientFactory */ + $clientFactory = $this->container->get(ClientFactory::class); + $client = $clientFactory->createClientFromSalesChannelId($salesChannelContext->getSalesChannelId(), $request); + $payment = $client->fetchPayment($paymentId); + + try { + $this->expressCheckoutService->startCheckoutSessionFromUnzerPayment($payment, PaymentInstaller::PAYMENT_ID_PAYPAL, $salesChannelContext); + } catch (\Exception $e) { + $this->logger->error('paypalExpressReturn Exception: ' . $e->getMessage()); + + return $this->redirectToRoute('frontend.checkout.cart.page'); // TODO + } + $request->getSession()->set(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD, PaymentInstaller::PAYMENT_ID_PAYPAL); + + return $this->redirectToRoute('frontend.checkout.confirm.page', ['isExpressCheckout' => 'true']); + } + + #[Route(path: '/unzer/google-pay-express', name: 'frontend.unzer.google-express', defaults: ['csrf_protected' => false], methods: ['POST', 'GET'])] + public function googleExpress(Request $request, SalesChannelContext $salesChannelContext): Response + { + $paymentTypeId = $request->get('paymentTypeId'); + $paymentData = $request->get('paymentData'); + try { + $this->expressCheckoutService->startCheckoutSessionFromGooglePayData($paymentData, PaymentInstaller::PAYMENT_ID_GOOGLE_PAY, $salesChannelContext); + } catch (\Exception $e) { + $this->logger->error('googleExpress Exception: ' . $e->getMessage()); + + return $this->redirectToRoute('frontend.checkout.cart.page'); // TODO + } + $request->getSession()->set(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID, $paymentTypeId); + $request->getSession()->set(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD, PaymentInstaller::PAYMENT_ID_GOOGLE_PAY); + + return new JsonResponse([ + 'redirectUrl' => $this->generateUrl('frontend.checkout.confirm.page', ['isExpressCheckout' => 'true']), + ]); + } + + #[Route(path: '/unzer/applepay-express', name: 'frontend.unzer.applepay-express', defaults: ['csrf_protected' => false], methods: ['POST', 'GET'])] + public function applepayExpress(Request $request, SalesChannelContext $salesChannelContext): Response + { + $paymentTypeId = $request->get('paymentTypeId'); + $paymentData = $request->get('paymentData'); + $shippingContact = $request->get('shippingContact'); + $billingContact = $request->get('billingContact'); + try { + $this->expressCheckoutService->startCheckoutSessionFromApplePayData($paymentData, $shippingContact, $billingContact, PaymentInstaller::PAYMENT_ID_APPLE_PAY_V2, $salesChannelContext); + } catch (\Exception $e) { + $this->logger->error('applepayExpress Exception: ' . $e->getMessage()); + + return $this->redirectToRoute('frontend.checkout.cart.page'); // TODO + } + $request->getSession()->set(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID, $paymentTypeId); + $request->getSession()->set(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD, PaymentInstaller::PAYMENT_ID_APPLE_PAY_V2); + + return new JsonResponse([ + 'redirectUrl' => $this->generateUrl('frontend.checkout.confirm.page', ['isExpressCheckout' => 'true']), + ]); + } +} diff --git a/src/Controllers/Storefront/UnzerPaymentApplePayController.php b/src/Controllers/Storefront/UnzerPaymentApplePayController.php deleted file mode 100644 index 77b8d0c9..00000000 --- a/src/Controllers/Storefront/UnzerPaymentApplePayController.php +++ /dev/null @@ -1,135 +0,0 @@ - ['storefront']])] -class UnzerPaymentApplePayController extends StorefrontController -{ - private const MERCHANT_VALIDATION_URL_PARAM = 'merchantValidationUrl'; - - public function __construct( - private readonly ConfigReaderInterface $configReader, - private readonly Filesystem $filesystem, - private readonly LoggerInterface $logger, - private readonly CertificateManager $certificateManager, - private readonly ClientFactory $clientFactory, - private readonly SystemConfigService $systemConfigService - ) - { - } - - #[Route(path: '/unzer/applePay/validateMerchant', name: 'frontend.unzer.apple_pay.validate_merchant', defaults: ['XmlHttpRequest' => true, 'csrf_protected' => false], methods: ['POST'])] - public function validateMerchant(Request $request, SalesChannelContext $salesChannelContext): Response - { - $salesChannelId = $salesChannelContext->getSalesChannel()->getId(); - $configuration = $this->configReader->read($salesChannelId, true); - - $displayName = $this->systemConfigService->get('core.basicInformation.shopName', $salesChannelId); - - if (!is_string($displayName)) { - $displayName = ''; - } - - $applePaySession = new ApplepaySession( - $configuration->get(ConfigReader::CONFIG_KEY_APPLE_PAY_MERCHANT_IDENTIFIER), - $displayName, - $request->getHost() - ); - $appleAdapter = new ApplepayAdapter(); - - $certificatePath = $this->certificateManager->getMerchantIdentificationCertificatePath($salesChannelId); - $keyPath = $this->certificateManager->getMerchantIdentificationKeyPath($salesChannelId); - - if (!$this->filesystem->has($certificatePath) || !$this->filesystem->has($keyPath)) { - // Try for fallback configuration - $certificatePath = $this->certificateManager->getMerchantIdentificationCertificatePath(''); - $keyPath = $this->certificateManager->getMerchantIdentificationKeyPath(''); - - if (!$this->filesystem->has($certificatePath) || !$this->filesystem->has($keyPath)) { - throw new MissingCertificateFiles('Merchant Identification'); - } - } - - // ApplepayAdapter requires certificate as local files - $certificateTempPath = tempnam(sys_get_temp_dir(), 'UnzerPayment6'); - $keyTempPath = tempnam(sys_get_temp_dir(), 'UnzerPayment6'); - - if (!$certificateTempPath || !$keyTempPath) { - throw new RuntimeException('Error on temporary file creation'); - } - - file_put_contents($certificateTempPath, $this->filesystem->read($certificatePath)); - file_put_contents($keyTempPath, $this->filesystem->read($keyPath)); - - try { - $appleAdapter->init($certificateTempPath, $keyTempPath); - - $merchantValidationUrl = urldecode($request->get(self::MERCHANT_VALIDATION_URL_PARAM)); - - try { - $validationResponse = $appleAdapter->validateApplePayMerchant( - $merchantValidationUrl, - $applePaySession - ); - - return new Response($validationResponse); - } catch (Exception $e) { - $this->logger->error('Error in Apple Pay merchant validation', ['exception' => $e]); - - throw $e; - } - } finally { - unlink($keyTempPath); - unlink($certificateTempPath); - } - } - - #[Route(path: '/unzer/applePay/authorizePayment', name: 'frontend.unzer.apple_pay.authorize_payment', defaults: ['XmlHttpRequest' => true, 'csrf_protected' => false], methods: ['POST'])] - public function authorizePayment(Request $request, SalesChannelContext $salesChannelContext): Response - { - $client = $this->clientFactory->createClient(KeyPairContext::createFromSalesChannelContext($salesChannelContext)); - $typeId = $request->get('id'); - - $response = ['transactionStatus' => 'error']; - - try { - // Charge/Authorize is done in payment handler, return pending to satisfy Apple Pay widget - $paymentType = $client->fetchPaymentType($typeId); - $response['transactionStatus'] = 'pending'; - } catch (UnzerApiException $e) { - return new JsonResponse([ - 'clientMessage' => $e->getClientMessage(), - 'merchantMessage' => $e->getMerchantMessage(), - ], Response::HTTP_INTERNAL_SERVER_ERROR); - } catch (Exception $e) { - $this->logger->error('Error in Apple Pay authorization call', ['exception' => $e]); - - throw $e; - } - - return new JsonResponse($response); - } -} diff --git a/src/Controllers/Storefront/UnzerPaymentDeviceController.php b/src/Controllers/Storefront/UnzerPaymentDeviceController.php index 350be30e..c1fe5b2b 100644 --- a/src/Controllers/Storefront/UnzerPaymentDeviceController.php +++ b/src/Controllers/Storefront/UnzerPaymentDeviceController.php @@ -16,11 +16,10 @@ class UnzerPaymentDeviceController extends StorefrontController { public function __construct( private readonly UnzerPaymentDeviceRepositoryInterface $deviceRepository - ) - { + ) { } - //TODO: evaluate if GET is the correct method for this route + // TODO: evaluate if GET is the correct method for this route #[Route(path: '/unzer/deleteDevice', name: 'frontend.unzer.device.delete', methods: ['GET'])] /** * @Route("/unzer/deleteDevice", name="frontend.unzer.device.delete", methods={"GET"}) @@ -31,9 +30,9 @@ public function deleteDevice(Request $request, SalesChannelContext $salesChannel return new RedirectResponse($this->generateUrl('frontend.account.payment.page')); } - $context = $salesChannelContext->getContext(); + $context = $salesChannelContext->getContext(); $deviceId = $request->get('id'); - $device = $this->deviceRepository->read($deviceId, $context); + $device = $this->deviceRepository->read($deviceId, $context); if ($device === null || $device->getCustomerId() !== $salesChannelContext->getCustomer()->getId()) { return new RedirectResponse($this->generateUrl('frontend.account.payment.page')); diff --git a/src/Controllers/Storefront/UnzerPaymentWebhookController.php b/src/Controllers/Storefront/UnzerPaymentWebhookController.php index 6a1ddb64..6cc65e58 100644 --- a/src/Controllers/Storefront/UnzerPaymentWebhookController.php +++ b/src/Controllers/Storefront/UnzerPaymentWebhookController.php @@ -10,8 +10,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; -use Throwable; -use Traversable; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\Struct\Configuration; @@ -21,18 +19,20 @@ #[Route(defaults: ['_routeScope' => ['storefront']])] class UnzerPaymentWebhookController extends StorefrontController { - /** @var Traversable|WebhookHandlerInterface[] */ - private Traversable $handlers; + /** + * @var \Traversable|WebhookHandlerInterface[] + */ + private \Traversable $handlers; private ConfigReaderInterface $configReader; private LoggerInterface $logger; - public function __construct(Traversable $handlers, ConfigReaderInterface $configReader, LoggerInterface $logger) + public function __construct(\Traversable $handlers, ConfigReaderInterface $configReader, LoggerInterface $logger) { - $this->handlers = $handlers; + $this->handlers = $handlers; $this->configReader = $configReader; - $this->logger = $logger; + $this->logger = $logger; } #[Route(path: '/unzer/webhook', name: 'frontend.unzer.webhook.execute', defaults: ['csrf_protected' => false], methods: ['POST', 'GET'])] @@ -48,7 +48,7 @@ public function execute(Request $request, SalesChannelContext $salesChannelConte } $webhook = new Webhook($requestContent); - $config = $this->configReader->read($salesChannelContext->getSalesChannel()->getId()); + $config = $this->configReader->read($salesChannelContext->getSalesChannel()->getId()); if (!$this->isValidPublicKey($webhook, $config)) { $this->logger->error('The provided public key does not match the configured public key'); @@ -63,22 +63,22 @@ public function execute(Request $request, SalesChannelContext $salesChannelConte try { $this->logger->debug( - sprintf( + \sprintf( 'Started handling of incoming webhook with content: %s', json_encode($request->getContent()) ) ); $handler->execute($webhook, $salesChannelContext); - } catch (Throwable $exception) { + } catch (\Throwable $exception) { $this->logger->error( 'An exception was caught when handling a webhook, but this may not be a failure.', [ 'message' => $exception->getMessage(), - 'code' => $exception->getCode(), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), - 'trace' => $exception->getTraceAsString(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTraceAsString(), ] ); } @@ -95,7 +95,7 @@ protected function isValidPublicKey(Webhook $webhook, Configuration $config): bo $paylaterInvoiceKeys = $config->get(ConfigReader::CONFIG_KEY_PAYLATER_INVOICE); - if (is_array($paylaterInvoiceKeys)) { + if (\is_array($paylaterInvoiceKeys)) { foreach ($paylaterInvoiceKeys as $keyPairConfig) { if ($keyPairConfig['publicKey'] === $webhook->getPublicKey()) { return true; @@ -105,7 +105,7 @@ protected function isValidPublicKey(Webhook $webhook, Configuration $config): bo $paylaterInstallmentKeys = $config->get(ConfigReader::CONFIG_KEY_PAYLATER_INSTALLMENT); - if (is_array($paylaterInstallmentKeys)) { + if (\is_array($paylaterInstallmentKeys)) { foreach ($paylaterInstallmentKeys as $keyPairConfig) { if ($keyPairConfig['publicKey'] === $webhook->getPublicKey()) { return true; @@ -115,7 +115,7 @@ protected function isValidPublicKey(Webhook $webhook, Configuration $config): bo $paylaterDirectDebitKeys = $config->get(ConfigReader::CONFIG_KEY_PAYLATER_DIRECT_DEBIT_SECURED); - if (is_array($paylaterDirectDebitKeys)) { + if (\is_array($paylaterDirectDebitKeys)) { foreach ($paylaterDirectDebitKeys as $keyPairConfig) { if ($keyPairConfig['publicKey'] === $webhook->getPublicKey()) { return true; diff --git a/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceCollection.php b/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceCollection.php index 6eabd202..c10d2d7a 100644 --- a/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceCollection.php +++ b/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceCollection.php @@ -7,13 +7,13 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; /** - * @method void add(UnzerPaymentDeviceEntity $entity) - * @method void set(string $key, UnzerPaymentDeviceEntity $entity) - * @method UnzerPaymentDeviceEntity[] getIterator() - * @method UnzerPaymentDeviceEntity[] getElements() - * @method null|UnzerPaymentDeviceEntity get(string $key) - * @method null|UnzerPaymentDeviceEntity first() - * @method null|UnzerPaymentDeviceEntity last() + * @method void add(UnzerPaymentDeviceEntity $entity) + * @method void set(string $key, UnzerPaymentDeviceEntity $entity) + * @method UnzerPaymentDeviceEntity[] getIterator() + * @method UnzerPaymentDeviceEntity[] getElements() + * @method UnzerPaymentDeviceEntity|null get(string $key) + * @method UnzerPaymentDeviceEntity|null first() + * @method UnzerPaymentDeviceEntity|null last() */ class UnzerPaymentDeviceCollection extends EntityCollection { diff --git a/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php b/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php index 36b554ca..4443d200 100644 --- a/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php +++ b/src/DataAbstractionLayer/Entity/PaymentDevice/UnzerPaymentDeviceEntity.php @@ -11,25 +11,20 @@ class UnzerPaymentDeviceEntity extends Entity { use EntityIdTrait; - public const DEVICE_TYPE_CREDIT_CARD = 'credit_card'; - public const DEVICE_TYPE_PAYPAL = 'paypal_account'; - public const DEVICE_TYPE_DIRECT_DEBIT = 'direct_debit'; + public const DEVICE_TYPE_CREDIT_CARD = 'credit_card'; + public const DEVICE_TYPE_PAYPAL = 'paypal_account'; + public const DEVICE_TYPE_DIRECT_DEBIT = 'direct_debit'; public const DEVICE_TYPE_DIRECT_DEBIT_SECURED = 'direct_debit_secured'; - /** @var string */ - protected $customerId; + protected string $customerId; - /** @var string */ - protected $deviceType; + protected string $deviceType; - /** @var string */ - protected $typeId; + protected string $typeId; - /** @var array */ - protected $data; + protected array $data; - /** @var string */ - protected $addressHash; + protected string $addressHash; public function getCustomerId(): string { diff --git a/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntity.php b/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntity.php index 19fb7003..ac42e3f1 100644 --- a/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntity.php +++ b/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntity.php @@ -11,26 +11,19 @@ class UnzerPaymentTransferInfoEntity extends Entity { use EntityIdTrait; - /** @var string */ - protected $transactionId; + protected string $transactionId; - /** @var null|string */ - protected $transactionVersionId; + protected ?string $transactionVersionId; - /** @var string */ - protected $iban; + protected string $iban; - /** @var string */ - protected $bic; + protected string $bic; - /** @var string */ - protected $holder; + protected string $holder; - /** @var string */ - protected $descriptor; + protected string $descriptor; - /** @var float */ - protected $amount; + protected float $amount; public function getTransactionId(): string { diff --git a/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityCollection.php b/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityCollection.php index 9ab2c12d..235691f0 100644 --- a/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityCollection.php +++ b/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityCollection.php @@ -7,13 +7,13 @@ use Shopware\Core\Framework\DataAbstractionLayer\EntityCollection; /** - * @method void add(UnzerPaymentTransferInfoEntity $entity) - * @method void set(string $key, UnzerPaymentTransferInfoEntity $entity) - * @method UnzerPaymentTransferInfoEntity[] getIterator() - * @method UnzerPaymentTransferInfoEntity[] getElements() - * @method null|UnzerPaymentTransferInfoEntity get(string $key) - * @method null|UnzerPaymentTransferInfoEntity first() - * @method null|UnzerPaymentTransferInfoEntity last() + * @method void add(UnzerPaymentTransferInfoEntity $entity) + * @method void set(string $key, UnzerPaymentTransferInfoEntity $entity) + * @method UnzerPaymentTransferInfoEntity[] getIterator() + * @method UnzerPaymentTransferInfoEntity[] getElements() + * @method UnzerPaymentTransferInfoEntity|null get(string $key) + * @method UnzerPaymentTransferInfoEntity|null first() + * @method UnzerPaymentTransferInfoEntity|null last() */ class UnzerPaymentTransferInfoEntityCollection extends EntityCollection { diff --git a/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityDefinition.php b/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityDefinition.php index af0cac69..1f9a0a0d 100644 --- a/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityDefinition.php +++ b/src/DataAbstractionLayer/Entity/TransferInfo/UnzerPaymentTransferInfoEntityDefinition.php @@ -42,13 +42,13 @@ protected function defineFields(): FieldCollection (new IdField('id', 'id'))->setFlags(new PrimaryKey(), new Required()), (new FkField('transaction_id', 'transactionId', OrderTransactionDefinition::class))->addFlags(new Required()), - (new FkField('transaction_version_id', 'transactionVersionId', OrderTransactionDefinition::class)), + new FkField('transaction_version_id', 'transactionVersionId', OrderTransactionDefinition::class), - (new StringField('iban', 'iban')), - (new StringField('bic', 'bic')), - (new StringField('holder', 'holder')), - (new StringField('descriptor', 'descriptor')), - (new FloatField('amount', 'amount')), + new StringField('iban', 'iban'), + new StringField('bic', 'bic'), + new StringField('holder', 'holder'), + new StringField('descriptor', 'descriptor'), + new FloatField('amount', 'amount'), new OneToOneAssociationField('transaction', 'transaction_id', 'id', OrderTransactionDefinition::class, false), diff --git a/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php b/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php index 8023d457..f420f7b4 100644 --- a/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php +++ b/src/DataAbstractionLayer/Extension/OrderTransactionExtension.php @@ -29,4 +29,9 @@ public function getDefinitionClass(): string { return OrderTransactionDefinition::class; } + + public function getEntityName(): string + { + return OrderTransactionDefinition::ENTITY_NAME; + } } diff --git a/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php b/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php index 29130985..e6194180 100644 --- a/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php +++ b/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepository.php @@ -4,7 +4,6 @@ namespace UnzerPayment6\DataAbstractionLayer\Repository\PaymentDevice; -use RuntimeException; use Shopware\Core\Checkout\Customer\CustomerEntity; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; @@ -16,23 +15,21 @@ use UnzerPayment6\Components\AddressHashGenerator\AddressHashGeneratorInterface; use UnzerPayment6\DataAbstractionLayer\Entity\PaymentDevice\UnzerPaymentDeviceEntity; -class UnzerPaymentDeviceRepository implements UnzerPaymentDeviceRepositoryInterface +readonly class UnzerPaymentDeviceRepository implements UnzerPaymentDeviceRepositoryInterface { - public function __construct( - private readonly EntityRepository $entityRepository, - private readonly AddressHashGeneratorInterface $addressHashService - ) - { + private EntityRepository $entityRepository, + private AddressHashGeneratorInterface $addressHashService + ) { } /** * {@inheritdoc} */ - public function getCollectionByCustomer(CustomerEntity $customer, Context $context, string $deviceType = null): EntitySearchResult + 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'); + throw new \RuntimeException('Customer has no active billing or shipping address'); } $addressHash = $this->addressHashService->generateHash($customer->getActiveBillingAddress(), $customer->getActiveShippingAddress()); @@ -61,17 +58,17 @@ public function create( Context $context ): EntityWrittenContainerEvent { if ($customer->getActiveBillingAddress() === null || $customer->getActiveShippingAddress() === null) { - throw new RuntimeException('Customer has no active billing or shipping address'); + 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(), + 'id' => Uuid::randomHex(), + 'deviceType' => $deviceType, + 'typeId' => $typeId, + 'data' => $data, + 'customerId' => $customer->getId(), 'addressHash' => $addressHash, ]; diff --git a/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepositoryInterface.php b/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepositoryInterface.php index 8cbe90dd..100387e1 100644 --- a/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepositoryInterface.php +++ b/src/DataAbstractionLayer/Repository/PaymentDevice/UnzerPaymentDeviceRepositoryInterface.php @@ -12,7 +12,7 @@ interface UnzerPaymentDeviceRepositoryInterface { - public function getCollectionByCustomer(CustomerEntity $customer, Context $context, string $deviceType = null): EntitySearchResult; + public function getCollectionByCustomer(CustomerEntity $customer, Context $context, ?string $deviceType = null): EntitySearchResult; public function create(CustomerEntity $customer, string $deviceType, string $typeId, array $data, Context $context): EntityWrittenContainerEvent; diff --git a/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php b/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php index b93f7393..aa152c3b 100644 --- a/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php +++ b/src/DataAbstractionLayer/Repository/TransferInfo/UnzerPaymentTransferInfoRepository.php @@ -12,9 +12,9 @@ use UnzerPayment6\Components\Struct\TransferInformation\TransferInformation; use UnzerPayment6\DataAbstractionLayer\Entity\TransferInfo\UnzerPaymentTransferInfoEntity; -class UnzerPaymentTransferInfoRepository implements UnzerPaymentTransferInfoRepositoryInterface +readonly class UnzerPaymentTransferInfoRepository implements UnzerPaymentTransferInfoRepositoryInterface { - public function __construct(private readonly EntityRepository $entityRepository) + public function __construct(private EntityRepository $entityRepository) { } diff --git a/src/EventListeners/Account/PaymentMethodPageEventListener.php b/src/EventListeners/Account/PaymentMethodPageEventListener.php index 869d683a..91ceba3a 100644 --- a/src/EventListeners/Account/PaymentMethodPageEventListener.php +++ b/src/EventListeners/Account/PaymentMethodPageEventListener.php @@ -12,7 +12,9 @@ class PaymentMethodPageEventListener implements EventSubscriberInterface { - /** @var UnzerPaymentDeviceRepositoryInterface */ + /** + * @var UnzerPaymentDeviceRepositoryInterface + */ private $deviceRepository; public function __construct(UnzerPaymentDeviceRepositoryInterface $deviceRepository) @@ -39,14 +41,14 @@ public function onLoadAccountPaymentMethod(AccountPaymentMethodPageLoadedEvent $ } $extension = new PaymentMethodPageExtension(); - $devices = $this->deviceRepository->getCollectionByCustomer($salesChannelContext->getCustomer(), $salesChannelContext->getContext()); + $devices = $this->deviceRepository->getCollectionByCustomer($salesChannelContext->getCustomer(), $salesChannelContext->getContext()); $extension->setDeviceRemoved((bool) $event->getRequest()->get('deviceRemoved')); if ($salesChannelContext->getCustomer() !== null) { - $creditCards = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_CREDIT_CARD)->getElements(); - $directDebitDevices = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_DIRECT_DEBIT)->getElements(); + $creditCards = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_CREDIT_CARD)->getElements(); + $directDebitDevices = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_DIRECT_DEBIT)->getElements(); $directDebitSecuredDevices = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_DIRECT_DEBIT_SECURED)->getElements(); - $payPalAccounts = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL)->getElements(); + $payPalAccounts = $devices->filterByProperty('deviceType', UnzerPaymentDeviceEntity::DEVICE_TYPE_PAYPAL)->getElements(); $extension->addPaymentDevices($creditCards); $extension->addPaymentDevices($directDebitDevices); diff --git a/src/EventListeners/Checkout/ConfirmPageEventListener.php b/src/EventListeners/Checkout/ConfirmPageEventListener.php index d98a7acd..6d1c10e0 100644 --- a/src/EventListeners/Checkout/ConfirmPageEventListener.php +++ b/src/EventListeners/Checkout/ConfirmPageEventListener.php @@ -8,28 +8,28 @@ use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; -use Shopware\Core\Framework\Uuid\Uuid; +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; use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent; use Shopware\Storefront\Page\PageLoadedEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Throwable; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\ConfigReader\KeyPairConfigReader; +use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Components\PaymentFrame\PaymentFrameFactoryInterface; use UnzerPayment6\Components\PaymentHandler\UnzerGooglePayPaymentHandler; +use UnzerPayment6\Components\ResourceHydrator\CustomerResourceHydrator\CustomerResourceHydratorInterface; +use UnzerPayment6\Components\Storefront\ExtensionFactory; use UnzerPayment6\Components\Struct\Configuration; use UnzerPayment6\Components\Struct\KeyPairContext; -use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\ApplePayPageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\ApplePayV2PageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\CreditCardPageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\DirectDebitPageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\DirectDebitSecuredPageExtension; -use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\FraudPreventionPageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\GooglePayPageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\InstallmentSecuredPageExtension; use UnzerPayment6\Components\Struct\PageExtension\Checkout\Confirm\PaylaterDirectDebitSecuredPageExtension; @@ -44,47 +44,19 @@ class ConfirmPageEventListener implements EventSubscriberInterface { - /** @var Configuration */ - protected $configData; - - /** @var UnzerPaymentDeviceRepositoryInterface */ - private $deviceRepository; - - /** @var ConfigReaderInterface */ - private $configReader; - - /** @var PaymentFrameFactoryInterface */ - private $paymentFrameFactory; - - /** @var SystemConfigService */ - private $systemConfigReader; - - /** @var EntityRepository */ - private $languageRepository; - - /** @var ClientFactoryInterface */ - private $clientFactory; - - /** @var KeyPairConfigReader */ - private $keyPairConfigReader; + private ?Configuration $configData = null; public function __construct( - UnzerPaymentDeviceRepositoryInterface $deviceRepository, - ConfigReaderInterface $configReader, - PaymentFrameFactoryInterface $paymentFrameFactory, - SystemConfigService $systemConfigReader, - EntityRepository $languageRepository, - ClientFactoryInterface $clientFactory, - KeyPairConfigReader $keyPairConfigReader - ) - { - $this->deviceRepository = $deviceRepository; - $this->configReader = $configReader; - $this->paymentFrameFactory = $paymentFrameFactory; - $this->systemConfigReader = $systemConfigReader; - $this->languageRepository = $languageRepository; - $this->clientFactory = $clientFactory; - $this->keyPairConfigReader = $keyPairConfigReader; + private readonly UnzerPaymentDeviceRepositoryInterface $deviceRepository, + private readonly ConfigReaderInterface $configReader, + private readonly PaymentFrameFactoryInterface $paymentFrameFactory, + private readonly SystemConfigService $systemConfigReader, + private readonly EntityRepository $languageRepository, + private readonly ClientFactoryInterface $clientFactory, + private readonly KeyPairConfigReader $keyPairConfigReader, + private readonly ExtensionFactory $extensionFactory, + private readonly CustomerResourceHydratorInterface $customerResourceHydrator, + ) { } /** @@ -122,32 +94,24 @@ public function onCheckoutConfirm(PageLoadedEvent $event): void case PaymentInstaller::PAYMENT_ID_DIRECT_DEBIT_SECURED: $this->addDirectDebitSecuredExtension($event); break; - case PaymentInstaller::PAYMENT_ID_PAYLATER_INVOICE: - $this->addFraudPreventionExtension($event); - break; case PaymentInstaller::PAYMENT_ID_INSTALLMENT_SECURED: $this->addInstallmentSecuredExtension($event); break; - case PaymentInstaller::PAYMENT_ID_APPLE_PAY: - $this->addApplePayExtension($event); - break; case PaymentInstaller::PAYMENT_ID_APPLE_PAY_V2: $this->addApplePayV2Extension($event); break; case PaymentInstaller::PAYMENT_ID_PAYLATER_INSTALLMENT: $this->addPaylaterInstallmentExtension($event); - $this->addFraudPreventionExtension($event); break; case PaymentInstaller::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED: $this->addPaylaterDirectDebitSecuredExtension($event); - $this->addFraudPreventionExtension($event); break; case PaymentInstaller::PAYMENT_ID_GOOGLE_PAY: $this->addGooglePayExtension($event); break; } - if (in_array($paymentMethodId, PaymentInstaller::PAYMENT_METHOD_IDS)) { + if (\in_array($paymentMethodId, PaymentInstaller::PAYMENT_METHOD_IDS, true)) { $this->addPaymentFrameExtension($event); $this->addUnzerDataExtension($event); } @@ -158,14 +122,6 @@ private function isActionRequired(PageLoadedEvent $event, PaymentMethodEntity $p return $event instanceof CheckoutConfirmPageLoadedEvent || ($event instanceof AccountEditOrderPageLoadedEvent && $paymentMethod->getAfterOrderEnabled()); } - private function addFraudPreventionExtension(PageLoadedEvent $event): void - { - $extension = new FraudPreventionPageExtension(); - $extension->setFraudPreventionSessionId(Uuid::randomHex()); - - $event->getPage()->addExtension(FraudPreventionPageExtension::EXTENSION_NAME, $extension); - } - private function addUnzerDataExtension(PageLoadedEvent $event): void { $context = $event->getSalesChannelContext()->getContext(); @@ -173,7 +129,7 @@ private function addUnzerDataExtension(PageLoadedEvent $event): void $extension = new UnzerDataPageExtension(); $extension->setPublicKey($this->getPublicKey($event->getSalesChannelContext())); $extension->setLocale($this->getLocaleByLanguageId($context->getLanguageId(), $context)); - $extension->setShowTestData((bool)$this->configData->get(ConfigReader::CONFIG_KEY_TEST_DATA)); + $extension->setShowTestData((bool) $this->configData->get(ConfigReader::CONFIG_KEY_TEST_DATA)); $extension->setUnzerCustomer($this->getUnzerCustomer($event)); $event->getPage()->addExtension(UnzerDataPageExtension::EXTENSION_NAME, $extension); @@ -181,25 +137,37 @@ private function addUnzerDataExtension(PageLoadedEvent $event): void private function getUnzerCustomer(PageLoadedEvent $event): ?Customer { - $customer = $event->getSalesChannelContext()->getCustomer(); + $shopwareCustomer = $event->getSalesChannelContext()->getCustomer(); - if ($customer === null) { + if ($shopwareCustomer === null) { return null; } - $client = $this->clientFactory->createClient(KeyPairContext::createFromSalesChannelContext($event->getSalesChannelContext())); - $customerNumber = $customer->getCustomerNumber(); - $billingAddress = $customer->getActiveBillingAddress(); - - if ($billingAddress !== null && !empty($billingAddress->getCompany())) { - $customerNumber .= '_b'; + $client = $this->clientFactory->createClientFromSalesChannelContext($event->getSalesChannelContext(), $event->getRequest()); + $customerNumber = $this->customerResourceHydrator->getShopCustomerId($shopwareCustomer); + try { + $existingCustomer = $client->fetchCustomerByExtCustomerId($customerNumber); + /** @var Customer $existingCustomer */ + $existingCustomer = $this->customerResourceHydrator->hydrateExistingCustomer($existingCustomer, $event->getSalesChannelContext()); + $customer = $client->updateCustomer($existingCustomer); + } catch (\Throwable) { + $customer = null; } - try { - return $client->fetchCustomerByExtCustomerId($customerNumber); - } catch (Throwable $t) { - return null; + if (empty($customer)) { + try { + // create one if not existing + $newCustomer = $this->customerResourceHydrator->hydrateObject( + $event->getSalesChannelContext()->getPaymentMethod()->getId(), + $event->getSalesChannelContext() + ); + $customer = $client->createCustomer($newCustomer); + } catch (\Throwable) { + $customer = null; + } } + + return $customer; } private function addPaymentFrameExtension(PageLoadedEvent $event): void @@ -220,7 +188,7 @@ private function addPaymentFrameExtension(PageLoadedEvent $event): void PaymentFramePageExtension::EXTENSION_NAME, (new PaymentFramePageExtension()) ->setPaymentFrame($mappedFrameTemplate) - ->setShopName(is_string($shopName) ? $shopName : '') + ->setShopName(\is_string($shopName) ? $shopName : '') ); } @@ -263,6 +231,13 @@ private function addPayPalExtension(PageLoadedEvent $event): void 'paypalShowSaveAccount' => $this->configData->get(ConfigReader::CONFIG_KEY_PAYPAL_SHOW_SAVE_ACCOUNT), ]); + if ($event->getRequest()->getSession()->get(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID)) { + $extension->setPayPalAccounts([]); + $extension->setPublicConfig([ + 'paypalShowSaveAccount' => false, + ]); + } + $event->getPage()->addExtension(PayPalPageExtension::EXTENSION_NAME, $extension); } @@ -319,37 +294,28 @@ private function addInstallmentSecuredExtension(PageLoadedEvent $event): void $event->getPage()->addExtension(InstallmentSecuredPageExtension::EXTENSION_NAME, $extension); } - private function addApplePayExtension(PageLoadedEvent $event): void - { - $event->getPage()->addExtension(ApplePayPageExtension::EXTENSION_NAME, new ApplePayPageExtension()); - } - private function addApplePayV2Extension(PageLoadedEvent $event): void { - $event->getPage()->addExtension(ApplePayV2PageExtension::EXTENSION_NAME, new ApplePayV2PageExtension()); + $extension = $this->extensionFactory->getApplePayExtension($event->getSalesChannelContext()->getSalesChannelId()); + $publicConfig = $extension->getPublicConfig(); + $publicConfig['paymentTypeId'] = $event->getRequest()->getSession()->get(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID); + $extension->setPublicConfig($publicConfig); + $event->getPage()->addExtension(ApplePayV2PageExtension::EXTENSION_NAME, $extension); } private function addGooglePayExtension(PageLoadedEvent $event): void { - - $extension = new GooglePayPageExtension(); - $extension->setPublicConfig([ - 'merchantName' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_MERCHANT_NAME), - 'merchantId' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_MERCHANT_ID), - 'gatewayMerchantId' => $this->fetchGooglePayChannelId($event), - 'countryCode' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_COUNTRY_CODE), - 'allowedCardNetworks' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_CARD_NETWORKS), - 'allowCreditCards' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_CREDIT_CARDS_ALLOWED), - 'allowPrepaidCards' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_PREPAID_CARDS_ALLOWED), - 'buttonColor' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_BUTTON_COLOR), - 'buttonSizeMode' => $this->configData->get(ConfigReader::CONFIG_KEY_GOOGLE_PAY_BUTTON_SIZE_MODE), - ]); + $extension = $this->extensionFactory->getGooglePayExtension($event->getSalesChannelContext()->getSalesChannelId()); + $publicConfig = $extension->getPublicConfig(); + $publicConfig['paymentTypeId'] = $event->getRequest()->getSession()->get(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID); + $extension->setPublicConfig($publicConfig); $event->getPage()->addExtension(GooglePayPageExtension::EXTENSION_NAME, $extension); } - private function fetchGooglePayChannelId(PageLoadedEvent $event) + private function fetchGooglePayChannelId(PageLoadedEvent $event): string { - $client = $this->clientFactory->createClient(KeyPairContext::createFromSalesChannelContext($event->getSalesChannelContext())); + $client = $this->clientFactory->createClientFromSalesChannelContext($event->getSalesChannelContext(), $event->getRequest()); + return UnzerGooglePayPaymentHandler::fetchChannelId($client); } @@ -383,11 +349,11 @@ private function addPaylaterDirectDebitSecuredExtension(PageLoadedEvent $event): private function getLocaleByLanguageId(string $languageId, Context $context): string { - $critera = new Criteria([$languageId]); - $critera->addAssociation('locale'); + $criteria = new Criteria([$languageId]); + $criteria->addAssociation('locale'); - /** @var null|\Shopware\Core\System\Language\LanguageEntity $searchResult */ - $searchResult = $this->languageRepository->search($critera, $context)->first(); + /** @var LanguageEntity|null $searchResult */ + $searchResult = $this->languageRepository->search($criteria, $context)->first(); if ($searchResult === null || $searchResult->getLocale() === null) { return ClientFactoryInterface::DEFAULT_LOCALE; diff --git a/src/EventListeners/Checkout/FinishPageEventListener.php b/src/EventListeners/Checkout/FinishPageEventListener.php index 8bd0e1d4..bbe8206c 100644 --- a/src/EventListeners/Checkout/FinishPageEventListener.php +++ b/src/EventListeners/Checkout/FinishPageEventListener.php @@ -5,34 +5,29 @@ namespace UnzerPayment6\EventListeners\Checkout; use Psr\Log\LoggerInterface; +use Shopware\Core\Checkout\Order\Aggregate\OrderTransaction\OrderTransactionEntity; +use Shopware\Core\Framework\Context; use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; use UnzerPayment6\Components\ClientFactory\ClientFactoryInterface; +use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Components\Struct\InstallmentSecured\InstallmentInfo; use UnzerPayment6\Components\Struct\KeyPairContext; use UnzerPayment6\Components\Struct\PageExtension\Checkout\FinishPageExtension; use UnzerPayment6\Components\TransactionSelectionHelper\TransactionSelectionHelperInterface; -use UnzerSDK\Exceptions\UnzerApiException; +use UnzerPayment6\Components\UnzerUtil\UnzerTransactionUtil; use UnzerSDK\Resources\InstalmentPlan; use UnzerSDK\Resources\Payment; use UnzerSDK\Unzer; -class FinishPageEventListener implements EventSubscriberInterface +readonly class FinishPageEventListener implements EventSubscriberInterface { - /** @var ClientFactoryInterface */ - private $clientFactory; - - /** @var LoggerInterface */ - private $logger; - - /** @var TransactionSelectionHelperInterface */ - private $transactionSelectionHelper; - - public function __construct(ClientFactoryInterface $clientFactory, LoggerInterface $logger, TransactionSelectionHelperInterface $transactionSelectionHelper) - { - $this->clientFactory = $clientFactory; - $this->logger = $logger; - $this->transactionSelectionHelper = $transactionSelectionHelper; + public function __construct( + private ClientFactoryInterface $clientFactory, + private LoggerInterface $logger, + private TransactionSelectionHelperInterface $transactionSelectionHelper, + ) { } public static function getSubscribedEvents(): array @@ -44,9 +39,10 @@ public static function getSubscribedEvents(): array public function onCheckoutFinish(CheckoutFinishPageLoadedEvent $event): void { + $this->unsetExpressData($event->getRequest()); $salesChannelContext = $event->getSalesChannelContext(); - $page = $event->getPage(); - $unzerTransaction = $this->transactionSelectionHelper->getBestUnzerTransaction($page->getOrder()); + $page = $event->getPage(); + $unzerTransaction = $this->transactionSelectionHelper->getBestUnzerTransaction($page->getOrder()); if (!$unzerTransaction) { return; @@ -61,14 +57,10 @@ public function onCheckoutFinish(CheckoutFinishPageLoadedEvent $event): void } $extension = new FinishPageExtension(); - $payment = $this->getPaymentByOrderId($unzerClient, $unzerTransaction->getId()); + $payment = $this->getPaymentByOrderTransaction($unzerClient, $unzerTransaction, $salesChannelContext->getContext()); if (!$payment) { - $payment = $this->getPaymentByOrderId($unzerClient, $unzerTransaction->getOrderId()); - - if (!$payment) { - return; - } + return; } $paymentType = $payment->getPaymentType(); @@ -81,20 +73,33 @@ public function onCheckoutFinish(CheckoutFinishPageLoadedEvent $event): void $event->getPage()->addExtension(FinishPageExtension::EXTENSION_NAME, $extension); } - private function getPaymentByOrderId(Unzer $unzerClient, string $orderId): ?Payment + private function getPaymentByOrderTransaction(Unzer $unzerClient, OrderTransactionEntity $orderTransaction, Context $context): ?Payment { try { - return $unzerClient->fetchPaymentByOrderId($orderId); - } catch (UnzerApiException $exception) { - //catch payment not found exception so that shopware can handle its own errors + return UnzerTransactionUtil::fetchPaymentFromOrderTransaction($orderTransaction, $unzerClient); + } catch (\Throwable $exception) { $this->logger->error($exception->getMessage(), [ - 'code' => $exception->getCode(), + 'code' => $exception->getCode(), 'clientMessage' => $exception->getClientMessage(), - 'file' => $exception->getFile(), - 'trace' => $exception->getTraceAsString(), + 'file' => $exception->getFile(), + 'trace' => $exception->getTraceAsString(), ]); } return null; } + + private function unsetExpressData(Request $request): void + { + try { + $session = $request->getSession(); + $session->remove(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID); + $session->remove(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID); + $session->remove(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID); + $session->remove(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_TYPE_ID); + $session->remove(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD); + } catch (\Throwable $exception) { + // not worth handling + } + } } diff --git a/src/EventListeners/DataAbstractionLayer/OrderTransactionEventListener.php b/src/EventListeners/DataAbstractionLayer/OrderTransactionEventListener.php index ec1d551b..4e8d900c 100644 --- a/src/EventListeners/DataAbstractionLayer/OrderTransactionEventListener.php +++ b/src/EventListeners/DataAbstractionLayer/OrderTransactionEventListener.php @@ -10,14 +10,11 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use UnzerPayment6\DataAbstractionLayer\Repository\TransferInfo\UnzerPaymentTransferInfoRepositoryInterface; -class OrderTransactionEventListener implements EventSubscriberInterface +readonly class OrderTransactionEventListener implements EventSubscriberInterface { - /** @var UnzerPaymentTransferInfoRepositoryInterface */ - private $transferInfoRepository; - - public function __construct(UnzerPaymentTransferInfoRepositoryInterface $transferInfoRepository) - { - $this->transferInfoRepository = $transferInfoRepository; + public function __construct( + private UnzerPaymentTransferInfoRepositoryInterface $transferInfoRepository + ) { } /** diff --git a/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php b/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php new file mode 100644 index 00000000..280c61c6 --- /dev/null +++ b/src/EventListeners/ExpressButtons/ExpressButtonsEventListener.php @@ -0,0 +1,56 @@ + 'addExpressButtons', + OffcanvasCartPageLoadedEvent::class => 'addExpressButtons', + ]; + } + + public function addExpressButtons(PageLoadedEvent $event): void + { + $config = $this->configReader->read($event->getSalesChannelContext()->getSalesChannel()->getId()); + + if (!$config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_PAYPAL) + && !$config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_GOOGLE) + && !$config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_APPLEPAY)) { + return; + } + + $event->getPage()->addExtension('UnzerExpressButtons', new ArrayStruct([ + 'publicKey' => $config->get(ConfigReader::CONFIG_KEY_PUBLIC_KEY), + 'usePaypal' => $config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_PAYPAL), + 'useGooglePay' => $config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_GOOGLE), + 'useApplePay' => $config->get(ConfigReader::CONFIG_KEY_USE_EXPRESS_APPLEPAY), + ])); + $googlePayExtension = $this->extensionFactory->getGooglePayExtension($event->getSalesChannelContext()->getSalesChannelId()); + $event->getPage()->addExtension(GooglePayPageExtension::EXTENSION_NAME, $googlePayExtension); + + $applePayExtension = $this->extensionFactory->getApplePayExtension($event->getSalesChannelContext()->getSalesChannelId()); + $event->getPage()->addExtension(ApplePayV2PageExtension::EXTENSION_NAME, $applePayExtension); + } +} diff --git a/src/EventListeners/Page/NavigationPageEventListener.php b/src/EventListeners/Page/NavigationPageEventListener.php new file mode 100644 index 00000000..bf6e6c1d --- /dev/null +++ b/src/EventListeners/Page/NavigationPageEventListener.php @@ -0,0 +1,48 @@ + 'onNavPageLoaded', + ]; + } + + public function onNavPageLoaded(NavigationPageLoadedEvent $event): void + { + $request = $event->getRequest(); + if (($request->attributes->get('_route') ?? null) !== 'frontend.home.page') { + return; + } + + if (!$request->hasSession()) { + return; + } + + $this->unsetExpressData($request); + } + + private function unsetExpressData(Request $request): void + { + try { + $session = $request->getSession(); + $session->remove(ExpressCheckoutService::SESSION_APPLEPAY_PAYMENT_TYPE_ID); + $session->remove(ExpressCheckoutService::SESSION_GOOGLE_PAYMENT_TYPE_ID); + $session->remove(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_ID); + $session->remove(ExpressCheckoutService::SESSION_PAYPAL_PAYMENT_TYPE_ID); + $session->remove(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD); + } catch (\Throwable $exception) { + // not worth handling + } + } +} diff --git a/src/EventListeners/PaymentMethod/PaymentMethodLoadedEventListener.php b/src/EventListeners/PaymentMethod/PaymentMethodLoadedEventListener.php index a1cdc679..73816b85 100644 --- a/src/EventListeners/PaymentMethod/PaymentMethodLoadedEventListener.php +++ b/src/EventListeners/PaymentMethod/PaymentMethodLoadedEventListener.php @@ -5,7 +5,9 @@ namespace UnzerPayment6\EventListeners\PaymentMethod; use Shopware\Core\Checkout\Payment\Cart\Error\PaymentMethodBlockedError; +use Shopware\Core\Checkout\Payment\Event\PaymentMethodRouteCacheKeyEvent; use Shopware\Core\Checkout\Payment\PaymentMethodEntity; +use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchResultLoadedEvent; use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult; use Shopware\Core\Framework\DataAbstractionLayer\Search\IdSearchResult; use Shopware\Core\System\Currency\CurrencyEntity; @@ -15,34 +17,47 @@ use Shopware\Storefront\Page\Account\Order\AccountEditOrderPageLoadedEvent; use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\RequestStack; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; +use UnzerPayment6\Components\ExpressCheckout\ExpressCheckoutService; use UnzerPayment6\Installer\PaymentInstaller; use UnzerPayment6\UnzerPayment6; -class PaymentMethodLoadedEventListener implements EventSubscriberInterface +readonly class PaymentMethodLoadedEventListener implements EventSubscriberInterface { - /** @var ConfigReaderInterface */ - private $configReader; - - public function __construct(ConfigReaderInterface $configReader) - { - $this->configReader = $configReader; + public function __construct( + private ConfigReaderInterface $configReader, + private RequestStack $requestStack + ) { } public static function getSubscribedEvents(): array { return [ 'sales_channel.payment_method.search.id.result.loaded' => ['onSalesChannelIdSearchResultLoaded', -1], - 'sales_channel.payment_method.search.result.loaded' => ['onSalesChannelSearchResultLoaded', -1], - AccountEditOrderPageLoadedEvent::class => 'onAccountEditOrderPageLoaded', - CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded', + 'sales_channel.payment_method.search.result.loaded' => ['onSalesChannelSearchResultLoaded', -1], + 'payment_method.search.result.loaded' => ['onSearchResultLoaded', -1], + AccountEditOrderPageLoadedEvent::class => 'onAccountEditOrderPageLoaded', + CheckoutConfirmPageLoadedEvent::class => 'onCheckoutConfirmPageLoaded', + PaymentMethodRouteCacheKeyEvent::class => 'addCacheKeyParts', ]; } + public function onSearchResultLoaded(EntitySearchResultLoadedEvent $event): void + { + foreach (PaymentInstaller::REMOVED_PAYMENT_METHOD_IDS as $removedPaymentMethodId) { + try { + $event->getResult()->remove($removedPaymentMethodId); + } catch (\Exception $e) { + // no worries + } + } + } + public function onSalesChannelIdSearchResultLoaded(SalesChannelEntityIdSearchResultLoadedEvent $event): void { - $result = $event->getResult(); + $result = $event->getResult(); $salesChannelContext = $event->getSalesChannelContext(); if (!$this->isConfigurationValid($salesChannelContext->getSalesChannel()->getId())) { @@ -52,6 +67,7 @@ public function onSalesChannelIdSearchResultLoaded(SalesChannelEntityIdSearchRes } $blockedPaymentMethods = $this->getBlockedPaymentMethods($salesChannelContext); + $blockedPaymentMethods = array_merge($blockedPaymentMethods, $this->getUnselectedExpressPaymentMethods($result)); if ($blockedPaymentMethods === []) { return; @@ -62,7 +78,7 @@ public function onSalesChannelIdSearchResultLoaded(SalesChannelEntityIdSearchRes public function onSalesChannelSearchResultLoaded(SalesChannelEntitySearchResultLoadedEvent $event): void { - $result = $event->getResult(); + $result = $event->getResult(); $salesChannelContext = $event->getSalesChannelContext(); if (!$this->isConfigurationValid($salesChannelContext->getSalesChannel()->getId())) { @@ -72,6 +88,7 @@ public function onSalesChannelSearchResultLoaded(SalesChannelEntitySearchResultL } $blockedPaymentMethods = $this->getBlockedPaymentMethods($salesChannelContext); + $blockedPaymentMethods = array_merge($blockedPaymentMethods, $this->getUnselectedExpressPaymentMethods($result)); if ($blockedPaymentMethods === []) { return; @@ -82,14 +99,15 @@ public function onSalesChannelSearchResultLoaded(SalesChannelEntitySearchResultL public function onAccountEditOrderPageLoaded(AccountEditOrderPageLoadedEvent $pageLoadedEvent): void { - $page = $pageLoadedEvent->getPage(); - $order = $page->getOrder(); + $page = $pageLoadedEvent->getPage(); + $order = $page->getOrder(); $totalAmount = $order->getAmountTotal(); if ($this->isZeroAmount($totalAmount, $pageLoadedEvent->getSalesChannelContext()->getCurrency())) { - $page->setPaymentMethods($page->getPaymentMethods()->filter(static function (PaymentMethodEntity $paymentMethod) { - return !in_array($paymentMethod->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true); - }) + $page->setPaymentMethods( + $page->getPaymentMethods()->filter(static function (PaymentMethodEntity $paymentMethod) { + return !\in_array($paymentMethod->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true); + }) ); $pageLoadedEvent->getSalesChannelContext()->assign(['paymentMethods' => $page->getPaymentMethods()]); } @@ -98,34 +116,68 @@ public function onAccountEditOrderPageLoaded(AccountEditOrderPageLoadedEvent $pa public function onCheckoutConfirmPageLoaded(CheckoutConfirmPageLoadedEvent $pageLoadedEvent): void { $salesChannelContext = $pageLoadedEvent->getSalesChannelContext(); - $page = $pageLoadedEvent->getPage(); - $cart = $page->getCart(); - $totalAmount = $cart->getPrice()->getTotalPrice(); + $page = $pageLoadedEvent->getPage(); + $cart = $page->getCart(); + $totalAmount = $cart->getPrice()->getTotalPrice(); if ($this->isZeroAmount($totalAmount, $salesChannelContext->getCurrency())) { - $page->setPaymentMethods($page->getPaymentMethods()->filter(static function (PaymentMethodEntity $paymentMethod) { - return !in_array($paymentMethod->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true); - }) + $page->setPaymentMethods( + $page->getPaymentMethods()->filter(static function (PaymentMethodEntity $paymentMethod) { + return !\in_array($paymentMethod->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true); + }) ); $salesChannelContext->assign(['paymentMethods' => $page->getPaymentMethods()]); } - if (in_array($salesChannelContext->getPaymentMethod()->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true) - && !array_key_exists($salesChannelContext->getPaymentMethod()->getId(), $page->getPaymentMethods()->getElements())) { + if (\in_array($salesChannelContext->getPaymentMethod()->getId(), PaymentInstaller::PAYMENT_METHOD_IDS, true) + && !\array_key_exists($salesChannelContext->getPaymentMethod()->getId(), $page->getPaymentMethods()->getElements())) { $page->getCart()->addErrors(new PaymentMethodBlockedError($salesChannelContext->getPaymentMethod()->getName() ?? 'unknown')); } } + public function addCacheKeyParts(PaymentMethodRouteCacheKeyEvent $event): void + { + $salesChannelContext = $event->getContext(); + $event->addPart('BlockedPaymentMethodsCurrency_' . $salesChannelContext->getCurrency()->getIsoCode()); + + $invoiceIso = $salesChannelContext + ->getCustomer()?->getActiveBillingAddress()?->getCountry()?->getIso(); + + if (!empty($invoiceIso)) { + $event->addPart('BlockedPaymentMethodsInvoiceCtry_' . $invoiceIso); + } + + $customerCompany = $salesChannelContext->getCustomer()?->getActiveBillingAddress()?->getCompany(); + + if (!empty($customerCompany)) { + $event->addPart('BlockedPaymentMethodsB2B'); + } + + if (!$event->getRequest()->hasSession()) { + return; + } + + $isExpress = $event->getRequest()->query->getBoolean('isExpressCheckout', false) ?? false; + if (!$isExpress) { + return; + } + + $session = $event->getRequest()->getSession(); + $expressPaymentMethodId = $session->get(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD); + + $event->addPart('unzerExpressActive_' . $expressPaymentMethodId); + } + protected function removePaymentMethodsFromIdResult(IdSearchResult $result, array $paymentIdsToBeRemoved): void { $filteredPaymentMethods = array_filter($result->getIds(), static function ($paymentMethod) use ($paymentIdsToBeRemoved) { - return !in_array($paymentMethod, $paymentIdsToBeRemoved, true); + return !\in_array($paymentMethod, $paymentIdsToBeRemoved, true); }); $result->assign([ - 'total' => count($filteredPaymentMethods), - 'ids' => $filteredPaymentMethods, + 'total' => \count($filteredPaymentMethods), + 'ids' => $filteredPaymentMethods, 'entities' => $filteredPaymentMethods, 'elements' => $filteredPaymentMethods, ]); @@ -134,11 +186,11 @@ protected function removePaymentMethodsFromIdResult(IdSearchResult $result, arra protected function removePaymentMethodsFromResult(EntitySearchResult $result, array $paymentIdsToBeRemoved): void { $filteredResult = $result->getEntities()->filter(static function (PaymentMethodEntity $entity) use ($paymentIdsToBeRemoved) { - return !in_array($entity->getId(), $paymentIdsToBeRemoved, true); + return !\in_array($entity->getId(), $paymentIdsToBeRemoved, true); }); $result->assign([ - 'total' => count($filteredResult), + 'total' => \count($filteredResult), 'entities' => $filteredResult, 'elements' => $filteredResult->getElements(), ]); @@ -153,7 +205,7 @@ protected function isConfigurationValid(string $salesChannelId): bool protected function isZeroAmount(float $totalAmount, CurrencyEntity $currency): bool { - $currencyPrecision = min($currency->getItemRounding()->getDecimals(), UnzerPayment6::MAX_DECIMAL_PRECISION); + $currencyPrecision = min($currency->getItemRounding()->getDecimals(), UnzerPayment6::MAX_DECIMAL_PRECISION); $roundedAmountTotal = (int) round($totalAmount * (10 ** $currencyPrecision)); return $roundedAmountTotal <= 0; @@ -161,12 +213,11 @@ protected function isZeroAmount(float $totalAmount, CurrencyEntity $currency): b protected function getBlockedPaymentMethods(SalesChannelContext $salesChannelContext): array { - $paymentMethodIdsToBeRemoved = [ - PaymentInstaller::PAYMENT_ID_GIROPAY, - ]; + $paymentMethodIdsToBeRemoved = PaymentInstaller::REMOVED_PAYMENT_METHOD_IDS; if ($salesChannelContext->getCurrency()->getIsoCode() !== 'EUR') { $paymentMethodIdsToBeRemoved[] = PaymentInstaller::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED; + $paymentMethodIdsToBeRemoved[] = PaymentInstaller::PAYMENT_ID_WERO; } $customer = $salesChannelContext->getCustomer(); @@ -186,7 +237,33 @@ protected function getBlockedPaymentMethods(SalesChannelContext $salesChannelCon if ($invoiceCountry !== null && $invoiceCountry->getIso() !== 'DE' && $invoiceCountry->getIso() !== 'AT') { $paymentMethodIdsToBeRemoved[] = PaymentInstaller::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED; } + if ($invoiceCountry !== null && $invoiceCountry->getIso() !== 'DE') { + $paymentMethodIdsToBeRemoved[] = PaymentInstaller::PAYMENT_ID_WERO; + } + + if (!empty($customer->getActiveBillingAddress()?->getCompany())) { + $paymentMethodIdsToBeRemoved[] = PaymentInstaller::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED; + $paymentMethodIdsToBeRemoved[] = PaymentInstaller::PAYMENT_ID_PAYLATER_INSTALLMENT; + } return $paymentMethodIdsToBeRemoved; } + + protected function getUnselectedExpressPaymentMethods(EntitySearchResult $result): array + { + $unselectedIds = []; + $request = $this->requestStack->getCurrentRequest(); + $isExpress = $request?->query->getBoolean('isExpressCheckout', false) ?? false; + if ($isExpress) { + $session = $request && $request->hasSession() ? $request->getSession() : null; + $expressPaymentMethodId = $session->get(ExpressCheckoutService::SESSION_SELECTED_EXPRESS_METHOD); + foreach ($result->getIds() as $id) { + if ($id !== $expressPaymentMethodId) { + $unselectedIds[] = $id; + } + } + } + + return $unselectedIds; + } } diff --git a/src/EventListeners/StateMachine/TransitionEventListener.php b/src/EventListeners/StateMachine/TransitionEventListener.php index c5559e85..ddc2277f 100644 --- a/src/EventListeners/StateMachine/TransitionEventListener.php +++ b/src/EventListeners/StateMachine/TransitionEventListener.php @@ -17,7 +17,6 @@ use Shopware\Core\System\StateMachine\Event\StateMachineTransitionEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Throwable; use UnzerPayment6\Components\ConfigReader\ConfigReader; use UnzerPayment6\Components\ConfigReader\ConfigReaderInterface; use UnzerPayment6\Components\Event\AutomaticShippingNotificationEvent; @@ -26,52 +25,19 @@ use UnzerPayment6\Components\Validator\AutomaticShippingValidatorInterface; use UnzerPayment6\Installer\CustomFieldInstaller; -class TransitionEventListener implements EventSubscriberInterface +readonly class TransitionEventListener implements EventSubscriberInterface { - /** @var EntityRepository */ - private $orderRepository; - - /** @var EntityRepository */ - private $orderDeliveryRepository; - - /** @var EntityRepository */ - private $transactionRepository; - - /** @var LoggerInterface */ - private $logger; - - /** @var AutomaticShippingValidatorInterface */ - private $automaticShippingValidator; - - /** @var EventDispatcherInterface */ - private $eventDispatcher; - - /** @var ShipServiceInterface */ - private $shipService; - private ConfigReaderInterface $configReader; - private UnzerTransactionUtil $unzerTransactionUtil; - public function __construct( - EntityRepository $orderRepository, - EntityRepository $orderDeliveryRepository, - EntityRepository $transactionRepository, - AutomaticShippingValidatorInterface $automaticShippingValidator, - LoggerInterface $logger, - EventDispatcherInterface $eventDispatcher, - ShipServiceInterface $shipService, - ConfigReaderInterface $configReader, - UnzerTransactionUtil $unzerTransactionUtil - ) - { - $this->orderRepository = $orderRepository; - $this->orderDeliveryRepository = $orderDeliveryRepository; - $this->transactionRepository = $transactionRepository; - $this->logger = $logger; - $this->automaticShippingValidator = $automaticShippingValidator; - $this->eventDispatcher = $eventDispatcher; - $this->shipService = $shipService; - $this->configReader = $configReader; - $this->unzerTransactionUtil = $unzerTransactionUtil; + private EntityRepository $orderRepository, + private EntityRepository $orderDeliveryRepository, + private EntityRepository $transactionRepository, + private AutomaticShippingValidatorInterface $automaticShippingValidator, + private LoggerInterface $logger, + private EventDispatcherInterface $eventDispatcher, + private ShipServiceInterface $shipService, + private ConfigReaderInterface $configReader, + private UnzerTransactionUtil $unzerTransactionUtil + ) { } /** @@ -86,22 +52,19 @@ public static function getSubscribedEvents(): array public function onStateMachineTransition(StateMachineTransitionEvent $event): void { - $order = $this->getOrderFromEvent($event); $this->registerShipping($event, $order); $this->doAutomaticTransactions($event, $order); - } protected function registerShipping(StateMachineTransitionEvent $event, ?OrderEntity $order): void { - if (!$order || !$this->automaticShippingValidator->shouldSendAutomaticShipping($order, $event->getToPlace())) { return; } if (!$this->automaticShippingValidator->hasInvoiceDocument($order)) { - $this->logger->error(sprintf('Error during automatic shipping validation for order [%s]: No invoice could be found', $order->getOrderNumber())); + $this->logger->error(\sprintf('Error during automatic shipping validation for order [%s]: No invoice could be found', $order->getOrderNumber())); return; } @@ -117,13 +80,13 @@ protected function registerShipping(StateMachineTransitionEvent $event, ?OrderEn } if (empty($firstTransaction)) { - $this->logger->error(sprintf('Error while executing automatic shipping notification for order [%s]: orderTransaction could not be found', $order->getOrderNumber())); + $this->logger->error(\sprintf('Error while executing automatic shipping notification for order [%s]: orderTransaction could not be found', $order->getOrderNumber())); return; } if (empty($invoiceNumber)) { - $this->logger->error(sprintf('Error while executing automatic shipping notification for order [%s]: Either invoice could not be found', $order->getOrderNumber())); + $this->logger->error(\sprintf('Error while executing automatic shipping notification for order [%s]: Either invoice could not be found', $order->getOrderNumber())); return; } @@ -133,15 +96,15 @@ protected function registerShipping(StateMachineTransitionEvent $event, ?OrderEn $this->setCustomFields($event->getContext(), $firstTransaction); $this->eventDispatcher->dispatch(new AutomaticShippingNotificationEvent($order, $invoiceNumber, $event->getContext())); - $this->logger->info(sprintf('The automatic shipping notification for order [%s] was executed with invoice [%s]', $order->getOrderNumber(), $invoiceNumber)); - } catch (Throwable $exception) { - $this->logger->error(sprintf('Error while executing automatic shipping notification for order [%s]: %s', $order->getOrderNumber(), $exception->getMessage()), [ + $this->logger->info(\sprintf('The automatic shipping notification for order [%s] was executed with invoice [%s]', $order->getOrderNumber(), $invoiceNumber)); + } catch (\Throwable $exception) { + $this->logger->error(\sprintf('Error while executing automatic shipping notification for order [%s]: %s', $order->getOrderNumber(), $exception->getMessage()), [ 'trace' => $exception->getTraceAsString(), ]); } } - protected function doAutomaticTransactions(StateMachineTransitionEvent $event, ?OrderEntity $order) + protected function doAutomaticTransactions(StateMachineTransitionEvent $event, ?OrderEntity $order): void { if ($order === null) { return; @@ -149,43 +112,41 @@ protected function doAutomaticTransactions(StateMachineTransitionEvent $event, ? $config = $this->configReader->read($order->getSalesChannelId()); $autoCaptureStatus = $config->get(ConfigReader::CONFIG_KEY_DELIVERY_STATUS_FOR_CAPTURE); - if (is_scalar($autoCaptureStatus)) { + if (\is_scalar($autoCaptureStatus)) { $autoCaptureStatus = [$autoCaptureStatus]; } - if (is_array($autoCaptureStatus) && in_array($event->getToPlace()->getId(), $autoCaptureStatus)) { - $this->logger->info(sprintf('Automatic capture for order [%s] was triggered', $order->getOrderNumber())); + 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->unzerTransactionUtil->captureOrder($order, $event->getContext()); - } catch (Throwable $exception) { - $this->logger->error(sprintf('Error while executing automatic capture for order [%s]: %s', $order->getOrderNumber(), $exception->getMessage()), [ + } catch (\Throwable $exception) { + $this->logger->error(\sprintf('Error while executing automatic capture for order [%s]: %s', $order->getOrderNumber(), $exception->getMessage()), [ 'trace' => $exception->getTraceAsString(), ]); } } $autoRefundStatus = $config->get(ConfigReader::CONFIG_KEY_DELIVERY_STATUS_FOR_REFUND); - if (is_scalar($autoRefundStatus)) { + if (\is_scalar($autoRefundStatus)) { $autoRefundStatus = [$autoRefundStatus]; } - if (is_array($autoRefundStatus) && in_array($event->getToPlace()->getId(), $autoRefundStatus)) { - $this->logger->info(sprintf('Automatic refund for order [%s] was triggered', $order->getOrderNumber())); + if (\is_array($autoRefundStatus) && \in_array($event->getToPlace()->getId(), $autoRefundStatus, true)) { + $this->logger->info(\sprintf('Automatic refund for order [%s] was triggered', $order->getOrderNumber())); try { $this->unzerTransactionUtil->refundOrder($order, $event->getContext()); - } catch (Throwable $exception) { - $this->logger->error(sprintf('Error while executing automatic refund for order [%s]: %s', $order->getOrderNumber(), $exception->getMessage()), [ + } catch (\Throwable $exception) { + $this->logger->error(\sprintf('Error while executing automatic refund for order [%s]: %s', $order->getOrderNumber(), $exception->getMessage()), [ 'trace' => $exception->getTraceAsString(), ]); } } - } protected function setCustomFields( - Context $context, + Context $context, OrderTransactionEntity $transaction - ): void - { + ): void { $customFields = $transaction->getCustomFields() ?? []; $customFields = array_merge($customFields, [ CustomFieldInstaller::UNZER_PAYMENT_IS_SHIPPED => true, @@ -201,8 +162,8 @@ protected function setCustomFields( private function getOrderFromEvent(StateMachineTransitionEvent $transitionEvent): ?OrderEntity { + $criteria = new Criteria([$transitionEvent->getEntityId()]); if ($transitionEvent->getEntityName() === OrderDeliveryDefinition::ENTITY_NAME) { - $criteria = new Criteria([$transitionEvent->getEntityId()]); $criteria->addAssociations([ 'order', 'order.transactions', @@ -210,17 +171,12 @@ private function getOrderFromEvent(StateMachineTransitionEvent $transitionEvent) 'order.documents.documentType', ]); - /** @var null|OrderDeliveryEntity $orderDeliveryEntity */ + /** @var OrderDeliveryEntity|null $orderDeliveryEntity */ $orderDeliveryEntity = $this->orderDeliveryRepository->search($criteria, $transitionEvent->getContext())->first(); - if ($orderDeliveryEntity === null) { - return null; - } - - return $orderDeliveryEntity->getOrder(); + return $orderDeliveryEntity?->getOrder(); } - $criteria = new Criteria([$transitionEvent->getEntityId()]); $criteria->addAssociations([ 'transactions', 'documents', diff --git a/src/Installer/CustomFieldInstaller.php b/src/Installer/CustomFieldInstaller.php index 657ef93c..8b459b67 100755 --- a/src/Installer/CustomFieldInstaller.php +++ b/src/Installer/CustomFieldInstaller.php @@ -12,43 +12,43 @@ use Shopware\Core\Framework\Plugin\Context\UpdateContext; use Shopware\Core\System\CustomField\CustomFieldTypes; -class CustomFieldInstaller implements InstallerInterface +readonly class CustomFieldInstaller implements InstallerInterface { public const UNZER_PAYMENT_IS_TRANSACTION = 'unzer_payment_is_transaction'; - public const UNZER_PAYMENT_IS_SHIPPED = 'unzer_payment_is_shipped'; + public const UNZER_PAYMENT_IS_SHIPPED = 'unzer_payment_is_shipped'; public const UNZER_PAYMENT_PAYMENT_ID_KEY = 'unzer_pay_payment_id'; - public const UNZER_PAYMENT_TRANSFER_INFO = 'unzer_payment_transfer_info'; + public const UNZER_PAYMENT_TRANSFER_INFO = 'unzer_payment_transfer_info'; public const UNZER_PAYMENT_FRAUD_PREVENTION_SESSION_ID = 'unzer_payment_fraud_prevention_session_id'; public const CUSTOM_FIELDS = [ [ - 'id' => '051351c0e4e64229a9a29b9893344d23', - 'name' => 'custom_unzer_payment', - 'global' => true, + 'id' => '051351c0e4e64229a9a29b9893344d23', + 'name' => 'custom_unzer_payment', + 'global' => true, 'customFields' => [ [ - 'id' => '6bb838751d65478992a5c0a1e80cb5fd', + 'id' => '6bb838751d65478992a5c0a1e80cb5fd', 'name' => self::UNZER_PAYMENT_IS_TRANSACTION, 'type' => CustomFieldTypes::BOOL, ], [ - 'id' => '4962176184c25acbd46f60a15c24b334', + 'id' => '4962176184c25acbd46f60a15c24b334', 'name' => self::UNZER_PAYMENT_IS_SHIPPED, 'type' => CustomFieldTypes::BOOL, ], [ - 'id' => 'ce3728a208204885a74552548147e985', + 'id' => 'ce3728a208204885a74552548147e985', 'name' => self::UNZER_PAYMENT_PAYMENT_ID_KEY, 'type' => CustomFieldTypes::TEXT, ], [ - 'id' => 'aae342fddd464116839222049bf26fd8', + 'id' => 'aae342fddd464116839222049bf26fd8', 'name' => self::UNZER_PAYMENT_TRANSFER_INFO, 'type' => CustomFieldTypes::JSON, ], [ - 'id' => 'cf2ab026784943f3a3597bb008ccdf8b', + 'id' => 'cf2ab026784943f3a3597bb008ccdf8b', 'name' => self::UNZER_PAYMENT_FRAUD_PREVENTION_SESSION_ID, 'type' => CustomFieldTypes::TEXT, ], @@ -56,12 +56,8 @@ class CustomFieldInstaller implements InstallerInterface ], ]; - /** @var EntityRepository */ - private $customFieldSetRepository; - - public function __construct(EntityRepository $customFieldSetRepository) + public function __construct(private EntityRepository $customFieldSetRepository) { - $this->customFieldSetRepository = $customFieldSetRepository; } /** diff --git a/src/Installer/PaymentInstaller.php b/src/Installer/PaymentInstaller.php index 3a083345..aedcde70 100755 --- a/src/Installer/PaymentInstaller.php +++ b/src/Installer/PaymentInstaller.php @@ -5,6 +5,7 @@ namespace UnzerPayment6\Installer; use League\Flysystem\Filesystem; +use Shopware\Core\Checkout\Payment\PaymentMethodEntity; use Shopware\Core\Framework\Context; use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository; use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria; @@ -15,29 +16,24 @@ use Shopware\Core\Framework\Plugin\Context\UpdateContext; use Shopware\Core\Framework\Plugin\Util\PluginIdProvider; use UnzerPayment6\Components\PaymentHandler\UnzerAlipayPaymentHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerApplePayPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerApplePayV2PaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerBancontactHandler; use UnzerPayment6\Components\PaymentHandler\UnzerCreditCardPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerDirectDebitPaymentHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerDirectDebitSecuredPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerEpsPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerGooglePayPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerIdealPaymentHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerInstallmentSecuredPaymentHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerInvoicePaymentHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerInvoiceSecuredPaymentHandler; +use UnzerPayment6\Components\PaymentHandler\UnzerKlarnaPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerOpenBankingPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerPaylaterDirectDebitSecuredPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerPaylaterInstallmentPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerPaylaterInvoicePaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerPayPalPaymentHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerPisPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerPrePaymentPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerPrzelewyHandler; -use UnzerPayment6\Components\PaymentHandler\UnzerSofortPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerTwintPaymentHandler; use UnzerPayment6\Components\PaymentHandler\UnzerWeChatPaymentHandler; +use UnzerPayment6\Components\PaymentHandler\UnzerWeroPaymentHandler; use UnzerPayment6\UnzerPayment6; class PaymentInstaller implements InstallerInterface @@ -67,41 +63,42 @@ class PaymentInstaller implements InstallerInterface public const PAYMENT_ID_GOOGLE_PAY = '67b6d50c1ecd11ef9e21d7850819bc50'; public const PAYMENT_ID_TWINT = '6493b43244eb11efa900b7a80e209d6a'; public const PAYMENT_ID_OPEN_BANKING = '105932c2e56b11ef9cd003e762195b4d'; + public const PAYMENT_ID_KLARNA = '21e0875e4d5b11f09349578ade9d62d8'; + public const PAYMENT_ID_WERO = 'e9d326149a3f11f082ea6b21179cd920'; public const PAYMENT_METHOD_IDS = [ self::PAYMENT_ID_ALIPAY, self::PAYMENT_ID_CREDIT_CARD, self::PAYMENT_ID_DIRECT_DEBIT, - self::PAYMENT_ID_DIRECT_DEBIT_SECURED, self::PAYMENT_ID_EPS, - self::PAYMENT_ID_FLEXIPAY, - self::PAYMENT_ID_GIROPAY, - self::PAYMENT_ID_INVOICE, - self::PAYMENT_ID_INVOICE_SECURED, self::PAYMENT_ID_IDEAL, self::PAYMENT_ID_PAYPAL, self::PAYMENT_ID_PRE_PAYMENT, self::PAYMENT_ID_PRZELEWY24, - self::PAYMENT_ID_SOFORT, self::PAYMENT_ID_WE_CHAT, self::PAYMENT_ID_BANCONTACT, self::PAYMENT_ID_PAYLATER_INVOICE, - self::PAYMENT_ID_APPLE_PAY, self::PAYMENT_ID_APPLE_PAY_V2, self::PAYMENT_ID_PAYLATER_INSTALLMENT, self::PAYMENT_ID_PAYLATER_DIRECT_DEBIT_SECURED, self::PAYMENT_ID_GOOGLE_PAY, self::PAYMENT_ID_TWINT, self::PAYMENT_ID_OPEN_BANKING, + self::PAYMENT_ID_KLARNA, + self::PAYMENT_ID_WERO, ]; - public const DEPRECATED_PAYMENT_METHOD_IDS = [ + public const DEPRECATED_PAYMENT_METHOD_IDS = []; + + public const REMOVED_PAYMENT_METHOD_IDS = [ self::PAYMENT_ID_FLEXIPAY, + self::PAYMENT_ID_GIROPAY, self::PAYMENT_ID_INVOICE, self::PAYMENT_ID_INVOICE_SECURED, self::PAYMENT_ID_INSTALLMENT_SECURED, self::PAYMENT_ID_DIRECT_DEBIT_SECURED, self::PAYMENT_ID_APPLE_PAY, + self::PAYMENT_ID_SOFORT, ]; public const PAYMENT_METHODS = [ @@ -121,22 +118,6 @@ class PaymentInstaller implements InstallerInterface ], ], ], - [ - 'id' => self::PAYMENT_ID_FLEXIPAY, - 'handlerIdentifier' => UnzerPisPaymentHandler::class, - 'name' => 'Bank Transfer (Deprecated)', - 'technicalName' => 'unzer_banktransfer_deorecated', - 'translations' => [ - 'de-DE' => [ - 'name' => 'Bank Transfer (Veraltet)', - 'description' => 'Unzer Bank Transfer Zahlungen mit Unzer payments', - ], - 'en-GB' => [ - 'name' => 'Bank Transfer (Deprecated)', - 'description' => 'Unzer Bank Transfer payments', - ], - ], - ], [ 'id' => self::PAYMENT_ID_CREDIT_CARD, 'handlerIdentifier' => UnzerCreditCardPaymentHandler::class, @@ -185,55 +166,6 @@ class PaymentInstaller implements InstallerInterface ], ], ], - [ - 'id' => self::PAYMENT_ID_INVOICE, - 'handlerIdentifier' => UnzerInvoicePaymentHandler::class, - 'name' => 'Invoice (Deprecated)', - 'technicalName' => 'unzer_invoice_deprecated', - 'translations' => [ - 'de-DE' => [ - 'name' => 'Rechnungskauf (Veraltet)', - 'description' => 'Rechnungskauf mit Unzer payments', - ], - 'en-GB' => [ - 'name' => 'Invoice (Deprecated)', - 'description' => 'Invoice payments with Unzer payments', - ], - ], - ], - [ - 'id' => self::PAYMENT_ID_INVOICE_SECURED, - 'handlerIdentifier' => UnzerInvoiceSecuredPaymentHandler::class, - 'name' => 'Invoice Secured (Deprecated)', - 'technicalName' => 'unzer_invoicesecured_deprecated', - 'translations' => [ - 'de-DE' => [ - 'name' => 'Rechnungskauf Gesichert (Veraltet)', - 'description' => 'Gesicherter Rechnungskauf mit Unzer payments', - ], - 'en-GB' => [ - 'name' => 'Invoice Secured (Deprecated)', - 'description' => 'Invoice Secured payments with Unzer payments', - ], - ], - ], - [ - 'id' => self::PAYMENT_ID_INSTALLMENT_SECURED, - 'handlerIdentifier' => UnzerInstallmentSecuredPaymentHandler::class, - 'name' => 'Installment (Deprecated)', - 'technicalName' => 'unzer_installment_deprecated', - 'active' => false, - 'translations' => [ - 'de-DE' => [ - 'name' => 'Ratenkauf (Veraltet)', - 'description' => 'Unzer Ratenkauf', - ], - 'en-GB' => [ - 'name' => 'Installment (Deprecated)', - 'description' => 'Unzer Installment', - ], - ], - ], [ 'id' => self::PAYMENT_ID_PAYLATER_INSTALLMENT, 'handlerIdentifier' => UnzerPaylaterInstallmentPaymentHandler::class, @@ -330,52 +262,19 @@ class PaymentInstaller implements InstallerInterface ], ], ], - [ - 'id' => self::PAYMENT_ID_DIRECT_DEBIT_SECURED, - 'handlerIdentifier' => UnzerDirectDebitSecuredPaymentHandler::class, - 'name' => 'SEPA Direct Debit Secured (Deprecated)', - 'technicalName' => 'unzer_directdebitsecured_deprecated', - 'active' => false, - 'translations' => [ - 'de-DE' => [ - 'name' => 'SEPA Lastschrift Gesichert (Veraltet)', - 'description' => 'Gesicherte SEPA Lastschrift Zahlungen mit Unzer payments', - ], - 'en-GB' => [ - 'name' => 'SEPA Direct Debit Secured (Deprecated)', - 'description' => 'Secured SEPA Direct Debit payments with Unzer payments', - ], - ], - ], - [ - 'id' => self::PAYMENT_ID_SOFORT, - 'handlerIdentifier' => UnzerSofortPaymentHandler::class, - 'name' => 'Sofort', - 'technicalName' => 'unzer_sofort', - 'translations' => [ - 'de-DE' => [ - 'name' => 'Sofort', - 'description' => 'Sofort mit Unzer payments', - ], - 'en-GB' => [ - 'name' => 'Sofort', - 'description' => 'Sofort with Unzer payments', - ], - ], - ], [ 'id' => self::PAYMENT_ID_WE_CHAT, 'handlerIdentifier' => UnzerWeChatPaymentHandler::class, - 'name' => 'WeChat', + 'name' => 'WeChat Pay', 'technicalName' => 'unzer_wechatpay', 'translations' => [ 'de-DE' => [ - 'name' => 'WeChat', - 'description' => 'WeChat Zahlungen mit Unzer payments', + 'name' => 'WeChat Pay', + 'description' => 'WeChat Pay Zahlungen mit Unzer payments', ], 'en-GB' => [ - 'name' => 'WeChat', - 'description' => 'WeChat payments with Unzer payments', + 'name' => 'WeChat Pay', + 'description' => 'WeChat Pay payments with Unzer payments', ], ], ], @@ -411,22 +310,6 @@ class PaymentInstaller implements InstallerInterface ], ], ], - [ - 'id' => self::PAYMENT_ID_APPLE_PAY, - 'handlerIdentifier' => UnzerApplePayPaymentHandler::class, - 'name' => 'Apple Pay (Deprecated)', - 'technicalName' => 'unzer_applepay', - 'translations' => [ - 'de-DE' => [ - 'name' => 'Apple Pay (Deprecated)', - 'description' => 'Apple Pay mit Unzer payments', - ], - 'en-GB' => [ - 'name' => 'Apple Pay (Veraltet)', - 'description' => 'Apple Pay with Unzer payments', - ], - ], - ], [ 'id' => self::PAYMENT_ID_APPLE_PAY_V2, 'handlerIdentifier' => UnzerApplePayV2PaymentHandler::class, @@ -491,13 +374,45 @@ class PaymentInstaller implements InstallerInterface ], ], ], + [ + 'id' => self::PAYMENT_ID_KLARNA, + 'handlerIdentifier' => UnzerKlarnaPaymentHandler::class, + 'name' => 'TWINT', + 'technicalName' => 'unzer_klarna', + 'translations' => [ + 'de-DE' => [ + 'name' => 'Klarna', + 'description' => 'Klarna mit Unzer payments', + ], + 'en-GB' => [ + 'name' => 'Klarna', + 'description' => 'Klarna with Unzer payments', + ], + ], + ], + [ + 'id' => self::PAYMENT_ID_WERO, + 'handlerIdentifier' => UnzerWeroPaymentHandler::class, + 'name' => 'Wero', + 'technicalName' => 'unzer_wero', + 'translations' => [ + 'de-DE' => [ + 'name' => 'Wero', + 'description' => 'Wero Zahlungen mit Unzer payments', + ], + 'en-GB' => [ + 'name' => 'Wero', + 'description' => 'Wero payments with Unzer payments', + ], + ], + ], ]; + + public const APPLE_PAY_DOMAIN_VERIFICATION_FILE_CONTENT = '7b2276657273696f6e223a312c227073704964223a2244303134343945313932433041444436323041333641443243393834373337433245313930423230333138343431393437433743423736364338344534323638222c22637265617465644f6e223a313731383839323737333837377d'; private const PLUGIN_VERSION_PAYLATER_INVOICE = '5.0.0'; private const PLUGIN_VERSION_PAYLATER_INSTALLMENT = '5.6.0'; private const PLUGIN_VERSION_PAYLATER_DIRECT_DEBIT = '5.7.0'; - public const APPLE_PAY_DOMAIN_VERIFICATION_FILE_CONTENT = '7b2276657273696f6e223a312c227073704964223a2244303134343945313932433041444436323041333641443243393834373337433245313930423230333138343431393437433743423736364338344534323638222c22637265617465644f6e223a313731383839323737333837377d'; - private EntityRepository $paymentMethodRepository; private PluginIdProvider $pluginIdProvider; @@ -516,7 +431,7 @@ public function install(InstallContext $context, ?object $publicFileSystem): voi public function update(UpdateContext $context, ?object $publicFileSystem): void { - $this->upsertPaymentMethods($context, $publicFileSystem); + $this->upsertPaymentMethods($context); $this->createApplePayDomainVerification($publicFileSystem); if ($context->getUpdatePluginVersion() === self::PLUGIN_VERSION_PAYLATER_INVOICE) { $this->paymentMethodRepository->upsert([ @@ -579,65 +494,73 @@ private function upsertPaymentMethods(InstallContext $context): void foreach (self::PAYMENT_METHODS as $paymentMethod) { if (!$this->isPaymentMethodInstalled($paymentMethod['id'], $context->getContext())) { + // method does not exist > create with complete data $paymentMethod['pluginId'] = $pluginId; + $paymentMethod['active'] = true; $this->paymentMethodRepository->upsert([$paymentMethod], $context->getContext()); } else { + // method does exist > only update necessary fields $upsertPayload = [ 'id' => $paymentMethod['id'], + 'pluginId' => $pluginId, 'technicalName' => $paymentMethod['technicalName'], ]; $this->paymentMethodRepository->upsert([$upsertPayload], $context->getContext()); } } - $this->deprecateGiropay($context); + $this->removeOldPaymentMethods($context); $this->deprecatePaymentMethods($context); } - private function deprecatePaymentMethods(InstallContext $context):void{ - $upsertPayload = []; - foreach (self::DEPRECATED_PAYMENT_METHOD_IDS as $paymentMethodId) { - $upsertPayload[] = [ - 'id' => $paymentMethodId, - 'translations' => [ - 'de-DE' => [ - 'customFields' => [ - 'isDeprecated' => 1, + private function deprecatePaymentMethods(InstallContext $context): void + { + foreach (self::DEPRECATED_PAYMENT_METHOD_IDS + self::REMOVED_PAYMENT_METHOD_IDS as $paymentMethodId) { + try { + $isRemoved = (\in_array($paymentMethodId, self::REMOVED_PAYMENT_METHOD_IDS, true)) ? 1 : 0; + $this->paymentMethodRepository->upsert([[ + 'id' => $paymentMethodId, + 'translations' => [ + 'de-DE' => [ + 'customFields' => [ + 'isDeprecated' => 1, + 'isRemoved' => $isRemoved, + ], ], - ], - 'en-GB' => [ - 'customFields' => [ - 'isDeprecated' => 1, + 'en-GB' => [ + 'customFields' => [ + 'isDeprecated' => 1, + 'isRemoved' => $isRemoved, + ], ], ], - ], - - ]; + ]], $context->getContext()); + } catch (\Exception) { + // this is not mission critical + } } - - $this->paymentMethodRepository->upsert($upsertPayload, $context->getContext()); } - private function deprecateGiropay(InstallContext $context): void + private function removeOldPaymentMethods(InstallContext $context): void { - $existingPaymentMethod = $this->paymentMethodRepository->search(new Criteria([self::PAYMENT_ID_GIROPAY]), $context->getContext())->first(); - if ($existingPaymentMethod === null) { - return; - } + foreach (self::REMOVED_PAYMENT_METHOD_IDS as $paymentMethodId) { + /** @var PaymentMethodEntity $existingPaymentMethod */ + $existingPaymentMethod = $this->paymentMethodRepository->search(new Criteria([$paymentMethodId]), $context->getContext())->first(); + if ($existingPaymentMethod === null) { + return; + } - $this->paymentMethodRepository->update([[ - 'id' => self::PAYMENT_ID_GIROPAY, - 'active' => false, - 'name' => 'Giropay (Veraltet)', - 'translations' => [ - 'de-DE' => [ - 'name' => 'Giropay (Veraltet)', - ], - 'en-GB' => [ - 'name' => 'Giropay (Deprecated)', - ], - ], - ]], $context->getContext()); + $updatePayload = [ + 'id' => $paymentMethodId, + 'active' => false, + ]; + + if (!str_contains($existingPaymentMethod->getName(), '(Veraltet)')) { + $updatePayload['name'] = $existingPaymentMethod->getName() . ' (Veraltet)'; + } + + $this->paymentMethodRepository->update([$updatePayload], $context->getContext()); + } } private function setAllPaymentMethodsActive(bool $active, InstallContext $context): void diff --git a/src/Migration/Migration1566288547AddAddressHash.php b/src/Migration/Migration1566288547AddAddressHash.php index c60e23d7..93a1fd74 100644 --- a/src/Migration/Migration1566288547AddAddressHash.php +++ b/src/Migration/Migration1566288547AddAddressHash.php @@ -6,7 +6,6 @@ use Doctrine\DBAL\Connection; use Shopware\Core\Framework\Migration\MigrationStep; -use Throwable; class Migration1566288547AddAddressHash extends MigrationStep { @@ -30,8 +29,8 @@ public function update(Connection $connection): void try { $connection->executeStatement($sql); - } catch (Throwable $ex) { - //The column may exist already + } catch (\Throwable $ex) { + // The column may exist already } } diff --git a/src/Migration/Migration1579964350AddTransferInfoTable.php b/src/Migration/Migration1579964350AddTransferInfoTable.php index a9f46687..16a02bcc 100644 --- a/src/Migration/Migration1579964350AddTransferInfoTable.php +++ b/src/Migration/Migration1579964350AddTransferInfoTable.php @@ -49,6 +49,6 @@ public function update(Connection $connection): void public function updateDestructive(Connection $connection): void { - //Nothing to do + // Nothing to do } } diff --git a/src/Migration/Migration1600784048RebrandingRenameTables.php b/src/Migration/Migration1600784048RebrandingRenameTables.php index 793c900e..51ea1367 100644 --- a/src/Migration/Migration1600784048RebrandingRenameTables.php +++ b/src/Migration/Migration1600784048RebrandingRenameTables.php @@ -31,6 +31,6 @@ public function update(Connection $connection): void public function updateDestructive(Connection $connection): void { - //Nothing to do + // Nothing to do } } diff --git a/src/Migration/Migration1601201837RebrandingAdjustConfig.php b/src/Migration/Migration1601201837RebrandingAdjustConfig.php index 73e07779..4ec32a9b 100644 --- a/src/Migration/Migration1601201837RebrandingAdjustConfig.php +++ b/src/Migration/Migration1601201837RebrandingAdjustConfig.php @@ -27,6 +27,6 @@ public function update(Connection $connection): void public function updateDestructive(Connection $connection): void { - //Nothing to do + // Nothing to do } } diff --git a/src/Migration/Migration1605179799ForeignKeyChanges.php b/src/Migration/Migration1605179799ForeignKeyChanges.php index 2f9f0569..6b0ef316 100644 --- a/src/Migration/Migration1605179799ForeignKeyChanges.php +++ b/src/Migration/Migration1605179799ForeignKeyChanges.php @@ -19,7 +19,8 @@ public function update(Connection $connection): void $transferInfoSql = $connection->fetchOne('SHOW KEYS FROM `unzer_payment_transfer_info` WHERE Key_name = "fk.heidelpay_transfer_info.transaction_id";'); if (!$transferInfoSql) { - $connection->executeStatement(<<executeStatement( + <<fetchOne('SHOW KEYS FROM `unzer_payment_payment_device` WHERE Key_name = "fk.heidelpay_payment_device.customer_id";'); if (!$paymentDeviceResult) { - $connection->executeStatement(<<executeStatement( + <<executeStatement(<<executeStatement( + <<executeStatement(<<executeStatement( + <<executeStatement(<<executeStatement( + <<dropIndex($connection, 'unzer_payment_transfer_info', 'fk.unzer_payment_transfer_info.transaction_id'); try { - $connection->executeStatement(<<executeStatement( + <<dropIndex($connection, 'unzer_payment_payment_device', 'fk.heidelpay_payment_device.customer_id'); try { - $connection->executeStatement(<<executeStatement( + <<executeStatement(<<executeStatement( + <<executeStatement(<<executeStatement( + << { - return ApiService.handleResponse(response); - }); - } - - async updateCertificates(salesChannelId, files, inheritMerchantIdentification, inheritPaymentProcessing) { - let url = `_action/${this.getApiBasePath()}/apple-pay/certificates`; - - if (salesChannelId) { - url += `/${salesChannelId}`; - } - - const data = {}; - - for (let key in files) { - if (files[key]) { - const file = files[key]; - - data[key] = await file.text(); - } - } - - if (inheritMerchantIdentification) { - data.inheritMerchantIdentification = true; - } - if (inheritPaymentProcessing) { - data.inheritPaymentProcessing = true; - } - - if (data.length === 0) { - return new Promise(); - } - - return this.httpClient - .post( - url, - data, - { - headers: this.getBasicHeaders() - } - ) - .then((response) => { - return ApiService.handleResponse(response); - }); - } -} - -Application.addServiceProvider('UnzerPaymentApplePayService', (container) => { - const initContainer = Application.getContainer('init'); - - return new UnzerPaymentApplePayService(initContainer.httpClient, container.loginService); -}); - 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 094aa6d1..47144374 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 @@ -12,7 +12,7 @@ class UnzerPaymentConfigurationService extends ApiService { `_action/${this.getApiBasePath()}/validate-credentials`, credentials, { - headers: this.getBasicHeaders() + headers: this.getBasicHeaders(), } ) .then((response) => { @@ -22,13 +22,9 @@ class UnzerPaymentConfigurationService extends ApiService { registerWebhooks(data) { return this.httpClient - .post( - `_action/${this.getApiBasePath()}/register-webhooks`, - data, - { - headers: this.getBasicHeaders() - } - ) + .post(`_action/${this.getApiBasePath()}/register-webhooks`, data, { + headers: this.getBasicHeaders(), + }) .then((response) => { return ApiService.handleResponse(response); }); @@ -36,13 +32,9 @@ class UnzerPaymentConfigurationService extends ApiService { clearWebhooks(data) { return this.httpClient - .post( - `_action/${this.getApiBasePath()}/clear-webhooks`, - data, - { - headers: this.getBasicHeaders() - } - ) + .post(`_action/${this.getApiBasePath()}/clear-webhooks`, data, { + headers: this.getBasicHeaders(), + }) .then((response) => { return ApiService.handleResponse(response); }); @@ -52,9 +44,9 @@ class UnzerPaymentConfigurationService extends ApiService { return this.httpClient .post( `_action/${this.getApiBasePath()}/get-webhooks`, - {'privateKey': privateKey}, + { privateKey: privateKey }, { - headers: this.getBasicHeaders() + headers: this.getBasicHeaders(), } ) .then((response) => { @@ -65,9 +57,9 @@ class UnzerPaymentConfigurationService extends ApiService { getGooglePayGatewayMerchantId(salesChannelId) { return this.httpClient .get( - `_action/${this.getApiBasePath()}/get-google-pay-gateway-merchant-id?salesChannelId=${salesChannelId||''}`, + `_action/${this.getApiBasePath()}/get-google-pay-gateway-merchant-id?salesChannelId=${salesChannelId || ''}`, { - headers: this.getBasicHeaders() + headers: this.getBasicHeaders(), } ) .then((response) => { @@ -76,9 +68,14 @@ class UnzerPaymentConfigurationService extends ApiService { } } -Application.addServiceProvider('UnzerPaymentConfigurationService', (container) => { - const initContainer = Application.getContainer('init'); - - return new UnzerPaymentConfigurationService(initContainer.httpClient, container.loginService); -}); +Application.addServiceProvider( + 'UnzerPaymentConfigurationService', + (container) => { + const initContainer = Application.getContainer('init'); + return new UnzerPaymentConfigurationService( + initContainer.httpClient, + container.loginService + ); + } +); diff --git a/src/Resources/app/administration/src/api/unzer-payment.service.js b/src/Resources/app/administration/src/api/unzer-payment.service.js index 45edd1e8..3b150535 100644 --- a/src/Resources/app/administration/src/api/unzer-payment.service.js +++ b/src/Resources/app/administration/src/api/unzer-payment.service.js @@ -9,27 +9,25 @@ class UnzerPaymentService extends ApiService { fetchPaymentDetails(transaction) { const apiRoute = `_action/${this.getApiBasePath()}/transaction/${transaction}/details`; - return this.httpClient.get( - apiRoute, - { - headers: this.getBasicHeaders() - } - ).then((response) => { - return ApiService.handleResponse(response); - }); + return this.httpClient + .get(apiRoute, { + headers: this.getBasicHeaders(), + }) + .then((response) => { + return ApiService.handleResponse(response); + }); } chargeTransaction(transaction, payment, amount) { const apiRoute = `_action/${this.getApiBasePath()}/transaction/${transaction}/charge/${amount}`; - return this.httpClient.get( - apiRoute, - { - headers: this.getBasicHeaders() - } - ).then((response) => { - return ApiService.handleResponse(response); - }); + return this.httpClient + .get(apiRoute, { + headers: this.getBasicHeaders(), + }) + .then((response) => { + return ApiService.handleResponse(response); + }); } refundTransaction(transaction, charge, amount, reasonCode = null) { @@ -39,46 +37,45 @@ class UnzerPaymentService extends ApiService { apiRoute = `${apiRoute}/${reasonCode}`; } - return this.httpClient.get( - apiRoute, - { - headers: this.getBasicHeaders() - } - ).then((response) => { - return ApiService.handleResponse(response); - }); + return this.httpClient + .get(apiRoute, { + headers: this.getBasicHeaders(), + }) + .then((response) => { + return ApiService.handleResponse(response); + }); } cancelTransaction(transaction, authorize, amount) { const apiRoute = `_action/${this.getApiBasePath()}/transaction/${transaction}/cancel/${authorize}/${amount}`; - return this.httpClient.get( - apiRoute, - { - headers: this.getBasicHeaders() - } - ).then((response) => { - return ApiService.handleResponse(response); - }); + return this.httpClient + .get(apiRoute, { + headers: this.getBasicHeaders(), + }) + .then((response) => { + return ApiService.handleResponse(response); + }); } ship(transaction) { const apiRoute = `_action/${this.getApiBasePath()}/transaction/${transaction}/ship`; - return this.httpClient.get( - apiRoute, - { - headers: this.getBasicHeaders() - } - ).then((response) => { - return ApiService.handleResponse(response); - }); + return this.httpClient + .get(apiRoute, { + headers: this.getBasicHeaders(), + }) + .then((response) => { + return ApiService.handleResponse(response); + }); } } Application.addServiceProvider('UnzerPaymentService', (container) => { const initContainer = Application.getContainer('init'); - return new UnzerPaymentService(initContainer.httpClient, container.loginService); + return new UnzerPaymentService( + initContainer.httpClient, + container.loginService + ); }); - diff --git a/src/Resources/app/administration/src/extension/sw-payment-card/index.js b/src/Resources/app/administration/src/extension/sw-payment-card/index.js index eee6684d..8e81b665 100644 --- a/src/Resources/app/administration/src/extension/sw-payment-card/index.js +++ b/src/Resources/app/administration/src/extension/sw-payment-card/index.js @@ -1,11 +1,11 @@ import template from './sw-payment-card.html.twig'; -import deDE from "../../snippets/de-DE.json"; -import enGB from "../../snippets/en-GB.json"; +import deDE from '../../snippets/de-DE.json'; +import enGB from '../../snippets/en-GB.json'; Shopware.Component.override('sw-payment-card', { template, snippets: { 'de-DE': deDE, - 'en-GB': enGB + 'en-GB': enGB, }, }); diff --git a/src/Resources/app/administration/src/extension/sw-payment-card/sw-payment-card.html.twig b/src/Resources/app/administration/src/extension/sw-payment-card/sw-payment-card.html.twig index 8d1dc4be..ca285ecc 100644 --- a/src/Resources/app/administration/src/extension/sw-payment-card/sw-payment-card.html.twig +++ b/src/Resources/app/administration/src/extension/sw-payment-card/sw-payment-card.html.twig @@ -1,9 +1,11 @@ {% block sw_payment_card_description %}
-
+
{{ $tc('sw-payment-card.deprecated') }} diff --git a/src/Resources/app/administration/src/main.js b/src/Resources/app/administration/src/main.js index a4c5c706..fd7d9805 100644 --- a/src/Resources/app/administration/src/main.js +++ b/src/Resources/app/administration/src/main.js @@ -1,7 +1,6 @@ import './module/unzer-payment'; import './module/unzer-payment-configuration'; import './api/unzer-payment.service'; -import './api/unzer-payment-apple-pay.service'; import './api/unzer-payment-configuration.service'; import './extension/sw-payment-card'; import './scss/base.scss'; diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/index.js index e4900e77..4af1ef58 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/index.js @@ -6,42 +6,37 @@ const Criteria = Shopware.Data.Criteria; Shopware.Component.register('unzer-payment-register-webhook', { template, - mixins: [ - Shopware.Mixin.getByName('notification') - ], + mixins: [Shopware.Mixin.getByName('notification')], - inject: [ - 'repositoryFactory', - 'UnzerPaymentConfigurationService' - ], + inject: ['repositoryFactory', 'UnzerPaymentConfigurationService'], props: { webhooks: { type: Array, - required: true + required: true, }, isLoading: { type: Boolean, - required: false + required: false, }, selectedSalesChannelId: { type: String, - required: false + required: false, }, privateKey: { type: String, - required: true + required: true, }, isDisabled: { type: Boolean, - required: false - } + required: false, + }, }, computed: { salesChannelRepository() { return this.repositoryFactory.create('sales_channel'); - } + }, }, data() { @@ -51,9 +46,9 @@ Shopware.Component.register('unzer-payment-register-webhook', { isRegistrationSuccessful: false, isDataLoading: false, selection: {}, - selectedDomain:null, + selectedDomain: null, entitySelection: {}, - salesChannels: {} + salesChannels: {}, }; }, @@ -69,12 +64,13 @@ Shopware.Component.register('unzer-payment-register-webhook', { loadData(page, limit) { let me = this; - me.isDataLoading = true; + this.isDataLoading = true; let criteria = new Criteria(page, limit); criteria.addAssociation('domains'); - this.salesChannelRepository.search(criteria, Shopware.Context.api) + this.salesChannelRepository + .search(criteria, Shopware.Context.api) .then((result) => { me.salesChannels = result; me.isDataLoading = false; @@ -100,7 +96,7 @@ Shopware.Component.register('unzer-payment-register-webhook', { this.isRegistering = true; this.UnzerPaymentConfigurationService.registerWebhooks({ - selection: this.entitySelection + selection: this.entitySelection, }) .then((response) => { me.isRegistrationSuccessful = true; @@ -113,8 +109,12 @@ Shopware.Component.register('unzer-payment-register-webhook', { }) .catch(() => { this.createNotificationError({ - title: this.$tc('unzer-payment-settings.webhook.globalError.title'), - message: this.$tc('unzer-payment-settings.webhook.globalError.message') + title: this.$tc( + 'unzer-payment-settings.webhook.globalError.title' + ), + message: this.$tc( + 'unzer-payment-settings.webhook.globalError.message' + ), }); }) .finally(() => { @@ -127,9 +127,7 @@ Shopware.Component.register('unzer-payment-register-webhook', { this.selection = {}; }, - onSelectItem(domainId, domain) { - console.log(domainId, domain); if (!domain) { return; } @@ -146,12 +144,20 @@ Shopware.Component.register('unzer-payment-register-webhook', { if (data[domain].success) { this.createNotificationSuccess({ title: this.$tc(data[domain].message, domainAmount), - message: this.$tc('unzer-payment-settings.webhook.messagePrefix', domainAmount) + domain + message: + this.$tc( + 'unzer-payment-settings.webhook.messagePrefix', + domainAmount + ) + domain, }); } else { this.createNotificationError({ title: this.$tc(data[domain].message, domainAmount), - message: this.$tc('unzer-payment-settings.webhook.messagePrefix', domainAmount) + domain + message: + this.$tc( + 'unzer-payment-settings.webhook.messagePrefix', + domainAmount + ) + domain, }); } }); @@ -195,9 +201,11 @@ Shopware.Component.register('unzer-payment-register-webhook', { let criteria = new Criteria(); criteria.addFilter(Criteria.prefix('url', 'https://')); - criteria.addFilter(Criteria.equals('salesChannelId', salesChannelId)); + criteria.addFilter( + Criteria.equals('salesChannelId', salesChannelId) + ); return criteria; - } - } + }, + }, }); 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 c91098bb..6d8a27ef 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 @@ -1,84 +1,90 @@ {% block unzer_payment_payment_register_webhook %}
{% block unzer_payment_payment_register_webhook_button %} - - {{ $tc('unzer-payment-settings.form.webhookButton') }} - + + {{ $tc('unzer-payment-settings.form.webhookButton') }} + {% endblock %} {% block unzer_payment_payment_register_webhook_modal %} - - - - - - + + {% endblock %}
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/style.scss b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/style.scss index c24fea80..f1e36ab7 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/style.scss +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/register-webhook/style.scss @@ -1,4 +1,4 @@ -@import "~scss/variables"; +@import '~scss/variables'; .register-webhook--container { margin-left: 8px; @@ -6,18 +6,18 @@ } .unzer-payment-detail--webhook-modal { - .sw-modal__footer { - border-top: 1px solid $color-gray-300; - } + .sw-modal__footer { + border-top: 1px solid $color-gray-300; + } - .unzer-payment-detail--webhook-modal { - padding: 20px 30px; - } - .saleschannel-name { - padding: 15px; - } + .unzer-payment-detail--webhook-modal { + padding: 20px 30px; + } + .saleschannel-name { + padding: 15px; + } - .sw-data-grid__bulk { - display: none; - } + .sw-data-grid__bulk { + display: none; + } } diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-multi-select-delivery-status/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-multi-select-delivery-status/index.js index cc0f21de..f4c1ec9d 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-multi-select-delivery-status/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-multi-select-delivery-status/index.js @@ -3,33 +3,36 @@ const { Criteria, EntityCollection } = Shopware.Data; // extend the existing component `sw-entity-single-select` by // overwriting the default criteria -Component.extend('unzer-entity-multi-select-delivery-status', 'sw-entity-multi-id-select', { - inject: ['repositoryFactory'], - props: { - repository: { - type: Object, - required: true, - default(){ - return this.repositoryFactory.create('state_machine_state'); - } - }, - criteria: { - type: Object, - required: false, - default() { - const criteria = new Criteria(1, 100); +Component.extend( + 'unzer-entity-multi-select-delivery-status', + 'sw-entity-multi-id-select', + { + inject: ['repositoryFactory'], + props: { + repository: { + type: Object, + required: true, + default() { + return this.repositoryFactory.create('state_machine_state'); + }, + }, + criteria: { + type: Object, + required: false, + default() { + const criteria = new Criteria(1, 100); - criteria.addFilter( - Criteria.equals( - 'stateMachine.technicalName', - 'order_delivery.state' - ) - ); + criteria.addFilter( + Criteria.equals( + 'stateMachine.technicalName', + 'order_delivery.state' + ) + ); - return criteria; - } + return criteria; + }, + }, + entityCollection() {}, }, - entityCollection() { - } } -}); +); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-single-select-delivery-status/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-single-select-delivery-status/index.js index c04a2a18..02cf49d7 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-single-select-delivery-status/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-entity-single-select-delivery-status/index.js @@ -3,24 +3,27 @@ const { Criteria } = Shopware.Data; // extend the existing component `sw-entity-single-select` by // overwriting the default criteria -Component.extend('unzer-entity-single-select-delivery-status', 'sw-entity-single-select', { +Component.extend( + 'unzer-entity-single-select-delivery-status', + 'sw-entity-single-select', + { + props: { + criteria: { + type: Object, + required: false, + default() { + const criteria = new Criteria(1, 100); - props: { - criteria: { - type: Object, - required: false, - default() { - const criteria = new Criteria(1, 100); + criteria.addFilter( + Criteria.equals( + 'stateMachine.technicalName', + 'order_delivery.state' + ) + ); - criteria.addFilter( - Criteria.equals( - 'stateMachine.technicalName', - 'order_delivery.state' - ) - ); - - return criteria; - } - } + return criteria; + }, + }, + }, } -}); +); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-apple-pay-certificates/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-apple-pay-certificates/index.js deleted file mode 100644 index 21708204..00000000 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-apple-pay-certificates/index.js +++ /dev/null @@ -1,203 +0,0 @@ -import template from './unzer-apple-pay-certificates.html.twig'; - -Shopware.Component.register('unzer-payment-apple-pay-certificates', { - template, - - mixins: [ - Shopware.Mixin.getByName('notification') - ], - - inject: [ - 'repositoryFactory', - 'UnzerPaymentApplePayService' - ], - - props: { - isLoading: { - type: Boolean, - required: false - }, - selectedSalesChannelId: { - type: String, - required: false - }, - parentRefs: { - required: true - }, - }, - - data() { - return { - isUpdating: false, - isUpdateSuccessful: false, - isDataLoading: false, - paymentProcessingCertificate: false, - paymentProcessingKey: false, - merchantIdentificationCertificate: false, - merchantIdentificationKey: false, - merchantIdentificationValid: false, - merchantIdentificationValidUntil: null, - paymentProcessingValid: false, - paymentProcessingActive: false, - }; - }, - - computed: { - isNotDefaultSalesChannel() { - return this.selectedSalesChannelId !== null; - }, - - now() { - return Date.now(); - }, - - parentConfigData() { - if(this.parentRefs && this.parentRefs.systemConfig && this.parentRefs.systemConfig.actualConfigData) { - return this.parentRefs.systemConfig.actualConfigData[this.selectedSalesChannelId] || {}; - }else{ - return {}; - } - } - }, - - created() { - this.createdComponent(); - }, - - methods: { - createdComponent() { - this.loadData(); - }, - - loadData() { - this.checkCertificates(); - }, - - checkCertificates() { - let me = this; - - me.isDataLoading = true; - - this.UnzerPaymentApplePayService.checkCertificates(this.selectedSalesChannelId) - .then((response) => { - if (typeof response !== "undefined") { - this.merchantIdentificationValid = response.merchantIdentificationValid; - this.merchantIdentificationValidUntil = response.merchantIdentificationValidUntil ? new Date(response.merchantIdentificationValidUntil) : null; - this.paymentProcessingValid = response.paymentProcessingValid; - this.paymentProcessingActive = response.paymentProcessingActive; - } - }) - .finally(() => { - me.isDataLoading = false; - }); - }, - - onSave() { - return this.updateCertificates(); - }, - - resetFileFieldsMerchantIdentification() { - this.$refs.merchantIdentificationCertificateInput.onRemoveIconClick(); - this.$refs.merchantIdentificationKeyInput.onRemoveIconClick(); - }, - - resetFileFieldsPaymentProcessing() { - this.$refs.paymentProcessingCertificateInput.onRemoveIconClick(); - this.$refs.paymentProcessingKeyInput.onRemoveIconClick(); - }, - - updateCertificates() { - const me = this; - this.isUpdateSuccessful = false; - this.isUpdating = true; - - if ( - !this.paymentProcessingCertificate - && !this.paymentProcessingKey - && !this.merchantIdentificationCertificate - && !this.merchantIdentificationKey - && !this.$refs.inheritWrapperMerchantIdentificationCertificate.isInherited - && !this.$refs.inheritWrapperPaymentProcessingCertificate.isInherited - ) { - this.isUpdateSuccessful = true; - this.isUpdating = false; - - return Promise.resolve(); - } - - return this.UnzerPaymentApplePayService.updateCertificates( - this.selectedSalesChannelId, - { - paymentProcessingCertificate: this.paymentProcessingCertificate, - paymentProcessingKey: this.paymentProcessingKey, - merchantIdentificationCertificate: this.merchantIdentificationCertificate, - merchantIdentificationKey: this.merchantIdentificationKey, - }, - this.$refs.inheritWrapperMerchantIdentificationCertificate.isInherited, - this.$refs.inheritWrapperPaymentProcessingCertificate.isInherited - ) - .then((response) => { - me.isUpdateSuccessful = true; - - me.createNotificationSuccess({ - title: me.$tc('unzer-payment-settings.apple-pay.certificates.update.success.title'), - message: me.$tc('unzer-payment-settings.apple-pay.certificates.update.success.message') - }); - - me.$emit('certificate-updated', response); - if (me.parentRefs.systemConfig.loadCurrentSalesChannelConfig) { - me.parentRefs.systemConfig.loadCurrentSalesChannelConfig(); - } else { - delete me.parentRefs.systemConfig.actualConfigData[this.selectedSalesChannelId]; // force reload of config data - me.parentRefs.systemConfig.readAll(); - } - me.checkCertificates(); - me.resetFileFieldsPaymentProcessing(); - me.resetFileFieldsMerchantIdentification(); - }) - .catch((errorResponse) => { - let message = 'unzer-payment-settings.apple-pay.certificates.update.error.message'; - if (errorResponse && errorResponse.response && errorResponse.response.data && errorResponse.response.data.message) { - message = errorResponse.response.data.message; - } - let translationData = {}; - if (errorResponse && errorResponse.response && errorResponse.response.data && errorResponse.response.data.translationData) { - translationData = errorResponse.response.data.translationData; - } - - me.createNotificationError({ - title: me.$tc('unzer-payment-settings.apple-pay.certificates.update.error.title'), - message: me.$t(message, translationData) - }); - }) - .finally(() => { - me.isUpdating = false; - }); - }, - - onInputChangePaymentProcessing(value) { - if (value) { - // Other field is handled with inheritance wrapper event - this.$refs.inheritWrapperPaymentProcessingCertificate.removeInheritance(); - } - }, - onInputChangeMerchantIdentification(value) { - if (value) { - // Other field is handled with inheritance wrapper event - this.$refs.inheritWrapperMerchantIdentificationCertificate.removeInheritance(); - } - }, - - getInheritedValue(name) { - const systemConfig = this.parentRefs.systemConfig; - if(!systemConfig) { - return null; - } - if (systemConfig.getInheritedValue && systemConfig.actualConfigData.null) { - return systemConfig.getInheritedValue({ name: 'UnzerPayment6.settings.' + name, type: 'text' }); - } else { - return systemConfig.actualConfigData.null ? (systemConfig.actualConfigData.null['UnzerPayment6.settings.' + name] || null) : null; - } - }, - } -}); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-apple-pay-certificates/unzer-apple-pay-certificates.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-apple-pay-certificates/unzer-apple-pay-certificates.html.twig deleted file mode 100644 index ec951442..00000000 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-apple-pay-certificates/unzer-apple-pay-certificates.html.twig +++ /dev/null @@ -1,139 +0,0 @@ -{% block unzer_payment_apple_pay_certificates %} - - - -{% endblock %} diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/index.js index 9d056e52..1c7f2c59 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/index.js @@ -4,9 +4,9 @@ const { Component } = Shopware; Component.register('unzer-payment-plugin-icon', { template, - computed:{ + computed: { assetFilter() { return Shopware.Filter.getByName('asset'); }, - } + }, }); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/unzer-payment-plugin-icon.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/unzer-payment-plugin-icon.html.twig index 73c9246b..2a630ac8 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/unzer-payment-plugin-icon.html.twig +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-payment-plugin-icon/unzer-payment-plugin-icon.html.twig @@ -1,3 +1,6 @@ {% block unzer_plugin_icon %} - -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/index.js new file mode 100644 index 00000000..b31a147d --- /dev/null +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/index.js @@ -0,0 +1,16 @@ +import template from './unzer-settings-subheading.html.twig'; +import './style.scss'; + +const { Component } = Shopware; + +Component.register('unzer-settings-subheading', { + template, + computed: { + label() { + // get part after last dot + const parts = this.$attrs.name.split('.'); + const key = parts[parts.length - 1]; + return this.$tc('unzer-payment-settings.subheadings.' + key); + }, + }, +}); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/style.scss b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/style.scss new file mode 100644 index 00000000..4b7246b5 --- /dev/null +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/style.scss @@ -0,0 +1,17 @@ +[class*='unzer-subheading'] { + label { + display: none; + } +} +.unzer-subheading { + margin: 3em 0 0.5em 0; + font-weight: 600; +} + +.sw-card__content { + .sw-inherit-wrapper:first-child { + .unzer-subheading { + margin-top: 0; + } + } +} diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/unzer-settings-subheading.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/unzer-settings-subheading.html.twig new file mode 100644 index 00000000..21624e7f --- /dev/null +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-settings-subheading/unzer-settings-subheading.html.twig @@ -0,0 +1,3 @@ +{% block unzer_settings_subheading %} +
{{ label }}
+{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/index.js index 91733977..b67b17af 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/index.js @@ -5,18 +5,14 @@ const { Component, Mixin, Context } = Shopware; Component.register('unzer-webhooks-modal', { template, - mixins: [ - Mixin.getByName('notification'), - ], + mixins: [Mixin.getByName('notification')], - inject: [ - 'UnzerPaymentConfigurationService' - ], + inject: ['UnzerPaymentConfigurationService'], props: { keyPair: { type: Array, - required: true + required: true, }, webhooks: { type: Array, @@ -24,7 +20,7 @@ Component.register('unzer-webhooks-modal', { }, isLoadingWebhooks: { type: Boolean, - } + }, }, data() { @@ -42,13 +38,13 @@ Component.register('unzer-webhooks-modal', { { property: 'event', dataIndex: 'event', - label: 'Event' + label: 'Event', }, { property: 'url', dataIndex: 'url', - label: 'URL' - } + label: 'URL', + }, ]; }, }, @@ -62,7 +58,7 @@ Component.register('unzer-webhooks-modal', { this.UnzerPaymentConfigurationService.clearWebhooks({ privateKey: privateKey, - selection: this.webhookSelection + selection: this.webhookSelection, }) .then((response) => { me.isClearingSuccessful = true; @@ -79,8 +75,12 @@ Component.register('unzer-webhooks-modal', { }) .catch(() => { this.createNotificationError({ - title: this.$tc('unzer-payment-settings.webhook.globalError.title'), - message: this.$tc('unzer-payment-settings.webhook.globalError.message') + title: this.$tc( + 'unzer-payment-settings.webhook.globalError.title' + ), + message: this.$tc( + 'unzer-payment-settings.webhook.globalError.message' + ), }); }) .finally(() => { @@ -106,15 +106,23 @@ Component.register('unzer-webhooks-modal', { if (data[url].success) { this.createNotificationSuccess({ title: this.$tc(data[url].message, domainAmount), - message: this.$tc('unzer-payment-settings.webhook.messagePrefix', domainAmount) + url + message: + this.$tc( + 'unzer-payment-settings.webhook.messagePrefix', + domainAmount + ) + url, }); } else { this.createNotificationError({ title: this.$tc(data[url].message, domainAmount), - message: this.$tc('unzer-payment-settings.webhook.messagePrefix', domainAmount) + url + message: + this.$tc( + 'unzer-payment-settings.webhook.messagePrefix', + domainAmount + ) + url, }); } }); }, - } + }, }); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/unzer-webhooks-modal.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/unzer-webhooks-modal.html.twig index 63fe68e9..758a8803 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/unzer-webhooks-modal.html.twig +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/component/unzer-webhooks-modal/unzer-webhooks-modal.html.twig @@ -8,10 +8,10 @@ variant="warning" appearance="default" :showIcon="true" - :closable="false"> + :closable="false" + > {{ $tc('unzer-payment-settings.webhook.empty') }} -
+ :columns="webhookColumns" + > - + variant="danger" + > {{ $tc('unzer-payment-settings.modal.webhook.submit.clear', webhookSelectionLength, {count: webhookSelectionLength}) }}
- + \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/index.js index db9eb5f1..c5239d27 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/index.js @@ -3,10 +3,12 @@ import template from './sw-settings-index.html.twig'; const { Component } = Shopware; const version = Shopware.Context.app.config.version; -const match = version.match(/((\d+)\.?(\d+?)\.?(\d+)?\.?(\d*))-?([A-z]+?\d+)?/i); +const match = version.match( + /((\d+)\.?(\d+?)\.?(\d+)?\.?(\d*))-?([A-z]+?\d+)?/i +); -if(match && parseInt(match[2]) === 6 && parseInt(match[3]) < 4) { +if (match && parseInt(match[2]) === 6 && parseInt(match[3]) < 4) { Component.override('sw-settings-index', { - template + template, }); } diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/sw-settings-index.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/sw-settings-index.html.twig index 2a39e683..01b235ab 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/sw-settings-index.html.twig +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-settings-index/sw-settings-index.html.twig @@ -1,11 +1,16 @@ {% block sw_settings_content_card_slot_plugins %} - {% parent %} + {% parent() %} - + -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/index.js b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/index.js index b2055296..9df15ad1 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/index.js +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/index.js @@ -1,27 +1,35 @@ -const {Component} = Shopware; +const { Component } = Shopware; import template from './sw-system-config.html.twig'; Component.override('sw-system-config', { template, - inject: [ - 'UnzerPaymentConfigurationService' - ], + inject: ['UnzerPaymentConfigurationService'], data() { return { - readOnlyUnzerGooglePayGatewayMerchantId: {} + readOnlyUnzerGooglePayGatewayMerchantId: {}, }; }, watch: { currentSalesChannelId() { this.getUnzerGooglePayGatewayMerchantId(); - this.$emit('sales-channel-changed', this.actualConfigData[this.currentSalesChannelId], this.currentSalesChannelId); + this.$emit( + 'sales-channel-changed', + this.actualConfigData[this.currentSalesChannelId], + this.currentSalesChannelId + ); + }, + isLoading(value) { + this.$emit('loading-changed', value); + if (value === false) { + this.getUnzerGooglePayGatewayMerchantId(); + } }, }, computed: { unzerGooglePayGatewayMerchantId() { return this.readOnlyUnzerGooglePayGatewayMerchantId || ''; - } + }, }, methods: { async createdComponent() { @@ -30,13 +38,15 @@ Component.override('sw-system-config', { }, getUnzerGooglePayGatewayMerchantId() { if (this.domain === 'UnzerPayment6.settings') { - this.UnzerPaymentConfigurationService.getGooglePayGatewayMerchantId(this.currentSalesChannelId).then((response) => { - this.readOnlyUnzerGooglePayGatewayMerchantId = response.gatewayMerchantId; - }) - .catch(() => { - - }); + this.UnzerPaymentConfigurationService.getGooglePayGatewayMerchantId( + this.currentSalesChannelId + ) + .then((response) => { + this.readOnlyUnzerGooglePayGatewayMerchantId = + response.gatewayMerchantId; + }) + .catch(() => {}); } - } - } + }, + }, }); diff --git a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/sw-system-config.html.twig b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/sw-system-config.html.twig index 8cacb069..02ad2b02 100644 --- a/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/sw-system-config.html.twig +++ b/src/Resources/app/administration/src/module/unzer-payment-configuration/extension/sw-system-config/sw-system-config.html.twig @@ -1,22 +1,15 @@ {% block sw_system_config_content_card_field %} -