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 6bdeaaa5 authored by Attila Kiss's avatar Attila Kiss Committed by GitHub

Merge pull request #730 from Adyen/develop

Release 6.1.1
parents 21350d08 c3a7e85c
* @msilvagarcia @cyattilakiss @Aleffio @AlexandrosMor @rikterbeek @acampos1916
* @cyattilakiss @AlexandrosMor @msilvagarcia @acampos1916 @Aleffio @rikterbeek
**Magento version**: x.y.z
**Plugin version**: x.y.z
**Description**
<!--
- Please provide a description of the issue. In case of bug report, please provide the necessary steps to reproduce.
- For merchant specific requests, please use https://support.adyen.com
-->
\ No newline at end of file
......@@ -29,4 +29,18 @@ class Boleto extends AbstractInfo
* @var string
*/
protected $_template = 'Adyen_Payment::info/adyen_boleto.phtml';
/**
* @param $data string
* @return string
*/
public function getPaymentActionData($data)
{
$paymentAction = $this->getMethod()->getInfoInstance()->getAdditionalInformation('action');
if (empty($paymentAction[$data])) {
return '';
}
return $paymentAction[$data];
}
}
......@@ -107,7 +107,14 @@ class Result extends \Magento\Framework\App\Action\Action
$session->getQuote()->setIsActive(false)->save();
$this->_redirect('checkout/onepage/success', ['_query' => ['utm_nooverride' => '1']]);
} else {
$this->_cancel($response);
$this->_adyenLogger->addAdyenResult(
sprintf(
'Payment for order %s was unsuccessful, ' .
'it will be cancelled when the OFFER_CLOSED notification has been processed.',
$this->_order->getIncrementId()
)
);
$this->restoreCart($response);
$failReturnPath = $this->_adyenHelper->getAdyenAbstractConfigData('return_path');
$this->_redirect($failReturnPath);
}
......@@ -121,17 +128,13 @@ class Result extends \Magento\Framework\App\Action\Action
/**
* @param $response
*/
protected function _cancel($response)
protected function restoreCart($response)
{
$session = $this->_session;
// restore the quote
$session->restoreQuote();
$order = $this->_order;
$this->_adyenHelper->cancelOrder($order);
if (isset($response['authResult']) && $response['authResult'] == \Adyen\Payment\Model\Notification::CANCELLED) {
$this->messageManager->addError(__('You have cancelled the order. Please try again'));
} else {
......@@ -270,17 +273,13 @@ class Result extends \Magento\Framework\App\Action\Action
$this->_adyenLogger->addAdyenResult('Do nothing wait for the notification');
break;
case Notification::CANCELLED:
$this->_adyenLogger->addAdyenResult('Cancel or Hold the order');
case Notification::ERROR:
$this->_adyenLogger->addAdyenResult('Cancel or Hold the order on OFFER_CLOSED notification');
$result = false;
break;
case Notification::REFUSED:
// if refused there will be a AUTHORIZATION : FALSE notification send only exception is idea
$this->_adyenLogger->addAdyenResult('Cancel or Hold the order');
$result = false;
break;
case Notification::ERROR:
//attempt to hold/cancel
$this->_adyenLogger->addAdyenResult('Cancel or Hold the order');
$this->_adyenLogger->addAdyenResult('Cancel or Hold the order on AUTHORISATION success = false notification');
$result = false;
break;
default:
......
<?php
/**
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
*
* Adyen Payment module (https://www.adyen.com/)
*
* Copyright (c) 2020 Adyen NV (https://www.adyen.com/)
* See LICENSE.txt for license details.
*
* Author: Adyen <magento@adyen.com>
*/
namespace Adyen\Payment\Gateway\Data\Order;
use Magento\Sales\Api\Data\OrderAddressInterface;
class AddressAdapter extends \Magento\Payment\Gateway\Data\Order\AddressAdapter
{
/**
* @var OrderAddressInterface
*/
private $address;
public function __construct(OrderAddressInterface $address)
{
$this->address = $address;
parent::__construct($address);
}
/**
* Get street line 3
*
* @return string
*/
public function getStreetLine3()
{
$street = $this->address->getStreet();
return isset($street[2]) ? $street[2] : '';
}
/**
* Get street line 4
*
* @return string
*/
public function getStreetLine4()
{
$street = $this->address->getStreet();
return isset($street[3]) ? $street[3] : '';
}
}
......@@ -64,14 +64,8 @@ class TransactionAuthorization implements ClientInterface
public function placeRequest(\Magento\Payment\Gateway\Http\TransferInterface $transferObject)
{
$request = $transferObject->getBody();
$headers = $transferObject->getHeaders();
$requestOptions = [];
if (!empty($headers['idempotencyKey'])) {
$requestOptions['idempotencyKey'] = $headers['idempotencyKey'];
}
// call lib
$service = new \Adyen\Service\Payment($this->client);
......
......@@ -63,7 +63,6 @@ class TransactionPayment implements ClientInterface
public function placeRequest(\Magento\Payment\Gateway\Http\TransferInterface $transferObject)
{
$request = $transferObject->getBody();
$headers = $transferObject->getHeaders();
// If the payments call is already done return the request
if (!empty($request['resultCode'])) {
......@@ -77,10 +76,6 @@ class TransactionPayment implements ClientInterface
$requestOptions = [];
if (!empty($headers['idempotencyKey'])) {
$requestOptions['idempotencyKey'] = $headers['idempotencyKey'];
}
$request = $this->applicationInfo->addMerchantApplicationIntoRequest($request);
try {
......
......@@ -156,8 +156,8 @@ class CheckoutDataBuilder implements BuilderInterface
$requestBody['shopperName']['firstName'] = $payment->getAdditionalInformation("firstname");
}
if ($payment->getAdditionalInformation("lastName")) {
$requestBody['shopperName']['lastName'] = $payment->getAdditionalInformation("lastName");
if ($payment->getAdditionalInformation("lastname")) {
$requestBody['shopperName']['lastName'] = $payment->getAdditionalInformation("lastname");
}
if ($payment->getMethod() == \Adyen\Payment\Model\Ui\AdyenBoletoConfigProvider::CODE) {
......
......@@ -64,8 +64,6 @@ class PaymentDataBuilder implements BuilderInterface
$request['body'] = $this->adyenRequestsHelper->buildPaymentData([], $amount, $currencyCode, $reference, $paymentMethod);
$request['headers'] = $this->adyenRequestsHelper->addIdempotencyKey([], $paymentMethod, $reference);
return $request;
}
}
......@@ -23,43 +23,79 @@
namespace Adyen\Payment\Gateway\Response;
use Adyen\Payment\Helper\Data;
use Adyen\Payment\Logger\AdyenLogger;
use Magento\Vault\Api\Data\PaymentTokenFactoryInterface;
use Magento\Payment\Gateway\Response\HandlerInterface;
use Magento\Payment\Model\InfoInterface;
use Magento\Vault\Model\PaymentTokenManagement;
use Magento\Payment\Gateway\Data\PaymentDataObject;
use Magento\Vault\Api\PaymentTokenRepositoryInterface;
class VaultDetailsHandler implements HandlerInterface
{
const RECURRING_DETAIL_REFERENCE = 'recurring.recurringDetailReference';
const CARD_SUMMARY = 'cardSummary';
const EXPIRY_DATE = 'expiryDate';
const PAYMENT_METHOD = 'paymentMethod';
const ADDITIONAL_DATA_ERRORS = array(
self::RECURRING_DETAIL_REFERENCE => 'Missing Token in Result please enable in ' .
'Settings -> API URLs and Response menu in the Adyen Customer Area Recurring details setting',
self::CARD_SUMMARY => 'Missing cardSummary in Result please login to the adyen portal ' .
'and go to Settings -> API URLs and Response and enable the Card summary property',
self::EXPIRY_DATE => 'Missing expiryDate in Result please login to the adyen portal and go to ' .
'Settings -> API URLs and Response and enable the Expiry date property',
self::PAYMENT_METHOD => 'Missing paymentMethod in Result please login to the adyen portal and go to ' .
'Settings -> API URLs and Response and enable the Variant property'
);
/**
* @var PaymentTokenInterfaceFactory
* @var PaymentTokenFactoryInterface
*/
protected $paymentTokenFactory;
/**
* @var \Adyen\Payment\Logger\AdyenLogger
* @var AdyenLogger
*/
private $adyenLogger;
/**
* @var \Adyen\Payment\Helper\Data
* @var Data
*/
private $adyenHelper;
/**
* @var PaymentTokenManagement
*/
private $paymentTokenManagement;
/**
* @var
*/
private $paymentTokenRepository;
/**
* VaultDetailsHandler constructor.
*
* @param PaymentTokenFactoryInterface $paymentTokenFactory
* @param \Adyen\Payment\Logger\AdyenLogger $adyenLogger
* @param \Adyen\Payment\Helper\Data $adyenHelper
* @param AdyenLogger $adyenLogger
* @param Data $adyenHelper
* @param PaymentTokenManagement $paymentTokenManagement
* @param PaymentTokenRepositoryInterface $paymentTokenRepository
*/
public function __construct(
PaymentTokenFactoryInterface $paymentTokenFactory,
\Adyen\Payment\Logger\AdyenLogger $adyenLogger,
\Adyen\Payment\Helper\Data $adyenHelper
AdyenLogger $adyenLogger,
Data $adyenHelper,
PaymentTokenManagement $paymentTokenManagement,
PaymentTokenRepositoryInterface $paymentTokenRepository
) {
$this->adyenLogger = $adyenLogger;
$this->adyenHelper = $adyenHelper;
$this->paymentTokenFactory = $paymentTokenFactory;
$this->paymentTokenManagement = $paymentTokenManagement;
$this->paymentTokenRepository = $paymentTokenRepository;
}
/**
......@@ -67,18 +103,23 @@ class VaultDetailsHandler implements HandlerInterface
*/
public function handle(array $handlingSubject, array $response)
{
$payment = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($handlingSubject);
/** @var PaymentDataObject $orderPayment */
$orderPayment = \Magento\Payment\Gateway\Helper\SubjectReader::readPayment($handlingSubject);
/** @var \Adyen\Payment\Api\Data\OrderPaymentInterface $payment */
$payment = $payment->getPayment();
$payment = $orderPayment->getPayment();
if ($this->adyenHelper->isCreditCardVaultEnabled($payment->getOrder()->getStoreId())) {
// add vault payment token entity to extension attributes
$paymentToken = $this->getVaultPaymentToken($response);
$paymentToken = $this->getVaultPaymentToken($response, $payment);
if (null !== $paymentToken) {
$extensionAttributes = $this->getExtensionAttributes($payment);
$extensionAttributes->setVaultPaymentToken($paymentToken);
} else {
$this->adyenLogger->error(
sprintf('Failure trying to save credit card token in vault for order %s',
$payment->getOrder()->getIncrementId())
);
}
}
}
......@@ -87,78 +128,70 @@ class VaultDetailsHandler implements HandlerInterface
* Get vault payment token entity
*
* @param array $response
* @param $payment
* @return PaymentTokenInterface|null
*/
private function getVaultPaymentToken(array $response)
private function getVaultPaymentToken(array $response, $payment)
{
$paymentToken = null;
if (!empty($response['additionalData'])) {
$additionalData = $response['additionalData'];
if (empty($additionalData['recurring.recurringDetailReference'])) {
$this->adyenLogger->error(
'Missing Token in Result please enable in ' .
'Settings -> API URLs and Response menu in the Adyen Customer Area Recurring details setting'
);
if (empty($response['additionalData'])) {
return null;
}
$token = $additionalData['recurring.recurringDetailReference'];
$additionalData = $response['additionalData'];
if (empty($additionalData['cardSummary'])) {
$this->adyenLogger->error(
'Missing cardSummary in Result please login to the adyen portal ' .
'and go to Settings -> API URLs and Response and enable the Card summary property'
);
return null;
}
$cardSummary = $additionalData['cardSummary'];
$paymentToken = null;
if (empty($additionalData['expiryDate'])) {
$this->adyenLogger->error(
'Missing expiryDate in Result please login to the adyen portal and go to ' .
'Settings -> API URLs and Response and enable the Expiry date property'
);
foreach (self::ADDITIONAL_DATA_ERRORS as $key => $errorMsg) {
if (empty($additionalData[$key])) {
$this->adyenLogger->error($errorMsg);
return null;
}
$expirationDate = $additionalData['expiryDate'];
if (empty($additionalData['paymentMethod'])) {
$this->adyenLogger->error(
'Missing paymentMethod in Result please login to the adyen portal and go to ' .
'Settings -> API URLs and Response and enable the Variant property'
);
return null;
}
$cardType = $additionalData['paymentMethod'];
try {
// Check if paymentToken exists already
$paymentToken = $this->paymentTokenManagement->getByGatewayToken($additionalData[self::RECURRING_DETAIL_REFERENCE],
$payment->getMethodInstance()->getCode(), $payment->getOrder()->getCustomerId());
$paymentTokenSaveRequired = false;
// In case the payment token does not exist, create it based on the additionalData
if (is_null($paymentToken)) {
/** @var PaymentTokenInterface $paymentToken */
$paymentToken = $this->paymentTokenFactory->create(
PaymentTokenFactoryInterface::TOKEN_TYPE_CREDIT_CARD
);
if (strpos($cardType, "paywithgoogle") !== false && !empty($additionalData['paymentMethodVariant'])) {
$cardType = $additionalData['paymentMethodVariant'];
$paymentToken->setGatewayToken($additionalData[self::RECURRING_DETAIL_REFERENCE]);
if (strpos($additionalData[self::PAYMENT_METHOD], "paywithgoogle") !== false
&& !empty($additionalData['paymentMethodVariant'])) {
$additionalData[self::PAYMENT_METHOD] = $additionalData['paymentMethodVariant'];
$paymentToken->setIsVisible(false);
}
$paymentToken->setGatewayToken($token);
$paymentToken->setExpiresAt($this->getExpirationDate($expirationDate));
} else {
$paymentTokenSaveRequired = true;
}
$paymentToken->setExpiresAt($this->getExpirationDate($additionalData[self::EXPIRY_DATE]));
$details = [
'type' => $cardType,
'maskedCC' => $cardSummary,
'expirationDate' => $expirationDate
'type' => $additionalData[self::PAYMENT_METHOD],
'maskedCC' => $additionalData[self::CARD_SUMMARY],
'expirationDate' => $additionalData[self::EXPIRY_DATE]
];
$paymentToken->setTokenDetails(json_encode($details));
// If the token is updated, it needs to be saved to keep the changes
if ($paymentTokenSaveRequired) {
$this->paymentTokenRepository->save($paymentToken);
}
} catch (\Exception $e) {
$this->adyenLogger->error(print_r($e, true));
}
}
return $paymentToken;
}
......
......@@ -487,26 +487,15 @@ class Requests extends AbstractHelper
// Parse address into street and house number where possible
$address = $this->adyenHelper->getStreetFromString($address->getStreetFull());
} else {
$address = $this->adyenHelper->getStreetFromString(implode(' ', [$address->getStreetLine1(), $address->getStreetLine2()]));
$address = $this->adyenHelper->getStreetFromString(
implode(' ', [
$address->getStreetLine1(),
$address->getStreetLine2(),
$address->getStreetLine3(),
$address->getStreetLine4()
]));
}
return $address;
}
/**
* Only adds idempotency key if payment method is adyen_hpp for now
*
* @param array $request
* @param $paymentMethod
* @param $idempotencyKey
* @return array
*/
public function addIdempotencyKey($request = [], $paymentMethod, $idempotencyKey)
{
if (!empty($paymentMethod) && $paymentMethod == 'adyen_hpp') {
$request['idempotencyKey'] = $idempotencyKey;
}
return $request;
}
}
......@@ -331,6 +331,34 @@ class Cron
}
}
/**
* @param $notification
* @return bool
*/
private function shouldSkipProcessingNotification($notification)
{
// OFFER_CLOSED notifications needs to be at least 10 minutes old to be processed
$offerClosedMinDate = new \DateTime();
$offerClosedMinDate->modify('-10 minutes');
// Remove OFFER_CLOSED notifications arrived in the last 10 minutes from the list to process to ensure it
// won't close any order which has an AUTHORISED notification arrived a bit later than the OFFER_CLOSED one.
$createdAt = \DateTime::createFromFormat('Y-m-d H:i:s', $notification['created_at']);
// To get the difference between $offerClosedMinDate and $createdAt, $offerClosedMinDate time in seconds is
// deducted from $createdAt time in seconds, divided by 60 and rounded down to integer
$minutesUntilProcessing = floor(($createdAt->getTimestamp() - $offerClosedMinDate->getTimestamp()) / 60);
if ($notification['event_code'] == Notification::OFFER_CLOSED && $minutesUntilProcessing > 0) {
$this->_adyenLogger->addAdyenNotificationCronjob(
sprintf('OFFER_CLOSED notification %s skipped! Wait %s minute(s) before processing.',
$notification->getEntityId(), $minutesUntilProcessing)
);
return true;
}
return false;
}
public function execute()
{
// needed for Magento < 2.2.0 https://github.com/magento/magento2/pull/8413
......@@ -341,22 +369,19 @@ class Cron
$this->_order = null;
// execute notifications from 2 minute or earlier because order could not yet been created by magento
$dateStart = new \DateTime();
$dateStart->modify('-5 day');
$dateEnd = new \DateTime();
$dateEnd->modify('-1 minute');
$dateRange = ['from' => $dateStart, 'to' => $dateEnd, 'datetime' => true];
// create collection
$notifications = $this->_notificationFactory->create();
$notifications->addFieldToFilter('done', 0);
$notifications->addFieldToFilter('processing', 0);
$notifications->addFieldToFilter('created_at', $dateRange);
$notifications->addFieldToFilter('error_count', ['lt' => Notification::MAX_ERROR_COUNT]);
$notifications->notificationsToProcessFilter();
// Loop thorugh notifications to set processing to true if notifiaction should not be skipped
foreach ($notifications as $notification) {
// set Cron processing to true
// Check if notification should be processed or not
if ($this->shouldSkipProcessingNotification($notification)) {
// Remove notification from collection which will be processed
$notifications->removeItemByKey($notification->getId());
continue;
}
// set notification processing to true
$this->_updateNotification($notification, true, false);
}
......@@ -459,7 +484,7 @@ class Cron
);
}
//Trigger admin notice for unsuccessful REFUND notifications
if ($this->_eventCode == Notification::REFUND){
if ($this->_eventCode == Notification::REFUND) {
$this->addRefundFailedNotice();
}
} else {
......@@ -882,7 +907,6 @@ class Cron
*/
protected function _processNotification()
{
$this->_adyenLogger->addAdyenNotificationCronjob('Processing the notification');
$_paymentCode = $this->_paymentMethodCode();
......@@ -1143,10 +1167,8 @@ class Cron
// Populate billing agreement data
$billingAgreement->parseRecurringContractData($contractDetail);
if ($billingAgreement->isValid()) {
if (!$this->agreementResourceModel->getOrderRelation($billingAgreement->getAgreementId(),
$this->_order->getId())) {
// save into sales_billing_agreement_order
$billingAgreement->addOrderRelation($this->_order);
......
......@@ -46,4 +46,30 @@ class Collection extends \Magento\Framework\Model\ResourceModel\Db\Collection\Ab
$this->addFieldToFilter('created_at', $dateRange);
return $this;
}
/**
* Filter the notifications table to get non processed or done notifications without 5 or more errors older than
* 2 minutes but not older than 5 days, ordered by created_at and event_code columns
*
* @return $this
*/
public function notificationsToProcessFilter()
{
// execute notifications from 2 minute or earlier because order could not yet been created by magento
$dateStart = new \DateTime();
$dateStart->modify('-5 day');
$dateEnd = new \DateTime();
$dateEnd->modify('-1 minute');
$dateRange = ['from' => $dateStart, 'to' => $dateEnd, 'datetime' => true];
$this->addFieldToFilter('done', 0);
$this->addFieldToFilter('processing', 0);
$this->addFieldToFilter('created_at', $dateRange);
$this->addFieldToFilter('error_count', ['lt' => \Adyen\Payment\Model\Notification::MAX_ERROR_COUNT]);
// Process the notifications in ascending order by creation date and event_code
$this->getSelect()->order('created_at ASC')->order('event_code ASC');
return $this;
}
}
......@@ -2,7 +2,7 @@
"name": "adyen/module-payment",
"description": "Official Magento2 Plugin to connect to Payment Service Provider Adyen.",
"type": "magento2-module",
"version": "6.1.0",
"version": "6.1.1",
"license": [
"OSL-3.0",
"AFL-3.0"
......
......@@ -914,6 +914,7 @@
</virtualType>
<preference for="Magento\Paypal\Model\Billing\Agreement" type="Adyen\Payment\Model\Billing\Agreement" />
<preference for="Magento\Payment\Gateway\Data\Order\AddressAdapter" type="Adyen\Payment\Gateway\Data\Order\AddressAdapter"/>
<type name="Adyen\Payment\Logger\Handler\AdyenDebug">
<arguments>
<argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
......
......@@ -73,9 +73,9 @@ $_isDemoMode = $block->isDemoMode();
</div>
<?php endif; ?>
<?php if($_info->getAdditionalInformation('expirationDate') != ""): ?>
<?php if(!empty($block->getPaymentActionData('expiresAt'))): ?>
<div>
<?php echo __('Expiration Date: %1', $_info->getAdditionalInformation('expirationDate')) ?>
<?php echo __('Expiration Date: %1', $block->getPaymentActionData('expiresAt')) ?>
</div>
<?php endif; ?>
......@@ -85,9 +85,9 @@ $_isDemoMode = $block->isDemoMode();
</div>
<?php endif; ?>
<?php if($_info->getAdditionalInformation('url') != ""): ?>
<?php if(!empty($block->getPaymentActionData('downloadUrl'))): ?>
<div>
<a target="_blank" href="<?php echo $_info->getAdditionalInformation('url'); ?>"><?php echo __('PDF Url'); ?></a>
<a target="_blank" href="<?php echo $block->getPaymentActionData('downloadUrl'); ?>"><?php echo __('PDF Url'); ?></a>
</div>
<?php endif; ?>
......
......@@ -33,5 +33,5 @@ $_info = $this->getInfo();
<dt class="title"><?php echo $_info->getAdditionalInformation('boleto_type'); ?></dt>
<dt class="title"><?php echo $_info->getAdditionalInformation('firstname'); ?></dt>
<dt class="title"><?php echo $_info->getAdditionalInformation('lastname'); ?></dt>
<dt class="title"><a target="_blank" href="<?php echo $this->getMethod()->getInfoInstance()->getAdditionalInformation('url'); ?>"><?php echo __("Click here to download Boleto PDF."); ?></a></dt>
<dt class="title"><a target="_blank" href="<?php echo $this->getPaymentActionData('downloadUrl'); ?>"><?php echo __("Click here to download Boleto PDF."); ?></a></dt>
</dl>
......@@ -78,6 +78,7 @@ define(
var isValid = ko.observable(false);
return Component.extend({
isPlaceOrderActionAllowed: ko.observable(quote.billingAddress() != null),
defaults: {
template: 'Adyen_Payment/payment/oneclick-form',
recurringDetailReference: '',
......@@ -195,7 +196,7 @@ define(
isButtonActive: function () {
return self.isActive() && this.getCode() == self.isChecked() && self.isBillingAgreementChecked() && this.placeOrderAllowed();
return self.isActive() && this.getCode() == self.isChecked() && self.isBillingAgreementChecked() && this.placeOrderAllowed() && self.isPlaceOrderActionAllowed();
},
/**
* Custom place order function
......@@ -528,7 +529,6 @@ define(
if (quote.paymentMethod().method == paymentMethod()) {
return recurringDetailReference();
}
return null;
}),
placeOrderHandler: null,
......
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