Skip to content

Instantly share code, notes, and snippets.

@robolmos
Created March 4, 2019 20:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robolmos/7a3bf336516b18f8e0bd48a13d93e1ef to your computer and use it in GitHub Desktop.
Save robolmos/7a3bf336516b18f8e0bd48a13d93e1ef to your computer and use it in GitHub Desktop.
Magento Auth.Net MD5 DPM composer-based patch with truncated paths for application via cweagans/composer-patches
Index: Model/Directpost.php
<+>UTF-8
===================================================================
+++ Model/Directpost.php (revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
--- Model/Directpost.php (revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
@@ -543,15 +543,16 @@
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;
}
Index: Model/Directpost/Request.php
<+>UTF-8
===================================================================
--- Model/Directpost/Request.php (revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ Model/Directpost/Request.php (revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -7,6 +7,7 @@
namespace Magento\Authorizenet\Model\Directpost;
use Magento\Authorizenet\Model\Request as AuthorizenetRequest;
+use Magento\Framework\Intl\DateTimeFactory;
/**
* Authorize.net request model for DirectPost model
@@ -18,9 +19,33 @@
*/
protected $_transKey = null;
+ /**
+ * Hexadecimal signature key.
+ *
+ * @var string
+ */
+ private $signatureKey = '';
+
+ /**
+ * @var DateTimeFactory
+ */
+ private $dateTimeFactory;
+
+ /**
+ * @param DateTimeFactory $dateTimeFactory
+ * @param array $data
+ */
+ public function __construct(
+ DateTimeFactory $dateTimeFactory,
+ array $data = []
+ ) {
+ $this->dateTimeFactory = $dateTimeFactory;
+ parent::__construct($data);
+ }
+
/**
* Return merchant transaction key.
- * Needed to generate sign.
+ * Needed to generate MD5 sign.
*
* @return string
*/
@@ -31,7 +56,7 @@
/**
* Set merchant transaction key.
- * Needed to generate sign.
+ * Needed to generate MD5 sign.
*
* @param string $transKey
* @return $this
@@ -43,7 +68,7 @@
}
/**
- * Generates the fingerprint for request.
+ * Generates the MD5 fingerprint for request.
*
* @param string $merchantApiLoginId
* @param string $merchantTransactionKey
@@ -63,7 +88,7 @@
) {
return hash_hmac(
"md5",
- $merchantApiLoginId . "^" . $fpSequence . "^" . $fpTimestamp . "^" . $amount . "^" . $currencyCode,
+ $merchantApiLoginId . '^' . $fpSequence . '^' . $fpTimestamp . '^' . $amount . '^' . $currencyCode,
$merchantTransactionKey
);
}
@@ -85,6 +110,7 @@
->setXRelayUrl($paymentMethod->getRelayUrl());
$this->_setTransactionKey($paymentMethod->getConfigData('trans_key'));
+ $this->setSignatureKey($paymentMethod->getConfigData('signature_key'));
return $this;
}
@@ -168,17 +194,81 @@
*/
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(
+ $this->getXLogin(),
+ $this->getSignatureKey(),
+ $this->getXAmount(),
+ $this->getXCurrencyCode(),
+ $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 string $fpTimestamp
+ * @return string The fingerprint.
+ */
+ private function generateSha2RequestSign(
+ $merchantApiLoginId,
+ $merchantSignatureKey,
+ $amount,
+ $currencyCode,
+ $fpSequence,
+ $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;
+ }
}
Index: Model/Directpost/Response.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- Model/Directpost/Response.php (revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ Model/Directpost/Response.php (revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -24,27 +24,33 @@
*/
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;
+ }
+
/**
* Return if this is approved response from Authorize.net auth request.
*
@@ -54,4 +60,54 @@
{
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)));
+ }
}
Index: etc/adminhtml/system.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- etc/adminhtml/system.xml (revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ etc/adminhtml/system.xml (revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -29,6 +29,10 @@
<label>Transaction Key</label>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
</field>
+ <field id="signature_key" translate="label" type="obscure" sortOrder="55" showInDefault="1" showInWebsite="1" showInStore="0">
+ <label>Signature Key</label>
+ <backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
+ </field>
<field id="trans_md5" translate="label" type="obscure" sortOrder="60" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Merchant MD5</label>
<backend_model>Magento\Config\Model\Config\Backend\Encrypted</backend_model>
Index: etc/config.xml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- etc/config.xml (revision 01fbfeba9bd743266199e30260376a9c1b95fbf5)
+++ etc/config.xml (revision a0dade4d8bc15b651d8b1ea0e7e4e3580fb1d3ae)
@@ -22,6 +22,7 @@
<title>Credit Card Direct Post (Authorize.net)</title>
<trans_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
<trans_md5 backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
+ <signature_key backend_model="Magento\Config\Model\Config\Backend\Encrypted" />
<allowspecific>0</allowspecific>
<currency>USD</currency>
<create_order_before>1</create_order_before>
@treii28
Copy link

treii28 commented Mar 11, 2019

FYI, to use a patch file like this with cweagan's plugin, be sure to prefix the paths with "a/" and "b/" (original and changed respectively)

This patch was also linked as the correct patch for v2.2.4 of Magento 2 but the DirectPost.php content is offset by 12 lines. (the first patched section actually begins at line 531, not line 543)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment