Orders: Calculate and Apply Tax Rates to API Orders

Extends a tax plugin (in this example, TaxJar) to ensure that tax rates are calculated and applied to orders created over the WooCommerce REST API.  

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 this one.

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.

/**
* 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 );