Contents:
Introduction • Generating a Timestamp • Generating a Signature • cmpi_lookup Request Structure
Centinel lookup requests will utilize the following credentials to authenticate: Algorithm
, Identifier
, OrgUnit
, Signature
and Timestamp
.
Field Name | Description | Required |
---|---|---|
Algorithm | The hash algorithm that was used to generate the Signature for the request. Possible Values: SHA-256 SHA-512 |
O |
Identifier | The unique identifier representing the API Key being used to generate the Signature that is specified on the request. This value will be provided by Cardinal at the time the API Key is generated. | O |
OrgUnit | The unique organizational unit for which the request is being processed for. Each merchant within the system will be assigned a unique OrgUnit value by Cardinal. | O |
Signature | The signature for the request being submitted. This value is generated by hashing the combination of the Timestamp and the API Key then base64 encoding the result. Please see example below. | O |
Timestamp | The unix epoch time in milliseconds for the point in time that the request is generated. Example output: 1467122891960 |
O |
Ensure that the timestamp is in proper millisecond format.
<?php
$timestamp = round(microtime(true) * 1000);
?>
The API Key Signature is generated by concatenating the Unix Epoch time in milliseconds and the API Key, hashing the concatenated value then base64 encoding the result. For Example:
<?php
$timestamp = round(microtime(true) * 1000);
$apiKey = '13f1fd1b-ab2d-4c1f-8e2c-ca61878f2a44';
# Now, concatenate the two values:
$preHash = $timestamp.$apiKey;
# Next, hash the concatenated value.
# For this example let's use the sha256 algorithm:
$hashed = hash("sha256", $preHash, true);
# Last, base64 encode the result:
$signature = base64_encode($hashed);
?>
The following example will help provide a conceptual demonstration of generating the APIKey Signature and the cmpi_lookup schema
<?php
header('Content-Type: text/plain');
$Timestamp = round(microtime(true) * 1000);
$ApiKey = '754be3dc-10b7-471f-af31-f20ce12b9ec1';
$ApiId = '582e0a2033fadd1260f990f6';
$OrgUnit = '582be9deda52932a946c45c4';
$preHash = $timestamp.$apiKey;
$hashed = hash("sha256", $preHash, true);
$Signature = base64_encode($hashed);
$cmpi_lookup = <<<XML
<CardinalMPI>
<Algorithm>SHA-256</Algorithm>
<Amount>12345</Amount>
<BillAddrPostCode>44060</BillAddrPostCode>
<BillAddrState>OH</BillAddrState>
<BillingAddress1>8100 Tyler Blvd</BillingAddress1>
<BillingAddress2></BillingAddress2>
<BillingCity>Mentor</BillingCity>
<BillingCountryCode>840</BillingCountryCode>
<BillingFirstName>John</BillingFirstName>
<BillingFullName>John Doe</BillingFullName>
<BillingLastName>Doe</BillingLastName>
<BillingPostalCode>44060</BillingPostalCode>
<BillingState>OH</BillingState>
<BrowserColorDepth>32</BrowserColorDepth>
<BrowserHeader>text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</BrowserHeader>
<BrowserHeader>text/html,application/xhtml+xml,application/xml;q=0.9,</BrowserHeader>
<BrowserJavaEnabled>true</BrowserJavaEnabled>
<BrowserLanguage>English</BrowserLanguage>
<BrowserScreenHeight>980</BrowserScreenHeight>
<BrowserScreenWidth>1080</BrowserScreenWidth>
<BrowserTimeZone>25200</BrowserTimeZone>
<CardExpMonth>02</CardExpMonth>
<CardExpYear>2024</CardExpYear>
<CardNumber>4000000000001092</CardNumber>
<CurrencyCode>840</CurrencyCode>
<DFReferenceId>c17dea31-9cf6-0c1b8f2d3c5</DFReferenceId>
<DeviceChannel>browser</DeviceChannel>
<Email>[email protected]</Email>
<IPAddress>67.17.219.20</IPAddress>
<Identifier>$ApiId</Identifier>
<MsgType>cmpi_lookup</MsgType>
<OrderNumber>order-0001</OrderNumber>
<OrgUnit>$OrgUnit</OrgUnit>
<ShippingAddress1>8100 Tyler Blvd</ShippingAddress1>
<ShippingAddress2></ShippingAddress2>
<ShippingCity>44060</ShippingCity>
<ShippingCountryCode>840</ShippingCountryCode>
<ShippingPostalCode>44060</ShippingPostalCode>
<ShippingState>OH</ShippingState>
<Signature>$Signature</Signature>
<Timestamp>$Timestamp</Timestamp>
<TransactionType>C</TransactionType>
<UserAgent>Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0</UserAgent>
<Version>1.7</Version>
</CardinalMPI>
XML;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://centineltest.cardinalcommerce.com/maps/txns.asp");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'cmpi_msg='."\n".urlencode($cmpi_lookup)."\n");
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLINFO_HEADER_OUT, 1);
$result = curl_exec($ch);
curl_close ($ch);
echo $result;
Next, the following PHP parses the results to populate the Challenge iFrame
$xmldata = simplexml_load_string($result);
$jsondata = json_encode($xmldata);
$json = json_decode($jsondata,TRUE);
$acsUrl = $json['ACSUrl'];
$payload = $json['Payload'];
$trxId = $json['TransactionId'];
function base64url_encode($input) {
return rtrim(strtr(base64_encode($input), '+/', '-_'), '=');
}
function base64url_decode($input) {
return base64_decode(str_pad(strtr($input, '-_', '+/'), strlen($input) + (4 - strlen($input) % 4) % 4, '=', STR_PAD_RIGHT));
}
//build jwt headers
$headers = ['alg'=>'HS256','typ'=>'JWT'];
$headers_encoded = base64url_encode(json_encode($headers));
//build jwt payload
$jwt = ['jti'=>$trxId,'iat'=>$Timestamps, 'iss'=>$ApiId, 'OrgUnitId'=>$OrgUnit, 'ObjectifyPayload'=>true, 'Payload'=>['ACSUrl'=>$acsUrl, 'Payload'=>$payload,'TransactionId'=>$trxId],'ReferenceId'=>$refId,'ReturnUrl'=>'https://postman-echo.com/post'];
$jwt_encoded = base64url_encode(json_encode($jwt));
//build jwt signature
$key = $ApiKey;
$signature = hash_hmac('sha256',"$headers_encoded.$jwt_encoded",$key,true);
$signature_encoded = base64url_encode($signature);
//build and return the token
$token = "$headers_encoded.$jwt_encoded.$signature_encoded";
//echo $token;
$url = 'https://centinelapistag.cardinalcommerce.com/V2/Cruise/StepUp';
?>
Last, construct the Challenge iFrame
<HTML>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" />
</head>
<pre id="response"></pre>
<pre id="payload"></pre>
<pre><?php echo $token ?></pre>
<iframe name="challenge-iframe" height="100%" width="100%" style="display: block;border-style:none;">
</iframe>
<form id="challenge-form" target="challenge-iframe" method="POST" action="<?php echo $url ?>">
<input type="hidden" name="JWT" value="<?php echo $token ?>" />
</form>
<script>
var response = <?php echo $jsondata ?>;
var payload = <?php echo json_encode($jwt) ?>;
document.getElementById("response").textContent = JSON.stringify(response, undefined, 2);
document.getElementById("payload").textContent = JSON.stringify(payload, undefined, 2);
</script>
<script>window.onload = function () {
// Auto submit form on page load
document.getElementById('challenge-form').submit();
}
const cruiseApiOrigin = 'https://centinelapistag.cardinalcommerce.com'
/**
* NOTE: This event binding will not work in older IE browsers.
* You will need to also implement attachEvent if you need to support older IE browsers.
* https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#legacy_internet_explorer_and_attachevent
**/
window.addEventListener('message', (evnt) => {
try {
// Filter postMessage events by origin and process only the Cardinal events
if (evnt.origin === cruiseApiOrigin) {
// CruiseAPI events are stringified JSON objects to ensure backwards compatibility with older browsers
let data = JSON.parse(evnt.data)
if (data !== undefined && data.MessageType !== undefined) {
// Do merchant logic
switch(data.MessageType)
{
case 'stepUp.acsRedirection':
// Implement Merchant logic
break;
case 'stepUp.completion':
// Implement Merchant logic
break;
case 'stepUp.error':
// Implement Merchant logic
break;
default:
console.error("Unknown MessageType found ["+data.MessageType+"]");
// Implement Merchant logic - Handle unknown MessageType
break;
}
}
}
} catch (e) {
console.error('failed to parse CruiseAPI postMessage event', e)
}
}, false)
</script>
</HTML>