Skip to content

Commit

Permalink
Merge pull request #2681 from magento-tsg/2.3-develop-pr22
Browse files Browse the repository at this point in the history
[TSG] Upporting for 2.3 (pr22) (2.3.0)
  • Loading branch information
Alexander Akimov authored Jun 11, 2018
2 parents 55256fa + 5cfc987 commit 02aaccb
Show file tree
Hide file tree
Showing 48 changed files with 1,627 additions and 858 deletions.
1 change: 1 addition & 0 deletions app/code/Magento/Backend/Block/GlobalSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public function getWidgetInitOptions()
'filterProperty' => 'name',
'preventClickPropagation' => false,
'minLength' => 2,
'submitInputOnEnter' => false,
]
];
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Braintree\Gateway\Response;

use Magento\Braintree\Gateway\SubjectReader;
use Magento\Payment\Gateway\Response\HandlerInterface;
use Magento\Sales\Model\Order\Payment;

/**
* Handles response details for order cancellation request.
*/
class CancelDetailsHandler implements HandlerInterface
{
/**
* @var SubjectReader
*/
private $subjectReader;

/**
* @param SubjectReader $subjectReader
*/
public function __construct(SubjectReader $subjectReader)
{
$this->subjectReader = $subjectReader;
}

/**
* @inheritdoc
*/
public function handle(array $handlingSubject, array $response)
{
$paymentDO = $this->subjectReader->readPayment($handlingSubject);
/** @var Payment $orderPayment */
$orderPayment = $paymentDO->getPayment();
$orderPayment->setIsTransactionClosed(true);
$orderPayment->setShouldCloseParentTransaction(true);
}
}
9 changes: 5 additions & 4 deletions app/code/Magento/Braintree/Gateway/SubjectReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,19 +43,20 @@ public function readPayment(array $subject)
}

/**
* Reads transaction from subject
* Reads transaction from the subject.
*
* @param array $subject
* @return \Braintree\Transaction
* @return Transaction
* @throws \InvalidArgumentException if the subject doesn't contain transaction details.
*/
public function readTransaction(array $subject)
{
if (!isset($subject['object']) || !is_object($subject['object'])) {
throw new \InvalidArgumentException('Response object does not exist');
throw new \InvalidArgumentException('Response object does not exist.');
}

if (!isset($subject['object']->transaction)
&& !$subject['object']->transaction instanceof Transaction
|| !$subject['object']->transaction instanceof Transaction
) {
throw new \InvalidArgumentException('The object is not a class \Braintree\Transaction.');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Braintree\Gateway\Validator;

use Braintree\Error\ErrorCollection;
use Braintree\Error\Validation;
use Magento\Payment\Gateway\Validator\AbstractValidator;
use Magento\Payment\Gateway\Validator\ResultInterface;
use Magento\Payment\Gateway\Validator\ResultInterfaceFactory;
use Magento\Braintree\Gateway\SubjectReader;

/**
* Decorates the general response validator to handle specific cases.
*
* This validator decorates the general response validator to handle specific cases like
* an expired or already voided on Braintree side authorization transaction.
*/
class CancelResponseValidator extends AbstractValidator
{
/**
* @var int
*/
private static $acceptableTransactionCode = 91504;

/**
* @var GeneralResponseValidator
*/
private $generalResponseValidator;

/**
* @var SubjectReader
*/
private $subjectReader;

/**
* @param ResultInterfaceFactory $resultFactory
* @param GeneralResponseValidator $generalResponseValidator
* @param SubjectReader $subjectReader
*/
public function __construct(
ResultInterfaceFactory $resultFactory,
GeneralResponseValidator $generalResponseValidator,
SubjectReader $subjectReader
) {
parent::__construct($resultFactory);
$this->generalResponseValidator = $generalResponseValidator;
$this->subjectReader = $subjectReader;
}

/**
* @inheritdoc
*/
public function validate(array $validationSubject): ResultInterface
{
$result = $this->generalResponseValidator->validate($validationSubject);
if (!$result->isValid()) {
$response = $this->subjectReader->readResponseObject($validationSubject);
if ($this->isErrorAcceptable($response->errors)) {
$result = $this->createResult(true, [__('Transaction is cancelled offline.')]);
}
}

return $result;
}

/**
* Checks if error collection has an acceptable error code.
*
* @param ErrorCollection $errorCollection
* @return bool
*/
private function isErrorAcceptable(ErrorCollection $errorCollection): bool
{
$errors = $errorCollection->deepAll();
// there is should be only one acceptable error
if (count($errors) > 1) {
return false;
}

/** @var Validation $error */
$error = array_pop($errors);

return (int)$error->code === self::$acceptableTransactionCode;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);

namespace Magento\Braintree\Test\Unit\Gateway\Response;

use Magento\Braintree\Gateway\Response\CancelDetailsHandler;
use Magento\Braintree\Gateway\SubjectReader;
use Magento\Payment\Gateway\Data\OrderAdapterInterface;
use Magento\Payment\Gateway\Data\PaymentDataObject;
use Magento\Sales\Model\Order\Payment;
use PHPUnit\Framework\TestCase;
use PHPUnit_Framework_MockObject_MockObject as MockObject;

/**
* Tests \Magento\Braintree\Gateway\Response\CancelDetailsHandler.
*/
class CancelDetailsHandlerTest extends TestCase
{
/**
* @var CancelDetailsHandler
*/
private $handler;

/**
* @inheritdoc
*/
protected function setUp()
{
$this->handler = new CancelDetailsHandler(new SubjectReader());
}

/**
* Checks a case when cancel handler closes the current and parent transactions.
*
* @return void
*/
public function testHandle(): void
{
/** @var OrderAdapterInterface|MockObject $order */
$order = $this->getMockForAbstractClass(OrderAdapterInterface::class);
/** @var Payment|MockObject $payment */
$payment = $this->getMockBuilder(Payment::class)
->disableOriginalConstructor()
->setMethods(['setOrder'])
->getMock();

$paymentDO = new PaymentDataObject($order, $payment);
$response = [
'payment' => $paymentDO,
];

$this->handler->handle($response, []);

self::assertTrue($payment->getIsTransactionClosed(), 'The current transaction should be closed.');
self::assertTrue($payment->getShouldCloseParentTransaction(), 'The parent transaction should be closed.');
}
}
98 changes: 86 additions & 12 deletions app/code/Magento/Braintree/Test/Unit/Gateway/SubjectReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
namespace Magento\Braintree\Test\Unit\Gateway;

use Braintree\Result\Successful;
use Braintree\Transaction;
use Magento\Braintree\Gateway\SubjectReader;

Expand All @@ -18,6 +19,9 @@ class SubjectReaderTest extends \PHPUnit\Framework\TestCase
*/
private $subjectReader;

/**
* @inheritdoc
*/
protected function setUp()
{
$this->subjectReader = new SubjectReader();
Expand All @@ -27,67 +31,137 @@ protected function setUp()
* @covers \Magento\Braintree\Gateway\SubjectReader::readCustomerId
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The "customerId" field does not exists
* @return void
*/
public function testReadCustomerIdWithException()
public function testReadCustomerIdWithException(): void
{
$this->subjectReader->readCustomerId([]);
}

/**
* @covers \Magento\Braintree\Gateway\SubjectReader::readCustomerId
* @return void
*/
public function testReadCustomerId()
public function testReadCustomerId(): void
{
$customerId = 1;
static::assertEquals($customerId, $this->subjectReader->readCustomerId(['customer_id' => $customerId]));
$this->assertEquals($customerId, $this->subjectReader->readCustomerId(['customer_id' => $customerId]));
}

/**
* @covers \Magento\Braintree\Gateway\SubjectReader::readPublicHash
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage The "public_hash" field does not exists
* @return void
*/
public function testReadPublicHashWithException()
public function testReadPublicHashWithException(): void
{
$this->subjectReader->readPublicHash([]);
}

/**
* @covers \Magento\Braintree\Gateway\SubjectReader::readPublicHash
* @return void
*/
public function testReadPublicHash()
public function testReadPublicHash(): void
{
$hash = 'fj23djf2o1fd';
static::assertEquals($hash, $this->subjectReader->readPublicHash(['public_hash' => $hash]));
$this->assertEquals($hash, $this->subjectReader->readPublicHash(['public_hash' => $hash]));
}

/**
* @covers \Magento\Braintree\Gateway\SubjectReader::readPayPal
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Transaction has't paypal attribute
* @return void
*/
public function testReadPayPalWithException()
public function testReadPayPalWithException(): void
{
$transaction = Transaction::factory([
'id' => 'u38rf8kg6vn'
'id' => 'u38rf8kg6vn',
]);
$this->subjectReader->readPayPal($transaction);
}

/**
* @covers \Magento\Braintree\Gateway\SubjectReader::readPayPal
* @return void
*/
public function testReadPayPal()
public function testReadPayPal(): void
{
$paypal = [
'paymentId' => '3ek7dk7fn0vi1',
'payerEmail' => '[email protected]'
'payerEmail' => '[email protected]',
];
$transaction = Transaction::factory([
'id' => '4yr95vb',
'paypal' => $paypal
'paypal' => $paypal,
]);

static::assertEquals($paypal, $this->subjectReader->readPayPal($transaction));
$this->assertEquals($paypal, $this->subjectReader->readPayPal($transaction));
}

/**
* Checks a case when subject reader retrieves successful Braintree transaction.
*
* @return void
*/
public function testReadTransaction(): void
{
$transaction = Transaction::factory(['id' => 1]);
$response = [
'object' => new Successful($transaction, 'transaction'),
];
$actual = $this->subjectReader->readTransaction($response);

$this->assertSame($transaction, $actual);
}

/**
* Checks a case when subject reader retrieves invalid data instead transaction details.
*
* @param array $response
* @param string $expectedMessage
* @dataProvider invalidTransactionResponseDataProvider
* @expectedException \InvalidArgumentException
* @return void
*/
public function testReadTransactionWithInvalidResponse(array $response, string $expectedMessage): void
{
$this->expectExceptionMessage($expectedMessage);
$this->subjectReader->readTransaction($response);
}

/**
* Gets list of variations with invalid subject data.
*
* @return array
*/
public function invalidTransactionResponseDataProvider(): array
{
$transaction = new \stdClass();
$response = new \stdClass();
$response->transaction = $transaction;

return [
[
'response' => [
'object' => [],
],
'expectedMessage' => 'Response object does not exist.',
],
[
'response' => [
'object' => new \stdClass(),
],
'expectedMessage' => 'The object is not a class \Braintree\Transaction.',
],
[
'response' => [
'object' => $response,
],
'expectedMessage' => 'The object is not a class \Braintree\Transaction.',
],
];
}
}
Loading

0 comments on commit 02aaccb

Please sign in to comment.