Search code examples
phpajaxwoocommercehook-woocommercecheckout

Update dynamically custom content on WooCommerce update checkout event



class CheckoutTax
{
    public function __construct()
    {
        // add a note after the order details, when a tax is charged and country changes 
        
        // the html snippet is shown after the order details on a page refresh
        add_action('woocommerce_review_order_before_payment', array($this, 'checkout_note_tax'));

        // the action is called when the country changes
        // but it does not show the html snippet at the correct place
        add_action('woocommerce_cart_calculate_fees', array($this, 'checkout_note_tax'));

        // how to combine
        // - show HTML snippet after the order details
        // - act on a country change
    }

    /**
     * Show checkout note, tax paid
     *
     * @return void
     */
    function checkout_note_tax() {
        
        if (is_admin() && !defined('DOING_AJAX')) return;

        if (!is_checkout()) return;

        global $WOOCS;

        $total_tax = WC()->cart->get_totals()['total_tax'];
        $shipping_country = WC()->customer->get_shipping_country();

        error_log( $total_tax );
        error_log( $shipping_country );

        if( $total_tax > 0 && $shipping_country != 'CH') {
            ?>
            <h3>VAT / Custom Duties</h3>
            <?php
        }
    }
}

The hook woocommerce_review_order_before_payment shows the html snippet at the correct page, but only on a page refresh.

The hook woocommerce_cart_calculate_fees triggers changes made to the country field.

How can I combine an action to show a HTML snippet after the order details on the checkout page and update the HTML snippet when the shipping country is changed?


Solution

  • You need something a bit different to get your custom content refreshed on checkout_update Ajax event. I have replaced woocommerce_review_order_before_payment hook with a more convenient one.

    Now your content is also added to Ajax "order review fragments", so on "Update checkout" event your display gets refreshed dynamically.

    Try the following (commented):

    class CheckoutTax
    {
    
        public function __construct()
        {
            add_action( 'woocommerce_checkout_order_review', array( $this, 'checkout_display_custom_taxes'), 15 );
            add_filter( 'woocommerce_update_order_review_fragments', array( $this, 'update_checkout_custom_fragments') );
        }
    
        /**
         * Here your HTML content
         *
         * @return string
         */   
        public function output() {
            $total_tax = WC()->cart->get_totals()['total_tax'];
            $shipping_country = WC()->customer->get_shipping_country();
        
            $html = '<div class="woocommerce-checkout-custom-tax">'; // Mandatory wrapper html
        
            if( $total_tax > 0 && $shipping_country !== 'CH') {
                $html .= sprintf('<h3>%s</h3>', __('VAT / Custom Duties', 'woocommerce') );
            }
            return $html . '</div>';
        }
    
        /**
         * Output html content in checkout page
         *
         * @return void
         */   
        public function checkout_display_custom_taxes() {
            echo $this->output();
        }
    
        /**
         * Add the html content to be refreshed on checkout update Ajax event
         *
         * @return array
         */   
        public function update_checkout_custom_fragments( $fragments ){
            // We add the selector class of our wrapper div as array key
            $fragments['.woocommerce-checkout-custom-tax'] = $this->output();
        
            return $fragments;
        }
    }
    

    For testing purpose in the child theme's functions.php file, I use additionally:

    add_action('woocommerce_checkout_init', function(){
        $CheckoutTax = new CheckoutTax();
    });
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works.


    Related: