From 1c945fd5bcb62ffecc8be5cd28c8a853cf91e759 Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 18 Dec 2025 11:59:35 +0100 Subject: [PATCH 01/11] Fix store PayPal and SEPA on Magento 2.4.8 ISSUE: UPP-374 --- .../DirectDebitVaultDetailsHandler.php | 42 +++++++- .../Handlers/PaypalVaultDetailsHandler.php | 16 ++- Model/Vault/Handlers/VaultTokenPersister.php | 98 +++++++++++++++++++ 3 files changed, 152 insertions(+), 4 deletions(-) create mode 100644 Model/Vault/Handlers/VaultTokenPersister.php diff --git a/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php b/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php index ceb54fa..4ea7de7 100644 --- a/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php +++ b/Model/Vault/Handlers/DirectDebitVaultDetailsHandler.php @@ -28,24 +28,58 @@ */ class DirectDebitVaultDetailsHandler implements VaultDetailsHandlerInterface { + /** + * @var PaymentTokenFactoryInterface + */ private PaymentTokenFactoryInterface $paymentTokenFactory; + + /** + * @var OrderPaymentExtensionInterfaceFactory + */ private OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory; + + /** + * @var Json + */ private Json $serializer; + + /** + * @var DateTimeFactory + */ private DateTimeFactory $dateTimeFactory; + + /** + * @var PaymentTokenResourceModel + */ private PaymentTokenResourceModel $paymentTokenResourceModel; + /** + * @var VaultTokenPersister + */ + private VaultTokenPersister $vaultTokenPersister; + + /** + * @param PaymentTokenFactoryInterface $paymentTokenFactory + * @param OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory + * @param Json $serializer + * @param DateTimeFactory $dateTimeFactory + * @param PaymentTokenResourceModel $paymentTokenResourceModel + * @param VaultTokenPersister $vaultTokenPersister + */ public function __construct( PaymentTokenFactoryInterface $paymentTokenFactory, OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory, Json $serializer, DateTimeFactory $dateTimeFactory, - PaymentTokenResourceModel $paymentTokenResourceModel + PaymentTokenResourceModel $paymentTokenResourceModel, + VaultTokenPersister $vaultTokenPersister ) { $this->paymentTokenFactory = $paymentTokenFactory; $this->paymentExtensionFactory = $paymentExtensionFactory; $this->serializer = $serializer; $this->dateTimeFactory = $dateTimeFactory; $this->paymentTokenResourceModel = $paymentTokenResourceModel; + $this->vaultTokenPersister = $vaultTokenPersister; } /** @@ -77,7 +111,11 @@ public function handle(PaymentDataObject $payment, AbstractTransactionType $tran $paymentToken = $this->createVaultPaymentToken($transaction, $payment); if ($paymentToken !== null) { - $extensionAttributes = $this->getExtensionAttributes($payment->getPayment()); + $orderPayment = $payment->getPayment(); + + $this->vaultTokenPersister->save($paymentToken, $orderPayment); + + $extensionAttributes = $this->getExtensionAttributes($orderPayment); $extensionAttributes->setVaultPaymentToken($paymentToken); } } diff --git a/Model/Vault/Handlers/PaypalVaultDetailsHandler.php b/Model/Vault/Handlers/PaypalVaultDetailsHandler.php index 7143c0b..5dc3bf5 100644 --- a/Model/Vault/Handlers/PaypalVaultDetailsHandler.php +++ b/Model/Vault/Handlers/PaypalVaultDetailsHandler.php @@ -53,6 +53,11 @@ class PaypalVaultDetailsHandler implements VaultDetailsHandlerInterface */ private PaymentTokenResourceModel $paymentTokenResourceModel; + /** + * @var VaultTokenPersister + */ + private VaultTokenPersister $vaultTokenPersister; + /** * Constructor * @@ -61,19 +66,22 @@ class PaypalVaultDetailsHandler implements VaultDetailsHandlerInterface * @param DateTimeFactory $dateTimeFactory * @param Json $serializer * @param PaymentTokenResourceModel $paymentTokenResourceModel + * @param VaultTokenPersister $vaultTokenPersister */ public function __construct( PaymentTokenFactoryInterface $paymentTokenFactory, OrderPaymentExtensionInterfaceFactory $paymentExtensionFactory, DateTimeFactory $dateTimeFactory, Json $serializer, - PaymentTokenResourceModel $paymentTokenResourceModel + PaymentTokenResourceModel $paymentTokenResourceModel, + VaultTokenPersister $vaultTokenPersister ) { $this->paymentTokenFactory = $paymentTokenFactory; $this->paymentExtensionFactory = $paymentExtensionFactory; $this->serializer = $serializer; $this->dateTimeFactory = $dateTimeFactory; $this->paymentTokenResourceModel = $paymentTokenResourceModel; + $this->vaultTokenPersister = $vaultTokenPersister; } /** @@ -106,7 +114,11 @@ public function handle(PaymentDataObject $payment, AbstractTransactionType $tran // add vault payment token entity to extension attributes $paymentToken = $this->createVaultPaymentToken($transaction, $payment); if (null !== $paymentToken) { - $extensionAttributes = $this->getExtensionAttributes($payment->getPayment()); + $orderPayment = $payment->getPayment(); + + $this->vaultTokenPersister->save($paymentToken, $orderPayment); + + $extensionAttributes = $this->getExtensionAttributes($orderPayment); $extensionAttributes->setVaultPaymentToken($paymentToken); } } diff --git a/Model/Vault/Handlers/VaultTokenPersister.php b/Model/Vault/Handlers/VaultTokenPersister.php new file mode 100644 index 0000000..8315d81 --- /dev/null +++ b/Model/Vault/Handlers/VaultTokenPersister.php @@ -0,0 +1,98 @@ +paymentTokenManagement = $paymentTokenManagement; + $this->encryptor = $encryptor; + } + + /** + * @param PaymentTokenInterface $paymentToken + * @param OrderPaymentInterface $payment + * + * @return void + */ + public function save(PaymentTokenInterface $paymentToken, OrderPaymentInterface $payment): void + { + if (!$paymentToken->getGatewayToken()) { + return; + } + + $order = $payment->getOrder(); + if (!$order) { + return; + } + + if ($paymentToken->getEntityId()) { + $this->paymentTokenManagement->addLinkToOrderPayment( + $paymentToken->getEntityId(), + $payment->getEntityId() + ); + + return; + } + + $paymentToken->setCustomerId($order->getCustomerId()); + $paymentToken->setIsActive(true); + $paymentToken->setPaymentMethodCode($payment->getMethod()); + + $additionalInformation = $payment->getAdditionalInformation(); + $paymentToken->setIsVisible( + (bool)(int)($additionalInformation[VaultConfigProvider::IS_ACTIVE_CODE] ?? 0) + ); + + $paymentToken->setPublicHash( + $this->generatePublicHash($paymentToken, (int)$order->getCustomerId()) + ); + + $this->paymentTokenManagement->saveTokenWithPaymentLink($paymentToken, $payment); + } + + /** + * @param PaymentTokenInterface $paymentToken + * @param int|null $customerId + * + * @return string + */ + private function generatePublicHash(PaymentTokenInterface $paymentToken, ?int $customerId): string + { + return $this->encryptor->getHash( + ($customerId ?: '') + . $paymentToken->getPaymentMethodCode() + . $paymentToken->getType() + . $paymentToken->getTokenDetails() + ); + } +} From cbbe5e0954f2f6fa4ba866b5387a23ea880b4604 Mon Sep 17 00:00:00 2001 From: Kristina Date: Tue, 20 Jan 2026 14:21:57 +0100 Subject: [PATCH 02/11] Fix the ApplePay button size and billing address form appearance ISSUE: UPP-365 --- .../web/js/view/payment/method-renderer/applepayv2.js | 10 ++-------- .../web/js/view/payment/method-renderer/basev2.js | 11 +++++++---- view/frontend/web/template/payment/applepayv2.html | 2 +- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/applepayv2.js b/view/frontend/web/js/view/payment/method-renderer/applepayv2.js index 9d300cd..946283d 100644 --- a/view/frontend/web/js/view/payment/method-renderer/applepayv2.js +++ b/view/frontend/web/js/view/payment/method-renderer/applepayv2.js @@ -25,17 +25,11 @@ define( paymentCode: 'unzer-apple-pay' }, - isApplePayAvailable: function () { - return window.ApplePaySession && ApplePaySession.canMakePayments(); - }, - selectPaymentMethod: function () { let retVal = this._super(); const componentContainer = $('#unzer-component-' + this.getCode()); - if(this.isApplePayAvailable()){ - componentContainer.addClass('apple-pay-button'); - } + componentContainer.addClass('apple-pay-button'); this.waitForSetApplePayData(); const unzerCheckoutElementId = 'unzer-checkout-' + this.getCode(); @@ -68,7 +62,7 @@ define( countryCode: quote.billingAddress().countryId, currencyCode: window.checkoutConfig.quoteData.base_currency_code, totalLabel: window.checkoutConfig.payment.unzer_applepayv2.label, - totalAmount: Number(totals['base_grand_total']).toFixed(2), + totalAmount: Number(totals['base_grand_total']).toFixed(2), supportedNetworks: supportedNetworks, merchantCapabilities: window.checkoutConfig.payment.unzer_applepayv2.merchantCapabilities, requiredShippingContactFields: [], 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 d14a2db..e3d4be1 100644 --- a/view/frontend/web/js/view/payment/method-renderer/basev2.js +++ b/view/frontend/web/js/view/payment/method-renderer/basev2.js @@ -167,7 +167,10 @@ define( return overridePublicKey; } - return window.checkoutConfig.payment.unzer.publicKey; + return ( + window.checkoutConfig.payment[this.getCode()]?.publicKey || + window.checkoutConfig.payment.unzer?.publicKey + ); }, /** @@ -260,7 +263,7 @@ define( firstname: billing ? billing.firstname : '', lastname: billing ? billing.lastname : '', email: quote.guestEmail ? quote.guestEmail : (window.customerData ? window.customerData.email : ''), - ...(customerData?.dob ? { birthDate: customerData.dob.split('T')[0] } : {}), + ...(customerData?.dob ? {birthDate: customerData.dob.split('T')[0]} : {}), billingAddress: billing ? { name: (billing.firstname || '') + ' ' + (billing.lastname || ''), street: Array.isArray(billing.street) ? billing.street.join(' ') : billing.street, @@ -277,7 +280,7 @@ define( country: shipping.countryId } : {}, ...(billing?.company && billing.company.trim() !== '' - ? { company: billing.company.trim() } + ? {company: billing.company.trim()} : {}), customerSettings: { type: billing?.company && billing.company.trim() !== '' ? 'B2B' : 'B2C' @@ -305,7 +308,7 @@ define( unzerCheckout.onPaymentSubmit = response => { if (response.submitResponse && response.submitResponse.success) { - if(response.customerResponse && response.customerResponse.success) { + if (response.customerResponse && response.customerResponse.success) { this.customer = response.customerResponse.data.id; } this.resourceId = response.submitResponse.data.id; diff --git a/view/frontend/web/template/payment/applepayv2.html b/view/frontend/web/template/payment/applepayv2.html index 9285e70..5112345 100644 --- a/view/frontend/web/template/payment/applepayv2.html +++ b/view/frontend/web/template/payment/applepayv2.html @@ -10,7 +10,7 @@
-
+
From 17285484db68f5fba97324a5cd319cebbd1c1382 Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 21 Jan 2026 11:51:07 +0100 Subject: [PATCH 03/11] Reorder default payment method list ISSUE: UPP-382 --- Model/Config/Provider.php | 26 +-- etc/config.xml | 352 +++++++++++++++++++------------------- 2 files changed, 189 insertions(+), 189 deletions(-) diff --git a/Model/Config/Provider.php b/Model/Config/Provider.php index 3edd2ce..d7ec8f9 100644 --- a/Model/Config/Provider.php +++ b/Model/Config/Provider.php @@ -22,26 +22,26 @@ class Provider implements ConfigProviderInterface * @var array */ protected array $_methodCodes = [ - Config::METHOD_CARDS, - Config::METHOD_DIRECT_DEBIT, - Config::METHOD_EPS, - Config::METHOD_IDEAL, Config::METHOD_PAYLATER_INVOICE, Config::METHOD_PAYLATER_INVOICE_B2B, Config::METHOD_PAYLATER_INSTALLMENT, Config::METHOD_PAYLATER_DIRECT_DEBIT, - Config::METHOD_PAYPAL, - Config::METHOD_ALIPAY, - Config::METHOD_WECHATPAY, - Config::METHOD_PRZELEWY24, - Config::METHOD_BANCONTACT, - Config::METHOD_PREPAYMENT, + Config::METHOD_OPEN_BANKING, + Config::METHOD_CARDS, Config::METHOD_APPLEPAYV2, Config::METHOD_GOOGLEPAY, - Config::METHOD_TWINT, - Config::METHOD_OPEN_BANKING, - Config::METHOD_KLARNA, Config::METHOD_WERO, + Config::METHOD_KLARNA, + Config::METHOD_EPS, + Config::METHOD_IDEAL, + Config::METHOD_PRZELEWY24, + Config::METHOD_TWINT, + Config::METHOD_DIRECT_DEBIT, + Config::METHOD_PREPAYMENT, + Config::METHOD_BANCONTACT, + Config::METHOD_PAYPAL, + Config::METHOD_ALIPAY, + Config::METHOD_WECHATPAY, ]; /** diff --git a/etc/config.xml b/etc/config.xml index 03240f7..1b4e84b 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -9,83 +9,6 @@ Unzer\PAPI\Model\Method\Base 0 - - 0 - order - authorize - <![CDATA[Credit Card / Debit Card]]> - 0 - 0 - 0 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 1 - 0 - 0 - Unzer\PAPI\Model\Method\Cards - - - 0 - 0 - - Unzer\PAPI\Model\InstantPurchase\CreditCard\AvailabilityChecker - Unzer\PAPI\Model\InstantPurchase\CreditCard\TokenFormatter - - UnzerCardsVault - <![CDATA[Stored Credit Card / Debit Card]]> - - - 0 - authorize_capture - authorize_capture - <![CDATA[SEPA Direct Debit]]> - 0 - 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 - authorize_capture - <![CDATA[iDEAL]]> - 0 - 0 - 0 - NL - EUR - 0 - 1 - 1 - 1 - 0 - Unzer\PAPI\Model\Method\Ideal - 0 order @@ -166,11 +89,29 @@ processing Unzer\PAPI\Model\Method\PaylaterDirectDebit - + + 0 + order + authorize_capture + <![CDATA[Direct Bank Transfer]]> + 0 + 0 + 0 + DE + EUR + 0 + 1 + 1 + 1 + 1 + 0 + Unzer\PAPI\Model\Method\OpenBanking + + 0 order authorize - <![CDATA[PayPal]]> + <![CDATA[Credit Card / Debit Card]]> 0 0 0 @@ -183,92 +124,99 @@ 1 1 0 - 1 - Unzer\PAPI\Model\Method\Paypal - - + 0 + Unzer\PAPI\Model\Method\Cards + + 0 0 - Unzer\PAPI\Model\InstantPurchase\PayPal\AvailabilityChecker - Unzer\PAPI\Model\InstantPurchase\PayPal\TokenFormatter + Unzer\PAPI\Model\InstantPurchase\CreditCard\AvailabilityChecker + Unzer\PAPI\Model\InstantPurchase\CreditCard\TokenFormatter - UnzerPaypalVault - <![CDATA[Stored Paypal Account]]> - - + UnzerCardsVault + <![CDATA[Stored Credit Card / Debit Card]]> + + 0 order authorize_capture - <![CDATA[EPS]]> + <![CDATA[Apple Pay]]> 0 0 0 - 0 + 1 1 1 + 1 1 0 - AT - EUR - Unzer\PAPI\Model\Method\EPS - - + Unzer\PAPI\Model\Method\ApplepayV2 + + 0 order authorize_capture - <![CDATA[Alipay]]> + <![CDATA[Google Pay]]> 0 0 0 - 0 + 1 1 + 1 1 + 1 1 0 - DE,AT,BE,IT,ES,NL - AUD,CAD,CHF,CNY,EUR,GBP,HKD,NZD,SGD,USD - Unzer\PAPI\Model\Method\Alipay - - + Unzer\PAPI\Model\Method\Googlepay + 12345678901234567890 + Example Merchant + DK + default + fill + 2 + + 0 order - authorize_capture - <![CDATA[WeChat Pay]]> + authorize + <![CDATA[Wero]]> 0 0 0 - 0 + EUR + DE + 1 1 + 1 1 + 1 1 0 - AT,BE,DK,FI,FR,DE,ES,GB,GR,HU,IE,IS,IT,LI,LU,MT,NL,NO,PT,SE - CHF,CNY,EUR,GBP,USD - Unzer\PAPI\Model\Method\Wechatpay - - + Unzer\PAPI\Model\Method\Wero + + 0 order - authorize_capture - <![CDATA[Przelewy24]]> + authorize + <![CDATA[Klarna]]> 0 0 0 - 0 + 1 1 + 1 1 + 1 1 0 - PL - PLN,EUR - Unzer\PAPI\Model\Method\Przelewy24 - - + Unzer\PAPI\Model\Method\Klarna + + 0 order authorize_capture - <![CDATA[Bancontact]]> + <![CDATA[EPS]]> 0 0 0 @@ -277,138 +225,190 @@ 1 1 0 - BE + AT EUR - Unzer\PAPI\Model\Method\Bancontact - - + Unzer\PAPI\Model\Method\EPS + + 0 order authorize_capture - <![CDATA[Unzer Prepayment]]> + <![CDATA[iDEAL]]> 0 0 0 + NL + EUR 0 1 1 - 1 1 0 - 1 - EUR - Unzer\PAPI\Model\Method\Prepayment - - + Unzer\PAPI\Model\Method\Ideal + + 0 order authorize_capture - <![CDATA[Apple Pay]]> + <![CDATA[Przelewy24]]> 0 0 0 - 1 + 0 1 1 - 1 1 0 - Unzer\PAPI\Model\Method\ApplepayV2 - - + PL + PLN,EUR + Unzer\PAPI\Model\Method\Przelewy24 + + 0 order authorize_capture - <![CDATA[Google Pay]]> + <![CDATA[TWINT]]> 0 0 0 - 1 + 0 1 - 1 1 - 1 1 0 - Unzer\PAPI\Model\Method\Googlepay - 12345678901234567890 - Example Merchant - DK - default - fill - 2 - - + CH + CHF + Unzer\PAPI\Model\Method\Twint + + + 0 + authorize_capture + authorize_capture + <![CDATA[SEPA Direct Debit]]> + 0 + 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 authorize_capture - <![CDATA[TWINT]]> + <![CDATA[Unzer Prepayment]]> 0 0 0 0 1 1 + 1 1 0 - CH - CHF - Unzer\PAPI\Model\Method\Twint - - + 1 + EUR + Unzer\PAPI\Model\Method\Prepayment + + 0 order authorize_capture - <![CDATA[Direct Bank Transfer]]> + <![CDATA[Bancontact]]> 0 0 0 - DE - EUR 0 1 1 - 1 1 0 - Unzer\PAPI\Model\Method\OpenBanking - - + BE + EUR + Unzer\PAPI\Model\Method\Bancontact + + 0 order authorize - <![CDATA[Klarna]]> + <![CDATA[PayPal]]> 0 0 0 1 + 1 + 1 1 1 1 1 1 0 - Unzer\PAPI\Model\Method\Klarna - - + 1 + Unzer\PAPI\Model\Method\Paypal + + + 0 + 0 + + Unzer\PAPI\Model\InstantPurchase\PayPal\AvailabilityChecker + Unzer\PAPI\Model\InstantPurchase\PayPal\TokenFormatter + + UnzerPaypalVault + <![CDATA[Stored Paypal Account]]> + + 0 order - authorize - <![CDATA[Wero]]> + authorize_capture + <![CDATA[Alipay]]> 0 0 0 - EUR - DE - 1 + 0 1 - 1 1 - 1 1 0 - Unzer\PAPI\Model\Method\Wero - + DE,AT,BE,IT,ES,NL + AUD,CAD,CHF,CNY,EUR,GBP,HKD,NZD,SGD,USD + Unzer\PAPI\Model\Method\Alipay + + + 0 + order + authorize_capture + <![CDATA[WeChat Pay]]> + 0 + 0 + 0 + 0 + 1 + 1 + 1 + 0 + AT,BE,DK,FI,FR,DE,ES,GB,GR,HU,IE,IS,IT,LI,LU,MT,NL,NO,PT,SE + CHF,CNY,EUR,GBP,USD + Unzer\PAPI\Model\Method\Wechatpay + From 38f6c26ab8a57382eb606745712f95ade18d0633 Mon Sep 17 00:00:00 2001 From: Kristina Date: Wed, 21 Jan 2026 16:29:13 +0100 Subject: [PATCH 04/11] Ensure existing customer is reused in setCustomerData ISSUE: UPP-325 --- Model/Config/Provider.php | 65 ++++++++++++- Model/Method/PaylaterInvoice.php | 2 +- Model/Method/PaylaterInvoiceB2b.php | 2 +- .../js/view/payment/method-renderer/basev2.js | 97 ++++++++++--------- 4 files changed, 115 insertions(+), 51 deletions(-) diff --git a/Model/Config/Provider.php b/Model/Config/Provider.php index d7ec8f9..f3d4fcf 100644 --- a/Model/Config/Provider.php +++ b/Model/Config/Provider.php @@ -3,13 +3,16 @@ namespace Unzer\PAPI\Model\Config; +use Magento\Checkout\Model\Session; use Magento\Framework\App\Config\ScopeConfigInterface; +use Magento\Quote\Model\Quote; use Magento\Vault\Model\VaultPaymentInterface; use Unzer\PAPI\Model\Config; use Unzer\PAPI\Model\Method\Base as MethodBase; use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Framework\Exception\LocalizedException; use Magento\Payment\Helper\Data as PaymentHelper; +use UnzerSDK\Resources\Customer; /** * JavaScript configuration provider @@ -59,20 +62,29 @@ class Provider implements ConfigProviderInterface */ private ScopeConfigInterface $scopeConfig; + /** + * @var Session + */ + private Session $checkoutSession; + + /** * Provider constructor. * @param Config $moduleConfig * @param PaymentHelper $paymentHelper * @param ScopeConfigInterface $scopeConfig + * @param Session $checkoutSession */ public function __construct( Config $moduleConfig, PaymentHelper $paymentHelper, - ScopeConfigInterface $scopeConfig + ScopeConfigInterface $scopeConfig, + Session $checkoutSession ) { $this->_moduleConfig = $moduleConfig; $this->_paymentHelper = $paymentHelper; $this->scopeConfig = $scopeConfig; + $this->checkoutSession = $checkoutSession; } /** @@ -83,6 +95,12 @@ public function __construct( */ public function getConfig(): array { + /** @var Quote $quote */ + $quote = $this->checkoutSession->getQuote(); + + /** @var Customer $baseCustomer */ + $baseCustomer = $quote ? $this->fetchUnzerCustomer($quote) : null; + $methodConfigs = [ Config::METHOD_BASE => [ 'publicKey' => $this->_moduleConfig->getPublicKey(), @@ -93,17 +111,56 @@ public function getConfig(): array foreach ($this->_methodCodes as $methodCode) { /** @var MethodBase $model */ $model = $this->_paymentHelper->getMethodInstance($methodCode); - if ($model instanceof VaultPaymentInterface) { + if ($model instanceof VaultPaymentInterface || !$model->isAvailable()) { continue; } - if ($model->isAvailable()) { - $methodConfigs[$model->getCode()] = $model->getFrontendConfig(); + $methodConfig = $model->getFrontendConfig(); + + if (!$model->hasMethodValidOverrideKeys()) { + if ($baseCustomer) { + $methodConfig['unzerCustomerId'] = $baseCustomer->getId(); + } + + $methodConfigs[$model->getCode()] = $methodConfig; + continue; + } + + $overrideCustomer = $this->fetchUnzerCustomer($quote, $model); + + if ($overrideCustomer) { + $methodConfig['unzerCustomerId'] = $overrideCustomer->getId(); } + + $methodConfigs[$model->getCode()] = $methodConfig; } return [ 'payment' => array_filter($methodConfigs), ]; } + + /** + * @param Quote $quote + * @param MethodBase|null $method + * + * @return Customer|null + */ + private function fetchUnzerCustomer(Quote $quote, ?MethodBase $method = null): ?Customer + { + if ($quote->getCustomerIsGuest() || !$quote->getCustomerId()) { + return null; + } + + try { + $client = $this->_moduleConfig->getUnzerClient( + $quote->getStore()->getCode(), + $method + ); + + return $client->fetchCustomerByExtCustomerId((string)$quote->getCustomerId()); + } catch (\Exception $e) { + return null; + } + } } diff --git a/Model/Method/PaylaterInvoice.php b/Model/Method/PaylaterInvoice.php index 52097ed..8fe93c2 100644 --- a/Model/Method/PaylaterInvoice.php +++ b/Model/Method/PaylaterInvoice.php @@ -7,7 +7,7 @@ * * @link https://docs.unzer.com/ */ -class PaylaterInvoice extends Invoice implements OverrideApiCredentialInterface +class PaylaterInvoice extends Base implements OverrideApiCredentialInterface { /** * @inheritDoc diff --git a/Model/Method/PaylaterInvoiceB2b.php b/Model/Method/PaylaterInvoiceB2b.php index 6355a62..2b752d0 100644 --- a/Model/Method/PaylaterInvoiceB2b.php +++ b/Model/Method/PaylaterInvoiceB2b.php @@ -7,7 +7,7 @@ * * @link https://docs.unzer.com/ */ -class PaylaterInvoiceB2b extends Invoice implements OverrideApiCredentialInterface +class PaylaterInvoiceB2b extends Base implements OverrideApiCredentialInterface { /** * @inheritDoc 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 e3d4be1..49ed373 100644 --- a/view/frontend/web/js/view/payment/method-renderer/basev2.js +++ b/view/frontend/web/js/view/payment/method-renderer/basev2.js @@ -167,10 +167,7 @@ define( return overridePublicKey; } - return ( - window.checkoutConfig.payment[this.getCode()]?.publicKey || - window.checkoutConfig.payment.unzer?.publicKey - ); + return window.checkoutConfig.payment.unzer.publicKey; }, /** @@ -250,50 +247,60 @@ define( const unzerCheckoutElementId = 'unzer-payment-' + this.getCode(); const unzerPayment = document.getElementById(unzerCheckoutElementId); - if (unzerPayment && typeof unzerPayment.setBasketData === 'function') { - unzerPayment.setBasketData({ - amount: (quote.totals() ? quote.totals() : quote)['base_grand_total'], - currencyType: (quote.totals() ? quote.totals() : quote)['base_currency_code'] - }) + if (!unzerPayment || typeof unzerPayment.setBasketData !== 'function') { + if (maxRetries > 0) { + setTimeout(() => this.waitForSetBasketData(maxRetries - 1, interval), interval); + } else { + console.error('setBasketData is not available after multiple retries.'); + } + return; + } - const billing = quote.billingAddress(); - const shipping = quote.shippingAddress(); - - const customer = { - firstname: billing ? billing.firstname : '', - lastname: billing ? billing.lastname : '', - email: quote.guestEmail ? quote.guestEmail : (window.customerData ? window.customerData.email : ''), - ...(customerData?.dob ? {birthDate: customerData.dob.split('T')[0]} : {}), - billingAddress: billing ? { - name: (billing.firstname || '') + ' ' + (billing.lastname || ''), - street: Array.isArray(billing.street) ? billing.street.join(' ') : billing.street, - zip: billing.postcode, - city: billing.city, - country: billing.countryId - } : {}, - - shippingAddress: shipping ? { - name: (shipping.firstname || '') + ' ' + (shipping.lastname || ''), - street: Array.isArray(shipping.street) ? shipping.street.join(' ') : shipping.street, - 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.setBasketData({ + amount: (quote.totals() ? quote.totals() : quote)['base_grand_total'], + currencyType: (quote.totals() ? quote.totals() : quote)['base_currency_code'] + }) - unzerPayment.setCustomerData(customer); - } else if (maxRetries > 0) { - console.log('Waiting for setBasketData function to be available...'); - setTimeout(() => this.waitForSetBasketData(maxRetries - 1, interval), interval); - } else { - console.error('setBasketData is not available after multiple retries.'); + const methodConfig = window.checkoutConfig.payment[this.getCode()] || {}; + const unzerCustomerId = methodConfig.unzerCustomerId || null; + + const billing = quote.billingAddress(); + const shipping = quote.shippingAddress(); + + if (unzerCustomerId) { + this.customer = unzerCustomerId; } + + const customer = { + id: unzerCustomerId || '', + customerId: billing?.customerId || '', + firstname: billing ? billing.firstname : '', + lastname: billing ? billing.lastname : '', + email: quote.guestEmail ? quote.guestEmail : (window.customerData ? window.customerData.email : ''), + ...(customerData?.dob ? {birthDate: customerData.dob.split('T')[0]} : {}), + billingAddress: billing ? { + name: (billing.firstname || '') + ' ' + (billing.lastname || ''), + street: Array.isArray(billing.street) ? billing.street.join(' ') : billing.street, + zip: billing.postcode, + city: billing.city, + country: billing.countryId + } : {}, + + shippingAddress: shipping ? { + name: (shipping.firstname || '') + ' ' + (shipping.lastname || ''), + street: Array.isArray(shipping.street) ? shipping.street.join(' ') : shipping.street, + 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); }, getPlaceOrderDeferredObject: function () { From 3e7026b4394579c22706dbbc8b3b4bd995de0856 Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 22 Jan 2026 11:14:10 +0100 Subject: [PATCH 05/11] Set booking mode for Wero to charge ISSUE: UPP-391 --- .../Data/UpdateWeroOrderPaymentAction.php | 57 +++++++++++++++++++ etc/adminhtml/system/wero.xml | 6 -- 2 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 Setup/Patch/Data/UpdateWeroOrderPaymentAction.php diff --git a/Setup/Patch/Data/UpdateWeroOrderPaymentAction.php b/Setup/Patch/Data/UpdateWeroOrderPaymentAction.php new file mode 100644 index 0000000..038ce4c --- /dev/null +++ b/Setup/Patch/Data/UpdateWeroOrderPaymentAction.php @@ -0,0 +1,57 @@ +moduleDataSetup = $moduleDataSetup; + } + + /** + * @return $this|UpdateWeroOrderPaymentAction + */ + public function apply(): UpdateWeroOrderPaymentAction + { + $connection = $this->moduleDataSetup->getConnection(); + + $connection->update( + $this->moduleDataSetup->getTable('core_config_data'), + ['value' => 'authorize_capture'], + ['path = ?' => self::CONFIG_PATH] + ); + + return $this; + } + + /** + * @return array|string[] + */ + public static function getDependencies(): array + { + return []; + } + + /** + * @return array|string[] + */ + public function getAliases(): array + { + return []; + } +} diff --git a/etc/adminhtml/system/wero.xml b/etc/adminhtml/system/wero.xml index 278ebb0..91e3ea8 100644 --- a/etc/adminhtml/system/wero.xml +++ b/etc/adminhtml/system/wero.xml @@ -11,12 +11,6 @@ payment/unzer_wero/title - - - payment/unzer_wero/order_payment_action - Unzer\PAPI\Model\System\Config\Source\PaymentAction - payment/unzer_wero/min_order_total From b21bf71d049699767e97150d53f85633af5b755b Mon Sep 17 00:00:00 2001 From: Boljanovic Date: Fri, 23 Jan 2026 15:39:44 +0100 Subject: [PATCH 06/11] Delete obsolete class --- Model/Method/Invoice.php | 113 --------------------------------------- 1 file changed, 113 deletions(-) delete mode 100644 Model/Method/Invoice.php diff --git a/Model/Method/Invoice.php b/Model/Method/Invoice.php deleted file mode 100644 index b92c496..0000000 --- a/Model/Method/Invoice.php +++ /dev/null @@ -1,113 +0,0 @@ -_priceCurrency = $priceCurrency; - } - - /** - * Calculate Remaining Amount - * - * @param Payment $payment - * @return float - * @throws UnzerApiException - */ - protected function calculateRemainingAmount(Payment $payment): float - { - $charges = $payment->getCharges(); - $initialTransaction = $payment->getInitialTransaction(); - - if ($initialTransaction instanceof Authorization && count($charges) === 0) { - return $initialTransaction->getAmount(); - } - - $chargedAmount = 0; - foreach ($charges as $charge) { - /** @var Charge $charge */ - if ($charge->isSuccess()) { - $chargedAmount += (float)$charge->getAmount(); - } - } - - return $payment->getAmount()->getTotal() - $charges[0]->getCancelledAmount() - $chargedAmount; - } -} From 3760be96e37cdc0f87f214a90abcbf14f70ccb25 Mon Sep 17 00:00:00 2001 From: Kristina Date: Mon, 26 Jan 2026 12:35:50 +0100 Subject: [PATCH 07/11] Fix ApplePay payment request ISSUE: UPP-365 --- Model/Method/ApplepayV2.php | 2 +- .../web/js/view/payment/method-renderer/applepayv2.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Model/Method/ApplepayV2.php b/Model/Method/ApplepayV2.php index 4dacc70..90fe48c 100644 --- a/Model/Method/ApplepayV2.php +++ b/Model/Method/ApplepayV2.php @@ -21,7 +21,7 @@ public function getFrontendConfig(): array return [ 'supportedNetworks' => $supportedNetworks, 'merchantCapabilities' => ['supports3DS'], - 'label' => $this->_scopeConfig->getValue('payment/unzer_applepayv2/display_name') //label + 'label' => 'Unzer GmbH' ]; } } diff --git a/view/frontend/web/js/view/payment/method-renderer/applepayv2.js b/view/frontend/web/js/view/payment/method-renderer/applepayv2.js index 946283d..950f6d0 100644 --- a/view/frontend/web/js/view/payment/method-renderer/applepayv2.js +++ b/view/frontend/web/js/view/payment/method-renderer/applepayv2.js @@ -55,14 +55,12 @@ define( if (unzerPaymentElement && typeof unzerPaymentElement.setApplePayData === 'function') { const supportedNetworks = window.checkoutConfig.payment.unzer_applepayv2.supportedNetworks.map((network) => network.toLowerCase()); - + const billing = quote.billingAddress && quote.billingAddress(); const totals = quote.totals() ? quote.totals() : window.checkoutConfig.quoteData; unzerPaymentElement.setApplePayData({ - countryCode: quote.billingAddress().countryId, + countryCode: billing?.countryId, currencyCode: window.checkoutConfig.quoteData.base_currency_code, - totalLabel: window.checkoutConfig.payment.unzer_applepayv2.label, - totalAmount: Number(totals['base_grand_total']).toFixed(2), supportedNetworks: supportedNetworks, merchantCapabilities: window.checkoutConfig.payment.unzer_applepayv2.merchantCapabilities, requiredShippingContactFields: [], From 77a68ac2deddae7c26365899d300da11e5459b1a Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 29 Jan 2026 17:19:36 +0100 Subject: [PATCH 08/11] Fix table alignment ISSUE: UPP-374 --- .../customer_account/credit_card.phtml | 50 ++++++++++--------- .../customer_account/direct_debit.phtml | 43 ++++++++-------- .../templates/customer_account/paypal.phtml | 32 ++++++------ view/frontend/templates/token_list.phtml | 39 ++++++++++++--- 4 files changed, 97 insertions(+), 67 deletions(-) diff --git a/view/frontend/templates/customer_account/credit_card.phtml b/view/frontend/templates/customer_account/credit_card.phtml index b2d486d..d7c0daf 100644 --- a/view/frontend/templates/customer_account/credit_card.phtml +++ b/view/frontend/templates/customer_account/credit_card.phtml @@ -9,38 +9,42 @@ use Magento\Vault\Block\CardRendererInterface; $ccNumberView = $block->escapeHtml($block->getNumberLast4Digits()); ?> - - - + + + - + + escapeHtml($block->getExpDate()) ?> - + + escapeHtml($block->getBrand()) ?> - -
+ + + getBlockHtml('formkey') ?> - + + +
diff --git a/view/frontend/templates/customer_account/direct_debit.phtml b/view/frontend/templates/customer_account/direct_debit.phtml index 402c5a7..d5a846e 100644 --- a/view/frontend/templates/customer_account/direct_debit.phtml +++ b/view/frontend/templates/customer_account/direct_debit.phtml @@ -2,37 +2,37 @@ declare(strict_types=1); use Magento\Framework\View\Element\Template; -use Unzer\PAPI\Block\Customer\DirectDebitRenderer; use Magento\Vault\Api\Data\PaymentTokenInterface; +use Unzer\PAPI\Block\Customer\DirectDebitRenderer; /** @var Template|DirectDebitRenderer $block */ -$maskedIban = (string)$block->escapeHtml($block->getMaskedIban()); -$holder = (string)$block->escapeHtml($block->getAccountHolder()); -?> - - - +$maskedIban = $block->escapeHtml($block->getMaskedIban()); +$holder = $block->escapeHtml($block->getAccountHolder()); +?> + + + - - + + + - + + escapeHtml(__('SEPA Direct Debit')) ?> - + +
getBlockHtml('formkey') ?> - + + +