Skip to content

Commit

Permalink
Merge pull request #3902 from magento-arcticfoxes/2.3-develop-pr
Browse files Browse the repository at this point in the history
[2.3-develop] Sync with 2.3.1-release
joanhe authored Mar 15, 2019

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
2 parents ce17220 + 383988d commit 7b1cd79
Showing 12 changed files with 353 additions and 77 deletions.
9 changes: 5 additions & 4 deletions app/code/Magento/Authorizenet/Model/Directpost.php
Original file line number Diff line number Diff line change
@@ -546,15 +546,16 @@ public function setResponseData(array $postData)
public function validateResponse()
{
$response = $this->getResponse();
//md5 check
if (!$this->getConfigData('trans_md5')
|| !$this->getConfigData('login')
|| !$response->isValidHash($this->getConfigData('trans_md5'), $this->getConfigData('login'))
$hashConfigKey = !empty($response->getData('x_SHA2_Hash')) ? 'signature_key' : 'trans_md5';

//hash check
if (!$response->isValidHash($this->getConfigData($hashConfigKey), $this->getConfigData('login'))
) {
throw new \Magento\Framework\Exception\LocalizedException(
__('The transaction was declined because the response hash validation failed.')
);
}

return true;
}

119 changes: 105 additions & 14 deletions app/code/Magento/Authorizenet/Model/Directpost/Request.php
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@
namespace Magento\Authorizenet\Model\Directpost;

use Magento\Authorizenet\Model\Request as AuthorizenetRequest;
use Magento\Framework\App\ObjectManager;
use Magento\Framework\Intl\DateTimeFactory;

/**
* Authorize.net request model for DirectPost model
@@ -20,10 +22,35 @@ class Request extends AuthorizenetRequest
*/
protected $_transKey = null;

/**
* Hexadecimal signature key.
*
* @var string
*/
private $signatureKey = '';

/**
* @var DateTimeFactory
*/
private $dateTimeFactory;

/**
* @param array $data
* @param DateTimeFactory $dateTimeFactory
*/
public function __construct(
array $data = [],
DateTimeFactory $dateTimeFactory = null
) {
$this->dateTimeFactory = $dateTimeFactory ?? ObjectManager::getInstance()
->get(DateTimeFactory::class);
parent::__construct($data);
}

/**
* Return merchant transaction key.
*
* Needed to generate sign.
* Needed to generate MD5 sign.
*
* @return string
*/
@@ -35,7 +62,7 @@ protected function _getTransactionKey()
/**
* Set merchant transaction key.
*
* Needed to generate sign.
* Needed to generate MD5 sign.
*
* @param string $transKey
* @return $this
@@ -47,7 +74,7 @@ protected function _setTransactionKey($transKey)
}

/**
* Generates the fingerprint for request.
* Generates the MD5 fingerprint for request.
*
* @param string $merchantApiLoginId
* @param string $merchantTransactionKey
@@ -67,7 +94,7 @@ public function generateRequestSign(
) {
return hash_hmac(
"md5",
$merchantApiLoginId . "^" . $fpSequence . "^" . $fpTimestamp . "^" . $amount . "^" . $currencyCode,
$merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode,
$merchantTransactionKey
);
}
@@ -82,7 +109,7 @@ public function setConstantData(\Magento\Authorizenet\Model\Directpost $paymentM
{
$this->setXVersion('3.1')->setXDelimData('FALSE')->setXRelayResponse('TRUE');

$this->setXTestRequest($paymentMethod->getConfigData('test') ? 'TRUE' : 'FALSE');
$this->setSignatureKey($paymentMethod->getConfigData('signature_key'));

$this->setXLogin($paymentMethod->getConfigData('login'))
->setXMethod(\Magento\Authorizenet\Model\Authorizenet::REQUEST_METHOD_CC)
@@ -173,17 +200,81 @@ public function setDataFromOrder(
*/
public function signRequestData()
{
$fpTimestamp = time();
$hash = $this->generateRequestSign(
$this->getXLogin(),
$this->_getTransactionKey(),
$this->getXAmount(),
$this->getXCurrencyCode(),
$this->getXFpSequence(),
$fpTimestamp
);
$fpDate = $this->dateTimeFactory->create('now', new \DateTimeZone('UTC'));
$fpTimestamp = $fpDate->getTimestamp();

if (!empty($this->getSignatureKey())) {
$hash = $this->generateSha2RequestSign(
(string)$this->getXLogin(),
(string)$this->getSignatureKey(),
(string)$this->getXAmount(),
(string)$this->getXCurrencyCode(),
(string)$this->getXFpSequence(),
$fpTimestamp
);
} else {
$hash = $this->generateRequestSign(
$this->getXLogin(),
$this->_getTransactionKey(),
$this->getXAmount(),
$this->getXCurrencyCode(),
$this->getXFpSequence(),
$fpTimestamp
);
}

$this->setXFpTimestamp($fpTimestamp);
$this->setXFpHash($hash);

return $this;
}

/**
* Generates the SHA2 fingerprint for request.
*
* @param string $merchantApiLoginId
* @param string $merchantSignatureKey
* @param string $amount
* @param string $currencyCode
* @param string $fpSequence An invoice number or random number.
* @param int $fpTimestamp
* @return string The fingerprint.
*/
private function generateSha2RequestSign(
string $merchantApiLoginId,
string $merchantSignatureKey,
string $amount,
string $currencyCode,
string $fpSequence,
int $fpTimestamp
): string {
$message = $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode;

return strtoupper(hash_hmac('sha512', $message, pack('H*', $merchantSignatureKey)));
}

/**
* Return merchant hexadecimal signature key.
*
* Needed to generate SHA2 sign.
*
* @return string
*/
private function getSignatureKey(): string
{
return $this->signatureKey;
}

/**
* Set merchant hexadecimal signature key.
*
* Needed to generate SHA2 sign.
*
* @param string $signatureKey
* @return void
*/
private function setSignatureKey(string $signatureKey)
{
$this->signatureKey = $signatureKey;
}
}
72 changes: 64 additions & 8 deletions app/code/Magento/Authorizenet/Model/Directpost/Response.php
Original file line number Diff line number Diff line change
@@ -27,25 +27,31 @@ class Response extends AuthorizenetResponse
*/
public function generateHash($merchantMd5, $merchantApiLogin, $amount, $transactionId)
{
if (!$amount) {
$amount = '0.00';
}

return strtoupper(md5($merchantMd5 . $merchantApiLogin . $transactionId . $amount));
}

/**
* Return if is valid order id.
*
* @param string $merchantMd5
* @param string $storedHash
* @param string $merchantApiLogin
* @return bool
*/
public function isValidHash($merchantMd5, $merchantApiLogin)
public function isValidHash($storedHash, $merchantApiLogin)
{
$hash = $this->generateHash($merchantMd5, $merchantApiLogin, $this->getXAmount(), $this->getXTransId());
if (empty($this->getData('x_amount'))) {
$this->setData('x_amount', '0.00');
}

return Security::compareStrings($hash, $this->getData('x_MD5_Hash'));
if (!empty($this->getData('x_SHA2_Hash'))) {
$hash = $this->generateSha2Hash($storedHash);
return Security::compareStrings($hash, $this->getData('x_SHA2_Hash'));
} elseif (!empty($this->getData('x_MD5_Hash'))) {
$hash = $this->generateHash($storedHash, $merchantApiLogin, $this->getXAmount(), $this->getXTransId());
return Security::compareStrings($hash, $this->getData('x_MD5_Hash'));
}

return false;
}

/**
@@ -57,4 +63,54 @@ public function isApproved()
{
return $this->getXResponseCode() == \Magento\Authorizenet\Model\Directpost::RESPONSE_CODE_APPROVED;
}

/**
* Generates an SHA2 hash to compare against AuthNet's.
*
* @param string $signatureKey
* @return string
* @see https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement
*/
private function generateSha2Hash(string $signatureKey): string
{
$hashFields = [
'x_trans_id',
'x_test_request',
'x_response_code',
'x_auth_code',
'x_cvv2_resp_code',
'x_cavv_response',
'x_avs_code',
'x_method',
'x_account_number',
'x_amount',
'x_company',
'x_first_name',
'x_last_name',
'x_address',
'x_city',
'x_state',
'x_zip',
'x_country',
'x_phone',
'x_fax',
'x_email',
'x_ship_to_company',
'x_ship_to_first_name',
'x_ship_to_last_name',
'x_ship_to_address',
'x_ship_to_city',
'x_ship_to_state',
'x_ship_to_zip',
'x_ship_to_country',
'x_invoice_num',
];

$message = '^';
foreach ($hashFields as $field) {
$message .= ($this->getData($field) ?? '') . '^';
}

return strtoupper(hash_hmac('sha512', $message, pack('H*', $signatureKey)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Authorizenet\Test\Unit\Model\Directpost;

use Magento\Authorizenet\Model\Directpost\Request;
use Magento\Framework\Intl\DateTimeFactory;
use PHPUnit_Framework_MockObject_MockObject as MockObject;

class RequestTest extends \PHPUnit\Framework\TestCase
{
/**
* @var DateTimeFactory|MockObject
*/
private $dateTimeFactory;

/**
* @var Request
*/
private $requestModel;

protected function setUp()
{
$this->dateTimeFactory = $this->getMockBuilder(DateTimeFactory::class)
->disableOriginalConstructor()
->getMock();
$dateTime = new \DateTime('2016-07-05 00:00:00', new \DateTimeZone('UTC'));
$this->dateTimeFactory->method('create')
->willReturn($dateTime);

$this->requestModel = new Request([], $this->dateTimeFactory);
}

/**
* @param string $signatureKey
* @param string $expectedHash
* @dataProvider signRequestDataProvider
*/
public function testSignRequestData(string $signatureKey, string $expectedHash)
{
/** @var \Magento\Authorizenet\Model\Directpost $paymentMethod */
$paymentMethod = $this->createMock(\Magento\Authorizenet\Model\Directpost::class);
$paymentMethod->method('getConfigData')
->willReturnMap(
[
['test', null, true],
['login', null, 'login'],
['trans_key', null, 'trans_key'],
['signature_key', null, $signatureKey],
]
);

$this->requestModel->setConstantData($paymentMethod);
$this->requestModel->signRequestData();
$signHash = $this->requestModel->getXFpHash();

$this->assertEquals($expectedHash, $signHash);
}

/**
* @return array
*/
public function signRequestDataProvider()
{
return [
[
'signatureKey' => '3EAFCE5697C1B4B9748385C1FCD29D86F3B9B41C7EED85A3A01DFF65' .
'70C8C29373C2A153355C3313CDF4AF723C0036DBF244A0821713A910024EE85547CEF37F',
'expectedHash' => '719ED94DF5CF3510CB5531E8115462C8F12CBCC8E917BD809E8D40B4FF06' .
'1E14953554403DD9813CCCE0F31B184EB4DEF558E9C0747505A0C25420372DB00BE1'
],
[
'signatureKey' => '',
'expectedHash' => '3656211f2c41d1e4c083606f326c0460'
],
];
}
}
Loading

0 comments on commit 7b1cd79

Please sign in to comment.