diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 2cb06db..358458c 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -28,6 +28,11 @@ Ubercart 4.x
     * uc_order_can_delete() => $order->access('delete')
     * uc_order_delete() => $order->delete()
     * uc_order_product_delete[_multiple]() => entity_delete_multiple('uc_order_product', ...)
+* hook_uc_order() has been split up:
+    * hook_uc_order('load') => hook_uc_order_load()
+    * hook_uc_order('save') => hook_uc_order_insert() and hook_uc_order_update()
+    * hook_uc_order('can_delete') => hook_uc_order_can_delete()
+    * hook_uc_order('delete') => hook_uc_order_delete()
 * Cart items are entities and old APIs have been removed:
     * uc_cart_update_item() => $item->save()
     * uc_cart_remove_item() => $item->delete()
diff --git a/payment/uc_credit/uc_credit.module b/payment/uc_credit/uc_credit.module
index 96f884d..8c468c2 100644
--- a/payment/uc_credit/uc_credit.module
+++ b/payment/uc_credit/uc_credit.module
@@ -7,6 +7,7 @@
 
 use Drupal\Component\Utility\String;
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\uc_order\UcOrderInterface;
 use Drupal\uc_store\Encryption;
 
 /** Just authorize an amount on a credit card account. */
@@ -202,14 +203,6 @@ function uc_credit_uc_store_status() {
  * Implements hook_uc_order().
  */
 function uc_credit_uc_order($op, $order, $arg2) {
-  // Set up the encryption key and object for saving and loading.
-  if ($order->getPaymentMethodId() == 'credit' && ($op == 'save' || $op == 'load')) {
-    // Log an error if encryption isn't configured properly.
-    if (!uc_credit_encryption_key()) {
-      watchdog('uc_credit', 'Credit card encryption must be set up to process credit cards.');
-    }
-  }
-
   switch ($op) {
     case 'submit':
       if ($order->getPaymentMethodId() == 'credit') {
@@ -234,24 +227,32 @@ function uc_credit_uc_order($op, $order, $arg2) {
         }
       }
       break;
+  }
+}
 
-    case 'save':
-      if ($order->getPaymentMethodId() == 'credit' && !empty($order->payment_details)) {
-        _uc_credit_save_cc_data_to_order($order->payment_details, $order->id());
+/**
+ * Implements hook_uc_order_load().
+ */
+function uc_credit_uc_order_load($orders) {
+  foreach ($orders as $order) {
+    if ($order->getPaymentMethodId() == 'credit') {
+      // Load the CC details from the credit cache if available.
+      $order->payment_details = uc_credit_cache('load');
+
+      // Otherwise load any details that might be stored in the data array.
+      if (empty($order->payment_details) && isset($order->data->cc_data)) {
+        $order->payment_details = uc_credit_cache('save', $order->data->cc_data);
       }
-      break;
-
-    case 'load':
-      if ($order->getPaymentMethodId() == 'credit') {
-        // Load the CC details from the credit cache if available.
-        $order->payment_details = uc_credit_cache('load');
+    }
+  }
+}
 
-        // Otherwise load any details that might be stored in the data array.
-        if (empty($order->payment_details) && isset($order->data->cc_data)) {
-          $order->payment_details = uc_credit_cache('save', $order->data->cc_data);
-        }
-      }
-      break;
+/**
+ * Implements hook_uc_order_update().
+ */
+function uc_credit_uc_order_update(UcOrderInterface $order) {
+  if ($order->getPaymentMethodId() == 'credit' && !empty($order->payment_details)) {
+    _uc_credit_save_cc_data_to_order($order->payment_details, $order->id());
   }
 }
 
diff --git a/payment/uc_payment/uc_payment.api.php b/payment/uc_payment/uc_payment.api.php
index af7684e..35f5778 100644
--- a/payment/uc_payment/uc_payment.api.php
+++ b/payment/uc_payment/uc_payment.api.php
@@ -166,11 +166,11 @@ function hook_uc_payment_method() {
  *   should clean up any extra data they stored related to the order.
  * - "order-details": Called when an order is being edited by an administrator.
  *   Return a string or a form array to be displayed to the administator.
- * - "order-load": Called from hook_uc_order('load') when this payment method
+ * - "order-load": Called from hook_uc_order_load() when this payment method
  *   is selected for the order.
  * - "order-process": Called when an order has been edited by an administrator.
  *   Process any form elements returned by the "order-details" op.
- * - "order-save": Called from hook_uc_order('save') when this payment method
+ * - "order-save": Called from hook_uc_order_update() when this payment method
  *   is selected for the order.
  * - "order-submit": Called from hook_uc_order('submit') when this payment
  *   method is selected for the order.
diff --git a/payment/uc_payment/uc_payment.module b/payment/uc_payment/uc_payment.module
index 4f69c78..f17cdb0 100644
--- a/payment/uc_payment/uc_payment.module
+++ b/payment/uc_payment/uc_payment.module
@@ -98,36 +98,6 @@ function uc_payment_uc_order($op, $order) {
   switch ($op) {
     case 'submit':
       return Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order)->orderSubmit($order);
-
-    case 'load':
-      if ($order->getPaymentMethodId()) {
-        Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order)->orderLoad($order);
-      }
-      break;
-
-    case 'save':
-      if ($order->getPaymentMethodId()) {
-        Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order)->orderSave($order);
-      }
-      break;
-
-    case 'can_delete':
-      if (uc_payment_load_payments($order->id()) !== FALSE) {
-        return FALSE;
-      }
-      break;
-
-    case 'delete':
-      db_delete('uc_payment_receipts')
-        ->condition('order_id', $order->id())
-        ->execute();
-
-      // Call each payment method to delete method-specific data from the
-      // database.
-      foreach (uc_payment_method_list() as $id => $method) {
-        Drupal::service('plugin.manager.uc_payment.method')->createInstance($id)->orderDelete($order);
-      }
-      break;
   }
 }
 
@@ -368,18 +338,55 @@ function uc_payment_uc_payment_entered(UcOrderInterface $order) {
 }
 
 /**
+ * Implements hook_uc_order_load().
+ */
+function uc_payment_uc_order_load($orders) {
+  foreach ($orders as $order) {
+    if ($order->getPaymentMethodId()) {
+      Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order)->orderLoad($order);
+    }
+  }
+}
+
+/**
  * Implements hook_uc_order_update().
- *
- * Move non-shippable orders from "payment received" to "completed".
- *
- * @todo Remove when Rules is available.
  */
 function uc_payment_uc_order_update(UcOrderInterface $order) {
+  if ($order->getPaymentMethodId()) {
+    Drupal::service('plugin.manager.uc_payment.method')->createFromOrder($order)->orderSave($order);
+  }
+
+  // Move non-shippable orders from "payment received" to "completed".
+  // @todo Remove when Rules is available.
   if ($order->getStatusId() == 'payment_received' && !$order->isShippable()) {
     $order->setStatusId('completed')->save();
   }
 }
 
+
+/**
+ * Implements hook_uc_order_can_delete().
+ */
+function uc_payment_uc_order_can_delete(UcOrderInterface $order) {
+  if (uc_payment_load_payments($order->id()) !== FALSE) {
+    return FALSE;
+  }
+}
+
+/**
+ * Implements hook_uc_order_delete().
+ */
+function uc_payment_uc_order_delete(UcOrderInterface $order) {
+  db_delete('uc_payment_receipts')
+    ->condition('order_id', $order->id())
+    ->execute();
+
+  // Call each payment method to delete method-specific data from the database.
+  foreach (uc_payment_method_list() as $id => $method) {
+    Drupal::service('plugin.manager.uc_payment.method')->createInstance($id)->orderDelete($order);
+  }
+}
+
 /**
  * Deletes a payment from the database.
  */
diff --git a/shipping/uc_quote/uc_quote.module b/shipping/uc_quote/uc_quote.module
index 244375f..84ec8ec 100644
--- a/shipping/uc_quote/uc_quote.module
+++ b/shipping/uc_quote/uc_quote.module
@@ -13,6 +13,7 @@ use Drupal\Component\Utility\Xss;
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\PrependCommand;
 use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\uc_order\UcOrderInterface;
 use Drupal\uc_store\Address;
 
 /**
@@ -257,38 +258,42 @@ function uc_quote_uc_order_pane() {
 }
 
 /**
- * Implements hook_uc_order().
+ * Implements hook_uc_order_load().
  */
-function uc_quote_uc_order($op, $order, $arg2) {
-  switch ($op) {
-    case 'save':
-      if (isset($order->quote['method'])) {
-        db_merge('uc_order_quotes')
-          ->key(array('order_id' => $order->id()))
-          ->fields(array(
-            'method' => $order->quote['method'],
-            'accessorials' => $order->quote['accessorials'],
-            'rate' => $order->quote['rate'],
-          ))
-          ->execute();
-      }
-      break;
-
-    case 'load':
-      $quote = db_query("SELECT method, accessorials, rate FROM {uc_order_quotes} WHERE order_id = :id", array(':id' => $order->id()))->fetchAssoc();
-      $order->quote = $quote;
-      $order->quote['accessorials'] = strval($quote['accessorials']);
-      break;
+function uc_quote_uc_order_load($orders) {
+  foreach ($orders as $order) {
+    $quote = db_query("SELECT method, accessorials, rate FROM {uc_order_quotes} WHERE order_id = :id", array(':id' => $order->id()))->fetchAssoc();
+    $order->quote = $quote;
+    $order->quote['accessorials'] = strval($quote['accessorials']);
+  }
+}
 
-    case 'delete':
-      db_delete('uc_order_quotes')
-        ->condition('order_id', $order->id())
-        ->execute();
-      break;
+/**
+ * Implements hook_uc_order_update().
+ */
+function uc_quote_uc_order_update(UcOrderInterface $order) {
+  if (isset($order->quote['method'])) {
+    db_merge('uc_order_quotes')
+      ->key(array('order_id' => $order->id()))
+      ->fields(array(
+        'method' => $order->quote['method'],
+        'accessorials' => $order->quote['accessorials'],
+        'rate' => $order->quote['rate'],
+      ))
+      ->execute();
   }
 }
 
 /**
+ * Implements hook_uc_order_delete().
+ */
+function uc_quote_uc_order_delete(UcOrderInterface $order) {
+  db_delete('uc_order_quotes')
+    ->condition('order_id', $order->id())
+    ->execute();
+}
+
+/**
  * Implements hook_uc_line_item().
  */
 function uc_quote_uc_line_item() {
@@ -737,7 +742,7 @@ function uc_quote_apply_quote_to_order($form, &$form_state) {
       }
 
       // Save selected shipping
-      uc_quote_uc_order('save', $order, '');
+      uc_quote_uc_order_update($order);
 
       // Update line items.
       $order->line_items = $order->getLineItems();
diff --git a/shipping/uc_shipping/uc_shipping.module b/shipping/uc_shipping/uc_shipping.module
index b933d98..928c66e 100644
--- a/shipping/uc_shipping/uc_shipping.module
+++ b/shipping/uc_shipping/uc_shipping.module
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\Json;
 use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\Xss;
+use Drupal\uc_order\UcOrderInterface;
 use Drupal\uc_store\Address;
 
 /**
@@ -814,64 +815,59 @@ function uc_shipping_date_views_fields($field) {
 }
 
 /**
- * Implements hook_uc_order().
+ * Implements hook_uc_order_can_delete().
  *
  * Prevent users from deleting orders with a shipment or package that has
  * a tracking number, unless the user has administrative privileges or the
  * "Unconditionally delete orders" permission.
- *
- * Delete packages and shipments attached to orders being deleted.
  */
-function uc_shipping_uc_order($op, $order, $arg2) {
-  switch ($op) {
-    case 'can_delete':
-      // Find and check the shipments for tracking numbers.
-      // {uc_shipments}.tracking_number is NOT NULL.
-      $shipment_count = db_select('uc_shipments')
-        ->condition('order_id', $order->id())
-        ->condition('tracking_number', '', '<>')
-        ->countQuery()
-        ->execute()
-        ->fetchField();
-      if ($shipment_count > 0) {
-        return FALSE;
-      }
-
-      // Find and check the packages.
-      $package_count = db_select('uc_packages')
-        ->condition('order_id', $order->id())
-        ->isNotNull('tracking_number')
-        ->condition('tracking_number', '', '<>')
-        ->countQuery()
-        ->execute()
-        ->fetchField();
-      if ($package_count > 0) {
-        return FALSE;
-      }
-
-      return TRUE;
-      break;
-
-    case 'delete':
-      // Find and delete the shipments.
-      $shipment_ids = db_select('uc_shipments')
-        ->fields(NULL, array('sid'))
-        ->condition('order_id', $order->id())
-        ->execute()
-        ->fetchCol();
-      foreach ($shipment_ids as $sid) {
-        uc_shipping_shipment_delete($sid);
-      }
+function uc_shipping_uc_order_can_delete(UcOrderInterface $order) {
+  // Find and check the shipments for tracking numbers.
+  // {uc_shipments}.tracking_number is NOT NULL.
+  $shipment_count = db_select('uc_shipments')
+    ->condition('order_id', $order->id())
+    ->condition('tracking_number', '', '<>')
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+  if ($shipment_count > 0) {
+    return FALSE;
+  }
+
+  // Find and check the packages.
+  $package_count = db_select('uc_packages')
+    ->condition('order_id', $order->id())
+    ->isNotNull('tracking_number')
+    ->condition('tracking_number', '', '<>')
+    ->countQuery()
+    ->execute()
+    ->fetchField();
+  if ($package_count > 0) {
+    return FALSE;
+  }
+}
 
-      // Find and delete the packages.
-      $package_ids = db_select('uc_packages')
-        ->fields(NULL, array('package_id'))
-        ->condition('order_id', $order->id())
-        ->execute()
-        ->fetchCol();
-      foreach ($package_ids as $pid) {
-        uc_shipping_package_delete($pid);
-      }
-      break;
+/**
+ * Implements hook_uc_order_delete().
+ */
+function uc_shipping_uc_order_delete(UcOrderInterface $order) {
+  // Find and delete the shipments.
+  $shipment_ids = db_select('uc_shipments')
+    ->fields(NULL, array('sid'))
+    ->condition('order_id', $order->id())
+    ->execute()
+    ->fetchCol();
+  foreach ($shipment_ids as $sid) {
+    uc_shipping_shipment_delete($sid);
+  }
+
+  // Find and delete the packages.
+  $package_ids = db_select('uc_packages')
+    ->fields(NULL, array('package_id'))
+    ->condition('order_id', $order->id())
+    ->execute()
+    ->fetchCol();
+  foreach ($package_ids as $pid) {
+    uc_shipping_package_delete($pid);
   }
 }
diff --git a/uc_order/src/Entity/UcOrder.php b/uc_order/src/Entity/UcOrder.php
index 7891b14..f696919 100644
--- a/uc_order/src/Entity/UcOrder.php
+++ b/uc_order/src/Entity/UcOrder.php
@@ -77,8 +77,6 @@ class UcOrder extends ContentEntityBase implements UcOrderInterface {
     foreach ($orders as $id => $order) {
       $order->products = entity_load_multiple_by_properties('uc_order_product', array('order_id' => $id));
 
-      uc_order_module_invoke('load', $order, NULL);
-
       // Load line items... has to be last after everything has been loaded.
       $order->line_items = $order->getLineItems();
     }
@@ -98,8 +96,6 @@ class UcOrder extends ContentEntityBase implements UcOrderInterface {
     }
     $this->host->value = \Drupal::request()->getClientIp();
     $this->modified->value = REQUEST_TIME;
-
-    uc_order_module_invoke('presave', $this, NULL);
   }
 
   /**
@@ -111,8 +107,6 @@ class UcOrder extends ContentEntityBase implements UcOrderInterface {
       uc_order_product_save($this->id(), $product);
     }
 
-    uc_order_module_invoke('save', $this, NULL);
-
     // Record a log entry if the order status has changed.
     if ($update && $this->getStatusId() != $this->original->getStatusId()) {
       $this->logChanges(array(t('Order status') => array(
diff --git a/uc_order/src/UcOrderAccessController.php b/uc_order/src/UcOrderAccessController.php
index 32c8dd8..6d78b11 100644
--- a/uc_order/src/UcOrderAccessController.php
+++ b/uc_order/src/UcOrderAccessController.php
@@ -50,18 +50,16 @@ class UcOrderAccessController extends EntityAccessController {
             return FALSE;
           }
           else {
-            $can_delete = TRUE;
             // See if any modules have a say in this order's eligibility for deletion.
             $module_handler = \Drupal::moduleHandler();
-            foreach ($module_handler->getImplementations('uc_order') as $module) {
-              $function = $module . '_uc_order';
-              if (function_exists($function) && $function('can_delete', $entity, NULL) === FALSE) {
-                $can_delete = FALSE;
-                break;
+            foreach ($module_handler->getImplementations('uc_order_can_delete') as $module) {
+              $function = $module . '_uc_order_can_delete';
+              if ($function($entity) === FALSE) {
+                return FALSE;
               }
             }
 
-            return $can_delete;
+            return TRUE;
           }
         }
         return FALSE;
diff --git a/uc_order/uc_order.api.php b/uc_order/uc_order.api.php
index a2a29ff..48bb601 100644
--- a/uc_order/uc_order.api.php
+++ b/uc_order/uc_order.api.php
@@ -1,5 +1,7 @@
 <?php
 
+use Drupal\uc_order\UcOrderInterface;
+
 /**
  * @file
  * Hooks provided by the Order module.
@@ -126,18 +128,6 @@ function hook_uc_line_item_data_alter(&$items) {
  *   This is the order object.
  * @param $arg2
  *   This is variable and is based on the value of $op:
- *   - new: Called when an order is created. $order is a reference to the new
- *     order object, so modules may add to or modify the order at creation.
- *   - presave: Before an order object is saved, the hook gets invoked with this
- *     op to let other modules alter order data before it is written to the
- *     database. $order is a reference to the order object.
- *   - save: When an order object is being saved, the hook gets invoked with
- *     this op to let other modules do any necessary saving. $order is a
- *     reference to the order object.
- *   - load: Called when an order is loaded after the order and product data has
- *     been loaded from the database. Passes $order as the reference to the
- *     order object, so modules may add to or modify the order object when it's
- *     loaded.
  *   - submit: When a sale is being completed and the customer has clicked the
  *     Submit order button from the checkout screen, the hook is invoked with
  *     this op. This gives modules a chance to determine whether or not the
@@ -152,13 +142,6 @@ function hook_uc_line_item_data_alter(&$items) {
  *         'message' => t('We were unable to process your credit card.'),
  *       ));
  *     @endcode
- *   - can_delete: Called before an order is deleted to verify that the order
- *     may be deleted. Returning FALSE will prevent a delete from happening.
- *     (For example, the payment module returns FALSE by default when an order
- *     has already received payments.)
- *   - delete: Called when an order is deleted and before the rest of the order
- *     information is removed from the database. Passes $order as the order
- *     object to let your module clean up it's tables.
  */
 function hook_uc_order($op, $order, $arg2) {
   switch ($op) {
@@ -217,6 +200,21 @@ function hook_uc_order_actions_alter(&$actions, $order) {
 }
 
 /**
+ * Verifies whether an order may be deleted.
+ *
+ * @param $order
+ *   An order object.
+ *
+ * @return bool
+ *   FALSE if the order should not be deleted.
+ */
+function hook_uc_order_can_delete(UcOrderInterface $order) {
+  if (uc_payment_load_payments($order->id()) !== FALSE) {
+    return FALSE;
+  }
+}
+
+/**
  * Registers callbacks for an order pane.
  *
  * This hook is used to add panes to the order viewing and administration
diff --git a/uc_order/uc_order.module b/uc_order/uc_order.module
index e74eea4..89fba77 100644
--- a/uc_order/uc_order.module
+++ b/uc_order/uc_order.module
@@ -479,8 +479,6 @@ function uc_order_new($uid = 0, $state = 'in_checkout') {
 
   $order->save();
 
-  uc_order_module_invoke('new', $order, NULL);
-
   return $order;
 }
 
@@ -598,22 +596,6 @@ function uc_order_comment_save($order_id, $uid, $message, $type = 'admin', $stat
 }
 
 /**
- * Invokes hook_uc_order() in every module.
- *
- * We cannot use module_invoke() for this, because the arguments need to
- * be passed by reference.
- */
-function uc_order_module_invoke($op, &$order, $edit) {
-  $module_handler = \Drupal::moduleHandler();
-  foreach ($module_handler->getImplementations('uc_order') as $module) {
-    $function = $module . '_uc_order';
-    if (function_exists($function)) {
-      $function($op, $order, $edit);
-    }
-  }
-}
-
-/**
  * Determines whether a product is shippable or not.
  */
 function uc_order_product_is_shippable($product) {
diff --git a/uc_taxes/uc_taxes.module b/uc_taxes/uc_taxes.module
index 6e8b825..04b366c 100644
--- a/uc_taxes/uc_taxes.module
+++ b/uc_taxes/uc_taxes.module
@@ -11,6 +11,7 @@ use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\entity\Entity\EntityViewDisplay;
 use Drupal\node\NodeInterface;
+use Drupal\uc_order\UcOrderInterface;
 
 /**
  * Implements hook_permission().
@@ -44,7 +45,7 @@ function uc_taxes_menu() {
  * calculations are made.
  */
 function uc_taxes_module_implements_alter(&$implementations, $hook) {
-  if (in_array($hook, array('uc_order', 'entity_view_alter'))) {
+  if (in_array($hook, array('uc_order_insert', 'uc_order_update', 'entity_view_alter'))) {
     $group = $implementations['uc_taxes'];
     unset($implementations['uc_taxes']);
     $implementations['uc_taxes'] = $group;
@@ -149,60 +150,63 @@ function uc_taxes_uc_line_item() {
 }
 
 /**
- * Implements hook_uc_order().
+ * Implements hook_uc_order_insert().
+ */
+function uc_taxes_uc_order_insert(UcOrderInterface $order) {
+  uc_taxes_uc_order_update($order);
+}
+
+/**
+ * Implements hook_uc_order_update().
  *
  * Updates and saves tax line items to the order.
  */
-function uc_taxes_uc_order($op, $order, $arg2) {
-  switch ($op) {
-    case 'save':
-      $changes = array();
-      $line_items = uc_taxes_calculate($order);
-      foreach ($line_items as $id => $tax) {
-        $line_items[$id] = _uc_taxes_to_line_item($tax);
-      }
-
-      // Loop through existing line items and update or delete as necessary.
-      if (is_array($order->line_items)) {
-        foreach ($order->line_items as $i => $line) {
-          if ($line['type'] == 'tax') {
-            $delete = TRUE;
-            foreach ($line_items as $id => $new_line) {
-              if ($new_line['data']['tax_id'] == $line['data']['tax_id']) {
-                if ($new_line['amount'] != $line['amount']) {
-                  uc_order_update_line_item($line['line_item_id'], $new_line['title'], $new_line['amount'], $new_line['data']);
-                  $order->line_items[$i]['amount'] = $new_line['amount'];
-                  $order->line_items[$i]['data'] = $new_line['data'];
-                  $changes[] = t('Changed %title to %amount.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
-                }
-                unset($line_items[$id]);
-                $delete = FALSE;
-                break;
-              }
-            }
-            if ($delete) {
-              uc_order_delete_line_item($line['line_item_id']);
-              unset($order->line_items[$i]);
-              $changes[] = t('Removed %title.', array('%title' => $line['title']));
+function uc_taxes_uc_order_update(UcOrderInterface $order) {
+  $changes = array();
+  $line_items = uc_taxes_calculate($order);
+  foreach ($line_items as $id => $tax) {
+    $line_items[$id] = _uc_taxes_to_line_item($tax);
+  }
+
+  // Loop through existing line items and update or delete as necessary.
+  if (is_array($order->line_items)) {
+    foreach ($order->line_items as $i => $line) {
+      if ($line['type'] == 'tax') {
+        $delete = TRUE;
+        foreach ($line_items as $id => $new_line) {
+          if ($new_line['data']['tax_id'] == $line['data']['tax_id']) {
+            if ($new_line['amount'] != $line['amount']) {
+              uc_order_update_line_item($line['line_item_id'], $new_line['title'], $new_line['amount'], $new_line['data']);
+              $order->line_items[$i]['amount'] = $new_line['amount'];
+              $order->line_items[$i]['data'] = $new_line['data'];
+              $changes[] = t('Changed %title to %amount.', array('%amount' => uc_currency_format($new_line['amount']), '%title' => $new_line['title']));
             }
+            unset($line_items[$id]);
+            $delete = FALSE;
+            break;
           }
         }
-      }
-
-      // Now add line items for any remaining new taxes.
-      if (is_array($line_items)) {
-        foreach ($line_items as $line) {
-          $order->line_items[] = uc_order_line_item_add($order->id(), 'tax', $line['title'], $line['amount'], $line['weight'], $line['data']);
-          $changes[] = t('Added %amount for %title.', array('%amount' => uc_currency_format($line['amount']), '%title' => $line['title']));
+        if ($delete) {
+          uc_order_delete_line_item($line['line_item_id']);
+          unset($order->line_items[$i]);
+          $changes[] = t('Removed %title.', array('%title' => $line['title']));
         }
       }
+    }
+  }
 
-      // And log the changes to the order.
-      if (count($changes)) {
-        $order->logChanges($changes);
-        usort($order->line_items, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
-      }
-    break;
+  // Now add line items for any remaining new taxes.
+  if (is_array($line_items)) {
+    foreach ($line_items as $line) {
+      $order->line_items[] = uc_order_line_item_add($order->id(), 'tax', $line['title'], $line['amount'], $line['weight'], $line['data']);
+      $changes[] = t('Added %amount for %title.', array('%amount' => uc_currency_format($line['amount']), '%title' => $line['title']));
+    }
+  }
+
+  // And log the changes to the order.
+  if (count($changes)) {
+    $order->logChanges($changes);
+    usort($order->line_items, 'Drupal\Component\Utility\SortArray::sortByWeightElement');
   }
 }
 
