From 45cb93abfc6fc677982746d4ab2100739e3b40f9 Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 8 Oct 2025 13:06:49 +0200 Subject: [PATCH 01/12] Add support for Wero payment method ISSUE: UPP-299 --- Model/Config.php | 1 + Model/Config/Provider.php | 3 +- Model/Method/Wero.php | 19 ++++++ etc/adminhtml/system.xml | 1 + etc/adminhtml/system/wero.xml | 33 ++++++++++ etc/config.xml | 19 ++++++ etc/di.xml | 50 +++++++++++++++ etc/events.xml | 3 + i18n/de_DE.csv | 1 + i18n/en_US.csv | 1 + view/frontend/layout/checkout_index_index.xml | 3 + .../web/js/view/payment/method-renderer.js | 4 ++ .../js/view/payment/method-renderer/wero.js | 61 +++++++++++++++++++ view/frontend/web/template/payment/wero.html | 27 ++++++++ 14 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 Model/Method/Wero.php create mode 100644 etc/adminhtml/system/wero.xml create mode 100644 view/frontend/web/js/view/payment/method-renderer/wero.js create mode 100644 view/frontend/web/template/payment/wero.html diff --git a/Model/Config.php b/Model/Config.php index a627f14..4b65a5a 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -53,6 +53,7 @@ class Config extends \Magento\Payment\Gateway\Config\Config public const METHOD_TWINT = 'unzer_twint'; public const METHOD_OPEN_BANKING = 'unzer_open_banking'; public const METHOD_KLARNA = 'unzer_klarna'; + public const METHOD_WERO = 'unzer_wero'; /** * @var DebugHandler diff --git a/Model/Config/Provider.php b/Model/Config/Provider.php index 5396daa..3edd2ce 100644 --- a/Model/Config/Provider.php +++ b/Model/Config/Provider.php @@ -40,7 +40,8 @@ class Provider implements ConfigProviderInterface Config::METHOD_GOOGLEPAY, Config::METHOD_TWINT, Config::METHOD_OPEN_BANKING, - Config::METHOD_KLARNA + Config::METHOD_KLARNA, + Config::METHOD_WERO, ]; /** diff --git a/Model/Method/Wero.php b/Model/Method/Wero.php new file mode 100644 index 0000000..d3aabf9 --- /dev/null +++ b/Model/Method/Wero.php @@ -0,0 +1,19 @@ + + diff --git a/etc/adminhtml/system/wero.xml b/etc/adminhtml/system/wero.xml new file mode 100644 index 0000000..53eb71c --- /dev/null +++ b/etc/adminhtml/system/wero.xml @@ -0,0 +1,33 @@ + + + + + + + + Magento\Config\Model\Config\Source\Yesno + payment/unzer_wero/active + + + + + payment/unzer_wero/title + + + + + payment/unzer_wero/min_order_total + + + + + payment/unzer_wero/max_order_total + Insert 0 to disable limit. + + + + + payment/unzer_wero/sort_order + + + diff --git a/etc/config.xml b/etc/config.xml index 2a97877..1e74b70 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -376,6 +376,25 @@ 0 Unzer\PAPI\Model\Method\Klarna + + 0 + order + authorize + <![CDATA[Wero]]> + 0 + 0 + 0 + EUR + DE + 1 + 1 + 1 + 1 + 1 + 1 + 0 + Unzer\PAPI\Model\Method\Wero + diff --git a/etc/di.xml b/etc/di.xml index 3a156cf..ea82523 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -1335,4 +1335,54 @@ UnzerAuthorizeAndCaptureKlarnaCommandPool + + + + + Unzer\PAPI\Model\Config::METHOD_WERO + + + + + + UnzerWeroConfig + + + + + + + UnzerWeroConfigValueHandler + Unzer\PAPI\Model\Config\CanCancelHandler + Unzer\PAPI\Model\Config\CanRefundHandler + Unzer\PAPI\Model\Config\CanRefundHandler + Unzer\PAPI\Model\Config\CanVoidHandler + + + + + + + UnzerWeroConfig + + + + + + + UnzerWeroValidatorCountry + + + + + + + Unzer\PAPI\Model\Config::METHOD_WERO + Magento\Payment\Block\Form + Magento\Payment\Block\Info + UnzerWeroValueHandlerPool + UnzerWeroValidatorPool + UnzerAuthorizeAndCaptureCommandPool + + diff --git a/etc/events.xml b/etc/events.xml index 1454606..0314925 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -57,4 +57,7 @@ + + + diff --git a/i18n/de_DE.csv b/i18n/de_DE.csv index ad0180c..7fd3ce0 100644 --- a/i18n/de_DE.csv +++ b/i18n/de_DE.csv @@ -21,6 +21,7 @@ "UNZER_TWINT","TWINT" "UNZER_OPEN_BANKING","Unzer Direktüberweisung" "UNZER_KLARNA","Klarna" +"UNZER_WERO","Wero" "MODULE_VERSION_LABEL","Modul-Version:" "ABOUT_UNZER_LABEL","Über Unzer:" diff --git a/i18n/en_US.csv b/i18n/en_US.csv index 30b4d4e..41dd5a7 100644 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -21,6 +21,7 @@ "UNZER_TWINT","TWINT" "UNZER_OPEN_BANKING","Direct Bank Transfer" "UNZER_KLARNA","Klarna" +"UNZER_WERO","Wero" "MODULE_VERSION_LABEL","Module version:" "ABOUT_UNZER_LABEL","About Unzer:" diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index 0bbb05b..36fd8a3 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -86,6 +86,9 @@ true + + true + diff --git a/view/frontend/web/js/view/payment/method-renderer.js b/view/frontend/web/js/view/payment/method-renderer.js index 4986f49..646129f 100644 --- a/view/frontend/web/js/view/payment/method-renderer.js +++ b/view/frontend/web/js/view/payment/method-renderer.js @@ -84,6 +84,10 @@ define( { type: 'unzer_klarna', component: 'Unzer_PAPI/js/view/payment/method-renderer/klarna' + }, + { + type: 'unzer_wero', + component: 'Unzer_PAPI/js/view/payment/method-renderer/wero' } ); return Component.extend({}); diff --git a/view/frontend/web/js/view/payment/method-renderer/wero.js b/view/frontend/web/js/view/payment/method-renderer/wero.js new file mode 100644 index 0000000..fa4e3dd --- /dev/null +++ b/view/frontend/web/js/view/payment/method-renderer/wero.js @@ -0,0 +1,61 @@ +define([ + 'jquery', + 'ko', + 'mage/translate', + 'Magento_Checkout/js/action/place-order', + 'Magento_Ui/js/model/messageList', + 'Unzer_PAPI/js/view/payment/method-renderer/basev2' +], function ($, ko, $t, placeOrderAction, globalMessageList, Component) { + 'use strict'; + + return Component.extend({ + defaults: { + isActivePaymentTokenEnabler: false, + template: 'Unzer_PAPI/payment/wero' + }, + + createSpecificPaymentElement: function () { + return $(''); + }, + + selectPaymentMethod: function () { + var ret = this._super(); + this.waitForSetBasketData(); + return ret; + }, + + getPlaceOrderDeferredObject: function () { + var deferred = $.Deferred(), self = this; + + Promise.all([ + customElements.whenDefined('unzer-wero') + ]).then(function () { + var unzerCheckout = document.getElementById('unzer-checkout-' + self.getCode()); + + unzerCheckout.onPaymentSubmit = function (response) { + if (response.submitResponse && response.submitResponse.success) { + self.resourceId = response.submitResponse.data.id; + placeOrderAction(self.getData(), self.messageContainer) + .done(function () { deferred.resolve.apply(deferred, arguments); }) + .fail(function (request) { + globalMessageList.addErrorMessage({ message: request.responseJSON.message }); + deferred.reject(request.responseJSON.message); + }); + } else { + globalMessageList.addErrorMessage({ + message: 'There was an error placing your order. ' + response.submitResponse.message + }); + deferred.reject($t('There was an error placing your order. ' + response.submitResponse.message)); + } + }; + }).catch(function (error) { + globalMessageList.addErrorMessage({ message: 'There was an error placing your order. ' + error }); + deferred.reject($t('There was an error placing your order. ' + error)); + }); + + return deferred.fail(function (error) { + globalMessageList.addErrorMessage({ message: error }); + }); + } + }); +}); diff --git a/view/frontend/web/template/payment/wero.html b/view/frontend/web/template/payment/wero.html new file mode 100644 index 0000000..de0d199 --- /dev/null +++ b/view/frontend/web/template/payment/wero.html @@ -0,0 +1,27 @@ +
+
+ + +
+
+
+ + + +
+ +
+ +
+ + + +
+
+
From 294dbb453ecef8d6c19fa24138c1184a70243020 Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 9 Oct 2025 10:47:04 +0200 Subject: [PATCH 02/12] Resolve a mismatch between the customerIds ISSUE: MAGENTO-310 --- Helper/Order.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Helper/Order.php b/Helper/Order.php index 19f03de..7fae7a4 100644 --- a/Helper/Order.php +++ b/Helper/Order.php @@ -346,7 +346,7 @@ public function createCustomerFromQuote(Quote $quote, string $email, bool $creat $customer->setPhone($billingAddress->getTelephone()); $customer->setBirthDate($quote->getCustomer()->getDob()); - $customerId = (string) $quote->getCustomerId() . '_' . $email; + $customerId = (string) $quote->getCustomerId(); if(!$quote->getCustomerIsGuest()) { $customer->setCustomerId($customerId); @@ -431,7 +431,7 @@ public function createCustomerFromOrder( } $customer->setEmail($email); - $customerId = (string) $order->getCustomerId() . '_' . $email; + $customerId = (string) $order->getCustomerId(); if(!$order->getCustomerIsGuest()) { $customer->setCustomerId($customerId); From 0eed850f78843ccbf389ec44d30a4f45ffad7a86 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 14 Oct 2025 15:41:25 +0200 Subject: [PATCH 03/12] Implement option to save SEPA payment method for registered customers ISSUE: MAGENTO-309 --- Block/Customer/DirectDebitRenderer.php | 84 ++++++++ Model/Config.php | 3 +- .../DirectDebit/AvailabilityChecker.php | 41 ++++ .../DirectDebit/TokenFormatter.php | 35 ++++ Model/Method/DirectDebit.php | 9 +- .../DirectDebitVaultDetailsHandler.php | 185 ++++++++++++++++++ .../DirectDebitTokenUiComponentProvider.php | 54 +++++ etc/adminhtml/system/direct_debit.xml | 17 ++ etc/config.xml | 14 ++ etc/di.xml | 40 ++++ etc/frontend/di.xml | 1 + view/frontend/layout/checkout_index_index.xml | 3 + .../layout/vault_cards_listaction.xml | 19 +- .../customer_account/direct_debit.phtml | 52 +++++ .../templates/customer_account/paypal.phtml | 45 +++-- view/frontend/templates/token_list.phtml | 46 +++++ .../payment/method-renderer/direct_debit.js | 120 +++++++++++- .../method-renderer/direct_debit_vault.js | 32 +++ .../web/template/payment/direct_debit.html | 4 + .../template/payment/direct_debit_vault.html | 43 ++++ 20 files changed, 822 insertions(+), 25 deletions(-) create mode 100644 Block/Customer/DirectDebitRenderer.php create mode 100644 Model/InstantPurchase/DirectDebit/AvailabilityChecker.php create mode 100644 Model/InstantPurchase/DirectDebit/TokenFormatter.php create mode 100644 Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php create mode 100644 Model/Vault/Type/DirectDebitTokenUiComponentProvider.php create mode 100644 view/frontend/templates/customer_account/direct_debit.phtml create mode 100644 view/frontend/templates/token_list.phtml create mode 100644 view/frontend/web/js/view/payment/method-renderer/direct_debit_vault.js create mode 100644 view/frontend/web/template/payment/direct_debit_vault.html diff --git a/Block/Customer/DirectDebitRenderer.php b/Block/Customer/DirectDebitRenderer.php new file mode 100644 index 0000000..d06aacd --- /dev/null +++ b/Block/Customer/DirectDebitRenderer.php @@ -0,0 +1,84 @@ +config = $config; + } + + /** + * Can render specified token + */ + public function canRender(PaymentTokenInterface $token): bool + { + return $token->getPaymentMethodCode() === Config::METHOD_DIRECT_DEBIT; + } + + /** + * Masked IBAN (e.g. DE************1234) + */ + public function getMaskedIban(): string + { + return $this->getTokenDetails()['maskedIban'] ?? (string)__('Unknown IBAN'); + } + + /** + * Account holder name + */ + public function getAccountHolder(): string + { + return $this->getTokenDetails()['accountHolder'] ?? (string)__('Unknown Holder'); + } + + /** + * Icon URL for SEPA (if you expose one in config). + */ + public function getIconUrl() + { + return ''; + } + + /** + * Icon height + */ + public function getIconHeight() + { + return ''; + } + + /** + * Icon width + */ + public function getIconWidth() + { + return ''; + } +} diff --git a/Model/Config.php b/Model/Config.php index 4b65a5a..c3c29e1 100644 --- a/Model/Config.php +++ b/Model/Config.php @@ -31,10 +31,9 @@ class Config extends \Magento\Payment\Gateway\Config\Config public const METHOD_BASE = 'unzer'; public const METHOD_CARDS = 'unzer_cards'; - public const METHOD_CARDS_VAULT = 'unzer_cards_vault'; - public const METHOD_DIRECT_DEBIT = 'unzer_direct_debit'; + public const METHOD_DIRECT_DEBIT_VAULT = 'unzer_direct_debit_vault'; public const METHOD_EPS = 'unzer_eps'; public const METHOD_IDEAL = 'unzer_ideal'; public const METHOD_PAYLATER_INVOICE = 'unzer_paylater_invoice'; diff --git a/Model/InstantPurchase/DirectDebit/AvailabilityChecker.php b/Model/InstantPurchase/DirectDebit/AvailabilityChecker.php new file mode 100644 index 0000000..4edc7f5 --- /dev/null +++ b/Model/InstantPurchase/DirectDebit/AvailabilityChecker.php @@ -0,0 +1,41 @@ +scopeConfig = $scopeConfig; + } + + /** + * @inheritdoc + */ + public function isAvailable(): bool + { + return (bool)$this->scopeConfig->getValue( + self::CONFIG_INSTANT_PURCHASE_ACTIVE + ); + } +} diff --git a/Model/InstantPurchase/DirectDebit/TokenFormatter.php b/Model/InstantPurchase/DirectDebit/TokenFormatter.php new file mode 100644 index 0000000..2677d2b --- /dev/null +++ b/Model/InstantPurchase/DirectDebit/TokenFormatter.php @@ -0,0 +1,35 @@ +getTokenDetails() ?: '{}', true, 512, JSON_THROW_ON_ERROR); + if (!isset($details['maskedIban'], $details['accountHolder'])) { + throw new InvalidArgumentException('Invalid Unzer SEPA Direct Debit token details.'); + } + + return sprintf( + '%s: %s (%s)', + __('SEPA Direct Debit'), + $details['maskedIban'], + $details['accountHolder'] + ); + } +} diff --git a/Model/Method/DirectDebit.php b/Model/Method/DirectDebit.php index 981be89..c766eec 100644 --- a/Model/Method/DirectDebit.php +++ b/Model/Method/DirectDebit.php @@ -6,13 +6,12 @@ /** * Direct debit payment method * - * @deprecated use paylater direct debit - * * @link https://docs.unzer.com/ */ class DirectDebit extends Base { public const CONFIG_PATH_STORE_NAME = 'general/store_information/name'; + public const VAULT_CODE = 'unzer_direct_debit_vault'; /** * @inheritDoc @@ -20,6 +19,7 @@ class DirectDebit extends Base public function getFrontendConfig(): array { $parentConfig = parent::getFrontendConfig(); + $parentConfig['vault_code'] = $this->getVaultCode(); $merchantName = $this->getConfigData('merchant_name'); @@ -31,4 +31,9 @@ public function getFrontendConfig(): array return $parentConfig; } + + public function getVaultCode(): ?string + { + return self::VAULT_CODE; + } } diff --git a/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php b/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php new file mode 100644 index 0000000..ceb54fa --- /dev/null +++ b/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php @@ -0,0 +1,185 @@ +paymentTokenFactory = $paymentTokenFactory; + $this->paymentExtensionFactory = $paymentExtensionFactory; + $this->serializer = $serializer; + $this->dateTimeFactory = $dateTimeFactory; + $this->paymentTokenResourceModel = $paymentTokenResourceModel; + } + + /** + * Handle tokens + * + * @param PaymentDataObject $payment + * @param AbstractTransactionType $transaction + * + * @return void + * + * @throws UnzerApiException + * @throws Exception + */ + public function handle(PaymentDataObject $payment, AbstractTransactionType $transaction): void + { + $isSaveToVaultActive = $payment->getPayment()->getAdditionalInformation( + VaultConfigProvider::IS_ACTIVE_CODE + ); + if ($isSaveToVaultActive === false) { + return; + } + + $isInstantPurchase = $payment->getPayment()->getAdditionalInformation( + PaymentConfiguration::MARKER + ); + if ($isInstantPurchase === true) { + return; + } + + $paymentToken = $this->createVaultPaymentToken($transaction, $payment); + if ($paymentToken !== null) { + $extensionAttributes = $this->getExtensionAttributes($payment->getPayment()); + $extensionAttributes->setVaultPaymentToken($paymentToken); + } + } + + /** + * Create vault payment token for SEPA + * + * @param AbstractTransactionType $transaction + * @param PaymentDataObject $payment + * + * @return PaymentTokenInterface|null + * + * @throws Exception + */ + private function createVaultPaymentToken( + AbstractTransactionType $transaction, + PaymentDataObject $payment + ): ?PaymentTokenInterface { + $paymentType = $transaction->getPayment()->getPaymentType(); + + if (!$paymentType instanceof SepaDirectDebit) { + return null; + } + + $token = $paymentType->getId(); + if (empty($token)) { + return null; + } + $iban = preg_replace('/\s+/', '', (string)$paymentType->getIban()); + $holder = (string)$paymentType->getHolder(); + + if ($iban === '' || $holder === '') { + return null; + } + $maskedIban = $this->maskIban($iban); + + $tokenData = $this->paymentTokenResourceModel->getByOrderPaymentId($payment->getPayment()->getId()); + + $paymentToken = null; + if (empty($tokenData)) { + $paymentToken = $this->paymentTokenFactory->create(PaymentTokenFactoryInterface::TOKEN_TYPE_ACCOUNT); + + $paymentToken->setGatewayToken($token); + $paymentToken->setExpiresAt($this->getExpirationDate()); + $paymentToken->setTokenDetails($this->convertDetailsToJSON([ + 'gatewayToken' => $token, + 'maskedIban' => $maskedIban, + 'accountHolder' => $holder + ])); + } + + return $paymentToken; + } + + /** + * @param array $details + * + * @return string + */ + private function convertDetailsToJSON(array $details): string + { + $json = $this->serializer->serialize($details); + return $json ?: '{}'; + } + + /** + * @param OrderPaymentInterface $payment + * + * @return OrderPaymentExtensionInterface + */ + private function getExtensionAttributes(OrderPaymentInterface $payment): OrderPaymentExtensionInterface + { + $extensionAttributes = $payment->getExtensionAttributes() + ?: $this->paymentExtensionFactory->create(); + + $payment->setExtensionAttributes($extensionAttributes); + return $extensionAttributes; + } + + /** + * @return string + */ + private function getExpirationDate(): string + { + $expDate = $this->dateTimeFactory->create('now', new DateTimeZone('UTC')); + $expDate->add(new DateInterval('P1Y')); + return $expDate->format('Y-m-d 00:00:00'); + } + + /** + * @param string $iban + * + * @return string + */ + private function maskIban(string $iban): string + { + $len = strlen($iban); + if ($len <= 6) { + return $iban; + } + $head = substr($iban, 0, 2); + $tail = substr($iban, -4); + return $head . str_repeat('*', max(0, $len - 6)) . $tail; + } +} diff --git a/Model/Vault/Type/DirectDebitTokenUiComponentProvider.php b/Model/Vault/Type/DirectDebitTokenUiComponentProvider.php new file mode 100644 index 0000000..9514873 --- /dev/null +++ b/Model/Vault/Type/DirectDebitTokenUiComponentProvider.php @@ -0,0 +1,54 @@ +componentFactory = $componentFactory; + } + + /** + * Get UI component for token + * + * @param PaymentTokenInterface $paymentToken + * @return TokenUiComponentInterface + * @throws \JsonException + */ + public function getComponentForToken(PaymentTokenInterface $paymentToken): TokenUiComponentInterface + { + $jsonDetails = json_decode($paymentToken->getTokenDetails() ?: '{}', true, 512, JSON_THROW_ON_ERROR); + return $this->componentFactory->create( + [ + 'config' => [ + 'code' => Config::METHOD_DIRECT_DEBIT_VAULT, + TokenUiComponentProviderInterface::COMPONENT_DETAILS => $jsonDetails, + TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash() + ], + 'name' => 'Unzer_PAPI/js/view/payment/method-renderer/direct_debit_vault', + ] + ); + } +} diff --git a/etc/adminhtml/system/direct_debit.xml b/etc/adminhtml/system/direct_debit.xml index 49d5ea8..7d4f38c 100644 --- a/etc/adminhtml/system/direct_debit.xml +++ b/etc/adminhtml/system/direct_debit.xml @@ -40,5 +40,22 @@ payment/unzer_direct_debit/sort_order + + + + Magento\Config\Model\Config\Source\Yesno + payment/unzer_direct_debit_vault/active + Save IBAN and account holder for registered customers + + + + Magento\Config\Model\Config\Source\Yesno + payment/unzer_direct_debit_vault/instant_purchase_active + + 1 + +
diff --git a/etc/config.xml b/etc/config.xml index 1e74b70..03240f7 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -48,13 +48,27 @@ 0 0 0 + 0 1 + 1 0 1 0 + 1 EUR Unzer\PAPI\Model\Method\DirectDebit + + 0 + 0 + + Unzer\PAPI\Model\InstantPurchase\DirectDebit\AvailabilityChecker + Unzer\PAPI\Model\InstantPurchase\DirectDebit\TokenFormatter + + UnzerDirectDebitVault + <![CDATA[Stored SEPA Direct Debit Mandate]]> + EUR + 0 order diff --git a/etc/di.xml b/etc/di.xml index ea82523..5a6085b 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -83,6 +83,7 @@ UnzerCardsCommandManager UnzerPaypalCommandManager + UnzerDirectDebitCommandManager @@ -95,6 +96,7 @@ Unzer\PAPI\Model\Vault\Handlers\PaypalVaultDetailsHandler + Unzer\PAPI\Model\Vault\Handlers\DirectDebitVaultDetailsHandler @@ -296,6 +298,44 @@ + + + + Unzer\PAPI\Model\Config::METHOD_DIRECT_DEBIT_VAULT + + + + + + UnzerDirectDebitVaultPaymentConfig + + + + + + + UnzerDirectDebitVaultPaymentValueHandler + + + + + + + UnzerDirectDebitVaultPaymentConfig + UnzerDirectDebitVaultPaymentValueHandlerPool + Unzer\PAPI\Model\Method\DirectDebit + Unzer\PAPI\Model\Config::METHOD_DIRECT_DEBIT_VAULT + UnzerVaultCommandManagerPool + + + + + + + UnzerAuthorizeAndCaptureVaultCommandPool + + + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index ab3f252..2012ce8 100644 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -17,6 +17,7 @@ Unzer\PAPI\Model\Vault\Type\CardsTokenUiComponentProvider Unzer\PAPI\Model\Vault\Type\PaypalTokenUiComponentProvider + Unzer\PAPI\Model\Vault\Type\DirectDebitTokenUiComponentProvider diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index 36fd8a3..e4a142f 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -32,6 +32,9 @@ true + + true + true diff --git a/view/frontend/layout/vault_cards_listaction.xml b/view/frontend/layout/vault_cards_listaction.xml index 89cd3f5..7de72d8 100644 --- a/view/frontend/layout/vault_cards_listaction.xml +++ b/view/frontend/layout/vault_cards_listaction.xml @@ -1,11 +1,24 @@ - + - + - + + Unzer_PAPI::token_list.phtml + + + + + diff --git a/view/frontend/templates/customer_account/direct_debit.phtml b/view/frontend/templates/customer_account/direct_debit.phtml new file mode 100644 index 0000000..402c5a7 --- /dev/null +++ b/view/frontend/templates/customer_account/direct_debit.phtml @@ -0,0 +1,52 @@ +escapeHtml($block->getMaskedIban()); +$holder = (string)$block->escapeHtml($block->getAccountHolder()); +?> + + + + + + + + + + escapeHtml(__('SEPA Direct Debit')) ?> + + +
+ getBlockHtml('formkey') ?> + + +
+ + diff --git a/view/frontend/templates/customer_account/paypal.phtml b/view/frontend/templates/customer_account/paypal.phtml index 332e375..d5c989b 100644 --- a/view/frontend/templates/customer_account/paypal.phtml +++ b/view/frontend/templates/customer_account/paypal.phtml @@ -1,33 +1,48 @@ escapeHtml($block->getPayerEmail()); ?> - - -escapeHtmlAttr($block->getIconUrl()) ?> -escapeHtmlAttr($block->getIconWidth()) ?> -escapeHtmlAttr($block->getIconHeight()) ?> -escapeHtmlAttr(__('PayPal Logo')) ?> - escapeHtml($block->getPayerEmail()) ?> + + + + + + + + + + escapeHtml(__('PayPal')) ?> - + +
getBlockHtml('formkey') ?> - + + + + + From c8a68f2ca1adffec04be1fe8db2a9bc81c247a1e Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 16 Oct 2025 15:32:22 +0200 Subject: [PATCH 04/12] Add support for Wero payment method ISSUE: MAGENTO-299 --- composer.json | 15 ++++++- etc/adminhtml/system/wero.xml | 11 ++--- .../js/view/payment/method-renderer/wero.js | 44 ------------------- 3 files changed, 19 insertions(+), 51 deletions(-) diff --git a/composer.json b/composer.json index 2c4a1fc..653c15d 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "license": "Apache-2.0", "require": { "php": "~7.4.0|~8.1.0|~8.2.0|~8.3.0|~8.4.0", - "unzerdev/php-sdk": "^3.13.0", + "unzerdev/php-sdk": "^3.13.1", "ext-json": "*", "magento/framework": "*", "magento/module-backend": "*", @@ -42,5 +42,16 @@ "payment", "php", "payment-gateway" - ] + ], + "repositories": { + "magento": { + "type": "composer", + "url": "https://repo.magento.com" + } + }, + "config": { + "allow-plugins": { + "magento/composer-dependency-version-audit-plugin": true + } + } } diff --git a/etc/adminhtml/system/wero.xml b/etc/adminhtml/system/wero.xml index 53eb71c..278ebb0 100644 --- a/etc/adminhtml/system/wero.xml +++ b/etc/adminhtml/system/wero.xml @@ -2,29 +2,30 @@ - Magento\Config\Model\Config\Source\Yesno payment/unzer_wero/active - payment/unzer_wero/title - + + + payment/unzer_wero/order_payment_action + Unzer\PAPI\Model\System\Config\Source\PaymentAction + payment/unzer_wero/min_order_total - payment/unzer_wero/max_order_total Insert 0 to disable limit. - payment/unzer_wero/sort_order diff --git a/view/frontend/web/js/view/payment/method-renderer/wero.js b/view/frontend/web/js/view/payment/method-renderer/wero.js index fa4e3dd..9b69b8c 100644 --- a/view/frontend/web/js/view/payment/method-renderer/wero.js +++ b/view/frontend/web/js/view/payment/method-renderer/wero.js @@ -12,50 +12,6 @@ define([ defaults: { isActivePaymentTokenEnabler: false, template: 'Unzer_PAPI/payment/wero' - }, - - createSpecificPaymentElement: function () { - return $(''); - }, - - selectPaymentMethod: function () { - var ret = this._super(); - this.waitForSetBasketData(); - return ret; - }, - - getPlaceOrderDeferredObject: function () { - var deferred = $.Deferred(), self = this; - - Promise.all([ - customElements.whenDefined('unzer-wero') - ]).then(function () { - var unzerCheckout = document.getElementById('unzer-checkout-' + self.getCode()); - - unzerCheckout.onPaymentSubmit = function (response) { - if (response.submitResponse && response.submitResponse.success) { - self.resourceId = response.submitResponse.data.id; - placeOrderAction(self.getData(), self.messageContainer) - .done(function () { deferred.resolve.apply(deferred, arguments); }) - .fail(function (request) { - globalMessageList.addErrorMessage({ message: request.responseJSON.message }); - deferred.reject(request.responseJSON.message); - }); - } else { - globalMessageList.addErrorMessage({ - message: 'There was an error placing your order. ' + response.submitResponse.message - }); - deferred.reject($t('There was an error placing your order. ' + response.submitResponse.message)); - } - }; - }).catch(function (error) { - globalMessageList.addErrorMessage({ message: 'There was an error placing your order. ' + error }); - deferred.reject($t('There was an error placing your order. ' + error)); - }); - - return deferred.fail(function (error) { - globalMessageList.addErrorMessage({ message: error }); - }); } }); }); From 86016101133c852bb7391135c24274b9abe93f47 Mon Sep 17 00:00:00 2001 From: Kristina Date: Fri, 17 Oct 2025 11:02:32 +0200 Subject: [PATCH 05/12] Refactor .js files ISSUE: MAGENTO-309 --- .../js/view/payment/method-renderer/basev2.js | 10 ++- .../js/view/payment/method-renderer/cards.js | 16 ----- .../payment/method-renderer/direct_debit.js | 63 ------------------- .../js/view/payment/method-renderer/paypal.js | 16 ----- 4 files changed, 9 insertions(+), 96 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/basev2.js b/view/frontend/web/js/view/payment/method-renderer/basev2.js index bc37c4c..c1ad936 100644 --- a/view/frontend/web/js/view/payment/method-renderer/basev2.js +++ b/view/frontend/web/js/view/payment/method-renderer/basev2.js @@ -344,7 +344,15 @@ define( message: error }); }); - } + }, + + isVaultEnabled: function () { + return this.vaultEnabler.isVaultEnabled(); + }, + + getVaultCode: function () { + return window.checkoutConfig.payment[this.getCode()].vault_code; + }, }); } ); diff --git a/view/frontend/web/js/view/payment/method-renderer/cards.js b/view/frontend/web/js/view/payment/method-renderer/cards.js index 4de6a6b..5412bb9 100644 --- a/view/frontend/web/js/view/payment/method-renderer/cards.js +++ b/view/frontend/web/js/view/payment/method-renderer/cards.js @@ -76,22 +76,6 @@ define( return retVal; }, - /** - * @returns {Boolean} - */ - isVaultEnabled: function () { - return this.vaultEnabler.isVaultEnabled(); - }, - - /** - * Returns vault code. - * - * @returns {String} - */ - getVaultCode: function () { - return window.checkoutConfig.payment[this.getCode()].vault_code; - }, - getData: function () { const data = this._super(); diff --git a/view/frontend/web/js/view/payment/method-renderer/direct_debit.js b/view/frontend/web/js/view/payment/method-renderer/direct_debit.js index 316b66b..adb47e8 100644 --- a/view/frontend/web/js/view/payment/method-renderer/direct_debit.js +++ b/view/frontend/web/js/view/payment/method-renderer/direct_debit.js @@ -58,73 +58,10 @@ define( return retVal; }, - /** - * @returns {Boolean} - */ - isVaultEnabled: function () { - return this.vaultEnabler.isVaultEnabled(); - }, - - /** - * Returns vault code. - * @returns {String} - */ - getVaultCode: function () { - return window.checkoutConfig.payment[this.getCode()].vault_code; - }, - getData: function () { const data = this._super(); this.vaultEnabler.visitAdditionalData(data); return data; - }, - - getPlaceOrderDeferredObject: function () { - const deferred = $.Deferred(); - const self = this; - - Promise.all([ - customElements.whenDefined('unzer-sepa-direct-debit') - ]).then(() => { - const unzerCheckout = document.getElementById('unzer-checkout-unzer_direct_debit'); - - if (!unzerCheckout) { - deferred.reject($t('SEPA component not found on the page.')); - return; - } - - unzerCheckout.onPaymentSubmit = function (response) { - const sr = response && response.submitResponse; - - if (sr && (sr.status === 'SUCCESS' || sr.success === true)) { - self.resourceId = sr.data && sr.data.id; - - placeOrderAction(self.getData(), self.messageContainer) - .done(function () { - deferred.resolve.apply(deferred, arguments); - }) - .fail(function (request) { - const msg = request && request.responseJSON && request.responseJSON.message - ? request.responseJSON.message - : $t('An unknown error occurred. Please try again.'); - globalMessageList.addErrorMessage({ message: msg }); - deferred.reject(msg); - }); - } else { - const msg = (sr && sr.message) ? sr.message : $t('Unknown submit error.'); - globalMessageList.addErrorMessage({ - message: $t('There was an error placing your order. ') + msg - }); - deferred.reject($t('There was an error placing your order. ') + msg); - } - }; - }).catch(function (error) { - deferred.reject($t('There was an error placing your order. ') + error); - }); - - return deferred.fail(function (error) { - globalMessageList.addErrorMessage({ message: error }); - }); } }); } diff --git a/view/frontend/web/js/view/payment/method-renderer/paypal.js b/view/frontend/web/js/view/payment/method-renderer/paypal.js index d9b57f9..00b4ca2 100644 --- a/view/frontend/web/js/view/payment/method-renderer/paypal.js +++ b/view/frontend/web/js/view/payment/method-renderer/paypal.js @@ -56,22 +56,6 @@ define( return retVal; }, - /** - * @returns {Boolean} - */ - isVaultEnabled: function () { - return this.vaultEnabler.isVaultEnabled(); - }, - - /** - * Returns vault code. - * - * @returns {String} - */ - getVaultCode: function () { - return window.checkoutConfig.payment[this.getCode()].vault_code; - }, - getData: function () { var data = this._super(); From b1ea15ea0bb185ad81513ff62ec6ad2e90cc49d4 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 20 Oct 2025 10:18:40 +0200 Subject: [PATCH 06/12] Fix wero.js ISSUE: MAGENTO-299 --- .../js/view/payment/method-renderer/wero.js | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/wero.js b/view/frontend/web/js/view/payment/method-renderer/wero.js index 9b69b8c..2c4281f 100644 --- a/view/frontend/web/js/view/payment/method-renderer/wero.js +++ b/view/frontend/web/js/view/payment/method-renderer/wero.js @@ -1,17 +1,16 @@ -define([ - 'jquery', - 'ko', - 'mage/translate', - 'Magento_Checkout/js/action/place-order', - 'Magento_Ui/js/model/messageList', - 'Unzer_PAPI/js/view/payment/method-renderer/basev2' -], function ($, ko, $t, placeOrderAction, globalMessageList, Component) { - 'use strict'; +define( + [ + 'Unzer_PAPI/js/view/payment/method-renderer/basev2' + ], + function ( + Component + ) { + 'use strict'; - return Component.extend({ - defaults: { - isActivePaymentTokenEnabler: false, - template: 'Unzer_PAPI/payment/wero' - } + return Component.extend({ + defaults: { + template: 'Unzer_PAPI/payment/wero', + paymentCode: 'unzer-wero', + } + }); }); -}); From 63b02296eb99759eddb96783a1477714c3fb415f Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 21 Oct 2025 16:29:18 +0200 Subject: [PATCH 07/12] Fix chargeback transactions display ISSUE: MAGENTO-298 --- Model/Command/TransactionSynchronizer.php | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Model/Command/TransactionSynchronizer.php b/Model/Command/TransactionSynchronizer.php index d67893d..57c8695 100644 --- a/Model/Command/TransactionSynchronizer.php +++ b/Model/Command/TransactionSynchronizer.php @@ -2,7 +2,10 @@ namespace Unzer\PAPI\Model\Command; +use Exception; +use Magento\Framework\Exception\LocalizedException; use Magento\Sales\Api\Data\OrderInterface; +use Magento\Sales\Api\Data\TransactionInterface; use Magento\Sales\Api\OrderPaymentRepositoryInterface; use Magento\Sales\Api\TransactionRepositoryInterface; use Magento\Sales\Model\Order\Payment as OrderPayment; @@ -142,6 +145,8 @@ public function applyCancellationOnMagento(OrderInterface $order, UnzerPayment $ * @param UnzerPayment $unzer * * @return void + * @throws LocalizedException + * @throws Exception */ public function applyChargebackOnMagento(OrderInterface $order, UnzerPayment $unzer): void { @@ -158,20 +163,28 @@ public function applyChargebackOnMagento(OrderInterface $order, UnzerPayment $un return; } - if ($this->hasTransaction($payment, $order, $chargebackId)) { - return; - } - $parent = $chargeback->getParentResource(); $parentTxnId = $parent->getId(); $chargebackTxnId = $parentTxnId . '-' . $chargebackId; + if ($this->hasTransaction($payment, $order, $chargebackTxnId)) { + return; + } + $payment->setParentTransactionId($parentTxnId); $payment->setTransactionId($chargebackTxnId); $payment->registerRefundNotification($chargeback->getAmount()); + $transaction = $payment->addTransaction(TransactionInterface::TYPE_REFUND, null, true); + + if ($transaction) { + $transaction->setTxnId($chargebackTxnId); + $transaction->setParentTxnId($parentTxnId); + $transaction->setIsClosed(true); + } + $this->paymentRepository->save($payment); } From 4e724d0baab1cade143a69d80349ec07ea5b6c7d Mon Sep 17 00:00:00 2001 From: Boljanovic Date: Wed, 22 Oct 2025 12:05:51 +0200 Subject: [PATCH 08/12] Update Invoice B2B component ISSUE: UPP-325 --- i18n/de_DE.csv | 7 -- i18n/en_US.csv | 7 -- view/frontend/web/css/unzer.css | 6 +- .../js/view/payment/method-renderer/basev2.js | 18 ++++-- .../method-renderer/paylater_direct_debit.js | 2 +- .../method-renderer/paylater_installment.js | 2 +- .../method-renderer/paylater_invoice.js | 2 +- .../method-renderer/paylater_invoice_b2b.js | 64 +------------------ .../payment/paylater_invoice_b2b.html | 10 --- 9 files changed, 17 insertions(+), 101 deletions(-) diff --git a/i18n/de_DE.csv b/i18n/de_DE.csv index 7fd3ce0..c4e4b58 100644 --- a/i18n/de_DE.csv +++ b/i18n/de_DE.csv @@ -87,10 +87,3 @@ "There will be no notification if the payment has been aborted.","Sollte die Zahlung abgebrochen worden sein, erfolgt keine weitere Mitteilung." "Please contact us for further information.","Bitte kontaktieren Sie uns für weitere Informationen." "Unknown Email Address","Unbekannte Email Adresse" - -"Company type","Art des Unternehmens" -"Other","Sonstige" -"Authority","Behörde" -"Association","Gesellschaft" -"Sole","Einzelunternehmen", -"Company","Firma" diff --git a/i18n/en_US.csv b/i18n/en_US.csv index 41dd5a7..9ec9d04 100644 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -87,10 +87,3 @@ "There will be no notification if the payment has been aborted. Please contact us for further information.","There will be no notification if the payment has been aborted." "Please contact us for further information.","Please contact us for further information." "Unknown Email Address","Unknown Email Address" - -"Company type","Company type" -"Other","Other" -"Authority","Authority" -"Association","Association" -"Sole","Sole", -"Company","Company" diff --git a/view/frontend/web/css/unzer.css b/view/frontend/web/css/unzer.css index 86a10e6..0f048a5 100644 --- a/view/frontend/web/css/unzer.css +++ b/view/frontend/web/css/unzer.css @@ -5335,11 +5335,7 @@ i.h-iconideal-van-lanschot:before { margin: 12px; } -.unzerCompanyTypeSelect { - margin-bottom: 15px; -} - .apple-pay-button { width: 240px; height: 40px; -} \ No newline at end of file +} diff --git a/view/frontend/web/js/view/payment/method-renderer/basev2.js b/view/frontend/web/js/view/payment/method-renderer/basev2.js index c1ad936..d14a2db 100644 --- a/view/frontend/web/js/view/payment/method-renderer/basev2.js +++ b/view/frontend/web/js/view/payment/method-renderer/basev2.js @@ -36,7 +36,7 @@ define( isThreatMetrixNeeded: false, buttonNeeded: true, paymentCode: null, - customersBirthDayNeeded: false, + customerNeeded: false, customerType: null, threatMetrixId: null, lastGrandTotal: null, @@ -107,7 +107,7 @@ define( componentContainer.append(unzerPayment); componentContainer.append(unzerCheckout); - if (this.customersBirthDayNeeded) { + if (this.customerNeeded) { this.waitForSetBasketData(); } @@ -275,7 +275,13 @@ define( zip: shipping.postcode, city: shipping.city, country: shipping.countryId - } : {} + } : {}, + ...(billing?.company && billing.company.trim() !== '' + ? { company: billing.company.trim() } + : {}), + customerSettings: { + type: billing?.company && billing.company.trim() !== '' ? 'B2B' : 'B2C' + } }; unzerPayment.setCustomerData(customer); @@ -298,11 +304,11 @@ define( const unzerCheckout = document.getElementById(unzerCheckoutElementId); unzerCheckout.onPaymentSubmit = response => { if (response.submitResponse && response.submitResponse.success) { - this.resourceId = response.submitResponse.data.id; - if (this.customersBirthDayNeeded) { - this.customersBirthDay = document.querySelector(this.paymentCode).shadowRoot?.querySelector('uds-input-date[name="birthDate"]').value; + if(response.customerResponse && response.customerResponse.success) { + this.customer = response.customerResponse.data.id; } + this.resourceId = response.submitResponse.data.id; if (response.threatMetrixId) { this.threatMetrixId = response.threatMetrixId; diff --git a/view/frontend/web/js/view/payment/method-renderer/paylater_direct_debit.js b/view/frontend/web/js/view/payment/method-renderer/paylater_direct_debit.js index 8e5c37b..68efb23 100644 --- a/view/frontend/web/js/view/payment/method-renderer/paylater_direct_debit.js +++ b/view/frontend/web/js/view/payment/method-renderer/paylater_direct_debit.js @@ -12,7 +12,7 @@ define( defaults: { template: 'Unzer_PAPI/payment/paylater_direct_debit', paymentCode: 'unzer-paylater-direct-debit', - customersBirthDayNeeded: true, + customerNeeded: true, } }); } diff --git a/view/frontend/web/js/view/payment/method-renderer/paylater_installment.js b/view/frontend/web/js/view/payment/method-renderer/paylater_installment.js index a8c7c77..b64eebf 100644 --- a/view/frontend/web/js/view/payment/method-renderer/paylater_installment.js +++ b/view/frontend/web/js/view/payment/method-renderer/paylater_installment.js @@ -8,7 +8,7 @@ define( return Component.extend({ isThreatMetrixNeeded: true, - customersBirthDayNeeded: true, + customerNeeded: true, defaults: { template: 'Unzer_PAPI/payment/paylater_installment', paymentCode: 'unzer-paylater-installment' diff --git a/view/frontend/web/js/view/payment/method-renderer/paylater_invoice.js b/view/frontend/web/js/view/payment/method-renderer/paylater_invoice.js index 3dc7e9e..26f625b 100644 --- a/view/frontend/web/js/view/payment/method-renderer/paylater_invoice.js +++ b/view/frontend/web/js/view/payment/method-renderer/paylater_invoice.js @@ -13,7 +13,7 @@ define( defaults: { template: 'Unzer_PAPI/payment/paylater_invoice', paymentCode: 'unzer-paylater-invoice', - customersBirthDayNeeded: true, + customerNeeded: true, } }); } diff --git a/view/frontend/web/js/view/payment/method-renderer/paylater_invoice_b2b.js b/view/frontend/web/js/view/payment/method-renderer/paylater_invoice_b2b.js index b54f448..193cafd 100644 --- a/view/frontend/web/js/view/payment/method-renderer/paylater_invoice_b2b.js +++ b/view/frontend/web/js/view/payment/method-renderer/paylater_invoice_b2b.js @@ -24,70 +24,8 @@ define( customerType: 'b2b', template: 'Unzer_PAPI/payment/paylater_invoice_b2b', paymentCode: 'unzer-paylater-invoice', - customersBirthDayNeeded: true, + customerNeeded: true, }, - - getPlaceOrderDeferredObject: function () { - let deferred = $.Deferred(), - self = this; - - Promise.all([ - customElements.whenDefined(this.paymentCode) - ]).then(() => { - const unzerCheckoutElementId = 'unzer-checkout-' + this.getCode(); - const unzerCheckout = document.getElementById(unzerCheckoutElementId); - - unzerCheckout.onPaymentSubmit = response => { - if (response.submitResponse && response.submitResponse.success) { - this.resourceId = response.submitResponse.data.id; - const dropdown = document.getElementById('unzer-dropdown-field'); - this.customerType = dropdown.value; - - if (this.customersBirthDayNeeded) { - this.customersBirthDay = document.querySelector(this.paymentCode).shadowRoot?.querySelector('uds-input-date[name="birthDate"]').value; - } - - if (response.threatMetrixId) { - this.threatMetrixId = response.threatMetrixId; - } - - placeOrderAction(self.getData(), self.messageContainer) - .done(function () { - deferred.resolve.apply(deferred, arguments); - }) - .fail(function (request) { - if (request.responseJSON && request.responseJSON.message) { - globalMessageList.addErrorMessage({ - message: request.responseJSON.message - }); - deferred.reject(request.responseJSON.message); - } else { - globalMessageList.addErrorMessage({ - message: 'An unknown error occurred. Please try again.' - }); - deferred.reject('An unknown error occurred.'); - } - }); - } else { - globalMessageList.addErrorMessage({ - message: 'There was an error placing your order. ' + response.submitResponse.message - }); - deferred.reject($t('There was an error placing your order. ' + response.submitResponse.message)); - } - }; - }).catch(error => { - globalMessageList.addErrorMessage({ - message: 'There was an error placing your order. ' + error - }); - deferred.reject($t('There was an error placing your order. ' + error)); - }); - - return deferred.fail(function (error) { - globalMessageList.addErrorMessage({ - message: error - }); - }); - } }); } ); diff --git a/view/frontend/web/template/payment/paylater_invoice_b2b.html b/view/frontend/web/template/payment/paylater_invoice_b2b.html index 2177b0a..b2a4f35 100644 --- a/view/frontend/web/template/payment/paylater_invoice_b2b.html +++ b/view/frontend/web/template/payment/paylater_invoice_b2b.html @@ -15,16 +15,6 @@ -
- - -
From 4be163a4a9741c6812c25336e46951506b0ab34a Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 22 Oct 2025 13:55:16 +0200 Subject: [PATCH 09/12] Fix order status for partial prepayment ISSUE: MAGENTO-323 --- Helper/Payment.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Helper/Payment.php b/Helper/Payment.php index 6a59a2c..ff1550a 100644 --- a/Helper/Payment.php +++ b/Helper/Payment.php @@ -39,6 +39,7 @@ class Payment { public const STATUS_READY_TO_CAPTURE = 'unzer_ready_to_capture'; + private const METHOD_PREPAYMENT = 'unzer_prepayment'; /** * @var InvoiceRepositoryInterface @@ -361,7 +362,13 @@ private function processPartlyState(OrderInterface $order, PaymentResource $paym $this->transactionSynchronizer->applyCancellationOnMagento($order, $payment); $this->transactionSynchronizer->applyCaptureOnMagento($order, $payment); - $this->setOrderState($order, Order::STATE_PROCESSING); + if($order->getPayment()->getMethod() === self::METHOD_PREPAYMENT) { + $this->setOrderState($order, Order::STATE_PENDING_PAYMENT); + + return; + } + + $this->setOrderState($order); } /** From 45264496fee81fe18644f8fcdd958bde696db9c6 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 27 Oct 2025 12:19:57 +0100 Subject: [PATCH 10/12] Fix order status for partial prepayment ISSUE: MAGENTO-323 --- Model/Command/TransactionSynchronizer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Model/Command/TransactionSynchronizer.php b/Model/Command/TransactionSynchronizer.php index 57c8695..7c61335 100644 --- a/Model/Command/TransactionSynchronizer.php +++ b/Model/Command/TransactionSynchronizer.php @@ -67,6 +67,7 @@ public function applyCaptureOnMagento(OrderInterface $order, UnzerPayment $unzer $payment->setTransactionId($captureId); if ($capture->isSuccess()) { + $payment->setIsTransactionPending($unzer->isPartlyPaid()); $payment->registerCaptureNotification($capture->getAmount(), true); $this->paymentRepository->save($payment); } From a0171e19559940c405a4b217bd2ff4975e26f219 Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 29 Oct 2025 15:38:40 +0100 Subject: [PATCH 11/12] Fix 404 on cancel Invoice for Prepayment ISSUE: MAGENTO-322 --- Controller/Adminhtml/Order/Invoice/Cancel.php | 40 +++++++++++++++++++ etc/adminhtml/routes.xml | 3 ++ etc/di.xml | 1 + 3 files changed, 44 insertions(+) create mode 100644 Controller/Adminhtml/Order/Invoice/Cancel.php diff --git a/Controller/Adminhtml/Order/Invoice/Cancel.php b/Controller/Adminhtml/Order/Invoice/Cancel.php new file mode 100644 index 0000000..a3d80c8 --- /dev/null +++ b/Controller/Adminhtml/Order/Invoice/Cancel.php @@ -0,0 +1,40 @@ +getInvoice(); + if (!$invoice) { + $resultForward = $this->resultForwardFactory->create(); + return $resultForward->forward('noroute'); + } + + try { + $invoice->cancel(); + $invoice->getOrder()->setIsInProcess(true); + $this->_objectManager->create( + \Magento\Framework\DB\Transaction::class + )->addObject( + $invoice + )->addObject( + $invoice->getOrder() + )->save(); + $this->messageManager->addSuccessMessage(__('You canceled the invoice.')); + } catch (\Magento\Framework\Exception\LocalizedException $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } catch (\Exception $e) { + $this->messageManager->addErrorMessage(__('Invoice canceling error')); + } + + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('sales/*/view', ['invoice_id' => $invoice->getId()]); + return $resultRedirect; + } +} diff --git a/etc/adminhtml/routes.xml b/etc/adminhtml/routes.xml index da0f5b9..294c5f3 100644 --- a/etc/adminhtml/routes.xml +++ b/etc/adminhtml/routes.xml @@ -5,5 +5,8 @@ + + + diff --git a/etc/di.xml b/etc/di.xml index 5a6085b..df3778e 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -6,6 +6,7 @@ + From 5f3edc53c23eb9dc5f195df7ae858c0b2a69cc5e Mon Sep 17 00:00:00 2001 From: Boljanovic Date: Mon, 8 Dec 2025 12:31:35 +0100 Subject: [PATCH 12/12] [Release 4.0.1 - 2025-12-08] --- CHANGELOG.md | 7 +++++++ composer.json | 2 +- etc/module.xml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c5a8d3..422b08c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [4.0.1](https://github.com/unzerdev/magento2/compare/4.0.0..4.0.1) +### Changed +* Invoice B2B component on checkout page +### Added +* Support for Wero payment method +* Support for Unzer Direct Debit recurring payments + ## [4.0.0](https://github.com/unzerdev/magento2/compare/3.2.9..4.0.0) ### Changed * Migration from Unzer UI Component V1 to Unzer UI Component V2 diff --git a/composer.json b/composer.json index 653c15d..7ec6365 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,7 @@ "name": "unzerdev/magento2", "description": "This extension for Magento 2 provides a direct integration of the Unzer payment types to your Magento 2 shop via the Unzer Payment API (PAPI).", "type": "magento2-module", - "version": "4.0.0", + "version": "4.0.1", "license": "Apache-2.0", "require": { "php": "~7.4.0|~8.1.0|~8.2.0|~8.3.0|~8.4.0", diff --git a/etc/module.xml b/etc/module.xml index d3dce32..32eb361 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,7 +1,7 @@ - +