Forcing 4 decimal places on a number without changing its type to string

Viewed 180

I receive some data including a decimal number from an api in post request with content-type:json

among the posted data , is a Amount parameters with 4 decimal places

{
  "Amount": 500.0000  ,
  "param1" : "value1" ,
  "param2" : "value2" 
}

after parsing the json

$data = json_decode(file_get_contents('php://input'), true);
var_dump($data);

here is my output

array(3) {
  ["Amount"]=>
  float(500)
  ["param1"]=>
  string(6) "value1"
  ["param2"]=>
  string(6) "value2"
}

the problem is I’m losing the decimal places on the Amount if they are zero

so instead of 500.0000 I get 500 , I know they are the same numbers mathematically but I need to generate a hash from these posted parameters which requires me to have Amount as a float number with 4 decimal places exactly as posted in the first place

I can do something like

$data['Amount'] = sprintf('%0.4f',   $data['Amount'] );

but than it would convert the Amount into an string , which results in a invalid hash

array(3) {
  ["Amount"]=>
  string(8) "500.0000"
  ["param1"]=>
  string(6) "value1"
  ["param2"]=>
  string(6) "value2"
}

I need my $data output to be

  ["Amount"]=>
  float(500.0000)

tried to run it throw floatval , lost the decimal zeros again ! I’m losing my mind over something so simple , any idea how to solve this ?

--------------- edit --------------------

purpose of this hash is to verify the origin of request (making sure its my provider who is sending me this data )

they send me publicKey with the request , I should generate the hash with some of the params and compare it against that public key

In order to create the hash , I need to json_encode some of the received parameters and run them throw sha256 , at the end I need to compare them to a received public_key

so if I receive

{
  "Amount": 5000.0000,
  "Token": "6789",
  "OperatorId": 7799,
  "TransactionId": 110883669553,
  "PublicKey": "f7bb8448c5701b9530fbecc8f9d6296b2ce0600118c402213709bcf19f533c1b"
}

this is how I calculate the hash

$shared_key  =  'someKey';
$hash_params = [
   'Amount'       => $data['Amount'] , 
   'TransactionId' => $data['TransactionId'] , 
];
$hash_string = json_encode($hash_params) . $shared_key ;
$hash =  hash('sha256',    $hash_string  );    
if($hash != $data['PublicKey'] )
{
    die( "hash check failed");
}

In this scenario I need to sha256 this $hash_string

{"WithdrawAmount":5000.0000,"TransactionId":12345 }someKey

if it turns to

{"WithdrawAmount":"5000.0000","TransactionId":12345 }someKey

it'll give me a wrong hash

4 Answers

I don't think there's an easy and simple way to perform a json_encode, of a Float value forcing 4 decimal places when the decimal part of the number is "0000", without manipulating the json string.

The json_enconde method has 2 predefined constants which helps us "almost" achieve the desired result, and these are the JSON_NUMERIC_CHECK and JSON_PRESERVE_ZERO_FRACTION but normally the 0 are removed.

echo json_encode(["amount" => "5.0000"], JSON_NUMERIC_CHECK | JSON_PRESERVE_ZERO_FRACTION);

Outputs: {"amount":5.0}

My solution, would be to perform the encoding using a unique string as amount value and then replace it using str_replace for the formatted amount like this:

$amount_needle = "###amount###";
$hash_params = [
   'Amount'       => $amount_needle, 
   'TransactionId' => $data['TransactionId'], 
];
$encoded_json = json_encode($hash_params);
$hash_string = str_replace("\"{$amount_needle}\"", sprintf('%0.4f', $data['Amount']), $encoded_json) . $shared_key ;

As already mentioned, there is no direct way to format the float value in the JSON string.

As an alternative to the replacement technique shown in Claudio's answer, here is a simple utility class that helps build the JSON string with the desired formatting:

class JSONBuilder
{
    private $params = [];

    public function add($name, $value, $format='')
    {
        if($format)
            $encoded = sprintf($format, $value);
        else
            $encoded = json_encode($value);
        $this->params[] = sprintf('"%s":%s', $name, $encoded);
    }

    public function get()
    {
        return '{' . join(',', $this->params) . '}';
    }
}

Example:

$b = new JSONBuilder();
$b->add('Amount', 5000.0, '%0.4f');
$b->add('Token', '6789');
$b->add('TransactionId', 12345);
echo $b->get();

Output:

{"Amount":5000.0000,"Token":"6789","TransactionId":12345}
Related