I am attempting to add a fee programatically based on the payment method selected by the user by calling some commerce_fees functions in an Ajax callback attached to a custom payment method field. This is a single page checkout and so this functionality is essential.

The custom fee line item does seem to be added successfully, at least in the updated cart summary view, but temporarily.

custom fee added after ajax cart refresh

I say temporarily for two reasons:

  1. After the checkout page is submitted and the completed order is viewed, no trace of the fee remains (the total is $400 again and a price of only $400 was sent to the payment gateway).
  2. When I dpm the $order (after refreshing the page) the only indication that the fee is indeed being added somewhere is that the commerce_order_total...['price']['components'] array contains the custom fee. The only item in the commerce_line_items array is the original product.

I am attempting this programatically because, while I could obtain the same fee-adding functionality with the Rules module, I faced a somewhat similar issue of the fee being deleted immediately after checkout submission, only to be reapplied after the payment was processed by the third-party payment gateway. That frustrated me to no end and so I thought by adding the fees manually they would stick.

Here's my custom module code, utilizing a hodge-podge of some commerce_fees functions along with hook_commerce_cart_order_refresh.

I utilized some of the logic explained in this question: Commerce checkout - Update cart pane after user select a shipping option (ajax).

function single_page_checkout_payment_surcharge_refresh($form, &$form_state) {

  // Get order from form
  list($order, $checkout_pane) = $form_state['build_info']['args'];

  // Load up to date order
  $order = commerce_order_load($order->order_id);

  // Get selected payment method
  $payment_method = $form['field_payment_methods']['und']['#value'];

  // Set new default value in case of page refresh
  $form['field_payment_methods']['und']['#default_value'] = $payment_method;

  // Set the selected payment method
  $order->field_payment_methods['und'][0]['value'] = $payment_method;

  // Wrap order for hook_checkout_commerce_cart_order_refresh
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

  * Implements hook_commerce_cart_order_refresh().
  * Remove any existing commerce custom fee line item.
  function singe_page_checkout_commerce_cart_order_refresh($order_wrapper) {
    $line_item_ids = array();

    foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
      // If the line item is a fee remove it.
      if ($line_item_wrapper->getBundle() == 'fee') {
        // Delete the line item, and remove it from the order.
        $line_item_ids[] = $line_item_wrapper->line_item_id->value();

    // If we found any fee type line items...
    if (!empty($line_item_ids)) {
      // Then delete the line items.


  // delete existing line items with same id
  commerce_fees_delete_fee_line_item($order, $fee, TRUE);

  switch ($payment_method) {

    case 'nmi_dp_cc|commerce_payment_nmi_dp_cc':
      $fee = 'cc_surcharge';
      $base_rate = $order->commerce_order_total['und'][0]['data']['components'][0]['price'];
      $base_rate['amount'] = $base_rate['amount'] * 0.04;

    case 'nmi_dp_echeck|commerce_payment_nmi_dp_echeck':
      $fee = 'check_processing';
      $base_rate = $order->commerce_order_total['und'][0]['data']['components'][0]['price'];
      $base_rate['amount'] = 150;

  $base_rate['data'] = commerce_price_component_add($base_rate, $fee, $base_rate, TRUE, FALSE);

  // Create new fee line item with the base_rate decided by the fee type
  $line_item = commerce_fees_line_item_new($fee, $base_rate, $order->order_id);

  // Save and add the line item to the order.
  $new_line_item = commerce_fees_add_fee_line_item($line_item, $order, FALSE);

  // Save the order

  // Update checkout cart review
  list($view_id, $display_id) = explode('|', variable_get('commerce_cart_contents_pane_view', 'commerce_cart_summary|default'));

  $commands[] = ajax_command_replace('.view-commerce-cart-summary', commerce_embed_view($view_id, $display_id, array($order->order_id)));

  $commands[] = ajax_command_after('.main form', theme('status_messages'));

  return array('#type' => 'ajax', '#commands' => $commands);

A side issue is also that this Ajax call seems to take a good while to fully execute, because if I select another payment method too soon, the existing fee(s) are not properly removed, and so fees begin to stack.

NOTE: I am fully aware of the need to alert the user of the surcharge and that some payment gateways do not allow it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.