Orders now have a total_paid field (with getTotalPaid() and setTotalPaid() methods), which is used by getBalance() to calculate the order balance (total_price - total_paid).
Sites can potentially have too many orders to be feasible for an update hook to resave, so there is no update hook, all existing orders have a total_paid of "0". Adding or refunding a payment for an existing order will rebuild the total_paid field.
Developers can use the commerce_payment.order_manager service to update orders without having to modify payments:
$order_storage = \Drupal::entityTypeManager()->getStorage('commerce_order');
// Load all placed orders.
$order_query = $order_storage->getQuery();
$order_query->condition('state', 'draft', '<>');
$order_ids = $order_query->execute();
$orders = $order_storage->loadMultiple($order_ids);
$payment_order_manager = \Drupal::service('commerce_payment.order_manager');
foreach ($orders as $order) {
$payment_order_manager->updateTotalPaid($order);
}
An OrderEvents::ORDER_PAID event is dispatched the first time an order is paid in full (balance reaches zero).
This event is guaranteed to run once (refunding a payment then adding a new one won't re-trigger it).