cancel
Showing results for 
Search instead for 
Did you mean: 

Working php hash verification

**If you find this helpful please kudo this post. This has consumed a huge chunk of my day and you will help me build credibility for when I enroll in the authorize.net certified developer program. **

 

Here is 100% tested, working php hash verification code for the php SDK. I believe this will also work with SIM/AIM, etc.

 

You need the following to have an apples to apples setup with what I used:

 

1: The most recent php SDK package from GitHub. I downloaded this today and installed. I believe it is a few days old.

 

2: If you have not generated a signature key from your production or sandbox merchant interface to use for testing, do so. You won’t get the hash in the response without it. Generate it and copy it for use in this script.

 

3: An API call script for some payment transaction that returns the hash. With the SDK I am getting this for voidTransaction, refundTransaction, capture, etc. I believe that any payment function that directly charges or affects a transaction will contain this. The Accept Hosted form API call obviously does not.

 

For requirement 1, the SIM/DPM, etc. users do not have this, if my understanding is correct. You should be able to use this as well, only substituting my method for extracting the transHashSha2 value from the response with however you accomplish this using your integration. You may also have to use different parameters in your delimited string, I would try this method first, but I have seen other developers posting attempts with more fields in the string than login, transId, and amount, and there is probably a good reason for this. 

 

 

Here is the code (p.s. do not follow the hyperlink to the C# byte array description and try to implement a php equivalent to the C# byte array script. This makes things 100X harder than they have to be, as I know well at this point. Without further delay…..)

 

 

$login = "copy and paste your merchant login id here";
$signatureKey ="copy and paste your signature key here";
$signatureKey = hex2bin($signatureKey);
$amount = $amount;

//$response stands for the response object returned by your API call
//e.g.  $response = refundTransaction($refTransId,$amount,$lastFour);

$transId = $response->getTransactionResponse()->getTransId();
$string = '^'.$login.'^'.$transId.'^'.$amount.'^';


$hash = $response->getTransactionResponse()->getTransHashSha2();
$digest = strtoupper(HASH_HMAC('sha512',$string,$signatureKey));

if(hash_equals ($digest,$hash)){

    //This if statement is the verification piece 
    //Put whatever you want your app to do with the transaction here
    //to test you can do something like echo "Hash verification validated";
    //or try this:
    //$dump = print_r($string,true);
    //$fp = file_put_contents( 'transhash.log', $dump );
    //and if your directory populates with a file named transhash.log you know 
    //verification succeeded
    

}

 

Renaissance
All Star
2 ACCEPTED SOLUTIONS

Accepted Solutions
Yes I was responding to @wenabar16.

It will work for any version of magento that exists, as long as you can edit the code appropriately. And again it is the second code example I posted. I do not use DPM/SIM so I never tested it. Others have tested pretty much identical code and theirs works. Follow the instructions on the code I posted. It looks like you use a different string depending on whether you pass the currency type. I am also not 100% sure if you convert to uppercase. That is also in the instructions on the code. Everything that starts with // is a comment or instructions.

I looked at that extension you posted. That is for accept.js. That’s a new method that has nothing to do with DPM. I am not sure if your timeframe for DPM is measured years or more or less. You will get a notification in advance for sure. If I were in your shoes I would get ahead of the curve and get your integration method upgraded ahead of time. The new stuff has a lot of capabilities. Accept.js is thr replacement for DPM. Give me a sec and I will try to find you the sample code that others posted that has been tested, before my food comes out.

View solution in original post

My original post (first post in this thread)  has the code for latest php SDK. Tested on API calls, should work with AIM but hasn't been tested. 

 

Here is the tested code for SIM/DPM.  Few notes: First SIM code I posted had not been tested and I in error put an extra ^ at the beginning of the string. The code below is correct and works. For the verification component, this is probably not the best way to do it but it has been tested and works. I do not use these integration methods but finally decided to write a script to test.   

 

 

//response verification code for DPM/SIM
//This code goes on your silent post URL


$anetResponse = file_get_contents('php://input');

$response = 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'=>''); $string = '^'; $responseCheck = explode('&',$anetResponse); foreach($responseCheck as $key=> $value){ $newKey = strstr($value,'=',true); $newVal = strstr($value,'='); $newVal = str_replace('=','',$newVal); $newVal = urldecode($newVal); if(array_key_exists($newKey,$response)){ $response[$newKey]= $newVal; } if($newKey=="x_SHA2_Hash"){ $hash = $newVal; } } foreach($response as $key => $value){ $string .= $value .='^'; } $signatureKey = "Copy and Paste Your Signature Key Here."; $signatureKey = hex2bin($signatureKey); $validation = strtoupper(HASH_HMAC('sha512',$string,$signatureKey)); if(hash_equals($hash,$validation)){ //Insert code to be executed if response //is validated here. } //end of response verification //sha512 transaction fingerprint for DPM, SIM date_default_timezone_set('UTC'); //^may not be necessary depending on your configuration $login = "Copy and Paste your Login Here"; $signatureKey = "Paste Signature Key Here"; $signatureKey = hex2bin($signatureKey); $amount = "43.23"; //or $amount = $amount //this assumes you have previously assigned the transaction //amount to a variable called $amount in your script $sequence = "123"; //you can use a variety of numbers //example in your docs uses 3 digit numbers $timeStamp = strtotime("now"); $currency = "USD"; //looks like that you only use this //if you specify currency type in your form request //you can use another value if you do things in a different currency //use one of the two strings below. $string = "$login^$sequence^$timeStamp^$amount^"; //the above is what you use if you don't submit //x_currency_code in your request $string2 = "$login^$sequence^$timeStamp^$amount^$currency"; //you use this if you specify currency

$digest = strtoupper(HASH_HMAC('sha512',$string,$signatureKey));

//or
$digest = strtoupper(HASH_HMAC('sha512',$string2,$signatureKey));
//this value is submitted in your request under "x_fp_hash" //Look in the SIM/DPM developer guide on for what "x_" to to use for $sequence, etc. //page 29.

 

View solution in original post

Renaissance
All Star
67 REPLIES 67

One question - 

I am also going to update md5 to sha512.
Currently, in my application I use (save at my end)- 

 

API Login ID - 

Transaction Key - 
MD5 - 

For sha512 - 

do I need transaction key or not? What will be the procedure to follow?

Thanks

Vikas_chauhan
Contributor

Renaissance, that's a good start at the problem!  However, some questions remain.

ISSUE #1
========
Authorize.Net's "Transaction Hash Upgrade Guide" (hereinafter, the Guide) makes no mention whatsoever of the fact that for MD5, Authorize.Net required TWO different methods for creating $string...
1) Generally, e.g. for SIM, one would use $string = $md5SecretText . $login . $transId . $amount;
2) However, for silent post / recurring billing, $string = $md5SecretText . $transId . $amount;

 

One could then obtain md5($string) and use one's favorite built-in PHP string comparison function to test if one's calculated md5 hash matches the x_md5_hash string allegedly sent from Authorize.Net.  The calculated md5 hashes (whether it's the one calculated by the merchant or the one sent by Authorize.net) both appear to be lower case hexadecimal strings.

For SHA512, I would guess that your post, in which you used
$string = '^'.$login.'^'.$transId.'^'.$amount.'^';
would cover case #1 above when transitioning to SHA512.  But I don't think you've tested it for case #2 (recurring billing).  Does it also work for case #2?  If so, Authorize.net should announce and confirm that they've changed more than just MD5 to SHA512 by requiring that $login be included in $string when verifying recurring billings.

ISSUE #2
========
Meanwhile, Authorize.net uses still another method for generating $string for fingerprints...
3) $string = $login . '^' . $sequence . '^' . $timeStamp . '^' . $amount . '^';
With SHA512, that stays the same when generating the $string for fingerprints.
And previously, with MD5, one could then generate the SIM fingerprint...
$fingerprint = HASH_HMAC('md5',$string,$transactionKey);
Now, with SHA512...
$fingerprint = HASH_HMAC('sha512',$string,$signatureKey);
It might look like simply changing "md5" to "sha512" and "$transactionKey" to "$signatureKey", but there's another difference...
And as we've been told, the "$signatureKey = hex2bin($signatureKey);" line of code is needed for SHA512, but a comparable line of code is not needed for the MD5 version.

Your solution does not include the changes required to upgrade the fingerprints from MD5 to SHA512.  I'm not sure that you intended to include that.  There is a post on upgrading fingerprints at https://support.authorize.net/s/article/Do-I-need-to-upgrade-my-transaction-fingerprint-from-HMAC-MD...

ISSUE #3
========
Apparently Authorize.Net has not updated their documentation to disclose where the SHA512 hash is positioned within the character delimited transaction response string.  For example, if the MD5 hash is being returned in the 38th element of the character delimited response, what is the position of the SHA512 hash?

karenb
Contributor

Still more issues...

 

ISSUE #4

=======

After the Signature Key has been created, the SHA512 hash is supposed to be sent to the merchant (along with the legacy MD5 hash for the time being) for each transaction.  And that's normally true.  However, it's not true for recurring billings.  For example, when a recurring billing was declined, Authorize.Net included only the MD5 hash.  The SHA512 hash element from Authorize.Net was empty.  Authorize.Net needs to correct its system to include the SHA512 hash with recurring billings.

Do you have the hash being sent in the transaction? Before I would pull the x_MD5_Hash parameter from the response, hash the string on my end and compare. 

 

Now I'm pulling transHashSha2 parameter from the response but it says null. Is this the same parameter you are pulling from the request or is it something else? Their API is crap in explaining this.

 

I already generated my Signature Key.

 

Edit: I found the parameter that I need to pull from the response. It is:

 

x_SHA2_Hash

 

 

Which is NO WHERE in their API or their schema. I had to query the parameters in the response myself. Why? Why? Why?

 

 

lightwave365
Regular Contributor

I am using following format for HASH_HMAC using sha512

 

$string = '^'.$login.'^'.$transId.'^'.$amount.'^';

I am getting x_MD5_Hash but it is not generating the equal hash value;

 

Please help me out

 

I found the parameter that I need to pull from the response. It is:

 

x_SHA2_Hash

 

My values don't match either but at least I have a damn value to compare against now.

Karen,

One at a time. The updating fingerprints is done simply by generating the signature key. Auth.net hashes the using the signature key on their end and you hash using it on your end. I just got up and am about to go get breakfast, when I’ll get there I’ll address more concerns.

And the link you posted explains why so many others were using different arguments in their hash function. For recurring billing, are you referring to the initial transaction that is created? I would imagine so.
So again the trend is that everyone posting about this is using deprecated methods.

Per the link from Karen, the fingerprint is generated like this-

The construction of the HMAC-SHA512 hash is similar to the HMAC-MD5 hash. In particular, the input to be hashed is built from these values, in order, and separated by carets ("^"):
The API Login ID (x_login);
The unique merchant-generated sequence number (x_fp_sequence);
The transaction's timestamp in UNIX Epoch time, i.e. how many seconds have passed since Midnight UTC on January 1, 1970 (x_fp_timestamp);
The transaction amount (x_amount);
The currency code (x_currency_code), which should be blank if no currency code is submitted.
For example, if we presume an API Login ID of "authnettest", a sequence number of "789", a timestamp of "67897654," an amount of "10.50", and no currency code, the hash input would look like this:
authnettest^789^67897654^10.50^

If a currency code of "USD" were submitted, the hash input would look like this:

authnettest^789^67897654^10.50^USD

You would then hash this input with the HMAC-SHA512 algorithm, using the binary-encoded Signature Key as the HMAC key.

The resulting hash should be submitted to us as x_fp_hash, just as you do with the HMAC-MD5 hash. We will know which hashing algorithm you used, by the size of the value for x_fp_hash


So you sumbit your hash under x_fp_hash in your request it appears.

RE: MD5 hash, the whole point of this is that you don’t use the MD5 value. Use the value lightwave posted.

My pancakes just got here.. I’ll put some more info up while I’m waiting on my check if I have time. It looks like the SIM, etc method has some quirks. Stay tuned for tomorrow’s breakfast or tonight’s dinner. I’ll look at the link provided by Karen and see what the docs about the deprecated methods say and see if I can help you out.

Renaissance
All Star

If someone will stack w/ generating verification of "x_SHA2_Hash" on "x_relay_url"...

 

According to this PDF: https://www.authorize.net/content/dam/authorize/documents/SIM_guide.pdf page 73, working code will be like this

 

<?php

$signatureKey = '128-bit-length-key-generated-in-account';

$requiredHash = $_POST['x_SHA2_Hash'];

$hashData = implode('^', [
    $_POST['x_trans_id'],
    $_POST['x_test_request'],
    $_POST['x_response_code'],
    $_POST['x_auth_code'],
    $_POST['x_cvv2_resp_code'],
    $_POST['x_cavv_response'],
    $_POST['x_avs_code'],
    $_POST['x_method'],
    $_POST['x_account_number'],
    $_POST['x_amount'],
    $_POST['x_company'],
    $_POST['x_first_name'],
    $_POST['x_last_name'],
    $_POST['x_address'],
    $_POST['x_city'],
    $_POST['x_state'],
    $_POST['x_zip'],
    $_POST['x_country'],
    $_POST['x_phone'],
    $_POST['x_fax'],
    $_POST['x_email'],
    $_POST['x_ship_to_company'],
    $_POST['x_ship_to_first_name'],
    $_POST['x_ship_to_last_name'],
    $_POST['x_ship_to_address'],
    $_POST['x_ship_to_city'],
    $_POST['x_ship_to_state'],
    $_POST['x_ship_to_zip'],
    $_POST['x_ship_to_country'],
    $_POST['x_invoice_num'],
]);

$hash = hash_hmac('sha512', $hashData, pack('H*', $signatureKey)); // If you running on >=PHP5.4 you can use "hex2bin" function instead of "pack" 
$hash = strtoupper($hash);

if (method_exists('hash_equals')) {
    $equals = hash_equals($requiredHash, $hash)
} else {
   $equals = $requiredHash === $hash;
}

var_dump($equals);

 

P.S. Dear authorize.net...Burn in hell with such updates and docs.

P.P.S Nice idea give ability to write comment and then on submit require to sign up

pudhgdfhgdf
Member