We will be off from 27/1 (Monday) to 31/1 (Friday) (GMT +7) for our Tet Holiday (Lunar New Year) in our country

Commit 2bbf8fd0 authored by Alexandros Moraitis's avatar Alexandros Moraitis Committed by GitHub

[PW-2858] SEPA tokenization for recurring payments (#795)

* First commit

* remove the storepayment methods from front end

* move the config to store payment methods

* request is complete

* Add the save token logic for alternative paymentmethods to Cron class

* Remove unused logger from Checkout builder class

* Remove rest od the logger

* remove more unused code

* remove commas

* remove space

* Working on adding the public_hash

* Alternative payment method is displayed in Stored Payment Methods page

* add adyen_hpp for alternative payment methods

* add the TokenHppUiProvider class

* [PW-2858] Moving HPP vault config to default Magento PM location

* [PW-2858] HPP vault virtual types

* [PW-2858] Extra configs and account constant usage

* Fix code smell 2/5

* fix suggestions

* Replace date time format

* [PW-2858] Missing virtualtypes

* [PW-2858] Extra virtualtypes and configs

* [PW-2858] Token formatter for HPP

* [PW-2858] Check if PM is adyen_hpp before saving recurring token

* [PW-2858] Using Vault helper for the expDate

* Restoring config label

* [PW-2858] Adjusting VaultDetailsHandler.php to use vault helper

* [PW-2858] Fix code smells

* Update Helper/Requests.php
Co-authored-by: default avatarMarcos Garcia <marcos.asgarcia@gmail.com>

* [PW-2858] phpcs fix

* [PW-2858] getExpirationDate made private and using Vault consts from Cron.php

* [PW-2858] Adding Date use statements

* [PW-2858] Adding cron messages

* Removing duplicated virtualtype

* [PW-2858] phpcs fix
Co-authored-by: default avatarAttila Kiss <42297201+cyattilakiss@users.noreply.github.com>
Co-authored-by: default avataracampos1916 <angel.campos@adyen.com>
Co-authored-by: default avatarMarcos Garcia <marcos.asgarcia@gmail.com>
parent 4891d651
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
namespace Adyen\Payment\Block\Customer; namespace Adyen\Payment\Block\Customer;
use Adyen\Payment\Model\Ui\AdyenCcConfigProvider; use Adyen\Payment\Model\Ui\AdyenCcConfigProvider;
use Adyen\Payment\Model\Ui\AdyenHppConfigProvider;
use Adyen\Payment\Helper\Data; use Adyen\Payment\Helper\Data;
use Magento\Framework\View\Element\Template; use Magento\Framework\View\Element\Template;
use Magento\Vault\Api\Data\PaymentTokenInterface; use Magento\Vault\Api\Data\PaymentTokenInterface;
...@@ -55,7 +56,7 @@ class CardRenderer extends AbstractCardRenderer ...@@ -55,7 +56,7 @@ class CardRenderer extends AbstractCardRenderer
*/ */
public function canRender(PaymentTokenInterface $token) public function canRender(PaymentTokenInterface $token)
{ {
return $token->getPaymentMethodCode() === AdyenCcConfigProvider::CODE; return $token->getPaymentMethodCode() === AdyenCcConfigProvider::CODE ||$token->getPaymentMethodCode() === AdyenHppConfigProvider::CODE;
} }
/** /**
...@@ -63,7 +64,7 @@ class CardRenderer extends AbstractCardRenderer ...@@ -63,7 +64,7 @@ class CardRenderer extends AbstractCardRenderer
*/ */
public function getNumberLast4Digits() public function getNumberLast4Digits()
{ {
return $this->getTokenDetails()['maskedCC']; return !empty($this->getTokenDetails()['maskedCC']) ? $this->getTokenDetails()['maskedCC'] : "";
} }
/** /**
......
...@@ -141,7 +141,18 @@ class Config ...@@ -141,7 +141,18 @@ class Config
} }
return $this->encryptor->decrypt(trim($key)); return $this->encryptor->decrypt(trim($key));
} }
/**
* Check if alternative payment methods vault is enabled
*
* @param null|int|string $storeId
* @return mixed
*/
public function isStoreAlternativePaymentMethodEnabled($storeId = null)
{
return $this->adyenHelper->getAdyenHppVaultConfigDataFlag('active', $storeId);
}
/** /**
* Retrieve information from payment configuration * Retrieve information from payment configuration
* *
......
...@@ -525,6 +525,18 @@ class Data extends AbstractHelper ...@@ -525,6 +525,18 @@ class Data extends AbstractHelper
return $this->getConfigData($field, 'adyen_hpp', $storeId, true); return $this->getConfigData($field, 'adyen_hpp', $storeId, true);
} }
/**
* Gives back adyen_hpp_vault configuration values as flag
*
* @param $field
* @param null|int|string $storeId
* @return mixed
*/
public function getAdyenHppVaultConfigDataFlag($field, $storeId = null)
{
return $this->getConfigData($field, 'adyen_hpp_vault', $storeId, true);
}
/** /**
* Gives back adyen_oneclick configuration values * Gives back adyen_oneclick configuration values
* *
...@@ -1785,6 +1797,17 @@ class Data extends AbstractHelper ...@@ -1785,6 +1797,17 @@ class Data extends AbstractHelper
return $this->getAdyenCcVaultConfigDataFlag('active', $storeId); return $this->getAdyenCcVaultConfigDataFlag('active', $storeId);
} }
/**
* Check if HPP vault is enabled
*
* @param null|int|string $storeId
* @return mixed
*/
public function isHppVaultEnabled($storeId = null)
{
return $this->getAdyenHppVaultConfigDataFlag('active', $storeId);
}
/** /**
* Checks if the house number needs to be sent to the Adyen API separately or as it is in the street field * Checks if the house number needs to be sent to the Adyen API separately or as it is in the street field
* *
......
...@@ -38,16 +38,24 @@ class Requests extends AbstractHelper ...@@ -38,16 +38,24 @@ class Requests extends AbstractHelper
* @var \Adyen\Payment\Helper\Data * @var \Adyen\Payment\Helper\Data
*/ */
private $adyenHelper; private $adyenHelper;
/**
* @var \Adyen\Payment\Helper\Config
*/
private $adyenConfig;
/** /**
* Requests constructor. * Requests constructor.
* *
* @param Data $adyenHelper * @param Data $adyenHelper
* @param Config $adyenConfig ;
*/ */
public function __construct( public function __construct(
\Adyen\Payment\Helper\Data $adyenHelper \Adyen\Payment\Helper\Data $adyenHelper,
\Adyen\Payment\Helper\Config $adyenConfig
) { ) {
$this->adyenHelper = $adyenHelper; $this->adyenHelper = $adyenHelper;
$this->adyenConfig = $adyenConfig;
} }
/** /**
...@@ -366,7 +374,10 @@ class Requests extends AbstractHelper ...@@ -366,7 +374,10 @@ class Requests extends AbstractHelper
if ($customerId > 0) { if ($customerId > 0) {
$isGuestUser = false; $isGuestUser = false;
} }
//active
if ( $this->adyenConfig->isStoreAlternativePaymentMethodEnabled($storeId)) {
$request['storePaymentMethod'] = true;
}
// If the vault feature is on this logic is handled in the VaultDataBuilder // If the vault feature is on this logic is handled in the VaultDataBuilder
if (!$this->adyenHelper->isCreditCardVaultEnabled()) { if (!$this->adyenHelper->isCreditCardVaultEnabled()) {
if ($areaCode !== \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) { if ($areaCode !== \Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) {
...@@ -375,17 +386,9 @@ class Requests extends AbstractHelper ...@@ -375,17 +386,9 @@ class Requests extends AbstractHelper
$enableOneclick = $this->adyenHelper->getAdyenAbstractConfigData('enable_oneclick', $storeId); $enableOneclick = $this->adyenHelper->getAdyenAbstractConfigData('enable_oneclick', $storeId);
$enableRecurring = $this->adyenHelper->getAdyenAbstractConfigData('enable_recurring', $storeId); $enableRecurring = $this->adyenHelper->getAdyenAbstractConfigData('enable_recurring', $storeId);
if ($enableOneclick && !$isGuestUser) {
$request['enableOneClick'] = true;
} else {
$request['enableOneClick'] = false;
}
if ($enableRecurring) { $request['enableOneClick'] = $enableOneclick && !$isGuestUser;
$request['enableRecurring'] = true; $request['enableRecurring'] = (bool)$enableRecurring;
} else {
$request['enableRecurring'] = false;
}
// value can be 0,1 or true // value can be 0,1 or true
if (!empty($additionalData[AdyenCcDataAssignObserver::STORE_CC]) || ($isGuestUser && $this->adyenHelper->isGuestTokenizationEnabled($storeId))) { if (!empty($additionalData[AdyenCcDataAssignObserver::STORE_CC]) || ($isGuestUser && $this->adyenHelper->isGuestTokenizationEnabled($storeId))) {
......
...@@ -92,11 +92,12 @@ class Vault ...@@ -92,11 +92,12 @@ class Vault
public function saveRecurringDetails($payment, array $additionalData) public function saveRecurringDetails($payment, array $additionalData)
{ {
if (!$this->adyenHelper->isCreditCardVaultEnabled($payment->getOrder()->getStoreId())) { if (!$this->adyenHelper->isCreditCardVaultEnabled($payment->getOrder()->getStoreId()) &&
!$this->adyenHelper->isHppVaultEnabled($payment->getOrder()->getStoreId())) {
return; return;
} }
if(!$this->validateAdditionalData($additionalData)) { if (!$this->validateAdditionalData($additionalData)) {
return; return;
} }
...@@ -156,11 +157,15 @@ class Vault ...@@ -156,11 +157,15 @@ class Vault
$paymentToken->setExpiresAt($this->getExpirationDate($additionalData[self::EXPIRY_DATE])); $paymentToken->setExpiresAt($this->getExpirationDate($additionalData[self::EXPIRY_DATE]));
$details = [ $details = ['type' => $additionalData[self::PAYMENT_METHOD]];
'type' => $additionalData[self::PAYMENT_METHOD],
'maskedCC' => $additionalData[self::CARD_SUMMARY], if (!empty($additionalData[self::CARD_SUMMARY])) {
'expirationDate' => $additionalData[self::EXPIRY_DATE] $details['maskedCC'] = $additionalData[self::CARD_SUMMARY];
]; }
if (!empty($additionalData[self::EXPIRY_DATE])) {
$details['expirationDate'] = $additionalData[self::EXPIRY_DATE];
}
$paymentToken->setTokenDetails(json_encode($details)); $paymentToken->setTokenDetails(json_encode($details));
...@@ -201,7 +206,7 @@ class Vault ...@@ -201,7 +206,7 @@ class Vault
$expirationDate = explode('/', $expirationDate); $expirationDate = explode('/', $expirationDate);
$expDate = new DateTime( $expDate = new DateTime(
//add leading zero to month //add leading zero to month
sprintf("%s-%02d-01 00:00:00", $expirationDate[1], $expirationDate[0]), sprintf("%s-%02d-01 00:00:00", $expirationDate[1], $expirationDate[0]),
new DateTimeZone('UTC') new DateTimeZone('UTC')
); );
......
...@@ -23,7 +23,10 @@ ...@@ -23,7 +23,10 @@
namespace Adyen\Payment\Model; namespace Adyen\Payment\Model;
use Adyen\Payment\Helper\Vault;
use Adyen\Payment\Model\Ui\AdyenHppConfigProvider;
use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Framework\Api\SearchCriteriaBuilder;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Webapi\Exception; use Magento\Framework\Webapi\Exception;
use Magento\Sales\Model\Order\Email\Sender\OrderSender; use Magento\Sales\Model\Order\Email\Sender\OrderSender;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
...@@ -32,6 +35,12 @@ use Magento\Framework\App\AreaList; ...@@ -32,6 +35,12 @@ use Magento\Framework\App\AreaList;
use Magento\Framework\Phrase\Renderer\Placeholder; use Magento\Framework\Phrase\Renderer\Placeholder;
use Magento\Framework\Phrase; use Magento\Framework\Phrase;
use Magento\Sales\Model\OrderRepository; use Magento\Sales\Model\OrderRepository;
use Magento\Vault\Api\Data\PaymentTokenFactoryInterface;
use Magento\Vault\Api\PaymentTokenRepositoryInterface;
use Magento\Vault\Model\PaymentTokenManagement;
use DateInterval;
use DateTime;
use DateTimeZone;
class Cron class Cron
{ {
...@@ -239,6 +248,36 @@ class Cron ...@@ -239,6 +248,36 @@ class Cron
*/ */
protected $configHelper; protected $configHelper;
/**
* @var
*/
protected $_recurringDetailReference;
/**
* @var
*/
protected $_expiryDate;
/**
* @var PaymentTokenManagement
*/
private $paymentTokenManagement;
/**
* @var PaymentTokenFactoryInterface
*/
protected $paymentTokenFactory;
/**
* @var PaymentTokenRepositoryInterface
*/
protected $paymentTokenRepository;
/**
* @var EncryptorInterface
*/
protected $encryptor;
/** /**
* Cron constructor. * Cron constructor.
* *
...@@ -262,6 +301,14 @@ class Cron ...@@ -262,6 +301,14 @@ class Cron
* @param OrderRepository $orderRepository * @param OrderRepository $orderRepository
* @param ResourceModel\Billing\Agreement $agreementResourceModel * @param ResourceModel\Billing\Agreement $agreementResourceModel
* @param \Magento\Sales\Model\Order\Payment\Transaction\Builder $transactionBuilder * @param \Magento\Sales\Model\Order\Payment\Transaction\Builder $transactionBuilder
* @param \Magento\Framework\Serialize\SerializerInterface $serializer
* @param \Magento\Framework\Notification\NotifierInterface $notifierPool
* @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone
* @param \Adyen\Payment\Helper\Config $configHelper
* @param PaymentTokenManagement $paymentTokenManagement
* @param PaymentTokenFactoryInterface $paymentTokenFactory
* @param PaymentTokenRepositoryInterface $paymentTokenRepository
* @param EncryptorInterface $encryptor
*/ */
public function __construct( public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig, \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
...@@ -287,7 +334,11 @@ class Cron ...@@ -287,7 +334,11 @@ class Cron
\Magento\Framework\Serialize\SerializerInterface $serializer, \Magento\Framework\Serialize\SerializerInterface $serializer,
\Magento\Framework\Notification\NotifierInterface $notifierPool, \Magento\Framework\Notification\NotifierInterface $notifierPool,
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone, \Magento\Framework\Stdlib\DateTime\TimezoneInterface $timezone,
\Adyen\Payment\Helper\Config $configHelper \Adyen\Payment\Helper\Config $configHelper,
PaymentTokenManagement $paymentTokenManagement,
PaymentTokenFactoryInterface $paymentTokenFactory,
PaymentTokenRepositoryInterface $paymentTokenRepository,
EncryptorInterface $encryptor
) { ) {
$this->_scopeConfig = $scopeConfig; $this->_scopeConfig = $scopeConfig;
$this->_adyenLogger = $adyenLogger; $this->_adyenLogger = $adyenLogger;
...@@ -313,6 +364,10 @@ class Cron ...@@ -313,6 +364,10 @@ class Cron
$this->notifierPool = $notifierPool; $this->notifierPool = $notifierPool;
$this->timezone = $timezone; $this->timezone = $timezone;
$this->configHelper = $configHelper; $this->configHelper = $configHelper;
$this->paymentTokenManagement = $paymentTokenManagement;
$this->paymentTokenFactory = $paymentTokenFactory;
$this->paymentTokenRepository = $paymentTokenRepository;
$this->encryptor = $encryptor;
} }
/** /**
...@@ -451,7 +506,7 @@ class Cron ...@@ -451,7 +506,7 @@ class Cron
$this->_adyenLogger->addAdyenNotificationCronjob('Going to cancel the order'); $this->_adyenLogger->addAdyenNotificationCronjob('Going to cancel the order');
// if payment is API check, check if API result pspreference is the same as reference // if payment is API check, check if API result pspreference is the same as reference
if ($this->_eventCode == NOTIFICATION::AUTHORISATION if ($this->_eventCode == NOTIFICATION::AUTHORISATION
&& $this->_getPaymentMethodType() == 'api') { && $this->_getPaymentMethodType() == 'api') {
// don't cancel the order becasue order was successfull through api // don't cancel the order becasue order was successfull through api
$this->_adyenLogger->addAdyenNotificationCronjob( $this->_adyenLogger->addAdyenNotificationCronjob(
...@@ -585,6 +640,12 @@ class Cron ...@@ -585,6 +640,12 @@ class Cron
$additionalData = !empty($notification->getAdditionalData()) ? $this->serializer->unserialize( $additionalData = !empty($notification->getAdditionalData()) ? $this->serializer->unserialize(
$notification->getAdditionalData() $notification->getAdditionalData()
) : ""; ) : "";
if (!empty($additionalData[Vault::RECURRING_DETAIL_REFERENCE])) {
$this->_recurringDetailReference = $additionalData[Vault::RECURRING_DETAIL_REFERENCE];
}
if (!empty($additionalData[Vault::EXPIRY_DATE])) {
$this->_expiryDate = $additionalData[Vault::EXPIRY_DATE];
}
// boleto data // boleto data
if ($this->_paymentMethodCode() == "adyen_boleto") { if ($this->_paymentMethodCode() == "adyen_boleto") {
...@@ -1063,8 +1124,8 @@ class Cron ...@@ -1063,8 +1124,8 @@ class Cron
*/ */
if (!$isOrderCc && strcmp($notificationPaymentMethod, $orderPaymentMethod) !== 0) { if (!$isOrderCc && strcmp($notificationPaymentMethod, $orderPaymentMethod) !== 0) {
$this->_adyenLogger->addAdyenNotificationCronjob( $this->_adyenLogger->addAdyenNotificationCronjob(
"Order is not a credit card, "Order is not a credit card,
or the payment method in the notification does not match the payment method of the order, or the payment method in the notification does not match the payment method of the order,
skipping OFFER_CLOSED" skipping OFFER_CLOSED"
); );
break; break;
...@@ -1184,7 +1245,7 @@ class Cron ...@@ -1184,7 +1245,7 @@ class Cron
$billingAgreement = $this->_billingAgreementFactory->create(); $billingAgreement = $this->_billingAgreementFactory->create();
$billingAgreement->load($recurringDetailReference, 'reference_id'); $billingAgreement->load($recurringDetailReference, 'reference_id');
// check if BA exists // check if BA exists
if (!($billingAgreement && $billingAgreement->getAgreementId() > 0 if (!($billingAgreement && $billingAgreement->getAgreementId() > 0
&& $billingAgreement->isValid())) { && $billingAgreement->isValid())) {
// create new // create new
$this->_adyenLogger->addAdyenNotificationCronjob("Creating new Billing Agreement"); $this->_adyenLogger->addAdyenNotificationCronjob("Creating new Billing Agreement");
...@@ -1232,6 +1293,67 @@ class Cron ...@@ -1232,6 +1293,67 @@ class Cron
$this->_adyenLogger->addAdyenNotificationCronjob($message); $this->_adyenLogger->addAdyenNotificationCronjob($message);
$comment = $this->_order->addStatusHistoryComment($message); $comment = $this->_order->addStatusHistoryComment($message);
$this->_order->addRelatedObject($comment); $this->_order->addRelatedObject($comment);
}
//store recurring contract for alternative payments methods
if ($_paymentCode == 'adyen_hpp' && $this->configHelper->isStoreAlternativePaymentMethodEnabled()) {
$paymentTokenAlternativePaymentMethod = null;
try {
//get the payment
$payment = $this->_order->getPayment();
$customerId = $this->_order->getCustomerId();
$this->_adyenLogger->addAdyenNotificationCronjob(
'$paymentMethodCode ' . $this->_paymentMethod
);
if (!empty($this->_recurringDetailReference)) {
// Check if $paymentTokenAlternativePaymentMethod exists already
$paymentTokenAlternativePaymentMethod = $this->paymentTokenManagement->getByGatewayToken(
$this->_recurringDetailReference,
$payment->getMethodInstance()->getCode(),
$payment->getOrder()->getCustomerId()
);
// In case the payment token for this payment method does not exist, create it based on the additionalData
if ($paymentTokenAlternativePaymentMethod === null) {
$this->_adyenLogger->addAdyenNotificationCronjob('Creating new gateway token');
/** @var PaymentTokenInterface $paymentTokenAlternativePaymentMethod */
$paymentTokenAlternativePaymentMethod = $this->paymentTokenFactory->create(
PaymentTokenFactoryInterface::TOKEN_TYPE_CREDIT_CARD
);
$details = [
'type' => $this->_paymentMethod,
'maskedCC' => $payment->getAdditionalInformation()['ibanNumber'],
'expirationDate' => $this->_expiryDate
];
$paymentTokenAlternativePaymentMethod->setCustomerId($customerId)
->setGatewayToken($this->_recurringDetailReference)
->setPaymentMethodCode(AdyenHppConfigProvider::CODE)
->setPublicHash($this->encryptor->getHash($customerId));
} else {
$this->_adyenLogger->addAdyenNotificationCronjob('Gateway token already ' .
'exists, updating expiration date');
$details = json_decode($paymentTokenAlternativePaymentMethod->getTokenDetails());
$details['expirationDate'] = $this->_expiryDate;
}
$paymentTokenAlternativePaymentMethod->setExpiresAt(
$this->getExpirationDate(
$this->_expiryDate
)
);
$paymentTokenAlternativePaymentMethod->setTokenDetails(json_encode($details));
$this->paymentTokenRepository->save($paymentTokenAlternativePaymentMethod);
$this->_adyenLogger->addAdyenNotificationCronjob('New gateway token saved');
}
} catch (\Exception $exception) {
$message = $exception->getMessage();
$this->_adyenLogger->addAdyenNotificationCronjob(
"An error occurred while saving the payment method " . $message
);
}
} else { } else {
$this->_adyenLogger->addAdyenNotificationCronjob( $this->_adyenLogger->addAdyenNotificationCronjob(
'Ignore recurring_contract notification because Vault feature is enabled' 'Ignore recurring_contract notification because Vault feature is enabled'
...@@ -1457,7 +1579,7 @@ class Cron ...@@ -1457,7 +1579,7 @@ class Cron
if (!$createPendingInvoice) { if (!$createPendingInvoice) {
$this->_adyenLogger->addAdyenNotificationCronjob( $this->_adyenLogger->addAdyenNotificationCronjob(
'Setting pending invoice is off so don\'t create 'Setting pending invoice is off so don\'t create
an invoice wait for the capture notification' an invoice wait for the capture notification'
); );
return; return;
...@@ -2008,7 +2130,7 @@ class Cron ...@@ -2008,7 +2130,7 @@ class Cron
$this->notifierPool->addNotice( $this->notifierPool->addNotice(
__("Adyen: Refund for order #%1 has failed", $this->_merchantReference), __("Adyen: Refund for order #%1 has failed", $this->_merchantReference),
__( __(
"Reason: %1 | PSPReference: %2 | You can go to Adyen Customer Area "Reason: %1 | PSPReference: %2 | You can go to Adyen Customer Area
and trigger this refund manually or contact our support.", and trigger this refund manually or contact our support.",
$this->_reason, $this->_reason,
$this->_pspReference $this->_pspReference
...@@ -2068,4 +2190,24 @@ class Cron ...@@ -2068,4 +2190,24 @@ class Cron
$this->_order->save(); $this->_order->save();
} }
} }
/**
* @param $expirationDate
* @return string
* @throws Exception
*/
private function getExpirationDate($expirationDate)
{
$expirationDate = explode('/', $expirationDate);
$expDate = new DateTime(
//add leading zero to month
sprintf("%s-%02d-01 00:00:00", $expirationDate[1], $expirationDate[0]),
new DateTimeZone('UTC')
);
// add one month
$expDate->add(new DateInterval('P1M'));
return $expDate->format('Y-m-d H:i:s');
}
} }
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment Module
*
* Copyright (c) 2020 Adyen B.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Model\InstantPurchase\Hpp;
use Magento\InstantPurchase\PaymentMethodIntegration\PaymentTokenFormatterInterface;
use Magento\Vault\Api\Data\PaymentTokenInterface;
/**
* Adyen stored credit card formatter.
*/
class TokenFormatter implements PaymentTokenFormatterInterface
{
/**
* Most used HPP types
*
* @var array
*/
public static $baseHppTypes = [
'sepadirectdebit' => 'SEPA Direct Debit'
];
/**
* @inheritdoc
*/
public function formatPaymentToken(PaymentTokenInterface $paymentToken): string
{
$details = json_decode($paymentToken->getTokenDetails() ?: '{}', true);
if (!isset($details['type'], $details['maskedCC'], $details['expirationDate'])) {
throw new \InvalidArgumentException('Invalid Adyen HPP token details.');
}
if (isset(self::$baseCardTypes[$details['type']])) {
$hppType = self::$baseCardTypes[$details['type']];
} else {
$hppType = $details['type'];
}
return sprintf(
'%s: %s, %s: %s (%s: %s)',
__('SEPA'),
$hppType,
__('number'),
$details['maskedCC'],
__('expires'),
$details['expirationDate']
);
}
}
...@@ -30,6 +30,7 @@ use Magento\Directory\Helper\Data; ...@@ -30,6 +30,7 @@ use Magento\Directory\Helper\Data;
class AdyenHppConfigProvider implements ConfigProviderInterface class AdyenHppConfigProvider implements ConfigProviderInterface
{ {
const CODE = 'adyen_hpp'; const CODE = 'adyen_hpp';
const HPP_VAULT_CODE = 'adyen_hpp_vault';
/** /**
* @var PaymentHelper * @var PaymentHelper
......
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2020 Adyen BV (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Model\Ui;
use Adyen\Payment\Helper\Data;
use Magento\Vault\Api\Data\PaymentTokenInterface;
use Magento\Vault\Model\Ui\TokenUiComponentInterface;
use Magento\Vault\Model\Ui\TokenUiComponentProviderInterface;
use Magento\Vault\Model\Ui\TokenUiComponentInterfaceFactory;
use Magento\Framework\UrlInterface;
class TokenHppUiComponentProvider implements TokenUiComponentProviderInterface
{
/**
* @var TokenUiComponentInterfaceFactory
*/
private $componentFactory;
/**
* @var Data
*/
private $adyenHelper;
/**
* @param TokenUiComponentInterfaceFactory $componentFactory
* @param UrlInterface $urlBuilder
*/
public function __construct(
TokenUiComponentInterfaceFactory $componentFactory,
Data $adyenHelper
) {
$this->componentFactory = $componentFactory;
$this->adyenHelper = $adyenHelper;
}
/**
* Get UI component for token
*
* @param PaymentTokenInterface $paymentToken
* @return TokenUiComponentInterface
*/
public function getComponentForToken(PaymentTokenInterface $paymentToken)
{
$details = json_decode($paymentToken->getTokenDetails() ?: '{}', true);
$details['icon'] = $this->adyenHelper->getVariantIcon($details['type']);
return $this->componentFactory->create(
[
'config' => [
'code' => AdyenHppConfigProvider::HPP_VAULT_CODE,
TokenUiComponentProviderInterface::COMPONENT_DETAILS => $details,
TokenUiComponentProviderInterface::COMPONENT_PUBLIC_HASH => $paymentToken->getPublicHash()
],
'name' => 'Adyen_Payment/js/view/payment/method-renderer/vault'
]
);
}
}
...@@ -33,11 +33,16 @@ ...@@ -33,11 +33,16 @@
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model> <source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/adyen_hpp/active</config_path> <config_path>payment/adyen_hpp/active</config_path>
</field> </field>
<field id="sort_order" translate="label" type="text" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="0"> <field id="sort_order" translate="label" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Sort Order</label> <label>Sort Order</label>
<frontend_class>validate-number</frontend_class> <frontend_class>validate-number</frontend_class>
<config_path>payment/adyen_hpp/sort_order</config_path> <config_path>payment/adyen_hpp/sort_order</config_path>
</field> </field>
<field id="adyen_hpp_vault_active" translate="label" type="select" sortOrder="30" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Store alternative payment methods</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<config_path>payment/adyen_hpp_vault/active</config_path>
</field>
<group id="adyen_hpp_openinvoice_settings" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100"> <group id="adyen_hpp_openinvoice_settings" translate="label" showInDefault="1" showInWebsite="1" showInStore="1" sortOrder="100">
<label>Klarna\RatePay\Afterpay Settings</label> <label>Klarna\RatePay\Afterpay Settings</label>
<frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model> <frontend_model>Magento\Config\Block\System\Config\Form\Fieldset</frontend_model>
...@@ -81,4 +86,4 @@ ...@@ -81,4 +86,4 @@
</field> </field>
</group> </group>
</group> </group>
</include> </include>
\ No newline at end of file
...@@ -73,6 +73,14 @@ ...@@ -73,6 +73,14 @@
<tokenFormat>Adyen\Payment\Model\InstantPurchase\CreditCard\TokenFormatter</tokenFormat> <tokenFormat>Adyen\Payment\Model\InstantPurchase\CreditCard\TokenFormatter</tokenFormat>
</instant_purchase> </instant_purchase>
</adyen_cc_vault> </adyen_cc_vault>
<adyen_hpp_vault>
<model>AdyenPaymentHppVaultFacade</model>
<title>Alternative Payment Methods (Adyen)</title>
<instant_purchase>
<available>Adyen\Payment\Model\InstantPurchase\CreditCard\AvailabilityChecker</available>
<tokenFormat>Adyen\Payment\Model\InstantPurchase\Hpp\TokenFormatter</tokenFormat>
</instant_purchase>
</adyen_hpp_vault>
<adyen_google_pay_vault> <adyen_google_pay_vault>
<model>AdyenPaymentGooglePayVaultFacade</model> <model>AdyenPaymentGooglePayVaultFacade</model>
<title>Stored Cards (Adyen)</title> <title>Stored Cards (Adyen)</title>
...@@ -113,7 +121,7 @@ ...@@ -113,7 +121,7 @@
<ratepay_id>oj9GsQ</ratepay_id> <ratepay_id>oj9GsQ</ratepay_id>
<sort_order>3</sort_order> <sort_order>3</sort_order>
<payment_action>authorize</payment_action> <payment_action>authorize</payment_action>
<can_initialize>1</can_initialize> <can_initialize>0</can_initialize>
<is_gateway>1</is_gateway> <is_gateway>1</is_gateway>
<can_use_checkout>1</can_use_checkout> <can_use_checkout>1</can_use_checkout>
<can_capture>1</can_capture> <can_capture>1</can_capture>
...@@ -123,6 +131,8 @@ ...@@ -123,6 +131,8 @@
<can_refund>1</can_refund> <can_refund>1</can_refund>
<can_void>1</can_void> <can_void>1</can_void>
<can_cancel>1</can_cancel> <can_cancel>1</can_cancel>
<can_authorize_vault>1</can_authorize_vault>
<can_capture_vault>1</can_capture_vault>
<group>adyen</group> <group>adyen</group>
</adyen_hpp> </adyen_hpp>
<adyen_pos_cloud> <adyen_pos_cloud>
......
...@@ -53,6 +53,15 @@ ...@@ -53,6 +53,15 @@
<argument name="vaultProvider" xsi:type="object">AdyenPaymentCcFacade</argument> <argument name="vaultProvider" xsi:type="object">AdyenPaymentCcFacade</argument>
</arguments> </arguments>
</virtualType> </virtualType>
<virtualType name="AdyenPaymentHppVaultFacade" type="Magento\Vault\Model\Method\Vault">
<arguments>
<argument name="code" xsi:type="const">Adyen\Payment\Model\Ui\AdyenHppConfigProvider::HPP_VAULT_CODE
</argument>
<argument name="config" xsi:type="object">AdyenPaymentHppVaultConfig</argument>
<argument name="valueHandlerPool" xsi:type="object">AdyenPaymentHppVaultPaymentValueHandlerPool</argument>
<argument name="vaultProvider" xsi:type="object">AdyenPaymentHppFacade</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentGooglePayVaultFacade" type="Magento\Vault\Model\Method\Vault"> <virtualType name="AdyenPaymentGooglePayVaultFacade" type="Magento\Vault\Model\Method\Vault">
<arguments> <arguments>
<argument name="code" xsi:type="const">Adyen\Payment\Model\Ui\AdyenGooglePayConfigProvider::GOOGLE_PAY_VAULT_CODE <argument name="code" xsi:type="const">Adyen\Payment\Model\Ui\AdyenGooglePayConfigProvider::GOOGLE_PAY_VAULT_CODE
...@@ -147,6 +156,18 @@ ...@@ -147,6 +156,18 @@
</argument> </argument>
</arguments> </arguments>
</virtualType> </virtualType>
<virtualType name="AdyenPaymentHppVaultPaymentValueHandler" type="VaultPaymentDefaultValueHandler">
<arguments>
<argument name="configInterface" xsi:type="object">AdyenPaymentHppVaultConfig</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentHppVaultPaymentValueHandlerPool" type="VaultPaymentValueHandlerPool">
<arguments>
<argument name="handlers" xsi:type="array">
<item name="default" xsi:type="string">AdyenPaymentHppVaultPaymentValueHandler</item>
</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentGenericValueHandlerPool" type="Magento\Payment\Gateway\Config\ValueHandlerPool"> <virtualType name="AdyenPaymentGenericValueHandlerPool" type="Magento\Payment\Gateway\Config\ValueHandlerPool">
<arguments> <arguments>
<argument name="handlers" xsi:type="array"> <argument name="handlers" xsi:type="array">
...@@ -283,6 +304,12 @@ ...@@ -283,6 +304,12 @@
</argument> </argument>
</arguments> </arguments>
</virtualType> </virtualType>
<virtualType name="AdyenPaymentHppVaultConfig" type="Magento\Payment\Gateway\Config\Config">
<arguments>
<argument name="methodCode" xsi:type="const">Adyen\Payment\Model\Ui\AdyenHppConfigProvider::HPP_VAULT_CODE
</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentOneclickConfig" type="Magento\Payment\Gateway\Config\Config"> <virtualType name="AdyenPaymentOneclickConfig" type="Magento\Payment\Gateway\Config\Config">
<arguments> <arguments>
<argument name="methodCode" xsi:type="const">Adyen\Payment\Model\Ui\AdyenOneclickConfigProvider::CODE</argument> <argument name="methodCode" xsi:type="const">Adyen\Payment\Model\Ui\AdyenOneclickConfigProvider::CODE</argument>
...@@ -344,11 +371,18 @@ ...@@ -344,11 +371,18 @@
<arguments> <arguments>
<argument name="executors" xsi:type="array"> <argument name="executors" xsi:type="array">
<item name="adyen_cc" xsi:type="string">AdyenPaymentCcCommandManager</item> <item name="adyen_cc" xsi:type="string">AdyenPaymentCcCommandManager</item>
<item name="adyen_hpp" xsi:type="string">AdyenPaymentHppCommandManager</item>
<item name="adyen_google_pay" xsi:type="string">AdyenPaymentCcCommandManager</item> <item name="adyen_google_pay" xsi:type="string">AdyenPaymentCcCommandManager</item>
</argument> </argument>
</arguments> </arguments>
</type> </type>
<virtualType name="AdyenPaymentHppCommandManager" type="Magento\Payment\Gateway\Command\CommandManager">
<arguments>
<argument name="commandPool" xsi:type="object">AdyenPaymentHppCommandPool</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentOneclickCommandPool" type="Magento\Payment\Gateway\Command\CommandPool"> <virtualType name="AdyenPaymentOneclickCommandPool" type="Magento\Payment\Gateway\Command\CommandPool">
<arguments> <arguments>
<argument name="commands" xsi:type="array"> <argument name="commands" xsi:type="array">
...@@ -372,6 +406,18 @@ ...@@ -372,6 +406,18 @@
</arguments> </arguments>
</virtualType> </virtualType>
<virtualType name="AdyenPaymentHppVaultAuthorizeCommand" type="Magento\Payment\Gateway\Command\GatewayCommand">
<arguments>
<argument name="requestBuilder" xsi:type="object">AdyenPaymentHppVaultAuthorizeRequest</argument>
<argument name="transferFactory" xsi:type="object">Adyen\Payment\Gateway\Http\TransferFactory</argument>
<argument name="client" xsi:type="object">Adyen\Payment\Gateway\Http\Client\TransactionAuthorization
</argument>
<argument name="validator" xsi:type="object">GeneralResponseValidator</argument>
<argument name="handler" xsi:type="object">AdyenPaymentHppVaultResponseHandlerComposite</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentHppCommandPool" type="Magento\Payment\Gateway\Command\CommandPool"> <virtualType name="AdyenPaymentHppCommandPool" type="Magento\Payment\Gateway\Command\CommandPool">
<arguments> <arguments>
<argument name="commands" xsi:type="array"> <argument name="commands" xsi:type="array">
...@@ -380,6 +426,7 @@ ...@@ -380,6 +426,7 @@
<item name="void" xsi:type="string">AdyenPaymentCancelCommand</item> <item name="void" xsi:type="string">AdyenPaymentCancelCommand</item>
<item name="refund" xsi:type="string">AdyenPaymentRefundCommand</item> <item name="refund" xsi:type="string">AdyenPaymentRefundCommand</item>
<item name="cancel" xsi:type="string">AdyenPaymentCancelCommand</item> <item name="cancel" xsi:type="string">AdyenPaymentCancelCommand</item>
<item name="vault_authorize" xsi:type="string">AdyenPaymentHppVaultAuthorizeCommand</item>
</argument> </argument>
</arguments> </arguments>
</virtualType> </virtualType>
...@@ -596,6 +643,21 @@ ...@@ -596,6 +643,21 @@
</arguments> </arguments>
</virtualType> </virtualType>
<virtualType name="AdyenPaymentHppVaultResponseHandlerComposite"
type="Magento\Payment\Gateway\Response\HandlerChain">
<arguments>
<argument name="handlers" xsi:type="array">
<item name="payment_details" xsi:type="string">
Adyen\Payment\Gateway\Response\PaymentAuthorisationDetailsHandler
</item>
<item name="payment_comments" xsi:type="string">
Adyen\Payment\Gateway\Response\PaymentCommentHistoryHandler
</item>
</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentOneclickAuthorizeRequest" type="Magento\Payment\Gateway\Request\BuilderComposite"> <virtualType name="AdyenPaymentOneclickAuthorizeRequest" type="Magento\Payment\Gateway\Request\BuilderComposite">
<arguments> <arguments>
<argument name="builders" xsi:type="array"> <argument name="builders" xsi:type="array">
...@@ -628,6 +690,22 @@ ...@@ -628,6 +690,22 @@
</arguments> </arguments>
</virtualType> </virtualType>
<virtualType name="AdyenPaymentHppVaultAuthorizeRequest" type="Magento\Payment\Gateway\Request\BuilderComposite">
<arguments>
<argument name="builders" xsi:type="array">
<item name="merchantaccount" xsi:type="string">
Adyen\Payment\Gateway\Request\MerchantAccountDataBuilder
</item>
<item name="customer" xsi:type="string">Adyen\Payment\Gateway\Request\CustomerDataBuilder</item>
<item name="customerip" xsi:type="string">Adyen\Payment\Gateway\Request\CustomerIpDataBuilder</item>
<item name="address" xsi:type="string">Adyen\Payment\Gateway\Request\AddressDataBuilder</item>
<item name="payment" xsi:type="string">Adyen\Payment\Gateway\Request\PaymentDataBuilder</item>
<item name="browserinfo" xsi:type="string">Adyen\Payment\Gateway\Request\BrowserInfoDataBuilder</item>
<item name="recurring" xsi:type="string">Adyen\Payment\Gateway\Request\RecurringVaultDataBuilder</item>
</argument>
</arguments>
</virtualType>
<virtualType name="AdyenPaymentBoletoAuthorizeRequest" type="Magento\Payment\Gateway\Request\BuilderComposite"> <virtualType name="AdyenPaymentBoletoAuthorizeRequest" type="Magento\Payment\Gateway\Request\BuilderComposite">
<arguments> <arguments>
<argument name="builders" xsi:type="array"> <argument name="builders" xsi:type="array">
......
...@@ -42,8 +42,9 @@ ...@@ -42,8 +42,9 @@
<arguments> <arguments>
<argument name="tokenUiComponentProviders" xsi:type="array"> <argument name="tokenUiComponentProviders" xsi:type="array">
<item name="adyen_cc" xsi:type="object">Adyen\Payment\Model\Ui\TokenUiComponentProvider</item> <item name="adyen_cc" xsi:type="object">Adyen\Payment\Model\Ui\TokenUiComponentProvider</item>
<item name="adyen_hpp" xsi:type="object">Adyen\Payment\Model\Ui\TokenHppUiComponentProvider</item>
<item name="adyen_google_pay" xsi:type="object">Adyen\Payment\Model\Ui\TokenGooglePayUiComponentProvider</item> <item name="adyen_google_pay" xsi:type="object">Adyen\Payment\Model\Ui\TokenGooglePayUiComponentProvider</item>
</argument> </argument>
</arguments> </arguments>
</type> </type>
</config> </config>
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment