Title: Custom fees plugin problem
Last modified: November 19, 2025

---

# Custom fees plugin problem

 *  [norbre](https://wordpress.org/support/users/norbre/)
 * (@norbre)
 * [6 months, 3 weeks ago](https://wordpress.org/support/topic/custom-fees-plugin-problem/)
 * Hi,
 * I have a custom plugin (developed by help of AI) to add custom packaging cost(
   _unfortunatelly I’m not that good at programming in this level_).
 * The currency switcher plugin works well with default Subtotal and Shipping costs,
   but doesn’t switch my custom fee. Can you help me what is missing from my code
   to cooperate with the currency switcher?
 * ![](https://i0.wp.com/i.postimg.cc/QCK4HqDJ/currency-exchange-problem.png?ssl
   =1)
 * Here is my plugin code below.
   It calculates the required containers based on 
   the shipping class and shipping class capacity set on the plugin’s settings page.
   The Shipping price also multiplied by the number of containers.
 *     ```wp-block-code
       <?php/* * Plugin Name:       WooCommerce Dynamic Packing Charge * Description:       Extra packaging fee for every X products, shipping fee multiplied by the number of containers. Separate step setting for each shipping class, plus option for mixing. * Version:           1.7.1 * Requires at least: 5.2 * Requires PHP:      7.2 * Author:            Gal * Text Domain:       packing-charge * Requires Plugins:  woocommerce*/if ( ! defined( 'ABSPATH' ) ) exit;/* Settings link */add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), function( $links ) {    $settings_url = add_query_arg( array( 'page' => 'wc-settings', 'tab' => 'packing_charges' ), admin_url('admin.php') );    $settings_link = '<a href="' . esc_url( $settings_url ) . '">' . __('Settings', 'packing-charge') . '</a>';    array_unshift($links, $settings_link);    return $links;});/* Settings tab */add_filter( 'woocommerce_settings_tabs_array', function( $tabs ) {    $tabs['packing_charges'] = __( 'Packing Charges', 'packing-charge' );    return $tabs;}, 999 );add_action( 'woocommerce_settings_tabs_packing_charges', function() {    woocommerce_admin_fields( pacs_get_settings() );});add_action( 'woocommerce_update_options_packing_charges', function() {    woocommerce_update_options( pacs_get_settings() );});/* Settings fields */function pacs_get_settings() {    $settings = array(        array(            'name' => __( 'Packing Charge Settings', 'packing-charge' ),            'type' => 'title',            'id'   => 'pacs_section_title'        ),        array(            'name'    => __( 'Enable Packing Charge', 'packing-charge' ),            'type'    => 'checkbox',            'id'      => 'pacs_enable',            'default' => 'no'        ),        array(            'name'     => __( 'Calculation Type', 'packing-charge' ),            'type'     => 'select',            'id'       => 'pacs_calculation_type',            'options'  => array(                'fixed'      => __( 'Fixed (Flat Fee)', 'packing-charge' ),                'percentage' => __( 'Percentage', 'packing-charge' ),                'per_step'   => __( 'Per step (every X items)', 'packing-charge' )            ),            'default'  => 'fixed'        ),        array(            'name' => __( 'Packing Charge Amount', 'packing-charge' ),            'type' => 'number',            'id'   => 'pacs_amount',            'default' => '0',            'custom_attributes' => array( 'min' => '0' )        ),        array(            'name' => __( 'Step (products per fee)', 'packing-charge' ),            'type' => 'number',            'id'   => 'pacs_step',            'default' => '1',            'custom_attributes' => array( 'min' => '1' )        ),        array(            'name'    => __( 'Packing Charge Label', 'packing-charge' ),            'type'    => 'text',            'id'      => 'pacs_label',            'default' => 'Packaging fee'        ),        array(            'name'    => __( 'Packing Container Label', 'packing-charge' ),            'type'    => 'text',            'id'      => 'pacs_container_label',            'default' => 'Container'        ),        array(            'name'    => __( 'Packing Unit Label', 'packing-charge' ),            'type'    => 'text',            'id'      => 'pacs_unit_label',            'default' => 'db'        ),        array(            'name'    => __( 'Packing Counts Per Shipping Class', 'packing-charge' ),            'type'    => 'checkbox',            'id'      => 'pacs_counts_per_shipping_class',            'default' => 'no'        ),        array(            'name' => __( 'Step for products without shipping class', 'packing-charge' ),            'type' => 'number',            'id'   => 'pacs_step_none',            'default' => '1',            'custom_attributes' => array( 'min' => '1' )        ),        array(            'name'    => __( 'Allow mixing shipping classes into one container', 'packing-charge' ),            'type'    => 'checkbox',            'id'      => 'pacs_allow_mix',            'default' => 'yes'        ),    );    $shipping_classes = WC()->shipping->get_shipping_classes();    foreach ( $shipping_classes as $class ) {        $settings[] = array(            'name' => sprintf( __( 'Step for %s', 'packing-charge' ), $class->name ),            'type' => 'number',            'id'   => 'pacs_step_' . $class->slug,            'default' => '1',            'custom_attributes' => array( 'min' => '1' )        );    }    $settings[] = array(        'type' => 'sectionend',        'id'   => 'pacs_section_end'    );    return $settings;}/* Container calculation */function pacs_calculate_groups() {    $allow_mix = get_option( 'pacs_allow_mix', 'yes' ) === 'yes';    $per_class = get_option( 'pacs_counts_per_shipping_class' ) === 'yes';    $global_step = max( 1, intval( get_option( 'pacs_step', 1 ) ) );    $none_step   = max( 1, intval( get_option( 'pacs_step_none', 1 ) ) );    if ( ! $per_class ) {        $qty = WC()->cart->get_cart_contents_count();        return max( 1, ceil( $qty / $global_step ) );    }    if ( ! $allow_mix ) {        $groups = 0;        foreach ( WC()->cart->get_cart() as $item ) {            $qty      = intval( $item['quantity'] );            $class_id = $item['data']->get_shipping_class_id();            if ( $class_id ) {                $class = get_term( $class_id, 'product_shipping_class' );                $step  = max( 1, intval( get_option( 'pacs_step_' . $class->slug, 1 ) ) );            } else {                $step  = $none_step;            }            $groups += ceil( $qty / $step );        }        return max( 1, $groups );    }    // Greedy: fill smallest container first    $classes = [];    foreach ( WC()->cart->get_cart() as $item ) {        $qty      = intval( $item['quantity'] );        $class_id = $item['data']->get_shipping_class_id();        if ( $class_id ) {            $class = get_term( $class_id, 'product_shipping_class' );            $cap   = max( 1, intval( get_option( 'pacs_step_' . $class->slug, 1 ) ) );        } else {            $cap   = $none_step;        }        $classes[] = ['qty' => $qty, 'cap' => $cap];    }    usort($classes, function($a, $b) {        return $a['cap'] <=> $b['cap'];    });    $containers = 0;    while (true) {        $remaining = 0;        foreach ($classes as $c) { $remaining += $c['qty']; }        if ($remaining <= 0) break;        // actual container capacity: smallest available class        $cap = 0;        foreach ($classes as $c) {            if ($c['qty'] > 0) { $cap = $c['cap']; break; }        }        if ($cap == 0) break;        $containers++;        $space_left = $cap;        // first smallest class        for ($i = 0; $i < count($classes); $i++) {            if ($space_left <= 0) break;            if ($classes[$i]['qty'] <= 0) continue;            $put = min($classes[$i]['qty'], $space_left);            $classes[$i]['qty'] -= $put;            $space_left -= $put;        }    }    return max(1, $containers);}/* Add packing fee as real WooCommerce fee to calculate total */add_action( 'woocommerce_cart_calculate_fees', function( $cart ) {    if ( get_option( 'pacs_enable' ) !== 'yes' ) return;    $amount   = (float) get_option( 'pacs_amount', 0 );    $type     = get_option( 'pacs_calculation_type', 'fixed' );    $groups   = pacs_calculate_groups();    $subtotal = (float) $cart->get_subtotal();    // Calculating fee    $fee = 0.0;    if ( $type === 'fixed' ) {        $fee = $groups * $amount;    } elseif ( $type === 'percentage' ) {                $fee = $subtotal * ( $amount / 100.0 );    } elseif ( $type === 'per_step' ) {        $fee = $groups * $amount;    }    if ( $fee <= 0 ) return;    $charge_label    = get_option( 'pacs_label', 'Packaging fee' );    $container_label = get_option( 'pacs_container_label', 'Container' );    $unit_label      = get_option( 'pacs_unit_label', 'db' );    $label = $charge_label . ' (' . $groups . ' ' . $unit_label . ' ' . $container_label . ')';        $cart->add_fee( $label, $fee, true );});/* Shupping costs multiply by containers */add_filter( 'woocommerce_package_rates', function( $rates, $package ) {    if ( get_option( 'pacs_enable' ) !== 'yes' ) return $rates;    $groups = pacs_calculate_groups();    foreach ( $rates as $rate_id => $rate ) {        $rates[ $rate_id ]->cost = $rates[ $rate_id ]->cost * $groups;                if ( ! empty( $rates[ $rate_id ]->taxes ) && is_array( $rates[ $rate_id ]->taxes ) ) {            $taxes = $rates[ $rate_id ]->taxes;            foreach ( $taxes as $i => $tax_amount ) {                $taxes[$i] = $tax_amount * $groups;            }            $rates[ $rate_id ]->taxes = $taxes;        }    }    return $rates;}, 10, 2 );/* Defaults on activation */register_activation_hook( __FILE__, function() {    $defaults = array(        'pacs_enable'                     => 'no',        'pacs_label'                      => 'Packing fee',        'pacs_amount'                     => '0',        'pacs_calculation_type'           => 'fixed',        'pacs_step'                       => '1',        'pacs_container_label'            => 'Packing container',        'pacs_unit_label'                 => 'db',        'pacs_counts_per_shipping_class'  => 'no',        'pacs_step_none'                  => '1',        'pacs_allow_mix'                  => 'yes'    );    foreach ( $defaults as $key => $value ) {        if ( get_option( $key ) === false ) {            update_option( $key, $value );        }    }    if ( function_exists( 'WC' ) && WC()->shipping ) {        $shipping_classes = WC()->shipping->get_shipping_classes();        if ( is_array( $shipping_classes ) ) {            foreach ( $shipping_classes as $class ) {                if ( isset( $class->slug ) ) {                    $opt_key = 'pacs_step_' . $class->slug;                    if ( get_option( $opt_key ) === false ) {                        update_option( $opt_key, '1' );                    }                }            }        }    }});
       ```
   

Viewing 3 replies - 1 through 3 (of 3 total)

 *  Plugin Author [RazyRx](https://wordpress.org/support/users/razyrx/)
 * (@razyrx)
 * [6 months, 3 weeks ago](https://wordpress.org/support/topic/custom-fees-plugin-problem/#post-18728240)
 * Hello,
   It is depend on option “**Visual only**” in plugin settings.If it is enabled
   then price on page must be displayed with correct structure same as other price.
   If this option disabled then plugin use specific hook to replace it.Regards,Oleg
 *  Thread Starter [norbre](https://wordpress.org/support/users/norbre/)
 * (@norbre)
 * [6 months, 3 weeks ago](https://wordpress.org/support/topic/custom-fees-plugin-problem/#post-18728390)
 * Hi,
 * I’ve tried the Visual only option and works well, but does not effect on the 
   outgoing emails and the content of the admin orders menu, so I would like to 
   use the plugin in not visual mode.
 * If I am right, my plugin adds the new fee to the cart . This fee is became visible
   in the emails and in the admin orders menu.
 *     ```wp-block-code
       $cart->add_fee( $label, $fee, true );
       ```
   
 * Does your hook goes through every fees or just specific ones?
   Can I tag or mark
   the extra fees for your plugin’s hook to calculate with?
 * 
   Thank you
 *  Plugin Author [RazyRx](https://wordpress.org/support/users/razyrx/)
 * (@razyrx)
 * [6 months, 2 weeks ago](https://wordpress.org/support/topic/custom-fees-plugin-problem/#post-18730812)
 * Hello,
   Visual only will be applied only on site frontend.In most cases plugin
   modify price before set it to cart because it can be used in different places.
   Maybe it is required to apply similar hook for thise custom fee before add it
   to cart.Regards,Oleg

Viewing 3 replies - 1 through 3 (of 3 total)

The topic ‘Custom fees plugin problem’ is closed to new replies.

 * ![](https://ps.w.org/currency-exchange-for-woocommerce/assets/icon-256x256.gif?
   rev=2776477)
 * [Currency Exchange for WooCommerce](https://wordpress.org/plugins/currency-exchange-for-woocommerce/)
 * [Frequently Asked Questions](https://wordpress.org/plugins/currency-exchange-for-woocommerce/#faq)
 * [Support Threads](https://wordpress.org/support/plugin/currency-exchange-for-woocommerce/)
 * [Active Topics](https://wordpress.org/support/plugin/currency-exchange-for-woocommerce/active/)
 * [Unresolved Topics](https://wordpress.org/support/plugin/currency-exchange-for-woocommerce/unresolved/)
 * [Reviews](https://wordpress.org/support/plugin/currency-exchange-for-woocommerce/reviews/)

 * 3 replies
 * 2 participants
 * Last reply from: [RazyRx](https://wordpress.org/support/users/razyrx/)
 * Last activity: [6 months, 2 weeks ago](https://wordpress.org/support/topic/custom-fees-plugin-problem/#post-18730812)
 * Status: not resolved