question

rakeshkumar125 avatar image
rakeshkumar125 asked martin7 answered

Please help me encrypt card details in card token api

I am trying to integrate pay API using PHP but stuck in generate card tokens. It always generates an error in encrypt details.
Below is my code.

API Name : ECOMMERCE SERVICE API
Enviroment : Sandbox

<?php
/*phpseclib1.0.2*/
include('phpseclib/Crypt/RSA.php');
include('phpseclib/Math/BigInteger.php');

/* ========== GET API KEY USING TOKEN ===========*/

$authToken = 'xxxxxxx-cdde-xxxxx-xxxx-xxxxxxxxx'; /* which is generate from clover backend system*/

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://apisandbox.dev.clover.com/pakms/apikey',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => array(
    'Accept: application/json',
    'Authorization: Bearer '.$authToken
  ),
));

$response = curl_exec($curl);
curl_close($curl);
$apikeyArr = json_decode($response);
$apiKey = $apikeyArr->apiAccessKey;

echo 'API KEY <br/> ';
echo $apiKey;

/* ========== GET PAY KEY FOR ENCRYPT CARD DETAILS ===========*/

$curl = curl_init();
curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://sandbox.dev.clover.com/v2/merchant/B0T4DYVTYBTZ1/pay/key',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'GET',
  CURLOPT_HTTPHEADER => array(
    'Accept: application/json',
    'Authorization: Bearer '.$authToken,
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);

curl_close($curl);
$pay_key_data = json_decode($response);

echo '<br/> Key Data Response <br/> <pre>';
print_r($pay_key_data);
echo '<pre>';

    $rsa = new Crypt_RSA();
    $prefix = $pay_key_data->prefix;
    $modulus = $pay_key_data->modulus;
    $exponent = $pay_key_data->exponent;
 
    /* ========= PREPARING CARD DETAILS TO PASS FOR TOKEN ============= */

    $user_cc_no = '6011366668';
    $user_cc_mo = '12';
    $user_cc_yr = '2021';
    $user_cc_cvv = '123';
    $user_zip = '23402';
    $stingToEnc = $prefix.$user_cc_no; 

    $modulus = new Math_BigInteger($modulus);
    $exponent = new Math_BigInteger($exponent);
    $rsa->loadKey(array('n' => $modulus, 'e' => $exponent));
    $rsa->setPublicKey();
    $mypublickey = $rsa->getPublicKey();
    $stingToEncPublickey = $stingToEnc; 
    $ciphertext =   $rsa->encrypt($stingToEncPublickey);
    $stingBase64Encpted = base64_encode($ciphertext);
$orderData = array();

    $orderData['cardEncrypted'] = $stingBase64Encpted;
    $orderData['first6'] = substr($user_cc_no, 0, 6);
    $orderData['last4'] = substr($user_cc_no,-4);
$orderData['exp_month'] = $user_cc_mo;
    $orderData['exp_year'] = $user_cc_yr;
    $orderData['cvv'] = $user_cc_cvv;
    $orderData['brand'] = "DISCOVER";
    $cardData['card'] = $orderData;
    $jsonCardData = json_encode($cardData);
    
    echo '<br/> Card data that Passed <br/>';
    echo '<pre>';
    print_r($cardData);
    echo '<pre>';
    /* =============== NOW CALL API FOR TOKEN =================*/

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'https://token-sandbox.dev.clover.com/v1/tokens',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>$jsonCardData,
  CURLOPT_HTTPHEADER => array(
    'Accept: application/json',
    'apikey: '.$apiKey,
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);

curl_close($curl);
$responseCardTokenDetails = json_decode($response);

echo '<br/> Final Result <br/>';
echo '<pre>';
print_r($responseCardTokenDetails);
echo '</pre>';

?>

Now there is the below result from card token API. The above two working fine but error from last API.

Array
(
    [card] => Array
        (
            [cardEncrypted] => qmLHcbgABk0abTNxm/tugSbR7OheVxlepRyciQDQBLw4WsGIr9UU6TjqxMcNuRxD/dD/e8p8c/G93B97Y1WygLpwmmN0H43E6QNlsNlQ/B/5AE3fpexTseOxLtB0/0wdmPbr1wA4vnxPfBMzNR7yPJ0Vk0iTsUc6ywkel+zn84wCBHjpAqMEtzy0WpdBe4VNVt0FxXZH7JTh7a/BxiGuvBiKPMvdGQChBvMBqEvPO8IQTjGlwjxoVKyKhuj7gT+AFA2SJ2NEIFigrbskkQQduGICxC5GdmT/YQWRPNlxgPvf8mckDNLfANBD5SeiuErmDT8DsdWu2kBhuKhGrf7FVQ==
            [first6] => 601136
            [last4] => 6668
            [exp_month] => 12
            [exp_year] => 2021
            [cvv] => 123
            [brand] => DISCOVER
        )

)

 Final Result 
stdClass Object
(
    [message] => 400 Bad Request
    [error] => stdClass Object
        (
            [type] => invalid_request_error
            [code] => invalid_request
            [message] => Please provide either raw pan or encrypted pan.
        )

)

One more thing in card details data if remove cardEncrypted field then errors still the same.
Does anyone have an idea bout the above issue?

Thanks,
Rakesh Kumar

Payments
3 comments
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

See our API reference - https://docs.clover.com/reference/createtoken.

I am not very familiar with PHP but your request looks wrong. You aren't passing the expected properties (e.g. encrypted_pan).

0 Likes 0 ·
rakeshkumar125 avatar image rakeshkumar125 David Marginian ♦♦ ·

Okay, now I have passed your suggested properties you can see in the below request.

{
   "card":{
      "encrypted_pan":"CfTUMOHgvt88V+pz3XocDS3/QQuWwHuS/9TdhUvvPcQIaf3uxpj6x8Y4s7lwzq3r4aG0DuL8HluICz5VGfIvLcnKmGJ41wWQKFbfT8Qjtlbmq6sOrDt+5haWjC6RKb4zCW2uhpYmbvAUgF1ka3X0ymxKYrbxvyOSv8zfu9zJ4K4nlBBSJsjCMhdMDDVlqsBK1paPcLgA/AydIR8MvBWNopozIB4hoy0wjNrdje7ArLxOv0cXjcDQTrsYgpJgs9CHv2/18RzLFVUOAM+yEXAbnKcS1OnM0CKN1NA4eLsXaA1uYmsazc/IbgM2vZzfnYp0z85Z7Ga0OjhkFxYvyL/zdw==",
      "first6":"601136",
      "last4":"6668",
      "exp_month":"12",
      "exp_year":"2021",
      "cvv":"123",
      "brand":"DISCOVER"
   }
}

Now Error Become changed. But still not working.

{
    "message": "500 Internal Server Error",
    "error": {
        "type": "api_error",
        "code": "processing_error",
        "message": "An error occurred while processing the token request."
    }
}
0 Likes 0 ·

It appears you aren't encrypting the card properly. Take a look at our documentation and make sure you are doing everything correctly - https://docs.clover.com/docs/ecommerce-generating-a-card-token#encrypting-card-data.

0 Likes 0 ·
martin7 avatar image
martin7 answered martin7 commented

@rakeshkumar125 Did you solve this? I am having the same issue and no idea what I am doing wrong with the encryption.

4 comments
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Have you seen our documentation - https://docs.clover.com/docs/ecommerce-generating-a-card-token#encrypting-card-data ? I recently went through these instructions as well as the Java example and everything was working fine.

0 Likes 0 ·
martin7 avatar image martin7 David Marginian ♦♦ ·

Yes, I am following it but doing in php. I've already spend two days trying to figure what is wrong with the encryption. I am using https://phpseclib.com/docs/rsa#raw-rsa-public-keys for creating the RSA key.

I am fetching the modulus, exponent and prefix from
https://sandbox.dev.clover.com/v2/merchant/MERCHANT_ID/pay/key

This is the code for generating RSA Key

function generate_rsa_key( $modulus, $exponent ) {
  $key = PublicKeyLoader::load(
    array(
       'e' => new BigInteger( $exponent ),
       'n' => new BigInteger( $modulus ),
    )
  );

  return $key;
}

$rsa = $this->generate_rsa_key( $modulus_exponent_data['modulus'], $modulus_exponent_data['exponent'] );

For encrypting is this

$pan = base64_encode( $rsa->encrypt( $modulus_exponent_data['prefix'] . $card_number ) );

I send the request to https://token-sandbox.dev.clover.com/v1/tokens

This is the error I get. I am pretty sure this is because of wrong card encryption.

[message] => 500 Internal Server Error
    [error] => Array
        (
            [type] => api_error
            [code] => processing_error
            [message] => An error occurred while processing the token request.
        )


This is full body request

{
"card": {
"encrypted_pan": "oGloPuHTlyjWtEg7hDZ/IXLYm497xXw/qDimAX/29E4g5FI6eOYxM8khe3sJ/V24hyoolgSb1J/3T+QkJX0OZnQhoHKRXjF+nMyvumT63wjLJoNvulrWxNTtiPN08kD9Phsyp50NpdmWzIBerVRwYvNT6pien9DzjTl6dCbXTN0beA2rsL5gcALz9+4CF9QHQa8Nov3XUJn9+zA+7gd15b9vFbGtDCX1yjeMMlgBpDeKJ3zg3rF68cjS98IrGCW8CdSUicO7LRd0y4dozb2jxFU1YTgcNiiV4jjkh+QMC7N4D0U5wuRjGV3z9h/Km7mKot82fNgrJUj60s2/Usrl5Q==",
"first6": "601136",
"last4": "6668",
"exp_month": "09",
"exp_year": "23",
"cvv": "123",
"brand": "Discover"
}
}
0 Likes 0 ·

You aren't following the instructions as they tell you to pull the encryption keys from the CDN, not pay/key. If you want to use the data returned from pay/key you will need to adjust your tokenize call to pass card.transarmor_key_id where transarmor_key_id is the "id" returned in the pay/key call:

"card": {
   ...
   "transarmor_key_id": id from the pay/key response here 
   ...
}


0 Likes 0 ·
Show more comments
martin7 avatar image
martin7 answered David Marginian edited

I also tried with keys from CDN.. Same error

1 comment
10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

With keys from CDN you have to parse out the modulus, so your code would need to change. From looking at the logs, it certainly appears to be an decryption error. I am not familiar with PHP, but a bit of Googling and I found this, https://github.com/phpseclib/phpseclib/issues/1290, may want to take a look at some of the comments as it looks like their code is a bit different. I am not sure what encryption algorithm PHP is using but it must be RSA/None/OAEPWithSHA1AndMGF1Padding.

0 Likes 0 ·
martin7 avatar image
martin7 answered martin7 commented

Yea, I checked that, but I am using the latest version of the library and that code shown there is not used anymore.

When using keys from CDN, I have the following:

$modulus  = substr( $pa_key, 0, 256 );
$exponent = substr( $pa_key, 256, 256 );

If I read the Java code correctly modulus is the first 256 characters of the decoded TA_PUBLIC_KEY_DEV and exponent is the second 256 characters starting from 256 position. The 3rd param here is the length of the sub string.

function generate_rsa_key( $modulus, $exponent ) {
   $key = PublicKeyLoader::load(
     array(
       'e' => new BigInteger( $exponent, 256 ),
       'n' => new BigInteger( $modulus, 256 ),
     )
  );

  return $key;
}

What should the prefix for the card be when keys are from CDN? 8 zeros like in the Java example?


10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

martin7 avatar image
martin7 answered

Any chance you can send me generated PAN for this test card 6011 3610 0000 6668 so I can check if it will pass?

10 |2000

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Welcome to the
Clover Developer Community