Commit 96e3e52090 for woocommerce
commit 96e3e520902284b9f9f9ad111f7d613facc01862
Author: Ayaan Ahmad <89711008+czarflix@users.noreply.github.com>
Date: Thu Jan 22 16:56:55 2026 +0530
Fix: Round percentage-based flat rate shipping to display decimals (#62722)
diff --git a/plugins/woocommerce/changelog/62722-fix-62692-shipping-tax-rounding-precision b/plugins/woocommerce/changelog/62722-fix-62692-shipping-tax-rounding-precision
new file mode 100644
index 0000000000..731d22223f
--- /dev/null
+++ b/plugins/woocommerce/changelog/62722-fix-62692-shipping-tax-rounding-precision
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix: Round percentage-based flat rate shipping fee to display decimals to prevent $0.01 rounding errors.
\ No newline at end of file
diff --git a/plugins/woocommerce/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php b/plugins/woocommerce/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php
index 50160136b3..7b63514e98 100644
--- a/plugins/woocommerce/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php
+++ b/plugins/woocommerce/includes/shipping/flat-rate/class-wc-shipping-flat-rate.php
@@ -139,6 +139,9 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
if ( $atts['percent'] ) {
$calculated_fee = $this->fee_cost * ( floatval( $atts['percent'] ) / 100 );
+ // Fix: Round to display decimals to prevent floating-point precision drift.
+ // @see https://github.com/woocommerce/woocommerce/issues/62692.
+ $calculated_fee = round( $calculated_fee, wc_get_price_decimals() );
}
if ( $atts['min_fee'] && $calculated_fee < $atts['min_fee'] ) {
@@ -149,7 +152,7 @@ class WC_Shipping_Flat_Rate extends WC_Shipping_Method {
$calculated_fee = $atts['max_fee'];
}
- return $calculated_fee;
+ return (string) $calculated_fee;
}
/**
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-cart-shipping-rounding-test.php b/plugins/woocommerce/tests/php/includes/class-wc-cart-shipping-rounding-test.php
new file mode 100644
index 0000000000..06eb6baa3f
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/class-wc-cart-shipping-rounding-test.php
@@ -0,0 +1,178 @@
+<?php
+declare( strict_types = 1 );
+
+/**
+ * Class WC_Cart_Shipping_Rounding_Test
+ *
+ * Regression test for Issue #62692:
+ * Order total off by $0.01 when combining percentage-based shipping with tax.
+ *
+ * @package WooCommerce\Tests\Cart
+ * @link https://github.com/woocommerce/woocommerce/issues/62692
+ */
+class WC_Cart_Shipping_Rounding_Test extends WC_Unit_Test_Case {
+
+ /**
+ * Tax rate ID created during test.
+ *
+ * @var int
+ */
+ private $tax_rate_id;
+
+ /**
+ * Product created during test.
+ *
+ * @var WC_Product
+ */
+ private $product;
+
+ /**
+ * Shipping zone created during test.
+ *
+ * @var WC_Shipping_Zone
+ */
+ private $zone;
+
+ /**
+ * Flat rate instance ID.
+ *
+ * @var int
+ */
+ private $flat_rate_id;
+
+ /**
+ * Clean up after each test.
+ */
+ public function tearDown(): void {
+ parent::tearDown();
+ WC()->cart->empty_cart();
+
+ if ( $this->zone ) {
+ $this->zone->delete();
+ }
+
+ if ( $this->flat_rate_id ) {
+ delete_option( 'woocommerce_flat_rate_' . $this->flat_rate_id . '_settings' );
+ }
+
+ if ( $this->tax_rate_id ) {
+ WC_Tax::_delete_tax_rate( $this->tax_rate_id );
+ }
+
+ if ( $this->product ) {
+ WC_Helper_Product::delete_product( $this->product->get_id() );
+ }
+
+ // Clear shipping caches.
+ WC_Cache_Helper::get_transient_version( 'shipping', true );
+ }
+
+ /**
+ * Tests that percentage-based shipping with tax does not cause $0.01 rounding error.
+ *
+ * @link https://github.com/woocommerce/woocommerce/issues/62692
+ */
+ public function test_percentage_shipping_with_tax_rounding_issue_62692() {
+ // 1. Configure WooCommerce Settings.
+ update_option( 'woocommerce_prices_include_tax', 'no' );
+ update_option( 'woocommerce_calc_taxes', 'yes' );
+ update_option( 'woocommerce_tax_round_at_subtotal', 'yes' ); // Key trigger.
+ update_option( 'woocommerce_price_num_decimals', 2 );
+
+ WC()->cart->empty_cart();
+
+ // 2. Create Tax Rate (8.25% on Items Only).
+ $tax_rate = array(
+ 'tax_rate_country' => '',
+ 'tax_rate_state' => '',
+ 'tax_rate' => '8.2500',
+ 'tax_rate_name' => 'TAX',
+ 'tax_rate_priority' => '1',
+ 'tax_rate_compound' => '0',
+ 'tax_rate_shipping' => '0', // Tax does NOT apply to shipping.
+ 'tax_rate_order' => '1',
+ 'tax_rate_class' => '',
+ );
+
+ // Insert the tax rate into the database.
+ $this->tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
+
+ // 3. Create Product ($110.50).
+ $this->product = WC_Helper_Product::create_simple_product(
+ true,
+ array(
+ 'regular_price' => '110.50',
+ )
+ );
+
+ // 4. Create Shipping Zone with Flat Rate Method (15% fee).
+ // Using WC_Shipping_Zone ensures the code path goes through:
+ // calculate_totals() -> WC_Shipping_Zone -> WC_Shipping_Flat_Rate::evaluate_cost() -> fee().
+ $this->zone = new WC_Shipping_Zone();
+ $this->zone->set_zone_name( 'Test Zone' );
+ $this->zone->set_zone_order( 1 );
+ $this->zone->save();
+
+ // Add US location to match force_customer_us_address().
+ $this->zone->add_location( 'US', 'country' );
+ $this->zone->save();
+
+ // Add flat rate method to zone.
+ $this->flat_rate_id = $this->zone->add_shipping_method( 'flat_rate' );
+
+ // Configure the flat rate instance with percentage cost.
+ update_option(
+ 'woocommerce_flat_rate_' . $this->flat_rate_id . '_settings',
+ array(
+ 'enabled' => 'yes',
+ 'title' => 'Flat rate',
+ 'tax_status' => 'none', // Shipping is not taxable.
+ 'cost' => '[fee percent="15"]',
+ )
+ );
+
+ // Clear shipping cache and reload methods.
+ WC_Cache_Helper::get_transient_version( 'shipping', true );
+ WC()->shipping()->load_shipping_methods();
+
+ // 5. Simulate Cart Interaction.
+ WC_Helper_Shipping::force_customer_us_address();
+ WC()->cart->add_to_cart( $this->product->get_id(), 1 );
+ WC()->session->set( 'chosen_shipping_methods', array( 'flat_rate:' . $this->flat_rate_id ) );
+ WC()->cart->calculate_totals();
+
+ // 6. Assertions.
+ // Expected calculation:
+ // Subtotal: $110.50
+ // Shipping (15%): $110.50 * 0.15 = $16.575 → rounded to $16.58.
+ // Tax (8.25% on items only): $110.50 * 0.0825 = $9.11625 → rounded to $9.12.
+ // Total: $110.50 + $16.58 + $9.12 = $136.20.
+
+ $this->assertEquals(
+ '110.50',
+ wc_format_decimal( WC()->cart->get_subtotal(), 2 ),
+ 'Subtotal should be $110.50.'
+ );
+
+ $this->assertEquals(
+ '16.58',
+ wc_format_decimal( WC()->cart->get_shipping_total(), 2 ),
+ 'Shipping should be $16.58 (15% of $110.50 = $16.575, rounded).'
+ );
+
+ $this->assertEquals(
+ '9.12',
+ wc_format_decimal( WC()->cart->get_total_tax(), 2 ),
+ 'Tax should be $9.12.'
+ );
+
+ // THE KEY ASSERTION.
+ // Bug behavior: Returns $136.19.
+ // Fixed behavior: Returns $136.20.
+ $this->assertEquals(
+ '136.20',
+ wc_format_decimal( WC()->cart->get_total( 'edit' ), 2 ),
+ 'Total should be $136.20. If $136.19, floating-point precision loss occurred in shipping fee calculation.'
+ );
+ }
+}