Proper Cart Total Conversion to Cents Without Odd Results

approximately 2 minutes of reading

When implementing e-commerce logic (especially a cart functionality or payment integration) it is very common to deal with all sorts of money values as integers. While it is tempting to simply multiply your cart's total float by 100, this approach my lead to some odd results and as a consequence break entire logic of your payment integration.

Background

The currency of your order does not matter at all. Let's simply assume that regardless of your cart's content, the total price to be paid for order is €1270,59. Next, when performing payment logic, your payment provider my require your to send this amount as cents in a shape of an integer value. At first glance, it looks like a basic elementary math operation.

$total = 1270.59;

Let's multiply it by 100.

$total = $total * 100;

This will give us 127059.0.

So far everything is looking good and the last step would be to simply drop the precision by converting this value to integer. Let's proceed.

You would expect that the following code:

$total = $total * 100;
$total = intval($total);

will output 127059, but to our surprise PHP (I'm on version 8.3.11) will return 127058.

This may crash your entire checkout logic, especially if you have to calculate some checksums where cart total is taken into consideration before payment.

Solution

The issue is that we're performing non-arbitrary precision math. PHP will round down numbers when converting floats to integers.

To get a correct result, we should perform the conversion using PHP's BCMath Arbitrary Precision Mathematics extension.

Let's add the extension to composer.json under require section:

"ext-bcmath": "*"

Next let's pull it. Assuming you're using Docker, you can do something similar to:

RUN docker-php-ext-install bcmath

Finally, let's alter the logic. I have wrapped it in a private method:

private function convertOrderTotalToCents(Order $order): int
{
    return intval(bcmul($order->getTotal(), 100));
}

The solution here is to use bcmul() method, which will perform the multiplication on a precision numbers.

This private method will then correctly return an integer of 127059 for a total of 1270.59.


Words: 326
Published in: PHP

Related Articles   📚