Skip to content

Latest commit

 

History

History

lookup

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

3DS Authentication cmpi_lookup

php Watchers

Contents:

IntroductionGenerating a TimestampGenerating a Signaturecmpi_lookup Request Structure

APIKey Signature Introduction

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

Generating a Timestamp

Ensure that the timestamp is in proper millisecond format.

<?php

$timestamp = round(microtime(true) * 1000);

?>

Generating a Signature Value

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);

?>

cmpi_lookup Request Structure

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>