- Overview
- Important Dates
- What Is a Hash Code and Why Should I Use It
- Automated Migration
- Before The Transaction: Generating A Hash Code (Version 2.0.0)
- Post-Transaction: Verifying The Result
Overview
This article covers utilizing NMI's eKashu Payment Page Hash Code version 2.0.0 feature to validate transaction results mitigating man-in-the-middle attacks. This article assumes a Hash Key
has already been generated against the terminal. If this isn't the case, please ensure you reach out to our Support teams and request a Hash Key be generated against your terminal(s).
We could encourage all integrators to upgrade to Hash Code version 2.0.0 (SHA256HMAC) as it provides additional security over version 1.0.0 (SHA1).
Important Dates
- 7th October 2020
- Staging: All new accounts will have Hash Code version 2.0.0 enabled by default.
- 6th April 2021
- Staging: All existing integrations will have Hash Code version 2.0.0 enabled.
- Production: All new accounts will have Hash Code version 2.0.0 enabled by default.
- 6th December 2021
- Production: All existing integrations will have Hash Code version 2.0.0 enabled.
What Is a Hash Code and Why Should I Use It
A Hash Code is a fixed-length value that can be used to verify data integrity, or in the instance of NMI's eKashu Payment Page, be used to validate the transaction data received by the integration post-transaction, mitigating man-in-the-middle attacks.
Using the Hash Key
assigned to the Terminal ID sending the request, a Hash Code is generated against a set of critical eKashu parameters. This code is then sent to NMI during the transaction request and another unique Hash Code is returned in the transaction results that is then used to validate the results.
Automated Migration
An automatic terminal upgrade will be triggered when a terminal that is currently set up to use version 1.0.0 provides a valid version 2.0.0 hash code. Once an account has been successfully upgraded, the account will be forbidden from submitting a version 1.0.0 hash for any future transactions, meaning that any subsequent requests must supply a valid version 2.0.0 hash. Terminals with no Hash Key assigned to them cannot use this method to upgrade.
Before The Transaction: Generating A Hash Code (Version 2.0.0)
Please note that values sent in the request and used during generation of the hash code should not have leading or trailing whitespaces.
C#
using System; using System.Collections.Generic; using System.Security.Cryptography; using System.Text; public class Program { public static void Main() { const string hashKey = "trVxrnoz22bvwvnV"; SortedDictionary<string, string> hashcodeInput = new SortedDictionary<string, string> { { "ekashu_3d_secure_verify", null }, { "ekashu_amount", "1.23" }, { "ekashu_amount_format", null }, { "ekashu_auto_confirm", null }, { "ekashu_callback_failure_url", null }, { "ekashu_callback_include_post", null }, { "ekashu_callback_success_url", null }, { "ekashu_card_address_editable", null }, { "ekashu_card_address_required", null }, { "ekashu_card_address_verify", null }, { "ekashu_card_email_address_mandatory", null }, { "ekashu_card_phone_number_mandatory", null }, { "ekashu_card_title_mandatory", null }, { "ekashu_card_zip_code_verify", null }, { "ekashu_currency", "GBP" }, { "ekashu_delivery_address_editable", null }, { "ekashu_delivery_address_required", null }, { "ekashu_delivery_email_address_mandatory", null }, { "ekashu_delivery_phone_number_mandatory", null }, { "ekashu_delivery_title_mandatory", null }, { "ekashu_description", null }, { "ekashu_device", null }, { "ekashu_duplicate_check", null }, { "ekashu_duplicate_minutes", null }, { "ekashu_failure_return_text", null }, { "ekashu_failure_url", null }, { "ekashu_hash_code_format", null }, { "ekashu_hash_code_type", "SHA256HMAC" }, { "ekashu_hash_code_version", "2.0.0" }, { "ekashu_include_post", null }, { "ekashu_invoice_address_editable", null }, { "ekashu_invoice_address_required", null }, { "ekashu_invoice_email_address_mandatory", null }, { "ekashu_invoice_phone_number_mandatory", null }, { "ekashu_invoice_title_mandatory", null }, { "ekashu_locale", null }, { "ekashu_payment_methods", null }, { "ekashu_reference", "0000000765" }, { "ekashu_request_type", null }, { "ekashu_return_text", null }, { "ekashu_seller_address", null }, { "ekashu_seller_email_address", null }, { "ekashu_seller_id", "99999999" }, { "ekashu_seller_key", "kIhy2V81" }, { "ekashu_seller_name", null }, { "ekashu_shortcut_icon", null }, { "ekashu_style_sheet", null }, { "ekashu_success_url", null }, { "ekashu_title", null }, { "ekashu_verification_value_mask", null }, { "ekashu_verification_value_verify", null }, { "ekashu_viewport", null }, }; byte[] hashcodeInputBytes = Encoding.UTF8.GetBytes(string.Join("&", hashcodeInput.Values)); using HMACSHA256 hmac = new HMACSHA256(Encoding.UTF8.GetBytes(hashKey)); byte[] hash = hmac.ComputeHash(hashcodeInputBytes); // ht3mNZVxrX+jUlN6C99ufuf3g/gfGY5JmXwCBi7nbq0= Console.WriteLine(Convert.ToBase64String(hash)); } }
PHP
<?php $hash_key = 'trVxrnoz22bvwvnV'; $hashcode_input['ekashu_3d_secure_verify'] = null; $hashcode_input['ekashu_amount'] = '1.23'; $hashcode_input['ekashu_amount_format'] = null; $hashcode_input['ekashu_auto_confirm'] = null; $hashcode_input['ekashu_callback_failure_url'] = null; $hashcode_input['ekashu_callback_include_post'] = null; $hashcode_input['ekashu_callback_success_url'] = null; $hashcode_input['ekashu_card_address_editable'] = null; $hashcode_input['ekashu_card_address_required'] = null; $hashcode_input['ekashu_card_address_verify'] = null; $hashcode_input['ekashu_card_email_address_mandatory'] = null; $hashcode_input['ekashu_card_phone_number_mandatory'] = null; $hashcode_input['ekashu_card_title_mandatory'] = null; $hashcode_input['ekashu_card_zip_code_verify'] = null; $hashcode_input['ekashu_currency'] = 'GBP'; $hashcode_input['ekashu_delivery_address_editable'] = null; $hashcode_input['ekashu_delivery_address_required'] = null; $hashcode_input['ekashu_delivery_email_address_mandatory'] = null; $hashcode_input['ekashu_delivery_phone_number_mandatory'] = null; $hashcode_input['ekashu_delivery_title_mandatory'] = null; $hashcode_input['ekashu_description'] = null; $hashcode_input['ekashu_device'] = null; $hashcode_input['ekashu_duplicate_check'] = null; $hashcode_input['ekashu_duplicate_minutes'] = null; $hashcode_input['ekashu_failure_return_text'] = null; $hashcode_input['ekashu_failure_url'] = null; $hashcode_input['ekashu_hash_code_format'] = null; $hashcode_input['ekashu_hash_code_type'] = 'SHA256HMAC'; $hashcode_input['ekashu_hash_code_version'] = '2.0.0'; $hashcode_input['ekashu_include_post'] = null; $hashcode_input['ekashu_invoice_address_editable'] = null; $hashcode_input['ekashu_invoice_address_required'] = null; $hashcode_input['ekashu_invoice_email_address_mandatory'] = null; $hashcode_input['ekashu_invoice_phone_number_mandatory'] = null; $hashcode_input['ekashu_invoice_title_mandatory'] = null; $hashcode_input['ekashu_locale'] = null; $hashcode_input['ekashu_payment_methods'] = null; $hashcode_input['ekashu_reference'] = '0000000765'; $hashcode_input['ekashu_request_type'] = null; $hashcode_input['ekashu_return_text'] = null; $hashcode_input['ekashu_seller_address'] = null; $hashcode_input['ekashu_seller_email_address'] = null; $hashcode_input['ekashu_seller_id'] = '99999999'; $hashcode_input['ekashu_seller_key'] = 'kIhy2V81'; $hashcode_input['ekashu_seller_name'] = null; $hashcode_input['ekashu_shortcut_icon'] = null; $hashcode_input['ekashu_style_sheet'] = null; $hashcode_input['ekashu_success_url'] = null; $hashcode_input['ekashu_title'] = null; $hashcode_input['ekashu_verification_value_mask'] = null; $hashcode_input['ekashu_verification_value_verify'] = null; $hashcode_input['ekashu_viewport'] = null; ksort($hashcode_input); $hash_code = null; foreach ($hashcode_input as $name => $value) { $hash_code .= $value; $hash_code .= '&'; } $hash_code = substr($hash_code, 0, -1); // ht3mNZVxrX+jUlN6C99ufuf3g/gfGY5JmXwCBi7nbq0= echo base64_encode(hash_hmac('sha256', $hash_code, $hash_key, true))."\n";
Post-Transaction: Verifying The Result
The validation method outlined below can be used for versions 1.0.0 and 2.0.0.
C#
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
public static class HashCodeResultValidator
{
public static void CheckHashCode(HttpContext httpContext)
{
NameValueCollection eKashuData = httpContext.Request.Form;
StringBuilder requestContent = new StringBuilder();
foreach (string key in eKashuData)
{
requestContent.Append((HttpUtility.UrlEncode(key)).Append('=').Append(HttpUtility.UrlEncode(eKashuData[key])).Append('&');
}
requestContent.Length--;
byte[] requestBuffer = Encoding.UTF8.GetBytes(requestContent.ToString());
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(@"https://test.ekashu.com/validate_hash_code.php");
webRequest.ContentLength = requestBuffer.Length;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Method = "POST";
using (Stream requestStream = webRequest.GetRequestStream())
{
requestStream.Write(requestBuffer, 0, requestBuffer.Length);
}
using HttpWebResponse result = (HttpWebResponse)webRequest.GetResponse();
result.Close();
if (result.StatusCode != HttpStatusCode.OK)
{
// Code to handle validation failure
}
}
}
PHP
<?php $post_data = null;
foreach ($_POST as $name => $value) { if (preg_match('/^ekashu_/', $name) == 1) { $post_data .= urlencode($name).'='.urlencode($value).'&'; } }
$post_data = substr($post_data, 0, -1);
$curl = curl_init();
$opt = [ CURLOPT_URL => "https://test.ekashu.com/validate_hash_code.php", CURLOPT_POSTFIELDS => $post_data, CURLOPT_POST => true, CURLOPT_HEADER => true, CURLOPT_RETURNTRANSFER => true ]; curl_setopt_array($curl, $opt);
curl_exec($curl);
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($http_code != 200) { // Code to handle validation failure }