Search code examples
phpwordpresswoocommercemetadataorders

Displaying a transaction Fee on WooCommerce Admin Order edit page


What I am trying to do is show a breakdown of the Woocommerce transaction fee being applied after the order total:

It would something like this:

  • Items Subtotal: £375.00
  • Shipping: £0.00
  • VAT: £75.00
  • Order Total: £450.00
  • Paid: £450.00
  • Transaction Fee: £7.00
  • Total (after fee): £443.00

A visual of where it should go, I have highlighted it and as you see it's just an error at the moment:

enter image description here

The code in my functions .php

    // Calculate and save transaction fee after order is created/paid
add_action('woocommerce_order_status_changed', 'add_transaction_fee_to_order_meta', 10, 3);
function add_transaction_fee_to_order_meta($order_id, $old_status, $new_status) {
    if ($new_status == 'processing' || $new_status == 'completed') {
        $order = wc_get_order($order_id);

        if (get_post_meta($order_id, '_transaction_fee', true) == '') {
            $order_total = $order->get_total();
            $transaction_fee = ($order_total * 0.015) + 0.25;

            update_post_meta($order_id, '_transaction_fee', $transaction_fee);

        }
    }
}


// Display transaction fee in admin order details *after* "Paid" line
add_action('woocommerce_admin_order_totals_after_total', 'display_transaction_fee_in_admin_order_details', 10, 2);
function display_transaction_fee_in_admin_order_details($order_id, $order) {
    $transaction_fee = get_post_meta($order_id, '_transaction_fee', true);

    if ($transaction_fee) {
        if (is_object($order) && is_a($order, 'WC_Order')) {
            ?>  <tr>
                <td class="label">Transaction Fee:</td>
                <td width="1%"></td>
                <td class="total">
                    <?php echo wc_price($transaction_fee); ?>
                </td>
            </tr>
            <tr>
                <td class="label">Total (after fee):</td>
                <td width="1%"></td>
                <td class="total">
                    <?php echo wc_price($order->get_total() - $transaction_fee); ?>
                </td>
            </tr>
            <?php
        } else {
            error_log("WC_Order object not available in display_transaction_fee_in_admin_order_details for order ID: " . $order_id);
        }
    }
}

Solution

  • Your code is not compatible High-Performance Order Storage (HPOS) and there are multiple mistakes, like:

    • woocommerce_order_status_changed has already $order available via its 4th argument,
    • woocommerce_admin_order_totals_after_total has only $order_id as available argument, but not any $order as 2nd argument…

    Try the following instead:

    // Calculate and save transaction fee after order is created/paid
    add_action('woocommerce_order_status_changed', 'add_transaction_fee_to_order_meta', 10, 4 );
    function add_transaction_fee_to_order_meta( $order_id, $old_status, $new_status, $order ) {
        $targeted_statuses = array('processing', 'completed');
    
        if ( in_array( $new_status, $targeted_statuses) && ! $order->get_meta('_transaction_fee') && $order->get_total() > 0 ) {
            $order->update_meta_data('_transaction_fee', ($order->get_total() * 0.015) + 0.25 );
            $order->save();
        }
    }
    
    
    // Display transaction fee in admin order details *after* "Paid" line
    add_action('woocommerce_admin_order_totals_after_total', 'display_transaction_fee_in_admin_order_details' );
    function display_transaction_fee_in_admin_order_details( $order_id ) {
        $order = wc_get_order($order_id);
        $args  = array( 'currency' => $order->get_currency() );
    
        if ( $transaction_fee = $order->get_meta('_transaction_fee') ) {
            printf('<tr>
                <td class="label">%s</td>
                <td width="%s"></td>
                <td class="total">%s</td>
            </tr>', esc_html__('Transaction Fee:'), '1%', wc_price($transaction_fee, $args ) );
    
            printf('<tr>
                <td class="label">%s</td>
                <td width="%s"></td>
                <td class="total">%s</td>
            </tr>', esc_html__('Total (after fee):'), '1%', wc_price( $order->get_total() - $transaction_fee, $args ) );
        }
    }
    

    It should better work without errors, and will be compatible with HPOS.

    enter image description here

    You may need to target the concerned payment gateway(s) in your first function, as actually your code is executed on all processing or completed orders, no matter what payment method has been used.