Commit 8b8be0f2 authored by Vincent Mrose's avatar Vincent Mrose 💬
Browse files

Merge branch 'develop' into 'master'

Develop

Closes #9 and #8

See merge request !4
parents d9db271a 42f75d7d
......@@ -7,6 +7,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
## [0.9.4] - 2019-05-28
### Added
- Added a setting to do an automatic capture based on categories
### Fixed
- Error messages in the frontend no longer default to an empty string in some cases
- Translations for all the translatable module strings were added
- Help texts for the module configuration were added
- Changed the initialization of the Javascript Library to always happen
- Changing the shipping address no longer causes a frontend error (#8)
- AutoCapture per payment method now works with a 0 amount in the notification (#9)
- AutoCapture category setting is now saved under the right config path
## [0.9.3-mnshandling] - 2019-05-02
### Fixed
- The MNS handling no longer throws an error when an order was paid with CrefoPay
......
<?php
namespace Trilix\CrefoPay\Block\Adminhtml\System\Config\Form\Field;
use Magento\Ui\Model\UiComponentGenerator;
use Magento\Backend\Block\Template\Context;
class Categories extends \Magento\Backend\Block\Template
implements \Magento\Framework\Data\Form\Element\Renderer\RendererInterface
{
private $context;
/** @var UiComponentGenerator */
private $uiComponentGenerator;
/**
* {@inheritdoc}
*/
public function __construct(Context $context, UiComponentGenerator $uiComponentGenerator, array $data = [])
{
parent::__construct($context, $data);
$this->uiComponentGenerator = $uiComponentGenerator;
$this->context = $context;
}
/**
* Get the UI component HTML as a form field.
*
* @param \Magento\Framework\Data\Form\Element\AbstractElement $element
*
* @return string
*/
public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element)
{
$uiComponent = $this->uiComponentGenerator->generateUiComponent('category_multiselect', $this->_layout);
$html = $uiComponent->render();
return $html;
}
}
\ No newline at end of file
......@@ -132,7 +132,7 @@ class CreateTransactionRequestFactory extends AbstractRequestFactory
$createTransactionRequest->setBillingAddress($this->addressBuilder->build($billingAddress));
$createTransactionRequest->setShippingAddress($this->addressBuilder->build($shippingAddress));
$createTransactionRequest->setAmount($this->amountBuilder->build($quote));
$createTransactionRequest->setAmount($this->amountBuilder->buildFromQuote($quote));
$this->basketBuilder->build($quote, $createTransactionRequest);
......
......@@ -4,10 +4,31 @@ namespace Trilix\CrefoPay\Client\Request;
use Magento\Payment\Gateway\Data\PaymentDataObjectInterface;
use Upg\Library\Request\Reserve as ReserveRequest;
use Trilix\CrefoPay\Client\ConfigFactory;
use Trilix\CrefoPay\Gateway\Request\AmountBuilder;
use Trilix\CrefoPay\Client\Request\Structure\AdditionalInfo as AdditionalInfoStructure;
class ReserveRequestFactory extends AbstractRequestFactory
{
/** @var AmountBuilder */
private $amountBuilder;
/**
* ReserveRequestFactory constructor.
* @param ConfigFactory $configFactory
* @param AmountBuilder $amountBuilder
*/
public function __construct(
ConfigFactory $configFactory,
AmountBuilder $amountBuilder
) {
parent::__construct($configFactory);
$this->amountBuilder = $amountBuilder;
}
public function create(string $paymentMethod, PaymentDataObjectInterface $paymentDO): ReserveRequest
{
$reserveRequest = new ReserveRequest($this->getConfig());
......@@ -26,6 +47,8 @@ class ReserveRequestFactory extends AbstractRequestFactory
$reserveRequest->setAdditionalInformation((string)$additionalInfoStruc);
}
$reserveRequest->setAmount($this->amountBuilder->buildFromOrder($paymentDO->getOrder()));
$this->setMac($reserveRequest);
return $reserveRequest;
......
<?php
namespace Trilix\CrefoPay\Controller\Adminhtml\Autocapture;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Trilix\CrefoPay\Gateway\Config\Config;
class Save extends \Magento\Backend\App\Action
{
/**
* @var Config
*/
private $config;
/**
* Save constructor.
*
* @param Config $config
* @param Context $context
*/
public function __construct(Config $config, Context $context)
{
parent::__construct($context);
$this->config = $config;
}
/**
* Save auto capture categories.
*
* @return \Magento\Framework\Controller\Result\Raw
*/
public function execute()
{
/** @var \Magento\Framework\Controller\Result\Raw $result */
$result = $this->resultFactory->create(ResultFactory::TYPE_RAW);
$categories = $this->getRequest()->getParam('categories');
$scope = $this->getRequest()->getParam('scope');
if (!is_array($categories)) {
return $result;
}
$configScope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT;
$configScopeId = 0;
if (is_array($scope) && array_key_exists('website', $scope) && $scope['website']) {
$configScope = 'websites';
$configScopeId = $scope['website'];
}
$this->config->setAutoCaptureCategoryIds($categories, $configScope, $configScopeId);
return $result;
}
}
\ No newline at end of file
......@@ -2,7 +2,9 @@
namespace Trilix\CrefoPay\Gateway\Config;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Storage\WriterInterface as ConfigWriter;
use Magento\Payment\Model\CcConfig;
use Trilix\CrefoPay\Model\Adminhtml\Source\Environment;
......@@ -16,6 +18,17 @@ class Config extends \Magento\Payment\Gateway\Config\Config
const KEY_PUBLIC_TOKEN = 'public_token';
const KEY_IS_B2B_ENABLED = 'is_b2b_enabled';
const KEY_RISK_CLASS = 'risk_class';
const KEY_AUTO_CAPTURE_CATEGORIES = 'auto_capture_categories';
/**
* @var string|null
*/
private $methodCode;
/**
* @var string|null
*/
private $pathPattern;
/**
* @var CcConfig
......@@ -33,21 +46,38 @@ class Config extends \Magento\Payment\Gateway\Config\Config
private $secureFieldsUrl;
/**
* Initialize dependencies.
* @var CategoryCollectionFactory
*/
private $categoryCollectionFactory;
/**
* @var ConfigWriter
*/
private $configWriter;
/**
* Config constructor.
*
* @param ScopeConfigInterface $scopeConfig
* @param CcConfig $ccConfig
* @param null $methodCode
* @param string $pathPattern
* @param ScopeConfigInterface $scopeConfig
* @param CcConfig $ccConfig
* @param CategoryCollectionFactory $categoryCollectionFactory
* @param ConfigWriter $configWriter
* @param null $methodCode
* @param string $pathPattern
*/
public function __construct(
ScopeConfigInterface $scopeConfig,
CcConfig $ccConfig,
CategoryCollectionFactory $categoryCollectionFactory,
ConfigWriter $configWriter,
$methodCode = null,
$pathPattern = self::DEFAULT_PATH_PATTERN
) {
parent::__construct($scopeConfig, $methodCode, $pathPattern);
$this->ccConfig = $ccConfig;
$this->categoryCollectionFactory = $categoryCollectionFactory;
$this->configWriter = $configWriter;
$this->methodCode = $methodCode;
$this->pathPattern = $pathPattern;
}
/**
......@@ -155,4 +185,65 @@ class Config extends \Magento\Payment\Gateway\Config\Config
return $this->baseUrl;
}
/**
* Save auto capture category IDs.
*
* @param array $categoryIds
* @param string $scope
* @param int $scopeId
*
* @return void
*/
public function setAutoCaptureCategoryIds(
array $categoryIds,
$scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
$scopeId = 0
) {
$this->setValue(self::KEY_AUTO_CAPTURE_CATEGORIES, join(',', $categoryIds), $scope, $scopeId);
}
/**
* Return auto capture category IDs as plain array.
*
* @return array
*/
public function getAutoCaptureCategoryIds(): array
{
return array_filter(explode(',', $this->getValue(self::KEY_AUTO_CAPTURE_CATEGORIES)));
}
/**
* Return auto capture category collection.
*
* @return \Magento\Catalog\Model\ResourceModel\Category\Collection
*/
public function getAutoCaptureCategories(): \Magento\Catalog\Model\ResourceModel\Category\Collection
{
$categories = $this->categoryCollectionFactory->create();
$categories->addIdFilter($this->getAutoCaptureCategoryIds());
return $categories;
}
/**
* Save config value.
*
* @param string $field
* @param $value
* @param string $scope
* @param int $scopeId
*
* @return void
*/
protected function setValue(
string $field,
$value,
string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
int $scopeId = 0
) {
$this->configWriter->save(
sprintf($this->pathPattern, $this->methodCode, $field), $value, $scope, $scopeId
);
}
}
......@@ -3,6 +3,7 @@
namespace Trilix\CrefoPay\Gateway\Request;
use Magento\Quote\Model\Quote;
use Magento\Payment\Gateway\Data\OrderAdapterInterface;
use Upg\Library\Request\Objects\Amount;
class AmountBuilder
......@@ -11,11 +12,23 @@ class AmountBuilder
* @param Quote $quote
* @return Amount
*/
public function build(Quote $quote): Amount
public function buildFromQuote(Quote $quote): Amount
{
$amount = new Amount();
$amount->setAmount(ceil($quote->getGrandTotal() * 100));
return $amount;
}
/**
* @param OrderAdapterInterface $order
* @return Amount
*/
public function buildFromOrder(OrderAdapterInterface $order): Amount
{
$amount = new Amount();
$amount->setAmount(ceil($order->getGrandTotalAmount() * 100));
return $amount;
}
}
......@@ -5,6 +5,7 @@ use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Exception\LocalizedException;
use Magento\Sales\Model\Order as SalesOrder;
use Magento\Sales\Model\OrderRepository;
use Trilix\CrefoPay\Gateway\Config\Config as GatewayConfig;
class Order
{
......@@ -18,16 +19,26 @@ class Order
*/
private $searchCriteriaBuilder;
/**
* @var GatewayConfig
*/
private $gatewayConfig;
/**
* Order constructor.
*
* @param OrderRepository $orderRepository
* @param OrderRepository $orderRepository
* @param SearchCriteriaBuilder $searchCriteriaBuilder
* @param GatewayConfig $gatewayConfig
*/
public function __construct(OrderRepository $orderRepository, SearchCriteriaBuilder $searchCriteriaBuilder)
{
public function __construct(
OrderRepository $orderRepository,
SearchCriteriaBuilder $searchCriteriaBuilder,
GatewayConfig $gatewayConfig
) {
$this->orderRepository = $orderRepository;
$this->searchCriteriaBuilder = $searchCriteriaBuilder;
$this->gatewayConfig = $gatewayConfig;
}
/**
......@@ -69,4 +80,49 @@ class Order
{
return $this->orderRepository;
}
/**
* Checks whether at least one product from the order is assigned to the allowed categories for auto capture.
* And whether payment method allowed to perform auto capture.
*
* @param SalesOrder $order
*
* @return bool
* @throws LocalizedException
*/
public function isEligibleForAutoCapture(SalesOrder $order): bool
{
/** @var \Magento\Sales\Model\Order\Payment $payment */
$payment = $order->getPayment();
if (!$payment->canCapture()) {
return false;
}
/** @var \Trilix\CrefoPay\Model\Method\Adapter $methodInstance */
$methodInstance = $payment->getMethodInstance();
if (!$methodInstance->isAutoCaptureEnabled()) {
return false;
}
$allowedCategoryIds = array_map('intval', $this->gatewayConfig->getAutoCaptureCategoryIds());
if (empty($allowedCategoryIds)) {
return false;
}
/** @var \Magento\Sales\Model\Order\Item $orderItem */
foreach ($order->getItemsCollection() as $orderItem) {
$productCategoryIds = array_map('intval', $orderItem->getProduct()->getCategoryIds());
foreach ($productCategoryIds as $categoryId) {
if (in_array($categoryId, $allowedCategoryIds)) {
return true;
}
}
}
return false;
}
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ class Adapter extends \Magento\Payment\Model\Method\Adapter
*
* @return bool
*/
public function isAutoCapture(): bool
public function isAutoCaptureEnabled(): bool
{
return (bool)$this->getConfigData('auto_capture');
}
......
......@@ -48,7 +48,7 @@ class MerchantPending extends AbstractConsumer implements MnsConsumerInterface
* @param MnsEvent $event
*
* @throws \Magento\Framework\Exception\LocalizedException
* @throws \Trilix\CrefoPay\Mns\Consumers\MnsConsumerException
* @throws \Trilix\CrefoPay\Model\Mns\Consumers\MnsConsumerException
*/
public function process(MnsEvent $event)
{
......@@ -62,13 +62,15 @@ class MerchantPending extends AbstractConsumer implements MnsConsumerInterface
$order->setState(Order::STATE_PROCESSING);
$order->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING));
if ($methodInstance->isAutoCapture() && $payment->canCapture()) {
$methodInstance->capture($payment, $event->getAmount());
$payment->registerCaptureNotification($event->getAmount());
$amount = $this->getAmount($order, $event);
if ($this->orderHelper->isEligibleForAutoCapture($order)) {
$methodInstance->capture($payment, $amount);
$payment->registerCaptureNotification($amount);
} else if ($payment->getMethod() === CIA::CODE) {
$this->addCommentToStatusHistory(
$order,
__('Incoming payment: %1', $this->getFormattedAmount($event->getAmount(), $event->getCurrency()))
__('Incoming payment: %1', $this->getFormattedAmount($amount, $event->getCurrency()))
);
}
......@@ -85,4 +87,22 @@ class MerchantPending extends AbstractConsumer implements MnsConsumerInterface
$currency
);
}
/**
* Sometimes CrefoPay returns '0.00' as amount up for capture. In this case we have to substitute it with the valid amount
* from the order.
*
* @param Order $order
* @param MnsEvent $event
*
* @return string
*/
private function getAmount(Order $order, MnsEvent $event): string
{
if (floatval($event->getAmount())) {
return $event->getAmount();
}
return (string)$order->getGrandTotal();
}
}
\ No newline at end of file
<?php
namespace Trilix\CrefoPay\Ui\Component\CategoryMultiselect;
use Magento\Config\Model\ResourceModel\Config\Data\Collection as ConfigCollection;
use Magento\Ui\DataProvider\AbstractDataProvider;
use Trilix\CrefoPay\Gateway\Config\Config as GatewayConfig;
class AutoCaptureDataProvider extends AbstractDataProvider
{
/**
* @var GatewayConfig
*/
private $gatewayConfig;
public function __construct(
string $name,
string $primaryFieldName,
string $requestFieldName,
GatewayConfig $gatewayConfig,
ConfigCollection $configCollection,
array $meta = [],
array $data = []
) {
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
$this->gatewayConfig = $gatewayConfig;
$this->collection = $configCollection;
}
public function getData()
{
$categoryIds = $this->gatewayConfig->getAutoCaptureCategoryIds();
return [null => $categoryIds];
}
}
\ No newline at end of file
......@@ -11,11 +11,13 @@
<frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model>
<field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Enable this Solution</label>
<tooltip>Activate the use of CrefoPay.</tooltip>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/crefopay/active</config_path>
</field>
<field id="environment" translate="label" type="select" sortOrder="21" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Environment</label>
<tooltip>You can switch between Sandbox (Testmode) and Production Mode.</tooltip>
<source_model>Trilix\CrefoPay\Model\Adminhtml\Source\Environment</source_model>
<config_path>payment/crefopay/environment</config_path>
<depends>
......@@ -24,6 +26,8 @@
</field>
<field id="merchant_id" translate="label comment" sortOrder="22" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Merchant ID</label>
<comment><![CDATA[You will get your Merchant ID from the CrefoPay <a href="mailto:service@crefopay.de">Integrations-Team</a>]]>
</comment>
<config_path>payment/crefopay/merchant_id</config_path>
<can_be_empty>0</can_be_empty>
<validate>required-entry validate-number validate-zero-or-greater</validate>
......@@ -33,6 +37,8 @@
</field>
<field id="private_key" translate="label comment" sortOrder="23" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Private Key</label>
<comment><![CDATA[You will get your individual Private Key from the CrefoPay <a href="mailto:service@crefopay.de">Integrations-Team</a>]]>
</comment>
<config_path>payment/crefopay/private_key</config_path>
<can_be_empty>0</can_be_empty>
<validate>required-entry</validate>
......@@ -42,6 +48,8 @@
</field>
<field id="store_id" translate="label comment" sortOrder="24" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Store ID</label>
<comment><![CDATA[You will get your Store ID from the CrefoPay <a href="mailto:service@crefopay.de">Integrations-Team</a>]]>
</comment>
<config_path>payment/crefopay/store_id</config_path>
<can_be_empty>0</can_be_empty>
<validate>required-entry</validate>
......@@ -51,6 +59,8 @@
</field>
<field id="public_token" translate="label comment" sortOrder="25" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Shop Public Token (JS Library)</label>
<comment><![CDATA[You will get your Shop Public Token from the CrefoPay <a href="mailto:service@crefopay.de">Integrations-Team</a>]]>
</comment>
<config_path>payment/crefopay/public_token</config_path>
<can_be_empty>0</can_be_empty>
<validate>required-entry</validate>
......@@ -60,6 +70,8 @@
</field>
<field id="is_b2b_enabled" translate="label" type="select" sortOrder="26" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Is B2B Customers Support Enabled</label>
<comment><![CDATA[ATTENTION: This functionality should be only enabled, if B2B Transaktions are defined as part of your CrefoPay Merchant-Contract.]]>
</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/crefopay/is_b2b_enabled</config_path>
<depends>
......@@ -68,6 +80,8 @@
</field>
<field id="risk_class" translate="label" type="select" sortOrder="28" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Risk Class</label>
<comment><![CDATA[Here you define your default risk. ATTENTION: The CrefoPay fraud prevention and risk checks will only perfomed on default risk class.]]>
</comment>
<source_model>Trilix\CrefoPay\Model\Adminhtml\Source\RiskClass</source_model>
<config_path>payment/crefopay/risk_class</config_path>
<depends>
......@@ -81,15 +95,18 @@
<label>Bill</label>
<field id="title" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Title</label>
<comment>Here you are able to define you individual display name.</comment>
<config_path>payment/crefopay_bill/title</config_path>
</field>
<field id="active" translate="label" type="select" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
<label>Enabled</label>
<comment>Activate the CrefoPay payment method Bill.</comment>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/crefopay_bill/active</config_path>
</field>
<field id="auto_capture" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Enable automatic capture</label>
<tooltip>Here you can activate the automatic capture for this payment mehtod.</tooltip>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>