Orders: Calculate and Apply Tax Rates to REST API Orders

WooCommerce Tax Rates calculate and apply without any need to extend the code, but if you use a Tax plugin to modify WooCommerce Tax Rates and it does not yet support WooCommerce Orders created via the REST API, you may need a solution similar to these. Below we provide two examples (one for TaxJar and one for Avalara AvaTax) to help solve for this issue.

Important: Always test customizations to your site on a staging environment before making changes to your live / production site.  If you are not experienced with testing changes on a staging site, here is a good article to review.

TaxJar

/**
* Hook into the Order Save and Taxjar to calculate totals.
* @param bool $and_taxes True if Taxes should be calculated.
* @param WC_Abstract_Order $order The current order.
*/
function xx_taxjar_extension( $and_taxes, $order ){
	if ( !( $order instanceof WC_Order ) || ( 'rest-api' != $order->get_created_via() ) )
	return;

	if ( !class_exists('WC_Taxjar_Integration') )
	return;

	$taxjar = new WC_Taxjar_Integration;
	$address = $taxjar->get_address_from_order( $order );

	$line_items = array();
	$taxjar->backend_tax_classes = array();
	$order_items = $order->get_items();

	foreach ( $order_items as $item_key => $item ) {
		if ( is_object( $item ) ) { // Woo 3.0+
			$id = $item->get_product_id();
			$quantity = $item->get_quantity();
			$unit_price = wc_format_decimal( $item->get_subtotal() / $quantity );
			$discount = wc_format_decimal( $item->get_subtotal() - $item->get_total() );
			$tax_class_name = $item->get_tax_class();
			$tax_status = $item->get_tax_status();
		} else { // Woo 2.6
			$id = $item['product_id'];
			$quantity = $item['qty'];
			$unit_price = wc_format_decimal( $item['line_subtotal'] / $quantity );
			$discount = wc_format_decimal( $item['line_subtotal'] - $item['line_total'] );
			$tax_class_name = $item['tax_class'];
			$product = $order->get_product_from_item( $item );
			$tax_status = $product ? $product->get_tax_status() : 'taxable';
		}

		$taxjar->backend_tax_classes[$id] = $tax_class_name;

		$tax_class = explode( '-', $tax_class_name );
		$tax_code = '';

		if ( isset( $tax_class ) && is_numeric( end( $tax_class ) ) ) {
			$tax_code = end( $tax_class );
		}

		if ( 'taxable' !== $tax_status ) {
			$tax_code = '99999';
		}

		if ( $unit_price ) {
			array_push($line_items, array(
				'id' => $id . '-' . $item_key,
				'quantity' => $quantity,
				'product_tax_code' => $tax_code,
				'unit_price' => $unit_price,
				'discount' => $discount,
			));
		}
	}

	if ( method_exists( $order, 'get_shipping_total' ) ) {
		$shipping = $order->get_shipping_total(); // Woo 3.0+
	} else {
		$shipping = $order->get_total_shipping(); // Woo 2.6
	}

	$taxes = $taxjar->calculate_tax( array(
		'to_country' => $address[ 'to_country' ],
		'to_state' => $address[ 'to_state' ],
		'to_zip' => $address[ 'to_zip' ],
		'to_city' => $address[ 'to_city' ],
		'to_street' => $address[ 'to_street' ],
		'shipping_amount' => $shipping,
		'line_items' => $line_items,
	) );

	foreach ( $order_items as $item_key => $item ) {
		$product_id = $item->get_product_id();
		$line_item_key = $product_id . '-' . $item_key;
		$item_taxes = $item->get_taxes();

		$taxes_charged = 0;
		if( sizeof($item_taxes) > 0 ){
			if( sizeof($item_taxes["total"]) > 0 ){
				foreach( $item_taxes["total"] AS $tax_total ){
					$taxes_charged = (float)$tax_total;
				}
			}
		}

		if( $taxes_charged == 0 ){
			if ( isset( $taxes['rate_ids'][ $line_item_key ] ) ) {
				$rate_id = $taxes['rate_ids'][ $line_item_key ];
				$item_tax = new WC_Order_Item_Tax();
				$item_tax->set_rate( $rate_id );
				$item_tax->set_order_id( $order->get_id() );
				$item_tax->save();
			}
		}
	}
}
add_action( 'woocommerce_order_before_calculate_totals', 'xx_taxjar_extension', 10, 2 );

Avalara AvaTax

/**
* Hook into the Order Save and calculate Avatax totals.
* @param bool $and_taxes True if Taxes should be calculated.
* @param WC_Abstract_Order $order The current order.
*/
function xx_avatax_calc_tax( $and_taxes, $wc_order ){
	if ( !( $wc_order instanceof WC_Order ) || ( 'rest-api' !== $wc_order->get_created_via() ) )
	return;

	if ( !function_exists( 'wc_avatax' ) )
	return;

  if( !$and_taxes )
  return;

  wc_avatax()->get_order_handler()->calculate_order_tax( $wc_order );
}
add_action( 'woocommerce_order_before_calculate_totals', 'xx_avatax_calc_tax', 10, 2 );