cancel
Showing results for 
Search instead for 
Did you mean: 

Help with SIM Relay Response after switching to SHA512

I have a very old and large php (5.3) site that uses A.Net SIM and Relay Response for CC processing.  I have managed to update the code which submits the transaction to A.Net and transactions are successfully occuring.  The problem is my code in the Relay Response URL which pulls the A.Net response and parses it so that the correct processing can occur.

 

I last worked on my A.Net integration 6 years ago, and my Relay reponse logic starts with the following call which is obviously no longer working.

 

 $response = new AuthorizeNetSIM($api_login_id, $md5_setting);

 

The AuthorizeNetSIM class is contained in the A.Net SDK from that time which is stored on my webserver.  Where do I find a replacement for this code?  Or if there is no simple replacement using SHA-512, what do I use instead to receive the response so I can parse it to determine the necessary logic?  Note that if I attempt to upgrade to Php 5.6 or higher, my entire site stops working.  Thus, I am constrained since I do not have time to upgrade my entire site to a newer php before md5 ends and my organization's credit card processing comes crashing down.

 

Any assistance would be greatly appreciated.

drsdouglas
Member
1 ACCEPTED SOLUTION

Accepted Solutions

@drsdouglas 

 

I sent you an IM. Does your class file have code in it that looks like this-

 

class AuthorizeNetSIM extends AuthorizeNetResponse
{

    // For ARB transactions
    public $subscription_id;
    public $subscription_paynum;

    /**
     * Constructor.
     *
     * @param string $api_login_id
     * @param string $md5_setting For verifying an Authorize.Net message.
     */
    public function __construct($api_login_id = false, $md5_setting = false)
    {
        $this->api_login_id = ($api_login_id ? $api_login_id : (defined('AUTHORIZENET_API_LOGIN_ID') ? AUTHORIZENET_API_LOGIN_ID : ""));
        $this->md5_setting = ($md5_setting ? $md5_setting : (defined('AUTHORIZENET_MD5_SETTING') ? AUTHORIZENET_MD5_SETTING : ""));
        $this->response = $_POST;
        
        // Set fields without x_ prefix
        foreach ($_POST as $key => $value) {
            $name = substr($key, 2);
            $this->$name = $value;
        }
        
        // Set some human readable fields
        $map = array(
            'invoice_number' => 'x_invoice_num',
            'transaction_type' => 'x_type',
            'zip_code' => 'x_zip',
            'email_address' => 'x_email',
            'ship_to_zip_code' => 'x_ship_to_zip',
            'account_number' => 'x_account_number',
            'avs_response' => 'x_avs_code',
            'authorization_code' => 'x_auth_code',
            'transaction_id' => 'x_trans_id',
            'customer_id' => 'x_cust_id',
            'md5_hash' => 'x_MD5_Hash',
            'card_code_response' => 'x_cvv2_resp_code',
            'cavv_response' => 'x_cavv_response',
        );
        foreach ($map as $key => $value) {
            $this->$key = (isset($_POST[$value]) ? $_POST[$value] : "");
        }
        
        $this->approved = ($this->response_code == self::APPROVED);
        $this->declined = ($this->response_code == self::DECLINED);
        $this->error    = ($this->response_code == self::ERROR);
        $this->held     = ($this->response_code == self::HELD);
    }
    
    /**
     * Verify the request is AuthorizeNet.
     *
     * @return bool
     */
    public function isAuthorizeNet()
    {
        return count($_POST) && $this->md5_hash && ($this->generateHash() == $this->md5_hash);
    }
    
    /**
     * Generates an Md5 hash to compare against Authorize.Net's.
     *
     * @return string Hash
     */
    public function generateHash()
    {
        $amount = ($this->amount ? $this->amount : "0.00");
        return strtoupper(md5($this->md5_setting . $this->api_login_id . $this->transaction_id . $amount));
    }

}

 

If so, change the above code to this-

 

class AuthorizeNetSIM extends AuthorizeNetResponse
{

    // For ARB transactions
    public $subscription_id;
    public $subscription_paynum;

    /**
     * Constructor.
     
     */
    public function __construct($signatureKey)
    {
         $this->signatureKey = hex2bin($signatureKey);
         $this->response = $_POST;
        
        // Set fields without x_ prefix
        foreach ($_POST as $key => $value) {
            $name = substr($key, 2);
            $this->$name = $value;
        }
        
        // Set some human readable fields
        $map = array(
            'invoice_number' => 'x_invoice_num',
            'transaction_type' => 'x_type',
            'zip_code' => 'x_zip',
            'email_address' => 'x_email',
            'ship_to_zip_code' => 'x_ship_to_zip',
            'account_number' => 'x_account_number',
            'avs_response' => 'x_avs_code',
            'authorization_code' => 'x_auth_code',
            'transaction_id' => 'x_trans_id',
            'customer_id' => 'x_cust_id',
            'md5_hash' => 'x_MD5_Hash',
            'card_code_response' => 'x_cvv2_resp_code',
            'cavv_response' => 'x_cavv_response',
            'sha512_hash'=>'x_SHA2_Hash',
        );
        foreach ($map as $key => $value) {
            $this->$key = (isset($_POST[$value]) ? $_POST[$value] : "");
            
        }
        
            
        
        
        
        
        $this->approved = ($this->response_code == self::APPROVED);
        $this->declined = ($this->response_code == self::DECLINED);
        $this->error    = ($this->response_code == self::ERROR);
        $this->held     = ($this->response_code == self::HELD);
    }
    
    /**
     * Verify the request is AuthorizeNet.
     *
     * @return bool
     */
    public function isAuthorizeNet()
    {
        return count($_POST) && $this->sha512_hash && ($this->generateHash() == $this->sha512_hash);
    }
    
    /**
     * Generates a sha512 hash to compare against Authorize.Net's.
     *
     * @return string Hash
     */
    public function generateHash()
    
    {
        $string = "^";
        
        $fields = array(
    
            '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'=>'');
        
            foreach($fields as $key => $value){ 
            $string .= $_POST[$key] .='^';

            }
    
        return strtoupper(HASH_HMAC('sha512',$string,$this->signatureKey));
    }

}

 

Then change your response var as below-

 

 this:

$response = new AuthorizeNetSIM($api_login_id, $md5_setting); 

needs to be replaced with:

$response = new AuthorizeNetSim($signatureKey);

where $signatureKey is the signature key you get
from the merchant interface

 

And if it works, please kindly mark this thread as solved.  

 

View solution in original post

8 REPLIES 8
@drsdouglas

I would alter that function to be new AuthorizeNetSIM($signaturekey); Then I would find the part of the code in that class where the API login and md5 setting are passed. That is where your hash verification is happening. I would change that code to something like the SIM code in solution to the thread working PHP hash verification.

Post the code for that entire class and if I have time at breakfast tomorrow I will edit it and repost here. For now my pancakes just got here and good luck.
Renaissance
All Star

@drsdouglas 

 

I sent you an IM. Does your class file have code in it that looks like this-

 

class AuthorizeNetSIM extends AuthorizeNetResponse
{

    // For ARB transactions
    public $subscription_id;
    public $subscription_paynum;

    /**
     * Constructor.
     *
     * @param string $api_login_id
     * @param string $md5_setting For verifying an Authorize.Net message.
     */
    public function __construct($api_login_id = false, $md5_setting = false)
    {
        $this->api_login_id = ($api_login_id ? $api_login_id : (defined('AUTHORIZENET_API_LOGIN_ID') ? AUTHORIZENET_API_LOGIN_ID : ""));
        $this->md5_setting = ($md5_setting ? $md5_setting : (defined('AUTHORIZENET_MD5_SETTING') ? AUTHORIZENET_MD5_SETTING : ""));
        $this->response = $_POST;
        
        // Set fields without x_ prefix
        foreach ($_POST as $key => $value) {
            $name = substr($key, 2);
            $this->$name = $value;
        }
        
        // Set some human readable fields
        $map = array(
            'invoice_number' => 'x_invoice_num',
            'transaction_type' => 'x_type',
            'zip_code' => 'x_zip',
            'email_address' => 'x_email',
            'ship_to_zip_code' => 'x_ship_to_zip',
            'account_number' => 'x_account_number',
            'avs_response' => 'x_avs_code',
            'authorization_code' => 'x_auth_code',
            'transaction_id' => 'x_trans_id',
            'customer_id' => 'x_cust_id',
            'md5_hash' => 'x_MD5_Hash',
            'card_code_response' => 'x_cvv2_resp_code',
            'cavv_response' => 'x_cavv_response',
        );
        foreach ($map as $key => $value) {
            $this->$key = (isset($_POST[$value]) ? $_POST[$value] : "");
        }
        
        $this->approved = ($this->response_code == self::APPROVED);
        $this->declined = ($this->response_code == self::DECLINED);
        $this->error    = ($this->response_code == self::ERROR);
        $this->held     = ($this->response_code == self::HELD);
    }
    
    /**
     * Verify the request is AuthorizeNet.
     *
     * @return bool
     */
    public function isAuthorizeNet()
    {
        return count($_POST) && $this->md5_hash && ($this->generateHash() == $this->md5_hash);
    }
    
    /**
     * Generates an Md5 hash to compare against Authorize.Net's.
     *
     * @return string Hash
     */
    public function generateHash()
    {
        $amount = ($this->amount ? $this->amount : "0.00");
        return strtoupper(md5($this->md5_setting . $this->api_login_id . $this->transaction_id . $amount));
    }

}

 

If so, change the above code to this-

 

class AuthorizeNetSIM extends AuthorizeNetResponse
{

    // For ARB transactions
    public $subscription_id;
    public $subscription_paynum;

    /**
     * Constructor.
     
     */
    public function __construct($signatureKey)
    {
         $this->signatureKey = hex2bin($signatureKey);
         $this->response = $_POST;
        
        // Set fields without x_ prefix
        foreach ($_POST as $key => $value) {
            $name = substr($key, 2);
            $this->$name = $value;
        }
        
        // Set some human readable fields
        $map = array(
            'invoice_number' => 'x_invoice_num',
            'transaction_type' => 'x_type',
            'zip_code' => 'x_zip',
            'email_address' => 'x_email',
            'ship_to_zip_code' => 'x_ship_to_zip',
            'account_number' => 'x_account_number',
            'avs_response' => 'x_avs_code',
            'authorization_code' => 'x_auth_code',
            'transaction_id' => 'x_trans_id',
            'customer_id' => 'x_cust_id',
            'md5_hash' => 'x_MD5_Hash',
            'card_code_response' => 'x_cvv2_resp_code',
            'cavv_response' => 'x_cavv_response',
            'sha512_hash'=>'x_SHA2_Hash',
        );
        foreach ($map as $key => $value) {
            $this->$key = (isset($_POST[$value]) ? $_POST[$value] : "");
            
        }
        
            
        
        
        
        
        $this->approved = ($this->response_code == self::APPROVED);
        $this->declined = ($this->response_code == self::DECLINED);
        $this->error    = ($this->response_code == self::ERROR);
        $this->held     = ($this->response_code == self::HELD);
    }
    
    /**
     * Verify the request is AuthorizeNet.
     *
     * @return bool
     */
    public function isAuthorizeNet()
    {
        return count($_POST) && $this->sha512_hash && ($this->generateHash() == $this->sha512_hash);
    }
    
    /**
     * Generates a sha512 hash to compare against Authorize.Net's.
     *
     * @return string Hash
     */
    public function generateHash()
    
    {
        $string = "^";
        
        $fields = array(
    
            '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'=>'');
        
            foreach($fields as $key => $value){ 
            $string .= $_POST[$key] .='^';

            }
    
        return strtoupper(HASH_HMAC('sha512',$string,$this->signatureKey));
    }

}

 

Then change your response var as below-

 

 this:

$response = new AuthorizeNetSIM($api_login_id, $md5_setting); 

needs to be replaced with:

$response = new AuthorizeNetSim($signatureKey);

where $signatureKey is the signature key you get
from the merchant interface

 

And if it works, please kindly mark this thread as solved.  

 

That works perfectly!  Thank you so much!  You saved me hours upon hours of work.

I tried using this but the function hex2bin does not return the binary value correctly. I also tried alternate methods to convert to binary (http://phprevolution.blogspot.com/2012/06/convert-hexadecimal-to-binary-string-in.html). That converts to binary but still the SHA values do not match. Could you please let me know if I am missing something?

 

The only change I did to the code you have given is that I removed some keys from $fields array in generateHash() which does not have POST values

 

NOTE: I am doing this with an sandbox account

Kalai
Member
@Kalai
All 30 fields should be left in. The value will be an empty string if not submitted by the customer. That is for sure why it doesn’t work.

Thanks. I tried it but does not generate matching SHA value. There are a lot of links related to generating the SHA value and they contradict with the values used in generating the SHA.

 

https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement - Matches with what you have told but still does not generate the correct SHA

 

https://www.authorize.net/content/dam/authorize/documents/SIM_guide.pdf - Contains less fields (refer page 73) which I believe is old document. This too does not work.

 

https://github.com/AuthorizeNet/sample-code-php/blob/master/Sha512/compute_trans_hashSHA2.php and https://developer.authorize.net/support/hash_upgrade/- Completely different fields but this too does not generate matching SHA

 

https://community.developer.authorize.net/t5/Integration-and-Testing/Coldfusion-SIM-HMAC-SHA512-Upda... and https://support.authorize.net/s/article/Do-I-need-to-upgrade-my-transaction-fingerprint-from-HMAC-MD... - This also does not work as many of the fields like x_login,x_fp_sequence,x_fp_timestamp,x_currency_code does not come in POST

 

Is this because we are using SIM/DPM which is old method? 

 

I can see the below statement in https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement which bring me to the question if it is really needed to compare the SHA value?

 

"The SHA2 has field contains HMAC-SHA512 hash that Authorize.Net generated for the transaction and can be used to validate the response came from Authorize.Net but is not required to do so."

 

Please advise since I am completely blocked here.

 

I hope the behavior does not vary between LIVE and Sandbox environments

@Kalai

Did you have a class file that has code like the above? That’s is what that code is for,
For people that use that class file. If you don’t use the class file then copying and pasting the code will not work.

SIM/DPM use different fields. You have a signature which and you have a response validation. Signature is not optional but at least for now you can use md5. Response verification is optional but exposes you to security problems if not implemented.

Post your original code from before you tried to copy and paste what I put here. Also, do you do business internationally? If your customers use diacritic characters that will cause your hash not to match.

Found the root cause of the issue. Looks like hex2bin  is available only in PHP >5.4 and we were having  5.3.3. Hence had to use a alternative as given in https://www.php.net/manual/en/function.hex2bin.php#110973

 

It would be better if you could you please update the same in your documentation so that others who implement this check their PHP version and implement accodingly.