Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions Block/Customer/DirectDebitRenderer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);

namespace Unzer\PAPI\Block\Customer;

use Magento\Framework\View\Element\Template;
use Magento\Vault\Api\Data\PaymentTokenInterface;
use Magento\Vault\Block\AbstractTokenRenderer;
use Unzer\PAPI\Model\Config;

/**
* @link https://docs.unzer.com/
*/
class DirectDebitRenderer extends AbstractTokenRenderer
{
/**
* @var Config
*/
private Config $config;

/**
* Constructor
*
* @param Template\Context $context
* @param Config $config
* @param array $data
*/
public function __construct(
Template\Context $context,
Config $config,
array $data = []
) {
parent::__construct($context, $data);
$this->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 '';
}
}
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
40 changes: 40 additions & 0 deletions Controller/Adminhtml/Order/Invoice/Cancel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace Unzer\PAPI\Controller\Adminhtml\Order\Invoice;

use Magento\Sales\Controller\Adminhtml\Invoice\AbstractInvoice\View;

class Cancel extends View
{
public const ADMIN_RESOURCE = 'Magento_Sales::invoice';

public function execute()
{
$invoice = $this->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;
}
}
4 changes: 2 additions & 2 deletions Helper/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 8 additions & 1 deletion Helper/Payment.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
class Payment
{
public const STATUS_READY_TO_CAPTURE = 'unzer_ready_to_capture';
private const METHOD_PREPAYMENT = 'unzer_prepayment';

/**
* @var InvoiceRepositoryInterface
Expand Down Expand Up @@ -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);
}

/**
Expand Down
22 changes: 18 additions & 4 deletions Model/Command/TransactionSynchronizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -64,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);
}
Expand Down Expand Up @@ -142,6 +146,8 @@ public function applyCancellationOnMagento(OrderInterface $order, UnzerPayment $
* @param UnzerPayment $unzer
*
* @return void
* @throws LocalizedException
* @throws Exception
*/
public function applyChargebackOnMagento(OrderInterface $order, UnzerPayment $unzer): void
{
Expand All @@ -158,20 +164,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);
}

Expand Down
4 changes: 2 additions & 2 deletions Model/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -53,6 +52,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
Expand Down
3 changes: 2 additions & 1 deletion Model/Config/Provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
];

/**
Expand Down
41 changes: 41 additions & 0 deletions Model/InstantPurchase/DirectDebit/AvailabilityChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);

namespace Unzer\PAPI\Model\InstantPurchase\DirectDebit;

use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\InstantPurchase\PaymentMethodIntegration\AvailabilityCheckerInterface;

/**
* @link https://docs.unzer.com/
*/
class AvailabilityChecker implements AvailabilityCheckerInterface
{
private const CONFIG_INSTANT_PURCHASE_ACTIVE = 'payment/unzer_direct_debit_vault/instant_purchase_active';

/**
* @var ScopeConfigInterface
*/
private ScopeConfigInterface $scopeConfig;

/**
* AvailabilityChecker constructor.
*
* @param ScopeConfigInterface $scopeConfig
*/
public function __construct(
ScopeConfigInterface $scopeConfig
) {
$this->scopeConfig = $scopeConfig;
}

/**
* @inheritdoc
*/
public function isAvailable(): bool
{
return (bool)$this->scopeConfig->getValue(
self::CONFIG_INSTANT_PURCHASE_ACTIVE
);
}
}
35 changes: 35 additions & 0 deletions Model/InstantPurchase/DirectDebit/TokenFormatter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
declare(strict_types=1);

namespace Unzer\PAPI\Model\InstantPurchase\DirectDebit;

use InvalidArgumentException;
use JsonException;
use Magento\InstantPurchase\PaymentMethodIntegration\PaymentTokenFormatterInterface;
use Magento\Vault\Api\Data\PaymentTokenInterface;

/**
* @link https://docs.unzer.com/
*/
class TokenFormatter implements PaymentTokenFormatterInterface
{
/**
* @inheritdoc
*
* @throws JsonException
*/
public function formatPaymentToken(PaymentTokenInterface $paymentToken): string
{
$details = json_decode($paymentToken->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']
);
}
}
9 changes: 7 additions & 2 deletions Model/Method/DirectDebit.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
/**
* 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
*/
public function getFrontendConfig(): array
{
$parentConfig = parent::getFrontendConfig();
$parentConfig['vault_code'] = $this->getVaultCode();

$merchantName = $this->getConfigData('merchant_name');

Expand All @@ -31,4 +31,9 @@ public function getFrontendConfig(): array

return $parentConfig;
}

public function getVaultCode(): ?string
{
return self::VAULT_CODE;
}
}
Loading