From 857d6315e690d6ad48e690c0120c218b1467b4c9 Mon Sep 17 00:00:00 2001 From: dwindown Date: Mon, 10 Nov 2025 16:25:52 +0700 Subject: [PATCH] fix(orders): Use WooCommerce cart for shipping calculation in order creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Issues Fixed: 1. ✅ Shipping cost was zero in created orders 2. ✅ Live rates (UPS, Rajaongkir) not calculated 3. ✅ Shipping title shows service level (e.g., "JNE - REG") ## Root Cause: Order creation was manually looking up static shipping cost: ```php $shipping_cost = $method->get_option( 'cost', 0 ); ``` This doesn't work for: - Live rate methods (UPS, FedEx, Rajaongkir) - Service-level rates (JNE REG vs YES vs OKE) - Dynamic pricing based on weight/destination ## Solution: Use WooCommerce cart to calculate actual shipping cost: ```php // Initialize cart WC()->cart->empty_cart(); WC()->cart->add_to_cart( $product_id, $qty ); // Set shipping address WC()->customer->set_shipping_address( $address ); // Set chosen method WC()->session->set( 'chosen_shipping_methods', [ $method_id ] ); // Calculate WC()->cart->calculate_shipping(); WC()->cart->calculate_totals(); // Get calculated rate $packages = WC()->shipping()->get_packages(); $rate = $packages[0]['rates'][ $method_id ]; $cost = $rate->get_cost(); $label = $rate->get_label(); // "JNE - REG (1-2 days)" $taxes = $rate->get_taxes(); ``` ## Benefits: - ✅ Live rates calculated correctly - ✅ Service-level labels preserved - ✅ Shipping taxes included - ✅ Works with all shipping plugins - ✅ Same logic as frontend preview ## Testing: 1. Create order with UPS → Shows "UPS Ground" + correct cost 2. Create order with Rajaongkir → Shows "JNE - REG" + correct cost 3. Order detail page → Shows full service name 4. Shipping cost → Matches preview calculation --- includes/Api/OrdersController.php | 96 +++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/includes/Api/OrdersController.php b/includes/Api/OrdersController.php index 39e7b90..01e7731 100644 --- a/includes/Api/OrdersController.php +++ b/includes/Api/OrdersController.php @@ -872,8 +872,65 @@ class OrdersController { ], 'shipping' ); } - // Shipping method line (parse id:instance if provided) + // Shipping method - calculate using WooCommerce cart for accurate rates if ( $shipping_method ) { + // Initialize cart if needed + if ( ! WC()->cart ) { + wc_load_cart(); + } + if ( ! WC()->session ) { + WC()->session = new \WC_Session_Handler(); + WC()->session->init(); + } + + // Temporarily use cart to calculate shipping + WC()->cart->empty_cart(); + + // Add items to cart + foreach ( $items as $row ) { + $pid = absint( $row['product_id'] ?? 0 ); + $qty = max( 1, absint( $row['qty'] ?? 1 ) ); + if ( $pid ) { + WC()->cart->add_to_cart( $pid, $qty ); + } + } + + // Set shipping address for calculation + if ( $ship_addr ) { + WC()->customer->set_shipping_country( $ship_addr['country'] ?? '' ); + WC()->customer->set_shipping_state( $ship_addr['state'] ?? '' ); + WC()->customer->set_shipping_postcode( $ship_addr['postcode'] ?? '' ); + WC()->customer->set_shipping_city( $ship_addr['city'] ?? '' ); + WC()->customer->set_shipping_address( $ship_addr['address_1'] ?? '' ); + } + + // Set chosen shipping method + WC()->session->set( 'chosen_shipping_methods', [ $shipping_method ] ); + + // Calculate shipping + WC()->cart->calculate_shipping(); + WC()->cart->calculate_totals(); + + // Get the calculated rate + $packages = WC()->shipping()->get_packages(); + $shipping_cost = 0; + $shipping_title = $shipping_method; + $shipping_taxes = []; + + foreach ( $packages as $package ) { + if ( isset( $package['rates'][ $shipping_method ] ) ) { + $rate = $package['rates'][ $shipping_method ]; + $shipping_cost = $rate->get_cost(); + $shipping_title = $rate->get_label(); + $shipping_taxes = $rate->get_taxes(); + break; + } + } + + // Clean up cart + WC()->cart->empty_cart(); + + // Parse method ID and instance ID $method_id = $shipping_method; $instance_id = null; if ( strpos( $shipping_method, ':' ) !== false ) { @@ -881,36 +938,7 @@ class OrdersController { $instance_id = absint( $instance_str ); } - // Get shipping method cost - $shipping_cost = 0; - $shipping_title = $method_id; - - if ( $instance_id ) { - // Get shipping zone and method instance - $zones = \WC_Shipping_Zones::get_zones(); - foreach ( $zones as $zone ) { - foreach ( $zone['shipping_methods'] as $method ) { - if ( $method->id === $method_id && $method->instance_id == $instance_id ) { - $shipping_cost = $method->get_option( 'cost', 0 ); - $shipping_title = $method->get_title(); - break 2; - } - } - } - - // Check zone 0 (rest of the world) - if ( $shipping_cost == 0 ) { - $zone = new \WC_Shipping_Zone( 0 ); - foreach ( $zone->get_shipping_methods() as $method ) { - if ( $method->id === $method_id && $method->instance_id == $instance_id ) { - $shipping_cost = $method->get_option( 'cost', 0 ); - $shipping_title = $method->get_title(); - break; - } - } - } - } - + // Add shipping line item $ship_item = new \WC_Order_Item_Shipping(); $ship_item->set_method_id( $method_id ); if ( null !== $instance_id && method_exists( $ship_item, 'set_instance_id' ) ) { @@ -918,6 +946,12 @@ class OrdersController { } $ship_item->set_method_title( $shipping_title ); $ship_item->set_total( $shipping_cost ); + + // Set shipping taxes if available + if ( ! empty( $shipping_taxes ) ) { + $ship_item->set_taxes( [ 'total' => $shipping_taxes ] ); + } + $order->add_item( $ship_item ); }