Calculate the IPN HASH signature
Overview
Using the IPN HASH signature is optional and it's only meant for source validation.
Availability
Available for all 2Checkout accounts.
Build the IPN HASH signature
- To build the HMAC_SHA source string, sent in IPN payload, you need to pre-pend each value (Sample value column in the Example table below) with its own length (Field length column in the Example table below) in bytes. You should use the same order for parameters as the one received in the payload. If you will change the order, the HMAC_SHA string will be different.
- Use 0 for null or empty values without prepending their length. However, when the value is 0 (zero), you do need to prepend its length (1).
- Note that for UTF-8 characters the length in bytes can be longer than the string length. When calculating the hash signature, you must use multibyte methods that return the number of bytes in a string, instead of methods that return the number of characters. Example: if using PHP, use the strlen method instead of length.
Each value from the body of the IPN call needs be included in the string in the exact same sequence as you received in the IPN payload. Also, this should match the HASH property of the IPN call body for the request to be considered valid, so you can verify that the request comes from our system.
Example
Field name | Field length | Sample value |
---|---|---|
SALEDATE | 19 | 2016-06-01 12:22:09 |
REFNO | 7 | 1000037 |
REFNOEXT | 0 | |
ORDERNO | 2 | 13 |
ORDERSTATUS | 8 | COMPLETE |
PAYMETHOD | 13 | Wire transfer |
FIRSTNAME | 4 | John |
LASTNAME | 5 | Smith |
COMPANY | 0 | |
REGISTRATIONNUMBER | 0 | |
FISCALCODE | 0 | |
CBANKNAME | 0 | |
CBANKACCOUNT | 0 | |
ADDRESS1 | 15 | 101 Main Street |
ADDRESS2 | 0 | |
CITY | 8 | New York |
STATE | 8 | New York |
ZIPCODE | 6 | 500365 |
COUNTRY | 24 | United States of America |
PHONE | 12 | 951-121-2121 |
FAX | 0 | |
CUSTOMEREMAIL | 19 | johnsmith@email.com |
FIRSTNAME_D | 4 | John |
LASTNAME_D | 5 | Smith |
COMPANY_D | 0 | |
ADDRESS1_D | 15 | 101 Main Street |
ADDRESS2_D | 0 | |
CITY_D | 8 | New York |
STATE_D | 8 | New York |
ZIPCODE_D | 6 | 500365 |
COUNTRY_D | 24 | United States of America |
PHONE_D | 12 | 951-121-2121 |
IPADDRESS | 14 | 213.233.121.50 |
CURRENCY | 3 | USD |
IPN_PID[0] | 1 | 1 |
IPN_PNAME[0] | 16 | Software program |
IPN_PCODE[0] | 5 | PM_11 |
IPN_INFO[0] | 0 | |
IPN_QTY[0] | 1 | 1 |
IPN_PRICE[0] | 5 | 29.00 |
IPN_VAT[0] | 4 | 0.00 |
IPN_VER[0] | 0 | |
IPN_DISCOUNT[0] | 4 | 0.00 |
IPN_PROMONAME[0] | 0 | |
IPN_DELIVEREDCODES[0] | 0 | |
IPN_TOTAL[0] | 5 | 29.00 |
IPN_TOTALGENERAL | 5 | 34.00 |
IPN_SHIPPING | 4 | 5.00 |
IPN_COMMISSION | 4 | 3.38 |
IPN_DATE | 14 | 20050303123434 |
TEST_ORDER | 1 | 1 |
2. Using the data in the example table you can calculate the following HMAC source string by prepending each length to each value, without adding any space that is not part of the value between them:
192016-06-01 12:22:097100003702138COMPLETE13Wire transfer4John5Smith9BV-66778800000015101 Main Street08New York8New York650036524United States of America12951-121-2121019johnsmith@email.com4John5Smith015101 Main Street08New York8New York650036524United States of America12951-121-212114213.233.121.503USD1116Software program5PM_11011529.0040.00040.0000529.00534.0045.0043.38142005030312343411
3. The Secret Key in this example is: AABBCCDDEEFF
To find your own Secret Key, log in to the Merchant Control Panel and navigate to Integrations → Webhooks & API. You can find the Secret Key in the API section, as shown in this image:
4. For this source string, the SHA-2 HASH value is:
d80f8520e989904df0d2b3caa710ba9907456ac6545eb75e357b10728234e495
For this source string, the SHA-3 HASH value is:
d0464d5712e893efc292be66ac6538bc4493706bd9deb43eae409142e848400e
Use the example below to test creating the IPN HASH and the response for the data supplied in this article.
PHP Hash Example
/* 2Checkout IPN HASH example */
/*
* possible values: sha256, sha3-256
* sha3-256 only for php version > 7.1
*/
$used_hash_algorithm = 'sha256';
/* pass to compute HASH. Retrieve your secret key by accessing https://secure.2checkout.com/cpanel/webhooks_api.php */
$secret_key = 'AABBCCDDEEFF';
date_default_timezone_set('UTC');
echo '<pre>';
//*********FUNCTIONS FOR HMAC*********
function serializeArray($array) {
$retval = "";
foreach ($array as $i => $value) {
if (is_array($value)) {
$retval .= serializeArray($value);
}
else {
$size = strlen($value);
$retval .= $size . $value;
}
}
return $retval;
}
//PARAMETERS
$IPN_parameters = array();
$IPN_parameters['SALEDATE'] = '2016-06-01 12:22:09';
$IPN_parameters['REFNO'] = '1000037';
$IPN_parameters['REFNOEXT'] = '';
$IPN_parameters['ORDERNO'] = '13';
$IPN_parameters['ORDERSTATUS'] = 'COMPLETE';
$IPN_parameters['PAYMETHOD'] = 'Wire transfer';
$IPN_parameters['FIRSTNAME'] = 'John';
$IPN_parameters['LASTNAME'] = 'Smith';
$IPN_parameters['COMPANY'] = '';
$IPN_parameters['REGISTRATIONNUMBER'] = '';
$IPN_parameters['FISCALCODE'] = '';
$IPN_parameters['CBANKNAME'] = '';
$IPN_parameters['CBANKACCOUNT'] = '';
$IPN_parameters['ADDRESS1'] = '101 Main Street';
$IPN_parameters['ADDRESS2'] = '';
$IPN_parameters['CITY'] = 'New York';
$IPN_parameters['STATE'] = 'New York';
$IPN_parameters['ZIPCODE'] = '500365';
$IPN_parameters['COUNTRY'] = 'United States of America';
$IPN_parameters['PHONE'] = '951-121-2121';
$IPN_parameters['FAX'] = '';
$IPN_parameters['CUSTOMEREMAIL'] = 'johnsmith@email.com';
$IPN_parameters['FIRSTNAME_D'] = 'John';
$IPN_parameters['LASTNAME_D'] = 'Smith';
$IPN_parameters['COMPANY_D'] = '';
$IPN_parameters['ADDRESS1_D'] = '101 Main Street';
$IPN_parameters['ADDRESS2_D'] = '';
$IPN_parameters['CITY_D'] = 'New York';
$IPN_parameters['STATE_D'] = 'New York';
$IPN_parameters['ZIPCODE_D'] = '500365';
$IPN_parameters['COUNTRY_D'] = 'United States of America';
$IPN_parameters['PHONE_D'] = '951-121-2121';
$IPN_parameters['IPADDRESS'] = '213.233.121.50';
$IPN_parameters['CURRENCY'] = 'USD';
$IPN_parameters['IPN_PID'][0] = '1';
$IPN_parameters['IPN_PNAME'][0] = 'Software program';
$IPN_parameters['IPN_PCODE'][0] = 'PM_11';
$IPN_parameters['IPN_INFO'][0] = '';
$IPN_parameters['IPN_QTY'][0] = '1';
$IPN_parameters['IPN_PRICE'][0] = '29.00';
$IPN_parameters['IPN_VAT'][0] = '0.00';
$IPN_parameters['IPN_VER'][0] = '';
$IPN_parameters['IPN_DISCOUNT'][0] = '0.00';
$IPN_parameters['IPN_PROMONAME'][0] = '';
$IPN_parameters['IPN_DELIVEREDCODES'][0] = '';
$IPN_parameters['IPN_TOTAL'][0] = '29.00';
$IPN_parameters['IPN_TOTALGENERAL'] = '34.00';
$IPN_parameters['IPN_SHIPPING'] = '5.00';
$IPN_parameters['IPN_COMMISSION'] = '3.38';
$IPN_parameters['IPN_DATE'] = '20050303123434';
$IPN_parameters['TEST_ORDER'] = '1';
//*********Base string for SHA2-256/SHA3-256 calculation:*********
echo "This is the base string for SHA2-256/SHA3-256 calculation: ";
$result = '';
foreach ($IPN_parameters as $key => $val){
$result .= serializeArray((array)$val);
}
var_dump($result);
//*********Calculated SHA2-256/SHA3-256 signature:*********
switch ($used_hash_algorithm) {
case 'sha256':
echo "This is the SHA2-256 signature: ";
$hash = hash_hmac('sha256', $result, $secret_key);
$IPN_parameters['SIGNATURE_SHA2_256'] = $hash;
var_dump($hash);
break;
case 'sha3-256':
echo "This is the SHA3-256 signature: ";
$hash = hash_hmac('sha3-256', $result, $secret_key);
$IPN_parameters['SIGNATURE_SHA3_256'] = $hash;
var_dump($hash);
break;
}
PHP Hash Response Example
$IPN_parameters_response = array();
$IPN_parameters_response['IPN_PID'][0] = '1';
$IPN_parameters_response['IPN_PNAME'][0] = 'Software program';
$IPN_parameters_response['IPN_DATE'] = '20050303123434';
$IPN_parameters_response['DATE'] = '20050303123434';
//*********Response base string for SHA2-256/SHA3-256 calculation:*********
echo "This is the response base string for SHA2-256/SHA3-256 calculation: ";
$result_response = '';
foreach ($IPN_parameters_response as $key => $val){
$result_response .= serializeArray((array)$val);
}
var_dump($result_response);
//*********Calculated response SHA2-256/SHA3-256 signature:*********
$responseString = '';
switch ($used_hash_algorithm) {
case 'sha256':
echo "This is the response SHA2-256 signature: ";
$hash = hash_hmac('sha256', $result_response, $secret_key);
var_dump($hash);
$responseString = '<sig algo="sha256" date="' . $IPN_parameters_response['DATE'] . '">' . $hash . '</sig>' . PHP_EOL;
break;
case 'sha3-256':
echo "This is the response SHA3-256 signature: ";
$hash = hash_hmac('sha3-256', $result_response, $secret_key);
var_dump($hash);
$responseString = '<sig algo="sha3-256" date="' . $IPN_parameters_response['DATE'] . '">' . $hash . '</sig>' . PHP_EOL;
break;
}
//Expected response
echo 'Expected response:' . PHP_EOL . $responseString;
Validation
To validate the request and create the HMAC hash string you can use the below sample:
Node.JS (ES6) sample
let hashString = '';
let valueLengthInBytes;
function byteLength(str) {
let s = str.length;
for (let i = str.length-1; i>=0; i--) {
var code = str.charCodeAt(i);
if (code > 0x7f && code <= 0x7ff) s++;
else if (code > 0x7ff && code <= 0xffff) s+=2;
if (code >= 0xDC00 && code <= 0xDFFF) i--;
}
return s;
}
Object.keys(request.params).forEach(function(key) {
valueLengthInBytes = byteLength(request.params[key].toString());
if (valueLengthInBytes > 0) {
hashString += valueLengthInBytes + request.params[key].toString();
}
});
Python sample (Flask)
from urllib import request
from flask import Flask, jsonify, request, Request
from urllib.parse import urlencode, urldefrag
from werkzeug.datastructures import ImmutableOrderedMultiDict
class MyRequest(Request):
parameter_storage_class = ImmutableOrderedMultiDict
class MyFlask(Flask):
request_class = MyRequest
app = MyFlask(__name__)
def bytes_length(string):
return len(string.encode('utf-8'))
def calculate_hash_string(payload_tuple_list):
hash_string = ''
for payload_key in payload_tuple_list:
payload_value = payload_tuple_list[payload_key]
bytes = bytes_length(payload_value)
if bytes > 0:
hash_string = hash_string + str(bytes) + payload_value
return hash_string
@app.route('/ipn', methods=['POST'])
def ipn():
ipn_payload_received = request.form
return calculate_hash_string(ipn_payload_received)
if __name__ == '__main__':
app.run()
Verifone IPN send request sample
For the parameters listed in the table below we will have the following request sample:
POST /vg8NfWXNBmaW8Lrsarfu HTTP/1.1
Host: putsreq.com
Content-Type: application/x-www-form-urlencoded
Cookie: __cfduid=d9526a7dbe99fe081deef1ae1940420891612782045;
owner_token=9631c27fbfa38e7da850137e6c23f7cceba0450debcd834a
Content-Length: 218
GIFT_ORDER=0&SALEDATE=2021-02-04 09:11:30&PAYMENTDATE=2021-02-04
09:14:53&REFNO=11758694&REFNOEXT=&SHOPPER_REFERENCE_NUMBER
=&IPCOUNTRY=&CURRENCY=USD&IPN_PID[]=30969748&IPN_PNAME[]=Antivi
rus 2021&IPN_DATE=20210208063206
IPN Parameter | Value |
---|---|
GIFT_ORDER | 0 |
SALEDATE | 2021-02-04 09:11:30 |
PAYMENTDATE | 2021-02-04 09:14:53 |
REFNO | 11758694 |
REFNOEXT | |
SHOPPER_REFERENCE_NUMBER | |
IPCOUNTRY | |
CURRENCY | |
IPN_PID[] | 30969748 |
IPN_PNAME[] | Product name |
IPN_DATE | 20210208063206 |